diff --git a/packages/nc-gui/components/dashboard/settings/data-sources/CreateBase.vue b/packages/nc-gui/components/dashboard/settings/data-sources/CreateBase.vue index 0c7fad4a75..7661d2c498 100644 --- a/packages/nc-gui/components/dashboard/settings/data-sources/CreateBase.vue +++ b/packages/nc-gui/components/dashboard/settings/data-sources/CreateBase.vue @@ -72,9 +72,20 @@ const customFormState = ref({ extraParameters: [], }) +const easterEgg = ref(false) + +const easterEggCount = ref(0) + +const onEasterEgg = () => { + easterEggCount.value += 1 + if (easterEggCount.value >= 2) { + easterEgg.value = true + } +} + const clientTypes = computed(() => { return _clientTypes.filter((type) => { - return ![ClientType.SNOWFLAKE, ClientType.DATABRICKS].includes(type.value) + return ![ClientType.SNOWFLAKE, ClientType.DATABRICKS, ...(easterEgg.value ? [ClientType.MSSQL] : [])].includes(type.value) }) }) @@ -404,7 +415,11 @@ const allowMetaWrite = computed({ get: () => !formState.value.is_schema_readonly, set: (v) => { formState.value.is_schema_readonly = !v - $e('c:source:schema-write-toggle', {allowed: !v}) + // if schema write is allowed, data write should be allowed too + if (v) { + formState.value.is_data_readonly = false + } + $e('c:source:schema-write-toggle', { allowed: !v, edit: true }) }, }) @@ -550,18 +565,30 @@ const allowDataWrite = computed({ - +
+ + + + +
@@ -730,6 +757,7 @@ const allowDataWrite = computed({
+
!formState.value.is_schema_readonly, set: (v) => { formState.value.is_schema_readonly = !v + // if schema write is allowed, data write should be allowed too + if (v) { + formState.value.is_data_readonly = false + } $e('c:source:schema-write-toggle', { allowed: !v, edit: true }) }, }) @@ -579,7 +583,19 @@ const allowDataWrite = computed({
- +
+ + + + +
diff --git a/packages/nc-gui/components/smartsheet/calendar/DayView/DateTimeField.vue b/packages/nc-gui/components/smartsheet/calendar/DayView/DateTimeField.vue index 5701131615..28669b5669 100644 --- a/packages/nc-gui/components/smartsheet/calendar/DayView/DateTimeField.vue +++ b/packages/nc-gui/components/smartsheet/calendar/DayView/DateTimeField.vue @@ -996,7 +996,7 @@ watch(
diff --git a/packages/nc-gui/components/smartsheet/header/Menu.vue b/packages/nc-gui/components/smartsheet/header/Menu.vue index 1f1e4c4fa4..69f4d407be 100644 --- a/packages/nc-gui/components/smartsheet/header/Menu.vue +++ b/packages/nc-gui/components/smartsheet/header/Menu.vue @@ -43,7 +43,7 @@ const { gridViewCols } = useViewColumnsOrThrow() const { fieldsToGroupBy, groupByLimit } = useViewGroupByOrThrow(view) -const { isUIAllowed, isMetaReadOnly } = useRoles() +const { isUIAllowed, isMetaReadOnly, isDataReadOnly } = useRoles() const isLoading = ref<'' | 'hideOrShow' | 'setDisplay'>('') @@ -325,7 +325,11 @@ const isDeleteAllowed = computed(() => { return column?.value && !column.value.system }) const isDuplicateAllowed = computed(() => { - return column?.value && !column.value.system + return ( + column?.value && + !column.value.system && + ((!isMetaReadOnly.value && !isDataReadOnly.value) || readonlyMetaAllowedTypes.includes(column.value?.uidt)) + ) }) const isFilterSupported = computed( () => @@ -394,7 +398,7 @@ const isColumnUpdateAllowed = computed(() => {
@@ -530,14 +534,16 @@ const isColumnUpdateAllowed = computed(() => { - -
+
{{ $t('general.delete') }} diff --git a/packages/nc-gui/lang/en.json b/packages/nc-gui/lang/en.json index df5652ae2d..962708b384 100644 --- a/packages/nc-gui/lang/en.json +++ b/packages/nc-gui/lang/en.json @@ -456,8 +456,8 @@ "looksLikeThisStackIsEmpty": "Looks like this stack does not have any records" }, "labels": { - "allowMetaWrite" : "Allow Schema Change", - "allowDataWrite" : "Allow Data Write/Edit", + "allowMetaWrite" : "Allow Schema Edit", + "allowDataWrite" : "Allow Data Edit", "selectView": "Select a View", "connectionDetails": "Connection Details", "metaSync": "Meta Sync", @@ -1040,6 +1040,7 @@ "group": "Group" }, "tooltip": { + "dataWriteOptionDisabled": "Data editing can disable only when ‘Schema edit’ is disabled and becomes enabled otherwise", "allowMetaWrite": "Enable this option to allow modifications to the database schema, including adding, altering, or deleting tables and columns. Use with caution, as changes may affect application functionality.", "allowDataWrite": "Enable this option to allow updating, deleting, or inserting data within the database tables. Ideal for administrative users who need to manage data directly.", "reachedSourceLimit": "Limited to only one data source for the moment", diff --git a/tests/playwright/pages/Dashboard/TreeView.ts b/tests/playwright/pages/Dashboard/TreeView.ts index d7847114e8..65e9450312 100644 --- a/tests/playwright/pages/Dashboard/TreeView.ts +++ b/tests/playwright/pages/Dashboard/TreeView.ts @@ -366,6 +366,19 @@ export class TreeViewPage extends BasePage { await this.rootPage.locator('div.ant-modal-content').locator(`button.ant-btn:has-text("Delete")`).click(); } + async duplicateProject(param: { title: string; context: NcContext }) { + param.title = this.scopedProjectTitle({ title: param.title, context: param.context }); + + await this.openProjectContextMenu({ baseTitle: param.title }); + const contextMenu = this.dashboard.get().locator('.ant-dropdown-menu.nc-scrollbar-md:visible'); + await contextMenu.waitFor(); + await contextMenu.locator(`.ant-dropdown-menu-item:has-text("Duplicate")`).click(); + + await this.rootPage.locator('div.ant-modal-content').locator(`button.ant-btn:has-text("Confirm")`).click(); + + await this.rootPage.waitForTimeout(10000); + } + async openProjectSourceSettings(param: { title: string; context: NcContext }) { param.title = this.scopedProjectTitle({ title: param.title, context: param.context }); diff --git a/tests/playwright/tests/db/general/sourceRestrictions.spec.ts b/tests/playwright/tests/db/general/sourceRestrictions.spec.ts index de6c1e00b1..8510170c14 100644 --- a/tests/playwright/tests/db/general/sourceRestrictions.spec.ts +++ b/tests/playwright/tests/db/general/sourceRestrictions.spec.ts @@ -34,6 +34,7 @@ test.describe('Source Restrictions', () => { await settingsPage.selectTab({ tab: 'dataSources' }); await dashboard.rootPage.waitForTimeout(300); + await settingsPage.source.updateSchemaReadOnly({ sourceName: 'Default', readOnly: true }); await settingsPage.source.updateDataReadOnly({ sourceName: 'Default', readOnly: true }); await settingsPage.close(); @@ -78,6 +79,13 @@ test.describe('Source Restrictions', () => { .locator('.nc-ui-dt-dropdown') .scrollIntoViewIfNeeded(); await dashboard.grid.get().locator(`th[data-title="LastName"]`).first().locator('.nc-ui-dt-dropdown').click(); - await expect(await dashboard.rootPage.locator('li[role="menuitem"]:has-text("Edit"):visible').last()).toBeVisible(); + for (const item of ['Edit', 'Delete', 'Duplicate']) { + await expect( + await dashboard.rootPage.locator(`li[role="menuitem"]:has-text("${item}"):visible`).last() + ).toBeVisible(); + await expect( + await dashboard.rootPage.locator(`li[role="menuitem"]:has-text("${item}"):visible`).last() + ).toHaveClass(/ant-dropdown-menu-item-disabled/); + } }); });