Browse Source

chore(nc-gui): fix type issues and set proper ref types

pull/3606/head
braks 2 years ago
parent
commit
81db2c8435
  1. 4
      packages/nc-gui/components/cell/attachment/Modal.vue
  2. 4
      packages/nc-gui/components/cell/attachment/index.vue
  3. 10
      packages/nc-gui/components/cell/attachment/utils.ts
  4. 2
      packages/nc-gui/components/dlg/ViewCreate.vue
  5. 9
      packages/nc-gui/components/smartsheet-column/EditOrAdd.vue
  6. 15
      packages/nc-gui/components/smartsheet-column/EditOrAddProvider.vue
  7. 12
      packages/nc-gui/components/smartsheet-column/FormulaOptions.vue
  8. 11
      packages/nc-gui/components/smartsheet-column/LinkedToAnotherRecordOptions.vue
  9. 4
      packages/nc-gui/components/smartsheet-column/LookupOptions.vue
  10. 2
      packages/nc-gui/components/smartsheet-column/PercentOptions.vue
  11. 2
      packages/nc-gui/components/smartsheet-column/RatingOptions.vue
  12. 7
      packages/nc-gui/components/smartsheet-column/RollupOptions.vue
  13. 11
      packages/nc-gui/components/smartsheet-header/CellIcon.vue
  14. 2
      packages/nc-gui/components/smartsheet-header/Menu.vue
  15. 4
      packages/nc-gui/components/smartsheet-header/VirtualCell.vue
  16. 7
      packages/nc-gui/components/smartsheet-header/VirtualCellIcon.vue
  17. 4
      packages/nc-gui/components/smartsheet-toolbar/ColumnFilter.vue
  18. 2
      packages/nc-gui/components/smartsheet-toolbar/ColumnFilterMenu.vue
  19. 10
      packages/nc-gui/components/smartsheet-toolbar/ExportSubActions.vue
  20. 31
      packages/nc-gui/components/smartsheet-toolbar/FieldListAutoCompleteDropdown.vue
  21. 4
      packages/nc-gui/components/smartsheet-toolbar/FieldsMenu.vue
  22. 12
      packages/nc-gui/components/smartsheet-toolbar/MoreActions.vue
  23. 14
      packages/nc-gui/components/smartsheet-toolbar/ShareView.vue
  24. 9
      packages/nc-gui/components/smartsheet-toolbar/SharedViewList.vue
  25. 8
      packages/nc-gui/components/smartsheet-toolbar/SortListMenu.vue
  26. 4
      packages/nc-gui/components/smartsheet-toolbar/ViewActions.vue
  27. 4
      packages/nc-gui/components/smartsheet/ApiSnippet.vue
  28. 12
      packages/nc-gui/components/smartsheet/Form.vue
  29. 6
      packages/nc-gui/components/smartsheet/Gallery.vue
  30. 8
      packages/nc-gui/components/smartsheet/Grid.vue
  31. 2
      packages/nc-gui/components/smartsheet/sidebar/toolbar/DeleteTable.vue
  32. 6
      packages/nc-gui/components/tabs/Auth.vue
  33. 4
      packages/nc-gui/components/tabs/Smartsheet.vue
  34. 7
      packages/nc-gui/components/tabs/auth/ApiTokenManagement.vue
  35. 4
      packages/nc-gui/components/tabs/auth/UserManagement.vue
  36. 6
      packages/nc-gui/components/tabs/auth/user-management/FeedbackForm.vue
  37. 8
      packages/nc-gui/components/tabs/auth/user-management/ShareBase.vue
  38. 18
      packages/nc-gui/components/tabs/auth/user-management/UsersModal.vue
  39. 12
      packages/nc-gui/components/template/Editor.vue
  40. 4
      packages/nc-gui/components/virtual-cell/Formula.vue
  41. 16
      packages/nc-gui/components/virtual-cell/HasMany.vue
  42. 4
      packages/nc-gui/components/virtual-cell/Lookup.vue
  43. 16
      packages/nc-gui/components/virtual-cell/ManyToMany.vue
  44. 6
      packages/nc-gui/components/webhook/ChannelMultiSelect.vue
  45. 19
      packages/nc-gui/components/webhook/Editor.vue
  46. 9
      packages/nc-gui/components/webhook/List.vue
  47. 17
      packages/nc-gui/components/webhook/Test.vue
  48. 28
      packages/nc-gui/composables/useColumnCreateStore.ts
  49. 193
      packages/nc-gui/composables/useSmartsheetRowStore.ts
  50. 13
      packages/nc-gui/composables/useViewColumns.ts
  51. 4
      packages/nc-gui/composables/useViewFilters.ts
  52. 9
      packages/nc-gui/composables/useViewSorts.ts
  53. 2
      packages/nc-gui/pages/[projectType]/[projectId]/index/index/[type]/[title]/[[viewTitle]].vue
  54. 2
      packages/nc-gui/pages/[projectType]/[projectId]/index/index/auth.vue
  55. 4
      packages/nc-gui/pages/[projectType]/form/[viewId].vue
  56. 4
      packages/nc-gui/pages/[projectType]/form/[viewId]/index.vue
  57. 2
      packages/nc-gui/pages/index/index/[projectId].vue
  58. 2
      packages/nc-gui/utils/iconUtils.ts

4
packages/nc-gui/components/cell/attachment/Modal.vue

