diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml
index e663273e91..5b9eb669a2 100644
--- a/.github/workflows/ci-cd.yml
+++ b/.github/workflows/ci-cd.yml
@@ -11,6 +11,7 @@ on:
- "packages/nocodb/**"
- ".github/workflows/ci-cd.yml"
pull_request:
+ types: [ready_for_review]
branches: [develop]
paths:
- "packages/nc-gui/**"
diff --git a/.github/workflows/release-draft.yml b/.github/workflows/release-draft.yml
index dcdfbbbdc3..dbcb3c9744 100644
--- a/.github/workflows/release-draft.yml
+++ b/.github/workflows/release-draft.yml
@@ -49,9 +49,9 @@ jobs:
# the SHA from the third commit (i.e. Auto PR from master to develop) will be taken
# else HEAD will be taken
run: |
- TARGET_SHA=$(git rev-parse HEAD~2)
+ TARGET_SHA=$(git rev-list -n 3 HEAD | tail -1)
if [[ ${{ github.event.inputs.tagHeadSHA || inputs.tagHeadSHA }} == "Y" ]]; then
- TARGET_SHA=$(git rev-parse HEAD)
+ TARGET_SHA=$(git rev-list -n 1 HEAD | tail -1)
fi
echo "::set-output name=TARGET_SHA::${TARGET_SHA}"
echo "Setting TARGET_SHA: ${TARGET_SHA}"
diff --git a/packages/nc-gui/assets/style.scss b/packages/nc-gui/assets/style.scss
index 78353c8cc8..1a899a48b1 100644
--- a/packages/nc-gui/assets/style.scss
+++ b/packages/nc-gui/assets/style.scss
@@ -1,4 +1,6 @@
@import 'ant-design-vue/dist/antd.variable.min.css';
+@import '@braks/vue-flow/dist/style.css';
+@import '@braks/vue-flow/dist/theme-default.css';
:root {
--header-height: 42px;
@@ -248,3 +250,8 @@ a {
.ant-dropdown-menu-submenu-title{
@apply !pr-2;
}
+
+.vue-flow__minimap {
+ transform: scale(75%);
+ transform-origin: bottom right;
+}
diff --git a/packages/nc-gui/components.d.ts b/packages/nc-gui/components.d.ts
index dbc76afd2d..7e914eb710 100644
--- a/packages/nc-gui/components.d.ts
+++ b/packages/nc-gui/components.d.ts
@@ -143,6 +143,7 @@ declare module '@vue/runtime-core' {
MdiEmailArrowRightOutline: typeof import('~icons/mdi/email-arrow-right-outline')['default']
MdiExitToApp: typeof import('~icons/mdi/exit-to-app')['default']
MdiExport: typeof import('~icons/mdi/export')['default']
+ MdiEyeCircleOutline: typeof import('~icons/mdi/eye-circle-outline')['default']
MdiEyeOffOutline: typeof import('~icons/mdi/eye-off-outline')['default']
MdiFileDocumentOutline: typeof import('~icons/mdi/file-document-outline')['default']
MdiFileExcel: typeof import('~icons/mdi/file-excel')['default']
@@ -175,6 +176,7 @@ declare module '@vue/runtime-core' {
MdiMoonFull: typeof import('~icons/mdi/moon-full')['default']
MdiNumeric: typeof import('~icons/mdi/numeric')['default']
MdiOpenInNew: typeof import('~icons/mdi/open-in-new')['default']
+ MdiOpenInNewIcon: typeof import('~icons/mdi/open-in-new-icon')['default']
MdiPencil: typeof import('~icons/mdi/pencil')['default']
MdiPlus: typeof import('~icons/mdi/plus')['default']
MdiPlusCircleOutline: typeof import('~icons/mdi/plus-circle-outline')['default']
@@ -190,6 +192,7 @@ declare module '@vue/runtime-core' {
MdiStarOutline: typeof import('~icons/mdi/star-outline')['default']
MdiTable: typeof import('~icons/mdi/table')['default']
MdiTableArrowRight: typeof import('~icons/mdi/table-arrow-right')['default']
+ MdiTableLarge: typeof import('~icons/mdi/table-large')['default']
MdiText: typeof import('~icons/mdi/text')['default']
MdiThumbUp: typeof import('~icons/mdi/thumb-up')['default']
MdiTrashCan: typeof import('~icons/mdi/trash-can')['default']
diff --git a/packages/nc-gui/components/dashboard/settings/Erd.vue b/packages/nc-gui/components/dashboard/settings/Erd.vue
new file mode 100644
index 0000000000..4ccfe17aeb
--- /dev/null
+++ b/packages/nc-gui/components/dashboard/settings/Erd.vue
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/packages/nc-gui/components/dashboard/settings/Misc.vue b/packages/nc-gui/components/dashboard/settings/Misc.vue
index a9ce1f6d2e..fc98644a58 100644
--- a/packages/nc-gui/components/dashboard/settings/Misc.vue
+++ b/packages/nc-gui/components/dashboard/settings/Misc.vue
@@ -10,7 +10,7 @@ watch(includeM2M, async () => await loadTables())
-
{{
+ {{
$t('msg.info.showM2mTables')
}}
diff --git a/packages/nc-gui/components/dashboard/settings/Modal.vue b/packages/nc-gui/components/dashboard/settings/Modal.vue
index e9f7546f3f..52dba591fc 100644
--- a/packages/nc-gui/components/dashboard/settings/Modal.vue
+++ b/packages/nc-gui/components/dashboard/settings/Modal.vue
@@ -5,6 +5,7 @@ import AppStore from './AppStore.vue'
import Metadata from './Metadata.vue'
import UIAcl from './UIAcl.vue'
import Misc from './Misc.vue'
+import Erd from './Erd.vue'
import { useNuxtApp } from '#app'
import { useI18n, useUIPermission, useVModel, watch } from '#imports'
import ApiTokenManagement from '~/components/tabs/auth/ApiTokenManagement.vue'
@@ -90,7 +91,7 @@ const tabsInfo: TabGroup = {
$e('c:settings:appstore')
},
},
- metaData: {
+ projMetaData: {
// Project Metadata
title: t('title.projMeta'),
icon: MultipleTableIcon,
@@ -108,6 +109,13 @@ const tabsInfo: TabGroup = {
$e('c:table:ui-acl')
},
},
+ erd: {
+ title: t('title.erdView'),
+ body: Erd,
+ onClick: () => {
+ $e('c:settings:erd')
+ },
+ },
misc: {
title: t('general.misc'),
body: Misc,
diff --git a/packages/nc-gui/components/dlg/AirtableImport.vue b/packages/nc-gui/components/dlg/AirtableImport.vue
index 0aeca558e4..f5278df1cf 100644
--- a/packages/nc-gui/components/dlg/AirtableImport.vue
+++ b/packages/nc-gui/components/dlg/AirtableImport.vue
@@ -65,8 +65,8 @@ const syncSource = ref({
})
const validators = computed(() => ({
- 'details.apiKey': [fieldRequiredValidator],
- 'details.syncSourceUrlOrId': [fieldRequiredValidator],
+ 'details.apiKey': [fieldRequiredValidator()],
+ 'details.syncSourceUrlOrId': [fieldRequiredValidator()],
}))
const dialogShow = computed({
diff --git a/packages/nc-gui/components/dlg/QuickImport.vue b/packages/nc-gui/components/dlg/QuickImport.vue
index 4d98ffbff2..d7e2567063 100644
--- a/packages/nc-gui/components/dlg/QuickImport.vue
+++ b/packages/nc-gui/components/dlg/QuickImport.vue
@@ -70,8 +70,8 @@ const isImportTypeCsv = computed(() => importType === 'csv')
const IsImportTypeExcel = computed(() => importType === 'excel')
const validators = computed(() => ({
- url: [fieldRequiredValidator, importUrlValidator, isImportTypeCsv.value ? importCsvUrlValidator : importExcelUrlValidator],
- maxRowsToParse: [fieldRequiredValidator],
+ url: [fieldRequiredValidator(), importUrlValidator, isImportTypeCsv.value ? importCsvUrlValidator : importExcelUrlValidator],
+ maxRowsToParse: [fieldRequiredValidator()],
}))
const { validate, validateInfos } = useForm(importState, validators)
diff --git a/packages/nc-gui/components/erd/Flow.vue b/packages/nc-gui/components/erd/Flow.vue
new file mode 100644
index 0000000000..3568a486ae
--- /dev/null
+++ b/packages/nc-gui/components/erd/Flow.vue
@@ -0,0 +1,227 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ $t('objects.table') }}
+
+
+
+
{{ $t('objects.sqlVIew') }}
+
+
+
+
diff --git a/packages/nc-gui/components/erd/RelationEdge.vue b/packages/nc-gui/components/erd/RelationEdge.vue
new file mode 100644
index 0000000000..fec3122b9d
--- /dev/null
+++ b/packages/nc-gui/components/erd/RelationEdge.vue
@@ -0,0 +1,161 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/nc-gui/components/erd/TableNode.vue b/packages/nc-gui/components/erd/TableNode.vue
new file mode 100644
index 0000000000..0c753e9856
--- /dev/null
+++ b/packages/nc-gui/components/erd/TableNode.vue
@@ -0,0 +1,121 @@
+
+
+
+
+
+ {{ data.table_name }}
+
+
+
+
+ {{ data.title }}
+
+
+
+
+
+
+
+
diff --git a/packages/nc-gui/components/erd/View.vue b/packages/nc-gui/components/erd/View.vue
new file mode 100644
index 0000000000..d480963e83
--- /dev/null
+++ b/packages/nc-gui/components/erd/View.vue
@@ -0,0 +1,162 @@
+
+
+
+
+
diff --git a/packages/nc-gui/components/smartsheet-header/VirtualCell.vue b/packages/nc-gui/components/smartsheet-header/VirtualCell.vue
index 8d4d458831..eeb5eea8eb 100644
--- a/packages/nc-gui/components/smartsheet-header/VirtualCell.vue
+++ b/packages/nc-gui/components/smartsheet-header/VirtualCell.vue
@@ -16,7 +16,7 @@ import {
useVirtualCell,
} from '#imports'
-const props = defineProps<{ column: ColumnType & { meta: any }; hideMenu?: boolean; required?: boolean | number }>()
+const props = defineProps<{ column: ColumnType; hideMenu?: boolean; required?: boolean | number }>()
const { t } = useI18n()
diff --git a/packages/nc-gui/components/smartsheet-toolbar/Erd.vue b/packages/nc-gui/components/smartsheet-toolbar/Erd.vue
new file mode 100644
index 0000000000..c1a131fdea
--- /dev/null
+++ b/packages/nc-gui/components/smartsheet-toolbar/Erd.vue
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+ {{ `${$t('title.erdView')}: ${selectedView?.title}` }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/nc-gui/components/smartsheet-toolbar/FieldsMenu.vue b/packages/nc-gui/components/smartsheet-toolbar/FieldsMenu.vue
index 537a830a78..3c8bafe524 100644
--- a/packages/nc-gui/components/smartsheet-toolbar/FieldsMenu.vue
+++ b/packages/nc-gui/components/smartsheet-toolbar/FieldsMenu.vue
@@ -26,6 +26,8 @@ const activeView = inject(ActiveViewInj, ref())
const reloadDataHook = inject(ReloadViewDataHookInj)!
+const reloadViewMetaHook = inject(ReloadViewMetaHookInj)!
+
const rootFields = inject(FieldsInj)
const isLocked = inject(IsLockedInj, ref(false))
@@ -93,7 +95,7 @@ const coverImageColumnId = computed({
fk_cover_image_col_id: val,
})
;(activeView.value?.view as GalleryType).fk_cover_image_col_id = val
- reloadDataHook.trigger()
+ reloadViewMetaHook.trigger()
}
},
})
diff --git a/packages/nc-gui/components/smartsheet-toolbar/ViewActions.vue b/packages/nc-gui/components/smartsheet-toolbar/ViewActions.vue
index f095ffa5d5..f789ffae0f 100644
--- a/packages/nc-gui/components/smartsheet-toolbar/ViewActions.vue
+++ b/packages/nc-gui/components/smartsheet-toolbar/ViewActions.vue
@@ -18,6 +18,7 @@ import { LockType } from '~/lib'
import MdiLockOutlineIcon from '~icons/mdi/lock-outline'
import MdiAccountIcon from '~icons/mdi/account'
import MdiAccountGroupIcon from '~icons/mdi/account-group'
+import AcountTreeRoundedIcon from '~icons/material-symbols/account-tree-rounded'
const { t } = useI18n()
@@ -37,6 +38,8 @@ const showWebhookDrawer = ref(false)
const showApiSnippetDrawer = ref(false)
+const showErd = ref(false)
+
const quickImportDialog = ref(false)
const { isUIAllowed } = useUIPermission()
@@ -160,9 +163,8 @@ const { isSqlView } = useSmartsheetStoreOrThrow()
-
+