Browse Source

Merge branch 'develop' into enable-decimals-and-numbers-for-qr-code

pull/4585/head
Daniel Spaude 2 years ago
parent
commit
60e7f18950
No known key found for this signature in database
GPG Key ID: 654A3D1FA4F35FFE
  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. 4
      packages/nc-gui/composables/useGlobal/state.ts
  28. 2
      packages/nc-gui/composables/useLTARStore.ts
  29. 5
      packages/nc-gui/composables/useSharedView.ts
  30. 2
      packages/nc-gui/composables/useSmartsheetStore.ts
  31. 11
      packages/nc-gui/package-lock.json
  32. 1
      packages/nc-gui/package.json
  33. 5
      packages/nc-gui/pages/[projectType]/form/[viewId]/index/index.vue
  34. 5
      packages/nc-gui/pages/forgot-password.vue
  35. 3
      packages/nc-gui/pages/index/index/[projectId].vue
  36. 3
      packages/nc-gui/pages/index/index/create.vue
  37. 3
      packages/nc-gui/pages/projects/index.vue
  38. 7
      packages/nc-gui/pages/projects/index/index.vue
  39. 9
      packages/nc-gui/pages/signup/[[token]].vue
  40. 2
      packages/nc-gui/utils/sortUtils.ts
  41. 8
      packages/nc-gui/utils/viewUtils.ts
  42. 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="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>
</div>
</div>
<LazyMonacoEditor
:model-value="localValue"
:model-value="localValue || ''"
class="min-w-full w-80"
:class="{ 'expanded-editor': isExpanded, 'editor': !isExpanded }"
: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'
const props = defineProps<{
modelValue?: string | null
modelValue?: string | number
}>()
const emits = defineEmits(['update:modelValue'])

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

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

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

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

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

