Browse Source

Merge pull request #4574 from nocodb/fix/types

fix(nc-gui): type issues
pull/4608/head
աɨռɢӄաօռɢ 2 years ago committed by GitHub
parent
commit
498f9e5b3d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      packages/nc-gui/components/cell/Json.vue
  2. 2
      packages/nc-gui/components/cell/TextArea.vue
  3. 4
      packages/nc-gui/components/cell/attachment/Carousel.vue
  4. 11
      packages/nc-gui/components/general/Tooltip.vue
  5. 9
      packages/nc-gui/components/smartsheet/ApiSnippet.vue
  6. 11
      packages/nc-gui/components/smartsheet/Grid.vue
  7. 2
      packages/nc-gui/components/smartsheet/VirtualCell.vue
  8. 2
      packages/nc-gui/components/smartsheet/column/EditOrAddProvider.vue
  9. 10
      packages/nc-gui/components/smartsheet/column/LinkedToAnotherRecordOptions.vue
  10. 39
      packages/nc-gui/components/smartsheet/column/LookupOptions.vue
  11. 32
      packages/nc-gui/components/smartsheet/column/RollupOptions.vue
  12. 9
      packages/nc-gui/components/smartsheet/column/utils.ts
  13. 7
      packages/nc-gui/components/smartsheet/header/Cell.vue
  14. 2
      packages/nc-gui/components/smartsheet/header/VirtualCell.vue
  15. 12
      packages/nc-gui/components/smartsheet/toolbar/KanbanStackEditOrAdd.vue
  16. 4
      packages/nc-gui/components/smartsheet/toolbar/MoreActions.vue
  17. 7
      packages/nc-gui/components/smartsheet/toolbar/SearchData.vue
  18. 9
      packages/nc-gui/components/smartsheet/toolbar/SortListMenu.vue
  19. 22
      packages/nc-gui/components/smartsheet/toolbar/ViewActions.vue
  20. 2
      packages/nc-gui/components/tabs/auth/UserManagement.vue
  21. 2
      packages/nc-gui/components/tabs/auth/user-management/UsersModal.vue
  22. 11
      packages/nc-gui/components/template/Editor.vue
  23. 27
      packages/nc-gui/components/virtual-cell/components/ListChildItems.vue
  24. 2
      packages/nc-gui/components/webhook/List.vue
  25. 11
      packages/nc-gui/composables/useColumnCreateStore.ts
  26. 2
      packages/nc-gui/composables/useExpandedFormDetached/index.ts
  27. 2
      packages/nc-gui/composables/useLTARStore.ts
  28. 5
      packages/nc-gui/composables/useSharedView.ts
  29. 2
      packages/nc-gui/composables/useSmartsheetStore.ts
  30. 11
      packages/nc-gui/package-lock.json
  31. 1
      packages/nc-gui/package.json
  32. 5
      packages/nc-gui/pages/[projectType]/form/[viewId]/index/index.vue
  33. 5
      packages/nc-gui/pages/forgot-password.vue
  34. 3
      packages/nc-gui/pages/index/index/[projectId].vue
  35. 3
      packages/nc-gui/pages/index/index/create.vue
  36. 3
      packages/nc-gui/pages/projects/index.vue
  37. 7
      packages/nc-gui/pages/projects/index/index.vue
  38. 9
      packages/nc-gui/pages/signup/[[token]].vue
  39. 2
      packages/nc-gui/utils/sortUtils.ts
  40. 8
      packages/nc-gui/utils/viewUtils.ts
  41. 4
      packages/nc-gui/web-types.json

4
packages/nc-gui/components/cell/Json.vue

