diff --git a/package.json b/package.json index 3a6c54469d..a0fce7e026 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ }, "pnpm": { "overrides": { - "vue": "3.3.13", + "vue": "latest", "typescript": "latest", "ajv@<6.12.3": ">=6.12.3", "node.extend@<1.1.7": ">=1.1.7", @@ -56,7 +56,8 @@ "axios@>=0.8.1 <0.28.0": ">=0.28.0", "ip@<1.1.9": ">=1.1.9", "ip@=2.0.0": ">=2.0.1", - "xml2js@<0.5.0": ">=0.5.0" + "xml2js@<0.5.0": ">=0.5.0", + "ufo": ">=1.5.3" } } } diff --git a/packages/nc-gui/assets/style.scss b/packages/nc-gui/assets/style.scss index 3f73e08ba7..5555e60382 100644 --- a/packages/nc-gui/assets/style.scss +++ b/packages/nc-gui/assets/style.scss @@ -19,6 +19,10 @@ body { --navbar-bg: #fafafa; --navbar-border: #e0e0e0; --nc-grid-bg: #fdfdfd; + + --ant-primary-color-hover: #5c85ff !important; + --ant-primary-color-active: #3366ff !important; + --ant-primary-color-outline: rgba(51, 102, 255, 0.24) !important; } ::-moz-selection { @@ -810,6 +814,6 @@ svg.nc-virtual-cell-icon { box-shadow: 0 0 0 2px #fff, 0 0 0 4px #3366ff; } -.text-nowrap{ +.text-nowrap { text-wrap: nowrap; -} \ No newline at end of file +} diff --git a/packages/nc-gui/components/cell/DatePicker.vue b/packages/nc-gui/components/cell/DatePicker.vue index 6945db88b3..ab5b0cd2ca 100644 --- a/packages/nc-gui/components/cell/DatePicker.vue +++ b/packages/nc-gui/components/cell/DatePicker.vue @@ -205,7 +205,18 @@ useEventListener(document, 'keydown', (e: KeyboardEvent) => { // To prevent event listener on non active cell if (!active.value) return - if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey || !isGrid.value || isExpandedForm.value || isEditColumn.value) return + if ( + e.altKey || + e.ctrlKey || + e.shiftKey || + e.metaKey || + !isGrid.value || + isExpandedForm.value || + isEditColumn.value || + isExpandedFormOpen() + ) { + return + } switch (e.key) { case ';': diff --git a/packages/nc-gui/components/cell/DateTimePicker.vue b/packages/nc-gui/components/cell/DateTimePicker.vue index d607b4def2..7a5e3a542c 100644 --- a/packages/nc-gui/components/cell/DateTimePicker.vue +++ b/packages/nc-gui/components/cell/DateTimePicker.vue @@ -269,7 +269,18 @@ useEventListener(document, 'keydown', (e: KeyboardEvent) => { // To prevent event listener on non active cell if (!active.value) return - if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey || !isGrid.value || isExpandedForm.value || isEditColumn.value) return + if ( + e.altKey || + e.ctrlKey || + e.shiftKey || + e.metaKey || + !isGrid.value || + isExpandedForm.value || + isEditColumn.value || + isExpandedFormOpen() + ) { + return + } switch (e.key) { case ';': diff --git a/packages/nc-gui/components/cell/TimePicker.vue b/packages/nc-gui/components/cell/TimePicker.vue index 6c535db1e6..d2b273099b 100644 --- a/packages/nc-gui/components/cell/TimePicker.vue +++ b/packages/nc-gui/components/cell/TimePicker.vue @@ -185,7 +185,18 @@ useEventListener(document, 'keydown', (e: KeyboardEvent) => { // To prevent event listener on non active cell if (!active.value) return - if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey || !isGrid.value || isExpandedForm.value || isEditColumn.value) return + if ( + e.altKey || + e.ctrlKey || + e.shiftKey || + e.metaKey || + !isGrid.value || + isExpandedForm.value || + isEditColumn.value || + isExpandedFormOpen() + ) { + return + } switch (e.key) { case ';': diff --git a/packages/nc-gui/components/cell/YearPicker.vue b/packages/nc-gui/components/cell/YearPicker.vue index 5c630b7eb7..d2f3f50aca 100644 --- a/packages/nc-gui/components/cell/YearPicker.vue +++ b/packages/nc-gui/components/cell/YearPicker.vue @@ -176,7 +176,18 @@ useEventListener(document, 'keydown', (e: KeyboardEvent) => { // To prevent event listener on non active cell if (!active.value) return - if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey || !isGrid.value || isExpandedForm.value || isEditColumn.value) return + if ( + e.altKey || + e.ctrlKey || + e.shiftKey || + e.metaKey || + !isGrid.value || + isExpandedForm.value || + isEditColumn.value || + isExpandedFormOpen() + ) { + return + } switch (e.key) { case ';': diff --git a/packages/nc-gui/components/dashboard/TreeView/TableNode.vue b/packages/nc-gui/components/dashboard/TreeView/TableNode.vue index dede1f8c00..8dfae7041c 100644 --- a/packages/nc-gui/components/dashboard/TreeView/TableNode.vue +++ b/packages/nc-gui/components/dashboard/TreeView/TableNode.vue @@ -331,6 +331,7 @@ const deleteTable = () => {
@@ -358,7 +359,7 @@ const deleteTable = () => {
diff --git a/packages/nc-gui/components/dashboard/settings/DataSources.vue b/packages/nc-gui/components/dashboard/settings/DataSources.vue index 0e4a480bdf..721fedda6e 100644 --- a/packages/nc-gui/components/dashboard/settings/DataSources.vue +++ b/packages/nc-gui/components/dashboard/settings/DataSources.vue @@ -18,8 +18,6 @@ const vReload = useVModel(props, 'reload', emits) const { $api, $e } = useNuxtApp() -const { t } = useI18n() - const basesStore = useBases() const { loadProject } = basesStore const { isDataSourceLimitReached } = storeToRefs(basesStore) @@ -27,6 +25,8 @@ const { isDataSourceLimitReached } = storeToRefs(basesStore) const baseStore = useBase() const { base } = storeToRefs(baseStore) +const { isUIAllowed } = useRoles() + const { projectPageTab } = storeToRefs(useConfigStore()) const { refreshCommandPalette } = useCommandPalette() @@ -42,6 +42,44 @@ const isReloading = ref(false) const isDeleteBaseModalOpen = ref(false) const toBeDeletedBase = ref() +async function updateIfSourceOrderIsNullOrDuplicate() { + const sourceOrderSet = new Set() + let hasNullOrDuplicates = false + + // Check if sources.value contains null or duplicate order + for (const source of sources.value) { + if (source.order === null || sourceOrderSet.has(source.order)) { + hasNullOrDuplicates = true + break + } + sourceOrderSet.add(source.order) + } + + if (!hasNullOrDuplicates) return + + // update the local state + sources.value = sources.value.map((source, i) => { + return { + ...source, + order: i + 1, + } + }) + + try { + await Promise.all( + sources.value.map(async (source) => { + await $api.source.update(source.base_id as string, source.id as string, { + id: source.id, + base_id: source.base_id, + order: source.order, + }) + }), + ) + } catch (e: any) { + message.error(await extractSdkResponseErrorMsg(e)) + } +} + async function loadBases(changed?: boolean) { try { if (changed) refreshCommandPalette() @@ -53,6 +91,7 @@ async function loadBases(changed?: boolean) { if (baseList.list && baseList.list.length) { sources.value = baseList.list } + await updateIfSourceOrderIsNullOrDuplicate() } catch (e) { console.error(e) } finally { @@ -90,7 +129,7 @@ const deleteBase = async () => { refreshCommandPalette() } } -const toggleBase = async (source: BaseType, state: boolean) => { +const toggleBase = async (source: SourceType, state: boolean) => { try { if (!state && sources.value.filter((src) => src.enabled).length < 2) { message.info('There should be at least one enabled source!') @@ -116,20 +155,26 @@ const moveBase = async (e: any) => { // sources list is mutated so we have to get the new index and mirror it to backend const source = sources.value[e.newIndex] if (source) { - if (!source.order) { - // empty update call to reorder sources (migration) - await $api.source.update(source.base_id as string, source.id as string, { - id: source.id, - base_id: source.base_id, - }) - message.info(t('info.basesMigrated')) + let nextOrder: number + + // set new order value based on the new order of the items + if (sources.value.length - 1 === e.newIndex) { + // If moving to the end, set nextOrder greater than the maximum order in the list + nextOrder = Math.max(...sources.value.map((item) => item?.order ?? 0)) + 1 } else { - await $api.source.update(source.base_id as string, source.id as string, { - id: source.id, - base_id: source.base_id, - order: e.newIndex + 1, - }) + nextOrder = + (parseFloat(String(sources.value[e.newIndex - 1]?.order ?? 0)) + + parseFloat(String(sources.value[e.newIndex + 1]?.order ?? 0))) / + 2 } + + const _nextOrder = !isNaN(Number(nextOrder)) ? nextOrder : e.oldIndex + + await $api.source.update(source.base_id as string, source.id as string, { + id: source.id, + base_id: source.base_id, + order: _nextOrder, + }) } await loadProject(base.value.id as string, true) await loadBases() @@ -210,400 +255,239 @@ const isNewBaseModalOpen = computed({ }, }) -const isErdModalOpen = computed({ - get: () => { - return [DataSourcesSubTab.ERD].includes(vState.value as any) - }, - set: (val) => { - if (!val) { - vState.value = '' - } - }, -}) - -const isMetaDataModal = computed({ - get: () => { - return [DataSourcesSubTab.Metadata].includes(vState.value as any) - }, - set: (val) => { - if (!val) { - vState.value = '' - } - }, -}) - -const isUIAclModalOpen = computed({ - get: () => { - return [DataSourcesSubTab.UIAcl].includes(vState.value as any) - }, - set: (val) => { - if (!val) { - vState.value = '' - } - }, -}) -const isBaseAuditModalOpen = computed({ - get: () => { - return [DataSourcesSubTab.Audit].includes(vState.value as any) - }, - set: (val) => { - if (!val) { - vState.value = '' - } - }, -}) - -const isEditBaseModalOpen = computed({ - get: () => { - return [DataSourcesSubTab.Edit].includes(vState.value as any) - }, - set: (val) => { - if (!val) { - vState.value = '' - } - }, -}) +const activeSource = ref(null) +const openedTab = ref('erd')
-
-
+
+
{{ day[0] }}
-
+
{ 'text-gray-400': !isDateInCurrentMonth(date), 'nc-selected-week-start': isSameDate(date, selectedWeek?.start), 'nc-selected-week-end': isSameDate(date, selectedWeek?.end), - 'rounded-md text-brand-500 !font-semibold nc-calendar-today ': - isSameDate(date, dayjs()) && isDateInCurrentMonth(date), - 'h-9 w-9': size === 'large', + 'rounded-md text-brand-500 !font-semibold nc-calendar-today': isSameDate(date, dayjs()) && isDateInCurrentMonth(date), 'text-gray-500': date.get('day') === 0 || date.get('day') === 6, - 'h-8 w-8': size === 'medium', - 'h-6 w-6 text-[10px]': size === 'small', }" - class="px-1 py-1 relative border-1 font-medium flex items-center cursor-pointer justify-center" + class="px-1 h-8 w-8 py-1 relative transition border-1 font-medium flex text-gray-700 items-center cursor-pointer justify-center" data-testid="nc-calendar-date" - @dblclick="emitDblClick(date)" @click="handleSelectDate(date)" > {{ date.get('date') }} @@ -270,7 +207,7 @@ const emitDblClick = (date: dayjs.Dayjs) => { diff --git a/packages/nc-gui/components/smartsheet/calendar/YearView.vue b/packages/nc-gui/components/smartsheet/calendar/YearView/index.vue similarity index 59% rename from packages/nc-gui/components/smartsheet/calendar/YearView.vue rename to packages/nc-gui/components/smartsheet/calendar/YearView/index.vue index 5505892067..8452844a5c 100644 --- a/packages/nc-gui/components/smartsheet/calendar/YearView.vue +++ b/packages/nc-gui/components/smartsheet/calendar/YearView/index.vue @@ -13,17 +13,27 @@ const months = computed(() => { const calendarContainer = ref(null) -const { width } = useWindowSize() +const { width } = useElementSize(calendarContainer) -const size = ref('small') +const size = ref<'small' | 'medium'>('small') +const cols = ref(4) const handleResize = () => { - if (width.value < 1608) { - size.value = 'small' - } else if (width.value < 2000) { + if (width.value > 1250) { + size.value = 'medium' + cols.value = 4 + } else if (width.value > 850) { size.value = 'medium' + cols.value = 3 + } else if (width.value > 680) { + size.value = 'small' + cols.value = 3 + } else if (width.value > 375) { + size.value = 'small' + cols.value = 2 } else { - size.value = 'large' + size.value = 'medium' + cols.value = 1 } } @@ -40,15 +50,19 @@ watch(width, handleResize)
- +
diff --git a/packages/nc-gui/components/smartsheet/column/FormulaOptions.vue b/packages/nc-gui/components/smartsheet/column/FormulaOptions.vue index dd541072a5..5a6e1db3da 100644 --- a/packages/nc-gui/components/smartsheet/column/FormulaOptions.vue +++ b/packages/nc-gui/components/smartsheet/column/FormulaOptions.vue @@ -102,36 +102,47 @@ const sortOrder: Record = { const suggestionsList = computed(() => { const unsupportedFnList = sqlUi.value.getUnsupportedFnList() - return [ - ...availableFunctions - .filter((fn: string) => !unsupportedFnList.includes(fn)) - .map((fn: string) => ({ + return ( + [ + ...availableFunctions.map((fn: string) => ({ text: `${fn}()`, type: 'function', description: formulas[fn].description, syntax: formulas[fn].syntax, examples: formulas[fn].examples, docsUrl: formulas[fn].docsUrl, + unsupported: unsupportedFnList.includes(fn), })), - ...supportedColumns.value - .filter((c) => { - // skip system LTAR columns - if (c.uidt === UITypes.LinkToAnotherRecord && c.system) return false - // v1 logic? skip the current column - if (!column) return true - return column.value?.id !== c.id - }) - .map((c: any) => ({ - text: c.title, - type: 'column', - icon: getUIDTIcon(c.uidt), - uidt: c.uidt, + ...supportedColumns.value + .filter((c) => { + // skip system LTAR columns + if (c.uidt === UITypes.LinkToAnotherRecord && c.system) return false + // v1 logic? skip the current column + if (!column) return true + return column.value?.id !== c.id + }) + .map((c: any) => ({ + text: c.title, + type: 'column', + icon: getUIDTIcon(c.uidt), + uidt: c.uidt, + })), + ...availableBinOps.map((op: string) => ({ + text: op, + type: 'op', })), - ...availableBinOps.map((op: string) => ({ - text: op, - type: 'op', - })), - ] + ] + // move unsupported functions to the end + .sort((a: Record, b: Record) => { + if (a.unsupported && !b.unsupported) { + return 1 + } + if (!a.unsupported && b.unsupported) { + return -1 + } + return 0 + }) + ) }) // set default suggestion list @@ -214,6 +225,7 @@ function handleInput() { function selectText() { if (suggestion.value && selected.value > -1 && selected.value < suggestionsList.value.length) { if (selected.value < suggestedFormulas.value.length) { + if (suggestedFormulas.value[selected.value].unsupported) return appendText(suggestedFormulas.value[selected.value]) } else { appendText(variableList.value[selected.value + suggestedFormulas.value.length]) @@ -276,7 +288,7 @@ onMounted(() => {