diff --git a/packages/nc-gui/assets/nc-icons/project.svg b/packages/nc-gui/assets/nc-icons/project.svg
new file mode 100644
index 0000000000..842336ef6e
--- /dev/null
+++ b/packages/nc-gui/assets/nc-icons/project.svg
@@ -0,0 +1,9 @@
+
diff --git a/packages/nc-gui/components/dashboard/Sidebar/UserInfo.vue b/packages/nc-gui/components/dashboard/Sidebar/UserInfo.vue
index 4ff81a7336..8a878878b5 100644
--- a/packages/nc-gui/components/dashboard/Sidebar/UserInfo.vue
+++ b/packages/nc-gui/components/dashboard/Sidebar/UserInfo.vue
@@ -26,7 +26,8 @@ const logout = async () => {
try {
await signOut(false)
- await clearWorkspaces()
+ // No need as all stores are cleared on signout
+ // await clearWorkspaces()
await navigateTo('/signin')
} catch (e) {
diff --git a/packages/nc-gui/components/dashboard/TreeView/ViewsList.vue b/packages/nc-gui/components/dashboard/TreeView/ViewsList.vue
index 222a0e6c99..c5133a76a7 100644
--- a/packages/nc-gui/components/dashboard/TreeView/ViewsList.vue
+++ b/packages/nc-gui/components/dashboard/TreeView/ViewsList.vue
@@ -45,7 +45,7 @@ const isDefaultBase = computed(() => {
return _isDefaultBase(base)
})
-const { viewsByTable, activeView } = storeToRefs(useViewsStore())
+const { viewsByTable, activeView, recentViews } = storeToRefs(useViewsStore())
const { navigateToTable } = useTablesStore()
@@ -57,7 +57,7 @@ const { refreshCommandPalette } = useCommandPalette()
const { addUndo, defineModelScope } = useUndoRedo()
-const { navigateToView, loadViews } = useViewsStore()
+const { navigateToView, loadViews, removeFromRecentViews } = useViewsStore()
/** Selected view(s) for menu */
const selected = ref([])
@@ -244,6 +244,13 @@ async function onRename(view: ViewType, originalTitle?: string, undo = false) {
scope: defineModelScope({ view: activeView.value }),
})
}
+ // update view name in recent views
+ recentViews.value = recentViews.value.map((rv) => {
+ if (rv.viewId === view.id && rv.tableID === view.fk_model_id) {
+ rv.viewName = view.title
+ }
+ return rv
+ })
// View renamed successfully
// message.success(t('msg.success.viewRenamed'))
@@ -265,6 +272,7 @@ function openDeleteDialog(view: ViewType) {
emits('deleted')
+ removeFromRecentViews({ viewId: view.id, tableId: view.fk_model_id, projectId: project.value.id })
refreshCommandPalette()
if (activeView.value?.id === view.id) {
navigateToTable({
diff --git a/packages/nc-gui/components/dlg/ProjectDelete.vue b/packages/nc-gui/components/dlg/ProjectDelete.vue
index fc65218da4..57165d9843 100644
--- a/packages/nc-gui/components/dlg/ProjectDelete.vue
+++ b/packages/nc-gui/components/dlg/ProjectDelete.vue
@@ -14,6 +14,8 @@ const projectsStore = useProjects()
const { deleteProject, navigateToFirstProjectOrHome } = projectsStore
const { projects } = storeToRefs(projectsStore)
+const { removeFromRecentViews } = useViewsStore()
+
const { refreshCommandPalette } = useCommandPalette()
const project = computed(() => projects.value.get(props.projectId))
@@ -41,6 +43,7 @@ const onDelete = async () => {
message.error(await extractSdkResponseErrorMsg(e))
} finally {
isLoading.value = false
+ removeFromRecentViews({ projectId: toBeDeletedProject.id! })
}
}
diff --git a/packages/nc-gui/components/dlg/TableDelete.vue b/packages/nc-gui/components/dlg/TableDelete.vue
index 401aa85387..d20824f7ae 100644
--- a/packages/nc-gui/components/dlg/TableDelete.vue
+++ b/packages/nc-gui/components/dlg/TableDelete.vue
@@ -20,6 +20,7 @@ const { getMeta, removeMeta } = useMetas()
const { loadTables, projectUrl, isXcdbBase } = useProject()
const { refreshCommandPalette } = useCommandPalette()
+const { removeFromRecentViews } = useViewsStore()
const { projectTables, activeTable } = storeToRefs(useTablesStore())
const { openTable } = useTablesStore()
@@ -69,6 +70,9 @@ const onDelete = async () => {
await loadTables()
+ // Remove from recent views
+ removeFromRecentViews({ projectId: props.projectId, tableId: toBeDeletedTable.id as string })
+
removeMeta(toBeDeletedTable.id as string)
refreshCommandPalette()
// Deleted table successfully
diff --git a/packages/nc-gui/store/views.ts b/packages/nc-gui/store/views.ts
index 320e595208..bad2368e7c 100644
--- a/packages/nc-gui/store/views.ts
+++ b/packages/nc-gui/store/views.ts
@@ -1,4 +1,4 @@
-import { type ViewType } from 'nocodb-sdk'
+import type { ViewType } from 'nocodb-sdk'
import { acceptHMRUpdate, defineStore } from 'pinia'
import type { ViewPageType } from '~/lib'
@@ -6,6 +6,7 @@ export const useViewsStore = defineStore('viewsStore', () => {
const { $api } = useNuxtApp()
const router = useRouter()
+ const recentViews = ref([])
const route = router.currentRoute
const tablesStore = useTablesStore()
@@ -20,6 +21,7 @@ export const useViewsStore = defineStore('viewsStore', () => {
viewsByTable.value.set(tablesStore.activeTableId, value)
},
})
+
const isViewsLoading = ref(true)
const isViewDataLoading = ref(true)
const isPublic = computed(() => route.value.meta?.public)
@@ -118,6 +120,10 @@ export const useViewsStore = defineStore('viewsStore', () => {
})
}
+ const changeView = async (..._args: any) => {}
+
+ const removeFromRecentViews = (..._args: any) => {}
+
watch(
() => tablesStore.activeTableId,
async (newId, oldId) => {
@@ -203,6 +209,7 @@ export const useViewsStore = defineStore('viewsStore', () => {
isViewDataLoading,
isPaginationLoading,
loadViews,
+ recentViews,
views,
activeView,
openedViewsTab,
@@ -211,6 +218,8 @@ export const useViewsStore = defineStore('viewsStore', () => {
viewsByTable,
activeViewTitleOrId,
navigateToView,
+ changeView,
+ removeFromRecentViews,
}
})
diff --git a/tests/playwright/pages/Dashboard/Command/CmdJPage.ts b/tests/playwright/pages/Dashboard/Command/CmdJPage.ts
new file mode 100644
index 0000000000..e7a4fcb15e
--- /dev/null
+++ b/tests/playwright/pages/Dashboard/Command/CmdJPage.ts
@@ -0,0 +1,39 @@
+import BasePage from '../../Base';
+import { DashboardPage } from '..';
+
+export class CmdJ extends BasePage {
+ readonly dashboardPage: DashboardPage;
+
+ constructor(dashboard: DashboardPage) {
+ super(dashboard.rootPage);
+ this.dashboardPage = dashboard;
+ }
+
+ get() {
+ return this.dashboardPage.get().locator('.DocSearch');
+ }
+
+ async openCmdJ() {
+ await this.dashboardPage.rootPage.keyboard.press(this.isMacOs() ? 'Meta+J' : 'Control+J');
+ // await this.dashboardPage.rootPage.waitForSelector('.DocSearch-Input');
+ }
+
+ async searchText(text: string) {
+ await this.dashboardPage.rootPage.fill('.DocSearch-Input', text);
+ }
+
+ async isCmdJVisible() {
+ const isVisible = this.get();
+ return await isVisible.count();
+ }
+
+ async isCmdJNotVisible() {
+ const isNotVisible = this.get();
+ return await isNotVisible.count();
+ }
+
+ async getPlaceholderText() {
+ const placeholderText = this.get().locator('.DocSearch-Input');
+ return await placeholderText.innerText();
+ }
+}
diff --git a/tests/playwright/pages/Dashboard/Command/CmdKPage.ts b/tests/playwright/pages/Dashboard/Command/CmdKPage.ts
new file mode 100644
index 0000000000..5ac62b25cd
--- /dev/null
+++ b/tests/playwright/pages/Dashboard/Command/CmdKPage.ts
@@ -0,0 +1,35 @@
+import BasePage from '../../Base';
+import { DashboardPage } from '..';
+
+export class CmdK extends BasePage {
+ readonly dashboardPage: DashboardPage;
+
+ constructor(dashboard: DashboardPage) {
+ super(dashboard.rootPage);
+ this.dashboardPage = dashboard;
+ }
+
+ get() {
+ return this.dashboardPage.get().locator('.cmdk-modal.cmdk-modal-active');
+ }
+
+ async openCmdK() {
+ await this.dashboardPage.rootPage.keyboard.press(this.isMacOs() ? 'Meta+K' : 'Control+K');
+ // await this.dashboardPage.rootPage.waitForSelector('.DocSearch-Input');
+ }
+
+ async searchText(text: string) {
+ await this.dashboardPage.rootPage.fill('.cmdk-input', text);
+ await this.rootPage.keyboard.press('Enter');
+ }
+
+ async isCmdKVisible() {
+ const isVisible = this.get();
+ return await isVisible.count();
+ }
+
+ async isCmdKNotVisible() {
+ const isNotVisible = this.get();
+ return await isNotVisible.count();
+ }
+}
diff --git a/tests/playwright/pages/Dashboard/Command/CmdLPage.ts b/tests/playwright/pages/Dashboard/Command/CmdLPage.ts
new file mode 100644
index 0000000000..3c9768323c
--- /dev/null
+++ b/tests/playwright/pages/Dashboard/Command/CmdLPage.ts
@@ -0,0 +1,49 @@
+import BasePage from '../../Base';
+import { DashboardPage } from '..';
+
+export class CmdL extends BasePage {
+ readonly dashboardPage: DashboardPage;
+
+ constructor(dashboard: DashboardPage) {
+ super(dashboard.rootPage);
+ this.dashboardPage = dashboard;
+ }
+
+ get() {
+ return this.dashboardPage.get().locator('.cmdl-modal.cmdl-modal-active');
+ }
+
+ async openCmdL() {
+ await this.dashboardPage.rootPage.keyboard.press(this.isMacOs() ? 'Meta+L' : 'Control+L');
+ }
+
+ async isCmdLVisible() {
+ const isVisible = this.get();
+ return await isVisible.count();
+ }
+
+ async isCmdLNotVisible() {
+ const isNotVisible = this.get();
+ return await isNotVisible.count();
+ }
+
+ async moveDown() {
+ await this.dashboardPage.rootPage.keyboard.press('ArrowDown');
+ }
+
+ async moveUp() {
+ await this.dashboardPage.rootPage.keyboard.press('ArrowUp');
+ }
+
+ async openRecent() {
+ await this.dashboardPage.rootPage.keyboard.press('Enter');
+ }
+
+ async getActiveViewTitle() {
+ return await this.dashboardPage.get().locator('.nc-active-view-title').innerText();
+ }
+
+ async getActiveTableTitle() {
+ return await this.dashboardPage.get().locator('.nc-active-table-title').innerText();
+ }
+}
diff --git a/tests/playwright/pages/Dashboard/index.ts b/tests/playwright/pages/Dashboard/index.ts
index 86675ea008..e89f1006ea 100644
--- a/tests/playwright/pages/Dashboard/index.ts
+++ b/tests/playwright/pages/Dashboard/index.ts
@@ -25,6 +25,9 @@ import { ProjectTypes } from 'nocodb-sdk';
import { WorkspacePage } from '../WorkspacePage';
import { DetailsPage } from './Details';
import { WorkspaceSettingsObject } from './WorkspaceSettings';
+import { CmdJ } from './Command/CmdJPage';
+import { CmdK } from './Command/CmdKPage';
+import { CmdL } from './Command/CmdLPage';
export class DashboardPage extends BasePage {
readonly project: any;
@@ -55,6 +58,9 @@ export class DashboardPage extends BasePage {
readonly shareProjectButton: ShareProjectButtonPage;
readonly details: DetailsPage;
readonly workspaceSettings: WorkspaceSettingsObject;
+ readonly cmdJ: CmdJ;
+ readonly cmdK: CmdK;
+ readonly cmdL: CmdL;
constructor(rootPage: Page, project: any) {
super(rootPage);
@@ -88,6 +94,9 @@ export class DashboardPage extends BasePage {
this.shareProjectButton = new ShareProjectButtonPage(this);
this.details = new DetailsPage(this);
this.workspaceSettings = new WorkspaceSettingsObject(this);
+ this.cmdJ = new CmdJ(this);
+ this.cmdK = new CmdK(this);
+ this.cmdL = new CmdL(this);
}
get() {