Browse Source

Merge branch 'develop' into chore/docker-build

pull/3273/head
Wing-Kam Wong 2 years ago
parent
commit
6377bedb62
  1. 1
      packages/nc-gui-v2/components.d.ts
  2. 14
      packages/nc-gui-v2/components/dashboard/TreeView.vue
  3. 2
      packages/nc-gui-v2/components/dlg/TableCreate.vue
  4. 4
      packages/nc-gui-v2/components/shared-view/Grid.vue
  5. 12
      packages/nc-gui-v2/components/smartsheet-toolbar/ColumnFilter.vue
  6. 6
      packages/nc-gui-v2/components/smartsheet-toolbar/ColumnFilterMenu.vue
  7. 14
      packages/nc-gui-v2/composables/useSmartsheetStore.ts
  8. 6
      packages/nc-gui-v2/composables/useViewColumns.ts
  9. 9
      packages/nc-gui-v2/composables/useViewData.ts
  10. 164
      packages/nc-gui-v2/composables/useViewFilters.ts
  11. 68
      packages/nc-gui-v2/composables/useViewSorts.ts
  12. 2
      packages/nc-gui-v2/layouts/shared-view.vue
  13. 12
      packages/nc-gui-v2/pages/[projectType]/[projectId]/index/index.vue
  14. 41
      packages/nc-gui-v2/pages/[projectType]/[projectId]/index/index/index.vue

1
packages/nc-gui-v2/components.d.ts vendored

