Browse Source

Merge branch 'develop' into docs/v2

pull/6467/head
Raju Udava 1 year ago
parent
commit
bc8f109ac1
  1. 12
      packages/nc-gui/assets/nc-icons/fields.svg
  2. 2
      packages/nc-gui/assets/nc-icons/filter.svg
  3. 6
      packages/nc-gui/assets/nc-icons/group.svg
  4. 22
      packages/nc-gui/assets/nc-icons/lookup.svg
  5. 18
      packages/nc-gui/assets/nc-icons/super-admin.svg
  6. 36
      packages/nc-gui/components/account/Token.vue
  7. 6
      packages/nc-gui/components/cell/Checkbox.vue
  8. 6
      packages/nc-gui/components/cell/MultiSelect.vue
  9. 4
      packages/nc-gui/components/cell/SingleSelect.vue
  10. 4
      packages/nc-gui/components/dashboard/TreeView/ViewsList.vue
  11. 9
      packages/nc-gui/components/dashboard/settings/DataSources.vue
  12. 4
      packages/nc-gui/components/dashboard/settings/data-sources/CreateBase.vue
  13. 4
      packages/nc-gui/components/dashboard/settings/data-sources/EditBase.vue
  14. 3
      packages/nc-gui/components/dlg/ProjectDuplicate.vue
  15. 3
      packages/nc-gui/components/dlg/TableCreate.vue
  16. 3
      packages/nc-gui/components/dlg/TableDuplicate.vue
  17. 10
      packages/nc-gui/components/dlg/TableRename.vue
  18. 4
      packages/nc-gui/components/dlg/ViewCreate.vue
  19. 4
      packages/nc-gui/components/dlg/ViewDelete.vue
  20. 4
      packages/nc-gui/components/dlg/WorkspaceDelete.vue
  21. 10
      packages/nc-gui/components/general/WorkspaceIcon.vue
  22. 1
      packages/nc-gui/components/nc/Badge.vue
  23. 23
      packages/nc-gui/components/project/AccessSettings.vue
  24. 1
      packages/nc-gui/components/roles/Badge.vue
  25. 5
      packages/nc-gui/components/smartsheet/Cell.vue
  26. 55
      packages/nc-gui/components/smartsheet/column/DefaultValue.vue
  27. 25
      packages/nc-gui/components/smartsheet/column/SelectOptions.vue
  28. 3
      packages/nc-gui/components/smartsheet/expanded-form/Comments.vue
  29. 118
      packages/nc-gui/components/smartsheet/expanded-form/index.vue
  30. 36
      packages/nc-gui/components/smartsheet/grid/index.vue
  31. 2
      packages/nc-gui/components/smartsheet/header/VirtualCellIcon.ts
  32. 7
      packages/nc-gui/components/smartsheet/toolbar/ColumnFilterMenu.vue
  33. 4
      packages/nc-gui/components/smartsheet/toolbar/FieldsMenu.vue
  34. 3
      packages/nc-gui/components/smartsheet/toolbar/GroupByMenu.vue
  35. 4
      packages/nc-gui/components/smartsheet/toolbar/SortListMenu.vue
  36. 8
      packages/nc-gui/components/virtual-cell/Links.vue
  37. 27
      packages/nc-gui/components/virtual-cell/QrCode.vue
  38. 58
      packages/nc-gui/components/virtual-cell/barcode/Barcode.vue
  39. 6
      packages/nc-gui/components/virtual-cell/components/ItemChip.vue
  40. 73
      packages/nc-gui/components/virtual-cell/components/ListChildItems.vue
  41. 7
      packages/nc-gui/components/workspace/View.vue
  42. 5
      packages/nc-gui/composables/useExpandedFormStore.ts
  43. 8
      packages/nc-gui/composables/useMultiSelect/convertCellData.ts
  44. 4
      packages/nc-gui/composables/useMultiSelect/index.ts
  45. 14
      packages/nc-gui/composables/useViewData.ts
  46. 5
      packages/nc-gui/lang/en.json
  47. 4
      packages/nc-gui/nuxt.config.ts
  48. 2
      packages/nc-gui/package.json
  49. 5
      packages/nc-gui/store/views.ts
  50. 15
      packages/nc-gui/utils/iconUtils.ts
  51. 12
      packages/nc-gui/windi.config.ts
  52. 4
      packages/nocodb-sdk/src/lib/enums.ts
  53. 2
      packages/nocodb/src/db/BaseModelSqlv2.ts
  54. 6
      packages/nocodb/src/db/sql-client/lib/sqlite/SqliteClient.ts
  55. 8
      packages/nocodb/src/helpers/catchError.ts
  56. 5
      packages/nocodb/src/modules/jobs/jobs.module.ts
  57. 2
      packages/nocodb/src/services/api-tokens.service.ts
  58. 99
      packages/nocodb/src/services/columns.service.ts
  59. 2
      packages/nocodb/src/services/org-tokens.service.ts
  60. 320
      pnpm-lock.yaml
  61. 1
      tests/playwright/pages/Dashboard/Command/CmdKPage.ts

12
packages/nc-gui/assets/nc-icons/fields.svg

@ -1,8 +1,8 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.3335 12H14.0002" stroke="#374151" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2 12H2.00667" stroke="#374151" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.3335 8H14.0002" stroke="#4A5268" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2 8H2.00667" stroke="#4A5268" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.3335 4H14.0002" stroke="#4A5268" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2 4H2.00667" stroke="#4A5268" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.3335 12H14.0002" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2 12H2.00667" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.3335 8H14.0002" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2 8H2.00667" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.3335 4H14.0002" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2 4H2.00667" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 792 B

After

Width:  |  Height:  |  Size: 822 B

2
packages/nc-gui/assets/nc-icons/filter.svg

@ -1,3 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.6668 2H1.3335L6.66683 8.30667V12.6667L9.3335 14V8.30667L14.6668 2Z" stroke="#374151" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14.6668 2H1.3335L6.66683 8.30667V12.6667L9.3335 14V8.30667L14.6668 2Z" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 273 B

After

Width:  |  Height:  |  Size: 278 B

6
packages/nc-gui/assets/nc-icons/group.svg

@ -1,5 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14 5.33331V14H2V5.33331" stroke="#374151" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6.6665 8H9.33317" stroke="#374151" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M15.3332 2H0.666504V5.33333H15.3332V2Z" stroke="#4A5268" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14 5.33331V14H2V5.33331" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6.6665 8H9.33317" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M15.3332 2H0.666504V5.33333H15.3332V2Z" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 482 B

After

Width:  |  Height:  |  Size: 497 B

22
packages/nc-gui/assets/nc-icons/lookup.svg

@ -1,15 +1,7 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Field Icons">
<mask id="mask0_3_121" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="24" height="24">
<rect id="Bounding box" width="24" height="24" fill="#6a7184"/>
</mask>
<g mask="url(#mask0_3_121)">
<g id="Group 58">
<path id="Vector" d="M15.4615 15.0769L21 15.0769" stroke="#6a7184" stroke-width="1.33333" stroke-linecap="square" stroke-linejoin="round"/>
<path id="Vector_2" d="M15.4615 9.53845L21 9.53845" stroke="#6a7184" stroke-width="1.33333" stroke-linecap="square" stroke-linejoin="round"/>
<rect id="Rectangle 1" width="5.53846" height="16.6154" rx="1" transform="matrix(-1 0 0 1 21 4)" stroke="#6a7184" stroke-width="1.33"/>
<path id="Union" fill-rule="evenodd" clip-rule="evenodd" d="M12.0256 12.3077C12.0256 10.3817 10.4644 8.82049 8.53846 8.82049C6.61254 8.82049 5.05128 10.3817 5.05128 12.3077C5.05128 14.2336 6.61254 15.7948 8.53846 15.7948C10.4644 15.7948 12.0256 14.2336 12.0256 12.3077ZM8.53846 7.48715C11.2008 7.48715 13.359 9.64537 13.359 12.3077C13.359 14.97 11.2008 17.1282 8.53846 17.1282C7.4872 17.1282 6.51454 16.7917 5.72231 16.2205L5.47138 16.4714L3.47138 18.4714C3.21103 18.7318 2.78892 18.7318 2.52858 18.4714C2.26823 18.2111 2.26823 17.789 2.52858 17.5286L4.52858 15.5286L4.75813 15.299C4.10685 14.4771 3.71794 13.4378 3.71794 12.3077C3.71794 9.64537 5.87616 7.48715 8.53846 7.48715Z" fill="#6a7184"/>
</g>
</g>
</g>
</svg>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 10L14 10" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10 6L14 6" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6 12.5V13C6 13.5523 6.44772 14 7 14H13C13.5523 14 14 13.5523 14 13V3C14 2.44772 13.5523 2 13 2H7C6.44772 2 6 2.44772 6 3V3.5" stroke="currentColor" stroke-width="1.33" stroke-linecap="round"/>
<path d="M5 11C3.34315 11 2 9.65685 2 8C2 6.34315 3.34315 5 5 5C6.65685 5 8 6.34315 8 8C8 9.65685 6.65685 11 5 11Z" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M1.4714 12.4714C1.21105 12.7318 0.788945 12.7318 0.528595 12.4714C0.268246 12.2111 0.268246 11.7889 0.528595 11.5286L1.4714 12.4714ZM2.5286 9.5286L3 9.05719L3.94281 10L3.4714 10.4714L2.5286 9.5286ZM0.528595 11.5286L2.5286 9.5286L3.4714 10.4714L1.4714 12.4714L0.528595 11.5286Z" fill="currentColor" stroke="none"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

18
packages/nc-gui/assets/nc-icons/super-admin.svg

@ -0,0 +1,18 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="mask0_1882_74955" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="16" height="16">
<rect width="16" height="16" fill="#D9D9D9"/>
</mask>
<g mask="url(#mask0_1882_74955)">
<path d="M7.33333 7.99996C6.6 7.99996 5.97222 7.73885 5.45 7.21663C4.92778 6.6944 4.66667 6.06663 4.66667 5.33329C4.66667 4.59996 4.92778 3.97218 5.45 3.44996C5.97222 2.92774 6.6 2.66663 7.33333 2.66663C8.06667 2.66663 8.69444 2.92774 9.21667 3.44996C9.73889 3.97218 10 4.59996 10 5.33329C10 6.06663 9.73889 6.6944 9.21667 7.21663C8.69444 7.73885 8.06667 7.99996 7.33333 7.99996ZM2 12V11.4666C2 11.1 2.09444 10.7555 2.28333 10.4333C2.47222 10.1111 2.73333 9.86663 3.06667 9.69996C3.63333 9.41107 4.27222 9.16663 4.98333 8.96663C5.69444 8.76663 6.47778 8.66663 7.33333 8.66663H7.56667C7.63333 8.66663 7.7 8.67774 7.76667 8.69996C7.67778 8.89996 7.60278 9.10829 7.54167 9.32496C7.48056 9.54163 7.43333 9.76663 7.4 9.99996H7.33333C6.54444 9.99996 5.83611 10.1 5.20833 10.3C4.58056 10.5 4.06667 10.7 3.66667 10.9C3.56667 10.9555 3.48611 11.0333 3.425 11.1333C3.36389 11.2333 3.33333 11.3444 3.33333 11.4666V12H7.53333C7.6 12.2333 7.68889 12.4638 7.8 12.6916C7.91111 12.9194 8.03333 13.1333 8.16667 13.3333H3.33333C2.96667 13.3333 2.65278 13.2027 2.39167 12.9416C2.13056 12.6805 2 12.3666 2 12ZM7.33333 6.66663C7.7 6.66663 8.01389 6.53607 8.275 6.27496C8.53611 6.01385 8.66667 5.69996 8.66667 5.33329C8.66667 4.96663 8.53611 4.65274 8.275 4.39163C8.01389 4.13051 7.7 3.99996 7.33333 3.99996C6.96667 3.99996 6.65278 4.13051 6.39167 4.39163C6.13056 4.65274 6 4.96663 6 5.33329C6 5.69996 6.13056 6.01385 6.39167 6.27496C6.65278 6.53607 6.96667 6.66663 7.33333 6.66663Z" fill="currentColor" stroke="none"/>
<path d="M11 14C10.9117 14 10.826 13.9815 10.7429 13.9444C10.6597 13.9074 10.587 13.8543 10.5247 13.7852L8.14026 11.0741C8.09351 11.0198 8.05844 10.9605 8.03506 10.8963C8.01169 10.8321 8 10.7654 8 10.6963C8 10.6519 8.0039 10.6062 8.01169 10.5593C8.01948 10.5123 8.03636 10.4691 8.06234 10.4296L8.64675 9.32593C8.7039 9.22716 8.78052 9.14815 8.87662 9.08889C8.97273 9.02963 9.08052 9 9.2 9H12.8C12.9195 9 13.0273 9.02963 13.1234 9.08889C13.2195 9.14815 13.2961 9.22716 13.3532 9.32593L13.9377 10.4296C13.9636 10.4691 13.9805 10.5123 13.9883 10.5593C13.9961 10.6062 14 10.6519 14 10.6963C14 10.7654 13.9883 10.8321 13.9649 10.8963C13.9416 10.9605 13.9065 11.0198 13.8597 11.0741L11.4753 13.7852C11.413 13.8543 11.3403 13.9074 11.2571 13.9444C11.174 13.9815 11.0883 14 11 14ZM10.2597 10.4815H11.7403L11.2727 9.59259H10.7273L10.2597 10.4815ZM10.6883 13.0519V11.0741H8.95844L10.6883 13.0519ZM11.3117 13.0519L13.0416 11.0741H11.3117V13.0519ZM12.4338 10.4815H13.2597L12.7922 9.59259H11.9662L12.4338 10.4815ZM8.74026 10.4815H9.56623L10.0338 9.59259H9.20779L8.74026 10.4815Z" fill="currentColor" stroke="currentColor" stroke-width="0.3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

36
packages/nc-gui/components/account/Token.vue