@ -172,13 +172,8 @@ watch($$(activeLang), (newLang) => {
hide-minimap
/>
<div v-if="activeLang.clients" class="flex flex-row w-full justify-end space-x-3 mt-4 uppercase">
<a-select
v-if="activeLang"
v-model:value="selectedClient"
style="width: 6rem"
dropdown-class-name="nc-dropdown-snippet-active-lang"
>
<div v-if="activeLang?.clients" class="flex flex-row w-full justify-end space-x-3 mt-4 uppercase">
<a-select 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">
{{ client }}
</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 expandedFormDlg = ref(false)
const expandedFormRow = ref<Row>()
@ -365,7 +366,7 @@ function expandForm(row: Row, state?: Record<string, any>, fromToolbar = false)
if (rowId) {
router.push({
query: {
...route.query,
...routeQuery,
rowId,
},
})
@ -569,13 +570,13 @@ onBeforeUnmount(() => {
const expandedFormOnRowIdDlg = computed({
get() {
return !!route.query.rowId
return !!routeQuery.rowId
},
set(val) {
if (!val)
router.push({
query: {
...route.query,
...routeQuery,
rowId: undefined,
},
})
@ -903,11 +904,11 @@ const closeAddColumnDropdown = () => {
<Suspense>
<LazySmartsheetExpandedForm
v-if="expandedFormOnRowIdDlg"
:key="route.query.rowId"
:key="routeQuery.rowId"
v-model="expandedFormOnRowIdDlg"
:row="{ row: {}, oldRow: {}, rowMeta: {} }"
:meta="meta"
:row-id="route.query.rowId"
:row-id="routeQuery.rowId"
:view="view"
/>
</Suspense>

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

@ -24,7 +24,7 @@ import { NavigateDir } from '~/lib'
const props = defineProps<{
column: ColumnType
modelValue: any
row: Row
row?: Row
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'
interface Props {
column?: ColumnType & { meta: any }
column?: ColumnType
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)
})
const filterOption = (value: string, option: { key: string }) => option.key.toLowerCase().includes(value.toLowerCase())
</script>
<template>
@ -66,11 +68,11 @@ const refTables = $computed(() => {
<a-select
v-model:value="vModel.childId"
show-search
:filter-option="(value, option) => option.key.toLowerCase().includes(value.toLowerCase())"
:filter-option="filterOption"
dropdown-class-name="nc-dropdown-ltar-child-table"
@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 }}
</a-select-option>
</a-select>
@ -96,7 +98,7 @@ const refTables = $computed(() => {
dropdown-class-name="nc-dropdown-on-update"
@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 }}
</a-select-option>
</a-select>
@ -110,7 +112,7 @@ const refTables = $computed(() => {
dropdown-class-name="nc-dropdown-on-delete"
@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 }}
</a-select-option>
</a-select>

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

@ -1,6 +1,7 @@
<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 { getRelationName } from './utils'
import { MetaInj, inject, ref, useColumnCreateStoreOrThrow, useMetas, useProject, useVModel } from '#imports'
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_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(() => {
if (!tables || !tables.length) {
if (!tables || !tables.length || !meta || !meta.columns) {
return []
}
return meta?.columns
?.filter((c: any) => c.uidt === UITypes.LinkToAnotherRecord && !c.system)
.map((c: ColumnType) => ({
col: c.colOptions,
column: c,
...tables.find((t) => t.id === (c.colOptions as LinkToAnotherRecordType).fk_related_model_id),
const _refTables = meta.columns
.filter((column) => column.uidt === UITypes.LinkToAnotherRecord && !column.system)
.map((column) => ({
col: column.colOptions,
column,
...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 selectedTable = refTables?.find((t) => t.column.id === vModel.value.fk_relation_column_id)
const columns = $computed<ColumnType[]>(() => {
const selectedTable = refTables.find((t) => t.column.id === vModel.value.fk_relation_column_id)
if (!selectedTable?.id) {
return []
}
return metas[selectedTable.id].columns.filter((c: any) => {
return !(isSystemColumn(c) || c.uidt === UITypes.QrCode)
})
return metas[selectedTable.id].columns.filter((c: ColumnType) => !isSystemColumn(c))
})
</script>
@ -69,11 +62,11 @@ const columns = $computed(() => {
dropdown-class-name="!w-64 nc-dropdown-relation-table"
@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="font-semibold text-xs">{{ table.column.title }}</div>
<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>
</a-select-option>

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

@ -1,5 +1,7 @@
<script setup lang="ts">
import type { ColumnType, LinkToAnotherRecordType, TableType } from 'nocodb-sdk'
import { UITypes, isSystemColumn, isVirtualCol } from 'nocodb-sdk'
import { getRelationName } from './utils'
import { MetaInj, inject, ref, useColumnCreateStoreOrThrow, useMetas, useProject, useVModel } from '#imports'
const props = defineProps<{
@ -24,11 +26,6 @@ setAdditionalValidations({
rollup_function: [{ required: true, message: 'Required' }],
})
const relationNames = {
mm: 'Many To Many',
hm: 'Has Many',
}
const aggrFunctionsList = [
{ text: 'count', value: 'count' },
{ 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
const refTables = $computed(() => {
if (!tables || !tables.length) {
if (!tables || !tables.length || !meta || !meta.columns) {
return []
}
return (
meta?.columns
?.filter((c: any) => c.uidt === UITypes.LinkToAnotherRecord && c.colOptions.type !== 'bt' && !c.system)
.map((c) => ({
col: c.colOptions,
column: c,
...tables.find((t) => t.id === (c.colOptions as any)?.fk_related_model_id),
})) ?? []
)
const _refTables = meta.columns
.filter((c) => c.uidt === UITypes.LinkToAnotherRecord && (c.colOptions as LinkToAnotherRecordType).type !== 'bt' && !c.system)
.map((c) => ({
col: c.colOptions,
column: c,
...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(() => {
@ -67,7 +63,7 @@ const columns = $computed(() => {
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>
@ -80,11 +76,11 @@ const columns = $computed(() => {
dropdown-class-name="!w-64 nc-dropdown-relation-table"
@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="font-semibold text-xs">{{ table.column.title }}</div>
<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>
</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 { 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')

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>
</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">
<div class="flex-1" />

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

@ -1,5 +1,15 @@
<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()

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

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

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

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

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

@ -40,6 +40,11 @@ const columnByID = computed(() =>
}, {} as Record<string, ColumnType>),
)
const getColumnUidtByID = (key?: string) => {
if (!key) return ''
return columnByID.value[key]?.uidt || ''
}
watch(
() => view.value?.id,
(viewId) => {
@ -74,7 +79,7 @@ useMenuCloseOnEsc(open)
data-testid="nc-sorts-menu"
>
<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)" />
<LazySmartsheetToolbarFieldListAutoCompleteDropdown
@ -95,7 +100,7 @@ useMenuCloseOnEsc(open)
@select="saveOrUpdate(sort, i)"
>
<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"
:value="option.value"
>

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

@ -4,6 +4,7 @@ import {
IsLockedInj,
IsPublicInj,
extractSdkResponseErrorMsg,
getViewIcon,
inject,
message,
ref,
@ -13,7 +14,6 @@ import {
useProject,
useSmartsheetStoreOrThrow,
useUIPermission,
viewIcons,
} from '#imports'
import { LockType } from '~/lib'
import MdiLockOutlineIcon from '~icons/mdi/lock-outline'
@ -30,9 +30,11 @@ const isView = false
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)
@ -47,7 +49,7 @@ const { isUIAllowed } = useUIPermission()
const { isSharedBase } = useProject()
const Icon = computed(() => {
switch (selectedView?.value.lock_type) {
switch (selectedView.value?.lock_type) {
case LockType.Personal:
return MdiAccountIcon
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) {
$e('a:grid:lockmenu', { lockType: type })
if (!selectedView?.value) return
if (!selectedView.value) return
if (type === 'personal') {
// Coming soon
@ -79,8 +83,6 @@ async function changeLockType(type: LockType) {
}
}
const { isSqlView } = useSmartsheetStoreOrThrow()
const open = ref(false)
useMenuCloseOnEsc(open)
@ -92,9 +94,9 @@ useMenuCloseOnEsc(open)
<a-button v-e="['c:actions']" class="nc-actions-menu-btn nc-toolbar-btn">
<div class="flex gap-2 items-center">
<component
:is="viewIcons[selectedView?.type].icon"
:is="getViewIcon(selectedView?.type)?.icon"
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">
@ -117,7 +119,7 @@ useMenuCloseOnEsc(open)
>
<template #title>
<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
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">
<LazyTabsAuthUserManagementUsersModal
:key="showUserModal"
:key="`${showUserModal}`"
:show="showUserModal"
:selected-user="selectedUser"
@closed="showUserModal = false"

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

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

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

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

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

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

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

@ -52,7 +52,7 @@ onMounted(() => {
<template>
<div class="">
<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
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 { UITypes } from 'nocodb-sdk'
import type { Ref } from 'vue'
import type { RuleObject } from 'ant-design-vue/es/form'
import {
Form,
computed,
@ -20,6 +21,10 @@ const useForm = Form.useForm
const columnToValidate = [UITypes.Email, UITypes.URL, UITypes.PhoneNumber]
interface ValidationsObj {
[key: string]: RuleObject[]
}
const [useProvideColumnCreateStore, useColumnCreateStore] = createInjectionState(
(meta: Ref<TableType | undefined>, column: Ref<ColumnType | undefined>) => {
const { sqlUi } = useProject()
@ -36,10 +41,10 @@ const [useProvideColumnCreateStore, useColumnCreateStore] = createInjectionState
const idType = null
const additionalValidations = ref<Record<string, any>>({})
const additionalValidations = ref<ValidationsObj>({})
const setAdditionalValidations = (validations: Record<string, any>) => {
additionalValidations.value = validations
const setAdditionalValidations = (validations: ValidationsObj) => {
additionalValidations.value = { ...additionalValidations.value, ...validations }
}
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 {
'isOpen'?: boolean
'row': Row | null
'row': Row
'state'?: Record<string, any> | null
'meta': TableType
'loadRow'?: boolean

4
packages/nc-gui/composables/useGlobal/state.ts

@ -84,8 +84,10 @@ export function useGlobalState(storageKey = 'nocodb-gui-v2'): State {
set: (val) => (storage.value.token = val),
})
const config = useRuntimeConfig()
const appInfo = ref<AppInfo>({
ncSiteUrl: BASE_FALLBACK_URL,
ncSiteUrl: config.public.ncBackendUrl || BASE_FALLBACK_URL,
authType: 'jwt',
connectToExternalDB: false,
defaultLimit: 0,

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

@ -94,7 +94,7 @@ const [useProvideLTARStore, useLTARStore] = useInjectionState(
}
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(() => {

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

@ -103,7 +103,7 @@ export function useSharedView() {
const page = paginationData.value.page || 1
const pageSize = paginationData.value.pageSize || appInfoDefaultLimit
const data = await $api.public.groupedDataList(
return await $api.public.groupedDataList(
sharedView.value.uuid!,
columnId,
{
@ -118,7 +118,6 @@ export function useSharedView() {
},
},
)
return data
}
const exportFile = async (
@ -126,7 +125,7 @@ export function useSharedView() {
offset: number,
type: ExportTypes.EXCEL | ExportTypes.CSV,
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, {
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 sorts = ref<SortType[]>(unref(initialSorts) ?? [])
const sorts = ref<Required<SortType>[]>((unref(initialSorts) as Required<SortType>[]) ?? [])
const nestedFilters = ref<FilterType[]>(unref(initialFilters) ?? [])
return {

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

@ -9,6 +9,7 @@
"license": "AGPL-3.0-or-later",
"dependencies": {
"@ckpack/vue-color": "^1.2.0",
"@types/file-saver": "^2.0.5",
"@vue-flow/additional-components": "^1.2.0",
"@vue-flow/core": "^1.3.0",
"@vuelidate/core": "^2.0.0-alpha.44",
@ -2999,6 +3000,11 @@
"integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==",
"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": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz",
@ -19704,6 +19710,11 @@
"integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==",
"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": {
"version": "0.0.33",
"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",
"d3-scale": "^4.0.2",
"dagre": "^0.8.5",
"@types/file-saver": "^2.0.5",
"dayjs": "^1.11.3",
"file-saver": "^2.0.5",
"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>
<LazySmartsheetVirtualCell
v-if="isVirtualCol(field)"
:model-value="null"
class="mt-0 nc-input"
: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"
/>
@ -95,7 +96,7 @@ function isRequired(_columnObj: Record<string, any>, required = false) {
v-model="formState[field.title]"
class="nc-input"
: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"
:edit-enabled="true"
/>

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

@ -1,4 +1,5 @@
<script setup lang="ts">
import type { RuleObject } from 'ant-design-vue/es/form'
import { definePageMeta, reactive, ref, useApi, useI18n, validateEmail } from '#imports'
definePageMeta({
@ -25,13 +26,13 @@ const formRules = {
{
validator: (_: unknown, v: string) => {
return new Promise((resolve, reject) => {
if (validateEmail(v)) return resolve(true)
if (validateEmail(v)) return resolve()
reject(new Error(t('msg.error.signUpRules.emailInvalid')))
})
},
message: t('msg.error.signUpRules.emailInvalid'),
},
],
] as RuleObject[],
}
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 { ProjectType } from 'nocodb-sdk'
import type { VNodeRef } from '@vue/runtime-core'
import type { RuleObject } from 'ant-design-vue/es/form'
import {
extractSdkResponseErrorMsg,
message,
@ -25,7 +26,7 @@ const nameValidationRules = [
message: 'Project name is required',
},
projectTitleValidator,
]
] as RuleObject[]
const form = ref<typeof Form>()

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

@ -1,5 +1,6 @@
<script lang="ts" setup>
import type { Form } from 'ant-design-vue'
import type { RuleObject } from 'ant-design-vue/es/form'
import type { VNodeRef } from '@vue/runtime-core'
import {
extractSdkResponseErrorMsg,
@ -25,7 +26,7 @@ const nameValidationRules = [
message: 'Project name is required',
},
projectTitleValidator,
]
] as RuleObject[]
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 type { ProjectType } from 'nocodb-sdk'
import { useI18n } from 'vue-i18n'
import { navigateTo } from '#app'
import { extractSdkResponseErrorMsg } from '~/utils'
import { extractSdkResponseErrorMsg, navigateTo, useNuxtApp, useRoute } from '#imports'
import MaterialSymbolsFormatListBulletedRounded from '~icons/material-symbols/format-list-bulleted-rounded'
import MaterialSymbolsGridView from '~icons/material-symbols/grid-view'
import MdiPlus from '~icons/mdi/plus'

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

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

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

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

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

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

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

@ -1,5 +1,5 @@
import { ViewTypes } from 'nocodb-sdk'
import { themeV2Colors } from '~/utils'
import { themeV2Colors } from '#imports'
import MdiGridIcon from '~icons/mdi/grid-large'
import MdiFormIcon from '~icons/mdi/form-select'
@ -41,3 +41,9 @@ export function applyLanguageDirection(dir: typeof rtl | typeof ltr) {
export function applyNonSelectable() {
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",
"description": "I18n directive"
},
{
"name": "xc-ver-resize",
"description": "Resize directive"
}
]
}

Loading…
Cancel
Save