From 2446b04de157e6d504d7a9b128b983204b1db567 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Wed, 17 Aug 2022 01:37:59 +0530 Subject: [PATCH 01/27] feat(gui-v2): add shared form view Signed-off-by: Pranav C --- .../nc-gui-v2/components/shared-view/Form.vue | 170 ++++++++++++------ packages/nc-gui-v2/composables/index.ts | 1 + .../composables/useSharedFormViewStore.ts | 160 +++++++++++++++++ packages/nc-gui-v2/package-lock.json | 100 +++++++++++ packages/nc-gui-v2/package.json | 2 + .../pages/[projectType]/form/[viewId].vue | 44 +++-- 6 files changed, 402 insertions(+), 75 deletions(-) create mode 100644 packages/nc-gui-v2/composables/useSharedFormViewStore.ts diff --git a/packages/nc-gui-v2/components/shared-view/Form.vue b/packages/nc-gui-v2/components/shared-view/Form.vue index 2a73cd22f5..d7171cb9f5 100644 --- a/packages/nc-gui-v2/components/shared-view/Form.vue +++ b/packages/nc-gui-v2/components/shared-view/Form.vue @@ -1,12 +1,9 @@ @@ -84,4 +138,8 @@ const formRef = ref() .nc-input { @apply w-full !bg-white rounded px-2 py-2 min-h-[40px] mt-2 mb-2 flex align-center border-solid border-1 border-primary; } + +.nc-form-wrapper { + @apply my-0 mx-auto max-w-[800px]; +} diff --git a/packages/nc-gui-v2/composables/index.ts b/packages/nc-gui-v2/composables/index.ts index b8a5c6339d..a242896d05 100644 --- a/packages/nc-gui-v2/composables/index.ts +++ b/packages/nc-gui-v2/composables/index.ts @@ -21,3 +21,4 @@ export * from './useColumnCreateStore' export * from './useSmartsheetStore' export * from './useLTARStore' export * from './useExpandedFormStore' +export * from './useSharedFormViewStore' diff --git a/packages/nc-gui-v2/composables/useSharedFormViewStore.ts b/packages/nc-gui-v2/composables/useSharedFormViewStore.ts new file mode 100644 index 0000000000..aab72bb898 --- /dev/null +++ b/packages/nc-gui-v2/composables/useSharedFormViewStore.ts @@ -0,0 +1,160 @@ +import useVuelidate from '@vuelidate/core' +import { minLength, required } from '@vuelidate/validators' +import { message } from 'ant-design-vue' +import type { ColumnType, LinkToAnotherRecordType, TableType } from 'nocodb-sdk' +import { ErrorMessages, RelationTypes, UITypes, isVirtualCol } from 'nocodb-sdk' +import { extractSdkResponseErrorMsg } from '~/utils' + +const [useProvideSharedFormStore, useSharedFormStore] = useInjectionState(() => { + const progress = ref(false) + const notFound = ref(false) + const showPasswordModal = ref(false) + const submitted = ref(false) + const password = ref(null) + + // todo: type + const sharedView = ref() + const meta = ref() + const columns = ref<(ColumnType & { required?: boolean })[]>() + + const { $api } = useNuxtApp() + const { metas, setMeta } = useMetas() + const formState = ref({}) + + const formColumns = computed( + () => + columns?.value + ?.filter?.( + (f: Record) => + f.show && f.uidt !== UITypes.Rollup && f.uidt !== UITypes.Lookup && f.uidt !== UITypes.Formula, + ) + .sort((a: Record, b: Record) => a.order - b.order) + .map((c: ColumnType & { required?: boolean }) => ({ + ...c, + required: !!(c.required || 0), + })) ?? [], + ) + const loadSharedView = async (viewId: string) => { + try { + // todo: swagget type correction + const viewMeta: any = await $api.public.sharedViewMetaGet(viewId) + sharedView.value = viewMeta + meta.value = viewMeta.model + columns.value = viewMeta.model.columns + + setMeta(viewMeta.model) + + const relatedMetas = { ...viewMeta.relatedMetas } + Object.keys(relatedMetas).forEach((key) => setMeta(relatedMetas[key])) + } catch (e: any) { + if (e.response && e.response.status === 404) { + notFound.value = true + } else if ((await extractSdkResponseErrorMsg(e)) === ErrorMessages.INVALID_SHARED_VIEW_PASSWORD) { + showPasswordModal.value = true + } + } + } + + const validators = computed(() => { + const obj: any = { + localState: {}, + virtual: {}, + } + for (const column of formColumns?.value ?? []) { + if ( + !isVirtualCol(column) && + ((column.rqd && !column.cdf) || (column.pk && !(column.ai || column.cdf)) || (column as any).required) + ) { + obj.localState[column.title!] = { required } + } else if ( + column.uidt === UITypes.LinkToAnotherRecord && + column.colOptions && + (column.colOptions as LinkToAnotherRecordType).type === RelationTypes.BELONGS_TO + ) { + const col = columns.value?.find((c) => c.id === (column?.colOptions as LinkToAnotherRecordType)?.fk_child_column_id) + + if ((col && col.rqd && !col.cdf) || column.required) { + if (col) { + obj.virtual[column.title!] = { required } + } + } + } else if (isVirtualCol(column) && column.required) { + obj.virtual[column.title!] = { + minLength: minLength(1), + required, + } + } + } + + console.log(obj) + return obj + }) + + const v$ = useVuelidate(validators, { localState: formState, virtual: {} }) + + const submitForm = async (formState: Record, additionalState: Record) => { + try { + // if (this.$v.localState.$invalid || this.$v.virtual.$invalid) { + // this.$toast.error('Provide values of all required field').goAway(3000); + // return; + // } + if (!(await v$.value?.$validate())) { + return + } + + progress.value = true + const data = { ...formState, ...additionalState } + const attachment: Record = {} + + for (const col of metas?.value?.[sharedView?.value?.fk_model_id]?.columns ?? []) { + if (col.uidt === UITypes.Attachment) { + attachment[`_${col.title}`] = data[col.title!] + delete data[col.title!] + } + } + + await $api.public.dataCreate( + sharedView?.value?.uuid, + { + data, + ...attachment, + }, + { + // todo: add password support + // headers: { 'xc-password': password }, + }, + ) + + progress.value = false + await message.success(sharedView.value.success_msg || 'Saved successfully.') + } catch (e: any) { + console.log(e) + await message.error(await extractSdkResponseErrorMsg(e)) + } + progress.value = false + } + + return { + sharedView, + loadSharedView, + columns, + submitForm, + progress, + meta, + validators, + v$, + formColumns, + formState, + notFound, + password, + submitted + } +}, 'expanded-form-store') + +export { useProvideSharedFormStore } + +export function useSharedFormStoreOrThrow() { + const sharedFormStore = useSharedFormStore() + if (sharedFormStore == null) throw new Error('Please call `useProvideSharedFormStore` on the appropriate parent component') + return sharedFormStore +} diff --git a/packages/nc-gui-v2/package-lock.json b/packages/nc-gui-v2/package-lock.json index c9bc82fe4c..6f084d334f 100644 --- a/packages/nc-gui-v2/package-lock.json +++ b/packages/nc-gui-v2/package-lock.json @@ -6,6 +6,8 @@ "": { "dependencies": { "@ckpack/vue-color": "^1.2.0", + "@vuelidate/core": "^2.0.0-alpha.44", + "@vuelidate/validators": "^2.0.0-alpha.31", "@vueuse/core": "^9.0.2", "@vueuse/integrations": "^9.0.2", "ant-design-vue": "^3.2.10", @@ -2862,6 +2864,72 @@ "vue": "^3.0.1" } }, + "node_modules/@vuelidate/core": { + "version": "2.0.0-alpha.44", + "resolved": "https://registry.npmjs.org/@vuelidate/core/-/core-2.0.0-alpha.44.tgz", + "integrity": "sha512-3DlCe3E0RRXbB+OfPacUetKhLmXzmnjeHkzjnbkc03p06mKm6h9pXR5pd6Mv4s4tus4sieuKDb2YWNmKK6rQeA==", + "dependencies": { + "vue-demi": "^0.13.4" + } + }, + "node_modules/@vuelidate/core/node_modules/vue-demi": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.8.tgz", + "integrity": "sha512-Vy1zbZhCOdsmvGR6tJhAvO5vhP7eiS8xkbYQSoVa7o6KlIy3W8Rc53ED4qI4qpeRDjv3mLfXSEpYU6Yq4pgXRg==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@vuelidate/validators": { + "version": "2.0.0-alpha.31", + "resolved": "https://registry.npmjs.org/@vuelidate/validators/-/validators-2.0.0-alpha.31.tgz", + "integrity": "sha512-+MFA9nZ7Y9zCpq383/voPDk/hiAmu6KqiJJhLOYB/FmrUPVoyKnuKnI9Bwiq8ok9GZlVkI8BnIrKPKGj9QpwiQ==", + "dependencies": { + "vue-demi": "^0.13.4" + } + }, + "node_modules/@vuelidate/validators/node_modules/vue-demi": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.8.tgz", + "integrity": "sha512-Vy1zbZhCOdsmvGR6tJhAvO5vhP7eiS8xkbYQSoVa7o6KlIy3W8Rc53ED4qI4qpeRDjv3mLfXSEpYU6Yq4pgXRg==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, "node_modules/@vuetify/loader-shared": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@vuetify/loader-shared/-/loader-shared-1.5.0.tgz", @@ -17366,6 +17434,38 @@ "dev": true, "requires": {} }, + "@vuelidate/core": { + "version": "2.0.0-alpha.44", + "resolved": "https://registry.npmjs.org/@vuelidate/core/-/core-2.0.0-alpha.44.tgz", + "integrity": "sha512-3DlCe3E0RRXbB+OfPacUetKhLmXzmnjeHkzjnbkc03p06mKm6h9pXR5pd6Mv4s4tus4sieuKDb2YWNmKK6rQeA==", + "requires": { + "vue-demi": "^0.13.4" + }, + "dependencies": { + "vue-demi": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.8.tgz", + "integrity": "sha512-Vy1zbZhCOdsmvGR6tJhAvO5vhP7eiS8xkbYQSoVa7o6KlIy3W8Rc53ED4qI4qpeRDjv3mLfXSEpYU6Yq4pgXRg==", + "requires": {} + } + } + }, + "@vuelidate/validators": { + "version": "2.0.0-alpha.31", + "resolved": "https://registry.npmjs.org/@vuelidate/validators/-/validators-2.0.0-alpha.31.tgz", + "integrity": "sha512-+MFA9nZ7Y9zCpq383/voPDk/hiAmu6KqiJJhLOYB/FmrUPVoyKnuKnI9Bwiq8ok9GZlVkI8BnIrKPKGj9QpwiQ==", + "requires": { + "vue-demi": "^0.13.4" + }, + "dependencies": { + "vue-demi": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.8.tgz", + "integrity": "sha512-Vy1zbZhCOdsmvGR6tJhAvO5vhP7eiS8xkbYQSoVa7o6KlIy3W8Rc53ED4qI4qpeRDjv3mLfXSEpYU6Yq4pgXRg==", + "requires": {} + } + } + }, "@vuetify/loader-shared": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@vuetify/loader-shared/-/loader-shared-1.5.0.tgz", diff --git a/packages/nc-gui-v2/package.json b/packages/nc-gui-v2/package.json index cca8e10ace..295b3e94d3 100644 --- a/packages/nc-gui-v2/package.json +++ b/packages/nc-gui-v2/package.json @@ -12,6 +12,8 @@ }, "dependencies": { "@ckpack/vue-color": "^1.2.0", + "@vuelidate/core": "^2.0.0-alpha.44", + "@vuelidate/validators": "^2.0.0-alpha.31", "@vueuse/core": "^9.0.2", "@vueuse/integrations": "^9.0.2", "ant-design-vue": "^3.2.10", diff --git a/packages/nc-gui-v2/pages/[projectType]/form/[viewId].vue b/packages/nc-gui-v2/pages/[projectType]/form/[viewId].vue index aa46ab0e85..56753cff65 100644 --- a/packages/nc-gui-v2/pages/[projectType]/form/[viewId].vue +++ b/packages/nc-gui-v2/pages/[projectType]/form/[viewId].vue @@ -1,39 +1,45 @@ From 1e75ff30cd2e04c52135830c23f4b2f2180ed9a8 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Wed, 17 Aug 2022 01:43:11 +0530 Subject: [PATCH 02/27] feat(gui-v2): shared form LTAR - load referenced table data Signed-off-by: Pranav C --- .../nc-gui-v2/components/shared-view/Form.vue | 8 ++++-- .../components/virtual-cell/ManyToMany.vue | 5 +++- .../nc-gui-v2/composables/useLTARStore.ts | 28 +++++++++++++++++-- .../composables/useSharedFormViewStore.ts | 8 +++--- 4 files changed, 39 insertions(+), 10 deletions(-) diff --git a/packages/nc-gui-v2/components/shared-view/Form.vue b/packages/nc-gui-v2/components/shared-view/Form.vue index d7171cb9f5..55572cff3a 100644 --- a/packages/nc-gui-v2/components/shared-view/Form.vue +++ b/packages/nc-gui-v2/components/shared-view/Form.vue @@ -1,7 +1,6 @@