@ -9,7 +9,7 @@ const { isUIAllowed } = useUIPermission()
const { const {
open, open,
isLoading, isLoading,
isPublicGrid, isPublic,
isReadonly, isReadonly,
visibleItems, visibleItems,
modalVisible, modalVisible,
@ -68,7 +68,7 @@ function onClick(item: Record<string, any>) {
<template #title> <template #title>
<div class="flex gap-4"> <div class="flex gap-4">
<div <div
v-if="isSharedForm || (!isReadonly && isUIAllowed('tableAttachment') && !isPublicGrid && !isLocked)" v-if="isSharedForm || (!isReadonly && isUIAllowed('tableAttachment') && !isPublic && !isLocked)"
class="nc-attach-file group" class="nc-attach-file group"
@click="open" @click="open"
> >

4
packages/nc-gui/components/cell/attachment/index.vue

@ -65,7 +65,7 @@ watch(
} else { } else {
nextTick(() => { nextTick(() => {
const nextCell = cellRefs.value.reduceRight((cell, curr) => { const nextCell = cellRefs.value.reduceRight((cell, curr) => {
if (!cell && curr.dataset.key === `${rowIndex}${column.value.id}`) cell = curr if (!cell && curr.dataset.key === `${rowIndex}${column.value!.id}`) cell = curr
return cell return cell
}, undefined as HTMLTableDataCellElement | undefined) }, undefined as HTMLTableDataCellElement | undefined)
@ -118,7 +118,7 @@ onKeyDown('Escape', () => {
watch( watch(
() => storedFiles.value.length || 0, () => storedFiles.value.length || 0,
() => { () => {
rowState.value[column.value.title!] = storedFiles.value rowState.value[column.value!.title!] = storedFiles.value
}, },
) )

10
packages/nc-gui/components/cell/attachment/utils.ts

@ -1,9 +1,9 @@
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import FileSaver from 'file-saver' import FileSaver from 'file-saver'
import { useI18n } from 'vue-i18n'
import { import {
ColumnInj, ColumnInj,
EditModeInj, EditModeInj,
IsFormInj,
IsPublicInj, IsPublicInj,
MetaInj, MetaInj,
NOCO, NOCO,
@ -14,11 +14,11 @@ import {
ref, ref,
useApi, useApi,
useFileDialog, useFileDialog,
useI18n,
useInjectionState, useInjectionState,
useProject, useProject,
watch, watch,
} from '#imports' } from '#imports'
import { IsFormInj } from '~/context'
import MdiPdfBox from '~icons/mdi/pdf-box' import MdiPdfBox from '~icons/mdi/pdf-box'
import MdiFileWordOutline from '~icons/mdi/file-word-outline' import MdiFileWordOutline from '~icons/mdi/file-word-outline'
import MdiFilePowerpointBox from '~icons/mdi/file-powerpoint-box' import MdiFilePowerpointBox from '~icons/mdi/file-powerpoint-box'
@ -40,9 +40,9 @@ export const [useProvideAttachmentCell, useAttachmentCell] = useInjectionState(
const isForm = inject(IsFormInj, ref(false)) const isForm = inject(IsFormInj, ref(false))
const meta = inject(MetaInj)! const meta = inject(MetaInj, ref())
const column = inject(ColumnInj)! const column = inject(ColumnInj, ref())
const editEnabled = inject(EditModeInj, ref(false)) const editEnabled = inject(EditModeInj, ref(false))
@ -119,7 +119,7 @@ export const [useProvideAttachmentCell, useAttachmentCell] = useInjectionState(
try { try {
const data = await api.storage.upload( const data = await api.storage.upload(
{ {
path: [NOCO, project.value.title, meta.value.title, column.value.title].join('/'), path: [NOCO, project.value.title, meta.value?.title, column.value?.title].join('/'),
}, },
{ {
files: file, files: file,

2
packages/nc-gui/components/dlg/ViewCreate.vue

@ -42,7 +42,7 @@ const { t } = useI18n()
const { isLoading: loading, api } = useApi() const { isLoading: loading, api } = useApi()
const meta = inject(MetaInj) const meta = inject(MetaInj, ref())
const viewList = inject(ViewListInj) const viewList = inject(ViewListInj)

9
packages/nc-gui/components/smartsheet-column/EditOrAdd.vue

@ -1,10 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { UITypes, isVirtualCol } from 'nocodb-sdk' import { UITypes, isVirtualCol } from 'nocodb-sdk'
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import { useNuxtApp } from '#app' import { IsFormInj, MetaInj, ReloadViewDataHookInj, computed, inject, uiTypes, useMetas, useNuxtApp, watchEffect } from '#imports'
import { computed, inject, useMetas, watchEffect } from '#imports'
import { IsFormInj, MetaInj, ReloadViewDataHookInj } from '~/context'
import { uiTypes } from '~/utils/columnUtils'
import MdiPlusIcon from '~icons/mdi/plus-circle-outline' import MdiPlusIcon from '~icons/mdi/plus-circle-outline'
import MdiMinusIcon from '~icons/mdi/minus-circle-outline' import MdiMinusIcon from '~icons/mdi/minus-circle-outline'
import MdiIdentifierIcon from '~icons/mdi/identifier' import MdiIdentifierIcon from '~icons/mdi/identifier'
@ -20,7 +17,7 @@ const { t } = useI18n()
const { $e } = useNuxtApp() const { $e } = useNuxtApp()
const meta = inject(MetaInj) const meta = inject(MetaInj, ref())
const isForm = inject(IsFormInj, ref(false)) const isForm = inject(IsFormInj, ref(false))
@ -48,7 +45,7 @@ const uiTypesOptions = computed<typeof uiTypes>(() => {
}) })
const reloadMetaAndData = async () => { const reloadMetaAndData = async () => {
await getMeta(meta?.value.id as string, true) await getMeta(meta.value?.id as string, true)
reloadDataTrigger?.trigger() reloadDataTrigger?.trigger()
} }

15
packages/nc-gui/components/smartsheet-column/EditOrAddProvider.vue

@ -1,8 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { ColumnType, TableType } from 'nocodb-sdk' import type { ColumnType } from 'nocodb-sdk'
import type { Ref } from 'vue' import type { Ref } from 'vue'
import { inject } from '#imports' import { MetaInj, inject } from '#imports'
import { MetaInj } from '~/context'
interface Props { interface Props {
column?: Ref<ColumnType & { meta: any }> column?: Ref<ColumnType & { meta: any }>
@ -12,14 +11,10 @@ const props = defineProps<Props>()
const emit = defineEmits(['submit', 'cancel']) const emit = defineEmits(['submit', 'cancel'])
const meta = inject(MetaInj) const meta = inject(MetaInj, ref())
if (props?.column) { const column = toRef(props, 'column')
const column = toRef(props, 'column') useProvideColumnCreateStore(meta, column as Ref<ColumnType | undefined>)
useProvideColumnCreateStore(meta as Ref<TableType>, column)
} else {
useProvideColumnCreateStore(meta as Ref<TableType>)
}
</script> </script>
<template> <template>

12
packages/nc-gui/components/smartsheet-column/FormulaOptions.vue

@ -41,7 +41,7 @@ enum JSEPNode {
ARRAY_EXP = 'ArrayExpression', ARRAY_EXP = 'ArrayExpression',
} }
const meta = inject(MetaInj) const meta = inject(MetaInj, ref())
const columns = computed(() => meta?.value?.columns || []) const columns = computed(() => meta?.value?.columns || [])
@ -102,7 +102,7 @@ const suggestionsList = computed(() => {
if (c.uidt === UITypes.LinkToAnotherRecord && c.system) return false if (c.uidt === UITypes.LinkToAnotherRecord && c.system) return false
// v1 logic? skip the current column // v1 logic? skip the current column
if (!column) return true if (!column) return true
return column.value.id !== c.id return column.value?.id !== c.id
}) })
.map((c: any) => ({ .map((c: any) => ({
text: c.title, text: c.title,
@ -238,7 +238,7 @@ function validateAgainstMeta(parsedTree: any, errors = new Set(), typeErrors = n
} else if (parsedTree.type === JSEPNode.IDENTIFIER) { } else if (parsedTree.type === JSEPNode.IDENTIFIER) {
if ( if (
columns.value columns.value
.filter((c: Record<string, any>) => !column || column.value.id !== c.id) .filter((c: Record<string, any>) => !column || column.value?.id !== c.id)
.every((c: Record<string, any>) => c.title !== parsedTree.name) .every((c: Record<string, any>) => c.title !== parsedTree.name)
) { ) {
errors.add(`Column '${parsedTree.name}' is not available`) errors.add(`Column '${parsedTree.name}' is not available`)
@ -249,7 +249,7 @@ function validateAgainstMeta(parsedTree: any, errors = new Set(), typeErrors = n
// get all formula columns excluding itself // get all formula columns excluding itself
const formulaPaths = columns.value const formulaPaths = columns.value
.filter((c: Record<string, any>) => c.id !== column?.value.id && c.uidt === UITypes.Formula) .filter((c: Record<string, any>) => c.id !== column.value?.id && c.uidt === UITypes.Formula)
.reduce((res: Record<string, any>[], c: Record<string, any>) => { .reduce((res: Record<string, any>[], c: Record<string, any>) => {
// in `formula`, get all the target neighbours // in `formula`, get all the target neighbours
// i.e. all column id (e.g. cl_xxxxxxxxxxxxxx) with formula type // i.e. all column id (e.g. cl_xxxxxxxxxxxxxx) with formula type
@ -265,9 +265,9 @@ function validateAgainstMeta(parsedTree: any, errors = new Set(), typeErrors = n
// include target formula column (i.e. the one to be saved if applicable) // include target formula column (i.e. the one to be saved if applicable)
const targetFormulaCol = columns.value.find((c: ColumnType) => c.title === parsedTree.name && c.uidt === UITypes.Formula) const targetFormulaCol = columns.value.find((c: ColumnType) => c.title === parsedTree.name && c.uidt === UITypes.Formula)
if (targetFormulaCol && column?.value.id) { if (targetFormulaCol && column.value?.id) {
formulaPaths.push({ formulaPaths.push({
[column?.value?.id as string]: [targetFormulaCol.id], [column.value?.id as string]: [targetFormulaCol.id],
}) })
} }
const vertices = formulaPaths.length const vertices = formulaPaths.length

11
packages/nc-gui/components/smartsheet-column/LinkedToAnotherRecordOptions.vue

@ -1,7 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { ModelTypes, MssqlUi, SqliteUi } from 'nocodb-sdk' import { ModelTypes, MssqlUi, SqliteUi } from 'nocodb-sdk'
import { inject, useProject } from '#imports' import { MetaInj, inject, useProject } from '#imports'
import { MetaInj } from '~/context'
import MdiPlusIcon from '~icons/mdi/plus-circle-outline' import MdiPlusIcon from '~icons/mdi/plus-circle-outline'
import MdiMinusIcon from '~icons/mdi/minus-circle-outline' import MdiMinusIcon from '~icons/mdi/minus-circle-outline'
@ -13,7 +12,7 @@ const props = defineProps<Props>()
const emit = defineEmits(['update:value']) const emit = defineEmits(['update:value'])
const vModel = useVModel(props, 'value', emit) const vModel = useVModel(props, 'value', emit)
const meta = $(inject(MetaInj)!) const meta = $(inject(MetaInj, ref()))
const { setAdditionalValidations, validateInfos, onDataTypeChange } = useColumnCreateStoreOrThrow() const { setAdditionalValidations, validateInfos, onDataTypeChange } = useColumnCreateStoreOrThrow()
@ -25,10 +24,10 @@ setAdditionalValidations({
const onUpdateDeleteOptions = sqlUi === MssqlUi ? ['NO ACTION'] : ['NO ACTION', 'CASCADE', 'RESTRICT', 'SET NULL', 'SET DEFAULT'] const onUpdateDeleteOptions = sqlUi === MssqlUi ? ['NO ACTION'] : ['NO ACTION', 'CASCADE', 'RESTRICT', 'SET NULL', 'SET DEFAULT']
if (!vModel.value.parentId) vModel.value.parentId = meta.id if (!vModel.value.parentId) vModel.value.parentId = meta?.id
if (!vModel.value.childId) vModel.value.childId = null if (!vModel.value.childId) vModel.value.childId = null
if (!vModel.value.childColumn) vModel.value.childColumn = `${meta.table_name}_id` if (!vModel.value.childColumn) vModel.value.childColumn = `${meta?.table_name}_id`
if (!vModel.value.childTable) vModel.value.childTable = meta.table_name if (!vModel.value.childTable) vModel.value.childTable = meta?.table_name
if (!vModel.value.parentTable) vModel.value.parentTable = vModel.value.rtn || '' if (!vModel.value.parentTable) vModel.value.parentTable = vModel.value.rtn || ''
if (!vModel.value.parentColumn) vModel.value.parentColumn = vModel.value.rcn || '' if (!vModel.value.parentColumn) vModel.value.parentColumn = vModel.value.rcn || ''

4
packages/nc-gui/components/smartsheet-column/LookupOptions.vue

@ -11,7 +11,7 @@ const props = defineProps<Props>()
const emit = defineEmits(['update:value']) const emit = defineEmits(['update:value'])
const vModel = useVModel(props, 'value', emit) const vModel = useVModel(props, 'value', emit)
const meta = $(inject(MetaInj)!) const meta = $(inject(MetaInj, ref()))
const { setAdditionalValidations, validateInfos, onDataTypeChange } = useColumnCreateStoreOrThrow() const { setAdditionalValidations, validateInfos, onDataTypeChange } = useColumnCreateStoreOrThrow()
@ -38,7 +38,7 @@ const refTables = $computed(() => {
return [] return []
} }
return meta.columns return meta?.columns
?.filter((c: any) => c.uidt === UITypes.LinkToAnotherRecord && !c.system) ?.filter((c: any) => c.uidt === UITypes.LinkToAnotherRecord && !c.system)
.map((c: ColumnType) => ({ .map((c: ColumnType) => ({
col: c.colOptions, col: c.colOptions,

2
packages/nc-gui/components/smartsheet-column/PercentOptions.vue

@ -1,7 +1,7 @@
<!-- File not in use for now --> <!-- File not in use for now -->
<script setup lang="ts"> <script setup lang="ts">
import { precisions } from '@/utils/percentUtils' import { precisions } from '#imports'
interface Props { interface Props {
value: Record<string, any> value: Record<string, any>

2
packages/nc-gui/components/smartsheet-column/RatingOptions.vue

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { getMdiIcon } from '@/utils' import { getMdiIcon } from '#imports'
interface Props { interface Props {
value: Record<string, any> value: Record<string, any>

7
packages/nc-gui/components/smartsheet-column/RollupOptions.vue

@ -1,7 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { UITypes, isSystemColumn, isVirtualCol } from 'nocodb-sdk' import { UITypes, isSystemColumn, isVirtualCol } from 'nocodb-sdk'
import { inject, useMetas, useProject } from '#imports' import { MetaInj, inject, useMetas, useProject } from '#imports'
import { MetaInj } from '~/context'
interface Props { interface Props {
value: Record<string, any> value: Record<string, any>
@ -11,7 +10,7 @@ const props = defineProps<Props>()
const emit = defineEmits(['update:value']) const emit = defineEmits(['update:value'])
const vModel = useVModel(props, 'value', emit) const vModel = useVModel(props, 'value', emit)
const meta = $(inject(MetaInj)!) const meta = $(inject(MetaInj, ref()))
const { setAdditionalValidations, validateInfos, onDataTypeChange } = useColumnCreateStoreOrThrow() const { setAdditionalValidations, validateInfos, onDataTypeChange } = useColumnCreateStoreOrThrow()
@ -51,7 +50,7 @@ const refTables = $computed(() => {
} }
return ( return (
meta.columns meta?.columns
?.filter((c: any) => c.uidt === UITypes.LinkToAnotherRecord && c.colOptions.type !== 'bt' && !c.system) ?.filter((c: any) => c.uidt === UITypes.LinkToAnotherRecord && c.colOptions.type !== 'bt' && !c.system)
.map((c) => ({ .map((c) => ({
col: c.colOptions, col: c.colOptions,

11
packages/nc-gui/components/smartsheet-header/CellIcon.vue

@ -1,14 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import type { ColumnType } from 'nocodb-sdk' import type { ColumnType } from 'nocodb-sdk'
import type { Ref } from 'vue' import type { Ref } from 'vue'
import { toRef } from 'vue' import { ColumnInj, toRef, useColumn } from '#imports'
import { ColumnInj } from '~/context'
import FilePhoneIcon from '~icons/mdi/file-phone' import FilePhoneIcon from '~icons/mdi/file-phone'
import { useColumn } from '#imports'
import KeyIcon from '~icons/mdi/key-variant' import KeyIcon from '~icons/mdi/key-variant'
import JSONIcon from '~icons/mdi/code-json' import JSONIcon from '~icons/mdi/code-json'
import ClockIcon from '~icons/mdi/clock-time-five' import ClockIcon from '~icons/mdi/clock-time-five'
// import FKIcon from '~icons/mdi/link-variant'
import WebIcon from '~icons/mdi/web' import WebIcon from '~icons/mdi/web'
import TextAreaIcon from '~icons/mdi/card-text-outline' import TextAreaIcon from '~icons/mdi/card-text-outline'
import StringIcon from '~icons/mdi/alpha-a-box-outline' import StringIcon from '~icons/mdi/alpha-a-box-outline'
@ -65,11 +62,7 @@ const icon = computed(() => {
return DecimalIcon return DecimalIcon
} else if (additionalColMeta.isPhoneNumber.value) { } else if (additionalColMeta.isPhoneNumber.value) {
return FilePhoneIcon return FilePhoneIcon
} } else if (additionalColMeta.isURL.value) {
// else if(additionalColMeta.isForeignKey) {
// return FKIcon
// }
else if (additionalColMeta.isURL.value) {
return WebIcon return WebIcon
} else if (additionalColMeta.isCurrency.value) { } else if (additionalColMeta.isCurrency.value) {
return CurrencyIcon return CurrencyIcon

2
packages/nc-gui/components/smartsheet-header/Menu.vue

@ -10,7 +10,7 @@ const emit = defineEmits(['edit'])
const column = inject(ColumnInj) const column = inject(ColumnInj)
const meta = inject(MetaInj) const meta = inject(MetaInj, ref())
const isLocked = inject(IsLockedInj) const isLocked = inject(IsLockedInj)

4
packages/nc-gui/components/smartsheet-header/VirtualCell.vue

@ -1,7 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import type { ColumnType, FormulaType, LinkToAnotherRecordType, LookupType, RollupType } from 'nocodb-sdk' import type { ColumnType, FormulaType, LinkToAnotherRecordType, LookupType, RollupType } from 'nocodb-sdk'
import { substituteColumnIdWithAliasInFormula } from 'nocodb-sdk' import { substituteColumnIdWithAliasInFormula } from 'nocodb-sdk'
import { useI18n } from 'vue-i18n'
import { import {
ColumnInj, ColumnInj,
IsFormInj, IsFormInj,
@ -11,6 +10,7 @@ import {
provide, provide,
ref, ref,
toRef, toRef,
useI18n,
useMetas, useMetas,
useUIPermission, useUIPermission,
useVirtualCell, useVirtualCell,
@ -32,7 +32,7 @@ const { metas } = useMetas()
const { isUIAllowed } = useUIPermission() const { isUIAllowed } = useUIPermission()
const meta = inject(MetaInj) const meta = inject(MetaInj, ref())
const isForm = inject(IsFormInj, ref(false)) const isForm = inject(IsFormInj, ref(false))

7
packages/nc-gui/components/smartsheet-header/VirtualCellIcon.vue

@ -2,8 +2,7 @@
import type { ColumnType, LinkToAnotherRecordType, LookupType } from 'nocodb-sdk' import type { ColumnType, LinkToAnotherRecordType, LookupType } from 'nocodb-sdk'
import { RelationTypes, UITypes } from 'nocodb-sdk' import { RelationTypes, UITypes } from 'nocodb-sdk'
import type { Ref } from 'vue' import type { Ref } from 'vue'
import { toRef } from 'vue' import { ColumnInj, toRef } from '#imports'
import { ColumnInj } from '~/context'
import GenericIcon from '~icons/mdi/square-rounded' import GenericIcon from '~icons/mdi/square-rounded'
import HMIcon from '~icons/mdi/table-arrow-right' import HMIcon from '~icons/mdi/table-arrow-right'
import BTIcon from '~icons/mdi/table-arrow-left' import BTIcon from '~icons/mdi/table-arrow-left'
@ -25,9 +24,9 @@ if (column) {
const { isLookup, isBt, isRollup, isMm, isHm } = useVirtualCell(column as Ref<ColumnType>) const { isLookup, isBt, isRollup, isMm, isHm } = useVirtualCell(column as Ref<ColumnType>)
if (isLookup || isBt || isRollup || isMm || isHm) { if (isLookup || isBt || isRollup || isMm || isHm) {
const meta = inject(MetaInj) const meta = inject(MetaInj, ref())
relationColumn = meta?.value.columns?.find((c) => c.id === column.value?.colOptions?.fk_relation_column_id) as ColumnType & { relationColumn = meta.value?.columns?.find((c) => c.id === column.value?.colOptions?.fk_relation_column_id) as ColumnType & {
colOptions: LinkToAnotherRecordType colOptions: LinkToAnotherRecordType
} }
} }

4
packages/nc-gui/components/smartsheet-toolbar/ColumnFilter.vue

@ -33,9 +33,9 @@ const logicalOps = [
{ value: 'or', text: 'OR' }, { value: 'or', text: 'OR' },
] ]
const meta = inject(MetaInj)! const meta = inject(MetaInj, ref())
const activeView = inject(ActiveViewInj)! const activeView = inject(ActiveViewInj, ref())
const reloadDataHook = inject(ReloadViewDataHookInj)! const reloadDataHook = inject(ReloadViewDataHookInj)!

2
packages/nc-gui/components/smartsheet-toolbar/ColumnFilterMenu.vue

@ -5,7 +5,7 @@ import { ActiveViewInj, IsLockedInj, IsPublicInj, computed, inject, ref, useGlob
const isLocked = inject(IsLockedInj, ref(false)) const isLocked = inject(IsLockedInj, ref(false))
const activeView = inject(ActiveViewInj) const activeView = inject(ActiveViewInj, ref())
const isPublic = inject(IsPublicInj, ref(false)) const isPublic = inject(IsPublicInj, ref(false))

10
packages/nc-gui/components/smartsheet-toolbar/ExportSubActions.vue

@ -16,7 +16,7 @@ const { project } = useProject()
const { $api } = useNuxtApp() const { $api } = useNuxtApp()
const meta = inject(MetaInj) const meta = inject(MetaInj, ref())
const selectedView = inject(ActiveViewInj) const selectedView = inject(ActiveViewInj)
@ -36,8 +36,8 @@ const exportFile = async (exportType: ExportTypes) => {
} else { } else {
res = await $api.dbViewRow.export( res = await $api.dbViewRow.export(
'noco', 'noco',
project?.value.title as string, project.value?.title as string,
meta?.value.title as string, meta.value?.title as string,
selectedView?.value.title as string, selectedView?.value.title as string,
exportType, exportType,
{ {
@ -54,10 +54,10 @@ const exportFile = async (exportType: ExportTypes) => {
const { data, headers } = res const { data, headers } = res
if (exportType === ExportTypes.EXCEL) { if (exportType === ExportTypes.EXCEL) {
const workbook = XLSX.read(data, { type: 'base64' }) const workbook = XLSX.read(data, { type: 'base64' })
XLSX.writeFile(workbook, `${meta?.value.title}_exported_${c++}.xlsx`) XLSX.writeFile(workbook, `${meta.value?.title}_exported_${c++}.xlsx`)
} else if (exportType === ExportTypes.CSV) { } else if (exportType === ExportTypes.CSV) {
const blob = new Blob([data], { type: 'text/plain;charset=utf-8' }) const blob = new Blob([data], { type: 'text/plain;charset=utf-8' })
FileSaver.saveAs(blob, `${meta?.value.title}_exported_${c++}.csv`) FileSaver.saveAs(blob, `${meta.value?.title}_exported_${c++}.csv`)
} }
offset = +headers['nc-export-offset'] offset = +headers['nc-export-offset']
if (offset > -1) { if (offset > -1) {

31
packages/nc-gui/components/smartsheet-toolbar/FieldListAutoCompleteDropdown.vue

@ -2,8 +2,7 @@
import type { SelectProps } from 'ant-design-vue' import type { SelectProps } from 'ant-design-vue'
import type { ColumnType, LinkToAnotherRecordType } from 'nocodb-sdk' import type { ColumnType, LinkToAnotherRecordType } from 'nocodb-sdk'
import { RelationTypes, UITypes, isVirtualCol } from 'nocodb-sdk' import { RelationTypes, UITypes, isVirtualCol } from 'nocodb-sdk'
import { computed } from 'vue' import { MetaInj, computed } from '#imports'
import { MetaInj } from '~/context'
import VirtualCellIcon from '~/components/smartsheet-header/VirtualCellIcon.vue' import VirtualCellIcon from '~/components/smartsheet-header/VirtualCellIcon.vue'
import CellIcon from '~/components/smartsheet-header/CellIcon.vue' import CellIcon from '~/components/smartsheet-header/CellIcon.vue'
@ -16,39 +15,13 @@ const { modelValue, isSort } = defineProps<Props>()
const emit = defineEmits(['update:modelValue']) const emit = defineEmits(['update:modelValue'])
const meta = inject(MetaInj) const meta = inject(MetaInj, ref())
const localValue = computed({ const localValue = computed({
get: () => modelValue, get: () => modelValue,
set: (val) => emit('update:modelValue', val), set: (val) => emit('update:modelValue', val),
}) })
/* export default {
name: 'FieldListAutoCompleteDropdown',
props: {
columns: Array,
value: String,
},
computed: {
localValue: {
set(v) {
this.$emit('input', v)
},
get() {
return this.value
},
},
},
mounted() {
const autocompleteInput = this.$refs.field.$refs.input
autocompleteInput.addEventListener('focus', this.onFocus, true)
},
methods: {
onFocus(e) {
this.$refs.field.isMenuActive = true // open item list
},
},
} */
const options = computed<SelectProps['options']>(() => const options = computed<SelectProps['options']>(() =>
meta?.value?.columns meta?.value?.columns
?.filter((c: ColumnType) => { ?.filter((c: ColumnType) => {

4
packages/nc-gui/components/smartsheet-toolbar/FieldsMenu.vue

@ -20,9 +20,9 @@ import {
import CellIcon from '~/components/smartsheet-header/CellIcon.vue' import CellIcon from '~/components/smartsheet-header/CellIcon.vue'
import VirtualCellIcon from '~/components/smartsheet-header/VirtualCellIcon.vue' import VirtualCellIcon from '~/components/smartsheet-header/VirtualCellIcon.vue'
const meta = inject(MetaInj)! const meta = inject(MetaInj, ref())
const activeView = inject(ActiveViewInj)! const activeView = inject(ActiveViewInj, ref())
const reloadDataHook = inject(ReloadViewDataHookInj)! const reloadDataHook = inject(ReloadViewDataHookInj)!

12
packages/nc-gui/components/smartsheet-toolbar/MoreActions.vue

@ -30,11 +30,11 @@ const { project } = useProject()
const { $api } = useNuxtApp() const { $api } = useNuxtApp()
const meta = inject(MetaInj) const meta = inject(MetaInj, ref())
const fields = inject(FieldsInj, ref([])) const fields = inject(FieldsInj, ref([]))
const selectedView = inject(ActiveViewInj) const selectedView = inject(ActiveViewInj, ref())
const { sorts, nestedFilters } = useSmartsheetStoreOrThrow() const { sorts, nestedFilters } = useSmartsheetStoreOrThrow()
@ -61,8 +61,8 @@ const exportFile = async (exportType: ExportTypes) => {
res = await $api.dbViewRow.export( res = await $api.dbViewRow.export(
'noco', 'noco',
project?.value.title as string, project?.value.title as string,
meta?.value.title as string, meta.value?.title as string,
selectedView?.value.title as string, selectedView.value?.title as string,
exportType, exportType,
{ {
responseType, responseType,
@ -78,10 +78,10 @@ const exportFile = async (exportType: ExportTypes) => {
const { data, headers } = res const { data, headers } = res
if (exportType === ExportTypes.EXCEL) { if (exportType === ExportTypes.EXCEL) {
const workbook = XLSX.read(data, { type: 'base64' }) const workbook = XLSX.read(data, { type: 'base64' })
XLSX.writeFile(workbook, `${meta?.value.title}_exported_${c++}.xlsx`) XLSX.writeFile(workbook, `${meta.value?.title}_exported_${c++}.xlsx`)
} else if (exportType === ExportTypes.CSV) { } else if (exportType === ExportTypes.CSV) {
const blob = new Blob([data], { type: 'text/plain;charset=utf-8' }) const blob = new Blob([data], { type: 'text/plain;charset=utf-8' })
FileSaver.saveAs(blob, `${meta?.value.title}_exported_${c++}.csv`) FileSaver.saveAs(blob, `${meta.value?.title}_exported_${c++}.csv`)
} }
offset = +headers['nc-export-offset'] offset = +headers['nc-export-offset']
if (offset > -1) { if (offset > -1) {

14
packages/nc-gui/components/smartsheet-toolbar/ShareView.vue

@ -1,9 +1,15 @@
<script lang="ts" setup> <script lang="ts" setup>
import { useClipboard } from '@vueuse/core'
import { ViewTypes } from 'nocodb-sdk' import { ViewTypes } from 'nocodb-sdk'
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import { useI18n } from 'vue-i18n' import {
import { computed, extractSdkResponseErrorMsg, useNuxtApp, useProject, useSmartsheetStoreOrThrow } from '#imports' computed,
extractSdkResponseErrorMsg,
useClipboard,
useI18n,
useNuxtApp,
useProject,
useSmartsheetStoreOrThrow,
} from '#imports'
import MdiOpenInNewIcon from '~icons/mdi/open-in-new' import MdiOpenInNewIcon from '~icons/mdi/open-in-new'
import MdiCopyIcon from '~icons/mdi/content-copy' import MdiCopyIcon from '~icons/mdi/content-copy'
@ -39,7 +45,7 @@ const allowCSVDownload = computed({
}) })
const genShareLink = async () => { const genShareLink = async () => {
shared.value = await $api.dbViewShare.create(view.value.id as string) shared.value = await $api.dbViewShare.create(view.value?.id as string)
shared.value.meta = shared.value.meta =
shared.value.meta && typeof shared.value.meta === 'string' ? JSON.parse(shared.value.meta) : shared.value.meta shared.value.meta && typeof shared.value.meta === 'string' ? JSON.parse(shared.value.meta) : shared.value.meta
passwordProtected.value = shared.value.password !== null && shared.value.password !== '' passwordProtected.value = shared.value.password !== null && shared.value.password !== ''

9
packages/nc-gui/components/smartsheet-toolbar/SharedViewList.vue

@ -1,17 +1,12 @@
<script lang="ts" setup> <script lang="ts" setup>
import { useClipboard } from '@vueuse/core'
import { ViewTypes } from 'nocodb-sdk' import { ViewTypes } from 'nocodb-sdk'
import { Empty, message } from 'ant-design-vue' import { Empty, message } from 'ant-design-vue'
import { useI18n } from 'vue-i18n' import { extractSdkResponseErrorMsg, onMounted, useClipboard, useI18n, useSmartsheetStoreOrThrow } from '#imports'
import { onMounted, useSmartsheetStoreOrThrow } from '#imports'
import { extractSdkResponseErrorMsg } from '~/utils/errorUtils'
import MdiVisibilityOnIcon from '~icons/mdi/visibility' import MdiVisibilityOnIcon from '~icons/mdi/visibility'
import MdiVisibilityOffIcon from '~icons/mdi/visibility-off' import MdiVisibilityOffIcon from '~icons/mdi/visibility-off'
import MdiCopyIcon from '~icons/mdi/content-copy' import MdiCopyIcon from '~icons/mdi/content-copy'
import MdiDeleteIcon from '~icons/mdi/delete-outline' import MdiDeleteIcon from '~icons/mdi/delete-outline'
const { t } = useI18n()
interface SharedViewType { interface SharedViewType {
password: string password: string
title: string title: string
@ -21,6 +16,8 @@ interface SharedViewType {
showPassword?: boolean showPassword?: boolean
} }
const { t } = useI18n()
const { $api, meta } = useSmartsheetStoreOrThrow() const { $api, meta } = useSmartsheetStoreOrThrow()
const { copy } = useClipboard() const { copy } = useClipboard()

8
packages/nc-gui/components/smartsheet-toolbar/SortListMenu.vue

@ -14,14 +14,14 @@ import {
watch, watch,
} from '#imports' } from '#imports'
const meta = inject(MetaInj) const meta = inject(MetaInj, ref())
const view = inject(ActiveViewInj) const view = inject(ActiveViewInj, ref())
const isLocked = inject(IsLockedInj, ref(false)) const isLocked = inject(IsLockedInj, ref(false))
const reloadDataHook = inject(ReloadViewDataHookInj) const reloadDataHook = inject(ReloadViewDataHookInj)
const { sorts, saveOrUpdate, loadSorts, addSort, deleteSort } = useViewSorts(view, () => reloadDataHook?.trigger()) const { sorts, saveOrUpdate, loadSorts, addSort, deleteSort } = useViewSorts(view, () => reloadDataHook?.trigger())
const columns = computed(() => meta?.value?.columns || []) const columns = computed(() => meta.value?.columns || [])
const columnByID = computed(() => const columnByID = computed(() =>
columns.value.reduce((obj, col) => { columns.value.reduce((obj, col) => {
@ -32,7 +32,7 @@ const columnByID = computed(() =>
) )
watch( watch(
() => view!.value?.id, () => view.value?.id,
() => { () => {
loadSorts() loadSorts()
}, },

4
packages/nc-gui/components/smartsheet-toolbar/ViewActions.vue

@ -1,6 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import { useI18n } from 'vue-i18n'
import { import {
ActiveViewInj, ActiveViewInj,
IsLockedInj, IsLockedInj,
@ -8,13 +7,14 @@ import {
extractSdkResponseErrorMsg, extractSdkResponseErrorMsg,
inject, inject,
ref, ref,
useI18n,
useNuxtApp, useNuxtApp,
useProject, useProject,
useSmartsheetStoreOrThrow, useSmartsheetStoreOrThrow,
useUIPermission, useUIPermission,
viewIcons,
} from '#imports' } from '#imports'
import { LockType } from '~/lib' import { LockType } from '~/lib'
import { viewIcons } from '~/utils'
import MdiLockOutlineIcon from '~icons/mdi/lock-outline' import MdiLockOutlineIcon from '~icons/mdi/lock-outline'
import MdiAccountIcon from '~icons/mdi/account' import MdiAccountIcon from '~icons/mdi/account'
import MdiAccountGroupIcon from '~icons/mdi/account-group' import MdiAccountGroupIcon from '~icons/mdi/account-group'

4
packages/nc-gui/components/smartsheet/ApiSnippet.vue

@ -28,9 +28,9 @@ const { project } = $(useProject())
const { appInfo, token } = $(useGlobal()) const { appInfo, token } = $(useGlobal())
const meta = $(inject(MetaInj)!) const meta = $(inject(MetaInj, ref()))
const view = $(inject(ActiveViewInj)!) const view = $(inject(ActiveViewInj, ref()))
const { xWhere } = useSmartsheetStoreOrThrow() const { xWhere } = useSmartsheetStoreOrThrow()

12
packages/nc-gui/components/smartsheet/Form.vue

@ -2,8 +2,6 @@
import Draggable from 'vuedraggable' import Draggable from 'vuedraggable'
import { RelationTypes, UITypes, getSystemColumns, isVirtualCol } from 'nocodb-sdk' import { RelationTypes, UITypes, getSystemColumns, isVirtualCol } from 'nocodb-sdk'
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import { useI18n } from 'vue-i18n'
import type { Permission } from '~/composables/useUIPermission/rolePermissions'
import { import {
ActiveViewInj, ActiveViewInj,
IsFormInj, IsFormInj,
@ -18,12 +16,14 @@ import {
ref, ref,
useDebounceFn, useDebounceFn,
useGlobal, useGlobal,
useI18n,
useNuxtApp, useNuxtApp,
useUIPermission, useUIPermission,
useViewColumns, useViewColumns,
useViewData, useViewData,
watch, watch,
} from '#imports' } from '#imports'
import type { Permission } from '~/composables/useUIPermission/rolePermissions'
provide(IsFormInj, ref(true)) provide(IsFormInj, ref(true))
provide(IsGalleryInj, ref(false)) provide(IsGalleryInj, ref(false))
@ -45,11 +45,11 @@ const secondsRemain = ref(0)
const isEditable = isUIAllowed('editFormView' as Permission) const isEditable = isUIAllowed('editFormView' as Permission)
const meta = inject(MetaInj)! const meta = inject(MetaInj, ref())
const view = inject(ActiveViewInj) const view = inject(ActiveViewInj, ref())
const { loadFormView, insertRow, formColumnData, formViewData, updateFormView } = useViewData(meta, view as any) const { loadFormView, insertRow, formColumnData, formViewData, updateFormView } = useViewData(meta, view)
const reloadEventHook = createEventHook<void>() const reloadEventHook = createEventHook<void>()
provide(ReloadViewDataHookInj, reloadEventHook) provide(ReloadViewDataHookInj, reloadEventHook)
@ -308,7 +308,7 @@ function isRequired(_columnObj: Record<string, any>, required = false) {
function updateEmail() { function updateEmail() {
try { try {
const data = JSON.parse(formViewData.value?.email) || {} const data = formViewData.value?.email ? JSON.parse(formViewData.value?.email) : {}
data[state.user.value?.email as string] = emailMe.value data[state.user.value?.email as string] = emailMe.value
formViewData.value!.email = JSON.stringify(data) formViewData.value!.email = JSON.stringify(data)
checkSMTPStatus() checkSMTPStatus()

6
packages/nc-gui/components/smartsheet/Gallery.vue

@ -24,8 +24,8 @@ interface Attachment {
url: string url: string
} }
const meta = inject(MetaInj)! const meta = inject(MetaInj, ref())
const view = inject(ActiveViewInj)! const view = inject(ActiveViewInj, ref())
const reloadViewDataHook = inject(ReloadViewDataHookInj) const reloadViewDataHook = inject(ReloadViewDataHookInj)
const openNewRecordFormHook = inject(OpenNewRecordFormHookInj, createEventHook()) const openNewRecordFormHook = inject(OpenNewRecordFormHookInj, createEventHook())
@ -58,7 +58,7 @@ const fieldsWithoutCover = computed(() => fields.value.filter((f) => f.id !== ga
const coverImageColumn: any = $( const coverImageColumn: any = $(
computed(() => computed(() =>
meta?.value.columnsById meta.value?.columnsById
? meta.value.columnsById[galleryData.value?.fk_cover_image_col_id as keyof typeof meta.value.columnsById] ? meta.value.columnsById[galleryData.value?.fk_cover_image_col_id as keyof typeof meta.value.columnsById]
: {}, : {},
), ),

8
packages/nc-gui/components/smartsheet/Grid.vue

@ -36,9 +36,9 @@ import { NavigateDir } from '~/lib'
const { t } = useI18n() const { t } = useI18n()
const meta = inject(MetaInj)! const meta = inject(MetaInj, ref())
const view = inject(ActiveViewInj)! const view = inject(ActiveViewInj, ref())
// keep a root fields variable and will get modified from // keep a root fields variable and will get modified from
// fields menu and get used in grid and gallery // fields menu and get used in grid and gallery
@ -136,7 +136,7 @@ const selectCell = (row: number, col: number) => {
} }
watch( watch(
() => view?.value.id, () => view.value?.id,
async (next, old) => { async (next, old) => {
if (next && old && next !== old) { if (next && old && next !== old) {
await loadData() await loadData()
@ -359,7 +359,7 @@ onBeforeUnmount(async () => {
/** if existing row check updated cell and invoke update method */ /** if existing row check updated cell and invoke update method */
if (currentRow.rowMeta.changed) { if (currentRow.rowMeta.changed) {
currentRow.rowMeta.changed = false currentRow.rowMeta.changed = false
for (const field of meta?.value.columns ?? []) { for (const field of meta.value?.columns ?? []) {
if (isVirtualCol(field)) continue if (isVirtualCol(field)) continue
if (currentRow.row[field.title!] !== currentRow.oldRow[field.title!]) { if (currentRow.row[field.title!] !== currentRow.oldRow[field.title!]) {
await updateOrSaveRow(currentRow, field.title!) await updateOrSaveRow(currentRow, field.title!)

2
packages/nc-gui/components/smartsheet/sidebar/toolbar/DeleteTable.vue

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { MetaInj, inject, useTable } from '#imports' import { MetaInj, inject, useTable } from '#imports'
const meta = inject(MetaInj)! const meta = inject(MetaInj, ref())
const { deleteTable } = useTable() const { deleteTable } = useTable()

6
packages/nc-gui/components/tabs/Auth.vue

@ -1,8 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { useI18n } from 'vue-i18n'
import UserManagement from './auth/UserManagement.vue' import UserManagement from './auth/UserManagement.vue'
import ApiTokenManagement from './auth/ApiTokenManagement.vue' import ApiTokenManagement from './auth/ApiTokenManagement.vue'
import { useUIPermission } from '#imports' import { useI18n, useUIPermission } from '#imports'
interface Tab { interface Tab {
title: string title: string
@ -10,6 +9,7 @@ interface Tab {
body: any body: any
isUIAllowed: boolean isUIAllowed: boolean
} }
const { t } = useI18n() const { t } = useI18n()
const { isUIAllowed } = useUIPermission() const { isUIAllowed } = useUIPermission()
@ -29,8 +29,6 @@ const tabsInfo: Tab[] = [
}, },
] ]
// const firstKeyOfObject = (obj: object) => Object.keys(obj)[0]
const selectedTabKey = $ref(0) const selectedTabKey = $ref(0)
const selectedTab = $computed(() => tabsInfo[selectedTabKey]) const selectedTab = $computed(() => tabsInfo[selectedTabKey])
</script> </script>

4
packages/nc-gui/components/tabs/Smartsheet.vue

@ -1,6 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import type { ColumnType, TableType } from 'nocodb-sdk' import type { ColumnType, TableType } from 'nocodb-sdk'
import type { Ref } from 'vue'
import SmartsheetGrid from '../smartsheet/Grid.vue' import SmartsheetGrid from '../smartsheet/Grid.vue'
import { import {
ActiveViewInj, ActiveViewInj,
@ -18,7 +17,6 @@ import {
useProvideSmartsheetStore, useProvideSmartsheetStore,
watch, watch,
} from '#imports' } from '#imports'
import type { TabItem } from '~/composables' import type { TabItem } from '~/composables'
const { activeTab } = defineProps<{ const { activeTab } = defineProps<{
@ -39,7 +37,7 @@ const meta = computed<TableType>(() => metas.value?.[activeTab?.id as string])
const reloadEventHook = createEventHook<void>() const reloadEventHook = createEventHook<void>()
const openNewRecordFormHook = createEventHook<void>() const openNewRecordFormHook = createEventHook<void>()
const { isGallery, isGrid, isForm, isLocked } = useProvideSmartsheetStore(activeView as Ref<TableType>, meta) const { isGallery, isGrid, isForm, isLocked } = useProvideSmartsheetStore(activeView, meta)
// provide the sidebar injection state // provide the sidebar injection state
provideSidebar({ storageKey: 'nc-right-sidebar', isOpen: true }) provideSidebar({ storageKey: 'nc-right-sidebar', isOpen: true })

7
packages/nc-gui/components/tabs/auth/ApiTokenManagement.vue

@ -1,8 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { ApiTokenType } from 'nocodb-sdk' import type { ApiTokenType } from 'nocodb-sdk'
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import { useClipboard } from '@vueuse/core' import { extractSdkResponseErrorMsg, useClipboard, useI18n } from '#imports'
import { useI18n } from 'vue-i18n'
import KebabIcon from '~icons/ic/baseline-more-vert' import KebabIcon from '~icons/ic/baseline-more-vert'
import MdiPlusIcon from '~icons/mdi/plus' import MdiPlusIcon from '~icons/mdi/plus'
import CloseIcon from '~icons/material-symbols/close-rounded' import CloseIcon from '~icons/material-symbols/close-rounded'
@ -11,13 +10,13 @@ import VisibilityOpenIcon from '~icons/material-symbols/visibility'
import VisibilityCloseIcon from '~icons/material-symbols/visibility-off' import VisibilityCloseIcon from '~icons/material-symbols/visibility-off'
import MdiDeleteOutlineIcon from '~icons/mdi/delete-outline' import MdiDeleteOutlineIcon from '~icons/mdi/delete-outline'
import MdiContentCopyIcon from '~icons/mdi/content-copy' import MdiContentCopyIcon from '~icons/mdi/content-copy'
import { extractSdkResponseErrorMsg } from '~/utils'
const { t } = useI18n()
interface ApiToken extends ApiTokenType { interface ApiToken extends ApiTokenType {
show?: boolean show?: boolean
} }
const { t } = useI18n()
const { $api, $e } = useNuxtApp() const { $api, $e } = useNuxtApp()
const { project } = $(useProject()) const { project } = $(useProject())

4
packages/nc-gui/components/tabs/auth/UserManagement.vue

@ -1,6 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import { useI18n } from 'vue-i18n'
import type { RequestParams } from 'nocodb-sdk' import type { RequestParams } from 'nocodb-sdk'
import UsersModal from './user-management/UsersModal.vue' import UsersModal from './user-management/UsersModal.vue'
import FeedbackForm from './user-management/FeedbackForm.vue' import FeedbackForm from './user-management/FeedbackForm.vue'
@ -11,6 +10,7 @@ import {
useApi, useApi,
useClipboard, useClipboard,
useDashboard, useDashboard,
useI18n,
useNuxtApp, useNuxtApp,
useProject, useProject,
useUIPermission, useUIPermission,
@ -128,7 +128,7 @@ const resendInvite = async (user: User) => {
if (!project.value?.id) return if (!project.value?.id) return
try { try {
await api.auth.projectUserResendInvite(project.value.id, user.id, null) await api.auth.projectUserResendInvite(project.value.id, user.id)
// Invite email sent successfully // Invite email sent successfully
message.success(t('msg.success.inviteEmailSent')) message.success(t('msg.success.inviteEmailSent'))

6
packages/nc-gui/components/tabs/auth/user-management/FeedbackForm.vue

@ -1,14 +1,14 @@
<script setup lang="ts"> <script setup lang="ts">
import CloseIcon from '~icons/material-symbols/close-rounded'
const { feedbackForm } = useGlobal() const { feedbackForm } = useGlobal()
const showForm = ref(false) const showForm = ref(false)
setTimeout(() => (showForm.value = true), 60000) setTimeout(() => (showForm.value = true), 60000)
</script> </script>
<template> <template>
<div v-if="showForm && feedbackForm && !feedbackForm.isHidden" class="nc-feedback-form-wrapper mt-6"> <div v-if="showForm && feedbackForm && !feedbackForm.isHidden" class="nc-feedback-form-wrapper mt-6">
<CloseIcon class="nc-close-icon" @click="feedbackForm.isHidden = true" /> <MaterialSymbolsCloseRounded class="nc-close-icon" @click="feedbackForm.isHidden = true" />
<iframe :src="feedbackForm.url" width="100%" height="500" frameborder="0" marginheight="0" marginwidth="0">Loading </iframe> <iframe :src="feedbackForm.url" width="100%" height="500" frameborder="0" marginheight="0" marginwidth="0">Loading </iframe>
</div> </div>

8
packages/nc-gui/components/tabs/auth/user-management/ShareBase.vue

@ -1,10 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import { useI18n } from 'vue-i18n' import { extractSdkResponseErrorMsg, onMounted, useClipboard, useI18n, useNuxtApp, useProject } from '#imports'
import { onMounted, useClipboard, useNuxtApp, useProject } from '#imports'
import { extractSdkResponseErrorMsg } from '~/utils'
const { t } = useI18n()
interface ShareBase { interface ShareBase {
uuid?: string uuid?: string
@ -17,6 +13,8 @@ enum ShareBaseRole {
Viewer = 'viewer', Viewer = 'viewer',
} }
const { t } = useI18n()
const { dashboardUrl } = $(useDashboard()) const { dashboardUrl } = $(useDashboard())
const { $api, $e } = useNuxtApp() const { $api, $e } = useNuxtApp()

18
packages/nc-gui/components/tabs/auth/user-management/UsersModal.vue

@ -1,28 +1,23 @@
<script setup lang="ts"> <script setup lang="ts">
import { Form, message } from 'ant-design-vue' import { Form, message } from 'ant-design-vue'
import { useI18n } from 'vue-i18n'
import ShareBase from './ShareBase.vue' import ShareBase from './ShareBase.vue'
import { import {
computed, computed,
extractSdkResponseErrorMsg, extractSdkResponseErrorMsg,
isEmail, isEmail,
onMounted, onMounted,
projectRoleTagColors,
projectRoles, projectRoles,
ref, ref,
useClipboard, useClipboard,
useDashboard, useDashboard,
useI18n,
useNuxtApp, useNuxtApp,
useProject, useProject,
} from '#imports' } from '#imports'
import type { User } from '~/lib' import type { User } from '~/lib'
import { ProjectRole } from '~/lib' import { ProjectRole } from '~/lib'
const { show, selectedUser } = defineProps<Props>()
const emit = defineEmits(['closed', 'reload'])
const { t } = useI18n()
interface Props { interface Props {
show: boolean show: boolean
selectedUser?: User selectedUser?: User
@ -34,6 +29,12 @@ interface Users {
invitationToken?: string invitationToken?: string
} }
const { show, selectedUser } = defineProps<Props>()
const emit = defineEmits(['closed', 'reload'])
const { t } = useI18n()
const { project } = useProject() const { project } = useProject()
const { $api, $e } = useNuxtApp() const { $api, $e } = useNuxtApp()
const { copy } = useClipboard() const { copy } = useClipboard()
@ -69,7 +70,8 @@ const { validateInfos } = useForm(usersData, validators)
onMounted(() => { onMounted(() => {
if (!usersData.emails && selectedUser?.email) { if (!usersData.emails && selectedUser?.email) {
usersData.emails = selectedUser.email usersData.emails = selectedUser.email
usersData.role = selectedUser.roles // todo: types not matching, probably a bug here?
usersData.role = selectedUser.roles as any
} }
}) })

12
packages/nc-gui/components/template/Editor.vue

@ -2,7 +2,6 @@
import type { ColumnType, TableType } from 'nocodb-sdk' import type { ColumnType, TableType } from 'nocodb-sdk'
import { UITypes, isVirtualCol } from 'nocodb-sdk' import { UITypes, isVirtualCol } from 'nocodb-sdk'
import { Empty, Form, message } from 'ant-design-vue' import { Empty, Form, message } from 'ant-design-vue'
import { useI18n } from 'vue-i18n'
import { srcDestMappingColumns, tableColumns } from './utils' import { srcDestMappingColumns, tableColumns } from './utils'
import { import {
MetaInj, MetaInj,
@ -17,6 +16,7 @@ import {
onMounted, onMounted,
reactive, reactive,
ref, ref,
useI18n,
useNuxtApp, useNuxtApp,
useProject, useProject,
useTabs, useTabs,
@ -44,7 +44,7 @@ interface Option {
value: string value: string
} }
const meta = inject(MetaInj, ref({} as TableType)) const meta = inject(MetaInj, ref())
const columns = computed(() => meta.value?.columns || []) const columns = computed(() => meta.value?.columns || [])
@ -255,7 +255,7 @@ function fieldsValidation(record: Record<string, any>) {
return true return true
} }
const tableName = meta?.value.title || '' const tableName = meta.value?.title || ''
if (!record.destCn) { if (!record.destCn) {
message.error(`${t('msg.error.columnDescriptionNotFound')} ${record.srcCn}`) message.error(`${t('msg.error.columnDescriptionNotFound')} ${record.srcCn}`)
@ -340,7 +340,7 @@ async function importTemplate() {
try { try {
isImporting.value = true isImporting.value = true
const tableName = meta.value.title const tableName = meta.value?.title
// only one file is allowed currently // only one file is allowed currently
const data = importData[Object.keys(importData)[0]] const data = importData[Object.keys(importData)[0]]
@ -381,7 +381,7 @@ async function importTemplate() {
}, {}), }, {}),
) )
await $api.dbTableRow.bulkCreate('noco', projectName, tableName, batchData) await $api.dbTableRow.bulkCreate('noco', projectName, tableName!, batchData)
importingTip.value = `Importing data to ${projectName}: ${progress}/${total} records` importingTip.value = `Importing data to ${projectName}: ${progress}/${total} records`
@ -440,7 +440,7 @@ async function importTemplate() {
table_name: table.ref_table_name, table_name: table.ref_table_name,
// leave title empty to get a generated one based on ref_table_name // leave title empty to get a generated one based on ref_table_name
title: '', title: '',
columns: table.columns, columns: table.columns || [],
}) })
table.title = tableMeta.title table.title = tableMeta.title

4
packages/nc-gui/components/virtual-cell/Formula.vue

@ -1,8 +1,10 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { ColumnType } from 'nocodb-sdk'
import type { Ref } from 'vue'
import { CellValueInj, ColumnInj, computed, handleTZ, inject, ref, replaceUrlsWithLink, useProject } from '#imports' import { CellValueInj, ColumnInj, computed, handleTZ, inject, ref, replaceUrlsWithLink, useProject } from '#imports'
// todo: column type doesn't have required property `error` - throws in typecheck // todo: column type doesn't have required property `error` - throws in typecheck
const column: any = inject(ColumnInj) const column = inject(ColumnInj) as Ref<ColumnType & { colOptions: { error: any } }>
const value = inject(CellValueInj) const value = inject(CellValueInj)

16
packages/nc-gui/components/virtual-cell/HasMany.vue

@ -82,6 +82,11 @@ const unlinkRef = async (rec: Record<string, any>) => {
await unlink(rec) await unlink(rec)
} }
} }
const onAttachRecord = () => {
childListDlg.value = false
listItemsDlg.value = true
}
</script> </script>
<template> <template>
@ -110,16 +115,7 @@ const unlinkRef = async (rec: Record<string, any>) => {
<ListItems v-model="listItemsDlg" /> <ListItems v-model="listItemsDlg" />
<ListChildItems <ListChildItems v-model="childListDlg" :cell-value="localCellValue" @attach-record="onAttachRecord" />
v-model="childListDlg"
:cell-value="localCellValue"
@attach-record="
() => {
childListDlg = false
listItemsDlg = true
}
"
/>
</div> </div>
</template> </template>

4
packages/nc-gui/components/virtual-cell/Lookup.vue

@ -21,13 +21,13 @@ provide(ReadonlyInj, true)
const column = inject(ColumnInj)! as Ref<ColumnType & { colOptions: LookupType }> const column = inject(ColumnInj)! as Ref<ColumnType & { colOptions: LookupType }>
const meta = inject(MetaInj) const meta = inject(MetaInj, ref())
const value = inject(CellValueInj) const value = inject(CellValueInj)
const arrValue = computed(() => (Array.isArray(value?.value) ? value?.value : [value?.value]) ?? []) const arrValue = computed(() => (Array.isArray(value?.value) ? value?.value : [value?.value]) ?? [])
const relationColumn = meta?.value.columns?.find((c) => c.id === column.value.colOptions?.fk_relation_column_id) as ColumnType & { const relationColumn = meta.value?.columns?.find((c) => c.id === column.value.colOptions?.fk_relation_column_id) as ColumnType & {
colOptions: LinkToAnotherRecordType colOptions: LinkToAnotherRecordType
} }

16
packages/nc-gui/components/virtual-cell/ManyToMany.vue

@ -81,6 +81,11 @@ const unlinkRef = async (rec: Record<string, any>) => {
await unlink(rec) await unlink(rec)
} }
} }
const onAttachRecord = () => {
childListDlg.value = false
listItemsDlg.value = true
}
</script> </script>
<template> <template>
@ -110,16 +115,7 @@ const unlinkRef = async (rec: Record<string, any>) => {
<ListItems v-model="listItemsDlg" /> <ListItems v-model="listItemsDlg" />
<ListChildItems <ListChildItems v-model="childListDlg" :cell-value="localCellValue" @attach-record="onAttachRecord" />
v-model="childListDlg"
:cell-value="localCellValue"
@attach-record="
() => {
childListDlg = false
listItemsDlg = true
}
"
/>
</div> </div>
</template> </template>

6
packages/nc-gui/components/webhook/ChannelMultiSelect.vue

@ -59,8 +59,8 @@ onMounted(() => {
<template> <template>
<a-select v-model:value="localChannelValues" mode="multiple" :placeholder="placeholder" max-tag-count="responsive"> <a-select v-model:value="localChannelValues" mode="multiple" :placeholder="placeholder" max-tag-count="responsive">
<a-select-option v-for="channel of availableChannelWithIdxList" :key="channel.idx" :value="channel.idx">{{ <a-select-option v-for="channel of availableChannelWithIdxList" :key="channel.idx" :value="channel.idx">
channel.channel {{ channel.channel }}
}}</a-select-option> </a-select-option>
</a-select> </a-select>
</template> </template>

19
packages/nc-gui/components/webhook/Editor.vue

@ -1,8 +1,17 @@
<script setup lang="ts"> <script setup lang="ts">
import { Form, message } from 'ant-design-vue' import { Form, message } from 'ant-design-vue'
import { useI18n } from 'vue-i18n'
import type { Ref } from 'vue' import type { Ref } from 'vue'
import { MetaInj, extractSdkResponseErrorMsg, fieldRequiredValidator, inject, reactive, useApi, useNuxtApp } from '#imports' import type { AuditType } from 'nocodb-sdk'
import {
MetaInj,
extractSdkResponseErrorMsg,
fieldRequiredValidator,
inject,
reactive,
useApi,
useI18n,
useNuxtApp,
} from '#imports'
const emit = defineEmits(['backToList', 'editOrAdd']) const emit = defineEmits(['backToList', 'editOrAdd'])
@ -12,7 +21,7 @@ const { $e } = useNuxtApp()
const { api, isLoading: loading } = useApi() const { api, isLoading: loading } = useApi()
const meta = inject(MetaInj) const meta = inject(MetaInj, ref())
const useForm = Form.useForm const useForm = Form.useForm
@ -339,13 +348,13 @@ async function saveHooks() {
}, },
}) })
} else { } else {
res = await api.dbTableWebhook.create(meta!.value.id!, { res = await api.dbTableWebhook.create(meta.value!.id!, {
...hook, ...hook,
notification: { notification: {
...hook.notification, ...hook.notification,
payload: hook.notification.payload, payload: hook.notification.payload,
}, },
} as any) } as AuditType)
} }
if (!hook.id && res) { if (!hook.id && res) {

9
packages/nc-gui/components/webhook/List.vue

@ -1,9 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import { useI18n } from 'vue-i18n' import { MetaInj, extractSdkResponseErrorMsg, inject, onMounted, ref, useI18n, useNuxtApp } from '#imports'
import { MetaInj } from '~/context'
import { inject, onMounted, ref, useNuxtApp } from '#imports'
import { extractSdkResponseErrorMsg } from '~/utils'
const emit = defineEmits(['edit', 'add']) const emit = defineEmits(['edit', 'add'])
@ -13,11 +10,11 @@ const { $api, $e } = useNuxtApp()
const hooks = ref<Record<string, any>[]>([]) const hooks = ref<Record<string, any>[]>([])
const meta = inject(MetaInj)! const meta = inject(MetaInj, ref())
async function loadHooksList() { async function loadHooksList() {
try { try {
const hookList = (await $api.dbTableWebhook.list(meta?.value.id as string)).list as Record<string, any>[] const hookList = (await $api.dbTableWebhook.list(meta.value?.id as string)).list as Record<string, any>[]
hooks.value = hookList.map((hook) => { hooks.value = hookList.map((hook) => {
hook.notification = hook.notification && JSON.parse(hook.notification) hook.notification = hook.notification && JSON.parse(hook.notification)
return hook return hook

17
packages/nc-gui/components/webhook/Test.vue

@ -1,21 +1,18 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted } from '@vue/runtime-core'
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import { useI18n } from 'vue-i18n' import { MetaInj, extractSdkResponseErrorMsg, onMounted, useI18n } from '#imports'
import { MetaInj } from '~/context'
import { extractSdkResponseErrorMsg } from '~/utils'
const { hook } = defineProps<Props>()
const { t } = useI18n()
interface Props { interface Props {
hook: Record<string, any> hook: Record<string, any>
} }
const { hook } = defineProps<Props>()
const { t } = useI18n()
const { $api } = useNuxtApp() const { $api } = useNuxtApp()
const meta = inject(MetaInj) const meta = inject(MetaInj, ref())
const sampleData = ref({ const sampleData = ref({
data: {}, data: {},
@ -37,7 +34,7 @@ async function loadSampleData() {
async function testWebhook() { async function testWebhook() {
try { try {
await $api.dbTableWebhook.test(meta?.value.id as string, { await $api.dbTableWebhook.test(meta.value?.id as string, {
hook, hook,
payload: sampleData.value, payload: sampleData.value,
}) })

28
packages/nc-gui/composables/useColumnCreateStore.ts

@ -12,14 +12,14 @@ const useForm = Form.useForm
const columnToValidate = [UITypes.Email, UITypes.URL, UITypes.PhoneNumber] const columnToValidate = [UITypes.Email, UITypes.URL, UITypes.PhoneNumber]
const [useProvideColumnCreateStore, useColumnCreateStore] = createInjectionState( const [useProvideColumnCreateStore, useColumnCreateStore] = createInjectionState(
(meta: Ref<TableType>, column?: Ref<ColumnType>) => { (meta: Ref<TableType | undefined>, column: Ref<ColumnType | undefined>) => {
const { sqlUi } = useProject() const { sqlUi } = useProject()
const { $api } = useNuxtApp() const { $api } = useNuxtApp()
const { getMeta } = useMetas() const { getMeta } = useMetas()
const { t } = useI18n() const { t } = useI18n()
const { $e } = useNuxtApp() const { $e } = useNuxtApp()
const isEdit = computed(() => !!column?.value?.id) const isEdit = computed(() => !!column.value?.id)
const idType = null const idType = null
@ -32,7 +32,7 @@ const [useProvideColumnCreateStore, useColumnCreateStore] = createInjectionState
const formState = ref<Record<string, any>>({ const formState = ref<Record<string, any>>({
title: 'title', title: 'title',
uidt: UITypes.SingleLineText, uidt: UITypes.SingleLineText,
...clone(column?.value || {}), ...clone(column.value || {}),
}) })
// actions // actions
@ -101,8 +101,8 @@ const [useProvideColumnCreateStore, useColumnCreateStore] = createInjectionState
formState.value.dtxs = sqlUi.value.getDefaultScaleForDatatype(formState.value.dt) formState.value.dtxs = sqlUi.value.getDefaultScaleForDatatype(formState.value.dt)
const selectTypes = [UITypes.MultiSelect, UITypes.SingleSelect] const selectTypes = [UITypes.MultiSelect, UITypes.SingleSelect]
if (column && selectTypes.includes(formState.value.uidt) && selectTypes.includes(column?.value?.uidt as UITypes)) { if (column && selectTypes.includes(formState.value.uidt) && selectTypes.includes(column.value?.uidt as UITypes)) {
formState.value.dtxp = column?.value?.dtxp formState.value.dtxp = column.value?.dtxp
} }
if (columnToValidate.includes(formState.value.uidt)) { if (columnToValidate.includes(formState.value.uidt)) {
@ -112,7 +112,7 @@ const [useProvideColumnCreateStore, useColumnCreateStore] = createInjectionState
} }
if (isCurrency.value) { if (isCurrency.value) {
if (column?.value?.uidt === UITypes.Currency) { if (column.value?.uidt === UITypes.Currency) {
formState.value.dtxp = column.value.dtxp formState.value.dtxp = column.value.dtxp
formState.value.dtxs = column.value.dtxs formState.value.dtxs = column.value.dtxs
} else { } else {
@ -140,12 +140,12 @@ const [useProvideColumnCreateStore, useColumnCreateStore] = createInjectionState
formState.value.dtx = 'specificType' formState.value.dtx = 'specificType'
const selectTypes = [UITypes.MultiSelect, UITypes.SingleSelect] const selectTypes = [UITypes.MultiSelect, UITypes.SingleSelect]
if (column?.value && selectTypes.includes(formState.value.uidt) && selectTypes.includes(column?.value.uidt as UITypes)) { if (column.value && selectTypes.includes(formState.value.uidt) && selectTypes.includes(column.value.uidt as UITypes)) {
formState.value.dtxp = column?.value.dtxp formState.value.dtxp = column.value.dtxp
} }
if (isCurrency.value) { if (isCurrency.value) {
if (column?.value?.uidt === UITypes.Currency) { if (column.value?.uidt === UITypes.Currency) {
formState.value.dtxp = column.value.dtxp formState.value.dtxp = column.value.dtxp
formState.value.dtxs = column.value.dtxs formState.value.dtxs = column.value.dtxs
} else { } else {
@ -177,10 +177,10 @@ const [useProvideColumnCreateStore, useColumnCreateStore] = createInjectionState
} }
try { try {
formState.value.table_name = meta.value.table_name formState.value.table_name = meta.value?.table_name
// formState.value.title = formState.value.column_name // formState.value.title = formState.value.column_name
if (column?.value) { if (column.value) {
await $api.dbTableColumn.update(column?.value?.id as string, formState.value) await $api.dbTableColumn.update(column.value?.id as string, formState.value)
// Column updated // Column updated
message.success(t('msg.success.columnUpdated')) message.success(t('msg.success.columnUpdated'))
} else { } else {
@ -193,10 +193,10 @@ const [useProvideColumnCreateStore, useColumnCreateStore] = createInjectionState
// }; // };
// } // }
} }
await $api.dbTableColumn.create(meta.value.id as string, formState.value) await $api.dbTableColumn.create(meta.value?.id as string, formState.value)
/** if LTAR column then force reload related table meta */ /** if LTAR column then force reload related table meta */
if (formState.value.uidt === UITypes.LinkToAnotherRecord && meta.value.id !== formState.value.childId) { if (formState.value.uidt === UITypes.LinkToAnotherRecord && meta.value?.id !== formState.value.childId) {
getMeta(formState.value.childId, true).then(() => {}) getMeta(formState.value.childId, true).then(() => {})
} }

193
packages/nc-gui/composables/useSmartsheetRowStore.ts

@ -20,123 +20,126 @@ import {
useVirtualCell, useVirtualCell,
} from '#imports' } from '#imports'
const [useProvideSmartsheetRowStore, useSmartsheetRowStore] = useInjectionState((meta: Ref<TableType>, row: MaybeRef<Row>) => { const [useProvideSmartsheetRowStore, useSmartsheetRowStore] = useInjectionState(
const { $api } = useNuxtApp() (meta: Ref<TableType | undefined>, row: MaybeRef<Row>) => {
const { $api } = useNuxtApp()
const { t } = useI18n() const { t } = useI18n()
const { project } = useProject() const { project } = useProject()
const { metas } = useMetas() const { metas } = useMetas()
const currentRow = ref(row) const currentRow = ref(row)
// state // state
const state = ref<Record<string, Record<string, any> | Record<string, any>[] | null>>({}) const state = ref<Record<string, Record<string, any> | Record<string, any>[] | null>>({})
// getters // getters
const isNew = computed(() => unref(row).rowMeta?.new ?? false) const isNew = computed(() => unref(row).rowMeta?.new ?? false)
// actions // actions
const addLTARRef = async (value: Record<string, any>, column: ColumnType) => { const addLTARRef = async (value: Record<string, any>, column: ColumnType) => {
const { isHm, isMm, isBt } = $(useVirtualCell(ref(column))) const { isHm, isMm, isBt } = $(useVirtualCell(ref(column)))
if (isHm || isMm) { if (isHm || isMm) {
if (!state.value[column.title!]) state.value[column.title!] = [] if (!state.value[column.title!]) state.value[column.title!] = []
if (state.value[column.title!]!.find((ln: Record<string, any>) => deepCompare(ln, value))) { if (state.value[column.title!]!.find((ln: Record<string, any>) => deepCompare(ln, value))) {
// This value is already in the list // This value is already in the list
return message.info(t('msg.info.valueAlreadyInList')) return message.info(t('msg.info.valueAlreadyInList'))
} }
state.value[column.title!]!.push(value) state.value[column.title!]!.push(value)
} else if (isBt) { } else if (isBt) {
state.value[column.title!] = value state.value[column.title!] = value
} }
}
// actions
const removeLTARRef = async (value: Record<string, any>, column: ColumnType) => {
const { isHm, isMm, isBt } = $(useVirtualCell(ref(column)))
if (isHm || isMm) {
state.value[column.title!]?.splice(state.value[column.title!]?.indexOf(value), 1)
} else if (isBt) {
state.value[column.title!] = null
} }
}
const linkRecord = async (rowId: string, relatedRowId: string, column: ColumnType, type: RelationTypes) => { // actions
try { const removeLTARRef = async (value: Record<string, any>, column: ColumnType) => {
await $api.dbTableRow.nestedAdd( const { isHm, isMm, isBt } = $(useVirtualCell(ref(column)))
NOCO, if (isHm || isMm) {
project.value.title as string, state.value[column.title!]?.splice(state.value[column.title!]?.indexOf(value), 1)
meta.value.title as string, } else if (isBt) {
rowId, state.value[column.title!] = null
type as 'mm' | 'hm', }
column.title as string,
relatedRowId,
)
} catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e))
} }
}
/** sync LTAR relations kept in local state */
const syncLTARRefs = async (row: Record<string, any>) => {
const id = extractPkFromRow(row, meta.value.columns as ColumnType[])
for (const column of meta?.value?.columns ?? []) {
if (column.uidt !== UITypes.LinkToAnotherRecord) continue
const colOptions = column?.colOptions as LinkToAnotherRecordType
const { isHm, isMm, isBt } = $(useVirtualCell(ref(column))) const linkRecord = async (rowId: string, relatedRowId: string, column: ColumnType, type: RelationTypes) => {
const relatedTableMeta = metas.value?.[colOptions?.fk_related_model_id as string] try {
await $api.dbTableRow.nestedAdd(
NOCO,
project.value.title as string,
meta.value?.title as string,
rowId,
type as 'mm' | 'hm',
column.title as string,
relatedRowId,
)
} catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e))
}
}
if (isHm || isMm) { /** sync LTAR relations kept in local state */
const relatedRows = (state.value?.[column.title!] ?? []) as Record<string, any>[] const syncLTARRefs = async (row: Record<string, any>) => {
for (const relatedRow of relatedRows) { const id = extractPkFromRow(row, meta.value?.columns as ColumnType[])
for (const column of meta?.value?.columns ?? []) {
if (column.uidt !== UITypes.LinkToAnotherRecord) continue
const colOptions = column?.colOptions as LinkToAnotherRecordType
const { isHm, isMm, isBt } = $(useVirtualCell(ref(column)))
const relatedTableMeta = metas.value?.[colOptions?.fk_related_model_id as string]
if (isHm || isMm) {
const relatedRows = (state.value?.[column.title!] ?? []) as Record<string, any>[]
for (const relatedRow of relatedRows) {
await linkRecord(
id,
extractPkFromRow(relatedRow, relatedTableMeta.columns as ColumnType[]),
column,
colOptions.type as RelationTypes,
)
}
} else if (isBt && state?.value?.[column.title!]) {
await linkRecord( await linkRecord(
id, id,
extractPkFromRow(relatedRow, relatedTableMeta.columns as ColumnType[]), extractPkFromRow(state.value?.[column.title!] as Record<string, any>, relatedTableMeta.columns as ColumnType[]),
column, column,
colOptions.type as RelationTypes, colOptions.type as RelationTypes,
) )
} }
} else if (isBt && state?.value?.[column.title!]) {
await linkRecord(
id,
extractPkFromRow(state.value?.[column.title!] as Record<string, any>, relatedTableMeta.columns as ColumnType[]),
column,
colOptions.type as RelationTypes,
)
} }
} }
}
const loadRow = async () => {
const loadRow = async () => { const record = await $api.dbTableRow.read(
const record = await $api.dbTableRow.read( NOCO,
NOCO, project.value?.id as string,
project?.value?.id as string, meta.value?.title as string,
meta.value.title, extractPkFromRow(ref(row).value?.row, meta.value?.columns as ColumnType[]),
extractPkFromRow(ref(row).value?.row, meta.value.columns as ColumnType[]), )
) Object.assign(ref(row).value, {
Object.assign(ref(row).value, { row: record,
row: record, oldRow: { ...record },
oldRow: { ...record }, rowMeta: {},
rowMeta: {}, })
}) }
}
return {
return { row,
row, state,
state, isNew,
isNew, // todo: use better name
// todo: use better name addLTARRef,
addLTARRef, removeLTARRef,
removeLTARRef, syncLTARRefs,
syncLTARRefs, loadRow,
loadRow, currentRow,
currentRow, }
} },
}, 'smartsheet-row-store') 'smartsheet-row-store',
)
export { useProvideSmartsheetRowStore } export { useProvideSmartsheetRowStore }

13
packages/nc-gui/composables/useViewColumns.ts

@ -6,7 +6,11 @@ import { useNuxtApp } from '#app'
import { IsPublicInj } from '#imports' import { IsPublicInj } from '#imports'
import type { Field } from '~/lib' import type { Field } from '~/lib'
export function useViewColumns(view: Ref<ViewType> | undefined, meta: ComputedRef<TableType>, reloadData?: () => void) { export function useViewColumns(
view: Ref<ViewType | undefined>,
meta: Ref<TableType | undefined> | ComputedRef<TableType | undefined>,
reloadData?: () => void,
) {
const isPublic = inject(IsPublicInj, ref(false)) const isPublic = inject(IsPublicInj, ref(false))
const fields = ref<Field[]>() const fields = ref<Field[]>()
@ -119,7 +123,7 @@ export function useViewColumns(view: Ref<ViewType> | undefined, meta: ComputedRe
const saveOrUpdate = async (field: any, index: number) => { const saveOrUpdate = async (field: any, index: number) => {
if (isPublic.value && fields.value) { if (isPublic.value && fields.value) {
fields.value[index] = field fields.value[index] = field
meta.value.columns = meta.value?.columns?.map((column: ColumnType) => { meta.value!.columns = meta.value!.columns?.map((column: ColumnType) => {
if (column.id === field.fk_column_id) { if (column.id === field.fk_column_id) {
return { return {
...column, ...column,
@ -136,7 +140,7 @@ export function useViewColumns(view: Ref<ViewType> | undefined, meta: ComputedRe
if (isUIAllowed('fieldsSync')) { if (isUIAllowed('fieldsSync')) {
if (field.id && view?.value?.id) { if (field.id && view?.value?.id) {
await $api.dbViewColumn.update(view.value.id, field.id, field) await $api.dbViewColumn.update(view.value.id, field.id, field)
} else if (view?.value.id) { } else if (view.value?.id) {
const insertedField = (await $api.dbViewColumn.create(view.value.id, field)) as any const insertedField = (await $api.dbViewColumn.create(view.value.id, field)) as any
/** update the field in fields if defined */ /** update the field in fields if defined */
@ -151,8 +155,7 @@ export function useViewColumns(view: Ref<ViewType> | undefined, meta: ComputedRe
const showSystemFields = computed({ const showSystemFields = computed({
get() { get() {
// todo: show_system_fields missing from ViewType return view.value?.show_system_fields || false
return view?.value.show_system_fields || false
}, },
set(v: boolean) { set(v: boolean) {
if (view?.value?.id) { if (view?.value?.id) {

4
packages/nc-gui/composables/useViewFilters.ts

@ -16,7 +16,7 @@ import {
import type { Filter } from '~/lib' import type { Filter } from '~/lib'
export function useViewFilters( export function useViewFilters(
view: Ref<ViewType> | undefined, view: Ref<ViewType | undefined>,
parentId?: string, parentId?: string,
autoApply?: ComputedRef<boolean>, autoApply?: ComputedRef<boolean>,
reloadData?: () => void, reloadData?: () => void,
@ -75,7 +75,7 @@ export function useViewFilters(
if (parentId) { if (parentId) {
filters.value = await $api.dbTableFilter.childrenRead(parentId) filters.value = await $api.dbTableFilter.childrenRead(parentId)
} else { } else {
filters.value = await $api.dbTableFilter.read(view!.value.id!) filters.value = await $api.dbTableFilter.read(view.value!.id!)
} }
} }
} catch (e: any) { } catch (e: any) {

9
packages/nc-gui/composables/useViewSorts.ts

@ -1,12 +1,9 @@
import type { GalleryType, GridType, KanbanType, SortType } from 'nocodb-sdk' import type { SortType, ViewType } from 'nocodb-sdk'
import type { Ref } from 'vue' import type { Ref } from 'vue'
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import { IsPublicInj, ReloadViewDataHookInj, extractSdkResponseErrorMsg, useNuxtApp } from '#imports' import { IsPublicInj, ReloadViewDataHookInj, extractSdkResponseErrorMsg, useNuxtApp } from '#imports'
export function useViewSorts( export function useViewSorts(view: Ref<ViewType | undefined>, reloadData?: () => void) {
view: Ref<(GridType | KanbanType | GalleryType) & { id?: string }> | undefined,
reloadData?: () => void,
) {
const { sharedView } = useSharedView() const { sharedView } = useSharedView()
const { sorts } = useSmartsheetStoreOrThrow() const { sorts } = useSmartsheetStoreOrThrow()
@ -49,7 +46,7 @@ export function useViewSorts(
await $api.dbTableSort.update(sort.id, sort) await $api.dbTableSort.update(sort.id, sort)
$e('sort-updated') $e('sort-updated')
} else { } else {
sorts.value[i] = (await $api.dbTableSort.create(view?.value.id as string, sort)) as unknown as SortType sorts.value[i] = (await $api.dbTableSort.create(view.value?.id as string, sort)) as unknown as SortType
} }
} }
reloadData?.() reloadData?.()

2
packages/nc-gui/pages/[projectType]/[projectId]/index/index/[type]/[title]/[[viewTitle]].vue

@ -22,5 +22,3 @@ getMeta(route.params.title as string, true).finally(() => {
</div> </div>
<TabsSmartsheet v-else :key="route.params.title" :active-tab="activeTab" /> <TabsSmartsheet v-else :key="route.params.title" :active-tab="activeTab" />
</template> </template>
<style scoped></style>

2
packages/nc-gui/pages/[projectType]/[projectId]/index/index/auth.vue

@ -1,5 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import Auth from '~~/components/tabs/Auth.vue' import Auth from '~/components/tabs/Auth.vue'
</script> </script>
<template> <template>

4
packages/nc-gui/pages/[projectType]/form/[viewId].vue

@ -1,6 +1,4 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Ref } from 'vue'
import type { TableType } from 'nocodb-sdk'
import { import {
IsFormInj, IsFormInj,
IsPublicInj, IsPublicInj,
@ -34,7 +32,7 @@ if (!notFound.value) {
provide(IsPublicInj, ref(true)) provide(IsPublicInj, ref(true))
provide(IsFormInj, ref(true)) provide(IsFormInj, ref(true))
useProvideSmartsheetStore(sharedView as Ref<TableType>, meta as Ref<TableType>, true) useProvideSmartsheetStore(sharedView, meta, true)
} }
</script> </script>

4
packages/nc-gui/pages/[projectType]/form/[viewId]/index.vue

@ -39,9 +39,9 @@ function isRequired(_columnObj: Record<string, any>, required = false) {
<template v-if="sharedFormView"> <template v-if="sharedFormView">
<general-noco-icon class="color-transition hover:(ring ring-accent)" :class="[isLoading ? 'animated-bg-gradient' : '']" /> <general-noco-icon class="color-transition hover:(ring ring-accent)" :class="[isLoading ? 'animated-bg-gradient' : '']" />
<h1 class="prose-2xl font-bold self-center my-4">{{ sharedFormView.heading }}</h1> <h1 class="prose-2xl font-bold self-center my-4">{{ sharedFormView?.heading }}</h1>
<h2 v-if="sharedFormView.subheading" class="prose-lg text-gray-500 self-center">{{ sharedFormView.subheading }}</h2> <h2 v-if="sharedFormView?.subheading" class="prose-lg text-gray-500 self-center">{{ sharedFormView.subheading }}</h2>
<a-alert v-if="notFound" type="warning" class="my-4 text-center" message="Not found" /> <a-alert v-if="notFound" type="warning" class="my-4 text-center" message="Not found" />

2
packages/nc-gui/pages/index/index/[projectId].vue

@ -57,7 +57,7 @@ onMounted(async () => {
setTimeout(() => { setTimeout(() => {
const input = form.value?.$el?.querySelector('input[type=text]') const input = form.value?.$el?.querySelector('input[type=text]')
input.setSelectionRange(0, formState.title.length) input.setSelectionRange(0, formState.title?.length)
input.focus() input.focus()
}, 500) }, 500)

2
packages/nc-gui/utils/iconUtils.ts

@ -13,7 +13,7 @@ import MdiThumbUpOutline from '~icons/mdi/thumb-up-outline'
import MdiFlag from '~icons/mdi/flag' import MdiFlag from '~icons/mdi/flag'
import MdiFlagOutline from '~icons/mdi/flag-outline' import MdiFlagOutline from '~icons/mdi/flag-outline'
export const getMdiIcon = (type: string) => { export const getMdiIcon = (type: string): any => {
switch (type) { switch (type) {
case 'mdi-check-bold': case 'mdi-check-bold':
return MdiCheckBold return MdiCheckBold

Loading…
Cancel
Save