-
-
-
+
-
+
{
data-testid="nc-token-input"
@press-enter="generateToken"
/>
- {{ errorMessage }}
+ {{ errorMessage }}
+
@@ -224,17 +237,19 @@ const handleCancel = () => {
-
-
+
@@ -250,39 +265,42 @@ const handleCancel = () => {
{{ el.token }}
- **************************************
+ ************************************
-
-
-
+
+
-
- {{ $t('labels.showOrHide') }}
-
-
-
- {{ $t('general.copy') }}
-
-
-
- {{ $t('general.delete') }}
-
-
-
-
+
+
+ {{ $t('labels.showOrHide') }}
+
+
+
+ {{ $t('general.copy') }}
+
+
+
+ {{ $t('general.delete') }}
+
+
+
+
+
{
+
+
diff --git a/packages/nc-gui/components/account/UserList.vue b/packages/nc-gui/components/account/UserList.vue
index cbffbbdfc8..4d29a0ed5e 100644
--- a/packages/nc-gui/components/account/UserList.vue
+++ b/packages/nc-gui/components/account/UserList.vue
@@ -153,9 +153,9 @@ const openDeleteModal = (user: UserType) => {
-
-
-
{{ $t('title.userManagement') }}
+
+
+
@@ -165,42 +165,50 @@ const openDeleteModal = (user: UserType) => {
-
-
{{ $t('title.userManagement') }}
+
{{ $t('activity.inviteUser') }}
-
- {{ $t('labels.email') }}
- {{ $t('objects.role') }}
- {{ $t('labels.action') }}
+
+
-
+
+ {{ $t('labels.email') }}
+
+ {{ $t('objects.role') }}
+
+ {{ $t('labels.action') }}
+
+
-
+
-
+
+
+
+
-
-
+
-
-
+
-
+
-
+
{{ $t('general.remove') }} {{ $t('objects.user') }}
@@ -271,7 +282,7 @@ const openDeleteModal = (user: UserType) => {
-
-
- {{ el.email }}
-
-
-
+
+ {{ el.email }}
+
+
+ {{ $t('labels.superAdmin') }}
-
+ {
:value="OrgUserRoles.CREATOR"
:label="$t(`objects.roleType.orgLevelCreator`)"
>
-
+
+
+ {{ $t('labels.superAdmin') }}
+
+ {{ $t(`objects.roleType.orgLevelCreator`) }}
-
+ {{ $t(`objects.roleType.orgLevelCreator`) }}
+
{{ $t('msg.info.roles.orgCreator') }}
@@ -223,14 +231,14 @@ const openDeleteModal = (user: UserType) => {
:value="OrgUserRoles.VIEWER"
:label="$t(`objects.roleType.orgLevelViewer`)"
>
- {{ $t(`objects.roleType.orgLevelViewer`) }}
-
+ {{ $t(`objects.roleType.orgLevelViewer`) }}
+
{{ $t('msg.info.roles.orgViewer') }}
-
-
-
+ {{ $t('activity.resendInvite') }}
+ {{ $t('activity.resendInvite') }}
{{ $t('activity.copyInviteURL') }}
+ {{ $t('activity.copyInviteURL') }}
{{ $t('activity.copyPasswordResetURL') }}
+
{
{{ deleteModalInfo?.email }}
@@ -301,7 +312,7 @@ const openDeleteModal = (user: UserType) => {
diff --git a/packages/nc-gui/components/account/UsersModal.vue b/packages/nc-gui/components/account/UsersModal.vue
index 489508d9f8..8c03f48ccc 100644
--- a/packages/nc-gui/components/account/UsersModal.vue
+++ b/packages/nc-gui/components/account/UsersModal.vue
@@ -80,7 +80,7 @@ const copyUrl = async () => {
await copy(inviteUrl.value)
// Copied shareable source url to clipboard!
- message.success(t('msg.success.shareableURLCopied'))
+ message.success(t('msg.toast.inviteUrlCopy'))
} catch (e: any) {
message.error(e.message)
}
@@ -110,7 +110,7 @@ const emailInput: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
>
- {{ $t('activity.inviteUser') }}
+ {{ $t('activity.inviteUser') }}
@@ -124,13 +124,13 @@ const emailInput: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
-
{{ $t('activity.copyInviteToken') }}
+ {{ $t('activity.copyInviteURL') }}
-
+ (el as HTMLInputElement)?.focus()
-
(el as HTMLInputElement)?.focus()
:value="OrgUserRoles.CREATOR"
:label="$t(`objects.roleType.orgLevelCreator`)"
>
-
@@ -210,8 +210,8 @@ const emailInput: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
:value="OrgUserRoles.VIEWER"
:label="$t(`objects.roleType.orgLevelViewer`)"
>
-
-
diff --git a/packages/nc-gui/components/api-client/Headers.vue b/packages/nc-gui/components/api-client/Headers.vue
index 9b58046552..4d76134580 100644
--- a/packages/nc-gui/components/api-client/Headers.vue
+++ b/packages/nc-gui/components/api-client/Headers.vue
@@ -66,10 +66,10 @@ const filterOption = (input: string, option: Option) => option.value.toUpperCase
-
-
@@ -124,7 +124,7 @@ const filterOption = (input: string, option: Option) => option.value.toUpperCase
diff --git a/packages/nc-gui/components/api-client/Params.vue b/packages/nc-gui/components/api-client/Params.vue
index 66a2e79336..70fbb5be89 100644
--- a/packages/nc-gui/components/api-client/Params.vue
+++ b/packages/nc-gui/components/api-client/Params.vue
@@ -24,11 +24,11 @@ const deleteParamRow = (i: number) => {
-
-
@@ -69,7 +69,7 @@ const deleteParamRow = (i: number) => {
diff --git a/packages/nc-gui/components/cell/Checkbox.vue b/packages/nc-gui/components/cell/Checkbox.vue
index edf550808b..be70d8d314 100644
--- a/packages/nc-gui/components/cell/Checkbox.vue
+++ b/packages/nc-gui/components/cell/Checkbox.vue
@@ -91,7 +91,11 @@ useSelectedCellKeyupListener(active, (e) => {
}"
@click="onClick(false, $event)"
>
-
-
+
@@ -550,7 +600,7 @@ const isEditBaseModalOpen = computed({
diff --git a/packages/nc-gui/components/nc/Tooltip.vue b/packages/nc-gui/components/nc/Tooltip.vue
index 0810b8b3ab..97b159e4a9 100644
--- a/packages/nc-gui/components/nc/Tooltip.vue
+++ b/packages/nc-gui/components/nc/Tooltip.vue
@@ -12,6 +12,7 @@ interface Props {
disabled?: boolean
placement?: TooltipPlacement | undefined
hideOnClick?: boolean
+ overlayClassName?: string
}
const props = defineProps()
@@ -36,6 +37,8 @@ const attrs = useAttrs()
const isKeyPressed = ref(false)
+const overlayClassName = computed(() => props.overlayClassName)
+
onKeyStroke(
(e) => e.key === modifierKey.value,
(e) => {
@@ -100,7 +103,7 @@ const onClick = () => {
{
-
-
+
diff --git a/packages/nc-gui/components/project/View.vue b/packages/nc-gui/components/project/View.vue
index e7d91c927d..00bbdd3079 100644
--- a/packages/nc-gui/components/project/View.vue
+++ b/packages/nc-gui/components/project/View.vue
@@ -3,7 +3,7 @@ import { useTitle } from '@vueuse/core'
import NcLayout from '~icons/nc-icons/layout'
const { openedProject } = storeToRefs(useBases())
const { activeTables } = storeToRefs(useTablesStore())
-const { activeWorkspace } = storeToRefs(useWorkspace())
+const { activeWorkspace, workspaceUserCount } = storeToRefs(useWorkspace())
const { navigateToProjectPage } = useBase()
@@ -113,6 +113,16 @@ watch(
@@ -130,7 +140,7 @@ watch(
'bg-gray-50': projectPageTab !== 'data-source',
}"
>
- {{ base.sources.length - 1 }}
+ {{ base.sources.length }}
diff --git a/packages/nc-gui/components/smartsheet/Details.vue b/packages/nc-gui/components/smartsheet/Details.vue
index 5c5915a452..32484a243f 100644
--- a/packages/nc-gui/components/smartsheet/Details.vue
+++ b/packages/nc-gui/components/smartsheet/Details.vue
@@ -33,7 +33,7 @@ const openedSubTab = computed({
watch(openedSubTab, () => {
// TODO: Find a good way to know when the roles are populated and check
// Re-enable this check for first render
- if (openedSubTab.value === 'field' && !isUIAllowed('hookList')) {
+ if (openedSubTab.value === 'field' && !isUIAllowed('fieldAdd')) {
onViewsTabChange('relation')
}
if (openedSubTab.value === 'webhook' && !isUIAllowed('hookList')) {
diff --git a/packages/nc-gui/components/smartsheet/Kanban.vue b/packages/nc-gui/components/smartsheet/Kanban.vue
index b9fd4b1e62..a0ce7c4859 100644
--- a/packages/nc-gui/components/smartsheet/Kanban.vue
+++ b/packages/nc-gui/components/smartsheet/Kanban.vue
@@ -1,6 +1,6 @@
@@ -425,7 +435,7 @@ watch(
e.target.classList.remove('grabbing')"
@change="onMove($event, stack.title)"
>
-
+
-
-
-
-
{{ inviteUrl }}
@@ -143,7 +143,7 @@ const emailInput: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
-
+
-
@@ -177,7 +177,7 @@ const emailInput: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
name="emails"
:rules="[{ required: true, message: 'Please input email' }]"
>
-
{{ $t('msg.info.userInviteNoSMTP') }}
{{ usersData.invitationToken && usersData.emails }}
@@ -153,7 +153,7 @@ const emailInput: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
{{ $t('activity.inviteMore') }}
+ {{ $t('activity.inviteMore') }}
{{ $t('datatype.Email') }}:
+ {{ $t('datatype.Email') }}:
{{ $t('labels.selectUserRole') }}
+ {{ $t('labels.selectUserRole') }}
{{ $t(`objects.roleType.orgLevelCreator`) }}
-
+ {{ $t(`objects.roleType.orgLevelCreator`) }}
+
{{ $t('msg.info.roles.orgCreator') }}
{{ $t(`objects.roleType.orgLevelViewer`) }}
-
+ {{ $t(`objects.roleType.orgLevelViewer`) }}
+
{{ $t('msg.info.roles.orgViewer') }}
@@ -224,7 +224,7 @@ const emailInput: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
{{ $t('activity.invite') }}
+ {{ $t('activity.invite') }}
{{ $t('labels.headerName') }}
+ {{ $t('labels.headerName') }}
{{ $t('placeholder.value') }}
+ {{ $t('placeholder.value') }}
-
{{ $t('labels.addHeader') }}
+ {{ $t('labels.addHeader') }}
{{ $t('title.parameterName') }}
+ {{ $t('title.parameterName') }}
{{ $t('placeholder.value') }}
+ {{ $t('placeholder.value') }}
-
{{ $t('activity.addParameter') }}
+ {{ $t('activity.addParameter') }}
+
()
const error = ref()
-const isExpanded = inject(JsonExpandInj, ref(false))
+const _isExpanded = inject(JsonExpandInj, ref(false))
+
+const isExpanded = ref(false)
const localValue = computed | undefined>({
get: () => localValueState.value,
@@ -137,6 +139,10 @@ useSelectedCellKeyupListener(active, (e) => {
break
}
})
+
+watch(isExpanded, () => {
+ _isExpanded.value = isExpanded.value
+})
diff --git a/packages/nc-gui/components/cell/attachment/index.vue b/packages/nc-gui/components/cell/attachment/index.vue
index b7565bec45..b96b862492 100644
--- a/packages/nc-gui/components/cell/attachment/index.vue
+++ b/packages/nc-gui/components/cell/attachment/index.vue
@@ -8,6 +8,7 @@ import {
DropZoneRef,
IsExpandedFormOpenInj,
IsGalleryInj,
+ IsKanbanInj,
RowHeightInj,
iconMap,
inject,
@@ -46,6 +47,8 @@ const isLockedMode = inject(IsLockedInj, ref(false))
const isGallery = inject(IsGalleryInj, ref(false))
+const isKanban = inject(IsKanbanInj, ref(false))
+
const isExpandedForm = inject(IsExpandedFormOpenInj, ref(false))
const { isSharedForm } = useSmartsheetStoreOrThrow()!
@@ -185,6 +188,7 @@ const onExpand = () => {
v-model="isOverDropZone"
inline
:target="currentCellRef"
+ data-rec="true"
class="nc-attachment-cell-dropzone text-white text-lg ring ring-accent ring-opacity-100 bg-gray-700/75 flex items-center justify-center gap-2 backdrop-blur-xl"
>
@@ -202,7 +206,9 @@ const onExpand = () => {
- {{ $t('activity.attachmentDrop') }}
+ {{ $t('activity.attachmentDrop') }}
{
/>
@@ -419,12 +443,12 @@ const isEditBaseModalOpen = computed({
-
{{ $t('activity.addFiles') }}
@@ -223,7 +230,7 @@ const onExpand = () => {
diff --git a/packages/nc-gui/components/dashboard/TreeView/ProjectNode.vue b/packages/nc-gui/components/dashboard/TreeView/ProjectNode.vue
index ee25b2f441..235a751640 100644
--- a/packages/nc-gui/components/dashboard/TreeView/ProjectNode.vue
+++ b/packages/nc-gui/components/dashboard/TreeView/ProjectNode.vue
@@ -307,12 +307,6 @@ function openErdView(source: SourceType) {
}
}
-const reloadTables = async () => {
- $e('a:table:refresh:navdraw')
-
- // await loadTables()
-}
-
const contextMenuBase = computed(() => {
if (contextMenuTarget.type === 'source') {
return contextMenuTarget.value
@@ -483,7 +477,7 @@ const projectDelete = () => {
-
+
{
class="source-context flex items-center gap-2 text-gray-800 nc-sidebar-node-title"
@contextmenu="setMenuContext('source', source)"
>
-
+
{{ $t('general.default') }}
@@ -351,58 +351,82 @@ const isEditBaseModalOpen = computed({
{
- if (isGallery || isMobileMode) return
+ if (isGallery || isMobileMode || (isKanban && !isExpandedForm)) return
selectedImage = item
}
"
diff --git a/packages/nc-gui/components/dashboard/Sidebar.vue b/packages/nc-gui/components/dashboard/Sidebar.vue
index e3e46b432e..b261f9f255 100644
--- a/packages/nc-gui/components/dashboard/Sidebar.vue
+++ b/packages/nc-gui/components/dashboard/Sidebar.vue
@@ -54,7 +54,7 @@ onUnmounted(() => {
'pt-0.25': isSharedBase,
}"
>
-
+
{
class="source-context flex flex-grow items-center gap-1.75 text-gray-800 min-w-1/20 max-w-full"
@contextmenu="setMenuContext('source', source)"
>
-
+
-
-
diff --git a/packages/nc-gui/components/dashboard/View.vue b/packages/nc-gui/components/dashboard/View.vue
index cafb2c3af2..d2fc9316ca 100644
--- a/packages/nc-gui/components/dashboard/View.vue
+++ b/packages/nc-gui/components/dashboard/View.vue
@@ -111,6 +111,14 @@ watch(isMobileMode, () => {
isLeftSidebarOpen.value = !isMobileMode.value
})
+watch(sidebarState, () => {
+ if (sidebarState.value === 'peekCloseEnd') {
+ setTimeout(() => {
+ sidebarState.value = 'hiddenEnd'
+ }, animationDuration)
+ }
+})
+
onMounted(() => {
handleSidebarOpenOnMobileForNonViews()
})
@@ -132,7 +140,7 @@ onMounted(() => {
>
{
-
-
-
- {{ $t('general.reload') }}
-
- {{ $t('general.visibility') }}
{{ $t('general.name') }}
{{ $t('general.type') }}
- {{ $t('labels.actions') }}
+ {{ $t('labels.actions') }}
-
-
-
-
-
-
-
-
+
+
+
+
-
+
+
{{ $t('tooltip.metaSync') }}
-
-
-
+
+
+
+
+
+
{{ $t('title.relations') }}
-
-
+
+
+
-
+
+
+
+
+
+
{{ $t('labels.uiAcl') }}
-
-
+
+
+
-
+
+
+
+
+
+
{{ $t('title.audit') }}
-
-
+
+
+
+
+
+
-
-
+
+
- -
-
+
{{ source.is_meta || source.is_local ? $t('general.base') : source.alias }}
-
+
@@ -437,71 +461,97 @@ const isEditBaseModalOpen = computed({
-
-
-
-
-
-
+
+
+
+
-
+
+
{{ $t('title.relations') }}
-
-
-
+
+
+
+
+
+
{{ $t('labels.uiAcl') }}
-
-
+
+
+
-
+
+
+
+
+
+
{{ $t('tooltip.metaSync') }}
-
-
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+ {{ $t('general.edit') }}
+
+
+
+
+
+
+
+ {{ $t('general.delete') }}
+
+
+
+
+
-
-
- -
-
-
+
@@ -158,11 +158,7 @@ const onCreateBaseClick = () => {
+
{{ sources.get(table.source_id!)?.alias }}
-
-
- {{ $t('labels.members') }}
+
+ {{ workspaceUserCount }}
+
-
+
+
+
+
+
-
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+ ()
@@ -34,6 +35,8 @@ const extraStyle = toRef(props, 'extraStyle')
const isGroupBy = inject(IsGroupByInj, ref(false))
+const alignLeft = computed(() => props.alignLeft ?? false)
+
const { isViewDataLoading, isPaginationLoading } = storeToRefs(useViewsStore())
const { isLeftSidebarOpen } = storeToRefs(useSidebarStore())
@@ -69,7 +72,12 @@ const isRTLLanguage = computed(() => isRtlLang(locale.value as keyof typeof Lang
isGroupBy ? 'margin-top:1px; border-radius: 0 0 12px 12px !important;' : ''
}${extraStyle}`"
>
-
+
+
+
+
+
+
+
@@ -755,4 +793,37 @@ watch(
transform-origin: left top 0px;
transition: left 0.2s ease-in-out 0s;
}
+
+:deep(.slick-dots li button) {
+ @apply !bg-black;
+}
+
+.ant-carousel.gallery-carousel :deep(.slick-dots) {
+ @apply !w-auto absolute h-auto bottom-[-15px] absolute h-auto;
+ height: auto;
+}
+
+.ant-carousel.gallery-carousel :deep(.slick-dots li div > div) {
+ @apply rounded-full border-0 cursor-pointer block opacity-100 p-0 outline-none transition-all duration-500 text-transparent h-2 w-2 bg-[#d9d9d9];
+ font-size: 0;
+}
+
+.ant-carousel.gallery-carousel :deep(.slick-dots li.slick-active div > div) {
+ @apply bg-brand-500 opacity-100;
+}
+
+.ant-carousel.gallery-carousel :deep(.slick-dots li) {
+ @apply !w-auto;
+}
+.ant-carousel.gallery-carousel :deep(.slick-prev) {
+ @apply left-0;
+}
+
+.ant-carousel.gallery-carousel :deep(.slick-next) {
+ @apply right-0;
+}
+
+:deep(.slick-slide) {
+ @apply !pointer-events-none;
+}
diff --git a/packages/nc-gui/components/smartsheet/Pagination.vue b/packages/nc-gui/components/smartsheet/Pagination.vue
index 5be2598992..3c3de49788 100644
--- a/packages/nc-gui/components/smartsheet/Pagination.vue
+++ b/packages/nc-gui/components/smartsheet/Pagination.vue
@@ -14,6 +14,7 @@ interface Props {
fixedSize?: number
extraStyle?: string
showApiTiming?: boolean
+ alignLeft?: boolean
}
const props = defineProps
+
+
+
+
-
+
+
-
-
+
isRtlLang(locale.value as keyof typeof Lang
v-if="!hidePagination"
class="transition-all duration-350"
:class="{
- '-ml-17': isLeftSidebarOpen,
+ '-ml-17': isLeftSidebarOpen && !alignLeft,
+ 'ml-8': alignLeft,
}"
>
diff --git a/packages/nc-gui/components/smartsheet/column/EditOrAdd.vue b/packages/nc-gui/components/smartsheet/column/EditOrAdd.vue
index a6056c8c20..9fea597e9d 100644
--- a/packages/nc-gui/components/smartsheet/column/EditOrAdd.vue
+++ b/packages/nc-gui/components/smartsheet/column/EditOrAdd.vue
@@ -48,7 +48,7 @@ const { getMeta } = useMetas()
const { t } = useI18n()
-const columnLabel = computed(() => props.columnLabel || t('objects.column'))
+const columnLabel = computed(() => props.columnLabel || t('objects.field'))
const { $e } = useNuxtApp()
@@ -66,7 +66,7 @@ const isForm = inject(IsFormInj, ref(false))
const isKanban = inject(IsKanbanInj, ref(false))
-const { isMysql, isMssql } = useBase()
+const { isMysql, isMssql, isXcdbBase } = useBase()
const reloadDataTrigger = inject(ReloadViewDataHookInj)
@@ -86,9 +86,9 @@ const showDeprecated = ref(false)
const uiTypesOptions = computed(() => {
return [
- ...uiTypes.filter(
- (t) => geoDataToggleCondition(t) && (!isEdit.value || !t.virtual) && (!t.deprecated || showDeprecated.value),
- ),
+ ...uiTypes
+ .filter((t) => geoDataToggleCondition(t) && (!isEdit.value || !t.virtual) && (!t.deprecated || showDeprecated.value))
+ .filter((t) => !(t.name === UITypes.SpecificDBType && isXcdbBase(meta.value?.source_id))),
...(!isEdit.value && meta?.value?.columns?.every((c) => !c.pk)
? [
{
@@ -214,7 +214,7 @@ if (props.fromTableExplorer) {
'bg-white': !props.fromTableExplorer,
'w-[400px]': !props.embedMode,
'!w-[600px]': formState.uidt === UITypes.Formula && !props.embedMode,
- '!w-[500px]': formState.uidt === UITypes.Attachment && !props.embedMode,
+ '!w-[500px]': formState.uidt === UITypes.Attachment && !props.embedMode && !appInfo.ee,
'shadow-lg border-1 border-gray-50 shadow-gray-100 rounded-md p-6': !embedMode,
}"
@keydown="handleEscape"
@@ -331,7 +331,7 @@ if (props.fromTableExplorer) {
diff --git a/packages/nc-gui/components/smartsheet/column/LookupOptions.vue b/packages/nc-gui/components/smartsheet/column/LookupOptions.vue
index b079c03deb..7efcfee430 100644
--- a/packages/nc-gui/components/smartsheet/column/LookupOptions.vue
+++ b/packages/nc-gui/components/smartsheet/column/LookupOptions.vue
@@ -44,7 +44,7 @@ const refTables = computed(() => {
isLinksOrLTAR(column) &&
!column.system &&
column.source_id === meta.value?.source_id &&
- (!appInfo.value.ee || (column?.colOptions as any)?.type === 'bt'),
+ (!appInfo.value.ee || vModel.value.fk_relation_column_id === column.id || (column?.colOptions as any)?.type === 'bt'),
)
.map((column) => ({
col: column.colOptions,
@@ -61,7 +61,8 @@ const columns = computed(() => {
return []
}
return metas.value[selectedTable.id].columns.filter(
- (c: ColumnType) => !isSystemColumn(c) && c.id !== vModel.value.id && c.uidt !== UITypes.Links,
+ (c: ColumnType) =>
+ vModel.value.fk_lookup_column_id === c.id || (!isSystemColumn(c) && c.id !== vModel.value.id && c.uidt !== UITypes.Links),
)
})
@@ -85,7 +86,7 @@ const cellIcon = (column: ColumnType) =>
- ()
+
const options = ref<(Option & { status?: 'remove' })[]>([])
+const isAddingOption = ref(false)
+
+// TODO: Implement proper top and bottom virtual scrolling
const OPTIONS_PAGE_COUNT = 20
-const loadedOptionCount = ref(OPTIONS_PAGE_COUNT)
+const loadedOptionAnchor = ref(OPTIONS_PAGE_COUNT)
+const isReverseLazyLoad = ref(false)
const renderedOptions = ref<(Option & { status?: 'remove' })[]>([])
const savedDefaultOption = ref
+
-
+
+
diff --git a/packages/nc-gui/components/smartsheet/column/SelectOptions.vue b/packages/nc-gui/components/smartsheet/column/SelectOptions.vue
index e33a858136..d834c586d0 100644
--- a/packages/nc-gui/components/smartsheet/column/SelectOptions.vue
+++ b/packages/nc-gui/components/smartsheet/column/SelectOptions.vue
@@ -3,7 +3,7 @@ import Draggable from 'vuedraggable'
import { UITypes } from 'nocodb-sdk'
import InfiniteLoading from 'v3-infinite-loading'
-import { IsKanbanInj, enumColor, iconMap, onMounted, useColumnCreateStoreOrThrow, useVModel, watch } from '#imports'
+import { IsKanbanInj, enumColor, iconMap, onMounted, useColumnCreateStoreOrThrow, useVModel } from '#imports'
interface Option {
color: string
@@ -21,16 +21,22 @@ const emit = defineEmits(['update:value'])
const vModel = useVModel(props, 'value', emit)
-const { setAdditionalValidations, validateInfos, isMysql, isPg } = useColumnCreateStoreOrThrow()
+const { setAdditionalValidations, validateInfos } = useColumnCreateStoreOrThrow()
// const { base } = storeToRefs(useBase())
const { optionsMagic: _optionsMagic } = useNocoEe()
+const optionsWrapperDomRef = ref{{ $t('msg.linkColumnClearNotSupportedYet') }}
-
+
+
+
+ Coming soon
+
+
@@ -135,7 +157,7 @@ watch(commentsWrapperEl, () => {
-
-
-
-
diff --git a/packages/nc-gui/composables/useColumnCreateStore.ts b/packages/nc-gui/composables/useColumnCreateStore.ts
index 049297535d..3a7eabea08 100644
--- a/packages/nc-gui/composables/useColumnCreateStore.ts
+++ b/packages/nc-gui/composables/useColumnCreateStore.ts
@@ -126,7 +126,7 @@ const [useProvideColumnCreateStore, useColumnCreateStore] = createInjectionState
})
},
},
- fieldLengthValidator(baseType.value || ClientType.MYSQL),
+ fieldLengthValidator(),
],
uidt: [
{
diff --git a/packages/nc-gui/composables/useCopySharedBase.ts b/packages/nc-gui/composables/useCopySharedBase.ts
new file mode 100644
index 0000000000..98344c1ddc
--- /dev/null
+++ b/packages/nc-gui/composables/useCopySharedBase.ts
@@ -0,0 +1,9 @@
+import { createSharedComposable, ref } from '#imports'
+
+export const useCopySharedBase = createSharedComposable(() => {
+ const sharedBaseId = ref(null)
+
+ return {
+ sharedBaseId,
+ }
+})
diff --git a/packages/nc-gui/composables/useData.ts b/packages/nc-gui/composables/useData.ts
index 5c000c786c..fa9c7475ab 100644
--- a/packages/nc-gui/composables/useData.ts
+++ b/packages/nc-gui/composables/useData.ts
@@ -494,7 +494,7 @@ export function useData(args: {
if (res.message) {
message.info(
- `Row delete failed: ${`Unable to delete row with ID ${id} because of the following:
+ `Record delete failed: ${`Unable to delete record with ID ${id} because of the following:
\n${res.message.join('\n')}.\n
Clear the data first & try again`})}`,
)
diff --git a/packages/nc-gui/composables/useExpandedFormStore.ts b/packages/nc-gui/composables/useExpandedFormStore.ts
index 31ea5bee56..a602a6b1d8 100644
--- a/packages/nc-gui/composables/useExpandedFormStore.ts
+++ b/packages/nc-gui/composables/useExpandedFormStore.ts
@@ -153,7 +153,16 @@ const [useProvideExpandedFormStore, useExpandedFormStore] = useInjectionState((m
$e('a:row-expand:comment')
}
- const save = async (ltarState: Record = {}, undo = false) => {
+ const save = async (
+ ltarState: Record = {},
+ undo = false,
+ // TODO: Hack. Remove this when kanban injection store issue is resolved
+ {
+ kanbanClbk,
+ }: {
+ kanbanClbk?: (row: Row, isNewRow: boolean) => void
+ } = {},
+ ) => {
let data
try {
const isNewRow = row.value.rowMeta?.new ?? false
@@ -266,13 +275,13 @@ const [useProvideExpandedFormStore, useExpandedFormStore] = useInjectionState((m
}
}
- if (activeView.value?.type === ViewTypes.KANBAN) {
- const { addOrEditStackRow } = useKanbanViewStoreOrThrow()
- addOrEditStackRow(row.value, isNewRow)
+ if (activeView.value?.type === ViewTypes.KANBAN && kanbanClbk) {
+ kanbanClbk(row.value, isNewRow)
}
changedColumns.value = new Set()
} catch (e: any) {
+ console.error(e)
message.error(`${t('msg.error.rowUpdateFailed')}: ${await extractSdkResponseErrorMsg(e)}`)
}
$e('a:row-expand:add')
@@ -313,7 +322,7 @@ const [useProvideExpandedFormStore, useExpandedFormStore] = useInjectionState((m
if (res.message) {
message.info(
- `Row delete failed: ${`Unable to delete row with ID ${rowId} because of the following:
+ `Record delete failed: ${`Unable to delete record with ID ${rowId} because of the following:
\n${res.message.join('\n')}.\n
Clear the data first & try again`})}`,
)
diff --git a/packages/nc-gui/composables/useGlobal/actions.ts b/packages/nc-gui/composables/useGlobal/actions.ts
index 9218bff03f..a22a3dcb05 100644
--- a/packages/nc-gui/composables/useGlobal/actions.ts
+++ b/packages/nc-gui/composables/useGlobal/actions.ts
@@ -146,5 +146,9 @@ export function useGlobalActions(state: State): Actions {
return undefined
}
- return { signIn, signOut, refreshToken, loadAppInfo, setIsMobileMode, navigateToProject, getBaseUrl, ncNavigateTo }
+ const getMainUrl = () => {
+ return undefined
+ }
+
+ return { signIn, signOut, refreshToken, loadAppInfo, setIsMobileMode, navigateToProject, getBaseUrl, ncNavigateTo, getMainUrl }
}
diff --git a/packages/nc-gui/composables/useGlobal/types.ts b/packages/nc-gui/composables/useGlobal/types.ts
index c3df96ba6e..8549fd2e01 100644
--- a/packages/nc-gui/composables/useGlobal/types.ts
+++ b/packages/nc-gui/composables/useGlobal/types.ts
@@ -78,6 +78,7 @@ export interface Actions {
viewId?: string
}) => void
getBaseUrl: (workspaceId: string) => string | undefined
+ getMainUrl: (workspaceId: string) => string | undefined
}
export type ReadonlyState = Readonly> & Omit
diff --git a/packages/nc-gui/composables/useGridViewColumn.ts b/packages/nc-gui/composables/useGridViewColumn.ts
index ff0899fab3..f0622163f3 100644
--- a/packages/nc-gui/composables/useGridViewColumn.ts
+++ b/packages/nc-gui/composables/useGridViewColumn.ts
@@ -1,11 +1,9 @@
import type { ColumnType, GridColumnReqType, GridColumnType, ViewType } from 'nocodb-sdk'
import type { Ref } from 'vue'
-import { IsPublicInj, computed, inject, ref, useMetas, useNuxtApp, useRoles, useStyleTag, useUndoRedo, watch } from '#imports'
+import { IsPublicInj, computed, inject, ref, useMetas, useNuxtApp, useRoles, useUndoRedo, watch } from '#imports'
const [useProvideGridViewColumn, useGridViewColumn] = useInjectionState(
(view: Ref<(ViewType & { columns?: GridColumnType[] }) | undefined>, statePublic = false) => {
- const { css, load: loadCss, unload: unloadCss } = useStyleTag('')
-
const { isUIAllowed } = useRoles()
const { $api } = useNuxtApp()
@@ -15,30 +13,11 @@ const [useProvideGridViewColumn, useGridViewColumn] = useInjectionState(
const { addUndo, defineViewScope } = useUndoRedo()
const gridViewCols = ref>({})
- const resizingCol = ref('')
- const resizingColWidth = ref('200px')
+ const resizingColOldWith = ref('200px')
const isPublic = inject(IsPublicInj, ref(statePublic))
const columns = computed(() => metas.value?.[view.value?.fk_model_id as string]?.columns || [])
- watch(
- [gridViewCols, resizingCol, resizingColWidth],
- () => {
- let style = ''
- for (const c of columns?.value || []) {
- const val = gridViewCols?.value?.[c?.id as string]?.width || '200px'
-
- if (val && c.title !== resizingCol?.value) {
- style += `[data-col="${c.id}"]{min-width:${val};max-width:${val};width: ${val};}`
- } else {
- style += `[data-col="${c.id}"]{min-width:${resizingColWidth?.value};max-width:${resizingColWidth?.value};width: ${resizingColWidth?.value};}`
- }
- }
- css.value = style
- },
- { deep: true, immediate: true },
- )
-
const loadGridViewColumns = async () => {
if (!view.value?.id && !isPublic.value) return
@@ -52,8 +31,6 @@ const [useProvideGridViewColumn, useGridViewColumn] = useInjectionState(
}),
{},
)
-
- loadCss()
}
/** when columns changes(create/delete) reload grid columns
@@ -70,7 +47,8 @@ const [useProvideGridViewColumn, useGridViewColumn] = useInjectionState(
if (!undo) {
const oldProps = Object.keys(props).reduce>((o: any, k) => {
if (gridViewCols.value[id][k as keyof GridColumnType]) {
- o[k] = gridViewCols.value[id][k as keyof GridColumnType]
+ if (k === 'width') o[k] = `${resizingColOldWith.value}px`
+ else o[k] = gridViewCols.value[id][k as keyof GridColumnType]
}
return o
}, {})
@@ -105,7 +83,7 @@ const [useProvideGridViewColumn, useGridViewColumn] = useInjectionState(
}
}
- return { loadGridViewColumns, updateGridViewColumn, resizingCol, resizingColWidth, gridViewCols, loadCss, unloadCss }
+ return { loadGridViewColumns, updateGridViewColumn, gridViewCols, resizingColOldWith }
},
'useGridViewColumn',
)
diff --git a/packages/nc-gui/composables/useKanbanViewStore.ts b/packages/nc-gui/composables/useKanbanViewStore.ts
index 43fbd3a223..c498e40937 100644
--- a/packages/nc-gui/composables/useKanbanViewStore.ts
+++ b/packages/nc-gui/composables/useKanbanViewStore.ts
@@ -679,7 +679,7 @@ const [useProvideKanbanViewStore, useKanbanViewStore] = useInjectionState(
if (res.message) {
message.info(
- `Row delete failed: ${`Unable to delete row with ID ${id} because of the following:
+ `Record delete failed: ${`Unable to delete record with ID ${id} because of the following:
\n${res.message.join('\n')}.\n
Clear the data first & try again`})}`,
)
diff --git a/packages/nc-gui/composables/useLTARStore.ts b/packages/nc-gui/composables/useLTARStore.ts
index 30469cb721..abf2a544e7 100644
--- a/packages/nc-gui/composables/useLTARStore.ts
+++ b/packages/nc-gui/composables/useLTARStore.ts
@@ -291,7 +291,7 @@ const [useProvideLTARStore, useLTARStore] = useInjectionState(
if (res.message) {
message.info(
- `Row delete failed: ${`Unable to delete row with ID ${id} because of the following:
+ `Record delete failed: ${`Unable to delete record with ID ${id} because of the following:
\n${res.message.join('\n')}.\n
Clear the data first & try again`})}`,
)
diff --git a/packages/nc-gui/context/index.ts b/packages/nc-gui/context/index.ts
index 0ad635562f..a6f1106d99 100644
--- a/packages/nc-gui/context/index.ts
+++ b/packages/nc-gui/context/index.ts
@@ -1,4 +1,4 @@
-import type { ColumnType, TableType, ViewType } from 'nocodb-sdk'
+import type { ColumnType, FilterType, TableType, ViewType } from 'nocodb-sdk'
import type { ComputedRef, InjectionKey, Ref } from 'vue'
import type { EventHook } from '@vueuse/core'
import type { NcProject, PageSidebarNode, Row, TabItem } from '#imports'
@@ -51,3 +51,4 @@ export const TreeViewInj: InjectionKey<{
contextMenuTarget: { type?: 'base' | 'base' | 'table' | 'main' | 'layout'; value?: any }
}> = Symbol('tree-view-functions-injection')
export const JsonExpandInj: InjectionKey> = Symbol('json-expand-injection')
+export const AllFiltersInj: InjectionKey>> = Symbol('all-filters-injection')
diff --git a/packages/nc-gui/ee/assets/img/fieldPlaceholder.svg b/packages/nc-gui/ee/assets/img/fieldPlaceholder.svg
deleted file mode 100644
index 7fc8286651..0000000000
--- a/packages/nc-gui/ee/assets/img/fieldPlaceholder.svg
+++ /dev/null
@@ -1,196 +0,0 @@
-
diff --git a/packages/nc-gui/helpers/parsers/CSVTemplateAdapter.ts b/packages/nc-gui/helpers/parsers/CSVTemplateAdapter.ts
index 3797eca943..7f0bf65a7f 100644
--- a/packages/nc-gui/helpers/parsers/CSVTemplateAdapter.ts
+++ b/packages/nc-gui/helpers/parsers/CSVTemplateAdapter.ts
@@ -224,7 +224,6 @@ export default class CSVTemplateAdapter {
const data = (row.data as [])[columnIdx] === '' ? null : (row.data as [])[columnIdx]
if (column.uidt === UITypes.Checkbox) {
rowData[column.column_name] = getCheckboxValue(data)
- rowData[column.column_name] = data
} else if (column.uidt === UITypes.SingleSelect || column.uidt === UITypes.MultiSelect) {
rowData[column.column_name] = (data || '').toString().trim() || null
} else {
diff --git a/packages/nc-gui/lang/en.json b/packages/nc-gui/lang/en.json
index 1e20360be1..8a5ba8e0ce 100644
--- a/packages/nc-gui/lang/en.json
+++ b/packages/nc-gui/lang/en.json
@@ -185,8 +185,8 @@
"tables": "Tables",
"field": "Field",
"fields": "Fields",
- "column": "Column",
- "columns": "Columns",
+ "column": "Field",
+ "columns": "Fields",
"page": "Page",
"pages": "Pages",
"record": "record",
@@ -219,7 +219,7 @@
"orgLevelViewer": "Organization Level Viewer"
},
"sqlVIew": "SQL View",
- "rowHeight": "Row Height",
+ "rowHeight": "Record Height",
"heightClass": {
"short": "Short",
"medium": "Medium",
@@ -291,11 +291,11 @@
"dateJoined": "Date Joined",
"tokenName": "Token name",
"inDesktop": "in Desktop",
- "rowData": "Row data",
+ "rowData": "Record data",
"creator": "Creator",
"qrCode": "QR Code",
"termsOfService": "Terms of Service",
- "updateSelectedRows": "Update Selected Rows",
+ "updateSelectedRows": "Update Selected Records",
"noFiltersAdded": "No filters added",
"editCards": "Edit Cards",
"noFieldsFound": "No fields found",
@@ -366,7 +366,7 @@
"codeSnippet": "Code Snippet",
"keyboardShortcut": "Keyboard Shortcuts",
"generateRandomName": "Generate Random Name",
- "findRowByScanningCode": "Find row by scanning a QR or Barcode",
+ "findRowByScanningCode": "Find record by scanning a QR or Barcode",
"tokenManagement": "Token Management",
"addNewToken": "Add new token",
"accountSettings": "Account Settings",
@@ -388,6 +388,7 @@
}
},
"labels": {
+ "noToken": "No Token",
"tokenLimit": "Only one token per user is allowed",
"duplicateAttachment": "File with name {filename} already attached",
"toAddress": "To Address",
@@ -457,9 +458,9 @@
"createKanbanView": "Create Kanban View",
"viewName": "View name",
"viewLink": "View Link",
- "columnName": "Column Name",
- "columnToScanFor": "Column to scan",
- "columnType": "Column Type",
+ "columnName": "Field Name",
+ "columnToScanFor": "Field to scan",
+ "columnType": "Field Type",
"roleName": "Role Name",
"roleDescription": "Role Description",
"databaseType": "Type in Database",
@@ -501,8 +502,8 @@
"sqlOutput": "SQL Output",
"addOption": "Add option",
"interfaceColor": "Interface Color",
- "qrCodeValueColumn": "Column with QR code value",
- "barcodeValueColumn": "Column with Barcode value",
+ "qrCodeValueColumn": "Field with QR code value",
+ "barcodeValueColumn": "Field with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
@@ -518,7 +519,7 @@
"requriedIdentity": "Required-IDENTITY",
"inflection": {
"tableName": "Inflection - Table name",
- "columnName": "Inflection - Column name"
+ "columnName": "Inflection - Field name"
},
"community": {
"starUs1": "Star",
@@ -535,7 +536,8 @@
"docReference": "Document Reference",
"selectUserRole": "Select User Role",
"childTable": "Child table",
- "childColumn": "Child column",
+ "childColumn": "Child field",
+ "childField": "Child field",
"linkToAnotherRecord": "Link to another record",
"links": "Links",
"onUpdate": "On Update",
@@ -551,16 +553,16 @@
"sharedBase": "Shared Base",
"importData": "Import Data",
"importSecondaryViews": "Import Secondary Views",
- "importRollupColumns": "Import Rollup Columns",
- "importLookupColumns": "Import Lookup Columns",
- "importAttachmentColumns": "Import Attachment Columns",
- "importFormulaColumns": "Import Formula Columns",
+ "importRollupColumns": "Import Rollup Fields",
+ "importLookupColumns": "Import Lookup Fields",
+ "importAttachmentColumns": "Import Attachment Fields",
+ "importFormulaColumns": "Import Formula Fields",
"importUsers": "Import Users (by email)",
"noData": "No Data",
"goToDashboard": "Go to Dashboard",
"importing": "Importing",
"formatJson": "Format JSON",
- "firstRowAsHeaders": "Use First Row as Headers",
+ "firstRowAsHeaders": "Use First Record as Headers",
"flattenNested": "Flatten Nested",
"downloadAllowed": "Download allowed",
"weAreHiring": "We are Hiring!",
@@ -577,8 +579,8 @@
"agreeToTos": "By signing up, you agree to the Terms of Service",
"welcomeToNc": "Welcome to NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url",
- "nextRow": "Next Row",
- "prevRow": "Previous Row",
+ "nextRow": "Next Record",
+ "prevRow": "Previous Record",
"addRowGrid": "Manually add data in grid view",
"addRowForm": "Enter record data through a form",
"noAccess": "No access",
@@ -587,7 +589,7 @@
"includeData": "Include Data",
"includeView": "Include View",
"includeWebhook": "Include Webhook",
- "zoomInToViewColumns": "Zoom in to view columns"
+ "zoomInToViewColumns": "Zoom in to view fields"
},
"activity": {
"onCondition": "On Condition",
@@ -675,8 +677,8 @@
"newUser": "New User",
"editUser": "Edit user",
"deleteUser": "Remove user from base",
- "resendInvite": "Resend invite E-mail",
- "copyInviteURL": "Copy invite URL",
+ "resendInvite": "Resend Invite E-mail",
+ "copyInviteURL": "Copy Invite URL",
"copyPasswordResetURL": "Copy password reset URL",
"newRole": "New role",
"reloadRoles": "Reload roles",
@@ -694,17 +696,17 @@
"deleteTable": "Delete Table",
"addField": "Add new field to this table",
"setDisplay": "Set as Display value",
- "addRow": "Add new row",
- "saveRow": "Save row",
+ "addRow": "Add new record",
+ "saveRow": "Save record",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
- "insertRow": "Insert new row",
- "duplicateRow": "Duplicate row",
- "deleteRow": "Delete row",
- "deleteRows": "Delete rows",
- "predictColumns": "Predict Columns",
+ "insertRow": "Insert new record",
+ "duplicateRow": "Duplicate record",
+ "deleteRow": "Delete record",
+ "deleteRows": "Delete records",
+ "predictColumns": "Predict Fields",
"predictFormulas": "Predict Formulas",
- "deleteSelectedRow": "Delete selected rows",
+ "deleteSelectedRow": "Delete Selected Records",
"importExcel": "Import Excel",
"importCSV": "Import CSV",
"downloadCSV": "Download as CSV",
@@ -718,7 +720,7 @@
"changePwd": "Change Password",
"createView": "Create a View",
"shareView": "Share View",
- "findRowByCodeScan": "Find row by scan",
+ "findRowByCodeScan": "Find record by scan",
"fillByCodeScan": "Fill by scan",
"listSharedView": "Shared View List",
"ListView": "Views List",
@@ -762,7 +764,7 @@
"expandRecord": "Expand Record",
"deleteRecord": "Delete Record",
"erd": {
- "showColumns": "Show Columns",
+ "showColumns": "Show Fields",
"showPkAndFk": "Show Primary and Foreign Keys",
"showSqlViews": "Show SQL Views",
"showMMTables": "Show Many to Many tables",
@@ -820,8 +822,8 @@
"selectDiscordChannels": "Select Discord channels",
"selectMattermostChannels": "Select Mattermost channels",
"webhookTitle": "Webhook Title",
- "barcodeColumn": "Select a column for the Barcode value",
- "notFoundContent": "No valid Column Type can be found.",
+ "barcodeColumn": "Select a field for the Barcode value",
+ "notFoundContent": "No valid field Type can be found.",
"selectBarcodeFormat": "Select a Barcode format",
"projName": "Enter Base Name",
"selectGroupField": "Select a Grouping Field",
@@ -835,11 +837,11 @@
"save": "Save password",
"confirm": "Confirm new password"
},
- "selectAColumnForTheQRCodeValue": "Select a column for the QR code value",
+ "selectAColumnForTheQRCodeValue": "Select a field for the QR code value",
"allowNegativeNumbers": "Allow negative numbers",
"searchProjectTree": "Search tables",
"searchFields": "Search fields",
- "searchColumn": "Search {search} column",
+ "searchColumn": "Search {search} field",
"searchApps": "Search apps",
"searchModels": "Search models",
"noItemsFound": "No items found",
@@ -870,8 +872,9 @@
"newFormWillBeLoaded": "New form will be loaded after {seconds} seconds",
"optimizedQueryDisabled": "Optimized query is disabled",
"optimizedQueryEnabled": "Optimized query is enabled",
+ "lookupNonBtWarning": "Lookup field is not supported for non-Belongs to relation",
"invalidTime": "Invalid Time",
- "linkColumnClearNotSupportedYet": "Link column clear is not supported yet",
+ "linkColumnClearNotSupportedYet": "You don't have any supported links for Lookup",
"recordCouldNotBeFound": "Record could not be found",
"invalidPhoneNumber": "Invalid phone number",
"pageSizeChanged": "Page size changed",
@@ -880,14 +883,14 @@
"webhookBodyMsg2": "body",
"webhookBodyMsg3": "to refer the record under consideration",
"formula": {
- "hintStart": "Hint: Use {placeholder1} to reference columns, e.g: {placeholder2}. For more, please check out",
+ "hintStart": "Hint: Use {placeholder1} to reference fields, e.g: {placeholder2}. For more, please check out",
"hintEnd": "Formulas.",
"noSuggestedFormulaFound": "No suggested formula found",
"numericTypeIsExpected": "Numeric type is expected",
"stringTypeIsExpected": "String type is expected",
"operationNotAvailable": "{operation} operation not available",
"cantSaveFieldFormulaInvalid": "Can’t save field because formula is invalid",
- "notSupportedToReferenceColumn": "Not supported to reference column {columnName}",
+ "notSupportedToReferenceColumn": "Not supported to reference field {columnName}",
"typeIsExpectedButFound": "Type {type} is expected but found Type {found}",
"requiredArgumentsFormula": "{calleeName} requires {requiredArguments} arguments",
"minRequiredArgumentsFormula": "{calleeName} required minimum {minRequiredArguments} arguments",
@@ -901,14 +904,14 @@
"firstParamDateDiffHaveDate": "The first parameter of DATEDIFF() should have date value",
"secondParamDateDiffHaveDate": "The second parameter of DATEDIFF() should have date value",
"thirdParamDateDiffHaveDate": "The third parameter of DATETIME_DIFF() should have value either \"milliseconds\", \"ms\", \"seconds\", \"s\", \"minutes\", \"m\", \"hours\", \"h\", \"days\", \"d\", \"weeks\", \"w\", \"months\", \"M\", \"quarters\", \"Q\", \"years\", or \"y\"",
- "columnNotAvailable": "Column {columnName} is not available",
+ "columnNotAvailable": "Field {columnName} is not available",
"cantSaveCircularReference": "Can’t save field because it causes a circular reference",
- "columnWithTypeFoundButExpected": "Column {columnName} with {columnType} type is found but {expectedType} type is expected",
+ "columnWithTypeFoundButExpected": "Field {columnName} with {columnType} type is found but {expectedType} type is expected",
"columnNotMatchedWithType": "{columnName} is not matched with {columnType}"
},
"selectOption": {
"cantBeNull": "Select options can't be null",
- "multiSelectCantHaveCommas": "MultiSelect columns can't have commas(',')",
+ "multiSelectCantHaveCommas": "MultiSelect fields can't have commas(',')",
"cantHaveDuplicates": "Select options can't have duplicates",
"createNewOptionNamed": "Create new option named"
},
@@ -917,7 +920,7 @@
"invalidLocale": "Invalid locale",
"invalidCurrencyCode": "Invalid Currency Code",
"postgresHasItsOwnCurrencySettings": "PostgreSQL 'money' type has own currency settings",
- "validColumnsForBarCode": "The valid Column Types for a Barcode Column are: Number, Single Line Text, Long Text, Phone Number, URL, Email, Decimal. Please create one first.",
+ "validColumnsForBarCode": "The valid Field Types for a Barcode Field are: Number, Single Line Text, Long Text, Phone Number, URL, Email, Decimal. Please create one first.",
"hm": {
"title": "Has Many Relation",
"tooltip_desc": "A single record from table ",
@@ -949,7 +952,7 @@
"createWebhookMsg2": "Create web-hooks to power you automations,",
"createWebhookMsg3": "Get notified as soon as there are changes in your data",
"areYouSureUWantTo": "Are you sure you want to delete the following",
- "idColumnRequired": "ID column is required, you can rename this later if required.",
+ "idColumnRequired": "ID field is required, you can rename this later if required.",
"length59Required": "The length exceeds the max 59 characters",
"warning": {
"dbValid": "Please make sure database you are trying to connect is valid! This operation can cause schema loss!!",
@@ -972,22 +975,22 @@
},
"codeScanner": {
"loadingScanner": "Loading the scanner...",
- "selectColumn": "Select a column (QR code or Barcode) that you want to use for finding a row by scanning.",
- "moreThanOneRowFoundForCode": "More than one row found for this code. Currently only unique codes are supported.",
- "noRowFoundForCode": "No row found for this code for the selected column"
+ "selectColumn": "Select a field (QR code or Barcode) that you want to use for finding a record by scanning.",
+ "moreThanOneRowFoundForCode": "More than one record found for this code. Currently only unique codes are supported.",
+ "noRowFoundForCode": "No record found for this code for the selected field"
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
- "footerInfo": "Rows per page",
+ "footerInfo": "Records per page",
"upload": "Select file to Upload",
"upload_sub": "or drag and drop file",
"excelSupport": "Supported: .xls, .xlsx, .xlsm, .ods, .ots",
"excelURL": "Enter excel file URL",
"csvURL": "Enter CSV file URL",
- "footMsg": "# of rows to parse to infer datatype",
+ "footMsg": "# of records to parse to infer datatype",
"excelImport": "sheet(s) are available for import",
"exportMetadata": "Do you want to export metadata from meta tables?",
"importMetadata": "Do you want to import metadata from meta tables?",
@@ -1071,8 +1074,8 @@
"enterTableName": "Enter table name",
"enterLayoutName": "Enter Layout name",
"enterDashboardName": "Enter Dashboard name",
- "defaultColumns": "Default columns",
- "addDefaultColumns": "Add default columns",
+ "defaultColumns": "Default fields",
+ "addDefaultColumns": "Add default fields",
"tableNameInDb": "Table name as saved in database",
"airtable": {
"credentials": "Where to find this?"
@@ -1092,7 +1095,7 @@
"cacheEmpty": "Cache is empty",
"exportedCache": "Exported Cache Successfully",
"valueAlreadyInList": "This value is already in the list",
- "noColumnsToUpdate": "No columns to update",
+ "noColumnsToUpdate": "No fields to update",
"tableDeleted": "Deleted table successfully",
"layoutDeleted": "Deleted layout successfully",
"generatePublicShareableReadonlyBase": "Generate publicly shareable readonly base",
@@ -1147,11 +1150,11 @@
"internalError": "Some internal error occurred",
"templateGeneratorNotFound": "Template Generator cannot be found!",
"fileUploadFailed": "Failed to upload file",
- "primaryColumnUpdateFailed": "Failed to update primary column",
+ "primaryColumnUpdateFailed": "Failed to update primary field",
"formDescriptionTooLong": "Data too long for Form Description",
- "columnsRequired": "Following columns are required",
- "selectAtleastOneColumn": "At least one column has to be selected",
- "columnDescriptionNotFound": "Cannot find the destination column for",
+ "columnsRequired": "Following fields are required",
+ "selectAtleastOneColumn": "At least one field has to be selected",
+ "columnDescriptionNotFound": "Cannot find the destination field for",
"duplicateMappingFound": "Duplicate mapping found, please remove one of the mapping",
"nullValueViolatesNotNull": "Null value violates not-null constraint",
"sourceHasInvalidNumbers": "Source data contains some invalid numbers",
@@ -1163,17 +1166,17 @@
"failedToLoadChildrenList": "Failed to load children list",
"deleteFailed": "Delete failed",
"unlinkFailed": "Unlink failed",
- "rowUpdateFailed": "Row update failed",
- "deleteRowFailed": "Failed to delete row",
+ "rowUpdateFailed": "Record update failed",
+ "deleteRowFailed": "Failed to delete record",
"setFormDataFailed": "Failed to set form data",
"formViewUpdateFailed": "Failed to update form view",
"tableNameRequired": "Table name is required",
"nameShouldStartWithAnAlphabetOr_": "Name should start with an alphabet or _",
"followingCharactersAreNotAllowed": "Following characters are not allowed",
- "columnNameRequired": "Column name is required",
- "duplicateColumnName": "Duplicate column name",
+ "columnNameRequired": "Field name is required",
+ "duplicateColumnName": "Duplicate field name",
"uiDataTypeRequired": "UI data type is required",
- "columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
+ "columnNameExceedsCharacters": "The length of field name exceeds the max {value} characters",
"projectNameExceeds50Characters": "Base name exceeds 50 characters",
"projectNameCannotStartWithSpace": "Base name cannot start with space",
"requiredField": "Required field",
@@ -1197,7 +1200,7 @@
"deleteProject": "Base deleted successfully",
"authToken": "Auth token copied to clipboard",
"projInfo": "Copied base info to clipboard",
- "inviteUrlCopy": "Copied invite URL to clipboard",
+ "inviteUrlCopy": "Copied Invite URL to clipboard",
"createView": "View created successfully",
"formEmailSMTP": "Please activate SMTP plugin in App store for enabling email notification",
"collabView": "Successfully Switched to collaborative view",
@@ -1206,8 +1209,8 @@
},
"success": {
"licenseKeyUpdated": "License Key Updated",
- "columnDuplicated": "Column duplicated successfully",
- "rowDuplicatedWithoutSavedYet": "Row duplicated (not saved)",
+ "columnDuplicated": "Field duplicated successfully",
+ "rowDuplicatedWithoutSavedYet": "Record duplicated (not saved)",
"updatedUIACL": "Updated UI ACL for tables successfully",
"pluginUninstalled": "Plugin uninstalled successfully",
"pluginSettingsSaved": "Plugin settings saved successfully",
@@ -1215,7 +1218,7 @@
"tableRenamed": "Table renamed successfully",
"layoutRenamed": "Layout renamed successfully",
"viewDeleted": "View deleted successfully",
- "primaryColumnUpdated": "Successfully updated as primary column",
+ "primaryColumnUpdated": "Successfully updated as primary field",
"tableDataExported": "Successfully exported all table data",
"updated": "Successfully updated",
"sharedViewDeleted": "Deleted shared view successfully",
@@ -1237,8 +1240,8 @@
"webhookUpdated": "Webhook details updated successfully",
"webhookDeleted": "Hook deleted successfully",
"webhookTested": "Webhook tested successfully",
- "columnUpdated": "Column updated",
- "columnCreated": "Column created",
+ "columnUpdated": "Field updated",
+ "columnCreated": "Field created",
"passwordChanged": "Password changed successfully. Please login again.",
"settingsSaved": "Settings saved successfully",
"roleUpdated": "Role updated successfully"
diff --git a/packages/nc-gui/lib/types.ts b/packages/nc-gui/lib/types.ts
index f819ac6525..2b1cbb9ce1 100644
--- a/packages/nc-gui/lib/types.ts
+++ b/packages/nc-gui/lib/types.ts
@@ -125,6 +125,7 @@ type NcProject = BaseType & {
temp_title?: string
edit?: boolean
starred?: boolean
+ uuid?: string
}
interface UndoRedoAction {
diff --git a/packages/nc-gui/middleware/auth.global.ts b/packages/nc-gui/middleware/auth.global.ts
index 5c6ca2b9d1..3ab3d1cf50 100644
--- a/packages/nc-gui/middleware/auth.global.ts
+++ b/packages/nc-gui/middleware/auth.global.ts
@@ -1,6 +1,7 @@
import type { Api } from 'nocodb-sdk'
import type { Actions } from '~/composables/useGlobal/types'
-import { defineNuxtRouteMiddleware, extractSdkResponseErrorMsg, message, navigateTo, useApi, useGlobal, useRoles } from '#imports'
+import { defineNuxtRouteMiddleware, message, navigateTo, useApi, useGlobal, useRoles } from '#imports'
+import { extractSdkResponseErrorMsg } from '~/utils'
/**
* Global auth middleware
@@ -46,7 +47,9 @@ export default defineNuxtRouteMiddleware(async (to, from) => {
}
/** if user isn't signed in and google auth is enabled, try to check if sign-in data is present */
- if (!state.signedIn.value && state.appInfo.value.googleAuthEnabled) await tryGoogleAuth(api, state.signIn)
+ if (!state.signedIn.value && state.appInfo.value.googleAuthEnabled) {
+ await tryGoogleAuth(api, state.signIn)
+ }
/** if public allow all visitors */
if (to.meta.public) return
@@ -54,13 +57,18 @@ export default defineNuxtRouteMiddleware(async (to, from) => {
/** if shared base allow without validating */
if (to.params.typeOrId === 'base') return
- /** if auth is required or unspecified (same as required) and user is not signed in, redirect to signin page */
+ /** if auth is required or unspecified (same `as required) and user is not signed in, redirect to signin page */
if ((to.meta.requiresAuth || typeof to.meta.requiresAuth === 'undefined') && !state.signedIn.value) {
/** If this is the first usern navigate to signup page directly */
if (state.appInfo.value.firstUser) {
+ const query = to.fullPath !== '/' && to.fullPath.match(/^\/(?!\?)/) ? { continueAfterSignIn: to.fullPath } : {}
+ if (query.continueAfterSignIn) {
+ localStorage.setItem('continueAfterSignIn', query.continueAfterSignIn)
+ }
+
return navigateTo({
path: '/signup',
- query: to.fullPath !== '/' && to.fullPath.match(/^\/(?!\?)/) ? { continueAfterSignIn: to.fullPath } : {},
+ query,
})
}
diff --git a/packages/nc-gui/pages/account/index.vue b/packages/nc-gui/pages/account/index.vue
index 04f53e6a23..86ddb9104a 100644
--- a/packages/nc-gui/pages/account/index.vue
+++ b/packages/nc-gui/pages/account/index.vue
@@ -145,7 +145,7 @@ const logout = async () => {
diff --git a/packages/nc-gui/plugins/a.i18n.ts b/packages/nc-gui/plugins/a.i18n.ts
index 01646600b1..0d64ca18eb 100644
--- a/packages/nc-gui/plugins/a.i18n.ts
+++ b/packages/nc-gui/plugins/a.i18n.ts
@@ -1,6 +1,6 @@
import { createI18n } from 'vue-i18n'
import { isClient } from '@vueuse/core'
-import { LanguageAlias, applyLanguageDirection, defineNuxtPlugin, isRtlLang, nextTick } from '#imports'
+import { LanguageAlias, applyLanguageDirection, defineNuxtPlugin, isEeUI, isRtlLang, nextTick } from '#imports'
import type { Language, NocoI18n } from '#imports'
let globalI18n: NocoI18n
@@ -43,10 +43,16 @@ export async function loadLocaleMessages(
return nextTick()
}
-export default defineNuxtPlugin(async (nuxtApp) => {
+const i18nPlugin = async (nuxtApp) => {
globalI18n = await createI18nPlugin()
nuxtApp.vueApp.i18n = globalI18n
nuxtApp.vueApp.use(globalI18n)
+}
+
+export default defineNuxtPlugin(async function (nuxtApp) {
+ if (!isEeUI) return await i18nPlugin(nuxtApp)
})
+
+export { i18nPlugin }
diff --git a/packages/nc-gui/plugins/api.ts b/packages/nc-gui/plugins/api.ts
index 0e868b2a64..ad1d0d81b7 100644
--- a/packages/nc-gui/plugins/api.ts
+++ b/packages/nc-gui/plugins/api.ts
@@ -1,6 +1,12 @@
-import { defineNuxtPlugin, useApi } from '#imports'
+import { defineNuxtPlugin, isEeUI, useApi } from '#imports'
-export default defineNuxtPlugin((nuxtApp) => {
+const apiPlugin = (nuxtApp) => {
/** injects a global api instance */
nuxtApp.provide('api', useApi().api)
+}
+
+export { apiPlugin }
+
+export default defineNuxtPlugin(function (nuxtApp) {
+ if (!isEeUI) return apiPlugin(nuxtApp)
})
diff --git a/packages/nc-gui/plugins/resizeDirective.ts b/packages/nc-gui/plugins/resizeDirective.ts
index 549131ee99..6b58a3eedd 100644
--- a/packages/nc-gui/plugins/resizeDirective.ts
+++ b/packages/nc-gui/plugins/resizeDirective.ts
@@ -36,6 +36,7 @@ export default defineNuxtPlugin((nuxtApp) => {
startWidth = parseInt(document.defaultView?.getComputedStyle(el)?.width || '0', 10)
document.documentElement.addEventListener('mousemove', doDrag, false)
document.documentElement.addEventListener('mouseup', stopDrag, false)
+ emit('xcstartresizing', startWidth)
}
;(el as any).initDrag = initDrag
diff --git a/packages/nc-gui/plugins/state.ts b/packages/nc-gui/plugins/state.ts
index da575163ec..de92471e5b 100644
--- a/packages/nc-gui/plugins/state.ts
+++ b/packages/nc-gui/plugins/state.ts
@@ -1,4 +1,4 @@
-import { Language, LanguageAlias, defineNuxtPlugin, useApi, useGlobal } from '#imports'
+import { Language, LanguageAlias, defineNuxtPlugin, isEeUI, useApi, useGlobal } from '#imports'
import { loadLocaleMessages, setI18nLanguage } from '~/plugins/a.i18n'
/**
@@ -13,7 +13,7 @@ import { loadLocaleMessages, setI18nLanguage } from '~/plugins/a.i18n'
* console.log($state.lang.value) // 'en'
* ```
*/
-export default defineNuxtPlugin(async () => {
+const statePlugin = async (_nuxtApp) => {
const state = useGlobal()
const { api } = useApi({ useGlobalInstance: true })
@@ -34,4 +34,10 @@ export default defineNuxtPlugin(async () => {
} catch (e) {
console.error(e)
}
+}
+
+export default defineNuxtPlugin(async function (nuxtApp) {
+ if (!isEeUI) return await statePlugin(nuxtApp)
})
+
+export { statePlugin }
diff --git a/packages/nc-gui/public/favicon.ico b/packages/nc-gui/public/favicon.ico
index a9fa2dd153..c062d29a0a 100644
Binary files a/packages/nc-gui/public/favicon.ico and b/packages/nc-gui/public/favicon.ico differ
diff --git a/packages/nc-gui/store/views.ts b/packages/nc-gui/store/views.ts
index a0360364d3..4cb30465e7 100644
--- a/packages/nc-gui/store/views.ts
+++ b/packages/nc-gui/store/views.ts
@@ -1,18 +1,35 @@
-import type { ViewType } from 'nocodb-sdk'
+import type { ViewType, ViewTypes } from 'nocodb-sdk'
import { acceptHMRUpdate, defineStore } from 'pinia'
import type { ViewPageType } from '~/lib'
export const useViewsStore = defineStore('viewsStore', () => {
const { $api } = useNuxtApp()
+ interface RecentView {
+ viewName: string
+ viewId: string | undefined
+ viewType: ViewTypes
+ tableID: string
+ isDefault: boolean
+ baseName: string
+ workspaceId: string
+ baseId: string
+ }
const router = useRouter()
- const recentViews = computed(() => [])
-
- const allRecentViews = ref([])
+ // Store recent views from all Workspaces
+ const allRecentViews = ref([])
const route = router.currentRoute
+ const bases = useBases()
+
const tablesStore = useTablesStore()
+ const { activeWorkspaceId } = storeToRefs(useWorkspace())
+
+ const recentViews = computed(() =>
+ allRecentViews.value.filter((f) => f.workspaceId === activeWorkspaceId.value).splice(0, 10),
+ )
+
const viewsByTable = ref
()
-const { user } = useGlobal()
+const { user, appInfo } = useGlobal()
const tab = ref<'comments' | 'audits'>('comments')
@@ -98,6 +98,12 @@ const saveComment = async () => {
watch(commentsWrapperEl, () => {
scrollComments()
})
+
+const onClickAudit = () => {
+ if (appInfo.value.ee) return
+
+ tab.value = 'audits'
+}
@@ -108,7 +114,7 @@ watch(commentsWrapperEl, () => {
v-e="['c:row-expand:comment']"
class="tab flex-1 px-4 py-2 transition-all text-gray-600 cursor-pointer rounded-lg"
:class="{
- 'bg-white shadow !text-brand-500 !hover:text-brand-500': tab === 'comments',
+ 'bg-white shadow !text-brand-500 !hover:text-brand-500': tab === 'comments' || appInfo.ee,
}"
@click="tab = 'comments'"
>
@@ -117,13 +123,29 @@ watch(commentsWrapperEl, () => {
Comments
+
+
+
+ Audits
+
+
@@ -251,6 +273,9 @@ watch(commentsWrapperEl, () => {
+
+
diff --git a/packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue b/packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue
index 786dced915..6d22c8dcf1 100644
--- a/packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue
+++ b/packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue
@@ -1,8 +1,9 @@
@@ -471,23 +489,44 @@ onMounted(async () => {
-
-
-
-
-
-
-
+
+
-
-
- {{ $t('activity.addFilter') }}
-
-
-
-
- {{ $t('activity.addFilterGroup') }}
-
-
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('activity.addFilter') }}
+
+
+
+
+ {{ $t('activity.addFilterGroup') }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('activity.addFilter') }}
+
+
+
+
+ {{ $t('activity.addFilterGroup') }}
+
+
import {
ActiveViewInj,
+ AllFiltersInj,
IsLockedInj,
computed,
+ iconMap,
inject,
ref,
useGlobal,
@@ -10,7 +12,6 @@ import {
useSmartsheetStoreOrThrow,
useViewFilters,
watch,
- iconMap,
} from '#imports'
const isLocked = inject(IsLockedInj, ref(false))
@@ -48,6 +49,10 @@ watch(
const open = ref(false)
+const allFilters = ref({})
+
+provide(AllFiltersInj, allFilters)
+
useMenuCloseOnEsc(open)
diff --git a/packages/nc-gui/components/smartsheet/toolbar/FieldsMenu.vue b/packages/nc-gui/components/smartsheet/toolbar/FieldsMenu.vue
index bf60eb389d..d85decd9b2 100644
--- a/packages/nc-gui/components/smartsheet/toolbar/FieldsMenu.vue
+++ b/packages/nc-gui/components/smartsheet/toolbar/FieldsMenu.vue
@@ -346,7 +346,7 @@ useMenuCloseOnEsc(open)
-
+
(() => {
})
const linkOrUnLink = (rowRef: Record, id: string) => {
+ if (isSharedBase.value) return
+
if (isPublic.value && !isForm.value) return
if (isNew.value || isChildrenListLinked.value[parseInt(id)]) {
unlinkRow(rowRef, parseInt(id))
@@ -345,6 +349,7 @@ const linkOrUnLink = (rowRef: Record, id: string) => {
new: true,
},
}"
+ :row-id="extractPkFromRow(expandedFormRow, relatedTableMeta.columns as ColumnType[])"
use-meta-fields
/>
diff --git a/packages/nc-gui/components/virtual-cell/components/ListItem.vue b/packages/nc-gui/components/virtual-cell/components/ListItem.vue
index 3bd8345cd2..745d7efefe 100644
--- a/packages/nc-gui/components/virtual-cell/components/ListItem.vue
+++ b/packages/nc-gui/components/virtual-cell/components/ListItem.vue
@@ -38,6 +38,8 @@ const row = useVModel(props, 'row')
const isPublic = inject(IsPublicInj, ref(false))
+const readonly = inject(ReadonlyInj, ref(false))
+
const { getPossibleAttachmentSrc } = useAttachment()
interface Attachment {
@@ -151,7 +153,7 @@ const attachments: ComputedRef = computed(() => {
{
onKeyStroke('Escape', () => {
vModel.value = false
})
+
+const onClick = (refRow: any, id: string) => {
+ if (isSharedBase.value) return
+ if (isChildrenExcludedListLinked.value[Number.parseInt(id)]) {
+ unlinkRow(refRow, Number.parseInt(id))
+ } else {
+ linkRow(refRow, Number.parseInt(id))
+ }
+}
@@ -272,12 +283,7 @@ onKeyStroke('Escape', () => {
expandedFormDlg = true
}
"
- @click="
- () => {
- if (isChildrenExcludedListLinked[Number.parseInt(id)]) unlinkRow(refRow, Number.parseInt(id))
- else linkRow(refRow, Number.parseInt(id))
- }
- "
+ @click="() => onClick(refRow, id)"
/>
@@ -335,6 +341,7 @@ onKeyStroke('Escape', () => {
new: true,
},
}"
+ :row-id="extractPkFromRow(expandedFormRow, relatedTableMeta.columns as ColumnType[])"
:state="newRowState"
use-meta-fields
/>
diff --git a/packages/nc-gui/components/workspace/Billing.vue b/packages/nc-gui/components/workspace/Billing.vue
deleted file mode 100644
index 2a77b88300..0000000000
--- a/packages/nc-gui/components/workspace/Billing.vue
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
-
- {
type="text"
@click.stop="removeFieldFromGroupBy(i)"
>
-
+
diff --git a/packages/nc-gui/components/smartsheet/toolbar/SortListMenu.vue b/packages/nc-gui/components/smartsheet/toolbar/SortListMenu.vue
index d84fcb799d..c70f2831b9 100644
--- a/packages/nc-gui/components/smartsheet/toolbar/SortListMenu.vue
+++ b/packages/nc-gui/components/smartsheet/toolbar/SortListMenu.vue
@@ -1,5 +1,5 @@
@@ -26,7 +36,7 @@ const { onViewsTabChange } = useViewsStore()
:class="{
active: openedViewsTab !== 'view',
}"
- @click="onViewsTabChange('field')"
+ @click="onClickDetails"
>
hasSelectColumn.value[tableIdx] = false
table.columns?.forEach((column, columnIdx) => {
- acc[`tables.${tableIdx}.columns.${columnIdx}.column_name`] = [
- fieldRequiredValidator(),
- fieldLengthValidator(base.value?.sources?.[0].type || ClientType.MYSQL),
- ]
+ acc[`tables.${tableIdx}.columns.${columnIdx}.column_name`] = [fieldRequiredValidator(), fieldLengthValidator()]
acc[`tables.${tableIdx}.columns.${columnIdx}.uidt`] = [fieldRequiredValidator()]
if (isSelect(column)) {
hasSelectColumn.value[tableIdx] = true
@@ -434,7 +431,7 @@ async function importTemplate() {
let input = row[col.srcCn]
// parse potential boolean values
if (v.uidt === UITypes.Checkbox) {
- input = input.replace(/["']/g, '').toLowerCase().trim()
+ input = input ? input.replace(/["']/g, '').toLowerCase().trim() : 'false'
if (input === 'false' || input === 'no' || input === 'n') {
input = '0'
} else if (input === 'true' || input === 'yes' || input === 'y') {
diff --git a/packages/nc-gui/components/virtual-cell/Links.vue b/packages/nc-gui/components/virtual-cell/Links.vue
index 580fba8dba..51179a4cd1 100644
--- a/packages/nc-gui/components/virtual-cell/Links.vue
+++ b/packages/nc-gui/components/virtual-cell/Links.vue
@@ -77,6 +77,8 @@ const onAttachRecord = () => {
}
const openChildList = () => {
+ if (isUnderLookup.value) return
+
if (!isLocked.value) {
childListDlg.value = true
}
@@ -98,6 +100,12 @@ const localCellValue = computed(() => {
}
return []
})
+
+const openListDlg = () => {
+ if (isUnderLookup.value) return
+
+ listItemsDlg.value = true
+}
@@ -120,7 +128,7 @@ const localCellValue = computed(() => {
diff --git a/packages/nc-gui/components/virtual-cell/components/ListChildItems.vue b/packages/nc-gui/components/virtual-cell/components/ListChildItems.vue
index 49f8111e23..f340e6585e 100644
--- a/packages/nc-gui/components/virtual-cell/components/ListChildItems.vue
+++ b/packages/nc-gui/components/virtual-cell/components/ListChildItems.vue
@@ -41,6 +41,8 @@ const injectedColumn = inject(ColumnInj, ref())
const readonly = inject(ReadonlyInj, ref(false))
+const { isSharedBase } = storeToRefs(useBase())
+
const {
childrenList,
childrenListCount,
@@ -165,6 +167,8 @@ const isDataExist = computed
-
-
diff --git a/packages/nc-gui/components/workspace/View.vue b/packages/nc-gui/components/workspace/View.vue
index 5504643048..d1eb44b7e2 100644
--- a/packages/nc-gui/components/workspace/View.vue
+++ b/packages/nc-gui/components/workspace/View.vue
@@ -69,17 +69,6 @@ onMounted(() => {
-
-
-
- Upgrade
-
-
-
-
- Upgrade your workspace
-
- Your workspace is upgraded
-
-
-
- Billing
-
-
-
-
+
@@ -185,8 +185,15 @@ const logout = async () => {
-
diff --git a/packages/nc-gui/pages/copy-shared-base.vue b/packages/nc-gui/pages/copy-shared-base.vue
new file mode 100644
index 0000000000..e4bea5597a
--- /dev/null
+++ b/packages/nc-gui/pages/copy-shared-base.vue
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/packages/nc-gui/pages/index.vue b/packages/nc-gui/pages/index.vue
index 9c4530be96..2d14565dd3 100644
--- a/packages/nc-gui/pages/index.vue
+++ b/packages/nc-gui/pages/index.vue
@@ -16,7 +16,9 @@ const basesStore = useBases()
const { populateWorkspace } = useWorkspace()
-const { signedIn } = useGlobal()
+const { signedIn, ncNavigateTo } = useGlobal()
+
+const { isUIAllowed } = useRoles()
const router = useRouter()
@@ -46,6 +48,10 @@ const isSharedFormView = computed(() => {
return routeName.startsWith('index-typeOrId-form-viewId')
})
+const { sharedBaseId } = useCopySharedBase()
+
+const isDuplicateDlgOpen = ref(false)
+
async function handleRouteTypeIdChange() {
// avoid loading bases for shared views
if (isSharedView.value) {
@@ -82,7 +88,29 @@ watch(
// immediate watch, because if route is changed during page transition
// It will error out nuxt
onMounted(() => {
- handleRouteTypeIdChange()
+ if (route.value.query?.continueAfterSignIn) {
+ localStorage.removeItem('continueAfterSignIn')
+ return navigateTo(route.value.query.continueAfterSignIn as string)
+ } else {
+ const continueAfterSignIn = localStorage.getItem('continueAfterSignIn')
+
+ if (continueAfterSignIn) {
+ return navigateTo({
+ path: continueAfterSignIn,
+ query: route.value.query,
+ })
+ }
+ }
+
+ handleRouteTypeIdChange().then(() => {
+ if (sharedBaseId.value) {
+ if (!isUIAllowed('baseDuplicate')) {
+ message.error('You are not allowed to create base')
+ return
+ }
+ isDuplicateDlgOpen.value = true
+ }
+ })
})
function toggleDialog(value?: boolean, key?: string, dsState?: string, pId?: string) {
@@ -93,6 +121,40 @@ function toggleDialog(value?: boolean, key?: string, dsState?: string, pId?: str
}
provide(ToggleDialogInj, toggleDialog)
+
+const { $e, $poller } = useNuxtApp()
+
+const DlgSharedBaseDuplicateOnOk = async (jobData: { id: string; base_id: string; workspace_id: string }) => {
+ await populateWorkspace()
+
+ $poller.subscribe(
+ { id: jobData.id },
+ async (data: {
+ id: string
+ status?: string
+ data?: {
+ error?: {
+ message: string
+ }
+ message?: string
+ result?: any
+ }
+ }) => {
+ if (data.status !== 'close') {
+ if (data.status === JobStatus.COMPLETED) {
+ await ncNavigateTo({
+ baseId: jobData.base_id,
+ })
+ } else if (data.status === JobStatus.FAILED) {
+ message.error('Failed to duplicate shared base')
+ await populateWorkspace()
+ }
+ }
+ },
+ )
+
+ $e('a:base:duplicate-shared-base')
+}
@@ -117,6 +179,12 @@ provide(ToggleDialogInj, toggleDialog)
v-model:data-sources-state="dataSourcesState"
:base-id="baseId"
/>
+
-
+
+
+
+