Browse Source

refactor(gui-v2): merge create view into `ViewCreate.vue`

pull/2837/head
braks 2 years ago
parent
commit
66b85d443c
  1. 168
      packages/nc-gui-v2/components/dlg/ViewCreate.vue
  2. 6
      packages/nc-gui-v2/components/smartsheet/Sidebar.vue
  3. 73
      packages/nc-gui-v2/composables/useViewCreate.ts
  4. 2
      packages/nc-gui-v2/context/index.ts
  5. 13
      packages/nc-gui-v2/pages/project/index/create-external.vue
  6. 13
      packages/nc-gui-v2/utils/generateName.ts

168
packages/nc-gui-v2/components/dlg/ViewCreate.vue

@ -1,37 +1,72 @@
<script setup lang="ts">
import { inject } from '@vue/runtime-core'
import type { TableType } from 'nocodb-sdk'
import type { ComponentPublicInstance } from '@vue/runtime-core'
import { notification } from 'ant-design-vue'
import type { Form as AntForm } from 'ant-design-vue'
import { capitalize, inject } from '@vue/runtime-core'
import type { GalleryType, GridType, KanbanType, ViewType } from 'nocodb-sdk'
import { ViewTypes } from 'nocodb-sdk'
import type { Ref } from 'vue'
import { ActiveViewInj, MetaInj, ViewListInj } from '~/context'
import useViewCreate from '~/composables/useViewCreate'
import { useI18n } from 'vue-i18n'
import { MetaInj, ViewListInj } from '~/context'
import { generateUniqueTitle } from '~/utils'
import { useNuxtApp } from '#app'
import { computed, nextTick, onMounted, reactive, unref, useVModel, watch } from '#imports'
const { modelValue, type } = defineProps<{ type: ViewTypes; modelValue: boolean }>()
interface Props {
modelValue: boolean
type: ViewTypes
}
const emit = defineEmits(['update:modelValue', 'created'])
interface Emits {
(event: 'update:modelValue', value: boolean): void
(event: 'created', value: ViewType): void
}
const valid = ref(false)
interface Form {
title: string
type: ViewTypes
copy_from_id: string | null
}
const viewList = inject(ViewListInj)
const props = defineProps<Props>()
const activeView = inject(ActiveViewInj)
const emits = defineEmits<Emits>()
const inputEl = ref()
const inputEl = $ref<ComponentPublicInstance>()
const form = ref()
const formValidator = $ref<typeof AntForm>()
const dialogShow = computed({
get() {
return modelValue
},
set(v) {
emit('update:modelValue', v)
},
const vModel = useVModel(props, 'modelValue', emits)
const { t } = useI18n()
const meta = inject(MetaInj)
const viewList = inject(ViewListInj)
const form = reactive<Form>({
title: '',
type: props.type,
copy_from_id: null,
})
const { view, createView, generateUniqueTitle, loading } = useViewCreate(inject(MetaInj) as Ref<TableType>, (view) =>
emit('created', view),
)
const formRules = [
// name is required
{ required: true, message: `${t('labels.viewName')} ${t('general.required')}` },
// name is unique
{
validator: (_: unknown, v: string) =>
new Promise((resolve, reject) => {
;(unref(viewList) || []).every((v1) => ((v1 as GridType | KanbanType | GalleryType).alias || v1.title) !== v)
? resolve(true)
: reject(new Error(`View name should be unique`))
}),
message: 'View name should be unique',
},
]
let loading = $ref(false)
const { $api } = useNuxtApp()
const typeAlias = computed(
() =>
@ -40,46 +75,85 @@ const typeAlias = computed(
[ViewTypes.GALLERY]: 'gallery',
[ViewTypes.FORM]: 'form',
[ViewTypes.KANBAN]: 'kanban',
}[type]),
}[props.type]),
)
watch(vModel, (value) => value && init())
watch(
() => modelValue,
(v) => {
if (v) {
generateUniqueTitle(viewList?.value || [])
nextTick(() => {
const el = inputEl?.value?.$el
el?.querySelector('input')?.focus()
el?.querySelector('input')?.select()
form?.value?.validate()
() => props.type,
(newType) => (form.type = newType),
)
function init() {
form.title = generateUniqueTitle(capitalize(ViewTypes[props.type].toLowerCase()), viewList?.value || [], 'title')
nextTick(() => {
const el = inputEl?.$el as HTMLInputElement
if (el) {
el.focus()
el.select()
}
})
}
async function onSubmit() {
const isValid = await formValidator.value?.validate()
if (isValid && form.type) {
loading = true
const _meta = unref(meta)
if (!_meta || !_meta.id) return
try {
let data
switch (form.type) {
case ViewTypes.GRID:
data = await $api.dbView.gridCreate(_meta.id, form)
break
case ViewTypes.GALLERY:
data = await $api.dbView.galleryCreate(_meta.id, form)
break
case ViewTypes.FORM:
data = await $api.dbView.formCreate(_meta.id, form)
break
}
notification.success({
message: 'View created successfully',
})
emits('created', data)
} catch (e: any) {
notification.error({
message: e.message,
})
}
},
)
const onSubmit = () => {
const isValid = form.value.validate()
if (isValid) {
createView(view.type!)
emit('update:modelValue', false)
loading = false
vModel.value = false
}
}
</script>
<template>
<a-modal v-model:visible="dialogShow">
<a-modal v-model:visible="vModel" :confirm-loading="loading">
<template #title>
{{ $t('general.create') }} <span class="text-capitalize">{{ typeAlias }}</span> {{ $t('objects.view') }}
</template>
<a-form ref="form" layout="vertical" :model="valid" name="createView" @finish="createView">
<a-form-item
:label="$t('labels.viewName')"
:name="$t('labels.viewName')"
:rules="[{ required: true, message: 'View name required' }]"
>
<a-input ref="inputEl" v-model:value="view.title" autofocus />
<a-form ref="formValidator" layout="vertical" :model="form">
<a-form-item :label="$t('labels.viewName')" name="title" :rules="formRules">
<a-input ref="inputEl" v-model:value="form.title" autofocus />
</a-form-item>
</a-form>
<template #footer>
<a-button key="back" @click="vModel = false">Return</a-button>
<a-button key="submit" type="primary" :loading="loading" @click="onSubmit">Submit</a-button>
</template>
</a-modal>
</template>

6
packages/nc-gui-v2/components/smartsheet/Sidebar.vue

@ -1,9 +1,9 @@
<script setup lang="ts">
import type { FormType, GalleryType, GridType, KanbanType } from 'nocodb-sdk'
import { ViewTypes } from 'nocodb-sdk'
import { inject, ref, useViews } from '#imports'
import { inject, provide, ref, useViews, watch } from '#imports'
import { ActiveViewInj, MetaInj, ViewListInj } from '~/context'
import { viewIcons } from '~/utils/viewUtils'
import { viewIcons } from '~/utils'
import MdiPlusIcon from '~icons/mdi/plus'
const meta = inject(MetaInj)
@ -157,6 +157,6 @@ function onViewCreate(view: GridType | FormType | KanbanType | GalleryType) {
</div>
</div>
<DlgViewCreate v-model="viewCreateDlg" :type="viewCreateType" @created="onViewCreate" />
<DlgViewCreate v-if="views" v-model="viewCreateDlg" :type="viewCreateType" @created="onViewCreate" />
</a-layout-sider>
</template>

73
packages/nc-gui-v2/composables/useViewCreate.ts

@ -1,73 +0,0 @@
import type { TableType, ViewType } from 'nocodb-sdk'
import { ViewTypes } from 'nocodb-sdk'
import type { Ref } from 'vue'
import { useToast } from 'vue-toastification'
import { useNuxtApp } from '#app'
// import useMetas from '~/composables/useMetas'
export default (meta: Ref<TableType>, onViewCreate?: (viewMeta: any) => void) => {
const view = reactive<{ title: string; type?: ViewTypes }>({
title: '',
})
const loading = ref(false)
const { $api } = useNuxtApp()
const toast = useToast()
// unused
// const { metas } = useMetas()
const createView = async (viewType: ViewTypes, selectedViewId = null) => {
loading.value = true
try {
let data
switch (viewType) {
case ViewTypes.GRID:
// todo: update swagger
data = await $api.dbView.gridCreate(
meta?.value?.id as string,
{
title: view?.title,
copy_from_id: selectedViewId,
} as any,
)
break
case ViewTypes.GALLERY:
data = await $api.dbView.galleryCreate(
meta?.value?.id as string,
{
title: view?.title,
copy_from_id: selectedViewId,
} as any,
)
break
case ViewTypes.FORM:
data = await $api.dbView.formCreate(
meta?.value?.id as string,
{
title: view?.title,
copy_from_id: selectedViewId,
} as any,
)
break
}
toast.success('View created successfully')
onViewCreate?.(data)
} catch (e: any) {
toast.error(e.message)
}
loading.value = false
}
const generateUniqueTitle = (views: ViewType[]) => {
let c = 1
while (views?.some((t) => t.title === `${meta?.value?.title}${c}`)) {
c++
}
view.title = `${meta?.value?.title}${c}`
}
return { view, createView, generateUniqueTitle, loading }
}

2
packages/nc-gui-v2/context/index.ts

@ -18,4 +18,4 @@ export const ActiveViewInj: InjectionKey<Ref<GridType | FormType | KanbanType |
export const ReadonlyInj: InjectionKey<any> = Symbol('readonly-injection')
export const ReloadViewDataHookInj: InjectionKey<EventHook<void>> = Symbol('reload-view-data-injection')
export const FieldsInj: InjectionKey<Ref<any[]>> = Symbol('fields-injection')
export const ViewListInj: InjectionKey<Ref<ViewType[]>> = Symbol('view-list-injection')
export const ViewListInj: InjectionKey<Ref<(GridType | FormType | KanbanType | GalleryType)[]>> = Symbol('view-list-injection')

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

@ -5,18 +5,19 @@ import { useI18n } from 'vue-i18n'
import { useToast } from 'vue-toastification'
import { ref } from '#imports'
import { navigateTo, useNuxtApp } from '#app'
import { ClientType } from '~/lib/enums'
import { extractSdkResponseErrorMsg } from '~/utils/errorUtils'
import { readFile } from '~/utils/fileUtils'
import type { ProjectCreateForm } from '~/utils/projectCreateUtils'
import { ClientType } from '~/lib'
import type { ProjectCreateForm } from '~/utils'
import {
clientTypes,
extractSdkResponseErrorMsg,
fieldRequiredValidator,
generateUniqueName,
getDefaultConnectionConfig,
getTestDatabaseName,
projectTitleValidator,
readFile,
sslUsage,
} from '~/utils/projectCreateUtils'
import { fieldRequiredValidator, projectTitleValidator } from '~/utils/validation'
} from '~/utils'
const useForm = Form.useForm
const loading = ref(false)

13
packages/nc-gui-v2/utils/generateName.ts

@ -7,3 +7,16 @@ export const generateUniqueName = () => {
.toLowerCase()
.replace(/[ -]/g, '_')
}
export const generateUniqueTitle = <T extends Record<string, any> = Record<string, any>>(
title: string,
arr: T[],
predicate: keyof T,
) => {
let c = 1
while (arr.some((item) => item[predicate] === (`${title}-${c}` as keyof T))) {
c++
}
return `${title}-${c}`
}

Loading…
Cancel
Save