Browse Source

Merge pull request #3280 from nocodb/refactor/create-pages

pull/3298/head
Braks 2 years ago committed by GitHub
parent
commit
7249faad2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      packages/nc-gui-v2/assets/style.scss
  2. 9
      packages/nc-gui-v2/components/dashboard/GithubStarButton.vue
  3. 17
      packages/nc-gui-v2/components/dashboard/TreeView.vue
  4. 8
      packages/nc-gui-v2/components/general/ShareBaseButton.vue
  5. 57
      packages/nc-gui-v2/components/smartsheet/sidebar/MenuBottom.vue
  6. 53
      packages/nc-gui-v2/components/smartsheet/sidebar/MenuTop.vue
  7. 4
      packages/nc-gui-v2/components/smartsheet/sidebar/RenameableMenuItem.vue
  8. 4
      packages/nc-gui-v2/components/smartsheet/sidebar/index.vue
  9. 4
      packages/nc-gui-v2/github-star.shims.d.ts
  10. 4
      packages/nc-gui-v2/layouts/base.vue
  11. 403
      packages/nc-gui-v2/package-lock.json
  12. 2
      packages/nc-gui-v2/package.json
  13. 7
      packages/nc-gui-v2/pages/[projectType]/[projectId]/index.vue
  14. 234
      packages/nc-gui-v2/pages/index/index.vue
  15. 140
      packages/nc-gui-v2/pages/index/index/[id].vue
  16. 134
      packages/nc-gui-v2/pages/index/index/create-external.vue
  17. 131
      packages/nc-gui-v2/pages/index/index/create.vue
  18. 211
      packages/nc-gui-v2/pages/index/index/index.vue
  19. 17
      packages/nc-gui-v2/pages/index/user/index/index.vue
  20. 5
      packages/nc-gui-v2/pages/project/index.vue
  21. 83
      packages/nc-gui-v2/pages/project/index/[id].vue
  22. 78
      packages/nc-gui-v2/pages/project/index/create.vue
  23. 2
      packages/nc-gui-v2/pages/signin.vue

4
packages/nc-gui-v2/assets/style.scss

@ -210,6 +210,10 @@ a {
}
}
.ant-dropdown-menu {
@apply !p-0 !rounded;
}
.ant-dropdown-menu-submenu-popup {
@apply scrollbar-thin-dull min-w-50 max-h-90vh overflow-auto !shadow !rounded;
}

9
packages/nc-gui-v2/components/dashboard/GithubStarButton.vue

@ -1,9 +0,0 @@
<script setup lang="ts">
import GithubButton from 'vue-github-button'
</script>
<template>
<GithubButton href="https://github.com/nocodb/nocodb" data-icon="octicon-star" data-show-count="true" data-size="large"
>Star</GithubButton
>
</template>

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

