From e3ed82d67eef4da265a37a6fdcdf032f244fd08f Mon Sep 17 00:00:00 2001 From: Muhammed Mustafa Date: Wed, 14 Sep 2022 19:03:27 +0530 Subject: [PATCH 01/17] feat/Added tooltip to show table name in table tree view --- .../nc-gui/components/dashboard/TreeView.vue | 89 ++++++++++--------- 1 file changed, 46 insertions(+), 43 deletions(-) diff --git a/packages/nc-gui/components/dashboard/TreeView.vue b/packages/nc-gui/components/dashboard/TreeView.vue index a40f81fd54..6a58161aa6 100644 --- a/packages/nc-gui/components/dashboard/TreeView.vue +++ b/packages/nc-gui/components/dashboard/TreeView.vue @@ -316,50 +316,53 @@ function openTableCreateDialog() { :data-id="table.id" @click="addTableTab(table)" > -
-
-
- -
- {{ table.title }} + + +
+
+
+ +
+ {{ table.title }} +
+ + + + + +
- - - - - - -
+
From f5fa3cd2d99db50fa5252c1943796ddf9bcb123d Mon Sep 17 00:00:00 2001 From: braks <78412429+bcakmakoglu@users.noreply.github.com> Date: Wed, 14 Sep 2022 23:15:25 +0200 Subject: [PATCH 02/17] fix(nc-gui): checkbox cell unselectable --- packages/nc-gui/components/cell/Checkbox.vue | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/nc-gui/components/cell/Checkbox.vue b/packages/nc-gui/components/cell/Checkbox.vue index abc49e68d5..d0240a1a7a 100644 --- a/packages/nc-gui/components/cell/Checkbox.vue +++ b/packages/nc-gui/components/cell/Checkbox.vue @@ -2,18 +2,23 @@ import { ColumnInj, IsFormInj, ReadonlyInj, getMdiIcon, inject } from '#imports' interface Props { - modelValue?: boolean | undefined | number + // If the previous cell value was a text, the initial checkbox value is a string type + // otherwise it can be either a boolean, or a string representing a boolean, i.e '0' or '1' + modelValue?: boolean | string | '0' | '1' } interface Emits { - (event: 'update:modelValue', model: boolean | undefined | number): void + (event: 'update:modelValue', model: boolean): void } const props = defineProps() const emits = defineEmits() -let vModel = $(useVModel(props, 'modelValue', emits)) +let vModel = $computed({ + get: () => !!props.modelValue && props.modelValue !== '0', + set: (val) => emits('update:modelValue', val), +}) const column = inject(ColumnInj) From 2d0de7aed9212acd9c36d9f17e25743d95e2b162 Mon Sep 17 00:00:00 2001 From: braks <78412429+bcakmakoglu@users.noreply.github.com> Date: Wed, 14 Sep 2022 19:15:25 +0200 Subject: [PATCH 03/17] feat(nc-gui): add route param types --- packages/nc-gui/nuxt-shim.d.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/nc-gui/nuxt-shim.d.ts b/packages/nc-gui/nuxt-shim.d.ts index 55dc6791c4..f1a4826f57 100644 --- a/packages/nc-gui/nuxt-shim.d.ts +++ b/packages/nc-gui/nuxt-shim.d.ts @@ -28,4 +28,9 @@ declare module 'vue-router' { hideHeader?: boolean title?: string } + + interface RouteParams { + projectId: string + projectType: 'base' | 'nc' | string + } } From 9c9a73e4667677fac4d79b1cd173c3e832e2c307 Mon Sep 17 00:00:00 2001 From: braks <78412429+bcakmakoglu@users.noreply.github.com> Date: Wed, 14 Sep 2022 19:15:40 +0200 Subject: [PATCH 04/17] chore(nc-gui): update Roles type --- packages/nc-gui/lib/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nc-gui/lib/types.ts b/packages/nc-gui/lib/types.ts index 74f2a6e4e0..ac8d3fc562 100644 --- a/packages/nc-gui/lib/types.ts +++ b/packages/nc-gui/lib/types.ts @@ -31,7 +31,7 @@ export interface Field { system?: boolean } -export type Roles = Record | string +export type Roles = Record | string export type Filter = FilterType & { status?: 'update' | 'delete' | 'create'; parentId?: string; readOnly?: boolean } From 52c311f7e3b6dca2d4acac74fcbb873e2c7504b8 Mon Sep 17 00:00:00 2001 From: braks <78412429+bcakmakoglu@users.noreply.github.com> Date: Wed, 14 Sep 2022 19:15:51 +0200 Subject: [PATCH 05/17] fix(nc-gui): project state not properly reset # What's changed? * try loading project when clicking on list item * reduces potential waiting times * only load project if no request is running already * move fetching project to parent page to prevent multiple loads --- packages/nc-gui/composables/useProject.ts | 90 +++++++++++++------ packages/nc-gui/composables/useViewData.ts | 4 +- .../pages/[projectType]/[projectId]/index.vue | 34 ++++++- .../[projectType]/[projectId]/index/index.vue | 37 +------- packages/nc-gui/pages/index/index/index.vue | 38 +++++--- 5 files changed, 124 insertions(+), 79 deletions(-) diff --git a/packages/nc-gui/composables/useProject.ts b/packages/nc-gui/composables/useProject.ts index 9d073426e3..d452d5c6b1 100644 --- a/packages/nc-gui/composables/useProject.ts +++ b/packages/nc-gui/composables/useProject.ts @@ -1,31 +1,53 @@ import type { MaybeRef } from '@vueuse/core' -import { SqlUiFactory } from 'nocodb-sdk' import type { OracleUi, ProjectType, TableType } from 'nocodb-sdk' -import { useNuxtApp, useRoute } from '#app' -import type { ProjectMetaInfo } from '~/lib' +import { SqlUiFactory } from 'nocodb-sdk' +import { isString } from '@vueuse/core' +import { + USER_PROJECT_ROLES, + computed, + createEventHook, + ref, + useApi, + useGlobal, + useInjectionState, + useNuxtApp, + useRoute, + useState, + useTheme, + watch, +} from '#imports' +import type { ProjectMetaInfo, Roles } from '~/lib' import type { ThemeConfig } from '@/composables/useTheme' -import { createEventHook, useInjectionState } from '#imports' const [setup, use] = useInjectionState((_projectId?: MaybeRef) => { - const { $api, $e } = useNuxtApp() + const { $e } = useNuxtApp() + + const { api, isLoading } = useApi() + const route = useRoute() + const { includeM2M } = useGlobal() + const { setTheme, theme } = useTheme() const projectLoadedHook = createEventHook() - const projectId = computed(() => (_projectId ? unref(_projectId) : (route.params.projectId as string))) const project = ref({}) + const tables = ref([]) - const projectRoles = useState>(USER_PROJECT_ROLES, () => ({})) + + const projectRoles = useState(USER_PROJECT_ROLES, () => ({})) + const projectMetaInfo = ref() + const projectId = computed(() => (_projectId ? unref(_projectId) : (route.params.projectId as string))) + // todo: refactor path param name and variable name const projectType = $computed(() => route.params.projectType as string) - const projectMeta = computed(() => { + const projectMeta = computed>(() => { try { - return typeof project.value.meta === 'string' ? JSON.parse(project.value.meta) : project.value.meta + return project.value.meta && isString(project.value.meta) ? JSON.parse(project.value.meta) : project.value.meta } catch (e) { return {} } @@ -44,15 +66,15 @@ const [setup, use] = useInjectionState((_projectId?: MaybeRef) => { async function loadProjectMetaInfo(force?: boolean) { if (!projectMetaInfo.value || force) { - const data = await $api.project.metaGet(project.value.id!, {}, {}) - projectMetaInfo.value = data + projectMetaInfo.value = await api.project.metaGet(project.value.id!, {}, {}) } } async function loadProjectRoles() { projectRoles.value = {} + if (isSharedBase.value) { - const user = await $api.auth.me( + const user = await api.auth.me( {}, { headers: { @@ -60,33 +82,40 @@ const [setup, use] = useInjectionState((_projectId?: MaybeRef) => { }, }, ) + projectRoles.value = user.roles } else if (project.value.id) { - const user = await $api.auth.me({ project_id: project.value.id }) + const user = await api.auth.me({ project_id: project.value.id }) projectRoles.value = user.roles } } async function loadTables() { if (project.value.id) { - const tablesResponse = await $api.dbTable.list(project.value.id, { + const tablesResponse = await api.dbTable.list(project.value.id, { includeM2M: includeM2M.value, }) + if (tablesResponse.list) tables.value = tablesResponse.list } } - async function loadProject() { - if (projectType === 'base') { - const baseData = await $api.public.sharedBaseGet(route.params.projectId as string) - project.value = await $api.project.read(baseData.project_id!) + async function loadProject(id?: string) { + if (id) { + project.value = await api.project.read(projectId.value) + } else if (projectType === 'base') { + const baseData = await api.public.sharedBaseGet(route.params.projectId as string) + project.value = await api.project.read(baseData.project_id!) } else if (projectId.value) { - project.value = await $api.project.read(projectId.value) + project.value = await api.project.read(projectId.value) } else { return } + await loadProjectRoles() + await loadTables() + setTheme(projectMeta.value?.theme) projectLoadedHook.trigger(project.value) @@ -97,15 +126,13 @@ const [setup, use] = useInjectionState((_projectId?: MaybeRef) => { return } if (data.meta && typeof data.meta === 'string') { - await $api.project.update(projectId.value, data) + await api.project.update(projectId.value, data) } else { - await $api.project.update(projectId.value, { ...data, meta: JSON.stringify(data.meta) }) + await api.project.update(projectId.value, { ...data, meta: JSON.stringify(data.meta) }) } } async function saveTheme(_theme: Partial) { - $e('c:themes:change') - const fullTheme = { primaryColor: theme.value.primaryColor, accentColor: theme.value.accentColor, @@ -119,25 +146,30 @@ const [setup, use] = useInjectionState((_projectId?: MaybeRef) => { theme: fullTheme, }, }) + setTheme(fullTheme) + + $e('c:themes:change') } watch( () => route.params, - (v) => { - if (!v?.projectId) { + (next) => { + if (!next.projectId) { setTheme() } }, ) - // TODO useProject should only called inside a project for now this doesn't work - onScopeDispose(() => { + const reset = () => { project.value = {} tables.value = [] projectMetaInfo.value = undefined projectRoles.value = {} - }) + } + + // TODO useProject should only called inside a project for now this doesn't work + // onScopeDispose(reset) return { project, @@ -156,6 +188,8 @@ const [setup, use] = useInjectionState((_projectId?: MaybeRef) => { projectMeta, saveTheme, projectLoadedHook: projectLoadedHook.on, + reset, + isLoading, } }, 'useProject') diff --git a/packages/nc-gui/composables/useViewData.ts b/packages/nc-gui/composables/useViewData.ts index 5bd5340d8e..b33fe25bb0 100644 --- a/packages/nc-gui/composables/useViewData.ts +++ b/packages/nc-gui/composables/useViewData.ts @@ -1,8 +1,6 @@ import type { Api, ColumnType, FormType, GalleryType, PaginatedType, TableType, ViewType } from 'nocodb-sdk' import type { ComputedRef, Ref } from 'vue' import { message } from 'ant-design-vue' -import { useI18n } from 'vue-i18n' -import { useNuxtApp } from '#app' import { IsPublicInj, NOCO, @@ -10,6 +8,8 @@ import { extractSdkResponseErrorMsg, getHTMLEncodedText, useApi, + useI18n, + useNuxtApp, useProject, useUIPermission, } from '#imports' diff --git a/packages/nc-gui/pages/[projectType]/[projectId]/index.vue b/packages/nc-gui/pages/[projectType]/[projectId]/index.vue index 37f731b86d..de2455f182 100644 --- a/packages/nc-gui/pages/[projectType]/[projectId]/index.vue +++ b/packages/nc-gui/pages/[projectType]/[projectId]/index.vue @@ -5,6 +5,8 @@ import { computed, definePageMeta, navigateTo, + onBeforeMount, + onBeforeUnmount, onKeyStroke, openLink, projectThemeColors, @@ -16,6 +18,7 @@ import { useI18n, useProject, useRoute, + useRouter, useTabs, useUIPermission, } from '#imports' @@ -29,9 +32,20 @@ const { t } = useI18n() const route = useRoute() +const router = useRouter() + const { appInfo, token, signOut, signedIn, user } = useGlobal() -const { projectLoadedHook, project, isSharedBase, loadProjectMetaInfo, projectMetaInfo, saveTheme } = useProject() +const { + project, + isSharedBase, + loadProjectMetaInfo, + projectMetaInfo, + saveTheme, + isLoading: isLoadingProject, + loadProject, + reset, +} = useProject() const { clearTabs, addTab } = useTabs() @@ -114,7 +128,7 @@ const copyProjectInfo = async () => { // Copied to clipboard message.info(t('msg.info.copiedToClipboard')) } catch (e: any) { - console.log(e) + console.error(e) message.error(e.message) } } @@ -125,7 +139,7 @@ const copyAuthToken = async () => { // Copied to clipboard message.info(t('msg.info.copiedToClipboard')) } catch (e: any) { - console.log(e) + console.error(e) message.error(e.message) } } @@ -140,11 +154,23 @@ onKeyStroke( clearTabs() -projectLoadedHook(() => { +onBeforeMount(async () => { + if (!isLoadingProject.value) { + await loadProject() + } + if (!route.params.type && isUIAllowed('teamAndAuth')) { addTab({ type: TabType.AUTH, title: t('title.teamAndAuth') }) } + + /** If v1 url found navigate to corresponding new url */ + const { type, name, view } = route.query + if (type && name) { + await router.replace(`/nc/${route.params.projectId}/${type}/${name}${view ? `/${view}` : ''}`) + } }) + +onBeforeUnmount(reset)