Browse Source

feat(gui-v2): table rename and minor improvements

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/2830/head
Pranav C 2 years ago
parent
commit
5d37bb97c6
  1. 30
      packages/nc-gui-v2/components/dashboard/TreeView.vue
  2. 117
      packages/nc-gui-v2/components/dlg/TableRename.vue
  3. 3
      packages/nc-gui-v2/pages/nc/[projectId].vue

30
packages/nc-gui-v2/components/dashboard/TreeView.vue

@ -23,7 +23,7 @@ import MdiAPIDocIcon from '~icons/mdi/open-in-new'
const { addTab } = useTabs() const { addTab } = useTabs()
const toast = useToast() const toast = useToast()
const { $api } = useNuxtApp() const { $api, $e } = useNuxtApp()
const { isUIAllowed } = useUIPermission() const { isUIAllowed } = useUIPermission()
const route = useRoute() const route = useRoute()
const { tables, loadTables } = useProject(route.params.projectId as string) const { tables, loadTables } = useProject(route.params.projectId as string)
@ -109,9 +109,11 @@ const contextMenuTarget = reactive<{ type?: 'table' | 'main'; value?: any }>({})
const setMenuContext = (type: 'table' | 'main', value?: any) => { const setMenuContext = (type: 'table' | 'main', value?: any) => {
contextMenuTarget.type = type contextMenuTarget.type = type
contextMenuTarget.value = value contextMenuTarget.value = value
$e('c:table:create:navdraw:right-click')
} }
const deleteTable = (table: TableType) => { const deleteTable = (table: TableType) => {
$e('c:table:delete');
// 'Click Submit to Delete The table' // 'Click Submit to Delete The table'
Modal.confirm({ Modal.confirm({
title: `Click Yes to Delete The table : ${table.title}`, title: `Click Yes to Delete The table : ${table.title}`,
@ -155,6 +157,7 @@ const deleteTable = (table: TableType) => {
removeMeta(table.id as string) removeMeta(table.id as string)
toast.info(`Deleted table ${table.title} successfully`) toast.info(`Deleted table ${table.title} successfully`)
$e('a:table:delete')
} catch (e: any) { } catch (e: any) {
toast.error(await extractSdkResponseErrorMsg(e)) toast.error(await extractSdkResponseErrorMsg(e))
} }
@ -192,7 +195,7 @@ const showRenameTableDlg = (table: TableType) => {
<span class="flex-grow text-bold" <span class="flex-grow text-bold"
>{{ $t('objects.tables') }} <template v-if="tables?.length">({{ tables.length }})</template></span >{{ $t('objects.tables') }} <template v-if="tables?.length">({{ tables.length }})</template></span
> >
<MdiPlus class="text-gray-500" @click.stop="tableCreateDlg = true" /> <MdiPlus class="text-gray-500" @click.stop="tableCreateDlg = true" v-t="['c:table:create:navdraw']"/>
<MdiMenuDown <MdiMenuDown
class="transition-transform !duration-100 text-gray-500" class="transition-transform !duration-100 text-gray-500"
:class="{ 'transform rotate-180': showTableList }" :class="{ 'transform rotate-180': showTableList }"
@ -208,17 +211,19 @@ const showRenameTableDlg = (table: TableType) => {
:data-order="table.order" :data-order="table.order"
:data-id="table.id" :data-id="table.id"
@click="addTab({ type: 'table', title: table.title, id: table.id })" @click="addTab({ type: 'table', title: table.title, id: table.id })"
v-t="['a:table:open']"
> >
<div class="flex align-center gap-1" @contextmenu="setMenuContext('table', table)"> <div class="flex align-center gap-1 h-full" @contextmenu="setMenuContext('table', table)">
<MdiDrag class="transition-opacity opacity-0 group-hover:opacity-100 text-gray-500 nc-drag-icon cursor-move" /> <MdiDrag class="transition-opacity opacity-0 group-hover:opacity-100 text-gray-500 nc-drag-icon cursor-move" />
<component :is="icon(table)" class="text-xs text-gray-500" /> <component :is="icon(table)" class="text-[10px] text-gray-500" />
<span class="text-xs flex-1">{{ table.title }}</span> <span class="text-xs flex-1 ml-2">{{ table.title }}</span>
<a-dropdown :trigger="['click']"> <a-dropdown :trigger="['click']" @click.stop>
<MdiMenuIcon class="transition-opacity opacity-0 group-hover:opacity-100" /> <MdiMenuIcon class="transition-opacity opacity-0 group-hover:opacity-100" />
<template #overlay> <template #overlay>
<a-menu> <a-menu class="cursor-pointer">
<a-menu-item class="!text-xs" @click="showRenameTableDlg(table)"> Rename</a-menu-item> <a-menu-item class="!text-xs"
v-t="['c:table:rename:navdraw:options']" @click="showRenameTableDlg(table)"> Rename</a-menu-item>
<a-menu-item class="!text-xs" @click="deleteTable(table)"> Delete</a-menu-item> <a-menu-item class="!text-xs" @click="deleteTable(table)"> Delete</a-menu-item>
</a-menu> </a-menu>
</template> </template>
@ -231,13 +236,14 @@ const showRenameTableDlg = (table: TableType) => {
</div> </div>
<template #overlay> <template #overlay>
<a-menu> <a-menu class="cursor-pointer">
<template v-if="contextMenuTarget.type === 'table'"> <template v-if="contextMenuTarget.type === 'table'">
<a-menu-item class="!text-xs" @click="showRenameTableDlg(contextMenuTarget.value)">Table Rename</a-menu-item> <a-menu-item class="!text-xs"
v-t="['c:table:rename:navdraw:right-click']" @click="showRenameTableDlg(contextMenuTarget.value)">Table Rename</a-menu-item>
<a-menu-item class="!text-xs" @click="deleteTable(contextMenuTarget.value)">Table Delete</a-menu-item> <a-menu-item class="!text-xs" @click="deleteTable(contextMenuTarget.value)">Table Delete</a-menu-item>
</template> </template>
<template v-else> <template v-else>
<a-menu-item class="!text-xs" @click="loadTables">Tables Refresh</a-menu-item> <a-menu-item class="!text-xs" @click="loadTables" v-t="['a:table:refresh:navdraw']">Tables Refresh</a-menu-item>
</template> </template>
</a-menu> </a-menu>
</template> </template>
@ -259,7 +265,7 @@ const showRenameTableDlg = (table: TableType) => {
<a-modal v-model:visible="settingsDlg" width="max(90vw, 600px)"> Team and settings</a-modal> <a-modal v-model:visible="settingsDlg" width="max(90vw, 600px)"> Team and settings</a-modal>
<DlgTableCreate v-model="tableCreateDlg" /> <DlgTableCreate v-model="tableCreateDlg" />
<DlgTableRename v-model="renameTableDlg" :table-meta="renameTableMeta" /> <DlgTableRename v-if="renameTableMeta" v-model="renameTableDlg" :table-meta="renameTableMeta" />
</div> </div>
</template> </template>

117
packages/nc-gui-v2/components/dlg/TableRename.vue

@ -1,92 +1,94 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, watchEffect } from '@vue/runtime-core' import { onMounted, watchEffect } from "@vue/runtime-core";
import { Form } from 'ant-design-vue' import { Form } from "ant-design-vue";
import type { TableType } from 'nocodb-sdk' import type { TableType } from "nocodb-sdk";
import { useToast } from 'vue-toastification' import { useToast } from "vue-toastification";
import { useProject, useTableCreate, useTabs } from '#imports' import { useProject, useTableCreate, useTabs } from "#imports";
import { extractSdkResponseErrorMsg } from '~/utils/errorUtils' import { extractSdkResponseErrorMsg } from "~/utils/errorUtils";
import { validateTableName } from '~/utils/validation' import { validateTableName } from "~/utils/validation";
import { useNuxtApp } from '#app' import { useNuxtApp } from "#app";
interface Props { interface Props {
modelValue?: boolean modelValue?: boolean;
tableMeta: TableType tableMeta: TableType;
} }
const { modelValue = false, tableMeta } = defineProps<Props>() const { modelValue = false, tableMeta } = defineProps<Props>();
const emit = defineEmits(['update:modelValue', 'updated']) const emit = defineEmits(["update:modelValue", "updated"]);
const { $e, $api } = useNuxtApp() const { $e, $api } = useNuxtApp();
const toast = useToast() const toast = useToast();
const dialogShow = computed({ const dialogShow = computed({
get() { get() {
return modelValue return modelValue;
}, },
set(v) { set(v) {
emit('update:modelValue', v) emit("update:modelValue", v);
}, }
}) });
const { addTab } = useTabs() const { addTab } = useTabs();
const { loadTables } = useProject() const { loadTables } = useProject();
const { project, tables } = useProject() const { project, tables } = useProject();
const prefix = computed(() => project?.value?.prefix || '') const prefix = computed(() => project?.value?.prefix || "");
const validateDuplicateAlias = (v: string) => { const validateDuplicateAlias = (v: string) => {
return (tables?.value || []).every((t) => t.title !== (v || '')) || 'Duplicate table alias' return (tables?.value || []).every((t) => t.title !== (v || "")) || "Duplicate table alias";
} };
const validateLeadingOrTrailingWhiteSpace = (v: string) => { const validateLeadingOrTrailingWhiteSpace = (v: string) => {
return !/^\s+|\s+$/.test(v) || 'Leading or trailing whitespace not allowed in table name' return !/^\s+|\s+$/.test(v) || "Leading or trailing whitespace not allowed in table name";
} };
const validateDuplicate = (v: string) => { const validateDuplicate = (v: string) => {
return (tables?.value || []).every((t) => t.table_name.toLowerCase() !== (v || '').toLowerCase()) || 'Duplicate table name' return (tables?.value || []).every((t) => t.table_name.toLowerCase() !== (v || "").toLowerCase()) || "Duplicate table name";
} };
const inputEl = ref<any>() const inputEl = $ref<any>();
const useForm = Form.useForm const useForm = Form.useForm;
const formState = reactive({ const formState = reactive({
title: '', title: ""
}) });
const validators = computed(() => { const validators = computed(() => {
return { return {
title: [validateTableName, validateDuplicateAlias], title: [validateTableName, validateDuplicateAlias],
table_name: [validateTableName], table_name: [validateTableName]
} };
}) });
const { resetFields, validate, validateInfos } = useForm(formState, validators) const { resetFields, validate, validateInfos } = useForm(formState, validators);
onMounted(() => {
// todo: focus input
})
watch(
() => tableMeta?.title, watchEffect(() => {
(title) => { if (tableMeta?.title) formState.title = tableMeta?.title;
if (title) formState.title = title // todo: replace setTimeout and follow better approach
}, nextTick(() => {
) const input = inputEl?.$el;
input.setSelectionRange(0, formState.title.length);
input.focus();
});
});
const renameTable = async () => { const renameTable = async () => {
try { try {
await $api.dbTable.update(tableMeta?.id as string, { await $api.dbTable.update(tableMeta?.id as string, {
title: formState.title, title: formState.title
}) });
loadTables() loadTables();
toast.success('Table renamed successfully') toast.success("Table renamed successfully");
$e('a:table:rename') $e("a:table:rename");
} catch (e) { } catch (e) {
toast.error(await extractSdkResponseErrorMsg(e)) toast.error(await extractSdkResponseErrorMsg(e));
} }
} };
</script> </script>
<template> <template>
<a-modal v-model:visible="dialogShow" @keydown.esc="dialogShow = false" @finish="renameTable"> <a-modal v-model:visible="dialogShow" @keydown.esc="dialogShow = false" @finish="renameTable">
<template #footer> <template #footer>
<a-button key="back" @click="dialogShow = false">{{ $t('general.cancel') }}</a-button> <a-button key="back" @click="dialogShow = false">{{ $t("general.cancel") }}</a-button>
<a-button key="submit" type="primary" @click="renameTable">{{ $t('general.submit') }}</a-button> <a-button key="submit" type="primary" @click="renameTable">{{ $t("general.submit") }}</a-button>
</template> </template>
<div class="pl-10 pr-10 pt-5"> <div class="pl-10 pr-10 pt-5">
<a-form :model="formState" name="create-new-table-form"> <a-form :model="formState" name="create-new-table-form">
<!-- Create A New Table --> <!-- Rename Table -->
<div class="prose-xl font-bold text-center my-4">Rename Table</div> <div class="prose-xl font-bold text-center my-4">Rename Table</div>
<!-- hint="Enter table name" --> <!-- hint="Enter table name" -->
<div class="mb-2">Table Name</div> <div class="mb-2">Table Name</div>
@ -97,8 +99,9 @@ const renameTable = async () => {
size="large" size="large"
hide-details hide-details
:placeholder="$t('msg.info.enterTableName')" :placeholder="$t('msg.info.enterTableName')"
/> </a-form-item />
></a-form> </a-form-item>
</a-form>
</div> </div>
</a-modal> </a-modal>
</template> </template>

3
packages/nc-gui-v2/pages/nc/[projectId].vue

@ -2,6 +2,7 @@
const route = useRoute() const route = useRoute()
const { loadProject, loadTables } = useProject(route.params.projectId as string) const { loadProject, loadTables } = useProject(route.params.projectId as string)
const { clearTabs, addTab } = useTabs() const { clearTabs, addTab } = useTabs()
const {$state} = useNuxtApp()
addTab({ type: 'auth', title: 'Team & Auth' }) addTab({ type: 'auth', title: 'Team & Auth' })
@ -17,6 +18,8 @@ watch(
} }
}, },
) )
$state.sidebarOpen.value = true
</script> </script>
<template> <template>

Loading…
Cancel
Save