@ -2,6 +2,7 @@
import type { TableType } from 'nocodb-sdk'
import Sortable from 'sortablejs'
import { Empty } from 'ant-design-vue'
import GithubButton from 'vue-github-button'
import {
computed,
inject,
@ -402,11 +403,21 @@ function openTableCreateDialog() {
<a-divider class="!my-0" />
<div class="flex items-start flex-col justify-start px-2 py-3 gap-2">
<GeneralShareBaseButton class="py-1.5 px-2 text-primary font-bold cursor-pointer select-none" />
<GeneralShareBaseButton
class="color-transition py-1.5 px-2 text-primary font-bold cursor-pointer select-none hover:text-accent"
/>
<GeneralHelpAndSupport class="px-2 text-gray-500 cursor-pointer select-none" />
<GeneralHelpAndSupport class="color-transition px-2 text-gray-500 cursor-pointer select-none hover:text-accent" />
<DashboardGithubStarButton class="ml-2 py-1" />
<GithubButton
class="ml-2 py-1"
href="https://github.com/nocodb/nocodb"
data-icon="octicon-star"
data-show-count="true"
data-size="large"
>
Star
</GithubButton>
</div>
</div>
</template>

8
packages/nc-gui-v2/components/general/ShareBaseButton.vue

@ -1,5 +1,5 @@
<script setup lang="ts">
import { useRoute } from '#imports'
import { useRoute, useUIPermission } from '#imports'
const route = useRoute()
@ -14,8 +14,8 @@ const { isUIAllowed } = useUIPermission()
v-if="
isUIAllowed('newUser') &&
route.name !== 'index' &&
route.name !== 'project-index-create' &&
route.name !== 'project-index-create-external' &&
route.name !== 'index-index-create' &&
route.name !== 'index-index-create-external' &&
route.name !== 'index-user-index'
"
@click="showUserModal = true"
@ -23,9 +23,11 @@ const { isUIAllowed } = useUIPermission()
<div class="flex items-center space-x-1">
<MdiAccountPlusOutline class="mr-1 nc-share-base" />
<!-- todo: i18n <div>{{ $t('activity.share') }}</div> -->
<div>{{ $t('activity.inviteTeam') }}</div>
</div>
</div>
<TabsAuthUserManagementUsersModal :key="showUserModal" :show="showUserModal" @closed="showUserModal = false" />
</div>
</template>

57
packages/nc-gui-v2/components/smartsheet/sidebar/MenuBottom.vue

@ -1,7 +1,6 @@
<script lang="ts" setup>
import { ViewTypes } from 'nocodb-sdk'
import { ref } from '#imports'
import { viewIcons } from '~/utils'
import { ref, useUIPermission, viewIcons } from '#imports'
interface Emits {
(event: 'openModal', data: { type: ViewTypes; title?: string }): void
@ -13,8 +12,6 @@ const { isUIAllowed } = useUIPermission()
const isView = ref(false)
const showWebhookDrawer = ref(false)
function onOpenModal(type: ViewTypes, title = '') {
emits('openModal', { type, title })
}
@ -22,7 +19,6 @@ function onOpenModal(type: ViewTypes, title = '') {
<template>
<a-menu :selected-keys="[]" class="flex flex-col">
<div class="flex-1"></div>
<div v-if="isUIAllowed('virtualViewsCreateOrEdit')">
<h3 class="px-3 py-1 text-xs font-semibold flex items-center gap-4 text-gray-500">
{{ $t('activity.createView') }}
@ -38,7 +34,7 @@ function onOpenModal(type: ViewTypes, title = '') {
{{ $t('msg.info.addView.grid') }}
</template>
<div class="text-xs flex items-center h-full w-full gap-2">
<div class="nc-project-menu-item text-xs flex items-center h-full w-full gap-2">
<component :is="viewIcons[ViewTypes.GRID].icon" :style="{ color: viewIcons[ViewTypes.GRID].color }" />
<div>{{ $t('objects.viewType.grid') }}</div>
@ -50,17 +46,13 @@ function onOpenModal(type: ViewTypes, title = '') {
</a-tooltip>
</a-menu-item>
<a-menu-item
key="gallery"
class="group !flex !items-center !-my0 !h-[30px] nc-create-2-view"
@click="onOpenModal(ViewTypes.GALLERY)"
>
<a-menu-item key="gallery" class="group !flex !items-center !my-0 nc-create-2-view" @click="onOpenModal(ViewTypes.GALLERY)">
<a-tooltip :mouse-enter-delay="1" placement="left">
<template #title>
{{ $t('msg.info.addView.gallery') }}
</template>
<div class="text-xs flex items-center h-full w-full gap-2">
<div class="nc-project-menu-item text-xs flex items-center h-full w-full gap-2">
<component :is="viewIcons[ViewTypes.GALLERY].icon" :style="{ color: viewIcons[ViewTypes.GALLERY].color }" />
<div>{{ $t('objects.viewType.gallery') }}</div>
@ -83,7 +75,7 @@ function onOpenModal(type: ViewTypes, title = '') {
{{ $t('msg.info.addView.form') }}
</template>
<div class="text-xs flex items-center h-full w-full gap-2">
<div class="nc-project-menu-item text-xs flex items-center h-full w-full gap-2">
<component :is="viewIcons[ViewTypes.FORM].icon" :style="{ color: viewIcons[ViewTypes.FORM].color }" />
<div>{{ $t('objects.viewType.form') }}</div>
@ -95,44 +87,7 @@ function onOpenModal(type: ViewTypes, title = '') {
</a-tooltip>
</a-menu-item>
<div class="w-full h-4"></div>
</div>
<!--
todo: bring back later
<general-flipping-card class="my-4 lg:my-6 min-h-[100px]" :triggers="['click', { duration: 15000 }]">
<template #front>
<div class="flex h-full w-full gap-6 flex-col">
<general-social />
<div>
<a
v-t="['e:hiring']"
class="px-4 py-3 rounded border text-xs text-current"
href="https://angel.co/company/nocodb"
target="_blank"
@click.stop
>
🚀 We are Hiring! 🚀
</a>
<div class="w-full h-4" />
</div>
</div>
</template>
<template #back>
&lt;!&ndash; todo: add project cost &ndash;&gt;
<a
href="https://github.com/sponsors/nocodb"
target="_blank"
class="group flex items-center gap-2 w-full mx-3 px-4 py-3 rounded-l border transform translate-x-4 hover:(translate-x-0 shadow-lg !opacity-100) transition duration-150 ease !text-xs text-current"
@click.stop
>
<mdi-cards-heart class="text-red-500" />
{{ $t('activity.sponsorUs') }}
</a>
</template>
</general-flipping-card> -->
<WebhookDrawer v-if="showWebhookDrawer" v-model="showWebhookDrawer" />
</a-menu>
</template>

53
packages/nc-gui-v2/components/smartsheet/sidebar/MenuTop.vue

@ -7,9 +7,20 @@ import { message } from 'ant-design-vue'
import type { Ref } from 'vue'
import Sortable from 'sortablejs'
import RenameableMenuItem from './RenameableMenuItem.vue'
import { inject, onMounted, ref, useApi, useRoute, useRouter, watch } from '#imports'
import { extractSdkResponseErrorMsg } from '~/utils'
import { ActiveViewInj, ViewListInj } from '~/context'
import {
ActiveViewInj,
ViewListInj,
extractSdkResponseErrorMsg,
inject,
onMounted,
ref,
useApi,
useDialog,
useRoute,
useRouter,
watch,
} from '#imports'
import DlgViewDelete from '~/components/dlg/ViewDelete.vue'
interface Emits {
(event: 'openModal', data: { type: ViewTypes; title?: string; copyViewId?: string }): void
@ -41,11 +52,6 @@ const selected = ref<string[]>([])
/** dragging renamable view items */
let dragging = $ref(false)
let deleteModalVisible = $ref(false)
/** view to delete for modal */
let toDelete = $ref<Record<string, any> | undefined>()
const menuRef = $ref<typeof AntMenu>()
let isMarked = $ref<string | false>(false)
@ -165,18 +171,27 @@ async function onRename(view: ViewType) {
}
/** Open delete modal */
async function onDelete(view: Record<string, any>) {
toDelete = view
deleteModalVisible = true
}
function openDeleteDialog(view: Record<string, any>) {
const isOpen = ref(true)
const { close } = useDialog(DlgViewDelete, {
'modelValue': isOpen,
'view': view,
'onUpdate:modelValue': closeDialog,
'onDeleted': () => {
closeDialog()
/** View was deleted, trigger reload */
function onDeleted() {
emits('deleted')
toDelete = undefined
deleteModalVisible = false
// return to the default view
activeView.value = views.value[0]
},
})
function closeDialog() {
isOpen.value = false
close(1000)
}
}
</script>
@ -197,12 +212,10 @@ function onDeleted() {
}"
@change-view="changeView"
@open-modal="$emit('openModal', $event)"
@delete="onDelete"
@delete="openDeleteDialog(view)"
@rename="onRename"
/>
</a-menu>
<dlg-view-delete v-model="deleteModalVisible" :view="toDelete" @deleted="onDeleted" />
</template>
<style lang="scss">
@ -225,7 +238,7 @@ function onDeleted() {
}
.ant-menu-item:not(.sortable-chosen) {
@apply color-transition hover:!bg-transparent;
@apply color-transition;
}
.sortable-chosen {

4
packages/nc-gui-v2/components/smartsheet/sidebar/RenameableMenuItem.vue

@ -146,14 +146,14 @@ function onStopEdit() {
<template>
<a-menu-item
class="select-none group !flex !items-center !my-0"
class="select-none group !flex !items-center !my-0 hover:(bg-primary !bg-opacity-5)"
@dblclick.stop="isUIAllowed('virtualViewsCreateOrEdit') && onDblClick()"
@click.stop="onClick"
>
<div v-t="['a:view:open', { view: vModel.type }]" class="text-xs flex items-center w-full gap-2">
<div class="flex w-auto">
<MdiDrag
class="nc-drag-icon hidden group-hover:block transition-opacity opacity-0 group-hover:opacity-100 text-gray-500 cursor-move"
class="nc-drag-icon hidden group-hover:block transition-opacity opacity-0 group-hover:opacity-100 text-gray-500 !cursor-move"
@click.stop.prevent
/>

4
packages/nc-gui-v2/components/smartsheet/sidebar/index.vue

@ -107,9 +107,7 @@ function onCreate(view: ViewType) {
<div v-if="isOpen" class="flex-1 flex flex-col min-h-0">
<MenuTop @open-modal="openModal" @deleted="loadViews" @sorted="loadViews" />
<div v-if="isUIAllowed('virtualViewsCreateOrEdit')" class="px-3">
<div class="!my-3 w-full border-b-1 border-dashed" />
</div>
<div v-if="isUIAllowed('virtualViewsCreateOrEdit')" class="!my-3 w-full border-b-1" />
<MenuBottom @open-modal="openModal" />
</div>

4
packages/nc-gui-v2/github-star.shims.d.ts vendored

@ -0,0 +1,4 @@
declare module 'vue-github-button' {
import type { Component } from '@vue/runtime-core'
export default Component
}

4
packages/nc-gui-v2/layouts/base.vue

@ -40,7 +40,7 @@ hooks.hook('page:finish', () => {
class="flex !bg-primary items-center text-white pl-4 pr-5 shadow-lg"
>
<div
v-if="route.name === 'index' || route.name === 'project-index-create' || route.name === 'project-index-create-external'"
v-if="!route.params.projectType"
class="transition-all duration-200 p-2 cursor-pointer transform hover:scale-105 nc-noco-brand-icon"
@click="navigateTo('/')"
>
@ -59,7 +59,7 @@ hooks.hook('page:finish', () => {
<GeneralReleaseInfo />
<GeneralShareBaseButton v-if="!isSharedBase" />
<GeneralShareBaseButton v-if="!isSharedBase" class="pr-4 font-semibold" />
<a-tooltip placement="bottom" :mouse-enter-delay="1">
<template #title> Switch language</template>

403
packages/nc-gui-v2/package-lock.json generated

@ -14,6 +14,7 @@
"dayjs": "^1.11.3",
"file-saver": "^2.0.5",
"jsep": "^1.3.6",
"json-schema-traverse": "^1.0.0",
"just-clone": "^6.1.1",
"jwt-decode": "^3.1.2",
"locale-codes": "^1.3.1",
@ -56,6 +57,7 @@
"@windicss/plugin-animations": "^1.0.9",
"@windicss/plugin-question-mark": "^0.1.1",
"@windicss/plugin-scrollbar": "^1.2.3",
"eslint": "^8.22.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.0.0",
"happy-dom": "^6.0.3",
@ -906,7 +908,6 @@
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz",
"integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==",
"dev": true,
"peer": true,
"dependencies": {
"ajv": "^6.12.4",
"debug": "^4.3.2",
@ -927,7 +928,6 @@
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@ -944,7 +944,6 @@
"resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz",
"integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==",
"dev": true,
"peer": true,
"dependencies": {
"type-fest": "^0.20.2"
},
@ -959,15 +958,13 @@
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schem-traverse/-/json-schem-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/@eslint/eslintrc/node_modules/type-fest": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
"dev": true,
"peer": true,
"engines": {
"node": ">=10"
},
@ -976,11 +973,10 @@
}
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.9.5",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz",
"integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==",
"version": "0.10.4",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz",
"integrity": "sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==",
"dev": true,
"peer": true,
"dependencies": {
"@humanwhocodes/object-schema": "^1.2.1",
"debug": "^4.1.1",
@ -990,12 +986,21 @@
"node": ">=10.10.0"
}
},
"node_modules/@humanwhocodes/gitignore-to-minimatch": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz",
"integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==",
"dev": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/nzakas"
}
},
"node_modules/@humanwhocodes/object-schema": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schem-1.2.1.tgz",
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/@iconify-json/bi": {
"version": "1.1.6",
@ -3744,8 +3749,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/array-includes": {
"version": "3.1.5",
@ -4140,7 +4144,6 @@
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"dev": true,
"peer": true,
"engines": {
"node": ">=6"
}
@ -5265,8 +5268,7 @@
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/deepmerge": {
"version": "4.2.2",
@ -5392,7 +5394,6 @@
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
"integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
"dev": true,
"peer": true,
"dependencies": {
"esutils": "^2.0.2"
},
@ -6224,14 +6225,14 @@
}
},
"node_modules/eslint": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.18.0.tgz",
"integrity": "sha512-As1EfFMVk7Xc6/CvhssHUjsAQSkpfXvUGMFC3ce8JDe6WvqCgRrLOBQbVpsBFr1X1V+RACOadnzVvcUS5ni2bA==",
"version": "8.22.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.22.0.tgz",
"integrity": "sha512-ci4t0sz6vSRKdmkOGmprBo6fmI4PrphDFMy5JEq/fNS0gQkJM3rLmrqcp8ipMcdobH3KtUP40KniAE9W19S4wA==",
"dev": true,
"peer": true,
"dependencies": {
"@eslint/eslintrc": "^1.3.0",
"@humanwhocodes/config-array": "^0.9.2",
"@humanwhocodes/config-array": "^0.10.4",
"@humanwhocodes/gitignore-to-minimatch": "^1.0.2",
"ajv": "^6.10.0",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
@ -6241,14 +6242,17 @@
"eslint-scope": "^7.1.1",
"eslint-utils": "^3.0.0",
"eslint-visitor-keys": "^3.3.0",
"espree": "^9.3.2",
"espree": "^9.3.3",
"esquery": "^1.4.0",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
"file-entry-cache": "^6.0.1",
"find-up": "^5.0.0",
"functional-red-black-tree": "^1.0.1",
"glob-parent": "^6.0.1",
"globals": "^13.15.0",
"globby": "^11.1.0",
"grapheme-splitter": "^1.0.4",
"ignore": "^5.2.0",
"import-fresh": "^3.0.0",
"imurmurhash": "^0.1.4",
@ -6767,7 +6771,6 @@
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@ -6784,7 +6787,6 @@
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"peer": true,
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@ -6801,7 +6803,6 @@
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true,
"peer": true,
"engines": {
"node": ">=10"
},
@ -6814,7 +6815,6 @@
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
"integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
"dev": true,
"peer": true,
"dependencies": {
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
@ -6828,17 +6828,31 @@
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
"dev": true,
"peer": true,
"engines": {
"node": ">=4.0"
}
},
"node_modules/eslint/node_modules/find-up": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
"dev": true,
"dependencies": {
"locate-path": "^6.0.0",
"path-exists": "^4.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/eslint/node_modules/glob-parent": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
"dev": true,
"peer": true,
"dependencies": {
"is-glob": "^4.0.3"
},
@ -6851,7 +6865,6 @@
"resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz",
"integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==",
"dev": true,
"peer": true,
"dependencies": {
"type-fest": "^0.20.2"
},
@ -6862,19 +6875,100 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/eslint/node_modules/globby": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
"integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
"dev": true,
"dependencies": {
"array-union": "^2.1.0",
"dir-glob": "^3.0.1",
"fast-glob": "^3.2.9",
"ignore": "^5.2.0",
"merge2": "^1.4.1",
"slash": "^3.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/eslint/node_modules/json-schem-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schem-traverse/-/json-schem-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
},
"node_modules/eslint/node_modules/locate-path": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
"dev": true,
"peer": true
"dependencies": {
"p-locate": "^5.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/eslint/node_modules/p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
"dev": true,
"dependencies": {
"yocto-queue": "^0.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/eslint/node_modules/p-locate": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
"dev": true,
"dependencies": {
"p-limit": "^3.0.2"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/eslint/node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/eslint/node_modules/slash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/eslint/node_modules/type-fest": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
"dev": true,
"peer": true,
"engines": {
"node": ">=10"
},
@ -6883,17 +6977,20 @@
}
},
"node_modules/espree": {
"version": "9.3.2",
"resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz",
"integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==",
"version": "9.3.3",
"resolved": "https://registry.npmjs.org/espree/-/espree-9.3.3.tgz",
"integrity": "sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng==",
"dev": true,
"dependencies": {
"acorn": "^8.7.1",
"acorn": "^8.8.0",
"acorn-jsx": "^5.3.2",
"eslint-visitor-keys": "^3.3.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/esprima": {
@ -7122,8 +7219,7 @@
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/fastq": {
"version": "1.13.0",
@ -7186,7 +7282,6 @@
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
"integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
"dev": true,
"peer": true,
"dependencies": {
"flat-cache": "^3.0.4"
},
@ -7306,7 +7401,6 @@
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
"integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
"dev": true,
"peer": true,
"dependencies": {
"flatted": "^3.1.0",
"rimraf": "^3.0.2"
@ -7319,8 +7413,7 @@
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz",
"integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/follow-redirects": {
"version": "1.15.1",
@ -7763,6 +7856,12 @@
"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
"dev": true
},
"node_modules/grapheme-splitter": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
"integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
"dev": true
},
"node_modules/gzip-size": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-7.0.0.tgz",
@ -8407,7 +8506,6 @@
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
"dev": true,
"peer": true,
"dependencies": {
"parent-module": "^1.0.0",
"resolve-from": "^4.0.0"
@ -8424,7 +8522,6 @@
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
"dev": true,
"peer": true,
"dependencies": {
"callsites": "^3.0.0"
},
@ -8437,7 +8534,6 @@
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true,
"peer": true,
"engines": {
"node": ">=4"
}
@ -8447,7 +8543,6 @@
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
"dev": true,
"peer": true,
"engines": {
"node": ">=0.8.19"
}
@ -9072,7 +9167,6 @@
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dev": true,
"peer": true,
"dependencies": {
"argparse": "^2.0.1"
},
@ -9200,12 +9294,16 @@
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"dev": true
},
"node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
},
"node_modules/json-stable-stringify-without-jsonify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/json5": {
"version": "2.2.1",
@ -9435,7 +9533,6 @@
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
"integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
"dev": true,
"peer": true,
"dependencies": {
"prelude-ls": "^1.2.1",
"type-check": "~0.4.0"
@ -9599,8 +9696,7 @@
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/lodash.pick": {
"version": "4.4.0",
@ -10803,7 +10899,6 @@
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
"integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
"dev": true,
"peer": true,
"dependencies": {
"deep-is": "^0.1.3",
"fast-levenshtein": "^2.0.6",
@ -11762,7 +11857,6 @@
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
"dev": true,
"peer": true,
"engines": {
"node": ">= 0.8.0"
}
@ -13201,7 +13295,6 @@
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
"dev": true,
"peer": true,
"engines": {
"node": ">=8"
},
@ -13534,8 +13627,7 @@
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/then-request": {
"version": "6.0.2",
@ -13762,7 +13854,6 @@
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
"integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
"dev": true,
"peer": true,
"dependencies": {
"prelude-ls": "^1.2.1"
},
@ -14166,8 +14257,7 @@
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
"integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/validate-npm-package-license": {
"version": "3.0.4",
@ -14912,7 +15002,6 @@
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
"dev": true,
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@ -15747,7 +15836,6 @@
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz",
"integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==",
"dev": true,
"peer": true,
"requires": {
"ajv": "^6.12.4",
"debug": "^4.3.2",
@ -15765,7 +15853,6 @@
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"peer": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@ -15778,7 +15865,6 @@
"resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz",
"integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==",
"dev": true,
"peer": true,
"requires": {
"type-fest": "^0.20.2"
}
@ -15787,36 +15873,38 @@
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schem-traverse/-/json-schem-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true,
"peer": true
"dev": true
},
"type-fest": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
"dev": true,
"peer": true
"dev": true
}
}
},
"@humanwhocodes/config-array": {
"version": "0.9.5",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz",
"integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==",
"version": "0.10.4",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz",
"integrity": "sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==",
"dev": true,
"peer": true,
"requires": {
"@humanwhocodes/object-schema": "^1.2.1",
"debug": "^4.1.1",
"minimatch": "^3.0.4"
}
},
"@humanwhocodes/gitignore-to-minimatch": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz",
"integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==",
"dev": true
},
"@humanwhocodes/object-schema": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schem-1.2.1.tgz",
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
"dev": true,
"peer": true
"dev": true
},
"@iconify-json/bi": {
"version": "1.1.6",
@ -17937,8 +18025,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true,
"peer": true
"dev": true
},
"array-includes": {
"version": "3.1.5",
@ -18218,8 +18305,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"dev": true,
"peer": true
"dev": true
},
"camelcase": {
"version": "6.3.0",
@ -19083,8 +19169,7 @@
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
"dev": true,
"peer": true
"dev": true
},
"deepmerge": {
"version": "4.2.2",
@ -19179,7 +19264,6 @@
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
"integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
"dev": true,
"peer": true,
"requires": {
"esutils": "^2.0.2"
}
@ -19710,14 +19794,14 @@
}
},
"eslint": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.18.0.tgz",
"integrity": "sha512-As1EfFMVk7Xc6/CvhssHUjsAQSkpfXvUGMFC3ce8JDe6WvqCgRrLOBQbVpsBFr1X1V+RACOadnzVvcUS5ni2bA==",
"version": "8.22.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.22.0.tgz",
"integrity": "sha512-ci4t0sz6vSRKdmkOGmprBo6fmI4PrphDFMy5JEq/fNS0gQkJM3rLmrqcp8ipMcdobH3KtUP40KniAE9W19S4wA==",
"dev": true,
"peer": true,
"requires": {
"@eslint/eslintrc": "^1.3.0",
"@humanwhocodes/config-array": "^0.9.2",
"@humanwhocodes/config-array": "^0.10.4",
"@humanwhocodes/gitignore-to-minimatch": "^1.0.2",
"ajv": "^6.10.0",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
@ -19727,14 +19811,17 @@
"eslint-scope": "^7.1.1",
"eslint-utils": "^3.0.0",
"eslint-visitor-keys": "^3.3.0",
"espree": "^9.3.2",
"espree": "^9.3.3",
"esquery": "^1.4.0",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
"file-entry-cache": "^6.0.1",
"find-up": "^5.0.0",
"functional-red-black-tree": "^1.0.1",
"glob-parent": "^6.0.1",
"globals": "^13.15.0",
"globby": "^11.1.0",
"grapheme-splitter": "^1.0.4",
"ignore": "^5.2.0",
"import-fresh": "^3.0.0",
"imurmurhash": "^0.1.4",
@ -19758,7 +19845,6 @@
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"peer": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@ -19771,7 +19857,6 @@
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"peer": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@ -19781,15 +19866,13 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true,
"peer": true
"dev": true
},
"eslint-scope": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
"integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
"dev": true,
"peer": true,
"requires": {
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
@ -19799,15 +19882,23 @@
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
"dev": true
},
"find-up": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
"dev": true,
"peer": true
"requires": {
"locate-path": "^6.0.0",
"path-exists": "^4.0.0"
}
},
"glob-parent": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
"dev": true,
"peer": true,
"requires": {
"is-glob": "^4.0.3"
}
@ -19817,24 +19908,74 @@
"resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz",
"integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==",
"dev": true,
"peer": true,
"requires": {
"type-fest": "^0.20.2"
}
},
"globby": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
"integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
"dev": true,
"requires": {
"array-union": "^2.1.0",
"dir-glob": "^3.0.1",
"fast-glob": "^3.2.9",
"ignore": "^5.2.0",
"merge2": "^1.4.1",
"slash": "^3.0.0"
}
},
"json-schem-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schem-traverse/-/json-schem-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
},
"locate-path": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
"dev": true,
"peer": true
"requires": {
"p-locate": "^5.0.0"
}
},
"p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
"dev": true,
"requires": {
"yocto-queue": "^0.1.0"
}
},
"p-locate": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
"dev": true,
"requires": {
"p-limit": "^3.0.2"
}
},
"path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"dev": true
},
"slash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
"dev": true
},
"type-fest": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
"dev": true,
"peer": true
"dev": true
}
}
},
@ -20188,12 +20329,12 @@
"dev": true
},
"espree": {
"version": "9.3.2",
"resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz",
"integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==",
"version": "9.3.3",
"resolved": "https://registry.npmjs.org/espree/-/espree-9.3.3.tgz",
"integrity": "sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng==",
"dev": true,
"requires": {
"acorn": "^8.7.1",
"acorn": "^8.8.0",
"acorn-jsx": "^5.3.2",
"eslint-visitor-keys": "^3.3.0"
}
@ -20379,8 +20520,7 @@
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
"dev": true,
"peer": true
"dev": true
},
"fastq": {
"version": "1.13.0",
@ -20423,7 +20563,6 @@
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
"integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
"dev": true,
"peer": true,
"requires": {
"flat-cache": "^3.0.4"
}
@ -20521,7 +20660,6 @@
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
"integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
"dev": true,
"peer": true,
"requires": {
"flatted": "^3.1.0",
"rimraf": "^3.0.2"
@ -20531,8 +20669,7 @@
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz",
"integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==",
"dev": true,
"peer": true
"dev": true
},
"follow-redirects": {
"version": "1.15.1",
@ -20863,6 +21000,12 @@
"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
"dev": true
},
"grapheme-splitter": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
"integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
"dev": true
},
"gzip-size": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-7.0.0.tgz",
@ -21343,7 +21486,6 @@
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
"dev": true,
"peer": true,
"requires": {
"parent-module": "^1.0.0",
"resolve-from": "^4.0.0"
@ -21354,7 +21496,6 @@
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
"dev": true,
"peer": true,
"requires": {
"callsites": "^3.0.0"
}
@ -21363,8 +21504,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true,
"peer": true
"dev": true
}
}
},
@ -21372,8 +21512,7 @@
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
"dev": true,
"peer": true
"dev": true
},
"indent-string": {
"version": "4.0.0",
@ -21823,7 +21962,6 @@
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dev": true,
"peer": true,
"requires": {
"argparse": "^2.0.1"
}
@ -21921,12 +22059,16 @@
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"dev": true
},
"json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
},
"json-stable-stringify-without-jsonify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
"dev": true,
"peer": true
"dev": true
},
"json5": {
"version": "2.2.1",
@ -22111,7 +22253,6 @@
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
"integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
"dev": true,
"peer": true,
"requires": {
"prelude-ls": "^1.2.1",
"type-check": "~0.4.0"
@ -22251,8 +22392,7 @@
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true,
"peer": true
"dev": true
},
"lodash.pick": {
"version": "4.4.0",
@ -23210,7 +23350,6 @@
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
"integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
"dev": true,
"peer": true,
"requires": {
"deep-is": "^0.1.3",
"fast-levenshtein": "^2.0.6",
@ -23878,8 +24017,7 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
"dev": true,
"peer": true
"dev": true
},
"prettier": {
"version": "2.7.1",
@ -24972,8 +25110,7 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
"dev": true,
"peer": true
"dev": true
},
"strip-literal": {
"version": "0.4.0",
@ -25216,8 +25353,7 @@
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true,
"peer": true
"dev": true
},
"then-request": {
"version": "6.0.2",
@ -25407,7 +25543,6 @@
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
"integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
"dev": true,
"peer": true,
"requires": {
"prelude-ls": "^1.2.1"
}
@ -25673,8 +25808,7 @@
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
"integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==",
"dev": true,
"peer": true
"dev": true
},
"validate-npm-package-license": {
"version": "3.0.4",
@ -26223,8 +26357,7 @@
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
"dev": true,
"peer": true
"dev": true
},
"wrap-ansi": {
"version": "7.0.0",

2
packages/nc-gui-v2/package.json

@ -20,6 +20,7 @@
"dayjs": "^1.11.3",
"file-saver": "^2.0.5",
"jsep": "^1.3.6",
"json-schema-traverse": "^1.0.0",
"just-clone": "^6.1.1",
"jwt-decode": "^3.1.2",
"locale-codes": "^1.3.1",
@ -62,6 +63,7 @@
"@windicss/plugin-animations": "^1.0.9",
"@windicss/plugin-question-mark": "^0.1.1",
"@windicss/plugin-scrollbar": "^1.2.3",
"eslint": "^8.22.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.0.0",
"happy-dom": "^6.0.3",

7
packages/nc-gui-v2/pages/[projectType]/[projectId]/index.vue

@ -144,7 +144,7 @@ const onMenuClose = (visible: boolean) => {
</script>
<template>
<NuxtLayout id="content" class="flex">
<NuxtLayout id="content">
<template #sidebar>
<a-layout-sider
ref="sidebar"
@ -212,7 +212,7 @@ const onMenuClose = (visible: boolean) => {
</div>
<template #overlay>
<a-menu class="ml-6 !w-[300px] !text-sm !p-0 !rounded">
<a-menu class="!ml-1 !w-[300px] !text-sm">
<a-menu-item-group>
<template #title>
<div class="group select-none flex items-center gap-4 py-1">
@ -405,9 +405,12 @@ const onMenuClose = (visible: boolean) => {
<DashboardTreeView v-show="isOpen" />
</a-layout-sider>
</template>
<div :key="$route.fullPath">
<dashboard-settings-modal v-model="dialogOpen" :open-key="openDialogKey" />
<NuxtPage />
<GeneralPreviewAs float />
</div>
</NuxtLayout>

234
packages/nc-gui-v2/pages/index/index.vue

@ -1,239 +1,37 @@
<script lang="ts" setup>
import { Modal, message } from 'ant-design-vue'
import type { ProjectType } from 'nocodb-sdk'
import {
computed,
definePageMeta,
extractSdkResponseErrorMsg,
navigateTo,
onMounted,
ref,
useApi,
useNuxtApp,
useSidebar,
useUIPermission,
} from '#imports'
import { useRoute } from '#imports'
definePageMeta({
title: 'title.myProject',
})
const { $e } = useNuxtApp()
const { api, isLoading } = useApi()
const { isUIAllowed } = useUIPermission()
useSidebar({ hasSidebar: true, isOpen: true })
const filterQuery = ref('')
const projects = ref<ProjectType[]>()
const loadProjects = async () => {
const response = await api.project.list({})
projects.value = response.list
}
const filteredProjects = computed(
() =>
projects.value?.filter(
(project) => !filterQuery.value || project.title?.toLowerCase?.().includes(filterQuery.value.toLowerCase()),
) ?? [],
)
const deleteProject = (project: ProjectType) => {
$e('c:project:delete')
Modal.confirm({
title: `Do you want to delete '${project.title}' project?`,
okText: 'Yes',
okType: 'danger',
cancelText: 'No',
async onOk() {
try {
await api.project.delete(project.id as string)
$e('a:project:delete')
return projects.value?.splice(projects.value.indexOf(project), 1)
} catch (e: any) {
return message.error(await extractSdkResponseErrorMsg(e))
}
},
})
}
onMounted(() => {
loadProjects()
})
const route = useRoute()
</script>
<template>
<NuxtLayout>
<div class="flex flex-col md:flex-row flex-wrap gap-6 py-6 px-12">
<div class="hidden xl:(block)">
<GeneralSponsors />
</div>
<div class="min-w-2/4 flex-auto">
<a-card class="transition-all duration-300 ease-out !rounded-lg shadow">
<h1 class="flex items-center justify-center gap-2 leading-8 mb-8">
<!-- My Projects -->
<span class="text-4xl">{{ $t('title.myProject') }}</span>
<a-tooltip title="Reload projects">
<span
class="transition-all duration-200 h-full flex items-center group hover:ring active:(ring ring-accent) rounded-full mt-1"
:class="isLoading ? 'animate-spin ring ring-gray-200' : ''"
>
<MdiRefresh
v-t="['a:project:refresh']"
class="text-xl text-gray-500 group-hover:text-accent cursor-pointer"
:class="isLoading ? '!text-primary' : ''"
@click="loadProjects"
/>
</span>
</a-tooltip>
</h1>
<div class="order-1 flex mb-6">
<a-input-search
v-model:value="filterQuery"
class="max-w-[250px] nc-project-page-search rounded"
:placeholder="$t('activity.searchProject')"
/>
<div class="flex-1" />
<a-dropdown v-if="isUIAllowed('projectCreate', true)" :trigger="['click']">
<button class="nc-new-project-menu">
<div class="flex items-center w-full">
{{ $t('title.newProj') }}
<MdiMenuDown class="menu-icon" />
</div>
</button>
<template #overlay>
<a-menu class="!py-0 rounded">
<a-menu-item>
<div
v-t="['c:project:create:xcdb']"
class="nc-project-menu-item group"
@click="navigateTo('/project/create')"
class="min-h-[calc(100vh_-_var(--header-height))] h-auto bg-primary bg-opacity-5 flex flex-col lg:flex-row flex-wrap gap-6 py-6 px-12 pt-65px"
>
<MdiPlusOutline class="group-hover:text-accent" />
<div>{{ $t('activity.createProject') }}</div>
</div>
</a-menu-item>
<a-menu-item>
<div
v-t="['c:project:create:extdb']"
class="nc-project-menu-item group"
@click="navigateTo('/project/create-external')"
>
<MdiDatabaseOutline class="group-hover:text-accent" />
<div v-html="$t('activity.createProjectExtended.extDB')" />
</div>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
<div class="flex-1 justify-end hidden xl:(flex)">
<div>
<GeneralSponsors />
</div>
<TransitionGroup name="layout" mode="out-in">
<div v-if="isLoading">
<a-skeleton />
</div>
<a-table
v-else
:custom-row="
(record) => ({
onClick: () => {
$e('a:project:open')
navigateTo(`/nc/${record.id}`)
},
class: ['group'],
})
"
:data-source="filteredProjects"
:pagination="{ position: ['bottomCenter'] }"
>
<!-- Title -->
<a-table-column key="title" :title="$t('general.title')" data-index="title">
<template #default="{ text }">
<div
class="capitalize color-transition group-hover:text-primary !w-[400px] overflow-hidden overflow-ellipsis whitespace-nowrap"
>
{{ text }}
</div>
</template>
</a-table-column>
<!-- Actions -->
<a-table-column key="id" :title="$t('labels.actions')" data-index="id">
<template #default="{ text, record }">
<div class="flex items-center gap-2">
<MdiEditOutline
v-t="['c:project:edit:rename']"
class="nc-action-btn"
@click.stop="navigateTo(`/project/${text}`)"
/>
<MdiDeleteOutline class="nc-action-btn" @click.stop="deleteProject(record)" />
</div>
</template>
</a-table-column>
</a-table>
</TransitionGroup>
</a-card>
<div class="min-w-2/4 xl:max-w-2/4 w-full mx-auto">
<NuxtPage />
</div>
<div class="flex gap-6 md:block">
<div class="flex flex-1 justify-between gap-6 lg:block">
<template v-if="route.name === 'index-index'">
<TransitionGroup name="page" mode="out-in">
<div>
<GeneralSocialCard />
</div>
<div class="block mt-0 md:(!mt-6) xl:hidden">
<div class="block mt-0 lg:(!mt-6) xl:hidden">
<GeneralSponsors />
</div>
</TransitionGroup>
</template>
</div>
</div>
</NuxtLayout>
</template>
<style scoped>
.nc-action-btn {
@apply text-gray-500 group-hover:text-accent active:(ring ring-accent) cursor-pointer p-2 w-[30px] h-[30px] hover:bg-gray-300/50 rounded-full;
}
.nc-new-project-menu {
@apply cursor-pointer z-1 relative color-transition rounded-md px-3 py-2 text-white;
&::after {
@apply rounded-md absolute top-0 left-0 right-0 bottom-0 transition-all duration-150 ease-in-out bg-primary;
content: '';
z-index: -1;
}
&:hover::after {
@apply transform scale-110 ring ring-accent;
}
&:active::after {
@apply ring ring-accent;
}
}
:deep(.ant-table-cell) {
@apply py-1;
}
:deep(.ant-table-row) {
@apply cursor-pointer;
}
:deep(.ant-table) {
@apply min-h-[428px];
}
</style>

140
packages/nc-gui-v2/pages/index/index/[id].vue

@ -0,0 +1,140 @@
<script lang="ts" setup>
import type { Form } from 'ant-design-vue'
import type { ProjectType } from 'nocodb-sdk'
import { message } from 'ant-design-vue'
import {
extractSdkResponseErrorMsg,
navigateTo,
nextTick,
onMounted,
projectTitleValidator,
reactive,
ref,
useApi,
useRoute,
useSidebar,
} from '#imports'
const { api, isLoading } = useApi()
useSidebar({ hasSidebar: false })
const route = useRoute()
const nameValidationRules = [
{
required: true,
message: 'Project name is required',
},
projectTitleValidator,
]
const form = ref<typeof Form>()
const formState = reactive({
title: '',
})
const getProject = async () => {
try {
const result: ProjectType = await api.project.read(route.params.id as string)
formState.title = result.title as string
} catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e))
}
}
const renameProject = async () => {
try {
await api.project.update(route.params.id as string, formState)
navigateTo(`/nc/${route.params.id}`)
} catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e))
}
}
// select and focus title field on load
onMounted(async () => {
await nextTick(() => {
// todo: replace setTimeout and follow better approach
setTimeout(() => {
const input = form.value?.$el?.querySelector('input[type=text]')
input.setSelectionRange(0, formState.title.length)
input.focus()
}, 500)
})
})
await getProject()
</script>
<template>
<div
class="update-project bg-white relative flex-auto flex flex-col justify-center gap-2 p-8 md:(rounded-lg border-1 border-gray-200 shadow-xl)"
>
<general-noco-icon class="color-transition hover:(ring ring-accent)" :class="[isLoading ? 'animated-bg-gradient' : '']" />
<div
class="color-transition transform group absolute top-5 left-5 text-4xl rounded-full bg-white cursor-pointer"
@click="navigateTo('/')"
>
<MdiChevronLeft class="text-black group-hover:(text-accent scale-110)" />
</div>
<h1 class="prose-2xl font-bold self-center my-4">{{ $t('activity.editProject') }}</h1>
<a-form
ref="form"
:model="formState"
name="basic"
layout="vertical"
class="lg:max-w-3/4 w-full !mx-auto"
no-style
autocomplete="off"
@finish="renameProject"
>
<a-form-item :label="$t('labels.projName')" name="title" :rules="nameValidationRules">
<a-input v-model:value="formState.title" name="title" class="nc-metadb-project-name" />
</a-form-item>
<div class="text-center">
<button type="submit" class="submit">
<span class="flex items-center gap-2">
<MaterialSymbolsRocketLaunchOutline />
{{ $t('general.edit') }}
</span>
</button>
</div>
</a-form>
</div>
</template>
<style lang="scss">
.update-project {
.ant-input-affix-wrapper,
.ant-input {
@apply !appearance-none my-1 border-1 border-solid rounded;
}
.submit {
@apply z-1 relative color-transition rounded p-3 text-white shadow-sm;
&::after {
@apply rounded absolute top-0 left-0 right-0 bottom-0 transition-all duration-150 ease-in-out bg-primary;
content: '';
z-index: -1;
}
&:hover::after {
@apply transform scale-110 ring ring-accent;
}
&:active::after {
@apply ring ring-accent;
}
}
}
</style>

