Browse Source

Merge pull request #3030 from nocodb/feat/gui-v2-form-view

feat(gui-v2): form view
pull/3078/head
Raju Udava 2 years ago committed by GitHub
parent
commit
a06f3b7c99
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      packages/nc-gui-v2/components.d.ts
  2. 7
      packages/nc-gui-v2/components/smartsheet-column/EditOrAdd.vue
  3. 10
      packages/nc-gui-v2/components/smartsheet-header/Cell.vue
  4. 9
      packages/nc-gui-v2/components/smartsheet-header/VirtualCell.vue
  5. 1534
      packages/nc-gui-v2/components/smartsheet/Form.vue
  6. 10
      packages/nc-gui-v2/components/smartsheet/Toolbar.vue
  7. 2
      packages/nc-gui-v2/composables/useColumnCreateStore.ts
  8. 24
      packages/nc-gui-v2/composables/useViewColumns.ts
  9. 55
      packages/nc-gui-v2/composables/useViewData.ts
  10. 3
      packages/nocodb-sdk/src/lib/Api.ts
  11. 37
      packages/nocodb/src/lib/models/View.ts
  12. 5
      scripts/sdk/swagger.json

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

@ -78,6 +78,7 @@ declare module '@vue/runtime-core' {
MaterialSymbolsTranslate: typeof import('~icons/material-symbols/translate')['default']
MdiAccountCircle: typeof import('~icons/mdi/account-circle')['default']
MdiAccountGroup: typeof import('~icons/mdi/account-group')['default']
MdiAlphaA: typeof import('~icons/mdi/alpha-a')['default']
MdiApi: typeof import('~icons/mdi/api')['default']
MdiArrowExpand: typeof import('~icons/mdi/arrow-expand')['default']
MdiArrowExpandIcon: typeof import('~icons/mdi/arrow-expand-icon')['default']
@ -100,6 +101,7 @@ declare module '@vue/runtime-core' {
MdiDotsVertical: typeof import('~icons/mdi/dots-vertical')['default']
MdiDownload: typeof import('~icons/mdi/download')['default']
MdiDrag: typeof import('~icons/mdi/drag')['default']
MdiDragVertical: typeof import('~icons/mdi/drag-vertical')['default']
MdiEmail: typeof import('~icons/mdi/email')['default']
MdiEyeOffOutline: typeof import('~icons/mdi/eye-off-outline')['default']
MdiFlag: typeof import('~icons/mdi/flag')['default']
@ -110,6 +112,7 @@ declare module '@vue/runtime-core' {
MdiHook: typeof import('~icons/mdi/hook')['default']
MdiInformation: typeof import('~icons/mdi/information')['default']
MdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default']
MdiKeyStar: typeof import('~icons/mdi/key-star')['default']
MdiLink: typeof import('~icons/mdi/link')['default']
MdiLinkVariantRemove: typeof import('~icons/mdi/link-variant-remove')['default']
MdiLogout: typeof import('~icons/mdi/logout')['default']
@ -118,6 +121,7 @@ declare module '@vue/runtime-core' {
MdiMicrosoftTeams: typeof import('~icons/mdi/microsoft-teams')['default']
MdiMoonFull: typeof import('~icons/mdi/moon-full')['default']
MdiNotebookCheckOutline: typeof import('~icons/mdi/notebook-check-outline')['default']
MdiNumeric: typeof import('~icons/mdi/numeric')['default']
MdiOpenInNew: typeof import('~icons/mdi/open-in-new')['default']
MdiOperator: typeof import('~icons/mdi/operator')['default']
MdiPlus: typeof import('~icons/mdi/plus')['default']
@ -128,8 +132,10 @@ declare module '@vue/runtime-core' {
MdiSlack: typeof import('~icons/mdi/slack')['default']
MdiStar: typeof import('~icons/mdi/star')['default']
MdiStore: typeof import('~icons/mdi/store')['default']
MdiTable: typeof import('~icons/mdi/table')['default']
MdiTableArrowRight: typeof import('~icons/mdi/table-arrow-right')['default']
MdiTableBorder: typeof import('~icons/mdi/table-border')['default']
MdiText: typeof import('~icons/mdi/text')['default']
MdiThumbUp: typeof import('~icons/mdi/thumb-up')['default']
MdiTrashCan: typeof import('~icons/mdi/trash-can')['default']
MdiWhatsapp: typeof import('~icons/mdi/whatsapp')['default']

7
packages/nc-gui-v2/components/smartsheet-column/EditOrAdd.vue

@ -13,7 +13,7 @@ interface Props {
const { editColumnDropdown } = defineProps<Props>()
const emit = defineEmits(['cancel'])
const emit = defineEmits(['cancel', 'submit'])
const meta = inject(MetaInj)
const reloadDataTrigger = inject(ReloadViewDataHookInj)
const advancedOptions = ref(false)
@ -43,10 +43,11 @@ const uiTypesOptions = computed<typeof uiTypes>(() => {
]
})
const reloadMetaAndData = () => {
const reloadMetaAndData = async () => {
emit('cancel')
getMeta(meta?.value.id as string, true)
await getMeta(meta?.value.id as string, true)
reloadDataTrigger?.trigger()
emit('submit')
}
function onCancel() {

10
packages/nc-gui-v2/components/smartsheet-header/Cell.vue

@ -5,14 +5,17 @@ import { inject, toRef } from 'vue'
import { ColumnInj, IsFormInj, MetaInj } from '~/context'
import { useProvideColumnCreateStore } from '#imports'
const props = defineProps<{ column: ColumnType & { meta: any }; hideMenu?: boolean }>()
const column = toRef(props, 'column')
const props = defineProps<{ column: ColumnType & { meta: any }; required: boolean; hideMenu?: boolean }>()
const hideMenu = toRef(props, 'hideMenu')
provide(ColumnInj, column)
const meta = inject(MetaInj)
const isForm = inject(IsFormInj, false)
const column = toRef(props, 'column')
provide(ColumnInj, column)
// instantiate column update store
useProvideColumnCreateStore(meta as Ref<TableType>, column)
</script>
@ -21,6 +24,7 @@ useProvideColumnCreateStore(meta as Ref<TableType>, column)
<div class="flex align-center w-full">
<SmartsheetHeaderCellIcon v-if="column" />
<span v-if="column" class="name" style="white-space: nowrap" :title="column.title">{{ column.title }}</span>
<span v-if="(column.rqd && !column.cdf) || required" class="text-red-500">&nbsp;*</span>
<template v-if="!hideMenu">
<div class="flex-1" />

9
packages/nc-gui-v2/components/smartsheet-header/VirtualCell.vue

@ -5,12 +5,14 @@ import type { Ref } from 'vue'
import { ColumnInj, IsFormInj, MetaInj } from '~/context'
import { provide, toRef, useMetas, useProvideColumnCreateStore } from '#imports'
const props = defineProps<{ column: ColumnType & { meta: any }; hideMenu?: boolean }>()
const props = defineProps<{ column: ColumnType & { meta: any }; hideMenu?: boolean; required: boolean }>()
const column = toRef(props, 'column')
const hideMenu = toRef(props, 'hideMenu')
provide(ColumnInj, column)
const { metas } = useMetas()
const meta = inject(MetaInj)
const isForm = inject(IsFormInj, false)
@ -78,19 +80,20 @@ useProvideColumnCreateStore(meta as Ref<TableType>, column)
</script>
<template>
<div class="d-flex align-center">
<div class="d-flex align-center w-full">
<!-- <v-tooltip bottom>
<template #activator="{ on }">
todo: bring tooltip
-->
<SmartsheetHeaderVirtualCellIcon v-if="column" />
<a-tooltip placement="bottom">
<template #title>
{{ tooltipMsg }}
</template>
<span class="name" style="white-space: nowrap" :title="column.title"> {{ column.title }}</span>
</a-tooltip>
<span v-if="column.rqd" class="error--text text--lighten-1">&nbsp;*</span>
<span v-if="column.rqd || required" class="text-red-500">&nbsp;*</span>
<!-- <span class="caption" v-html="tooltipMsg" /> -->

1534
packages/nc-gui-v2/components/smartsheet/Form.vue

File diff suppressed because it is too large Load Diff

10
packages/nc-gui-v2/components/smartsheet/Toolbar.vue

@ -6,17 +6,17 @@ const { isGrid, isForm } = useSmartsheetStoreOrThrow()
<template>
<div class="nc-table-toolbar w-full py-1 flex gap-1 items-center" style="z-index: 7">
<SmartsheetToolbarSearchData class="flex-shrink" />
<SmartsheetToolbarSearchData v-if="isGrid" class="flex-shrink" />
<SmartsheetToolbarFieldsMenu :show-system-fields="false" />
<SmartsheetToolbarFieldsMenu v-if="isGrid" :show-system-fields="false" />
<SmartsheetToolbarColumnFilterMenu />
<SmartsheetToolbarColumnFilterMenu v-if="isGrid" />
<SmartsheetToolbarSortListMenu />
<SmartsheetToolbarSortListMenu v-if="isGrid" />
<SmartsheetToolbarShareView v-if="isForm || isGrid" />
<SmartsheetToolbarMoreActions />
<SmartsheetToolbarMoreActions v-if="isGrid" />
<div class="flex-1" />
</div>

2
packages/nc-gui-v2/composables/useColumnCreateStore.ts

@ -203,7 +203,7 @@ const [useProvideColumnCreateStore, useColumnCreateStore] = createInjectionState
toast.success('Column created')
}
onSuccess()
onSuccess?.()
} catch (e: any) {
const error = await extractSdkResponseErrorMsg(e)
if (error) toast.error(await extractSdkResponseErrorMsg(e))

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

@ -58,14 +58,30 @@ export function useViewColumns(
}
}
const showAll = async () => {
if (view?.value?.id) await $api.dbView.showAllColumn(view.value.id)
const showAll = async (ignoreIds?: any) => {
if (view?.value?.id) {
if (ignoreIds) {
await $api.dbView.showAllColumn(view.value.id, {
ignoreIds,
})
} else {
await $api.dbView.showAllColumn(view.value.id)
}
}
await loadViewColumns()
reloadData?.()
}
const hideAll = async () => {
if (view?.value?.id) await $api.dbView.hideAllColumn(view.value.id)
const hideAll = async (ignoreIds?: any) => {
if (view?.value?.id) {
if (ignoreIds) {
await $api.dbView.hideAllColumn(view.value.id, {
ignoreIds,
})
} else {
await $api.dbView.hideAllColumn(view.value.id)
}
}
await loadViewColumns()
reloadData?.()

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

@ -1,4 +1,4 @@
import type { Api, PaginatedType, TableType, ViewType } from 'nocodb-sdk'
import type { Api, FormType, GalleryType, PaginatedType, TableType, ViewType } from 'nocodb-sdk'
import type { ComputedRef, Ref } from 'vue'
import { notification } from 'ant-design-vue'
import { useNuxtApp } from '#app'
@ -35,6 +35,8 @@ export function useViewData(
const paginationData = ref<PaginatedType>({ page: 1, pageSize: 25 })
const aggCommentCount = ref<Record<string, number>>({})
const galleryData = ref<GalleryType | undefined>(undefined)
const formColumnData = ref<FormType | undefined>(undefined)
const formViewData = ref<FormType | undefined>(undefined)
const { project } = useProject()
const { $api } = useNuxtApp()
@ -285,6 +287,53 @@ export function useViewData(
syncCount()
}
const loadFormView = async () => {
if (!viewMeta?.value?.id) return
try {
const { columns, ...view } = (await $api.dbView.formRead(viewMeta.value.id)) as Record<string, any>
const fieldById = columns.reduce(
(o: Record<string, any>, f: Record<string, any>) => ({
...o,
[f.fk_column_id]: f,
}),
{},
)
let order = 1
formViewData.value = view
formColumnData.value = meta?.value?.columns
?.map((c: Record<string, any>) => ({
...c,
fk_column_id: c.id,
fk_view_id: viewMeta.value.id,
...(fieldById[c.id] ? fieldById[c.id] : {}),
order: (fieldById[c.id] && fieldById[c.id].order) || order++,
id: fieldById[c.id] && fieldById[c.id].id,
}))
.sort((a: Record<string, any>, b: Record<string, any>) => a.order - b.order) as Record<string, any>
} catch (e: any) {
return notification.error({
message: 'Failed to set form data',
description: await extractSdkResponseErrorMsg(e),
})
}
}
const updateFormView = async (view: FormType | undefined) => {
try {
if (!viewMeta?.value?.id || !view) return
await $api.dbView.formUpdate(viewMeta.value.id, view)
} catch (e: any) {
return notification.error({
message: 'Failed to update form view',
description: await extractSdkResponseErrorMsg(e),
})
}
}
return {
loadData,
paginationData,
@ -301,6 +350,10 @@ export function useViewData(
syncCount,
galleryData,
loadGalleryData,
loadFormView,
formColumnData,
formViewData,
updateFormView,
aggCommentCount,
loadAggCommentsCount,
}

3
packages/nocodb-sdk/src/lib/Api.ts

@ -382,13 +382,14 @@ export interface FormType {
title?: string;
heading?: string;
subheading?: string;
sucess_msg?: string;
success_msg?: string;
redirect_url?: string;
redirect_after_secs?: string;
email?: string;
banner_image_url?: string;
logo_url?: string;
submit_another_form?: boolean;
show_blank_form?: boolean;
columns?: FormColumnType[];
fk_model_id?: string;
lock_type?: 'collaborative' | 'locked' | 'personal';

37
packages/nocodb/src/lib/models/View.ts

@ -551,9 +551,10 @@ export default class View implements ViewType {
show: boolean;
},
ncMeta = Noco.ncMeta
): Promise<Array<GridViewColumn | any>> {
): Promise<GridViewColumn | FormViewColumn | GalleryViewColumn | any> {
const view = await this.get(viewId);
const table = this.extractViewColumnsTableName(view);
console.log(table);
const existingCol = await ncMeta.metaGet2(null, null, table, {
fk_view_id: viewId,
@ -573,7 +574,39 @@ export default class View implements ViewType {
);
return { ...existingCol, ...colData };
} else {
return await ncMeta.metaInsert2(null, null, table, {
switch (view.type) {
case ViewTypes.GRID:
return await GridViewColumn.insert({
fk_view_id: viewId,
fk_column_id: fkColId,
order: colData.order,
show: colData.show,
});
case ViewTypes.GALLERY:
return await GalleryViewColumn.insert({
fk_view_id: viewId,
fk_column_id: fkColId,
order: colData.order,
show: colData.show,
});
case ViewTypes.KANBAN:
// TODO: Use the following when KanbanViewColumn is ready to avoid cache issue
// return await KanbanViewColumn.insert({
// fk_view_id: viewId,
// fk_column_id: fkColId,
// order: colData.order,
// show: colData.show,
// });
break;
case ViewTypes.FORM:
return await FormViewColumn.insert({
fk_view_id: viewId,
fk_column_id: fkColId,
order: colData.order,
show: colData.show,
});
}
return await ncMeta.metaInsert2(view.project_id, view.base_id, table, {
fk_view_id: viewId,
fk_column_id: fkColId,
order: colData.order,

5
scripts/sdk/swagger.json

@ -7079,7 +7079,7 @@
"subheading": {
"type": "string"
},
"sucess_msg": {
"success_msg": {
"type": "string"
},
"redirect_url": {
@ -7100,6 +7100,9 @@
"submit_another_form": {
"type": "boolean"
},
"show_blank_form": {
"type": "boolean"
},
"columns": {
"type": "array",
"items": {

Loading…
Cancel
Save