@ -2,7 +2,7 @@
import type { VNodeRef } from '@vue/runtime-core'
import { message } from 'ant-design-vue'
import type { ApiTokenType, RequestParams } from 'nocodb-sdk'
import { extractSdkResponseErrorMsg, ref, useApi, useCopy, useNuxtApp } from '#imports'
import { extractSdkResponseErrorMsg, isEeUI, ref, useApi, useCopy, useNuxtApp } from '#imports'
const { api, isLoading } = useApi()
@ -164,21 +164,25 @@ const handleCancel = () => {
<div class="max-w-[810px] mx-auto p-4" data-testid="nc-token-list">
<div class="py-2 flex gap-4 items-center justify-between">
<h6 class="text-2xl my-4 text-left font-bold">{{ $t('title.apiTokens') }}</h6>
<NcButton
:disabled="showNewTokenModal"
class="!rounded-md"
data-testid="nc-token-create"
size="middle"
type="primary"
@click="showNewTokenModal = true"
>
<span class="hidden md:block">
{{ $t('title.addNewToken') }}
</span>
<span class="flex items-center justify-center md:hidden">
<component :is="iconMap.plus" />
</span>
</NcButton>
<NcTooltip :disabled="!(isEeUI && tokens.length)">
<template #title>{{ $t('labels.tokenLimit') }}</template>
<NcButton
:disabled="showNewTokenModal || (isEeUI && tokens.length)"
class="!rounded-md"
data-testid="nc-token-create"
size="middle"
type="primary"
tooltip="bottom"
@click="showNewTokenModal = true"
>
<span class="hidden md:block">
{{ $t('title.addNewToken') }}
</span>
<span class="flex items-center justify-center md:hidden">
<component :is="iconMap.plus" />
</span>
</NcButton>
</NcTooltip>
</div>
<span>{{ $t('msg.apiTokenCreate') }}</span>
<div class="w-full mt-5 rounded-md h-136 overflow-y-scroll">

6
packages/nc-gui/components/cell/Checkbox.vue

@ -40,6 +40,8 @@ const isGallery = inject(IsGalleryInj, ref(false))
const readOnly = inject(ReadonlyInj)
const isExpandedFormOpen = inject(IsExpandedFormOpenInj, ref(false))
const checkboxMeta = computed(() => {
return {
icon: {
@ -82,8 +84,8 @@ useSelectedCellKeyupListener(active, (e) => {
<div
class="flex cursor-pointer w-full h-full items-center"
:class="{
'justify-center': !isForm || !isGallery,
'w-full flex-start': isForm || isGallery,
'w-full flex-start pl-2': isForm || isGallery || isExpandedFormOpen,
'w-full justify-center': !isForm && !isGallery && !isExpandedFormOpen,
'nc-cell-hover-show': !vModel && !readOnly,
'opacity-0': readOnly && !vModel,
}"

6
packages/nc-gui/components/cell/MultiSelect.vue

@ -142,8 +142,8 @@ const selectedTitles = computed(() =>
}
return 0
})
: modelValue.split(',')
: modelValue
: modelValue.split(',').map((el) => el.trim())
: modelValue.map((el) => el.trim())
: [],
)
@ -255,7 +255,7 @@ async function addIfMissingAndSave() {
}
// Mysql escapes single quotes with backslash so we keep quotes but others have to unescaped
if (!isMysql(column.value.source_id)) {
if (!isMysql(column.value.source_id) && !isPg(column.value.source_id)) {
updatedColMeta.cdf = updatedColMeta.cdf.replace(/''/g, "'")
}
}

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

@ -102,7 +102,7 @@ const hasEditRoles = computed(() => isUIAllowed('dataEdit'))
const editAllowed = computed(() => (hasEditRoles.value || isForm.value) && active.value)
const vModel = computed({
get: () => tempSelectedOptState.value ?? modelValue,
get: () => tempSelectedOptState.value ?? modelValue?.trim(),
set: (val) => {
if (val && isNewOptionCreateEnabled.value && (options.value ?? []).every((op) => op.title !== val)) {
tempSelectedOptState.value = val
@ -183,7 +183,7 @@ async function addIfMissingAndSave() {
}
// Mysql escapes single quotes with backslash so we keep quotes but others have to unescaped
if (!isMysql(column.value.source_id)) {
if (!isMysql(column.value.source_id) && !isPg(column.value.source_id)) {
updatedColMeta.cdf = updatedColMeta.cdf.replace(/''/g, "'")
}
}

4
packages/nc-gui/components/dashboard/TreeView/ViewsList.vue

@ -38,7 +38,7 @@ const { $e } = useNuxtApp()
const { t } = useI18n()
const { viewsByTable, activeView, recentViews } = storeToRefs(useViewsStore())
const { viewsByTable, activeView, allRecentViews } = storeToRefs(useViewsStore())
const { navigateToTable } = useTablesStore()
@ -239,7 +239,7 @@ async function onRename(view: ViewType, originalTitle?: string, undo = false) {
})
}
// update view name in recent views
recentViews.value = recentViews.value.map((rv) => {
allRecentViews.value = allRecentViews.value.map((rv) => {
if (rv.viewId === view.id && rv.tableID === view.fk_model_id) {
rv.viewName = view.title
}

9
packages/nc-gui/components/dashboard/settings/DataSources.vue

@ -87,9 +87,11 @@ const deleteBase = async () => {
await loadProject(base.value.id as string, true)
} catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e))
} finally {
// TODO @mertmit
refreshCommandPalette()
}
}
const toggleBase = async (source: BaseType, state: boolean) => {
try {
if (!state && sources.value.filter((src) => src.enabled).length < 2) {
@ -105,6 +107,8 @@ const toggleBase = async (source: BaseType, state: boolean) => {
await loadProject(base.value.id as string, true)
} catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e))
} finally {
refreshCommandPalette()
}
}
@ -133,6 +137,8 @@ const moveBase = async (e: any) => {
await loadBases()
} catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e))
} finally {
await refreshCommandPalette()
}
}
@ -210,6 +216,7 @@ watch(
}
break
}
refreshCommandPalette()
},
{ immediate: true },
)

4
packages/nc-gui/components/dashboard/settings/data-sources/CreateBase.vue

@ -39,6 +39,8 @@ const { base } = storeToRefs(baseStore)
const { loadProjectTables } = useTablesStore()
const { refreshCommandPalette } = useCommandPalette()
const _projectId = inject(ProjectIdInj, undefined)
const baseId = computed(() => _projectId?.value ?? base.value?.id)
@ -293,6 +295,8 @@ const createSource = async () => {
)
} catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e))
} finally {
refreshCommandPalette()
}
}

4
packages/nc-gui/components/dashboard/settings/data-sources/EditBase.vue

@ -39,6 +39,8 @@ const { base } = storeToRefs(baseStore)
const _projectId = inject(ProjectIdInj, undefined)
const baseId = computed(() => _projectId?.value ?? base.value?.id)
const { refreshCommandPalette } = useCommandPalette()
const useForm = Form.useForm
const testSuccess = ref(false)
@ -235,6 +237,8 @@ const editBase = async () => {
emit('close')
} catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e))
} finally {
refreshCommandPalette()
}
}

3
packages/nc-gui/components/dlg/ProjectDuplicate.vue

@ -11,6 +11,8 @@ const props = defineProps<{
const emit = defineEmits(['update:modelValue'])
const { refreshCommandPalette } = useCommandPalette()
const { api } = useApi()
const dialogShow = useVModel(props, 'modelValue', emit)
@ -60,6 +62,7 @@ const _duplicate = async () => {
message.error(await extractSdkResponseErrorMsg(e))
} finally {
isLoading.value = false
refreshCommandPalette()
dialogShow.value = false
}
}

3
packages/nc-gui/components/dlg/TableCreate.vue

@ -34,6 +34,8 @@ const { isMysql, isMssql, isPg } = useBase()
const { loadProjectTables, addTable } = useTablesStore()
const { refreshCommandPalette } = useCommandPalette()
const { table, createTable, generateUniqueTitle, tables, base } = useTableNew({
async onTableCreate(table) {
// await loadProject(props.baseId)
@ -120,6 +122,7 @@ const _createTable = async () => {
setTimeout(() => {
creating.value = false
}, 500)
refreshCommandPalette()
}
}

3
packages/nc-gui/components/dlg/TableDuplicate.vue

@ -14,6 +14,8 @@ const { api } = useApi()
const dialogShow = useVModel(props, 'modelValue', emit)
const { refreshCommandPalette } = useCommandPalette()
const options = ref({
includeData: true,
includeViews: true,
@ -41,6 +43,7 @@ const _duplicate = async () => {
} finally {
isLoading.value = false
dialogShow.value = false
refreshCommandPalette()
}
}

10
packages/nc-gui/components/dlg/TableRename.vue

@ -46,6 +46,8 @@ const baseStore = useBase()
const { loadTables, isMysql, isMssql, isPg } = baseStore
const { tables, base } = storeToRefs(baseStore)
const { allRecentViews } = storeToRefs(useViewsStore())
const { refreshCommandPalette } = useCommandPalette()
const { addUndo, defineProjectScope } = useUndoRedo()
@ -158,6 +160,14 @@ const renameTable = async (undo = false, disableTitleDiffCheck?: boolean | undef
await loadTables()
// update recent views if default view is renamed
allRecentViews.value = allRecentViews.value.map((v) => {
if (v.tableID === tableMeta.id && v.isDefault) {
v.viewName = formState.title
}
return v
})
// update metas
const newMeta = await $api.dbTable.read(tableMeta.id as string)
await setMeta(newMeta)

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

@ -42,6 +42,8 @@ const { getMeta } = useMetas()
const { viewsByTable } = storeToRefs(useViewsStore())
const { refreshCommandPalette } = useCommandPalette()
const { selectedViewId, groupingFieldColumnId, geoDataFieldColumnId, tableId } = toRefs(props)
const meta = ref<TableType | undefined>()
@ -171,6 +173,8 @@ async function onSubmit() {
}
} catch (e: any) {
message.error(e.message)
} finally {
refreshCommandPalette()
}
vModel.value = false

4
packages/nc-gui/components/dlg/ViewDelete.vue

@ -15,6 +15,8 @@ const props = defineProps<Props>()
const emits = defineEmits<Emits>()
const { refreshCommandPalette } = useCommandPalette()
const { view } = props
const vModel = useVModel(props, 'modelValue', emits)
@ -35,6 +37,8 @@ async function onDelete() {
$e('a:view:delete', { view: props.view.type })
} catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e))
} finally {
refreshCommandPalette()
}
// telemetry event

4
packages/nc-gui/components/dlg/WorkspaceDelete.vue

@ -13,6 +13,8 @@ const { deleteWorkspace: _deleteWorkspace, loadWorkspaces, navigateToWorkspace }
const { workspaces, workspacesList } = storeToRefs(workspaceStore)
const { refreshCommandPalette } = useCommandPalette()
const workspace = computed(() => workspaces.value.get(props.workspaceId))
const onDelete = async () => {
@ -29,6 +31,8 @@ const onDelete = async () => {
await navigateToWorkspace(workspacesList.value?.[0]?.id)
} catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e))
} finally {
refreshCommandPalette()
}
}
</script>

10
packages/nc-gui/components/general/WorkspaceIcon.vue

@ -1,6 +1,6 @@
<script lang="ts" setup>
import type { WorkspaceType } from 'nocodb-sdk'
import { stringToColor } from '~/utils/colorsUtils'
import { isColorDark, stringToColor } from '~/utils/colorsUtils'
const props = defineProps<{
workspace: WorkspaceType | undefined
@ -26,7 +26,13 @@ const size = computed(() => props.size || 'medium')
:style="{ backgroundColor: workspaceColor }"
>
<template v-if="!props.hideLabel">
<div class="font-black">
<div
class="font-semibold"
:class="{
'text-white': isColorDark(workspaceColor),
'text-black': !isColorDark(workspaceColor),
}"
>
{{ props.workspace?.title?.slice(0, 2) }}
</div>
</template>

1
packages/nc-gui/components/nc/Badge.vue