@ -122,6 +122,7 @@ declare module '@vue/runtime-core' {
MdiCheck: typeof import('~icons/mdi/check')['default']
MdiChevronDoubleLeft: typeof import('~icons/mdi/chevron-double-left')['default']
MdiChevronDown: typeof import('~icons/mdi/chevron-down')['default']
MdiChevronLeft: typeof import('~icons/mdi/chevron-left')['default']
MdiClose: typeof import('~icons/mdi/close')['default']
MdiCloseBox: typeof import('~icons/mdi/close-box')['default']
MdiCloseCircle: typeof import('~icons/mdi/close-circle')['default']

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

@ -231,8 +231,8 @@ function openTableCreateDialog() {
<div class="flex-1">
<div
class="group flex items-center gap-2 pl-5 pr-3 py-2 text-primary/70 hover:(text-primary/100) cursor-pointer select-none"
v-if="isUIAllowed('table-create')"
class="group flex items-center gap-2 pl-5 pr-3 py-2 text-primary/70 hover:(text-primary/100) cursor-pointer select-none"
@click="openTableCreateDialog"
>
<MdiPlus />
@ -366,15 +366,9 @@ function openTableCreateDialog() {
</div>
</div>
<a-card v-else class="mt-4 mx-4 !bg-gray-50">
<div class="flex flex-col items-center">
<a-empty :image="Empty.PRESENTED_IMAGE_SIMPLE" />
<a-button type="primary" @click.stop="openTableCreateDialog">
{{ $t('tooltip.addTable') }}
</a-button>
</div>
</a-card>
<div v-else class="mt-0.5 pt-16 mx-3 flex flex-col items-center border-t-1 border-gray-50">
<a-empty :image="Empty.PRESENTED_IMAGE_SIMPLE" />
</div>
</div>
</div>

2
packages/nc-gui-v2/components/dlg/TableCreate.vue

@ -58,7 +58,7 @@ onMounted(() => {
</script>
<template>
<a-modal v-model:visible="dialogShow" width="max(30vw, 600px)" @keydown.esc="dialogShow = false">
<a-modal v-model:visible="dialogShow" width="max(30vw, 600px)" centered @keydown.esc="dialogShow = false">
<template #footer>
<a-button key="back" size="large" @click="dialogShow = false">{{ $t('general.cancel') }}</a-button>

4
packages/nc-gui-v2/components/shared-view/Grid.vue

@ -4,7 +4,7 @@ import type { TableType } from 'nocodb-sdk'
import { ActiveViewInj, FieldsInj, IsPublicInj, MetaInj, ReadonlyInj, ReloadViewDataHookInj } from '~/context'
const { sharedView, meta } = useSharedView()
const { sharedView, meta, sorts, nestedFilters } = useSharedView()
const reloadEventHook = createEventHook<void>()
provide(ReloadViewDataHookInj, reloadEventHook)
@ -14,7 +14,7 @@ provide(ActiveViewInj, sharedView)
provide(FieldsInj, ref(meta.value.columns as any[]))
provide(IsPublicInj, ref(true))
useProvideSmartsheetStore(sharedView as Ref<TableType>, meta)
useProvideSmartsheetStore(sharedView as Ref<TableType>, meta, true, sorts, nestedFilters)
</script>
<template>

12
packages/nc-gui-v2/components/smartsheet-toolbar/ColumnFilter.vue

@ -41,6 +41,7 @@ const reloadDataHook = inject(ReloadViewDataHookInj)!
const { $e } = useNuxtApp()
const { nestedFilters } = useSmartsheetStoreOrThrow()
const { filters, deleteFilter, saveOrUpdate, loadFilters, addFilter, addFilterGroup, sync } = useViewFilters(
activeView,
parentId,
@ -48,10 +49,11 @@ const { filters, deleteFilter, saveOrUpdate, loadFilters, addFilter, addFilterGr
() => {
reloadDataHook.trigger()
},
modelValue,
modelValue || nestedFilters.value,
!modelValue,
)
const nestedFilters = ref()
const localNestedFilters = ref()
const filterUpdateCondition = (filter: FilterType, i: number) => {
saveOrUpdate(filter, i)
@ -115,9 +117,9 @@ watch(
const applyChanges = async (hookId?: string) => {
await sync(hookId)
if (!nestedFilters.value?.length) return
if (!localNestedFilters.value?.length) return
for (const nestedFilter of nestedFilters.value) {
for (const nestedFilter of localNestedFilters.value) {
if (nestedFilter.parentId) {
await nestedFilter.applyChanges(hookId, true)
}
@ -166,7 +168,7 @@ defineExpose({
<div class="col-span-5">
<SmartsheetToolbarColumnFilter
v-if="filter.id || filter.children"
ref="nestedFilters"
ref="localNestedFilters"
v-model="filter.children"
:parent-id="filter.id"
nested

6
packages/nc-gui-v2/components/smartsheet-toolbar/ColumnFilterMenu.vue

@ -12,11 +12,15 @@ const { filterAutoSave } = useGlobal()
const filterComp = ref<typeof ColumnFilter>()
const { nestedFilters } = useSmartsheetStoreOrThrow()
// todo: avoid duplicate api call by keeping a filter store
const { filters, loadFilters } = useViewFilters(
activeView,
undefined,
computed(() => false),
computed(() => true),
() => false,
nestedFilters.value,
true,
)
const filtersLength = ref(0)

14
packages/nc-gui-v2/composables/useSmartsheetStore.ts

@ -1,10 +1,16 @@
import { ViewTypes } from 'nocodb-sdk'
import type { TableType, ViewType } from 'nocodb-sdk'
import type { FilterType, SortType, TableType, ViewType } from 'nocodb-sdk'
import type { Ref } from 'vue'
import { computed, reactive, useInjectionState, useNuxtApp, useProject, useTemplateRefsList } from '#imports'
const [useProvideSmartsheetStore, useSmartsheetStore] = useInjectionState(
(view: Ref<ViewType>, meta: Ref<TableType>, shared = false) => {
(
view: Ref<ViewType>,
meta: Ref<TableType>,
shared = false,
initalSorts?: Ref<SortType[]>,
initialFilters?: Ref<FilterType[]>,
) => {
const { $api } = useNuxtApp()
const { sqlUi } = useProject()
@ -37,6 +43,8 @@ const [useProvideSmartsheetStore, useSmartsheetStore] = useInjectionState(
}
return where
})
const sorts = initalSorts ?? ref<SortType[]>([])
const nestedFilters: Ref<FilterType[]> = initialFilters ?? ref<FilterType[]>([])
return {
view,
@ -51,6 +59,8 @@ const [useProvideSmartsheetStore, useSmartsheetStore] = useInjectionState(
isGallery,
cellRefs,
isSharedForm,
sorts,
nestedFilters,
}
},
'smartsheet-store',

6
packages/nc-gui-v2/composables/useViewColumns.ts

@ -52,7 +52,7 @@ export function useViewColumns(view: Ref<ViewType> | undefined, meta: ComputedRe
}
const showAll = async (ignoreIds?: any) => {
if (isPublic.value || isSharedBase) {
if (isPublic.value || isSharedBase.value) {
fields.value = fields.value?.map((field: Field) => ({
...field,
show: true,
@ -75,7 +75,7 @@ export function useViewColumns(view: Ref<ViewType> | undefined, meta: ComputedRe
reloadData?.()
}
const hideAll = async (ignoreIds?: any) => {
if (isPublic.value || isSharedBase) {
if (isPublic.value || isSharedBase.value) {
fields.value = fields.value?.map((field: Field) => ({
...field,
show: false,
@ -144,7 +144,7 @@ export function useViewColumns(view: Ref<ViewType> | undefined, meta: ComputedRe
},
set(v: boolean) {
if (view?.value?.id) {
if (!isPublic.value && !isSharedBase) {
if (!isPublic.value && !isSharedBase.value) {
$api.dbView
.update(view.value.id, {
show_system_fields: v,

9
packages/nc-gui-v2/composables/useViewData.ts

@ -39,10 +39,10 @@ export function useViewData(
const formattedData = ref<Row[]>([])
const isPublic = inject(IsPublicInj, ref(false))
const { project } = useProject()
const { project, isSharedBase } = useProject()
const { fetchSharedViewData, paginationData: sharedPaginationData } = useSharedView()
const { $api } = useNuxtApp()
const { sorts, nestedFilters: filters } = useSharedView()
const { sorts, nestedFilters } = useSmartsheetStoreOrThrow()
const { isUIAllowed } = useUIPermission()
const paginationData = computed({
@ -83,8 +83,7 @@ export function useViewData(
/** load row comments count */
const loadAggCommentsCount = async () => {
// todo: handle in public api
if (isPublic.value) return
if (isPublic.value || isSharedBase.value) return
const ids = formattedData.value
?.filter(({ rowMeta: { new: isNew } }) => !isNew)
@ -112,7 +111,7 @@ export function useViewData(
? await $api.dbViewRow.list('noco', project.value.id!, meta.value.id!, viewMeta!.value.id, {
...params,
...(isUIAllowed('sortSync') ? {} : { sortArrJson: JSON.stringify(sorts.value) }),
...(isUIAllowed('filterSync') ? {} : { filterArrJson: JSON.stringify(filters.value) }),
...(isUIAllowed('filterSync') ? {} : { filterArrJson: JSON.stringify(nestedFilters.value) }),
where: where?.value,
})
: await fetchSharedViewData()

164
packages/nc-gui-v2/composables/useViewFilters.ts

@ -1,14 +1,15 @@
import type { ViewType } from 'nocodb-sdk'
import type { ComputedRef, Ref } from 'vue'
import { message } from 'ant-design-vue'
import {
IsPublicInj,
ReloadViewDataHookInj,
computed,
extractSdkResponseErrorMsg,
inject,
ref,
useMetas,
useNuxtApp,
useSharedView,
useUIPermission,
watch,
} from '#imports'
@ -19,13 +20,12 @@ export function useViewFilters(
parentId?: string,
autoApply?: ComputedRef<boolean>,
reloadData?: () => void,
siblingFilters?: Filter[],
currentFilters?: Filter[],
isNestedRoot?: boolean,
) {
const { nestedFilters } = useSharedView()
const reloadHook = inject(ReloadViewDataHookInj)
const _filters = ref<Filter[]>([])
const { nestedFilters } = useSmartsheetStoreOrThrow()
const isPublic = inject(IsPublicInj, ref(false))
@ -34,21 +34,23 @@ export function useViewFilters(
const { isUIAllowed } = useUIPermission()
const { metas } = useMetas()
const _filters = ref<Filter[]>([])
const nestedMode = computed(() => isPublic.value || !isUIAllowed('filterSync' || !isUIAllowed('filterChildrenRead')))
const filters = computed<Filter[]>({
get: () => (nestedMode.value ? currentFilters! : _filters.value),
set: (value: Filter[]) => {
if (nestedMode.value) {
currentFilters = value
if (isNestedRoot) nestedFilters.value = value
const filters = computed({
get: () => (isPublic.value ? siblingFilters || nestedFilters.value : _filters.value) ?? [],
set: (value) => {
if (isPublic.value) {
if (siblingFilters) {
siblingFilters = value
} else {
nestedFilters.value = value
}
nestedFilters.value = [...nestedFilters.value]
reloadHook?.trigger()
} else {
_filters.value = value
return
}
_filters.value = value
},
})
@ -60,53 +62,65 @@ export function useViewFilters(
}
const loadFilters = async (hookId?: string) => {
if (hookId) {
if (parentId) {
filters.value = await $api.dbTableFilter.childrenRead(parentId)
} else {
filters.value = (await $api.dbTableWebhookFilter.read(hookId as string)) as any
}
} else {
if (parentId) {
filters.value = await $api.dbTableFilter.childrenRead(parentId)
if (nestedMode.value) return
try {
if (hookId) {
if (parentId) {
filters.value = await $api.dbTableFilter.childrenRead(parentId)
} else {
filters.value = (await $api.dbTableWebhookFilter.read(hookId as string)) as any
}
} else {
filters.value = await $api.dbTableFilter.read(view?.value?.id as string)
if (parentId) {
filters.value = await $api.dbTableFilter.childrenRead(parentId)
} else {
filters.value = await $api.dbTableFilter.read(view?.value?.id as string)
}
}
} catch (e: any) {
console.log(e)
message.error(await extractSdkResponseErrorMsg(e))
}
}
const sync = async (hookId?: string, _nested = false) => {
for (const [i, filter] of Object.entries(filters.value)) {
if (filter.status === 'delete') {
await $api.dbTableFilter.delete(filter.id as string)
} else if (filter.status === 'update') {
await $api.dbTableFilter.update(filter.id as string, {
...filter,
fk_parent_id: parentId,
})
} else if (filter.status === 'create') {
if (hookId) {
filters.value[+i] = (await $api.dbTableWebhookFilter.create(hookId, {
try {
for (const [i, filter] of Object.entries(filters.value)) {
if (filter.status === 'delete') {
await $api.dbTableFilter.delete(filter.id as string)
} else if (filter.status === 'update') {
await $api.dbTableFilter.update(filter.id as string, {
...filter,
fk_parent_id: parentId,
})) as any
} else {
filters.value[+i] = (await $api.dbTableFilter.create(view?.value?.id as string, {
...filter,
fk_parent_id: parentId,
})) as any
})
} else if (filter.status === 'create') {
if (hookId) {
filters.value[+i] = (await $api.dbTableWebhookFilter.create(hookId, {
...filter,
fk_parent_id: parentId,
})) as any
} else {
filters.value[+i] = (await $api.dbTableFilter.create(view?.value?.id as string, {
...filter,
fk_parent_id: parentId,
})) as any
}
}
}
}
reloadData?.()
reloadData?.()
} catch (e: any) {
console.log(e)
message.error(await extractSdkResponseErrorMsg(e))
}
}
const deleteFilter = async (filter: Filter, i: number) => {
// if shared or sync permission not allowed simply remove it from array
if (isPublic.value || !isUIAllowed('filterSync')) {
if (nestedMode.value) {
filters.value.splice(i, 1)
filters.value = [...filters.value]
reloadData?.()
} else {
if (filter.id) {
@ -115,11 +129,16 @@ export function useViewFilters(
filter.status = 'delete'
// if auto-apply enabled invoke delete api and remove from array
} else {
await $api.dbTableFilter.delete(filter.id)
try {
await $api.dbTableFilter.delete(filter.id)
reloadData?.()
reloadData?.()
filters.value.splice(i, 1)
filters.value.splice(i, 1)
} catch (e: any) {
console.log(e)
message.error(await extractSdkResponseErrorMsg(e))
}
}
// if not synced yet remove it from array
} else {
@ -129,30 +148,29 @@ export function useViewFilters(
}
const saveOrUpdate = async (filter: Filter, i: number, force = false) => {
if (isPublic.value) {
filters.value[i] = { ...filter }
filters.value = [...filters.value]
return
}
if (!view?.value) return
if (!isUIAllowed('filterSync')) {
// skip
} else if (!autoApply?.value && !force) {
filter.status = filter.id ? 'update' : 'create'
} else if (filter.id) {
await $api.dbTableFilter.update(filter.id, {
...filter,
fk_parent_id: parentId,
})
} else {
// todo: return type of dbTableFilter is void?
filters.value[i] = (await $api.dbTableFilter.create(view?.value?.id as string, {
...filter,
fk_parent_id: parentId,
})) as any
try {
if (nestedMode.value) {
filters.value[i] = { ...filter }
filters.value = [...filters.value]
} else if (!autoApply?.value && !force) {
filter.status = filter.id ? 'update' : 'create'
} else if (filter.id) {
await $api.dbTableFilter.update(filter.id, {
...filter,
fk_parent_id: parentId,
})
} else {
// todo: return type of dbTableFilter is void?
filters.value[i] = (await $api.dbTableFilter.create(view?.value?.id as string, {
...filter,
fk_parent_id: parentId,
})) as any
}
} catch (e: any) {
console.log(e)
message.error(await extractSdkResponseErrorMsg(e))
}
reloadData?.()
@ -168,7 +186,7 @@ export function useViewFilters(
logical_op: 'and',
}
if (isPublic.value) placeHolderGroupFilter.children = [child]
if (nestedMode.value) placeHolderGroupFilter.children = [child]
filters.value.push(placeHolderGroupFilter)

68
packages/nc-gui-v2/composables/useViewSorts.ts

@ -1,36 +1,23 @@
import type { GalleryType, GridType, KanbanType, SortType } from 'nocodb-sdk'
import type { Ref } from 'vue'
import { IsPublicInj, ReloadViewDataHookInj, useNuxtApp } from '#imports'
import { message } from 'ant-design-vue'
import { IsPublicInj, ReloadViewDataHookInj, extractSdkResponseErrorMsg, useNuxtApp } from '#imports'
export function useViewSorts(
view: Ref<(GridType | KanbanType | GalleryType) & { id?: string }> | undefined,
reloadData?: () => void,
) {
const _sorts = ref<SortType[]>([])
const { sorts: sharedViewSorts, sharedView } = useSharedView()
const { sharedView } = useSharedView()
const { sorts } = useSmartsheetStoreOrThrow()
const reloadHook = inject(ReloadViewDataHookInj)
const isPublic = inject(IsPublicInj, ref(false))
const { isSharedBase } = useProject()
const sorts = computed<SortType[]>({
get: () => (isPublic.value || isSharedBase.value ? sharedViewSorts.value : _sorts.value),
set: (value) => {
if (isPublic.value || isSharedBase.value) {
sharedViewSorts.value = value
} else {
_sorts.value = value
}
reloadHook?.trigger()
},
})
const { $api } = useNuxtApp()
const { isUIAllowed } = useUIPermission()
const { isSharedBase } = useProject()
const loadSorts = async () => {
if (isPublic.value) {
@ -38,8 +25,14 @@ export function useViewSorts(
sorts.value = [...sharedSorts]
return
}
if (!view?.value) return
sorts.value = ((await $api.dbTableSort.list(view?.value?.id as string)) as any)?.sorts?.list
try {
if (!view?.value) return
sorts.value = ((await $api.dbTableSort.list(view?.value?.id as string)) as any)?.sorts?.list
} catch (e: any) {
console.error(e)
message.error(await extractSdkResponseErrorMsg(e))
}
}
const saveOrUpdate = async (sort: SortType, i: number) => {
@ -49,14 +42,19 @@ export function useViewSorts(
return
}
if (isUIAllowed('sortSync')) {
if (sort.id) {
await $api.dbTableSort.update(sort.id, sort)
} else {
sorts.value[i] = (await $api.dbTableSort.create(view?.value?.id as string, sort)) as any
try {
if (isUIAllowed('sortSync')) {
if (sort.id) {
await $api.dbTableSort.update(sort.id, sort)
} else {
sorts.value[i] = (await $api.dbTableSort.create(view?.value?.id as string, sort)) as any
}
}
reloadData?.()
} catch (e: any) {
console.error(e)
message.error(await extractSdkResponseErrorMsg(e))
}
reloadData?.()
}
const addSort = () => {
sorts.value = [
@ -68,11 +66,21 @@ export function useViewSorts(
}
const deleteSort = async (sort: SortType, i: number) => {
if (isUIAllowed('sortSync') && sort.id && !isPublic.value && !isSharedBase.value) {
await $api.dbTableSort.delete(sort.id)
try {
if (isUIAllowed('sortSync') && sort.id && !isPublic.value && !isSharedBase.value) {
await $api.dbTableSort.delete(sort.id)
}
sorts.value.splice(i, 1)
sorts.value = [...sorts.value]
} catch (e: any) {
console.error(e)
message.error(await extractSdkResponseErrorMsg(e))
}
sorts.value.splice(i, 1)
sorts.value = [...sorts.value]
}
watch(sorts, () => {
reloadHook?.trigger()
})
return { sorts, loadSorts, addSort, deleteSort, saveOrUpdate }
}

2
packages/nc-gui-v2/layouts/shared-view.vue

@ -25,7 +25,7 @@ export default {
<MdiReload :class="{ 'animate-infinite animate-spin': isLoading }" />
</template>
<div v-else class="text-xl font-semibold truncate">
<div v-else class="text-xl font-semibold truncate text-white">
{{ sharedView?.title }}
</div>
</div>

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

@ -1,7 +1,7 @@
<script setup lang="ts">
import type { TabItem } from '~/composables'
import { TabMetaInj, provide, useSidebar, useTabs } from '#imports'
import { TabType, useGlobal } from '~/composables'
import { TabType } from '~/composables'
import { TabMetaInj, provide, useGlobal, useSidebar, useTabs } from '#imports'
import MdiAirTableIcon from '~icons/mdi/table-large'
import MdiView from '~icons/mdi/eye-circle-outline'
import MdiAccountGroup from '~icons/mdi/account-group'
@ -24,6 +24,10 @@ const icon = (tab: TabItem) => {
}
const { isOpen, toggle } = useSidebar()
function onEdit(targetKey: number, action: 'add' | 'remove' | string) {
if (action === 'remove') closeTab(targetKey)
}
</script>
<template>
@ -41,8 +45,8 @@ const { isOpen, toggle } = useSidebar()
/>
</div>
<a-tabs v-model:activeKey="activeTabIndex" class="nc-root-tabs" type="editable-card" @edit="closeTab(activeTabIndex)">
<a-tab-pane v-for="(tab, i) in tabs" :key="i">
<a-tabs v-model:activeKey="activeTabIndex" class="nc-root-tabs" type="editable-card" @edit="onEdit">
<a-tab-pane v-for="(tab, i) of tabs" :key="i">
<template #tab>
<div class="flex items-center gap-2">
<component :is="icon(tab)" class="text-sm" />

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

@ -3,6 +3,7 @@ import type { UploadChangeParam, UploadFile } from 'ant-design-vue'
import { message } from 'ant-design-vue'
import { ref, useDialog, useDropZone, useFileDialog, useNuxtApp, useProject, watch } from '#imports'
import DlgQuickImport from '~/components/dlg/QuickImport.vue'
import DlgTableCreate from '~/components/dlg/TableCreate.vue'
const dropZone = ref<HTMLDivElement>()
@ -105,6 +106,36 @@ function openQuickImportDialog(type: QuickImportTypes, file: File) {
reset()
}
}
function openCreateTable() {
$e('a:table:open')
const isOpen = ref(true)
const { close } = useDialog(DlgTableCreate, {
'modelValue': isOpen,
'onUpdate:modelValue': closeDialog,
})
function closeDialog() {
isOpen.value = false
close(1000)
reset()
}
}
function onDropZoneClick(e: MouseEvent) {
const elements = document.elementsFromPoint(e.clientX, e.clientY)
const isCreateTableBtnClicked = elements.some((element) => element.classList.contains('create-table-btn'))
if (isCreateTableBtnClicked) {
openCreateTable()
return
}
open()
}
</script>
<template>
@ -119,7 +150,7 @@ function openQuickImportDialog(type: QuickImportTypes, file: File) {
inline
style="top: 20%; left: 20%; right: 20%; bottom: 20%"
class="text-3xl flex items-center justify-center gap-2 border-1 border-dashed rounded hover:border-primary"
@click="open"
@click="onDropZoneClick"
>
<template v-if="isOverDropZone"> <MaterialSymbolsFileCopyOutline class="text-accent" /> Drop here </template>
</general-overlay>
@ -128,11 +159,13 @@ function openQuickImportDialog(type: QuickImportTypes, file: File) {
<div class="text-3xl">Welcome to NocoDB!</div>
<div class="flex items-center flex-wrap justify-center gap-2 prose-lg leading-8">
To get started, either drop a <span class="flex items-center gap-2"><PhFileCsv /> CSV</span>,
To get started, either drop a <span class="flex items-center gap-2"><PhFileCsv /> CSV,</span>
<span class="flex items-center gap-2"><BiFiletypeJson /> JSON</span> or
<span class="flex items-center gap-2"><BiFiletypeXlsx /> Excel</span> file here or click the button in the top-left of
this page.
<span class="flex items-center gap-2"><BiFiletypeXlsx /> Excel file here or</span>
</div>
<a-button type="primary" ghost class="create-table-btn">
<span class="prose text-[1rem] text-primary z-50" @click.stop="openCreateTable">Create new table</span>
</a-button>
</div>
</div>
</div>

Loading…
Cancel
Save