134
packages/nc-gui-v2/pages/project/index/create-external.vue → packages/nc-gui-v2/pages/index/index/create-external.vue

@ -1,28 +1,36 @@
<script lang="ts" setup>
import { onMounted } from '@vue/runtime-core'
import { Form, Modal, message } from 'ant-design-vue'
import { useI18n } from 'vue-i18n'
import { computed, ref, useSidebar, watch } from '#imports'
import { navigateTo, useNuxtApp } from '#app'
import { ClientType } from '~/lib'
import type { ProjectCreateForm } from '~/utils'
import {
clientTypes,
computed,
extractSdkResponseErrorMsg,
fieldRequiredValidator,
generateUniqueName,
getDefaultConnectionConfig,
getTestDatabaseName,
navigateTo,
nextTick,
onMounted,
projectTitleValidator,
readFile,
ref,
sslUsage,
} from '~/utils'
useApi,
useI18n,
useNuxtApp,
useSidebar,
watch,
} from '#imports'
import { ClientType } from '~/lib'
import type { ProjectCreateForm } from '~/utils'
const useForm = Form.useForm
const loading = ref(false)
const testSuccess = ref(false)
const { $api, $e } = useNuxtApp()
const { api, isLoading } = useApi()
const { $e } = useNuxtApp()
useSidebar({ hasSidebar: false })
@ -123,11 +131,13 @@ const createProject = async () => {
focusInvalidInput()
return
}
loading.value = true
try {
const connection = getConnectionConfig()
const config = { ...formState.dataSource, connection }
const result = await $api.project.create({
const result = await api.project.create({
title: formState.title,
bases: [
{
@ -139,12 +149,13 @@ const createProject = async () => {
],
external: true,
})
$e('a:project:create:extdb')
await navigateTo(`/nc/${result.id}`)
} catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e))
}
loading.value = false
}
const testConnection = async () => {
@ -154,19 +165,23 @@ const testConnection = async () => {
focusInvalidInput()
return
}
$e('a:project:create:extdb:test-connection', [])
try {
if (formState.dataSource.client === ClientType.SQLITE) {
testSuccess.value = true
} else {
const connection: any = getConnectionConfig()
connection.database = getTestDatabaseName(formState.dataSource)
const connection = getConnectionConfig()
connection.database = getTestDatabaseName(formState.dataSource)!
const testConnectionConfig = {
...formState.dataSource,
connection,
}
const result = await $api.utils.testConnection(testConnectionConfig)
const result = await api.utils.testConnection(testConnectionConfig)
if (result.code === 0) {
testSuccess.value = true
@ -183,11 +198,13 @@ const testConnection = async () => {
})
} else {
testSuccess.value = false
message.error(`${t('msg.error.dbConnectionFailed')} ${result.message}`)
}
}
} catch (e: any) {
testSuccess.value = false
message.error(await extractSdkResponseErrorMsg(e))
}
}
@ -213,26 +230,34 @@ onMounted(() => {
</script>
<template>
<a-card class="max-w-[600px] !mx-auto !mt-100px !mb-5 !shadow-md">
<GeneralNocoIcon />
<div
class="create-external bg-white relative flex flex-col justify-center gap-2 w-full p-8 md:(rounded-lg border-1 border-gray-200 shadow-xl)"
>
<general-noco-icon class="color-transition hover:(ring ring-accent)" :class="[isLoading ? 'animated-bg-gradient' : '']" />
<h3 class="text-3xl text-center font-semibold mt-8 mb-4">{{ $t('activity.createProject') }}</h3>
<div
class="color-transition transform group absolute top-5 left-5 text-4xl rounded-full bg-white cursor-pointer"
@click="navigateTo('/')"
>
<MdiChevronLeft class="text-black group-hover:(text-accent scale-110)" />
</div>
<h1 class="prose-2xl font-bold self-center my-4">{{ $t('activity.createProject') }}</h1>
<a-form
ref="form"
:model="formState"
name="external-project-create-form"
layout="horizontal"
no-style
:label-col="{ span: 8 }"
:wrapper-col="{ span: 18 }"
class="!pr-5"
>
<a-form-item :label="$t('placeholder.projName')" v-bind="validateInfos.title">
<a-input v-model:value="formState.title" size="small" class="nc-extdb-proj-name" />
<a-input v-model:value="formState.title" class="nc-extdb-proj-name" />
</a-form-item>
<a-form-item :label="$t('labels.dbType')" v-bind="validateInfos['dataSource.client']">
<a-select v-model:value="formState.dataSource.client" size="small" class="nc-extdb-db-type" @change="onClientChange">
<a-select v-model:value="formState.dataSource.client" class="nc-extdb-db-type" @change="onClientChange">
<a-select-option v-for="client in clientTypes" :key="client.value" :value="client.value"
>{{ client.text }}
</a-select-option>
@ -245,32 +270,28 @@ onMounted(() => {
:label="$t('labels.sqliteFile')"
v-bind="validateInfos['dataSource.connection.connection.filename']"
>
<a-input v-model:value="formState.dataSource.connection.connection.filename" size="small" />
<a-input v-model:value="formState.dataSource.connection.connection.filename" />
</a-form-item>
<template v-else>
<!-- Host Address -->
<a-form-item :label="$t('labels.hostAddress')" v-bind="validateInfos['dataSource.connection.host']">
<a-input v-model:value="formState.dataSource.connection.host" size="small" class="nc-extdb-host-address" />
<a-input v-model:value="formState.dataSource.connection.host" class="nc-extdb-host-address" />
</a-form-item>
<!-- Port Number -->
<a-form-item :label="$t('labels.port')" v-bind="validateInfos['dataSource.connection.port']">
<a-input-number v-model:value="formState.dataSource.connection.port" class="!w-full nc-extdb-host-port" size="small" />
<a-input-number v-model:value="formState.dataSource.connection.port" class="!w-full nc-extdb-host-port" />
</a-form-item>
<!-- Username -->
<a-form-item :label="$t('labels.username')" v-bind="validateInfos['dataSource.connection.user']">
<a-input v-model:value="formState.dataSource.connection.user" size="small" class="nc-extdb-host-user" />
<a-input v-model:value="formState.dataSource.connection.user" class="nc-extdb-host-user" />
</a-form-item>
<!-- Password -->
<a-form-item :label="$t('labels.password')">
<a-input-password
v-model:value="formState.dataSource.connection.password"
size="small"
class="nc-extdb-host-password"
/>
<a-input-password v-model:value="formState.dataSource.connection.password" class="nc-extdb-host-password" />
</a-form-item>
<!-- Database -->
@ -279,24 +300,24 @@ onMounted(() => {
<a-input
v-model:value="formState.dataSource.connection.database"
:placeholder="$t('labels.dbCreateIfNotExists')"
size="small"
class="nc-extdb-host-database"
/>
</a-form-item>
<!-- Schema name -->
<a-form-item
v-if="[ClientType.MSSQL, ClientType.PG].includes(formState.dataSource.client) && formState.dataSource.searchPath"
:label="$t('labels.schemaName')"
v-bind="validateInfos['dataSource.searchPath.0']"
>
<a-input v-model:value="formState.dataSource.searchPath[0]" size="small" />
<a-input v-model:value="formState.dataSource.searchPath[0]" />
</a-form-item>
<a-collapse ghost expand-icon-position="right" class="mt-6">
<a-collapse ghost expand-icon-position="right" class="!mt-6">
<a-collapse-panel key="1" :header="$t('title.advancedParameters')">
<!-- todo: add in i18n -->
<a-form-item label="SSL mode">
<a-select v-model:value="formState.sslUse" size="small" @change="onClientChange">
<a-select v-model:value="formState.sslUse" @change="onClientChange">
<a-select-option v-for="opt in sslUsage" :key="opt" :value="opt">{{ opt }}</a-select-option>
</a-select>
</a-form-item>
@ -308,25 +329,29 @@ onMounted(() => {
<template #title>
<span>{{ $t('tooltip.clientCert') }}</span>
</template>
<a-button :disabled="!sslFilesRequired" size="small" class="shadow" @click="certFileInput.click()">
<a-button :disabled="!sslFilesRequired" class="shadow" @click="certFileInput.click()">
{{ $t('labels.clientCert') }}
</a-button>
</a-tooltip>
<a-tooltip placement="top">
<!-- Select .key file -->
<template #title>
<span>{{ $t('tooltip.clientKey') }}</span>
</template>
<a-button :disabled="!sslFilesRequired" size="small" class="shadow" @click="keyFileInput.click()">
<a-button :disabled="!sslFilesRequired" class="shadow" @click="keyFileInput.click()">
{{ $t('labels.clientKey') }}
</a-button>
</a-tooltip>
<a-tooltip placement="top">
<!-- Select CA file -->
<template #title>
<span>{{ $t('tooltip.clientCA') }}</span>
</template>
<a-button :disabled="!sslFilesRequired" size="small" class="shadow" @click="caFileInput.click()">
<a-button :disabled="!sslFilesRequired" class="shadow" @click="caFileInput.click()">
{{ $t('labels.serverCA') }}
</a-button>
</a-tooltip>
@ -334,21 +359,25 @@ onMounted(() => {
</a-form-item>
<input ref="caFileInput" type="file" class="!hidden" @change="onFileSelect('ca', caFileInput)" />
<input ref="certFileInput" type="file" class="!hidden" @change="onFileSelect('cert', certFileInput)" />
<input ref="keyFileInput" type="file" class="!hidden" @change="onFileSelect('key', keyFileInput)" />
<a-form-item :label="$t('labels.inflection.tableName')">
<a-select v-model:value="formState.inflection.inflectionTable" size="small" @change="onClientChange">
<a-select v-model:value="formState.inflection.inflectionTable" @change="onClientChange">
<a-select-option v-for="type in inflectionTypes" :key="type" :value="type">{{ type }}</a-select-option>
</a-select>
</a-form-item>
<a-form-item :label="$t('labels.inflection.columnName')">
<a-select v-model:value="formState.inflection.inflectionColumn" size="small" @change="onClientChange">
<a-select v-model:value="formState.inflection.inflectionColumn" @change="onClientChange">
<a-select-option v-for="type in inflectionTypes" :key="type" :value="type">{{ type }}</a-select-option>
</a-select>
</a-form-item>
<div class="flex justify-end">
<a-button size="small" class="!shadow-md" @click="configEditDlg = true">
<a-button class="!shadow-md" @click="configEditDlg = true">
<!-- Edit connection JSON -->
{{ $t('activity.editConnJson') }}
</a-button>
@ -357,11 +386,12 @@ onMounted(() => {
</a-collapse>
</template>
<a-form-item class="flex justify-center mt-5">
<a-form-item class="flex justify-center !mt-5">
<div class="flex justify-center gap-2">
<a-button type="primary" ghost class="nc-extdb-btn-test-connection" @click="testConnection">
{{ $t('activity.testDbConn') }}
</a-button>
<a-button type="primary" :disabled="!testSuccess" class="nc-extdb-btn-submit !shadow" @click="createProject">
Submit
</a-button>
@ -369,15 +399,17 @@ onMounted(() => {
</a-form-item>
</a-form>
<!-- todo: needs replacement
<v-dialog v-model="configEditDlg">
<a-card>
<MonacoEditor v-if="configEditDlg" v-model="formState" class="h-[400px] w-[600px]" />
</a-card>
</v-dialog>
</a-card>
-->
</div>
</template>
<style scoped>
<style lang="scss" scoped>
:deep(.ant-collapse-header) {
@apply !pr-10 !-mt-4 text-right justify-end;
}
@ -398,7 +430,17 @@ onMounted(() => {
@apply !min-h-0;
}
:deep(.ant-card-head-title) {
@apply !text-3xl;
.create-external {
:deep(.ant-input-affix-wrapper),
:deep(.ant-input),
:deep(.ant-select) {
@apply !appearance-none border-1 border-solid rounded;
}
:deep(.ant-input-password) {
input {
@apply !border-none my-0;
}
}
}
</style>

131
packages/nc-gui-v2/pages/index/index/create.vue

@ -0,0 +1,131 @@
<script lang="ts" setup>
import type { Form } from 'ant-design-vue'
import { message } from 'ant-design-vue'
import {
extractSdkResponseErrorMsg,
navigateTo,
nextTick,
onMounted,
projectTitleValidator,
reactive,
ref,
useApi,
useNuxtApp,
useSidebar,
} from '#imports'
const { $e } = useNuxtApp()
const { api, isLoading } = useApi()
useSidebar({ hasSidebar: false })
const nameValidationRules = [
{
required: true,
message: 'Project name is required',
},
projectTitleValidator,
]
const form = ref<typeof Form>()
const formState = reactive({
title: '',
})
const createProject = async () => {
$e('a:project:create:xcdb')
try {
const result = await api.project.create({
title: formState.title,
})
await navigateTo(`/nc/${result.id}`)
} catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e))
}
}
// select and focus title field on load
onMounted(async () => {
await nextTick(() => {
// todo: replace setTimeout and follow better approach
setTimeout(() => {
const input = form.value?.$el?.querySelector('input[type=text]')
input.setSelectionRange(0, formState.title.length)
input.focus()
}, 500)
})
})
</script>
<template>
<div
class="create bg-white relative flex flex-col justify-center gap-2 w-full p-8 md:(rounded-lg border-1 border-gray-200 shadow-xl)"
>
<general-noco-icon class="color-transition hover:(ring ring-accent)" :class="[isLoading ? 'animated-bg-gradient' : '']" />
<div
class="color-transition transform group absolute top-5 left-5 text-4xl rounded-full bg-white cursor-pointer"
@click="navigateTo('/')"
>
<MdiChevronLeft class="text-black group-hover:(text-accent scale-110)" />
</div>
<h1 class="prose-2xl font-bold self-center my-4">{{ $t('activity.createProject') }}</h1>
<a-form
ref="form"
:model="formState"
name="basic"
layout="vertical"
class="lg:max-w-3/4 w-full !mx-auto"
no-style
autocomplete="off"
@finish="createProject"
>
<a-form-item :label="$t('labels.projName')" name="title" :rules="nameValidationRules" class="m-10">
<a-input v-model:value="formState.title" name="title" class="nc-metadb-project-name" />
</a-form-item>
<div class="text-center">
<button class="submit" type="submit">
<span class="flex items-center gap-2">
<MaterialSymbolsRocketLaunchOutline />
{{ $t('general.create') }}
</span>
</button>
</div>
</a-form>
</div>
</template>
<style lang="scss">
.create {
.ant-input-affix-wrapper,
.ant-input {
@apply !appearance-none my-1 border-1 border-solid rounded;
}
.submit {
@apply z-1 relative color-transition rounded p-3 text-white shadow-sm;
&::after {
@apply rounded absolute top-0 left-0 right-0 bottom-0 transition-all duration-150 ease-in-out bg-primary;
content: '';
z-index: -1;
}
&:hover::after {
@apply transform scale-110 ring ring-accent;
}
&:active::after {
@apply ring ring-accent;
}
}
}
</style>

211
packages/nc-gui-v2/pages/index/index/index.vue

@ -0,0 +1,211 @@
<script lang="ts" setup>
import { Modal, message } from 'ant-design-vue'
import type { ProjectType } from 'nocodb-sdk'
import {
computed,
definePageMeta,
extractSdkResponseErrorMsg,
navigateTo,
ref,
useApi,
useNuxtApp,
useSidebar,
useUIPermission,
} from '#imports'
definePageMeta({
title: 'title.myProject',
})
const { $e } = useNuxtApp()
const { api, isLoading } = useApi()
const { isUIAllowed } = useUIPermission()
useSidebar({ hasSidebar: true, isOpen: true })
const filterQuery = ref('')
const projects = ref<ProjectType[]>()
const loadProjects = async () => {
const response = await api.project.list({})
projects.value = response.list
}
const filteredProjects = computed(
() =>
projects.value?.filter(
(project) => !filterQuery.value || project.title?.toLowerCase?.().includes(filterQuery.value.toLowerCase()),
) ?? [],
)
const deleteProject = (project: ProjectType) => {
$e('c:project:delete')
Modal.confirm({
title: `Do you want to delete '${project.title}' project?`,
okText: 'Yes',
okType: 'danger',
cancelText: 'No',
async onOk() {
try {
await api.project.delete(project.id as string)
$e('a:project:delete')
projects.value?.splice(projects.value?.indexOf(project), 1)
} catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e))
}
},
})
}
await loadProjects()
</script>
<template>
<div class="bg-white relative flex flex-col justify-center gap-2 w-full p-8 md:(rounded-lg border-1 border-gray-200 shadow-xl)">
<general-noco-icon class="color-transition hover:(ring ring-accent)" :class="[isLoading ? 'animated-bg-gradient' : '']" />
<h1 class="flex items-center justify-center gap-2 leading-8 mb-8 mt-4">
<!-- My Projects -->
<span class="text-4xl">{{ $t('title.myProject') }}</span>
<a-tooltip title="Reload projects">
<span
class="transition-all duration-200 h-full flex items-center group hover:ring active:(ring ring-accent) rounded-full mt-1"
:class="isLoading ? 'animate-spin ring ring-gray-200' : ''"
>
<MdiRefresh
v-t="['a:project:refresh']"
class="text-xl text-gray-500 group-hover:text-accent cursor-pointer"
:class="isLoading ? '!text-primary' : ''"
@click="loadProjects"
/>
</span>
</a-tooltip>
</h1>
<div class="flex mb-6">
<a-input-search
v-model:value="filterQuery"
class="max-w-[250px] nc-project-page-search rounded"
:placeholder="$t('activity.searchProject')"
/>
<div class="flex-1" />
<a-dropdown v-if="isUIAllowed('projectCreate', true)" :trigger="['click']">
<button class="nc-new-project-menu">
<span class="flex items-center w-full">
{{ $t('title.newProj') }}
<MdiMenuDown class="menu-icon" />
</span>
</button>
<template #overlay>
<a-menu class="!py-0 rounded">
<a-menu-item>
<div v-t="['c:project:create:xcdb']" class="nc-project-menu-item group" @click="navigateTo('/create')">
<MdiPlusOutline class="group-hover:text-accent" />
<div>{{ $t('activity.createProject') }}</div>
</div>
</a-menu-item>
<a-menu-item>
<div v-t="['c:project:create:extdb']" class="nc-project-menu-item group" @click="navigateTo('/create-external')">
<MdiDatabaseOutline class="group-hover:text-accent" />
<div v-html="$t('activity.createProjectExtended.extDB')" />
</div>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</div>
<TransitionGroup name="layout" mode="out-in">
<div v-if="isLoading">
<a-skeleton />
</div>
<a-table
v-else
:custom-row="
(record) => ({
onClick: () => {
$e('a:project:open')
navigateTo(`/nc/${record.id}`)
},
class: ['group'],
})
"
:data-source="filteredProjects"
:pagination="{ position: ['bottomCenter'] }"
>
<!-- Title -->
<a-table-column key="title" :title="$t('general.title')" data-index="title">
<template #default="{ text }">
<div
class="capitalize color-transition group-hover:text-primary !w-[400px] overflow-hidden overflow-ellipsis whitespace-nowrap"
>
{{ text }}
</div>
</template>
</a-table-column>
<!-- Actions -->
<a-table-column key="id" :title="$t('labels.actions')" data-index="id">
<template #default="{ text, record }">
<div class="flex items-center gap-2">
<MdiEditOutline v-t="['c:project:edit:rename']" class="nc-action-btn" @click.stop="navigateTo(`/${text}`)" />
<MdiDeleteOutline class="nc-action-btn" @click.stop="deleteProject(record)" />
</div>
</template>
</a-table-column>
</a-table>
</TransitionGroup>
</div>
</template>
<style scoped>
.nc-action-btn {
@apply text-gray-500 group-hover:text-accent active:(ring ring-accent) cursor-pointer p-2 w-[30px] h-[30px] hover:bg-gray-300/50 rounded-full;
}
.nc-new-project-menu {
@apply cursor-pointer z-1 relative color-transition rounded-md px-3 py-2 text-white;
&::after {
@apply rounded-md absolute top-0 left-0 right-0 bottom-0 transition-all duration-150 ease-in-out bg-primary bg-opacity-100;
content: '';
z-index: -1;
}
&:hover::after {
@apply transform scale-110 ring ring-accent;
}
&:active::after {
@apply ring ring-accent;
}
}
:deep(.ant-table-cell) {
@apply py-1;
}
:deep(.ant-table-row) {
@apply cursor-pointer;
}
:deep(.ant-table) {
@apply min-h-[428px];
}
</style>

17
packages/nc-gui-v2/pages/index/user/index/index.vue

@ -1,11 +1,6 @@
<script lang="ts" setup>
import { useI18n } from 'vue-i18n'
import { message } from 'ant-design-vue'
import { navigateTo } from '#app'
import { extractSdkResponseErrorMsg } from '~/utils'
import { reactive, ref, useApi, useGlobal } from '#imports'
import MaterialSymbolsWarning from '~icons/material-symbols/warning'
import MdiKeyChange from '~icons/mdi/key-change'
import { extractSdkResponseErrorMsg, navigateTo, reactive, ref, useApi, useGlobal, useI18n } from '#imports'
const { api } = useApi()
@ -81,7 +76,10 @@ const resetError = () => {
<Transition name="layout">
<div v-if="error" class="self-center mb-4 bg-red-500 text-white rounded-lg w-3/4 p-1">
<div class="flex items-center gap-2 justify-center"><MaterialSymbolsWarning /> {{ error }}</div>
<div class="flex items-center gap-2 justify-center">
<MaterialSymbolsWarning />
{{ error }}
</div>
</div>
</Transition>
@ -117,7 +115,10 @@ const resetError = () => {
<div class="flex flex-wrap gap-4 items-center mt-4 md:justify-between w-full">
<button class="submit" type="submit">
<span class="flex items-center gap-2"><MdiKeyChange /> {{ $t('activity.changePwd') }}</span>
<span class="flex items-center gap-2">
<MdiKeyChange />
{{ $t('activity.changePwd') }}
</span>
</button>
</div>
</div>

5
packages/nc-gui-v2/pages/project/index.vue

@ -1,5 +0,0 @@
<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>

83
packages/nc-gui-v2/pages/project/index/[id].vue

@ -1,83 +0,0 @@
<script lang="ts" setup>
import { onMounted } from '@vue/runtime-core'
import type { Form } from 'ant-design-vue'
import type { ProjectType } from 'nocodb-sdk'
import { ref } from 'vue'
import { message } from 'ant-design-vue'
import { navigateTo, useRoute } from '#app'
import { extractSdkResponseErrorMsg, projectTitleValidator } from '~/utils'
import MaterialSymbolsRocketLaunchOutline from '~icons/material-symbols/rocket-launch-outline'
import { nextTick, reactive, useSidebar } from '#imports'
const { api } = useApi()
useSidebar({ hasSidebar: false })
const route = useRoute()
const nameValidationRules = [
{
required: true,
message: 'Project name is required',
},
projectTitleValidator,
]
const formState = reactive({
title: '',
})
const getProject = async () => {
try {
const result: ProjectType = await api.project.read(route.params.id as string)
formState.title = result.title as string
} catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e))
}
}
const renameProject = async () => {
try {
await api.project.update(route.params.id as string, formState)
navigateTo(`/nc/${route.params.id}`)
} catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e))
}
}
const form = ref<typeof Form>()
// select and focus title field on load
onMounted(async () => {
await getProject()
await nextTick(() => {
// todo: replace setTimeout and follow better approach
setTimeout(() => {
const input = form.value?.$el?.querySelector('input[type=text]')
input.setSelectionRange(0, formState.title.length)
input.focus()
}, 500)
})
})
</script>
<template>
<a-card class="w-[500px] !mx-auto !mt-100px shadow-md">
<h3 class="text-3xl text-center font-semibold mb-2">{{ $t('activity.editProject') }}</h3>
<a-form ref="form" :model="formState" name="basic" layout="vertical" autocomplete="off" @finish="renameProject">
<a-form-item :label="$t('labels.projName')" name="title" :rules="nameValidationRules" class="my-10 mx-10">
<a-input v-model:value="formState.title" name="title" class="nc-metadb-project-name" />
</a-form-item>
<a-form-item style="text-align: center" class="mt-2">
<a-button type="primary" html-type="submit" class="mx-auto flex justify-self-center">
<MaterialSymbolsRocketLaunchOutline class="mr-1" />
<span> {{ $t('general.edit') }} </span></a-button
>
</a-form-item>
</a-form>
</a-card>
</template>

78
packages/nc-gui-v2/pages/project/index/create.vue

@ -1,78 +0,0 @@
<script lang="ts" setup>
import { onMounted } from '@vue/runtime-core'
import type { Form } from 'ant-design-vue'
import { message } from 'ant-design-vue'
import { nextTick, reactive, ref, useApi, useSidebar } from '#imports'
import { navigateTo, useNuxtApp } from '#app'
import { extractSdkResponseErrorMsg } from '~/utils/errorUtils'
import { projectTitleValidator } from '~/utils/validation'
import MaterialSymbolsRocketLaunchOutline from '~icons/material-symbols/rocket-launch-outline'
const { $e } = useNuxtApp()
const { api, isLoading } = useApi()
useSidebar({ hasSidebar: false })
const nameValidationRules = [
{
required: true,
message: 'Project name is required',
},
projectTitleValidator,
]
const formState = reactive({
title: '',
})
const createProject = async () => {
$e('a:project:create:xcdb')
try {
const result = await api.project.create({
title: formState.title,
})
await navigateTo(`/nc/${result.id}`)
} catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e))
}
}
const form = ref<typeof Form>()
// select and focus title field on load
onMounted(async () => {
await nextTick(() => {
// todo: replace setTimeout and follow better approach
setTimeout(() => {
const input = form.value?.$el?.querySelector('input[type=text]')
input.setSelectionRange(0, formState.title.length)
input.focus()
}, 500)
})
})
</script>
<template>
<a-card :loading="isLoading" class="w-[500px] !mx-auto !mt-100px shadow-md">
<GeneralNocoIcon />
<h3 class="text-3xl text-center font-semibold mt-8 mb-2">{{ $t('activity.createProject') }}</h3>
<a-form ref="form" :model="formState" name="basic" layout="vertical" autocomplete="off" @finish="createProject">
<a-form-item :label="$t('labels.projName')" name="title" :rules="nameValidationRules" class="my-10 mx-10">
<a-input v-model:value="formState.title" name="title" class="nc-metadb-project-name" />
</a-form-item>
<a-form-item style="text-align: center" class="mt-2">
<a-button type="primary" html-type="submit">
<div class="flex items-center">
<MaterialSymbolsRocketLaunchOutline class="mr-1" />
{{ $t('general.create') }}
</div>
</a-button>
</a-form-item>
</a-form>
</a-card>
</template>

2
packages/nc-gui-v2/pages/signin.vue

@ -80,7 +80,7 @@ function resetError() {
<template>
<NuxtLayout>
<div class="md:bg-primary bg-opacity-5 signin h-full min-h-[600px] flex flex-col justify-center items-center nc-form-signup">
<div class="md:bg-primary bg-opacity-5 signin h-full min-h-[600px] flex flex-col justify-center items-center nc-form-signin">
<div
class="bg-white mt-[60px] relative flex flex-col justify-center gap-2 w-full max-w-[500px] mx-auto p-8 md:(rounded-lg border-1 border-gray-200 shadow-xl)"
>

Loading…
Cancel
Save