@ -22,6 +22,7 @@ const props = withDefaults(
'border-orange-500 bg-orange-100': props.color === 'orange',
'border-yellow-500 bg-yellow-100': props.color === 'yellow',
'border-red-500 bg-red-100': props.color === 'red',
'border-maroon-500 bg-maroon-50': props.color === 'maroon',
'border-gray-300': !props.color,
'border-1': props.border,
'h-6': props.size === 'sm',

23
packages/nc-gui/components/project/AccessSettings.vue

@ -1,6 +1,6 @@
<script lang="ts" setup>
import type { OrgUserRoles, WorkspaceUserRoles } from 'nocodb-sdk'
import { OrderedProjectRoles, ProjectRoles, WorkspaceRolesToProjectRoles } from 'nocodb-sdk'
import type { WorkspaceUserRoles } from 'nocodb-sdk'
import { OrderedProjectRoles, OrgUserRoles, ProjectRoles, WorkspaceRolesToProjectRoles, extractRolesObj } from 'nocodb-sdk'
import InfiniteLoading from 'v3-infinite-loading'
import { isEeUI, storeToRefs, timeAgo } from '#imports'
@ -8,7 +8,9 @@ const basesStore = useBases()
const { getProjectUsers, createProjectUser, updateProjectUser, removeProjectUser } = basesStore
const { activeProjectId } = storeToRefs(basesStore)
const { baseRoles } = useRoles()
const { orgRoles, baseRoles } = useRoles()
const isSuper = computed(() => orgRoles.value?.[OrgUserRoles.SUPER_ADMIN])
interface Collaborators {
id: string
@ -44,11 +46,12 @@ const loadCollaborators = async () => {
...users.map((user: any) => ({
...user,
base_roles: user.roles,
roles:
user.roles ??
(user.workspace_roles
? WorkspaceRolesToProjectRoles[user.workspace_roles as WorkspaceUserRoles] ?? ProjectRoles.NO_ACCESS
: ProjectRoles.NO_ACCESS),
roles: extractRolesObj(user.main_roles)?.[OrgUserRoles.SUPER_ADMIN]
? OrgUserRoles.SUPER_ADMIN
: user.roles ??
(user.workspace_roles
? WorkspaceRolesToProjectRoles[user.workspace_roles as WorkspaceUserRoles] ?? ProjectRoles.NO_ACCESS
: ProjectRoles.NO_ACCESS),
})),
]
} catch (e: any) {
@ -135,7 +138,9 @@ onMounted(async () => {
const currentRoleIndex = OrderedProjectRoles.findIndex(
(role) => baseRoles.value && Object.keys(baseRoles.value).includes(role),
)
if (currentRoleIndex !== -1) {
if (isSuper.value) {
accessibleRoles.value = OrderedProjectRoles.slice(1)
} else if (currentRoleIndex !== -1) {
accessibleRoles.value = OrderedProjectRoles.slice(currentRoleIndex + 1)
}
} catch (e: any) {

1
packages/nc-gui/components/roles/Badge.vue

@ -56,6 +56,7 @@ const roleProperties = computed(() => {
'text-orange-700': roleProperties.color === 'orange',
'text-yellow-700': roleProperties.color === 'yellow',
'text-red-700': roleProperties.color === 'red',
'text-maroon-700': roleProperties.color === 'maroon',
'text-gray-300': !roleProperties.color,
sizeSelect,
}"

5
packages/nc-gui/components/smartsheet/Cell.vue

@ -63,7 +63,7 @@ interface Props {
const props = defineProps<Props>()
const emit = defineEmits(['update:modelValue', 'save', 'navigate', 'update:editEnabled', 'update:value'])
const emit = defineEmits(['update:modelValue', 'save', 'navigate', 'update:editEnabled', 'update:cdf'])
const column = toRef(props, 'column')
@ -118,8 +118,7 @@ const vModel = computed({
},
set: (val) => {
if (isEditColumnMenu.value) {
column.value.cdf = val
emit('update:value', val)
emit('update:cdf', val)
} else if (val !== props.modelValue) {
currentRow.value.rowMeta.changed = true
emit('update:modelValue', val)

55
packages/nc-gui/components/smartsheet/column/DefaultValue.vue

@ -5,59 +5,52 @@ import { iconMap } from '#imports'
const props = defineProps<{
value: any
}>()
const emit = defineEmits(['update:value'])
const emits = defineEmits(['update:value'])
const meta = inject(MetaInj, ref())
provide(EditColumnInj, ref(true))
const vModel = useVModel(props, 'value', emit)
const vModel = useVModel(props, 'value', emits)
const rowRef = ref({
row: {},
oldRow: {},
rowMeta: {
isUpdatedFromCopyNPaste: [vModel?.value.title],
isUpdatedFromCopyNPaste: [vModel.value?.title],
},
})
const cdfValue = computed({
get: () => {
if (vModel.value.uidt === UITypes.MultiSelect || vModel.value.uidt === UITypes.SingleSelect) {
return (vModel.value.cdf ?? '').replaceAll("'", '')
} else if (
vModel.value.uidt === UITypes.SingleLineText ||
vModel.value.uidt === UITypes.LongText ||
vModel.value.uidt === UITypes.Email ||
vModel.value.uidt === UITypes.URL ||
vModel.value.uidt === UITypes.JSON ||
vModel.value.uidt === UITypes.DateTime ||
vModel.value.uidt === UITypes.Time ||
vModel.value.uidt === UITypes.Year ||
vModel.value.uidt === UITypes.Date
) {
return (vModel.value.cdf ?? '').replace(/^'/, '').replace(/'$/, '')
}
return vModel.value.cdf
},
set: (value) => {
vModel.value.cdf = value
},
})
useProvideSmartsheetRowStore(meta, rowRef)
const cdfValue = ref<string | null>(null)
useProvideSmartsheetRowStore(vModel, rowRef)
const updateCdfValue = (cdf: string | null) => {
vModel.value.cdf = cdf
cdfValue.value = vModel.value.cdf
}
onMounted(() => {
updateCdfValue(vModel.value?.cdf ? vModel.value.cdf : null)
})
</script>
<template>
<div class="!my-3 text-xs">{{ $t('placeholder.defaultValue') }}</div>
<div class="flex flex-row gap-2">
<div class="border-1 flex items-center w-full px-3 my-[-4px] border-gray-300 rounded-md">
<LazySmartsheetCell :column="vModel" :model-value="cdfValue" :edit-enabled="true" class="!border-none" />
<LazySmartsheetCell
:model-value="cdfValue"
:column="vModel"
:edit-enabled="true"
class="!border-none"
@update:cdf="updateCdfValue"
/>
<component
:is="iconMap.close"
v-if="vModel.uidt !== UITypes.Year"
v-if="![UITypes.Year, UITypes.SingleSelect, UITypes.MultiSelect].includes(vModel.uidt)"
class="w-4 h-4 cursor-pointer rounded-full !text-black-500 text-gray-500 cursor-pointer hover:bg-gray-50"
@click="cdfValue = null"
/>
</div>
</div>
</template>
<style scoped></style>

25
packages/nc-gui/components/smartsheet/column/SelectOptions.vue

@ -21,7 +21,7 @@ const emit = defineEmits(['update:value'])
const vModel = useVModel(props, 'value', emit)
const { setAdditionalValidations, validateInfos, isMysql } = useColumnCreateStoreOrThrow()
const { setAdditionalValidations, validateInfos, isMysql, isPg } = useColumnCreateStoreOrThrow()
// const { base } = storeToRefs(useBase())
@ -101,9 +101,9 @@ onMounted(() => {
}
if (vModel.value.cdf) {
// Mysql escapes single quotes with backslash so we keep quotes but others have to unescaped
if (!isMysql.value) {
vModel.value.cdf = vModel.value.cdf.replace(/''/g, "'")
const fndDefaultOption = options.value.find((el) => el.title === vModel.value.cdf)
if (!fndDefaultOption) {
vModel.value.cdf = vModel.value.cdf.replace(/^'/, '').replace(/'$/, '')
}
}
@ -181,23 +181,6 @@ const undoRemoveRenderedOption = (index: number) => {
}
}
// focus last created input
// watch(inputs, () => {
// if (inputs.value?.$el) {
// inputs.value.$el.focus()
// }
// })
// Removes the Select Option from cdf if the option is removed
watch(vModel.value, (next) => {
const cdfs = (next.cdf ?? '').split(',')
const values = (next.colOptions.options ?? []).map((col) => {
return col.title.replace(/^'/, '').replace(/'$/, '')
})
const newCdf = cdfs.filter((c: string) => values.includes(c)).join(',')
next.cdf = newCdf.length === 0 ? null : newCdf
})
const loadListData = async ($state: any) => {
if (loadedOptionCount.value === options.value.length) {
$state.complete()

3
packages/nc-gui/components/smartsheet/expanded-form/Comments.vue

@ -194,7 +194,7 @@ watch(commentsWrapperEl, () => {
</div>
<div v-if="hasEditPermission" class="p-2 bg-gray-50 gap-2 flex">
<div class="h-14 flex flex-row w-full bg-white py-2.75 px-1.5 items-center rounded-xl border-1 border-gray-200">
<GeneralUserIcon size="base" class="!w-10" />
<GeneralUserIcon size="base" class="!w-10" :email="user?.email" />
<a-input
v-model:value="comment"
class="!rounded-lg border-1 bg-white !px-2.5 !py-2 !border-gray-200 nc-comment-box !outline-none"
@ -230,6 +230,7 @@ watch(commentsWrapperEl, () => {
<div class="flex justify-between">
<div class="flex items-center gap-2">
<GeneralUserIcon size="base" :email="log.user" />
<div class="flex flex-col">
<span class="truncate font-bold max-w-50">
{{ log.display_name ?? log.user.split('@')[0].slice(0, 2) ?? 'Shared source' }}

118
packages/nc-gui/components/smartsheet/expanded-form/index.vue

@ -67,6 +67,10 @@ const state = toRef(props, 'state')
const meta = toRef(props, 'meta')
const islastRow = toRef(props, 'lastRow')
const isFirstRow = toRef(props, 'firstRow')
const route = useRoute()
const router = useRouter()
@ -76,6 +80,8 @@ const isPublic = inject(IsPublicInj, ref(false))
// to check if a expanded form which is not yet saved exist or not
const isUnsavedFormExist = ref(false)
const isRecordLinkCopied = ref(false)
const { isUIAllowed } = useRoles()
const reloadTrigger = inject(ReloadRowDataHookInj, createEventHook())
@ -120,6 +126,7 @@ const {
syncLTARRefs,
save: _save,
loadCommentsAndLogs,
clearColumns,
} = useProvideExpandedFormStore(meta, row)
const duplicatingRowInProgress = ref(false)
@ -143,8 +150,12 @@ const isExpanded = useVModel(props, 'modelValue', emits, {
})
const onClose = () => {
if (_row.value?.rowMeta?.new) emits('cancel')
isExpanded.value = false
if (changedColumns.value.size > 0) {
isCloseModalOpen.value = true
} else {
if (_row.value?.rowMeta?.new) emits('cancel')
isExpanded.value = false
}
}
const onDuplicateRow = () => {
@ -180,35 +191,57 @@ const save = async () => {
}
const isPreventChangeModalOpen = ref(false)
const isCloseModalOpen = ref(false)
const discardPreventModal = () => {
emits('next')
isPreventChangeModalOpen.value = false
// when user click on next or previous button
if (isPreventChangeModalOpen.value) {
emits('next')
if (_row.value?.rowMeta?.new) emits('cancel')
isPreventChangeModalOpen.value = false
}
// when user click on close button
if (isCloseModalOpen.value) {
isCloseModalOpen.value = false
if (_row.value?.rowMeta?.new) emits('cancel')
isExpanded.value = false
}
// clearing all new modifed change on close
clearColumns()
}
const onNext = async () => {
if (changedColumns.value.size > 0) {
isPreventChangeModalOpen.value = true
} else {
emits('next')
return
}
emits('next')
}
const copyRecordUrl = () => {
copy(
const copyRecordUrl = async () => {
await copy(
encodeURI(
`${dashboardUrl?.value}#/${route.params.typeOrId}/${route.params.projectId}/${meta.value?.id}${
`${dashboardUrl?.value}#/${route.params.typeOrId}/${route.params.baseId}/${meta.value?.id}${
props.view ? `/${props.view.title}` : ''
}?rowId=${primaryKey.value}`,
),
)
message.success('Copied to clipboard')
isRecordLinkCopied.value = true
}
const saveChanges = async () => {
isUnsavedFormExist.value = false
await save()
emits('next')
if (isPreventChangeModalOpen.value) {
isUnsavedFormExist.value = false
await save()
emits('next')
isPreventChangeModalOpen.value = false
}
if (isCloseModalOpen.value) {
isCloseModalOpen.value = false
await save()
isExpanded.value = false
}
}
const reloadParentRowHook = inject(ReloadRowDataHookInj, createEventHook())
@ -235,6 +268,7 @@ provide(IsExpandedFormOpenInj, isExpanded)
const cellWrapperEl = ref()
onMounted(async () => {
isRecordLinkCopied.value = false
isLoading.value = true
if (props.loadRow) {
await _loadRow()
@ -369,6 +403,8 @@ watch(rowId, async (nRow) => {
const showRightSections = computed(() => {
return !isNew.value && commentsDrawer.value && isUIAllowed('commentList')
})
const preventModalStatus = computed(() => isCloseModalOpen.value || isPreventChangeModalOpen.value)
</script>
<script lang="ts">
@ -395,7 +431,7 @@ export default {
<div class="flex gap-2">
<NcButton
v-if="props.showNextPrevIcons"
:disabled="props.firstRow"
:disabled="isFirstRow"
type="secondary"
class="nc-prev-arrow !w-10"
@click="$emit('prev')"
@ -404,7 +440,7 @@ export default {
</NcButton>
<NcButton
v-if="props.showNextPrevIcons"
:disabled="props.lastRow"
:disabled="islastRow"
type="secondary"
class="nc-next-arrow !w-10"
@click="onNext"
@ -423,6 +459,18 @@ export default {
<div v-if="row.rowMeta?.new" class="flex items-center truncate font-bold text-gray-800 text-xl">New Record</div>
</div>
<div class="flex gap-2">
<NcButton
v-if="!isNew"
type="secondary"
class="!xs:hidden text-gray-700"
@click="!isNew ? copyRecordUrl() : () => {}"
>
<div v-e="['c:row-expand:copy-url']" data-testid="nc-expanded-form-copy-url" class="flex gap-2 items-center">
<component :is="iconMap.check" v-if="isRecordLinkCopied" class="cursor-pointer nc-duplicate-row" />
<component :is="iconMap.link" v-else class="cursor-pointer nc-duplicate-row" />
{{ isRecordLinkCopied ? $t('labels.copiedRecordURL') : $t('labels.copyRecordURL') }}
</div>
</NcButton>
<NcDropdown v-if="!isNew">
<NcButton type="secondary" class="nc-expand-form-more-actions w-10">
<GeneralIcon icon="threeDotVertical" class="text-md text-gray-700" />
@ -435,10 +483,10 @@ export default {
{{ $t('general.reload') }}
</div>
</NcMenuItem>
<NcMenuItem v-if="!isNew" class="text-gray-700" @click="!isNew ? copyRecordUrl() : () => {}">
<NcMenuItem v-if="!isNew && isMobileMode" class="text-gray-700" @click="!isNew ? copyRecordUrl() : () => {}">
<div v-e="['c:row-expand:copy-url']" data-testid="nc-expanded-form-copy-url" class="flex gap-2 items-center">
<component :is="iconMap.link" class="cursor-pointer nc-duplicate-row" />
Copy record URL
{{ $t('labels.copyRecordURL') }}
</div>
</NcMenuItem>
<NcMenuItem
@ -452,7 +500,9 @@ export default {
class="flex gap-2 items-center"
>
<component :is="iconMap.copy" class="cursor-pointer nc-duplicate-row" />
Duplicate record
<span class="-ml-0.25">
{{ $t('labels.duplicateRecord') }}
</span>
</div>
</NcMenuItem>
<NcDivider v-if="isUIAllowed('dataEdit') && !isNew" />
@ -463,7 +513,9 @@ export default {
@click="!isNew && onDeleteRowClick()"
>
<component :is="iconMap.delete" data-testid="nc-expanded-form-delete" class="cursor-pointer nc-delete-row" />
Delete record
<span class="-ml-0.5">
{{ $t('activity.deleteRecord') }}
</span>
</NcMenuItem>
</NcMenu>
</template>
@ -524,15 +576,15 @@ export default {
:col-id="col.id"
:data-testid="`nc-expand-col-${col.title}`"
>
<div class="flex items-start flex-row xs:(flex-col w-full) nc-expanded-cell min-h-10">
<div class="w-[12rem] xs:(w-full) mt-1.5 !h-[35px]">
<div class="flex items-start flex-row sm:(gap-x-6) xs:(flex-col w-full) nc-expanded-cell min-h-10">
<div class="w-[12rem] xs:(w-full) mt-0.25 !h-[35px]">
<LazySmartsheetHeaderVirtualCell
v-if="isVirtualCol(col)"
class="nc-expanded-cell-header !text-gray-600"
class="nc-expanded-cell-header h-full !text-gray-500"
:column="col"
/>
<LazySmartsheetHeaderCell v-else class="nc-expanded-cell-header !text-gray-600" :column="col" />
<LazySmartsheetHeaderCell v-else class="nc-expanded-cell-header !text-gray-500" :column="col" />
</div>
<template v-if="isLoading">
@ -706,7 +758,6 @@ export default {
<template #entity-preview>
<span>
<div class="flex flex-row items-center py-2.25 px-2.5 bg-gray-50 rounded-lg text-gray-700 mb-4">
<component :is="iconMap.record" class="nc-view-icon" />
<div class="capitalize text-ellipsis overflow-hidden select-none w-full pl-1.75 break-keep whitespace-nowrap">
{{ displayValue }}
</div>
@ -716,16 +767,19 @@ export default {
</GeneralDeleteModal>
<!-- Prevent unsaved change modal -->
<NcModal v-model:visible="isPreventChangeModalOpen" size="small">
<template #header>
<div class="flex flex-row items-center gap-x-2">Do you want to save the changes ?</div>
</template>
<div class="mt-2">
<div class="flex flex-row justify-end gap-x-2 mt-6">
<NcButton type="secondary" @click="discardPreventModal">{{ $t('general.quit') }}</NcButton>
<NcModal v-model:visible="preventModalStatus" size="small">
<div class="">
<div class="flex flex-row items-center gap-x-2 text-base font-bold">
{{ $t('tooltip.saveChanges') }}
</div>
<div class="flex font-medium mt-2">
{{ $t('activity.doYouWantToSaveTheChanges') }}
</div>
<div class="flex flex-row justify-end gap-x-2 mt-5">
<NcButton type="secondary" @click="discardPreventModal">{{ $t('labels.discard') }}</NcButton>
<NcButton key="submit" type="primary" label="Rename Table" loading-label="Renaming Table" @click="saveChanges">
{{ $t('activity.saveAndQuit') }}
{{ $t('tooltip.saveChanges') }}
</NcButton>
</div>
</div>

36
packages/nc-gui/components/smartsheet/grid/index.vue

@ -68,6 +68,8 @@ const {
bulkUpdateRows,
bulkUpdateView,
optimisedQuery,
islastRow,
isFirstRow,
} = useViewData(meta, view, xWhere)
const rowHeight = computed(() => {
@ -183,6 +185,30 @@ onMounted(() => {
if (coreWrapperRef.value) resizeObserver.observe(coreWrapperRef.value)
})
})
const goToNextRow = () => {
const currentIndex = getExpandedRowIndex()
/* when last index of current page is reached we should move to next page */
if (!paginationData.value.isLastPage && currentIndex === paginationData.value.pageSize) {
const nextPage = paginationData.value?.page ? paginationData.value?.page + 1 : 1
changePage(nextPage)
}
navigateToSiblingRow(NavigateDir.NEXT)
}
const goToPreviousRow = () => {
const currentIndex = getExpandedRowIndex()
/* when first index of current page is reached and then clicked back
previos page should be loaded
*/
if (!paginationData.value.isFirstPage && currentIndex === 1) {
const nextPage = paginationData.value?.page ? paginationData.value?.page - 1 : 1
changePage(nextPage)
}
navigateToSiblingRow(NavigateDir.PREV)
}
</script>
<template>
@ -226,7 +252,6 @@ onMounted(() => {
:expand-form="expandForm"
:view-width="viewWidth"
/>
<Suspense>
<LazySmartsheetExpandedForm
v-if="expandedFormRow && expandedFormDlg"
@ -238,7 +263,6 @@ onMounted(() => {
@update:model-value="addRowExpandOnClose(expandedFormRow)"
/>
</Suspense>
<SmartsheetExpandedForm
v-if="expandedFormOnRowIdDlg"
v-model="expandedFormOnRowIdDlg"
@ -248,10 +272,10 @@ onMounted(() => {
:row-id="routeQuery.rowId"
:view="view"
show-next-prev-icons
:first-row="getExpandedRowIndex() === 0"
:last-row="getExpandedRowIndex() === data.length - 1"
@next="navigateToSiblingRow(NavigateDir.NEXT)"
@prev="navigateToSiblingRow(NavigateDir.PREV)"
:first-row="isFirstRow"
:last-row="islastRow"
@next="goToNextRow()"
@prev="goToPreviousRow()"
/>
<Suspense>

2
packages/nc-gui/components/smartsheet/header/VirtualCellIcon.ts

@ -39,7 +39,7 @@ const renderIcon = (column: ColumnType, relationColumn?: ColumnType) => {
case UITypes.QrCode:
return { icon: iconMap.qrCode, color: 'text-grey' }
case UITypes.Barcode:
return { icon: iconMap.qrCode, color: 'text-grey' }
return { icon: iconMap.barCode, color: 'text-grey' }
case UITypes.Lookup:
switch ((relationColumn?.colOptions as LinkToAnotherRecordType)?.type) {
case RelationTypes.MANY_TO_MANY:

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

@ -1,5 +1,4 @@
<script setup lang="ts">
import type ColumnFilter from './ColumnFilter.vue'
import {
ActiveViewInj,
IsLockedInj,
@ -11,10 +10,9 @@ import {
useSmartsheetStoreOrThrow,
useViewFilters,
watch,
iconMap,
} from '#imports'
import FilterIcon from '~icons/nc-icons/filter'
const isLocked = inject(IsLockedInj, ref(false))
const activeView = inject(ActiveViewInj, ref())
@ -63,8 +61,7 @@ useMenuCloseOnEsc(open)
<div :class="{ 'nc-active-btn': filtersLength }">
<a-button v-e="['c:filter']" class="nc-filter-menu-btn nc-toolbar-btn txt-sm" :disabled="isLocked">
<div class="flex items-center gap-2">
<FilterIcon class="h-4 w-4" />
<component :is="iconMap.filter" class="h-4 w-4" />
<!-- Filter -->
<span v-if="!isMobileMode" class="text-capitalize !text-sm font-medium">{{ $t('activity.filter') }}</span>

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

@ -2,8 +2,8 @@
import type { ColumnType, GalleryType, KanbanType } from 'nocodb-sdk'
import { UITypes, ViewTypes, isVirtualCol } from 'nocodb-sdk'
import Draggable from 'vuedraggable'
import type { SelectProps } from 'ant-design-vue'
import FieldsIcon from '~icons/nc-icons/fields'
import {
ActiveViewInj,
@ -300,7 +300,7 @@ useMenuCloseOnEsc(open)
icon="creditCard"
class="h-4 w-4"
/>
<FieldsIcon v-else class="h-4 w-4" />
<component :is="iconMap.fields" v-else class="h-4 w-4" />
<!-- Fields -->
<span v-if="!isMobileMode" class="text-capitalize text-sm font-medium">

3
packages/nc-gui/components/smartsheet/toolbar/GroupByMenu.vue

@ -15,7 +15,6 @@ import {
useNuxtApp,
useSmartsheetStoreOrThrow,
} from '#imports'
import GroupIcon from '~icons/nc-icons/group'
const groupingUidt = [
UITypes.SingleSelect,
@ -220,7 +219,7 @@ onMounted(async () => {
<div :class="{ 'nc-active-btn': groupedByColumnIds?.length }">
<a-button v-e="['c:group-by']" class="nc-group-by-menu-btn nc-toolbar-btn" :disabled="isLocked">
<div class="flex items-center gap-2">
<GroupIcon class="h-4 w-4" />
<component :is="iconMap.group" class="h-4 w-4" />
<!-- Group By -->
<span v-if="!isMobileMode" class="text-capitalize !text-sm font-medium">{{ $t('activity.groupBy') }}</span>

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

@ -17,8 +17,6 @@ import {
watch,
} from '#imports'
import SortIcon from '~icons/nc-icons/sort'
const meta = inject(MetaInj, ref())
const view = inject(ActiveViewInj, ref())
const isLocked = inject(IsLockedInj, ref(false))
@ -115,7 +113,7 @@ watch(open, () => {
<div :class="{ 'nc-active-btn': sorts?.length }">
<a-button v-e="['c:sort']" class="nc-sort-menu-btn nc-toolbar-btn" :disabled="isLocked">
<div class="flex items-center gap-2">
<SortIcon class="h-4 w-4" />
<component :is="iconMap.sort" class="h-4 w-4" />
<!-- Sort -->
<span v-if="!isMobileMode" class="text-capitalize !text-sm font-medium">{{ $t('activity.sort') }}</span>

8
packages/nc-gui/components/virtual-cell/Links.vue

@ -64,6 +64,13 @@ const textVal = computed(() => {
}
})
const toatlRecordsLinked = computed(() => {
if (isForm?.value) {
return state.value?.[colTitle.value]?.length
}
return +value?.value || 0
})
const onAttachRecord = () => {
childListDlg.value = false
listItemsDlg.value = true
@ -126,6 +133,7 @@ const localCellValue = computed<any[]>(() => {
<LazyVirtualCellComponentsListChildItems
v-if="listItemsDlg || childListDlg"
v-model="childListDlg"
:items="toatlRecordsLinked"
:column="relatedTableDisplayColumn"
:cell-value="localCellValue"
@attach-record="onAttachRecord"

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

@ -11,6 +11,8 @@ const isGallery = inject(IsGalleryInj, ref(false))
const qrValue = computed(() => String(cellValue?.value))
const isExpandedFormOpen = inject(IsExpandedFormOpenInj, ref(false))
const tooManyCharsForQrCode = computed(() => qrValue?.value.length > maxNumberOfAllowedCharsForQrValue)
const showQrCode = computed(() => qrValue?.value?.length > 0 && !tooManyCharsForQrCode.value)
@ -66,15 +68,22 @@ const { showEditNonEditableFieldWarning, showClearNonEditableFieldWarning } = us
<div v-if="tooManyCharsForQrCode" class="text-left text-wrap mt-2 text-[#e65100] text-[10px]">
{{ $t('labels.qrCodeValueTooLong') }}
</div>
<img
v-if="showQrCode && rowHeight"
:class="{ 'mx-auto': !isGallery }"
:style="{ height: rowHeight ? `${rowHeight * 1.4}rem` : `1.4rem` }"
:src="qrCode"
:alt="$t('title.qrCode')"
@click="showQrModal"
/>
<img v-else-if="showQrCode" class="mx-auto" :src="qrCode" :alt="$t('title.qrCode')" @click="showQrModal" />
<div
class="pl-2 w-full flex"
:class="{
'flex-start': isExpandedFormOpen,
'justify-center': !isExpandedFormOpen,
}"
>
<img
v-if="showQrCode && rowHeight"
:style="{ height: rowHeight ? `${rowHeight * 1.4}rem` : `1.4rem` }"
:src="qrCode"
:alt="$t('title.qrCode')"
@click="showQrModal"
/>
<img v-else-if="showQrCode" class="mx-auto" :src="qrCode" :alt="$t('title.qrCode')" @click="showQrModal" />
</div>
<div v-if="showEditNonEditableFieldWarning" class="text-left text-wrap mt-2 text-[#e65100] text-xs">
{{ $t('msg.warning.nonEditableFields.computedFieldUnableToClear') }}
</div>

58
packages/nc-gui/components/virtual-cell/barcode/Barcode.vue

@ -15,6 +15,8 @@ const tooManyCharsForBarcode = computed(() => barcodeValue.value.length > maxNum
const modalVisible = ref(false)
const isExpandedFormOpen = inject(IsExpandedFormOpenInj, ref(false))
const showBarcodeModal = () => {
modalVisible.value = true
}
@ -46,31 +48,39 @@ const rowHeight = inject(RowHeightInj, ref(undefined))
>
<JsBarcodeWrapper v-if="showBarcode" :barcode-value="barcodeValue" :barcode-format="barcodeMeta.barcodeFormat" />
</a-modal>
<JsBarcodeWrapper
v-if="showBarcode && rowHeight"
:barcode-value="barcodeValue"
:barcode-format="barcodeMeta.barcodeFormat"
:custom-style="{ height: rowHeight ? `${rowHeight * 1.4}rem` : `1.4rem` }"
@on-click-barcode="showBarcodeModal"
>
<template #barcodeRenderError>
<div class="text-left text-wrap mt-2 text-[#e65100] text-xs" data-testid="barcode-invalid-input-message">
{{ $t('msg.warning.barcode.renderError') }}
</div>
</template>
</JsBarcodeWrapper>
<JsBarcodeWrapper
v-else-if="showBarcode"
:barcode-value="barcodeValue"
:barcode-format="barcodeMeta.barcodeFormat"
@on-click-barcode="showBarcodeModal"
<div
class="flex ml-2 w-full items-center"
:class="{
'justify-start': isExpandedFormOpen,
'justify-center': !isExpandedFormOpen,
}"
>
<template #barcodeRenderError>
<div class="text-left text-wrap mt-2 text-[#e65100] text-xs" data-testid="barcode-invalid-input-message">
{{ $t('msg.warning.barcode.renderError') }}
</div>
</template>
</JsBarcodeWrapper>
<JsBarcodeWrapper
v-if="showBarcode && rowHeight"
:barcode-value="barcodeValue"
:barcode-format="barcodeMeta.barcodeFormat"
:custom-style="{ height: rowHeight ? `${rowHeight * 1.4}rem` : `1.4rem`, width: 40 }"
@on-click-barcode="showBarcodeModal"
>
<template #barcodeRenderError>
<div class="text-left text-wrap mt-2 text-[#e65100] text-xs" data-testid="barcode-invalid-input-message">
{{ $t('msg.warning.barcode.renderError') }}
</div>
</template>
</JsBarcodeWrapper>
<JsBarcodeWrapper
v-else-if="showBarcode"
:barcode-value="barcodeValue"
:barcode-format="barcodeMeta.barcodeFormat"
@on-click-barcode="showBarcodeModal"
>
<template #barcodeRenderError>
<div class="text-left text-wrap mt-2 text-[#e65100] text-xs" data-testid="barcode-invalid-input-message">
{{ $t('msg.warning.barcode.renderError') }}
</div>
</template>
</JsBarcodeWrapper>
</div>
<div v-if="tooManyCharsForBarcode" class="text-left text-wrap mt-2 text-[#e65100] text-xs">
{{ $t('labels.barcodeValueTooLong') }}

6
packages/nc-gui/components/virtual-cell/components/ItemChip.vue

@ -1,4 +1,5 @@
<script lang="ts" setup>
import type { ColumnType } from 'nocodb-sdk'
import { UITypes, isVirtualCol } from 'nocodb-sdk'
import {
ActiveCellInj,
@ -41,12 +42,13 @@ const isLocked = inject(IsLockedInj, ref(false))
const { open } = useExpandedFormDetached()
function openExpandedForm() {
if (!readOnly.value && !isLocked.value && !readonlyProp) {
const rowId = extractPkFromRow(item, relatedTableMeta.value.columns as ColumnType[])
if (!readOnly.value && !isLocked.value && !readonlyProp && rowId) {
open({
isOpen: true,
row: { row: item, rowMeta: {}, oldRow: { ...item } },
meta: relatedTableMeta.value,
loadRow: true,
rowId,
useMetaFields: true,
})
}

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

@ -18,7 +18,14 @@ import {
useVModel,
} from '#imports'
const props = defineProps<{ modelValue?: boolean; cellValue: any; column: any }>()
interface Prop {
modelValue?: boolean
cellValue: any
column: any
items: number
}
const props = defineProps<Prop>()
const emit = defineEmits(['update:modelValue', 'attachRecord'])
@ -126,25 +133,45 @@ onKeyStroke('Escape', () => {
vModel.value = false
})
const skeltonAmountToShow = ref(childrenListCount.value === 0 ? 10 : childrenListCount.value)
/*
/*
to render same number of skelton as the number of cards
displayed
*/
watch(childrenListPagination, () => {
const skeltonCount = computed(() => {
if (props.items < 10 && childrenListPagination.page === 1) {
return props.items
}
if (childrenListCount.value < 10 && childrenListPagination.page === 1) {
skeltonAmountToShow.value = childrenListCount.value === 0 ? 10 : childrenListCount.value
return childrenListCount.value || 10
}
const totalRows = Math.ceil(childrenListCount.value / 10)
const totlaRows = Math.ceil(childrenListCount.value / 10)
if (totalRows === childrenListPagination.page) {
return childrenListCount.value % 10
}
return 10
})
if (totlaRows === childrenListPagination.page) {
skeltonAmountToShow.value = childrenListCount.value % 10
} else {
skeltonAmountToShow.value = 10
const totalItemsToShow = computed(() => {
if (isChildrenLoading.value) {
return props.items
}
return childrenListCount.value
})
const isDataExist = computed<boolean>(() => {
return childrenList.value?.pageInfo?.totalRows || (isNew.value && state.value?.[colTitle.value]?.length)
})
const linkOrUnLink = (rowRef: Record<string, string>, id: string) => {
if (isPublic.value && !isForm.value) return
if (isNew.value || isChildrenListLinked.value[parseInt(id)]) {
unlinkRow(rowRef, parseInt(id))
} else {
linkRow(rowRef, parseInt(id))
}
}
</script>
<template>
@ -188,13 +215,12 @@ watch(childrenListPagination, () => {
</a-input>
</div>
</div>
<div class="flex flex-col flex-grow nc-scrollbar-md">
<template v-if="(isNew && state?.[colTitle]?.length) || childrenList?.pageInfo?.totalRows">
<div class="flex flex-col flex-grow nc-scrollbar-md cursor-pointer pr-1">
<div v-if="isDataExist || isChildrenLoading" class="mt-2 mb-2">
<div class="cursor-pointer pr-1">
<template v-if="isChildrenLoading">
<div
v-for="(x, i) in Array.from({ length: 10 })"
v-for="(x, i) in Array.from({ length: skeltonCount })"
:key="i"
class="!border-2 flex flex-row gap-2 mb-2 transition-all !rounded-xl relative !border-gray-200 hover:bg-gray-50"
>
@ -234,20 +260,11 @@ watch(childrenListPagination, () => {
:is-linked="childrenList?.list ? isChildrenListLinked[Number.parseInt(id)] : true"
:is-loading="isChildrenListLoading[Number.parseInt(id)]"
@expand="onClick(refRow)"
@click="
() => {
if (isPublic && !isForm) return
isNew
? unlinkRow(refRow, Number.parseInt(id))
: isChildrenListLinked[Number.parseInt(id)]
? unlinkRow(refRow, Number.parseInt(id))
: linkRow(refRow, Number.parseInt(id))
}
"
@click="linkOrUnLink(refRow, id)"
/>
</template>
</div>
</template>
</div>
<div v-else class="pt-1 flex flex-col gap-3 my-auto items-center justify-center text-gray-500">
<InboxIcon class="w-16 h-16 mx-auto" />
<p>
@ -278,8 +295,8 @@ watch(childrenListPagination, () => {
<div class="flex flex-row justify-between bg-white relative pt-1">
<div v-if="!isForm" class="flex items-center justify-center px-2 rounded-md text-gray-500 bg-brand-50">
{{ childrenListCount || 0 }} {{ !isMobileMode ? $t('objects.records') : '' }}
{{ !isMobileMode && childrenListCount !== 0 ? $t('general.are') : '' }}
{{ totalItemsToShow || 0 }} {{ !isMobileMode ? $t('objects.records') : '' }}
{{ !isMobileMode && totalItemsToShow !== 0 ? $t('general.are') : '' }}
{{ $t('general.linked') }}
</div>
<div v-else class="flex items-center justify-center px-2 rounded-md text-gray-500 bg-brand-50">

7
packages/nc-gui/components/workspace/View.vue

@ -1,6 +1,5 @@
<script lang="ts" setup>
import { useTitle } from '@vueuse/core'
import type { WorkspaceType } from 'nocodb-sdk'
const router = useRouter()
const route = router.currentRoute
@ -21,8 +20,6 @@ const tab = computed({
},
})
const getWorkspaceColor = (workspace: WorkspaceType) => workspace.meta?.color || stringToColor(workspace.id!)
watch(
() => activeWorkspace.value?.title,
(title: string) => {
@ -53,9 +50,7 @@ onMounted(() => {
<template>
<div v-if="activeWorkspace" class="flex flex-col nc-workspace-settings">
<div class="flex gap-2 items-center min-w-0 p-6">
<span class="nc-workspace-avatar !w-8 !h-8" :style="{ backgroundColor: getWorkspaceColor(activeWorkspace) }">
{{ activeWorkspace?.title?.slice(0, 2) }}
</span>
<GeneralWorkspaceIcon :workspace="activeWorkspace" />
<h1 class="text-3xl font-weight-bold tracking-[0.5px] mb-0 nc-workspace-title truncate min-w-10 capitalize">
{{ activeWorkspace?.title }}
</h1>

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

@ -279,6 +279,10 @@ const [useProvideExpandedFormStore, useExpandedFormStore] = useInjectionState((m
return data
}
const clearColumns = () => {
changedColumns.value = new Set()
}
const loadRow = async (rowId?: string) => {
const record = await $api.dbTableRow.read(
NOCO,
@ -344,6 +348,7 @@ const [useProvideExpandedFormStore, useExpandedFormStore] = useInjectionState((m
primaryKey,
saveRowAndStay,
updateComment,
clearColumns,
}
}, 'expanded-form-store')

8
packages/nc-gui/composables/useMultiSelect/convertCellData.ts

@ -17,6 +17,14 @@ export default function convertCellData(
if (value === '') return null
switch (to) {
case UITypes.SingleLineText:
case UITypes.LongText:
// This is to remove the quotes added from LongText
// TODO (refactor): remove this when we have a better way to handle this
if (value.match(/^".*"$/)) {
return value.slice(1, -1)
}
return value
case UITypes.Number: {
const parsedNumber = Number(value)
if (isNaN(parsedNumber)) {

4
packages/nc-gui/composables/useMultiSelect/index.ts

@ -184,7 +184,7 @@ export function useMultiSelect(
}
if (columnObj.uidt === UITypes.LongText) {
textToCopy = `"${textToCopy.replace(/\"/g, '""')}"`
textToCopy = `"${textToCopy.replace(/"/g, '\\"')}"`
}
return textToCopy
@ -202,7 +202,7 @@ export function useMultiSelect(
const value = valueToCopy(row, col)
copyRow += `<td>${value}</td>`
text = `${text}${value}${cols.length - 1 !== i ? '\t' : ''}`
jsonRow.push(col.uidt === UITypes.LongText ? value.replace(/^"/, '').replace(/"$/, '').replace(/""/g, '"') : value)
jsonRow.push(value)
})
html += `${copyRow}</tr>`
if (rows.length - 1 !== i) {

14
packages/nc-gui/composables/useViewData.ts

@ -99,6 +99,16 @@ export function useViewData(
},
})
const islastRow = computed(() => {
const currentIndex = getExpandedRowIndex()
return paginationData.value?.isLastPage && currentIndex === formattedData.value.length - 1
})
const isFirstRow = computed(() => {
const currentIndex = getExpandedRowIndex()
return paginationData.value?.isFirstPage && currentIndex === 0
})
const queryParams = computed(() => ({
offset: ((paginationData.value.page ?? 0) - 1) * (paginationData.value.pageSize ?? appInfoDefaultLimit),
limit: paginationData.value.pageSize ?? appInfoDefaultLimit,
@ -303,6 +313,8 @@ export function useViewData(
}
const navigateToSiblingRow = async (dir: NavigateDir) => {
console.log('test')
const expandedRowIndex = getExpandedRowIndex()
// calculate next row index based on direction
@ -378,5 +390,7 @@ export function useViewData(
navigateToSiblingRow,
getExpandedRowIndex,
optimisedQuery,
islastRow,
isFirstRow,
}
}

5
packages/nc-gui/lang/en.json

@ -388,6 +388,7 @@
}
},
"labels": {
"tokenLimit": "Only one token per user is allowed",
"duplicateAttachment": "File with name {filename} already attached",
"toAddress": "To Address",
"subject": "Subject",
@ -396,6 +397,9 @@
"headerName": "Header Name",
"icon": "Icon",
"max": "Max",
"copiedRecordURL": "Copied Record URL",
"copyRecordURL": "Copy Record URL",
"duplicateRecord": "Duplicate record",
"binaryEncodingFormat": "Binary encoding format",
"syntax": "Syntax",
"examples": "Examples",
@ -605,6 +609,7 @@
"newSource": "New Data Source",
"newWebhook": "New Webhook",
"enablePublicAccess": "Enable Public Access",
"doYouWantToSaveTheChanges": "Do you want to save the changes ?",
"editingAccess": "Editing access",
"enabledPublicViewing": "Enable public viewing",
"restrictAccessWithPassword": "Restrict access with password",

4
packages/nc-gui/nuxt.config.ts

@ -112,7 +112,9 @@ export default defineNuxtConfig({
compiler: 'vue3',
defaultClass: 'nc-icon',
customCollections: {
'nc-icons': FileSystemIconLoader('./assets/nc-icons', (svg) => svg.replace(/^<svg /, '<svg stroke="currentColor" ')),
'nc-icons': FileSystemIconLoader('./assets/nc-icons', (svg) =>
svg.replace(/^<svg (?!=\s*data-ignore)/, '<svg stroke="currentColor" '),
),
},
}),
Components({

2
packages/nc-gui/package.json

@ -55,7 +55,7 @@
"emoji-mart-vue-fast": "^15.0.0",
"file-saver": "^2.0.5",
"fuse.js": "^6.6.2",
"httpsnippet": "^3.0.1",
"httpsnippet": "^2.0.0",
"jsbarcode": "^3.11.5",
"jsep": "^1.3.6",
"jwt-decode": "^3.1.2",

5
packages/nc-gui/store/views.ts

@ -6,7 +6,9 @@ export const useViewsStore = defineStore('viewsStore', () => {
const { $api } = useNuxtApp()
const router = useRouter()
const recentViews = ref<any>([])
const recentViews = computed(() => [])
const allRecentViews = ref<any>([])
const route = router.currentRoute
const tablesStore = useTablesStore()
@ -237,6 +239,7 @@ export const useViewsStore = defineStore('viewsStore', () => {
isPaginationLoading,
loadViews,
recentViews,
allRecentViews,
views,
activeView,
openedViewsTab,

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

@ -40,7 +40,6 @@ import PlusSquare from '~icons/nc-icons/plus-square'
import MobileShare from '~icons/nc-icons/share'
import PhLayout from '~icons/ph/layout'
import Delete from '~icons/material-symbols/delete-outline-rounded'
import CiFilter from '~icons/mdi/filter-outline'
import Minus from '~icons/material-symbols/remove-rounded'
import Code from '~icons/material-symbols/code-rounded'
import Palette from '~icons/material-symbols/palette-outline'
@ -92,6 +91,7 @@ import Project from '~icons/nc-icons/project'
import LookupIcon from '~icons/nc-icons/lookup'
// Roles
import SuperAdmin from '~icons/nc-icons/super-admin'
import Owner from '~icons/nc-icons/owner'
import Creator from '~icons/nc-icons/creator'
import Editor from '~icons/nc-icons/editor'
@ -99,6 +99,12 @@ import Commentor from '~icons/nc-icons/commentor'
import Viewer from '~icons/nc-icons/viewer'
import NoAccess from '~icons/nc-icons/no-access'
// toolbar
import Fields from '~icons/nc-icons/fields'
import Filter from '~icons/nc-icons/filter'
import Group from '~icons/nc-icons/group'
import Sort from '~icons/nc-icons/sort'
// keep it for reference
// todo: remove it after all icons are migrated
/* export const iconMapOld = {
@ -247,6 +253,10 @@ import NoAccess from '~icons/nc-icons/no-access'
} as const */
export const iconMap = {
sort: Sort,
group: Group,
filter: Filter,
fields: Fields,
mm: ManytoManyIcon,
hm: HasManyIcon,
bt: BelongsToIcon,
@ -289,7 +299,6 @@ export const iconMap = {
viewList: h('span', { class: 'material-symbols' }, 'view_list'),
cloud: h('span', { class: 'material-symbols' }, 'cloud'),
apiAndSupport: h('span', { class: 'material-symbols' }, 'api'),
sort: h('span', { class: 'material-symbols', style: { transform: 'scaleY(-1)' } }, 'sort'),
translate: h('span', { class: 'material-symbols' }, 'translate'),
preview: h('span', { class: 'material-symbols' }, 'visibility'),
link: h('span', { class: 'material-symbols' }, 'link'),
@ -405,7 +414,6 @@ export const iconMap = {
view: h('span', { class: 'material-symbols' }, 'visibility'),
// rowHeight: h('span', { class: 'material-symbols' }, 'height'),
rowHeight: h(PhSplitVerticalThin, { style: { fontSize: '14px' } }),
filter: CiFilter,
closeCircle: h('span', { class: 'material-symbols' }, 'cancel'),
closeBox: h('span', { class: 'material-symbols' }, 'close'),
close: MSCloseRounded,
@ -439,6 +447,7 @@ export const iconMap = {
role_editor: Editor,
role_commenter: Commentor,
role_viewer: Viewer,
role_super: SuperAdmin,
role_no_access: NoAccess,
commentHere: NcCommentHere,
}

12
packages/nc-gui/windi.config.ts

@ -186,6 +186,18 @@ export default defineConfig({
800: '#654c17',
900: '#32260c',
},
maroon: {
50: '#FFF0F7',
100: '#FFCFE6',
200: '#FFABD2',
300: '#EC7DB1',
400: '#D45892',
500: '#B33771',
600: '#9D255D',
700: '#801044',
800: '#690735',
900: '#42001F',
},
primary: 'rgba(var(--color-primary), var(--tw-bg-opacity))',
accent: 'rgba(var(--color-accent), var(--tw-bg-opacity))',

4
packages/nocodb-sdk/src/lib/enums.ts

@ -188,7 +188,7 @@ export const RoleColors = {
[ProjectRoles.EDITOR]: 'green',
[ProjectRoles.COMMENTER]: 'orange',
[ProjectRoles.VIEWER]: 'yellow',
[OrgUserRoles.SUPER_ADMIN]: 'purple',
[OrgUserRoles.SUPER_ADMIN]: 'maroon',
[ProjectRoles.NO_ACCESS]: 'red',
[OrgUserRoles.CREATOR]: 'blue',
[OrgUserRoles.VIEWER]: 'yellow',
@ -223,7 +223,7 @@ export const RoleIcons = {
[ProjectRoles.COMMENTER]: 'role_commenter',
[ProjectRoles.VIEWER]: 'role_viewer',
[ProjectRoles.NO_ACCESS]: 'role_no_access',
[OrgUserRoles.SUPER_ADMIN]: 'role_owner',
[OrgUserRoles.SUPER_ADMIN]: 'role_super',
[OrgUserRoles.CREATOR]: 'role_creator',
[OrgUserRoles.VIEWER]: 'role_viewer',
};

2
packages/nocodb/src/db/BaseModelSqlv2.ts

@ -3458,7 +3458,7 @@ class BaseModelSqlv2 {
}
public async afterDelete(data: any, _trx: any, req): Promise<void> {
const id = req?.params?.id;
const id = this._extractPksValues(data);
await Audit.insert({
fk_model_id: this.model.id,
row_id: id,

6
packages/nocodb/src/db/sql-client/lib/sqlite/SqliteClient.ts

@ -2090,7 +2090,7 @@ class SqliteClient extends KnexClient {
addNewColumnQuery +=
n.dtxp && n.dt !== 'text' ? `(${this.genRaw(n.dtxp)})` : '';
addNewColumnQuery += n.cdf
? ` DEFAULT ${this.sanitiseDefaultValue(n.cdf)}`
? ` DEFAULT ${this.genValue(n.cdf)}`
: !n.rqd
? ' '
: ` DEFAULT ''`;
@ -2122,7 +2122,7 @@ class SqliteClient extends KnexClient {
shouldSanitize,
);
query += n.dtxp && n.dt !== 'text' ? `(${this.genRaw(n.dtxp)})` : '';
query += n.cdf ? ` DEFAULT ${this.sanitiseDefaultValue(n.cdf)}` : ' ';
query += n.cdf ? ` DEFAULT ${this.genValue(n.cdf)}` : ' ';
query += n.rqd ? ` NOT NULL` : ' ';
} else if (change === 1) {
shouldSanitize = true;
@ -2133,7 +2133,7 @@ class SqliteClient extends KnexClient {
);
query += n.dtxp && n.dt !== 'text' ? `(${this.genRaw(n.dtxp)})` : '';
query += n.cdf
? ` DEFAULT ${this.sanitiseDefaultValue(n.cdf)}`
? ` DEFAULT ${this.genValue(n.cdf)}`
: !n.rqd
? ' '
: ` DEFAULT ''`;

8
packages/nocodb/src/helpers/catchError.ts

@ -415,6 +415,8 @@ export default function (
return res.status(400).json({ msg: e.message, errors: e.errors });
} else if (e instanceof UnprocessableEntity) {
return res.status(422).json({ msg: e.message });
} else if (e instanceof NotAllowed) {
return res.status(405).json({ msg: e.message });
}
next(e);
}
@ -423,6 +425,8 @@ export default function (
export class BadRequest extends Error {}
export class NotAllowed extends Error {}
export class Unauthorized extends Error {}
export class Forbidden extends Error {}
@ -476,4 +480,8 @@ export class NcError {
static unprocessableEntity(message = 'Unprocessable entity') {
throw new UnprocessableEntity(message);
}
static notAllowed(message = 'Not allowed') {
throw new NotAllowed(message);
}
}

5
packages/nocodb/src/modules/jobs/jobs.module.ts

@ -1,4 +1,4 @@
import { Module } from '@nestjs/common';
import { forwardRef, Module } from '@nestjs/common';
import { BullModule } from '@nestjs/bull';
// Jobs
@ -34,7 +34,7 @@ import { GlobalModule } from '~/modules/global/global.module';
@Module({
imports: [
GlobalModule,
forwardRef(() => GlobalModule),
DatasModule,
MetasModule,
...(process.env.NC_REDIS_JOB_URL
@ -80,5 +80,6 @@ import { GlobalModule } from '~/modules/global/global.module';
SourceCreateProcessor,
SourceDeleteProcessor,
],
exports: ['JobsService'],
})
export class JobsModule {}

2
packages/nocodb/src/services/api-tokens.service.ts

@ -9,7 +9,7 @@ import { ApiToken } from '~/models';
@Injectable()
export class ApiTokensService {
constructor(private readonly appHooksService: AppHooksService) {}
constructor(protected readonly appHooksService: AppHooksService) {}
async apiTokenList(param: { userId: string }) {
return await ApiToken.list(param.userId);

99
packages/nocodb/src/services/columns.service.ts

@ -303,9 +303,14 @@ export class ColumnsService {
);
}
} else if (
[UITypes.SingleLineText, UITypes.LongText].includes(column.uidt)
[
UITypes.SingleLineText,
UITypes.Email,
UITypes.PhoneNumber,
UITypes.URL,
].includes(column.uidt)
) {
// SingleLineText/LongText to SingleSelect/MultiSelect
// Text to SingleSelect/MultiSelect
const dbDriver = await reuseOrSave('dbDriver', reuse, async () =>
NcConnectionMgrv2.get(source),
);
@ -371,23 +376,48 @@ export class ColumnsService {
);
if (colBody.cdf) {
if (colBody.uidt === UITypes.SingleSelect) {
if (!optionTitles.includes(colBody.cdf.replace(/'/g, "''"))) {
NcError.badRequest(
`Default value '${colBody.cdf}' is not a select option.`,
);
}
} else {
for (const cdf of colBody.cdf.split(',')) {
if (!optionTitles.includes(cdf.replace(/'/g, "''"))) {
try {
if (!optionTitles.includes(colBody.cdf.replace(/'/g, "''"))) {
NcError.badRequest(
`Default value '${cdf}' is not a select option.`,
`Default value '${colBody.cdf}' is not a select option.`,
);
}
} catch (e) {
colBody.cdf = colBody.cdf.replace(/^'/, '').replace(/'$/, '');
if (!optionTitles.includes(colBody.cdf.replace(/'/g, "''"))) {
NcError.badRequest(
`Default value '${colBody.cdf}' is not a select option.`,
);
}
}
} else {
try {
for (const cdf of colBody.cdf.split(',')) {
if (!optionTitles.includes(cdf.replace(/'/g, "''"))) {
NcError.badRequest(
`Default value '${cdf}' is not a select option.`,
);
}
}
} catch (e) {
colBody.cdf = colBody.cdf.replace(/^'/, '').replace(/'$/, '');
for (const cdf of colBody.cdf.split(',')) {
if (!optionTitles.includes(cdf.replace(/'/g, "''"))) {
NcError.badRequest(
`Default value '${cdf}' is not a select option.`,
);
}
}
}
}
// handle single quote for default value
if (driverType === 'mysql' || driverType === 'mysql2') {
if (
driverType === 'mysql' ||
driverType === 'mysql2' ||
driverType === 'pg' ||
driverType === 'sqlite3'
) {
colBody.cdf = colBody.cdf.replace(/'/g, "'");
} else {
colBody.cdf = colBody.cdf.replace(/'/g, "''");
@ -1142,23 +1172,48 @@ export class ColumnsService {
// Handle default values
if (colBody.cdf) {
if (colBody.uidt === UITypes.SingleSelect) {
if (!optionTitles.includes(colBody.cdf.replace(/'/g, "''"))) {
NcError.badRequest(
`Default value '${colBody.cdf}' is not a select option.`,
);
}
} else {
for (const cdf of colBody.cdf.split(',')) {
if (!optionTitles.includes(cdf.replace(/'/g, "''"))) {
try {
if (!optionTitles.includes(colBody.cdf.replace(/'/g, "''"))) {
NcError.badRequest(
`Default value '${colBody.cdf}' is not a select option.`,
);
}
} catch (e) {
colBody.cdf = colBody.cdf.replace(/^'/, '').replace(/'$/, '');
if (!optionTitles.includes(colBody.cdf.replace(/'/g, "''"))) {
NcError.badRequest(
`Default value '${cdf}' is not a select option.`,
`Default value '${colBody.cdf}' is not a select option.`,
);
}
}
} else {
try {
for (const cdf of colBody.cdf.split(',')) {
if (!optionTitles.includes(cdf.replace(/'/g, "''"))) {
NcError.badRequest(
`Default value '${cdf}' is not a select option.`,
);
}
}
} catch (e) {
colBody.cdf = colBody.cdf.replace(/^'/, '').replace(/'$/, '');
for (const cdf of colBody.cdf.split(',')) {
if (!optionTitles.includes(cdf.replace(/'/g, "''"))) {
NcError.badRequest(
`Default value '${cdf}' is not a select option.`,
);
}
}
}
}
// handle single quote for default value
if (driverType === 'mysql' || driverType === 'mysql2') {
if (
driverType === 'mysql' ||
driverType === 'mysql2' ||
driverType === 'pg' ||
driverType === 'sqlite3'
) {
colBody.cdf = colBody.cdf.replace(/'/g, "'");
} else {
colBody.cdf = colBody.cdf.replace(/'/g, "''");

2
packages/nocodb/src/services/org-tokens.service.ts

@ -10,7 +10,7 @@ import { ApiToken } from '~/models';
@Injectable()
export class OrgTokensService {
constructor(private readonly appHooksService: AppHooksService) {}
constructor(protected readonly appHooksService: AppHooksService) {}
async apiTokenList(param: { user: User; query: any }) {
const fk_user_id = param.user.id;

320
pnpm-lock.yaml

@ -38,7 +38,7 @@ importers:
version: 4.1.1(vue@3.3.4)
'@pinia/nuxt':
specifier: ^0.4.11
version: 0.4.11(vue@3.3.4)
version: 0.4.11(typescript@5.2.2)(vue@3.3.4)
'@vue-flow/additional-components':
specifier: ^1.2.0
version: 1.2.0(@vue-flow/core@1.3.0)(vue@3.3.4)
@ -85,8 +85,8 @@ importers:
specifier: ^6.6.2
version: 6.6.2
httpsnippet:
specifier: ^3.0.1
version: 3.0.1
specifier: ^2.0.0
version: 2.0.0(mkdirp@2.1.3)
jsbarcode:
specifier: ^3.11.5
version: 3.11.5
@ -122,7 +122,7 @@ importers:
version: 1.0.2
pinia:
specifier: ^2.1.4
version: 2.1.4(vue@3.3.4)
version: 2.1.4(typescript@5.2.2)(vue@3.3.4)
qrcode:
specifier: ^1.5.1
version: 1.5.1
@ -192,7 +192,7 @@ importers:
devDependencies:
'@antfu/eslint-config':
specifier: ^0.26.0
version: 0.26.0(eslint@8.33.0)
version: 0.26.0(eslint@8.33.0)(typescript@5.2.2)
'@esbuild-plugins/node-modules-polyfill':
specifier: ^0.2.2
version: 0.2.2(esbuild@0.19.2)
@ -342,7 +342,7 @@ importers:
version: 6.0.3
nuxt:
specifier: ^3.6.5
version: 3.6.5(@types/node@20.3.1)(eslint@8.33.0)(sass@1.63.4)
version: 3.6.5(@types/node@20.3.1)(eslint@8.33.0)(sass@1.63.4)(typescript@5.2.2)
nuxt-windicss:
specifier: ^2.6.1
version: 2.6.1(vite@4.4.9)
@ -354,7 +354,7 @@ importers:
version: 1.63.4
ts-loader:
specifier: ^9.4.4
version: 9.4.4(webpack@5.88.2)
version: 9.4.4(typescript@5.2.2)(webpack@5.88.2)
unplugin-icons:
specifier: ^0.14.15
version: 0.14.15(@vue/compiler-sfc@3.2.37)
@ -1113,20 +1113,20 @@ packages:
/@ant-design/icons-vue@6.1.0(vue@3.3.4):
resolution: {integrity: sha512-EX6bYm56V+ZrKN7+3MT/ubDkvJ5rK/O2t380WFRflDcVFgsvl3NLH7Wxeau6R8DbrO5jWR6DSTC3B6gYFp77AA==}
peerDependencies:
vue: latest
vue: '>=3.0.3'
dependencies:
'@ant-design/colors': 6.0.0
'@ant-design/icons-svg': 4.3.1
vue: 3.3.4
dev: false
/@antfu/eslint-config-basic@0.26.0(@typescript-eslint/parser@5.62.0)(eslint@8.33.0):
/@antfu/eslint-config-basic@0.26.0(@typescript-eslint/parser@5.62.0)(eslint@8.33.0)(typescript@5.2.2):
resolution: {integrity: sha512-l/Omn5HuI3VgHPGJ13pqdGEdF7D7HSzN85KF0HaR/jm24QzL2kkFeZJuglv+YSXyVDI1eAbzF52TlJUz1cxqzQ==}
peerDependencies:
eslint: '>=7.4.0'
dependencies:
eslint: 8.33.0
eslint-plugin-antfu: 0.26.0(eslint@8.33.0)
eslint-plugin-antfu: 0.26.0(eslint@8.33.0)(typescript@5.2.2)
eslint-plugin-eslint-comments: 3.2.0(eslint@8.33.0)
eslint-plugin-html: 7.1.0
eslint-plugin-import: 2.28.1(@typescript-eslint/parser@5.62.0)(eslint@8.33.0)
@ -1146,12 +1146,12 @@ packages:
- typescript
dev: true
/@antfu/eslint-config-react@0.26.0(eslint@8.33.0):
/@antfu/eslint-config-react@0.26.0(eslint@8.33.0)(typescript@5.2.2):
resolution: {integrity: sha512-QkRhzivKAtTVtLMFXt7Sl+hBM/LCAU+EUpIxTqw/h0uufYmzIrbdVwkxPntZaXl5p/qLZqQOZG3Iu7oek+MNeA==}
peerDependencies:
eslint: '>=7.4.0'
dependencies:
'@antfu/eslint-config-ts': 0.26.0(eslint@8.33.0)
'@antfu/eslint-config-ts': 0.26.0(eslint@8.33.0)(typescript@5.2.2)
eslint: 8.33.0
eslint-plugin-react: 7.33.2(eslint@8.33.0)
transitivePeerDependencies:
@ -1161,28 +1161,29 @@ packages:
- typescript
dev: true
/@antfu/eslint-config-ts@0.26.0(eslint@8.33.0):
/@antfu/eslint-config-ts@0.26.0(eslint@8.33.0)(typescript@5.2.2):
resolution: {integrity: sha512-LnJgBP+XAsGijfmjAZk6BDxlt7UYGiXyCIviLDgg8bmPaLAX3I+i0wmj9DwhDIYeXForeP/Io+dBT63k5BheBw==}
peerDependencies:
eslint: '>=7.4.0'
typescript: latest
typescript: '>=3.9'
dependencies:
'@antfu/eslint-config-basic': 0.26.0(@typescript-eslint/parser@5.62.0)(eslint@8.33.0)
'@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.33.0)
'@typescript-eslint/parser': 5.62.0(eslint@8.33.0)
'@antfu/eslint-config-basic': 0.26.0(@typescript-eslint/parser@5.62.0)(eslint@8.33.0)(typescript@5.2.2)
'@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.33.0)(typescript@5.2.2)
'@typescript-eslint/parser': 5.62.0(eslint@8.33.0)(typescript@5.2.2)
eslint: 8.33.0
typescript: 5.2.2
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
- supports-color
dev: true
/@antfu/eslint-config-vue@0.26.0(eslint@8.33.0):
/@antfu/eslint-config-vue@0.26.0(eslint@8.33.0)(typescript@5.2.2):
resolution: {integrity: sha512-a4ov8vzOzV5WPBSXIwg1oDnpSEOaxpvqz9cZK/lwqDsS+tk2QAI6yfcxwbhMIDUy3pA3u9B62K9tP+9HhqncIw==}
peerDependencies:
eslint: '>=7.4.0'
dependencies:
'@antfu/eslint-config-ts': 0.26.0(eslint@8.33.0)
'@antfu/eslint-config-ts': 0.26.0(eslint@8.33.0)(typescript@5.2.2)
eslint: 8.33.0
eslint-plugin-vue: 9.17.0(eslint@8.33.0)
transitivePeerDependencies:
@ -1192,15 +1193,15 @@ packages:
- typescript
dev: true
/@antfu/eslint-config@0.26.0(eslint@8.33.0):
/@antfu/eslint-config@0.26.0(eslint@8.33.0)(typescript@5.2.2):
resolution: {integrity: sha512-3f+JKBYPZZot5cdS4gkZxuiytUTfHWhsKS1UmNhqEn11G5BmzYSu1LRf0K/n10hb/u+GUtZJwKAovu6qnQBM9w==}
peerDependencies:
eslint: '>=7.4.0'
dependencies:
'@antfu/eslint-config-react': 0.26.0(eslint@8.33.0)
'@antfu/eslint-config-vue': 0.26.0(eslint@8.33.0)
'@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.33.0)
'@typescript-eslint/parser': 5.62.0(eslint@8.33.0)
'@antfu/eslint-config-react': 0.26.0(eslint@8.33.0)(typescript@5.2.2)
'@antfu/eslint-config-vue': 0.26.0(eslint@8.33.0)(typescript@5.2.2)
'@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.33.0)(typescript@5.2.2)
'@typescript-eslint/parser': 5.62.0(eslint@8.33.0)(typescript@5.2.2)
eslint: 8.33.0
eslint-plugin-eslint-comments: 3.2.0(eslint@8.33.0)
eslint-plugin-html: 7.1.0
@ -3225,7 +3226,7 @@ packages:
resolution: {integrity: sha512-wmsfNHJQgSX0HFA726r+cm6ixYs+z/KLxfg1cx6xZ1PBGeZsuQCACdNdoKckyr/IH8FgIbBoIH0/6QiSbW3tEQ==}
peerDependencies:
'@vue/composition-api': ^1.0.0-rc.1
vue: latest
vue: ^2.0.0 || >=3.0.5
peerDependenciesMeta:
'@vue/composition-api':
optional: true
@ -3239,7 +3240,7 @@ packages:
resolution: {integrity: sha512-c9X82nppYjSxjlITO6jdLLdt9HoyZzqEWpqDL2V6NJd859d6GCh/2AHeRXk+37uRJ1UdTkCuty93WOEqja8quw==}
engines: {node: '>=12'}
peerDependencies:
vue: latest
vue: ^3.2.0
dependencies:
'@ctrl/tinycolor': 3.6.1
lodash-es: 4.17.21
@ -4376,7 +4377,7 @@ packages:
/@iconify/vue@4.1.1(vue@3.3.4):
resolution: {integrity: sha512-RL85Bm/DAe8y6rT6pux7D2FJSiUEM/TPfyK7GrbAOfTSwrhvwJW+S5yijdGcmtXouA8MtuH9C7l4hiSE4mLMjg==}
peerDependencies:
vue: latest
vue: '>=3'
dependencies:
'@iconify/types': 2.0.0
vue: 3.3.4
@ -5228,7 +5229,7 @@ packages:
/@nestjs/schematics@10.0.1(chokidar@3.5.3)(typescript@5.2.2):
resolution: {integrity: sha512-buxpYtSwOmWyf0nUJWJCkCkYITwbOfIEKHTnGS7sDbcfaajrOFXb5pPAGD2E1CUb3C1+NkQIURPKzs0IouZTQg==}
peerDependencies:
typescript: latest
typescript: '>=4.8.2'
dependencies:
'@angular-devkit/core': 16.1.0(chokidar@3.5.3)
'@angular-devkit/schematics': 16.1.0(chokidar@3.5.3)
@ -5652,11 +5653,11 @@ packages:
/@nuxt/ui-templates@1.3.1:
resolution: {integrity: sha512-5gc02Pu1HycOVUWJ8aYsWeeXcSTPe8iX8+KIrhyEtEoOSkY0eMBuo0ssljB8wALuEmepv31DlYe5gpiRwkjESA==}
/@nuxt/vite-builder@3.6.5(@types/node@20.3.1)(eslint@8.33.0)(sass@1.63.4)(vue@3.3.4):
/@nuxt/vite-builder@3.6.5(@types/node@20.3.1)(eslint@8.33.0)(sass@1.63.4)(typescript@5.2.2)(vue@3.3.4):
resolution: {integrity: sha512-pwSpt257ApCp3XWUs8vrC7X9QHeHUv5PbbIR3+5w0n5f95XPNOQWDJa2fTPX/H6oaRJCPYAsBPqiQhQ7qW/NZQ==}
engines: {node: ^14.18.0 || >=16.10.0}
peerDependencies:
vue: latest
vue: ^3.3.4
dependencies:
'@nuxt/kit': 3.6.5
'@rollup/plugin-replace': 5.0.2(rollup@3.28.1)
@ -5691,7 +5692,7 @@ packages:
unplugin: 1.4.0
vite: 4.3.9(@types/node@20.3.1)(sass@1.63.4)
vite-node: 0.33.0(@types/node@20.3.1)(sass@1.63.4)
vite-plugin-checker: 0.6.2(eslint@8.33.0)(vite@4.3.9)
vite-plugin-checker: 0.6.2(eslint@8.33.0)(typescript@5.2.2)(vite@4.3.9)
vue: 3.3.4
vue-bundle-renderer: 1.0.3
transitivePeerDependencies:
@ -6115,11 +6116,11 @@ packages:
'@parcel/watcher-win32-x64': 2.3.0
dev: true
/@pinia/nuxt@0.4.11(vue@3.3.4):
/@pinia/nuxt@0.4.11(typescript@5.2.2)(vue@3.3.4):
resolution: {integrity: sha512-bhuNFngJpmBCdAqWguezNJ/oJFR7wvKieqiZrmmdmPR07XjsidAw8RLXHMZE9kUm32M9E6T057OBbG/22jERTg==}
dependencies:
'@nuxt/kit': 3.7.0
pinia: 2.1.4(vue@3.3.4)
pinia: 2.1.4(typescript@5.2.2)(vue@3.3.4)
transitivePeerDependencies:
- '@vue/composition-api'
- rollup
@ -7809,7 +7810,7 @@ packages:
dev: false
optional: true
/@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.33.0):
/@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.33.0)(typescript@5.2.2):
resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@ -7821,7 +7822,7 @@ packages:
optional: true
dependencies:
'@eslint-community/regexpp': 4.8.0
'@typescript-eslint/parser': 5.62.0(eslint@8.33.0)
'@typescript-eslint/parser': 5.62.0(eslint@8.33.0)(typescript@5.2.2)
'@typescript-eslint/scope-manager': 5.62.0
'@typescript-eslint/type-utils': 5.62.0(eslint@8.33.0)(typescript@5.2.2)
'@typescript-eslint/utils': 5.62.0(eslint@8.33.0)(typescript@5.2.2)
@ -7832,6 +7833,7 @@ packages:
natural-compare-lite: 1.4.0
semver: 7.5.4
tsutils: 3.21.0(typescript@5.2.2)
typescript: 5.2.2
transitivePeerDependencies:
- supports-color
dev: true
@ -7895,7 +7897,7 @@ packages:
- supports-color
dev: true
/@typescript-eslint/parser@5.62.0(eslint@8.33.0):
/@typescript-eslint/parser@5.62.0(eslint@8.33.0)(typescript@5.2.2):
resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@ -7910,6 +7912,7 @@ packages:
'@typescript-eslint/typescript-estree': 5.62.0(typescript@5.2.2)
debug: 4.3.4(supports-color@8.1.1)
eslint: 8.33.0
typescript: 5.2.2
transitivePeerDependencies:
- supports-color
dev: true
@ -8230,7 +8233,7 @@ packages:
/@unhead/vue@1.3.9(vue@3.3.4):
resolution: {integrity: sha512-rVAsRLBc+3Y//NRmr7vmRs5yhIf65jYSvcj0V5DtDfDwql7BbGgc3VIIEvY0+EjLQuNsS5kxwm78LSPCIl/3Xw==}
peerDependencies:
vue: latest
vue: '>=2.7 || >=3'
dependencies:
'@unhead/schema': 1.3.9
'@unhead/shared': 1.3.9
@ -8498,7 +8501,7 @@ packages:
engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies:
vite: ^4.0.0
vue: latest
vue: ^3.0.0
dependencies:
'@babel/core': 7.22.11
'@babel/plugin-transform-typescript': 7.22.11(@babel/core@7.22.11)
@ -8514,7 +8517,7 @@ packages:
engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies:
vite: ^4.0.0
vue: latest
vue: ^3.2.25
dependencies:
vite: 4.3.9(@types/node@20.3.1)(sass@1.63.4)
vue: 3.3.4
@ -8569,7 +8572,7 @@ packages:
resolution: {integrity: sha512-a5F2y0WdPxaxTAcN7mSYVZ6A2kDSxenp+YwMt/2ldFRmCIP4jCbXEsTZfcpUe5zhlehfbyTTYbEw03w3YW24aA==}
peerDependencies:
'@vue-flow/core': ^1.0.0
vue: latest
vue: ^3.2.37
dependencies:
'@types/d3-selection': 3.0.6
'@types/d3-zoom': 3.0.4
@ -8582,7 +8585,7 @@ packages:
/@vue-flow/core@1.3.0(vue@3.3.4):
resolution: {integrity: sha512-hitjBy8RTw8gixcgJ9sjfZWyI6KNyKp4ffxTz/O4ZN/7TMwunEdc3cFHuU7R6J1OEhZ+HnlMRGrXmzXiIfdJow==}
peerDependencies:
vue: latest
vue: ^3.2.25
dependencies:
'@vueuse/core': 9.13.0(vue@3.3.4)
d3-drag: 3.0.0
@ -8597,7 +8600,7 @@ packages:
resolution: {integrity: sha512-0/2A4kWLTCNEx+DDQKLvs7zXpfjgAbGBZ58SIvDN1DjGXhG4WaIUZtgMqzA6bvc5dNN7RaOatZYubkVumwmjWA==}
engines: {node: '>=16.14.0'}
peerDependencies:
vue: latest
vue: ^2.7.0 || ^3.2.25
peerDependenciesMeta:
vue:
optional: true
@ -8639,7 +8642,7 @@ packages:
/@vue/compat@3.3.4(vue@3.3.4):
resolution: {integrity: sha512-VwAsPqUqRJVxeLQPUC03Sa5d+T8UG2Qv4VItq74KmNvtQlRXICpa/sqq12BcyBB4Tz1U5paOEZxWCUoXkrZ9QQ==}
peerDependencies:
vue: latest
vue: 3.3.4
dependencies:
'@babel/parser': 7.22.11
estree-walker: 2.0.2
@ -8762,7 +8765,7 @@ packages:
/@vue/server-renderer@3.3.4(vue@3.3.4):
resolution: {integrity: sha512-Q6jDDzR23ViIb67v+vM1Dqntu+HUexQcsWKhhQa4ARVzxOY2HbC7QRW/ggkDBd5BU+uM1sV6XOAP0b216o34JQ==}
peerDependencies:
vue: latest
vue: 3.3.4
dependencies:
'@vue/compiler-ssr': 3.3.4
'@vue/shared': 3.3.4
@ -8778,7 +8781,7 @@ packages:
/@vue/test-utils@2.0.2(vue@3.3.4):
resolution: {integrity: sha512-E2P4oXSaWDqTZNbmKZFVLrNN/siVN78YkEqs7pHryWerrlZR9bBFLWdJwRoguX45Ru6HxIflzKl4vQvwRMwm5g==}
peerDependencies:
vue: latest
vue: ^3.0.1
dependencies:
vue: 3.3.4
dev: true
@ -8816,7 +8819,7 @@ packages:
resolution: {integrity: sha512-PRRgbATMpoeUmkCEBtUeJgOwtew8s+4UsEd+Pm7MhkjL2ihCNrSqxNVtM6NFE4uP2sWnkGcZpCjPuNSxowJ1Ow==}
peerDependencies:
'@vue/composition-api': ^1.1.0
vue: latest
vue: ^2.6.0 || ^3.2.0
peerDependenciesMeta:
'@vue/composition-api':
optional: true
@ -8909,7 +8912,7 @@ packages:
'@vueuse/core': 10.2.1(vue@3.3.4)
'@vueuse/metadata': 10.2.1
local-pkg: 0.4.3
nuxt: 3.6.5(@types/node@20.3.1)(eslint@8.33.0)(sass@1.63.4)
nuxt: 3.6.5(@types/node@20.3.1)(eslint@8.33.0)(sass@1.63.4)(typescript@5.2.2)
vue-demi: 0.14.5(vue@3.3.4)
transitivePeerDependencies:
- '@vue/composition-api'
@ -8930,7 +8933,7 @@ packages:
resolution: {integrity: sha512-rN2qd22AUl7VdBxihagWyhUNHCyVk9IpvBTTfHoLH9G7rGE552X1f+zeCfehuno0zXif13jPw+icW/wn2a0rnQ==}
peerDependencies:
'@vue/composition-api': ^1.1.0
vue: latest
vue: ^2.6.0 || ^3.2.0
peerDependenciesMeta:
'@vue/composition-api':
optional: true
@ -9396,6 +9399,11 @@ packages:
type-fest: 0.21.3
dev: true
/ansi-regex@2.1.1:
resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==}
engines: {node: '>=0.10.0'}
dev: false
/ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
@ -9405,6 +9413,11 @@ packages:
engines: {node: '>=12'}
dev: true
/ansi-styles@2.2.1:
resolution: {integrity: sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==}
engines: {node: '>=0.10.0'}
dev: false
/ansi-styles@3.2.1:
resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
engines: {node: '>=4'}
@ -9431,7 +9444,7 @@ packages:
resolution: {integrity: sha512-QKCAcOY5EJF0PepiVGA4X5PzUetYUvG5qALmA+2TON40pc2+brOEiVTwr3kjF9N+f7q4MpyiLPu4pIErwoajOQ==}
engines: {node: '>=12.22.0'}
peerDependencies:
vue: latest
vue: '>=3.2.0'
dependencies:
'@ant-design/colors': 6.0.0
'@ant-design/icons-vue': 6.1.0(vue@3.3.4)
@ -10467,6 +10480,17 @@ packages:
type-detect: 4.0.8
dev: true
/chalk@1.1.3:
resolution: {integrity: sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==}
engines: {node: '>=0.10.0'}
dependencies:
ansi-styles: 2.2.1
escape-string-regexp: 1.0.5
has-ansi: 2.0.0
strip-ansi: 3.0.1
supports-color: 2.0.0
dev: false
/chalk@2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
engines: {node: '>=4'}
@ -10720,6 +10744,7 @@ packages:
string-width: 4.2.3
strip-ansi: 6.0.1
wrap-ansi: 7.0.0
dev: true
/clone-deep@4.0.1:
resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==}
@ -10854,7 +10879,6 @@ packages:
/commander@2.20.3:
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
dev: true
/commander@4.1.1:
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
@ -12083,7 +12107,7 @@ packages:
/emoji-mart-vue-fast@15.0.0(vue@3.3.4):
resolution: {integrity: sha512-3BzkDrs60JyT00dLHMAxWKbpFhbyaW9C+q1AjtqGovSxTu8TC2mYAGsvTmXNYKm39IRRAS56v92TihOcB98IsQ==}
peerDependencies:
vue: latest
vue: '>2.0.0'
dependencies:
'@babel/runtime': 7.22.11
core-js: 3.32.1
@ -12534,7 +12558,7 @@ packages:
eslint-import-resolver-webpack:
optional: true
dependencies:
'@typescript-eslint/parser': 5.62.0(eslint@8.33.0)
'@typescript-eslint/parser': 5.62.0(eslint@8.33.0)(typescript@5.2.2)
debug: 3.2.7(supports-color@5.5.0)
eslint: 8.33.0
eslint-import-resolver-node: 0.3.9
@ -12600,7 +12624,7 @@ packages:
- supports-color
dev: true
/eslint-plugin-antfu@0.26.0(eslint@8.33.0):
/eslint-plugin-antfu@0.26.0(eslint@8.33.0)(typescript@5.2.2):
resolution: {integrity: sha512-hc5Bb6EH6zM/Vjy0scOQydlG9I1DDocG4AikyUfqjSFneWv6eNItej9LHYTXEGc0iGosCysNS4tPUAPuhBHkCA==}
dependencies:
'@typescript-eslint/utils': 5.62.0(eslint@8.33.0)(typescript@5.2.2)
@ -12637,7 +12661,7 @@ packages:
engines: {node: '>=16.10.0'}
peerDependencies:
eslint: ^8.0.0
typescript: latest
typescript: '>=4.0.2'
peerDependenciesMeta:
typescript:
optional: true
@ -12736,7 +12760,7 @@ packages:
'@typescript-eslint/parser':
optional: true
dependencies:
'@typescript-eslint/parser': 5.62.0(eslint@8.33.0)
'@typescript-eslint/parser': 5.62.0(eslint@8.33.0)(typescript@5.2.2)
array-includes: 3.1.6
array.prototype.findlastindex: 1.2.2
array.prototype.flat: 1.3.1
@ -13104,15 +13128,15 @@ packages:
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
engines: {node: '>= 0.6'}
/event-stream@4.0.1:
resolution: {integrity: sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA==}
/event-stream@3.3.4:
resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==}
dependencies:
duplexer: 0.1.2
from: 0.1.7
map-stream: 0.0.7
map-stream: 0.1.0
pause-stream: 0.0.11
split: 1.0.1
stream-combiner: 0.2.2
split: 0.3.3
stream-combiner: 0.0.4
through: 2.3.8
dev: false
@ -13650,7 +13674,7 @@ packages:
resolution: {integrity: sha512-mX3qW3idpueT2klaQXBzrIM/pHw+T0B/V9KHEvNrqijTq9NFnMZU6oreVxDYcf33P8a5cW+67PjodNHthGnNVg==}
engines: {node: '>=12.13.0', yarn: '>=1.0.0'}
peerDependencies:
typescript: latest
typescript: '>3.6.0'
webpack: ^5.11.0
dependencies:
'@babel/code-frame': 7.22.10
@ -13687,6 +13711,15 @@ packages:
mime-types: 2.1.35
dev: true
/form-data@3.0.0:
resolution: {integrity: sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==}
engines: {node: '>= 6'}
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
mime-types: 2.1.35
dev: false
/form-data@3.0.1:
resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==}
engines: {node: '>= 6'}
@ -13815,6 +13848,22 @@ packages:
resolution: {integrity: sha512-INM/fWAxMICjttnD0DX1rBvinKskj5G1w+oy/pnm9u/tSlnBrzFonJMcalKJ30P8RRsPzKcCG7Q8l0jx5Fh9YQ==}
dev: true
/fs-readfile-promise@2.0.1:
resolution: {integrity: sha512-7+P9eOOMnkIOmtxrBWTzWOBQlE7Nz/cBx9EYTX5hm8DzmZ/Fj9YWeUY2O9G+Q8YblScd1hyEkcmNcZMDj5U8Ug==}
dependencies:
graceful-fs: 4.2.11
dev: false
/fs-writefile-promise@1.0.3(mkdirp@2.1.3):
resolution: {integrity: sha512-yI+wDwj0FsgX7tyIQJR+EP60R64evMSixtGb9AzGWjJVKlF5tCet95KomfqGBg/aIAG1Dhd6wjCOQe5HbX/qLA==}
engines: {node: '>=0.10'}
dependencies:
mkdirp-promise: 1.1.0(mkdirp@2.1.3)
pinkie-promise: 1.0.0
transitivePeerDependencies:
- mkdirp
dev: false
/fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
@ -14470,6 +14519,13 @@ packages:
engines: {node: '>=6'}
dev: true
/has-ansi@2.0.0:
resolution: {integrity: sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==}
engines: {node: '>=0.10.0'}
dependencies:
ansi-regex: 2.1.1
dev: false
/has-bigints@1.0.2:
resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
@ -14678,17 +14734,23 @@ packages:
- supports-color
dev: false
/httpsnippet@3.0.1:
resolution: {integrity: sha512-RJbzVu9Gq97Ti76MPKAb9AknKbRluRbzOqswM2qgEW48QUShVEIuJjl43dZG5q0Upj2SZlKqzR6B6ah1q5znfg==}
engines: {node: ^14.19.1 || ^16.14.2 || ^18.0.0}
/httpsnippet@2.0.0(mkdirp@2.1.3):
resolution: {integrity: sha512-Hb2ttfB5OhasYxwChZ8QKpYX3v4plNvwMaMulUIC7M3RHRDf1Op6EMp47LfaU2sgQgfvo5spWK4xRAirMEisrg==}
engines: {node: '>=10'}
hasBin: true
dependencies:
chalk: 4.1.2
event-stream: 4.0.1
form-data: 4.0.0
har-schema: 2.0.0
chalk: 1.1.3
commander: 2.20.3
debug: 2.6.9
event-stream: 3.3.4
form-data: 3.0.0
fs-readfile-promise: 2.0.1
fs-writefile-promise: 1.0.3(mkdirp@2.1.3)
har-validator: 5.1.5
stringify-object: 3.3.0
yargs: 17.7.2
transitivePeerDependencies:
- mkdirp
- supports-color
dev: false
/httpxy@0.1.4:
@ -15197,7 +15259,7 @@ packages:
resolution: {integrity: sha512-DDx04RjLpGNT4vtF49vGW5CECP6lAx8SL2keq99ogIxwLvJPBvgThdhb43ED5uYO4nq0kZ51tMj7VdCCQgdZ5Q==}
peerDependencies:
eslint: '*'
typescript: latest
typescript: '>=4.7.4'
dependencies:
'@typescript-eslint/type-utils': 5.62.0(eslint@8.33.0)(typescript@5.2.2)
eslint: 8.33.0
@ -17003,8 +17065,8 @@ packages:
engines: {node: '>=8'}
dev: true
/map-stream@0.0.7:
resolution: {integrity: sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==}
/map-stream@0.1.0:
resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==}
dev: false
/marked@4.3.0:
@ -17353,6 +17415,16 @@ packages:
dev: true
optional: true
/mkdirp-promise@1.1.0(mkdirp@2.1.3):
resolution: {integrity: sha512-xzB0UZFcW1UGS2xkXeDh39jzTP282lb3Vwp4QzCQYmkTn4ysaV5dBdbkOXmhkcE1TQlZebQlgTceaWvDr3oFgw==}
engines: {node: '>=4'}
deprecated: This package is broken and no longer maintained. 'mkdirp' itself supports promises now, please switch to that.
peerDependencies:
mkdirp: '>=0.5.0'
dependencies:
mkdirp: 2.1.3
dev: false
/mkdirp@0.5.6:
resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
hasBin: true
@ -18240,7 +18312,7 @@ packages:
- vite
dev: true
/nuxt@3.6.5(@types/node@20.3.1)(eslint@8.33.0)(sass@1.63.4):
/nuxt@3.6.5(@types/node@20.3.1)(eslint@8.33.0)(sass@1.63.4)(typescript@5.2.2):
resolution: {integrity: sha512-0A7V8B1HrIXX9IlqPc2w+5ZPXi+7MYa9QVhtuGYuLvjRKoSFANhCoMPRP6pKdoxigM1MBxhLue2VmHA/VbtJCw==}
engines: {node: ^14.18.0 || >=16.10.0}
hasBin: true
@ -18256,7 +18328,7 @@ packages:
'@nuxt/schema': 3.6.5
'@nuxt/telemetry': 2.4.1
'@nuxt/ui-templates': 1.3.1
'@nuxt/vite-builder': 3.6.5(@types/node@20.3.1)(eslint@8.33.0)(sass@1.63.4)(vue@3.3.4)
'@nuxt/vite-builder': 3.6.5(@types/node@20.3.1)(eslint@8.33.0)(sass@1.63.4)(typescript@5.2.2)(vue@3.3.4)
'@types/node': 20.3.1
'@unhead/ssr': 1.3.9
'@unhead/vue': 1.3.9(vue@3.3.4)
@ -19130,12 +19202,12 @@ packages:
engines: {node: '>=10'}
dev: true
/pinia@2.1.4(vue@3.3.4):
/pinia@2.1.4(typescript@5.2.2)(vue@3.3.4):
resolution: {integrity: sha512-vYlnDu+Y/FXxv1ABo1vhjC+IbqvzUdiUC3sfDRrRyY2CQSrqqaa+iiHmqtARFxJVqWQMCJfXx1PBvFs9aJVLXQ==}
peerDependencies:
'@vue/composition-api': ^1.4.0
typescript: latest
vue: latest
typescript: '>=4.4.4'
vue: ^2.6.14 || ^3.3.0
peerDependenciesMeta:
'@vue/composition-api':
optional: true
@ -19143,10 +19215,23 @@ packages:
optional: true
dependencies:
'@vue/devtools-api': 6.5.0
typescript: 5.2.2
vue: 3.3.4
vue-demi: 0.14.5(vue@3.3.4)
dev: false
/pinkie-promise@1.0.0:
resolution: {integrity: sha512-5mvtVNse2Ml9zpFKkWBpGsTPwm3DKhs+c95prO/F6E7d6DN0FPqxs6LONpLNpyD7Iheb7QN4BbUoKJgo+DnkQA==}
engines: {node: '>=0.10.0'}
dependencies:
pinkie: 1.0.0
dev: false
/pinkie@1.0.0:
resolution: {integrity: sha512-VFVaU1ysKakao68ktZm76PIdOhvEfoNNRaGkyLln9Os7r0/MCxqHjHyBM7dT3pgTiBybqiPtpqKfpENwdBp50Q==}
engines: {node: '>=0.10.0'}
dev: false
/pirates@4.0.6:
resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==}
engines: {node: '>= 6'}
@ -21024,10 +21109,17 @@ packages:
resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
engines: {node: '>= 10.x'}
/split@0.3.3:
resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==}
dependencies:
through: 2.3.8
dev: false
/split@1.0.1:
resolution: {integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==}
dependencies:
through: 2.3.8
dev: true
/splitpanes@3.1.5:
resolution: {integrity: sha512-r3Mq2ITFQ5a2VXLOy4/Sb2Ptp7OfEO8YIbhVJqJXoFc9hc5nTXXkCvtVDjIGbvC0vdE7tse+xTM9BMjsszP6bw==}
@ -21149,11 +21241,10 @@ packages:
engines: {node: '>=4', npm: '>=6'}
dev: false
/stream-combiner@0.2.2:
resolution: {integrity: sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==}
/stream-combiner@0.0.4:
resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==}
dependencies:
duplexer: 0.1.2
through: 2.3.8
dev: false
/stream-events@1.0.5:
@ -21281,6 +21372,13 @@ packages:
is-regexp: 1.0.0
dev: false
/strip-ansi@3.0.1:
resolution: {integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==}
engines: {node: '>=0.10.0'}
dependencies:
ansi-regex: 2.1.1
dev: false
/strip-ansi@6.0.1:
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
engines: {node: '>=8'}
@ -21395,6 +21493,11 @@ packages:
- supports-color
dev: true
/supports-color@2.0.0:
resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==}
engines: {node: '>=0.8.0'}
dev: false
/supports-color@5.5.0:
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
engines: {node: '>=4'}
@ -21887,7 +21990,7 @@ packages:
resolution: {integrity: sha512-Cbu4nIqnEdd+THNEsBdkolnOXhg0I8XteoHaEKgvsxpsbWda4IsUut2c187HxywQCvveojow0Dgw/amxtSKVkQ==}
engines: {node: '>=16.13.0'}
peerDependencies:
typescript: latest
typescript: '>=4.2.0'
dependencies:
typescript: 5.2.2
dev: true
@ -21907,7 +22010,7 @@ packages:
babel-jest: ^29.0.0
esbuild: '*'
jest: ^29.0.0
typescript: latest
typescript: '>=4.3'
peerDependenciesMeta:
'@babel/core':
optional: true
@ -21935,7 +22038,7 @@ packages:
resolution: {integrity: sha512-MLukxDHBl8OJ5Dk3y69IsKVFRA/6MwzEqBgh+OXMPB/OD01KQuWPFd1WAQP8a5PeSCAxfnkhiuWqfmFJzJQt9w==}
engines: {node: '>=12.0.0'}
peerDependencies:
typescript: latest
typescript: '*'
webpack: ^5.0.0
dependencies:
chalk: 4.1.2
@ -21943,20 +22046,6 @@ packages:
micromatch: 4.0.5
semver: 7.5.4
typescript: 5.2.2
webpack: 5.88.2(webpack-cli@5.1.4)
dev: true
/ts-loader@9.4.4(webpack@5.88.2):
resolution: {integrity: sha512-MLukxDHBl8OJ5Dk3y69IsKVFRA/6MwzEqBgh+OXMPB/OD01KQuWPFd1WAQP8a5PeSCAxfnkhiuWqfmFJzJQt9w==}
engines: {node: '>=12.0.0'}
peerDependencies:
typescript: latest
webpack: ^5.0.0
dependencies:
chalk: 4.1.2
enhanced-resolve: 5.15.0
micromatch: 4.0.5
semver: 7.5.4
webpack: 5.88.2(esbuild@0.19.2)
dev: true
@ -21981,7 +22070,7 @@ packages:
'@swc/core': '>=1.2.50'
'@swc/wasm': '>=1.2.50'
'@types/node': '*'
typescript: latest
typescript: '>=2.7'
peerDependenciesMeta:
'@swc/core':
optional: true
@ -22071,7 +22160,7 @@ packages:
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
engines: {node: '>= 6'}
peerDependencies:
typescript: latest
typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta'
dependencies:
tslib: 1.14.1
typescript: 5.2.2
@ -22229,7 +22318,6 @@ packages:
resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==}
engines: {node: '>=14.17'}
hasBin: true
dev: true
/ufo@1.3.0:
resolution: {integrity: sha512-bRn3CsoojyNStCZe0BG0Mt4Nr/4KF+rhFlnNXybgqt5pXHNFRlqinSoQaTrGyzE4X8aHplSb+TorH+COin9Yxw==}
@ -22491,7 +22579,7 @@ packages:
engines: {node: '>=14'}
peerDependencies:
'@babel/parser': ^7.15.8
vue: latest
vue: 2 || 3
peerDependenciesMeta:
'@babel/parser':
optional: true
@ -22844,7 +22932,7 @@ packages:
- terser
dev: true
/vite-plugin-checker@0.6.2(eslint@8.33.0)(vite@4.3.9):
/vite-plugin-checker@0.6.2(eslint@8.33.0)(typescript@5.2.2)(vite@4.3.9):
resolution: {integrity: sha512-YvvvQ+IjY09BX7Ab+1pjxkELQsBd4rPhWNw8WLBeFVxu/E7O+n6VYAqNsKdK/a2luFlX/sMpoWdGFfg4HvwdJQ==}
engines: {node: '>=14.16'}
peerDependencies:
@ -22852,7 +22940,7 @@ packages:
meow: ^9.0.0
optionator: ^0.9.1
stylelint: '>=13'
typescript: latest
typescript: '*'
vite: '>=2.0.0'
vls: '*'
vti: '*'
@ -22889,6 +22977,7 @@ packages:
semver: 7.5.4
strip-ansi: 6.0.1
tiny-invariant: 1.3.1
typescript: 5.2.2
vite: 4.3.9(@types/node@20.3.1)(sass@1.63.4)
vscode-languageclient: 7.0.0
vscode-languageserver: 7.0.0
@ -23147,7 +23236,7 @@ packages:
resolution: {integrity: sha512-d3zpKmGZr2OWHQ1xmxBcAn5ShTG917+/UCLaSpaCDDqT0U7DBsvFzTs69ZnHCgKoXT55GZDW8YEj9Av+dlONLA==}
peerDependencies:
chart.js: ^4.1.1
vue: latest
vue: ^3.0.0-0 || ^2.7.0
dependencies:
chart.js: 4.3.0
vue: 3.3.4
@ -23160,7 +23249,7 @@ packages:
requiresBuild: true
peerDependencies:
'@vue/composition-api': ^1.0.0-rc.1
vue: latest
vue: ^3.0.0-0 || ^2.6.0
peerDependenciesMeta:
'@vue/composition-api':
optional: true
@ -23175,7 +23264,7 @@ packages:
requiresBuild: true
peerDependencies:
'@vue/composition-api': ^1.0.0-rc.1
vue: latest
vue: ^3.0.0-0 || ^2.6.0
peerDependenciesMeta:
'@vue/composition-api':
optional: true
@ -23189,7 +23278,7 @@ packages:
requiresBuild: true
peerDependencies:
'@vue/composition-api': ^1.0.0-rc.1
vue: latest
vue: ^3.0.0-0 || ^2.6.0
peerDependenciesMeta:
'@vue/composition-api':
optional: true
@ -23203,7 +23292,7 @@ packages:
/vue-dompurify-html@3.0.0(vue@3.3.4):
resolution: {integrity: sha512-S6PMeJU7S3w0TnxMWWd4iydc7oPdOER1GmW9rsgiRwHvcw+nUi2v6BgERcFBULlM+x6PXsfu5P/Rm4reVvWH5A==}
peerDependencies:
vue: latest
vue: ^3.0.0
dependencies:
dompurify: 2.4.7
vue: 3.3.4
@ -23237,7 +23326,7 @@ packages:
resolution: {integrity: sha512-yswpwtj89rTBhegUAv9Mu37LNznyu3NpyLQmozF3i1hYOhwpG8RjcjIFIIfnu+2MDZJGSZPXaKWvnQA71Yv9TQ==}
engines: {node: '>= 14'}
peerDependencies:
vue: latest
vue: ^3.0.0
dependencies:
'@intlify/core-base': 9.2.2
'@intlify/shared': 9.2.2
@ -23259,7 +23348,7 @@ packages:
/vue-router@4.2.4(vue@3.3.4):
resolution: {integrity: sha512-9PISkmaCO02OzPVOMq2w82ilty6+xJmQrarYZDkjZBfl4RvYAlt4PKnEX21oW4KTtWfa9OuO/b3qk1Od3AEdCQ==}
peerDependencies:
vue: latest
vue: ^3.2.0
dependencies:
'@vue/devtools-api': 6.5.0
vue: 3.3.4
@ -23269,7 +23358,7 @@ packages:
resolution: {integrity: sha512-IwUC0Aq2zwaXqy74h4WCvFCUtoV0iSWr0snWnE9TnU18S66GAQyqQbRf2qfJtUuiFsBf6qp0MEwdonlwznlcrw==}
engines: {node: '>=10.15.0'}
peerDependencies:
vue: latest
vue: ^3.0.0
dependencies:
is-plain-object: 3.0.1
vue: 3.3.4
@ -23279,7 +23368,7 @@ packages:
resolution: {integrity: sha512-BchyC33WiZryYatFINj3LWqgyE6X82Huzf7abA23tsF/IbaRZVwZzie8SmGaYvezEBiPXhJogQ3dtxIuXFjkBw==}
engines: {node: '>=12'}
peerDependencies:
vue: latest
vue: ^3.2.24
dependencies:
tippy.js: 6.3.7
vue: 3.3.4
@ -23314,7 +23403,7 @@ packages:
resolution: {integrity: sha512-l/30RvXLkw50axAjswAK1DmvbUc5Oyhq9GkvD98p8pykrLkIajRi3evVsMnahMBK0O7+EGIK9RbIOKPyRfuw7w==}
peerDependencies:
resize-detector: ^0.3.0
vue: latest
vue: ^3.2.37
dependencies:
resize-detector: 0.3.0
vue: 3.3.4
@ -23332,7 +23421,7 @@ packages:
/vuedraggable@4.1.0(vue@3.3.4):
resolution: {integrity: sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==}
peerDependencies:
vue: latest
vue: ^3.0.1
dependencies:
sortablejs: 1.14.0
vue: 3.3.4
@ -24050,6 +24139,7 @@ packages:
/y18n@5.0.8:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
engines: {node: '>=10'}
dev: true
/yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
@ -24100,6 +24190,7 @@ packages:
/yargs-parser@21.1.1:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
engines: {node: '>=12'}
dev: true
/yargs-unparser@2.0.0:
resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==}
@ -24152,6 +24243,7 @@ packages:
string-width: 4.2.3
y18n: 5.0.8
yargs-parser: 21.1.1
dev: true
/yauzl@2.10.0:
resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==}

1
tests/playwright/pages/Dashboard/Command/CmdKPage.ts

@ -21,6 +21,7 @@ export class CmdK extends BasePage {
async searchText(text: string) {
await this.dashboardPage.rootPage.fill('.cmdk-input', text);
await this.rootPage.keyboard.press('Enter');
await this.rootPage.keyboard.press('Enter');
}
async isCmdKVisible() {

Loading…
Cancel
Save