@ -135,13 +135,13 @@ useSelectedCellKeyupListener(active, (e) => {
<a-button type="text" size="small" :onclick="clear"><div class="text-xs">Cancel</div></a-button> <a-button type="text" size="small" :onclick="clear"><div class="text-xs">Cancel</div></a-button>
<a-button type="primary" size="small" :disabled="!!error || localValue === vModel"> <a-button type="primary" size="small" :disabled="!!error || localValue === vModel">
<div class="text-xs" :onclick="onSave">Save</div> <div class="text-xs" @click="onSave">Save</div>
</a-button> </a-button>
</div> </div>
</div> </div>
<LazyMonacoEditor <LazyMonacoEditor
:model-value="localValue" :model-value="localValue || ''"
class="min-w-full w-80" class="min-w-full w-80"
:class="{ 'expanded-editor': isExpanded, 'editor': !isExpanded }" :class="{ 'expanded-editor': isExpanded, 'editor': !isExpanded }"
:hide-minimap="true" :hide-minimap="true"

2
packages/nc-gui/components/cell/TextArea.vue

@ -3,7 +3,7 @@ import type { VNodeRef } from '@vue/runtime-core'
import { EditModeInj, inject, useVModel } from '#imports' import { EditModeInj, inject, useVModel } from '#imports'
const props = defineProps<{ const props = defineProps<{
modelValue?: string | null modelValue?: string | number
}>() }>()
const emits = defineEmits(['update:modelValue']) const emits = defineEmits(['update:modelValue'])

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

@ -47,7 +47,7 @@ onClickOutside(carouselRef, () => {
</script> </script>
<template> <template>
<general-overlay v-model="selectedImage" :z-index="1001"> <GeneralOverlay v-model="selectedImage" :z-index="1001">
<template v-if="selectedImage"> <template v-if="selectedImage">
<div class="overflow-hidden p-12 text-center relative"> <div class="overflow-hidden p-12 text-center relative">
<div class="text-white group absolute top-5 right-5"> <div class="text-white group absolute top-5 right-5">
@ -101,7 +101,7 @@ onClickOutside(carouselRef, () => {
</a-carousel> </a-carousel>
</div> </div>
</template> </template>
</general-overlay> </GeneralOverlay>
</template> </template>
<style scoped> <style scoped>

11
packages/nc-gui/components/general/Tooltip.vue

@ -1,7 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onKeyStroke } from '@vueuse/core' import { onKeyStroke } from '@vueuse/core'
import type { CSSProperties } from '@vue/runtime-dom' import type { CSSProperties } from '@vue/runtime-dom'
import { controlledRef, ref, useElementHover, watch } from '#imports' import { controlledRef, ref, useAttrs, useElementHover, watch } from '#imports'
interface Props { interface Props {
// Key to be pressed on hover to trigger the tooltip // Key to be pressed on hover to trigger the tooltip
@ -23,6 +23,8 @@ const showTooltip = controlledRef(false, {
const isHovering = useElementHover(() => el.value) const isHovering = useElementHover(() => el.value)
const attrs = useAttrs()
const isKeyPressed = ref(false) const isKeyPressed = ref(false)
onKeyStroke( onKeyStroke(
@ -73,6 +75,11 @@ watch([isHovering, () => modifierKey, () => disabled], ([hovering, key, isDisabl
showTooltip.value = true showTooltip.value = true
} }
}) })
const divStyles = $computed(() => ({
style: attrs.style as CSSProperties,
class: attrs.class as string,
}))
</script> </script>
<template> <template>
@ -81,7 +88,7 @@ watch([isHovering, () => modifierKey, () => disabled], ([hovering, key, isDisabl
<slot name="title" /> <slot name="title" />
</template> </template>
<div ref="el" class="w-full" :class="$attrs.class" :style="$attrs.style"> <div ref="el" class="w-full" v-bind="divStyles">
<slot /> <slot />
</div> </div>
</a-tooltip> </a-tooltip>

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

@ -172,13 +172,8 @@ watch($$(activeLang), (newLang) => {
hide-minimap hide-minimap
/> />
<div v-if="activeLang.clients" class="flex flex-row w-full justify-end space-x-3 mt-4 uppercase"> <div v-if="activeLang?.clients" class="flex flex-row w-full justify-end space-x-3 mt-4 uppercase">
<a-select <a-select v-model:value="selectedClient" style="width: 6rem" dropdown-class-name="nc-dropdown-snippet-active-lang">
v-if="activeLang"
v-model:value="selectedClient"
style="width: 6rem"
dropdown-class-name="nc-dropdown-snippet-active-lang"
>
<a-select-option v-for="(client, i) in activeLang?.clients" :key="i" class="!w-full uppercase" :value="client"> <a-select-option v-for="(client, i) in activeLang?.clients" :key="i" class="!w-full uppercase" :value="client">
{{ client }} {{ client }}
</a-select-option> </a-select-option>

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

@ -92,6 +92,7 @@ const contextMenu = computed({
}, },
}) })
const routeQuery = $computed(() => route.query as Record<string, string>)
const contextMenuTarget = ref<{ row: number; col: number } | null>(null) const contextMenuTarget = ref<{ row: number; col: number } | null>(null)
const expandedFormDlg = ref(false) const expandedFormDlg = ref(false)
const expandedFormRow = ref<Row>() const expandedFormRow = ref<Row>()
@ -365,7 +366,7 @@ function expandForm(row: Row, state?: Record<string, any>, fromToolbar = false)
if (rowId) { if (rowId) {
router.push({ router.push({
query: { query: {
...route.query, ...routeQuery,
rowId, rowId,
}, },
}) })
@ -569,13 +570,13 @@ onBeforeUnmount(() => {
const expandedFormOnRowIdDlg = computed({ const expandedFormOnRowIdDlg = computed({
get() { get() {
return !!route.query.rowId return !!routeQuery.rowId
}, },
set(val) { set(val) {
if (!val) if (!val)
router.push({ router.push({
query: { query: {
...route.query, ...routeQuery,
rowId: undefined, rowId: undefined,
}, },
}) })
@ -903,11 +904,11 @@ const closeAddColumnDropdown = () => {
<Suspense> <Suspense>
<LazySmartsheetExpandedForm <LazySmartsheetExpandedForm
v-if="expandedFormOnRowIdDlg" v-if="expandedFormOnRowIdDlg"
:key="route.query.rowId" :key="routeQuery.rowId"
v-model="expandedFormOnRowIdDlg" v-model="expandedFormOnRowIdDlg"
:row="{ row: {}, oldRow: {}, rowMeta: {} }" :row="{ row: {}, oldRow: {}, rowMeta: {} }"
:meta="meta" :meta="meta"
:row-id="route.query.rowId" :row-id="routeQuery.rowId"
:view="view" :view="view"
/> />
</Suspense> </Suspense>

2
packages/nc-gui/components/smartsheet/VirtualCell.vue

@ -24,7 +24,7 @@ import { NavigateDir } from '~/lib'
const props = defineProps<{ const props = defineProps<{
column: ColumnType column: ColumnType
modelValue: any modelValue: any
row: Row row?: Row
active?: boolean active?: boolean
}>() }>()

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

@ -4,7 +4,7 @@ import type { ColumnReqType, ColumnType } from 'nocodb-sdk'
import { MetaInj, inject, ref, toRef, useProvideColumnCreateStore } from '#imports' import { MetaInj, inject, ref, toRef, useProvideColumnCreateStore } from '#imports'
interface Props { interface Props {
column?: ColumnType & { meta: any } column?: ColumnType
columnPosition?: Pick<ColumnReqType, 'column_order'> columnPosition?: Pick<ColumnReqType, 'column_order'>
} }

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

@ -46,6 +46,8 @@ const refTables = $computed(() => {
return tables.filter((t) => t.type === ModelTypes.TABLE) return tables.filter((t) => t.type === ModelTypes.TABLE)
}) })
const filterOption = (value: string, option: { key: string }) => option.key.toLowerCase().includes(value.toLowerCase())
</script> </script>
<template> <template>
@ -66,11 +68,11 @@ const refTables = $computed(() => {
<a-select <a-select
v-model:value="vModel.childId" v-model:value="vModel.childId"
show-search show-search
:filter-option="(value, option) => option.key.toLowerCase().includes(value.toLowerCase())" :filter-option="filterOption"
dropdown-class-name="nc-dropdown-ltar-child-table" dropdown-class-name="nc-dropdown-ltar-child-table"
@change="onDataTypeChange" @change="onDataTypeChange"
> >
<a-select-option v-for="table in refTables" :key="table.title" :value="table.id"> <a-select-option v-for="table of refTables" :key="table.title" :value="table.id">
{{ table.title }} {{ table.title }}
</a-select-option> </a-select-option>
</a-select> </a-select>
@ -96,7 +98,7 @@ const refTables = $computed(() => {
dropdown-class-name="nc-dropdown-on-update" dropdown-class-name="nc-dropdown-on-update"
@change="onDataTypeChange" @change="onDataTypeChange"
> >
<a-select-option v-for="(option, index) in onUpdateDeleteOptions" :key="index" :value="option"> <a-select-option v-for="(option, i) of onUpdateDeleteOptions" :key="i" :value="option">
{{ option }} {{ option }}
</a-select-option> </a-select-option>
</a-select> </a-select>
@ -110,7 +112,7 @@ const refTables = $computed(() => {
dropdown-class-name="nc-dropdown-on-delete" dropdown-class-name="nc-dropdown-on-delete"
@change="onDataTypeChange" @change="onDataTypeChange"
> >
<a-select-option v-for="(option, index) in onUpdateDeleteOptions" :key="index" :value="option"> <a-select-option v-for="(option, i) of onUpdateDeleteOptions" :key="i" :value="option">
{{ option }} {{ option }}
</a-select-option> </a-select-option>
</a-select> </a-select>

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

@ -1,6 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { ColumnType, LinkToAnotherRecordType } from 'nocodb-sdk' import type { ColumnType, LinkToAnotherRecordType, TableType } from 'nocodb-sdk'
import { UITypes, isSystemColumn } from 'nocodb-sdk' import { UITypes, isSystemColumn } from 'nocodb-sdk'
import { getRelationName } from './utils'
import { MetaInj, inject, ref, useColumnCreateStoreOrThrow, useMetas, useProject, useVModel } from '#imports' import { MetaInj, inject, ref, useColumnCreateStoreOrThrow, useMetas, useProject, useVModel } from '#imports'
const props = defineProps<{ const props = defineProps<{
@ -27,36 +28,28 @@ setAdditionalValidations({
if (!vModel.value.fk_relation_column_id) vModel.value.fk_relation_column_id = null if (!vModel.value.fk_relation_column_id) vModel.value.fk_relation_column_id = null
if (!vModel.value.fk_lookup_column_id) vModel.value.fk_lookup_column_id = null if (!vModel.value.fk_lookup_column_id) vModel.value.fk_lookup_column_id = null
const relationNames = {
mm: 'Many To Many',
hm: 'Has Many',
bt: 'Belongs To',
}
const refTables = $computed(() => { const refTables = $computed(() => {
if (!tables || !tables.length) { if (!tables || !tables.length || !meta || !meta.columns) {
return [] return []
} }
return meta?.columns const _refTables = meta.columns
?.filter((c: any) => c.uidt === UITypes.LinkToAnotherRecord && !c.system) .filter((column) => column.uidt === UITypes.LinkToAnotherRecord && !column.system)
.map((c: ColumnType) => ({ .map((column) => ({
col: c.colOptions, col: column.colOptions,
column: c, column,
...tables.find((t) => t.id === (c.colOptions as LinkToAnotherRecordType).fk_related_model_id), ...tables.find((table) => table.id === (column.colOptions as LinkToAnotherRecordType).fk_related_model_id),
})) }))
.filter((table: any) => table.col.fk_related_model_id === table.id && !table.mm) .filter((table) => (table.col as LinkToAnotherRecordType)?.fk_related_model_id === table.id && !table.mm)
return _refTables as Required<TableType & { column: ColumnType; col: Required<LinkToAnotherRecordType> }>[]
}) })
const columns = $computed(() => { const columns = $computed<ColumnType[]>(() => {
const selectedTable = refTables?.find((t) => t.column.id === vModel.value.fk_relation_column_id) const selectedTable = refTables.find((t) => t.column.id === vModel.value.fk_relation_column_id)
if (!selectedTable?.id) { if (!selectedTable?.id) {
return [] return []
} }
return metas[selectedTable.id].columns.filter((c: ColumnType) => !isSystemColumn(c))
return metas[selectedTable.id].columns.filter((c: any) => {
return !(isSystemColumn(c) || c.uidt === UITypes.QrCode)
})
}) })
</script> </script>
@ -69,11 +62,11 @@ const columns = $computed(() => {
dropdown-class-name="!w-64 nc-dropdown-relation-table" dropdown-class-name="!w-64 nc-dropdown-relation-table"
@change="onDataTypeChange" @change="onDataTypeChange"
> >
<a-select-option v-for="(table, index) in refTables" :key="index" :value="table.col.fk_column_id"> <a-select-option v-for="(table, i) of refTables" :key="i" :value="table.col.fk_column_id">
<div class="flex flex-row space-x-0.5 h-full pb-0.5 items-center justify-between"> <div class="flex flex-row space-x-0.5 h-full pb-0.5 items-center justify-between">
<div class="font-semibold text-xs">{{ table.column.title }}</div> <div class="font-semibold text-xs">{{ table.column.title }}</div>
<div class="text-[0.65rem] text-gray-600"> <div class="text-[0.65rem] text-gray-600">
{{ relationNames[table.col.type] }} {{ table.title || table.table_name }} {{ getRelationName(table.col.type) }} {{ table.title || table.table_name }}
</div> </div>
</div> </div>
</a-select-option> </a-select-option>

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

@ -1,5 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { ColumnType, LinkToAnotherRecordType, TableType } from 'nocodb-sdk'
import { UITypes, isSystemColumn, isVirtualCol } from 'nocodb-sdk' import { UITypes, isSystemColumn, isVirtualCol } from 'nocodb-sdk'
import { getRelationName } from './utils'
import { MetaInj, inject, ref, useColumnCreateStoreOrThrow, useMetas, useProject, useVModel } from '#imports' import { MetaInj, inject, ref, useColumnCreateStoreOrThrow, useMetas, useProject, useVModel } from '#imports'
const props = defineProps<{ const props = defineProps<{
@ -24,11 +26,6 @@ setAdditionalValidations({
rollup_function: [{ required: true, message: 'Required' }], rollup_function: [{ required: true, message: 'Required' }],
}) })
const relationNames = {
mm: 'Many To Many',
hm: 'Has Many',
}
const aggrFunctionsList = [ const aggrFunctionsList = [
{ text: 'count', value: 'count' }, { text: 'count', value: 'count' },
{ text: 'min', value: 'min' }, { text: 'min', value: 'min' },
@ -45,19 +42,18 @@ if (!vModel.value.fk_rollup_column_id) vModel.value.fk_rollup_column_id = null
if (!vModel.value.rollup_function) vModel.value.rollup_function = null if (!vModel.value.rollup_function) vModel.value.rollup_function = null
const refTables = $computed(() => { const refTables = $computed(() => {
if (!tables || !tables.length) { if (!tables || !tables.length || !meta || !meta.columns) {
return [] return []
} }
return ( const _refTables = meta.columns
meta?.columns .filter((c) => c.uidt === UITypes.LinkToAnotherRecord && (c.colOptions as LinkToAnotherRecordType).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, column: c,
column: c, ...tables.find((t) => t.id === (c.colOptions as any)?.fk_related_model_id),
...tables.find((t) => t.id === (c.colOptions as any)?.fk_related_model_id), }))
})) ?? [] return _refTables as Required<TableType & { column: ColumnType; col: Required<LinkToAnotherRecordType> }>[]
)
}) })
const columns = $computed(() => { const columns = $computed(() => {
@ -67,7 +63,7 @@ const columns = $computed(() => {
return [] return []
} }
return metas[selectedTable.id].columns.filter((c: any) => !isVirtualCol(c.uidt) && !isSystemColumn(c)) return metas[selectedTable.id].columns.filter((c: ColumnType) => !isVirtualCol(c.uidt as UITypes) && !isSystemColumn(c))
}) })
</script> </script>
@ -80,11 +76,11 @@ const columns = $computed(() => {
dropdown-class-name="!w-64 nc-dropdown-relation-table" dropdown-class-name="!w-64 nc-dropdown-relation-table"
@change="onDataTypeChange" @change="onDataTypeChange"
> >
<a-select-option v-for="(table, index) in refTables" :key="index" :value="table.col.fk_column_id"> <a-select-option v-for="(table, i) of refTables" :key="i" :value="table.col.fk_column_id">
<div class="flex flex-row space-x-0.5 h-full pb-0.5 items-center justify-between"> <div class="flex flex-row space-x-0.5 h-full pb-0.5 items-center justify-between">
<div class="font-semibold text-xs">{{ table.column.title }}</div> <div class="font-semibold text-xs">{{ table.column.title }}</div>
<div class="text-[0.65rem] text-gray-600"> <div class="text-[0.65rem] text-gray-600">
({{ relationNames[table.col.type] }} {{ table.title || table.table_name }}) ({{ getRelationName(table.col.type) }} {{ table.title || table.table_name }})
</div> </div>
</div> </div>
</a-select-option> </a-select-option>

9
packages/nc-gui/components/smartsheet/column/utils.ts

@ -0,0 +1,9 @@
const relationNames = {
mm: 'Many To Many',
hm: 'Has Many',
bt: 'Belongs To',
} as const
export function getRelationName(type: string) {
return relationNames[type as keyof typeof relationNames]
}

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

@ -2,7 +2,12 @@
import type { ColumnReqType, ColumnType } from 'nocodb-sdk' import type { ColumnReqType, ColumnType } from 'nocodb-sdk'
import { ColumnInj, IsFormInj, IsKanbanInj, inject, provide, ref, toRef, useUIPermission } from '#imports' import { ColumnInj, IsFormInj, IsKanbanInj, inject, provide, ref, toRef, useUIPermission } from '#imports'
const props = defineProps<{ column: ColumnType & { meta: any }; required?: boolean | number; hideMenu?: boolean }>() interface Props {
column: ColumnType
required?: boolean | number
hideMenu?: boolean
}
const props = defineProps<Props>()
const hideMenu = toRef(props, 'hideMenu') const hideMenu = toRef(props, 'hideMenu')

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

@ -124,7 +124,7 @@ const closeAddColumnDropdown = () => {
<span class="name" style="white-space: nowrap" :title="column.title"> {{ column.title }}</span> <span class="name" style="white-space: nowrap" :title="column.title"> {{ column.title }}</span>
</a-tooltip> </a-tooltip>
<span v-if="isVirtualColRequired(column, meta.columns) || required" class="text-red-500">&nbsp;*</span> <span v-if="isVirtualColRequired(column, meta?.columns || []) || required" class="text-red-500">&nbsp;*</span>
<template v-if="!hideMenu"> <template v-if="!hideMenu">
<div class="flex-1" /> <div class="flex-1" />

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

@ -1,5 +1,15 @@
<script setup lang="ts"> <script setup lang="ts">
import { IsLockedInj, IsPublicInj, useKanbanViewStoreOrThrow, useMenuCloseOnEsc } from '#imports' import {
IsKanbanInj,
IsLockedInj,
IsPublicInj,
inject,
provide,
ref,
useKanbanViewStoreOrThrow,
useMenuCloseOnEsc,
useUIPermission,
} from '#imports'
const { isUIAllowed } = useUIPermission() const { isUIAllowed } = useUIPermission()

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

@ -14,6 +14,7 @@ import {
useI18n, useI18n,
useNuxtApp, useNuxtApp,
useProject, useProject,
useSharedView,
useSmartsheetStoreOrThrow, useSmartsheetStoreOrThrow,
useUIPermission, useUIPermission,
} from '#imports' } from '#imports'
@ -38,6 +39,8 @@ const selectedView = inject(ActiveViewInj, ref())
const { sorts, nestedFilters } = useSmartsheetStoreOrThrow() const { sorts, nestedFilters } = useSmartsheetStoreOrThrow()
const { exportFile: sharedViewExportFile } = useSharedView()
const isLocked = inject(IsLockedInj) const isLocked = inject(IsLockedInj)
const showWebhookDrawer = ref(false) const showWebhookDrawer = ref(false)
@ -58,7 +61,6 @@ const exportFile = async (exportType: ExportTypes) => {
while (!isNaN(offset) && offset > -1) { while (!isNaN(offset) && offset > -1) {
let res let res
if (isPublicView.value) { if (isPublicView.value) {
const { exportFile: sharedViewExportFile } = useSharedView()
res = await sharedViewExportFile(fields.value, offset, exportType, responseType) res = await sharedViewExportFile(fields.value, offset, exportType, responseType)
} else { } else {
res = await $api.dbViewRow.export( res = await $api.dbViewRow.export(

7
packages/nc-gui/components/smartsheet/toolbar/SearchData.vue

@ -1,4 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { TableType } from 'nocodb-sdk'
import { import {
ActiveViewInj, ActiveViewInj,
ReloadViewDataHookInj, ReloadViewDataHookInj,
@ -25,9 +26,9 @@ const searchDropdown = ref(null)
onClickOutside(searchDropdown, () => (isDropdownOpen.value = false)) onClickOutside(searchDropdown, () => (isDropdownOpen.value = false))
const columns = computed(() => const columns = computed(() =>
meta.value?.columns?.map((c) => ({ (meta.value as TableType)?.columns?.map((column) => ({
value: c.id, value: column.id,
label: c.title, label: column.title,
})), })),
) )

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

@ -40,6 +40,11 @@ const columnByID = computed(() =>
}, {} as Record<string, ColumnType>), }, {} as Record<string, ColumnType>),
) )
const getColumnUidtByID = (key?: string) => {
if (!key) return ''
return columnByID.value[key]?.uidt || ''
}
watch( watch(
() => view.value?.id, () => view.value?.id,
(viewId) => { (viewId) => {
@ -74,7 +79,7 @@ useMenuCloseOnEsc(open)
data-testid="nc-sorts-menu" data-testid="nc-sorts-menu"
> >
<div v-if="sorts?.length" class="sort-grid mb-2" @click.stop> <div v-if="sorts?.length" class="sort-grid mb-2" @click.stop>
<template v-for="(sort, i) in sorts || []" :key="i"> <template v-for="(sort, i) of sorts" :key="i">
<MdiCloseBox class="nc-sort-item-remove-btn text-grey self-center" small @click.stop="deleteSort(sort, i)" /> <MdiCloseBox class="nc-sort-item-remove-btn text-grey self-center" small @click.stop="deleteSort(sort, i)" />
<LazySmartsheetToolbarFieldListAutoCompleteDropdown <LazySmartsheetToolbarFieldListAutoCompleteDropdown
@ -95,7 +100,7 @@ useMenuCloseOnEsc(open)
@select="saveOrUpdate(sort, i)" @select="saveOrUpdate(sort, i)"
> >
<a-select-option <a-select-option
v-for="(option, j) in getSortDirectionOptions(columnByID[sort.fk_column_id]?.uidt)" v-for="(option, j) of getSortDirectionOptions(getColumnUidtByID(sort.fk_column_id))"
:key="j" :key="j"
:value="option.value" :value="option.value"
> >

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

@ -4,6 +4,7 @@ import {
IsLockedInj, IsLockedInj,
IsPublicInj, IsPublicInj,
extractSdkResponseErrorMsg, extractSdkResponseErrorMsg,
getViewIcon,
inject, inject,
message, message,
ref, ref,
@ -13,7 +14,6 @@ import {
useProject, useProject,
useSmartsheetStoreOrThrow, useSmartsheetStoreOrThrow,
useUIPermission, useUIPermission,
viewIcons,
} from '#imports' } from '#imports'
import { LockType } from '~/lib' import { LockType } from '~/lib'
import MdiLockOutlineIcon from '~icons/mdi/lock-outline' import MdiLockOutlineIcon from '~icons/mdi/lock-outline'
@ -30,9 +30,11 @@ const isView = false
const { $api, $e } = useNuxtApp() const { $api, $e } = useNuxtApp()
const selectedView = inject(ActiveViewInj) const { isSqlView } = useSmartsheetStoreOrThrow()
const selectedView = inject(ActiveViewInj, ref())
const isLocked = inject(IsLockedInj) const isLocked = inject(IsLockedInj, ref(false))
const showWebhookDrawer = ref(false) const showWebhookDrawer = ref(false)
@ -47,7 +49,7 @@ const { isUIAllowed } = useUIPermission()
const { isSharedBase } = useProject() const { isSharedBase } = useProject()
const Icon = computed(() => { const Icon = computed(() => {
switch (selectedView?.value.lock_type) { switch (selectedView.value?.lock_type) {
case LockType.Personal: case LockType.Personal:
return MdiAccountIcon return MdiAccountIcon
case LockType.Locked: case LockType.Locked:
@ -58,10 +60,12 @@ const Icon = computed(() => {
} }
}) })
const lockType = $computed(() => (selectedView.value?.lock_type as LockType) || LockType.Collaborative)
async function changeLockType(type: LockType) { async function changeLockType(type: LockType) {
$e('a:grid:lockmenu', { lockType: type }) $e('a:grid:lockmenu', { lockType: type })
if (!selectedView?.value) return if (!selectedView.value) return
if (type === 'personal') { if (type === 'personal') {
// Coming soon // Coming soon
@ -79,8 +83,6 @@ async function changeLockType(type: LockType) {
} }
} }
const { isSqlView } = useSmartsheetStoreOrThrow()
const open = ref(false) const open = ref(false)
useMenuCloseOnEsc(open) useMenuCloseOnEsc(open)
@ -92,9 +94,9 @@ useMenuCloseOnEsc(open)
<a-button v-e="['c:actions']" class="nc-actions-menu-btn nc-toolbar-btn"> <a-button v-e="['c:actions']" class="nc-actions-menu-btn nc-toolbar-btn">
<div class="flex gap-2 items-center"> <div class="flex gap-2 items-center">
<component <component
:is="viewIcons[selectedView?.type].icon" :is="getViewIcon(selectedView?.type)?.icon"
class="nc-view-icon group-hover:hidden" class="nc-view-icon group-hover:hidden"
:style="{ color: viewIcons[selectedView?.type].color }" :style="{ color: getViewIcon(selectedView?.type)?.color }"
/> />
<span class="!text-sm font-weight-normal"> <span class="!text-sm font-weight-normal">
@ -117,7 +119,7 @@ useMenuCloseOnEsc(open)
> >
<template #title> <template #title>
<div v-e="['c:navdraw:preview-as']" class="nc-project-menu-item group px-0 !py-0"> <div v-e="['c:navdraw:preview-as']" class="nc-project-menu-item group px-0 !py-0">
<LazySmartsheetToolbarLockType hide-tick :type="selectedView?.lock_type || LockType.Collaborative" /> <LazySmartsheetToolbarLockType hide-tick :type="lockType" />
<MaterialSymbolsChevronRightRounded <MaterialSymbolsChevronRightRounded
class="transform group-hover:(scale-115 text-accent) text-xl text-gray-400" class="transform group-hover:(scale-115 text-accent) text-xl text-gray-400"

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

@ -174,7 +174,7 @@ const isSuperAdmin = (user: { main_roles?: string }) => {
<div v-else class="flex flex-col w-full px-6"> <div v-else class="flex flex-col w-full px-6">
<LazyTabsAuthUserManagementUsersModal <LazyTabsAuthUserManagementUsersModal
:key="showUserModal" :key="`${showUserModal}`"
:show="showUserModal" :show="showUserModal"
:selected-user="selectedUser" :selected-user="selectedUser"
@closed="showUserModal = false" @closed="showUserModal = false"

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

@ -21,7 +21,7 @@ import { ProjectRole } from '~/lib'
interface Props { interface Props {
show: boolean show: boolean
selectedUser?: User selectedUser?: User | null
} }
interface Users { interface Users {

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

@ -693,7 +693,7 @@ function isSelectDisabled(uidt: string, disableSelect = false) {
size="large" size="large"
hide-details hide-details
:bordered="false" :bordered="false"
@click="$event.stopPropagation()" @click.stop
@blur="handleEditableTnChange(tableIdx)" @blur="handleEditableTnChange(tableIdx)"
@keydown.enter="handleEditableTnChange(tableIdx)" @keydown.enter="handleEditableTnChange(tableIdx)"
/> />
@ -749,14 +749,7 @@ function isSelectDisabled(uidt: string, disableSelect = false) {
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'column_name'"> <template v-if="column.key === 'column_name'">
<a-form-item v-bind="validateInfos[`tables.${tableIdx}.columns.${record.key}.${column.key}`]"> <a-form-item v-bind="validateInfos[`tables.${tableIdx}.columns.${record.key}.${column.key}`]">
<a-input <a-input :ref="(el: HTMLInputElement) => (inputRefs[record.key] = el)" v-model:value="record.column_name" />
:ref="
(el) => {
inputRefs[record.key] = el
}
"
v-model:value="record.column_name"
/>
</a-form-item> </a-form-item>
</template> </template>

27
packages/nc-gui/components/virtual-cell/components/ListChildItems.vue

@ -1,5 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { ColumnType } from 'nocodb-sdk' import type { ColumnType } from 'nocodb-sdk'
import type { Row } from '~/lib'
import { import {
ColumnInj, ColumnInj,
Empty, Empty,
@ -27,7 +28,7 @@ const isForm = inject(IsFormInj, ref(false))
const isPublic = inject(IsPublicInj, ref(false)) const isPublic = inject(IsPublicInj, ref(false))
const column = inject(ColumnInj) const column = inject(ColumnInj, ref())
const readonly = inject(ReadonlyInj, ref(false)) const readonly = inject(ReadonlyInj, ref(false))
@ -81,6 +82,8 @@ const expandedFormDlg = ref(false)
const expandedFormRow = ref() const expandedFormRow = ref()
const colTitle = $computed(() => column.value?.title || '')
/** reload children list whenever cell value changes and list is visible */ /** reload children list whenever cell value changes and list is visible */
watch( watch(
() => props.cellValue, () => props.cellValue,
@ -88,6 +91,12 @@ watch(
if (!isNew.value && vModel.value) loadChildrenList() if (!isNew.value && vModel.value) loadChildrenList()
}, },
) )
const onClick = (row: Row) => {
if (readonly.value) return
expandedFormRow.value = row
expandedFormDlg.value = true
}
</script> </script>
<template> <template>
@ -119,25 +128,19 @@ watch(
@click="emit('attachRecord')" @click="emit('attachRecord')"
> >
<div class="flex items-center gap-1"> <div class="flex items-center gap-1">
<MdiLinkVariantRemove class="text-xs" type="primary" @click="unlinkRow(row)" /> <MdiLinkVariant class="text-xs" type="primary" />
Link to '{{ relatedTableMeta.title }}' Link to '{{ relatedTableMeta.title }}'
</div> </div>
</a-button> </a-button>
</div> </div>
<template v-if="(isNew && state?.[column?.title]?.length) || childrenList?.pageInfo?.totalRows"> <template v-if="(isNew && state?.[colTitle]?.length) || childrenList?.pageInfo?.totalRows">
<div class="flex-1 overflow-auto min-h-0 scrollbar-thin-dull px-12 cursor-pointer"> <div class="flex-1 overflow-auto min-h-0 scrollbar-thin-dull px-12 cursor-pointer">
<a-card <a-card
v-for="(row, i) of childrenList?.list ?? state?.[column?.title] ?? []" v-for="(row, i) of childrenList?.list ?? state?.[colTitle] ?? []"
:key="i" :key="i"
class="!my-4 hover:(!bg-gray-200/50 shadow-md)" class="!my-4 hover:(!bg-gray-200/50 shadow-md)"
@click=" @click="onClick(row)"
() => {
if (readonly) return
expandedFormRow = row
expandedFormDlg = true
}
"
> >
<div class="flex items-center"> <div class="flex items-center">
<div class="flex-1 overflow-hidden min-w-0"> <div class="flex-1 overflow-hidden min-w-0">
@ -169,7 +172,7 @@ watch(
v-model:page-size="childrenListPagination.size" v-model:page-size="childrenListPagination.size"
class="mt-2 mx-auto" class="mt-2 mx-auto"
size="small" size="small"
:total="childrenList.pageInfo.totalRows" :total="childrenList?.pageInfo.totalRows"
show-less-items show-less-items
/> />
</div> </div>

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

@ -52,7 +52,7 @@ onMounted(() => {
<template> <template>
<div class=""> <div class="">
<div class="mb-2"> <div class="mb-2">
<div class="float-left font-bold text-xl mt-2 mb-4">{{ meta.title }} : Webhooks</div> <div class="float-left font-bold text-xl mt-2 mb-4">{{ meta?.title }} : Webhooks</div>
<a-button <a-button
v-e="['c:webhook:add']" v-e="['c:webhook:add']"

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

@ -2,6 +2,7 @@ import clone from 'just-clone'
import type { ColumnReqType, ColumnType, TableType } from 'nocodb-sdk' import type { ColumnReqType, ColumnType, TableType } from 'nocodb-sdk'
import { UITypes } from 'nocodb-sdk' import { UITypes } from 'nocodb-sdk'
import type { Ref } from 'vue' import type { Ref } from 'vue'
import type { RuleObject } from 'ant-design-vue/es/form'
import { import {
Form, Form,
computed, computed,
@ -20,6 +21,10 @@ const useForm = Form.useForm
const columnToValidate = [UITypes.Email, UITypes.URL, UITypes.PhoneNumber] const columnToValidate = [UITypes.Email, UITypes.URL, UITypes.PhoneNumber]
interface ValidationsObj {
[key: string]: RuleObject[]
}
const [useProvideColumnCreateStore, useColumnCreateStore] = createInjectionState( const [useProvideColumnCreateStore, useColumnCreateStore] = createInjectionState(
(meta: Ref<TableType | undefined>, column: Ref<ColumnType | undefined>) => { (meta: Ref<TableType | undefined>, column: Ref<ColumnType | undefined>) => {
const { sqlUi } = useProject() const { sqlUi } = useProject()
@ -36,10 +41,10 @@ const [useProvideColumnCreateStore, useColumnCreateStore] = createInjectionState
const idType = null const idType = null
const additionalValidations = ref<Record<string, any>>({}) const additionalValidations = ref<ValidationsObj>({})
const setAdditionalValidations = (validations: Record<string, any>) => { const setAdditionalValidations = (validations: ValidationsObj) => {
additionalValidations.value = validations additionalValidations.value = { ...additionalValidations.value, ...validations }
} }
const formState = ref<Record<string, any>>({ const formState = ref<Record<string, any>>({

2
packages/nc-gui/composables/useExpandedFormDetached/index.ts

@ -4,7 +4,7 @@ import type { Row } from '~/lib'
interface UseExpandedFormDetachedProps { interface UseExpandedFormDetachedProps {
'isOpen'?: boolean 'isOpen'?: boolean
'row': Row | null 'row': Row
'state'?: Record<string, any> | null 'state'?: Record<string, any> | null
'meta': TableType 'meta': TableType
'loadRow'?: boolean 'loadRow'?: boolean

2
packages/nc-gui/composables/useLTARStore.ts

@ -94,7 +94,7 @@ const [useProvideLTARStore, useLTARStore] = useInjectionState(
} }
const relatedTablePrimaryValueProp = computed(() => { const relatedTablePrimaryValueProp = computed(() => {
return (relatedTableMeta.value?.columns?.find((c) => c.pv) || relatedTableMeta?.value?.columns?.[0])?.title return (relatedTableMeta.value?.columns?.find((c) => c.pv) || relatedTableMeta?.value?.columns?.[0])?.title || ''
}) })
const relatedTablePrimaryKeyProps = computed(() => { const relatedTablePrimaryKeyProps = computed(() => {

5
packages/nc-gui/composables/useSharedView.ts

@ -103,7 +103,7 @@ export function useSharedView() {
const page = paginationData.value.page || 1 const page = paginationData.value.page || 1
const pageSize = paginationData.value.pageSize || appInfoDefaultLimit const pageSize = paginationData.value.pageSize || appInfoDefaultLimit
const data = await $api.public.groupedDataList( return await $api.public.groupedDataList(
sharedView.value.uuid!, sharedView.value.uuid!,
columnId, columnId,
{ {
@ -118,7 +118,6 @@ export function useSharedView() {
}, },
}, },
) )
return data
} }
const exportFile = async ( const exportFile = async (
@ -126,7 +125,7 @@ export function useSharedView() {
offset: number, offset: number,
type: ExportTypes.EXCEL | ExportTypes.CSV, type: ExportTypes.EXCEL | ExportTypes.CSV,
responseType: 'base64' | 'blob', responseType: 'base64' | 'blob',
{ sortsArr, filtersArr }: { sortsArr: SortType[]; filtersArr: FilterType[] }, { sortsArr, filtersArr }: { sortsArr: SortType[]; filtersArr: FilterType[] } = { sortsArr: [], filtersArr: [] },
) => { ) => {
return await $api.public.csvExport(sharedView.value!.uuid!, type, { return await $api.public.csvExport(sharedView.value!.uuid!, type, {
format: responseType, format: responseType,

2
packages/nc-gui/composables/useSmartsheetStore.ts

@ -47,7 +47,7 @@ const [useProvideSmartsheetStore, useSmartsheetStore] = useInjectionState(
}) })
const isSqlView = computed(() => (meta.value as TableType)?.type === 'view') const isSqlView = computed(() => (meta.value as TableType)?.type === 'view')
const sorts = ref<SortType[]>(unref(initialSorts) ?? []) const sorts = ref<Required<SortType>[]>((unref(initialSorts) as Required<SortType>[]) ?? [])
const nestedFilters = ref<FilterType[]>(unref(initialFilters) ?? []) const nestedFilters = ref<FilterType[]>(unref(initialFilters) ?? [])
return { return {

11
packages/nc-gui/package-lock.json generated

@ -9,6 +9,7 @@
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"dependencies": { "dependencies": {
"@ckpack/vue-color": "^1.2.0", "@ckpack/vue-color": "^1.2.0",
"@types/file-saver": "^2.0.5",
"@vue-flow/additional-components": "^1.2.0", "@vue-flow/additional-components": "^1.2.0",
"@vue-flow/core": "^1.3.0", "@vue-flow/core": "^1.3.0",
"@vuelidate/core": "^2.0.0-alpha.44", "@vuelidate/core": "^2.0.0-alpha.44",
@ -3021,6 +3022,11 @@
"integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==",
"dev": true "dev": true
}, },
"node_modules/@types/file-saver": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.5.tgz",
"integrity": "sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ=="
},
"node_modules/@types/form-data": { "node_modules/@types/form-data": {
"version": "0.0.33", "version": "0.0.33",
"resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz", "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz",
@ -19714,6 +19720,11 @@
"integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==",
"dev": true "dev": true
}, },
"@types/file-saver": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.5.tgz",
"integrity": "sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ=="
},
"@types/form-data": { "@types/form-data": {
"version": "0.0.33", "version": "0.0.33",
"resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz", "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz",

1
packages/nc-gui/package.json

@ -41,6 +41,7 @@
"ant-design-vue": "^3.2.11", "ant-design-vue": "^3.2.11",
"d3-scale": "^4.0.2", "d3-scale": "^4.0.2",
"dagre": "^0.8.5", "dagre": "^0.8.5",
"@types/file-saver": "^2.0.5",
"dayjs": "^1.11.3", "dayjs": "^1.11.3",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"httpsnippet": "^2.0.0", "httpsnippet": "^2.0.0",

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

@ -84,9 +84,10 @@ function isRequired(_columnObj: Record<string, any>, required = false) {
<div> <div>
<LazySmartsheetVirtualCell <LazySmartsheetVirtualCell
v-if="isVirtualCol(field)" v-if="isVirtualCol(field)"
:model-value="null"
class="mt-0 nc-input" class="mt-0 nc-input"
:data-testid="`nc-form-input-cell-${field.label || field.title}`" :data-testid="`nc-form-input-cell-${field.label || field.title}`"
:class="`nc-form-input-${field.title.replaceAll(' ', '')}`" :class="`nc-form-input-${field.title?.replaceAll(' ', '')}`"
:column="field" :column="field"
/> />
@ -95,7 +96,7 @@ function isRequired(_columnObj: Record<string, any>, required = false) {
v-model="formState[field.title]" v-model="formState[field.title]"
class="nc-input" class="nc-input"
:data-testid="`nc-form-input-cell-${field.label || field.title}`" :data-testid="`nc-form-input-cell-${field.label || field.title}`"
:class="`nc-form-input-${field.title.replaceAll(' ', '')}`" :class="`nc-form-input-${field.title?.replaceAll(' ', '')}`"
:column="field" :column="field"
:edit-enabled="true" :edit-enabled="true"
/> />

5
packages/nc-gui/pages/forgot-password.vue

@ -1,4 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import type { RuleObject } from 'ant-design-vue/es/form'
import { definePageMeta, reactive, ref, useApi, useI18n, validateEmail } from '#imports' import { definePageMeta, reactive, ref, useApi, useI18n, validateEmail } from '#imports'
definePageMeta({ definePageMeta({
@ -25,13 +26,13 @@ const formRules = {
{ {
validator: (_: unknown, v: string) => { validator: (_: unknown, v: string) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (validateEmail(v)) return resolve(true) if (validateEmail(v)) return resolve()
reject(new Error(t('msg.error.signUpRules.emailInvalid'))) reject(new Error(t('msg.error.signUpRules.emailInvalid')))
}) })
}, },
message: t('msg.error.signUpRules.emailInvalid'), message: t('msg.error.signUpRules.emailInvalid'),
}, },
], ] as RuleObject[],
} }
async function resetPassword() { async function resetPassword() {

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

@ -2,6 +2,7 @@
import type { Form } from 'ant-design-vue' import type { Form } from 'ant-design-vue'
import type { ProjectType } from 'nocodb-sdk' import type { ProjectType } from 'nocodb-sdk'
import type { VNodeRef } from '@vue/runtime-core' import type { VNodeRef } from '@vue/runtime-core'
import type { RuleObject } from 'ant-design-vue/es/form'
import { import {
extractSdkResponseErrorMsg, extractSdkResponseErrorMsg,
message, message,
@ -25,7 +26,7 @@ const nameValidationRules = [
message: 'Project name is required', message: 'Project name is required',
}, },
projectTitleValidator, projectTitleValidator,
] ] as RuleObject[]
const form = ref<typeof Form>() const form = ref<typeof Form>()

3
packages/nc-gui/pages/index/index/create.vue

@ -1,5 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { Form } from 'ant-design-vue' import type { Form } from 'ant-design-vue'
import type { RuleObject } from 'ant-design-vue/es/form'
import type { VNodeRef } from '@vue/runtime-core' import type { VNodeRef } from '@vue/runtime-core'
import { import {
extractSdkResponseErrorMsg, extractSdkResponseErrorMsg,
@ -25,7 +26,7 @@ const nameValidationRules = [
message: 'Project name is required', message: 'Project name is required',
}, },
projectTitleValidator, projectTitleValidator,
] ] as RuleObject[]
const form = ref<typeof Form>() const form = ref<typeof Form>()

3
packages/nc-gui/pages/projects/index.vue

@ -2,8 +2,7 @@
import { Modal, message } from 'ant-design-vue' import { Modal, message } from 'ant-design-vue'
import type { ProjectType } from 'nocodb-sdk' import type { ProjectType } from 'nocodb-sdk'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { navigateTo } from '#app' import { extractSdkResponseErrorMsg, navigateTo, useNuxtApp, useRoute } from '#imports'
import { extractSdkResponseErrorMsg } from '~/utils'
import MaterialSymbolsFormatListBulletedRounded from '~icons/material-symbols/format-list-bulleted-rounded' import MaterialSymbolsFormatListBulletedRounded from '~icons/material-symbols/format-list-bulleted-rounded'
import MaterialSymbolsGridView from '~icons/material-symbols/grid-view' import MaterialSymbolsGridView from '~icons/material-symbols/grid-view'
import MdiPlus from '~icons/mdi/plus' import MdiPlus from '~icons/mdi/plus'

7
packages/nc-gui/pages/projects/index/index.vue

@ -1,7 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { ProjectType } from 'nocodb-sdk' import type { ProjectType } from 'nocodb-sdk'
import { navigateTo } from '#app' import { navigateTo, useColors, useNuxtApp } from '#imports'
import { useColors } from '#imports'
import MdiMenuDown from '~icons/mdi/menu-down' import MdiMenuDown from '~icons/mdi/menu-down'
import MdiDeleteOutline from '~icons/mdi/delete-outline' import MdiDeleteOutline from '~icons/mdi/delete-outline'
import MdiPlus from '~icons/mdi/plus' import MdiPlus from '~icons/mdi/plus'
@ -25,9 +24,9 @@ const openProject = async (project: ProjectType) => {
$e('a:project:open', { count: projects.length }) $e('a:project:open', { count: projects.length })
} }
const formatTitle = (title: string) => const formatTitle = (title?: string) =>
title title
.split(' ') ?.split(' ')
.map((w) => w[0]) .map((w) => w[0])
.slice(0, 2) .slice(0, 2)
.join('') .join('')

9
packages/nc-gui/pages/signup/[[token]].vue

@ -1,5 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { validatePassword } from 'nocodb-sdk' import { validatePassword } from 'nocodb-sdk'
import type { RuleObject } from 'ant-design-vue/es/form'
import { import {
definePageMeta, definePageMeta,
navigateTo, navigateTo,
@ -44,24 +45,24 @@ const formRules = {
{ {
validator: (_: unknown, v: string) => { validator: (_: unknown, v: string) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!v?.length || validateEmail(v)) return resolve(true) if (!v?.length || validateEmail(v)) return resolve()
reject(new Error(t('msg.error.signUpRules.emailInvalid'))) reject(new Error(t('msg.error.signUpRules.emailInvalid')))
}) })
}, },
message: t('msg.error.signUpRules.emailInvalid'), message: t('msg.error.signUpRules.emailInvalid'),
}, },
], ] as RuleObject[],
password: [ password: [
{ {
validator: (_: unknown, v: string) => { validator: (_: unknown, v: string) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const { error, valid } = validatePassword(v) const { error, valid } = validatePassword(v)
if (valid) return resolve(true) if (valid) return resolve()
reject(new Error(error)) reject(new Error(error))
}) })
}, },
}, },
], ] as RuleObject[],
} }
async function signUp() { async function signUp() {

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

@ -1,6 +1,6 @@
import { UITypes } from 'nocodb-sdk' import { UITypes } from 'nocodb-sdk'
export const getSortDirectionOptions = (uidt: UITypes) => { export const getSortDirectionOptions = (uidt: UITypes | string) => {
switch (uidt) { switch (uidt) {
case UITypes.Year: case UITypes.Year:
case UITypes.Number: case UITypes.Number:

8
packages/nc-gui/utils/viewUtils.ts

@ -1,5 +1,5 @@
import { ViewTypes } from 'nocodb-sdk' import { ViewTypes } from 'nocodb-sdk'
import { themeV2Colors } from '~/utils' import { themeV2Colors } from '#imports'
import MdiGridIcon from '~icons/mdi/grid-large' import MdiGridIcon from '~icons/mdi/grid-large'
import MdiFormIcon from '~icons/mdi/form-select' import MdiFormIcon from '~icons/mdi/form-select'
@ -41,3 +41,9 @@ export function applyLanguageDirection(dir: typeof rtl | typeof ltr) {
export function applyNonSelectable() { export function applyNonSelectable() {
document.body.classList.add('non-selectable') document.body.classList.add('non-selectable')
} }
export const getViewIcon = (key?: string | number) => {
if (!key) return
return viewIcons[key]
}

4
packages/nc-gui/web-types.json

@ -13,6 +13,10 @@
{ {
"name": "t", "name": "t",
"description": "I18n directive" "description": "I18n directive"
},
{
"name": "xc-ver-resize",
"description": "Resize directive"
} }
] ]
} }

Loading…
Cancel
Save