mirror of https://github.com/nocodb/nocodb
Pranav C
2 years ago
6 changed files with 402 additions and 75 deletions
@ -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<any>() |
||||
const meta = ref<TableType>() |
||||
const columns = ref<(ColumnType & { required?: boolean })[]>() |
||||
|
||||
const { $api } = useNuxtApp() |
||||
const { metas, setMeta } = useMetas() |
||||
const formState = ref({}) |
||||
|
||||
const formColumns = computed( |
||||
() => |
||||
columns?.value |
||||
?.filter?.( |
||||
(f: Record<string, any>) => |
||||
f.show && f.uidt !== UITypes.Rollup && f.uidt !== UITypes.Lookup && f.uidt !== UITypes.Formula, |
||||
) |
||||
.sort((a: Record<string, any>, b: Record<string, any>) => a.order - b.order) |
||||
.map<ColumnType & { required: boolean }>((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<string, any>, additionalState: Record<string, any>) => { |
||||
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<string, any> = {} |
||||
|
||||
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 |
||||
} |
@ -1,39 +1,45 @@
|
||||
<script setup lang="ts"> |
||||
import type { Ref } from 'vue' |
||||
import type { TableType } from 'nocodb-sdk' |
||||
import { ActiveViewInj, FieldsInj, IsPublicInj, MetaInj, ReloadViewDataHookInj, useRoute } from '#imports' |
||||
import type { TableType } from 'nocodb-sdk/build/main' |
||||
import { useProvideSharedFormStore } from '~/composables/useSharedFormViewStore' |
||||
import { ActiveViewInj, FieldsInj, IsFormInj, IsPublicInj, MetaInj, ReloadViewDataHookInj } from '~/context' |
||||
import { createEventHook, definePageMeta, provide, ref, useProvideSmartsheetStore, useRoute } from '#imports' |
||||
|
||||
definePageMeta({ |
||||
requiresAuth: false, |
||||
layout: 'false', |
||||
}) |
||||
|
||||
const route = useRoute() |
||||
|
||||
const reloadEventHook = createEventHook<void>() |
||||
const { sharedView, loadSharedView, meta, formColumns } = useSharedView() |
||||
|
||||
await loadSharedView(route.params.viewId as string) |
||||
|
||||
provide(ReloadViewDataHookInj, reloadEventHook) |
||||
provide(MetaInj, meta) |
||||
provide(ActiveViewInj, sharedView) |
||||
provide(FieldsInj, formColumns) |
||||
provide(IsPublicInj, ref(true)) |
||||
const { loadSharedView, sharedView, meta, columns, notFound, formColumns } = useProvideSharedFormStore() |
||||
|
||||
useProvideSmartsheetStore(sharedView as Ref<TableType>, meta) |
||||
await loadSharedView(route.params.viewId as string) |
||||
if (!notFound.value) { |
||||
provide(ReloadViewDataHookInj, reloadEventHook) |
||||
provide(MetaInj, meta) |
||||
provide(ActiveViewInj, sharedView) |
||||
provide(FieldsInj, formColumns) |
||||
provide(IsPublicInj, ref(true)) |
||||
provide(IsFormInj, ref(true)) |
||||
|
||||
useProvideSmartsheetStore(sharedView as Ref<TableType>, meta) |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<NuxtLayout id="content" class="flex"> |
||||
<div class="nc-container flex flex-col h-full mt-2 px-6"> |
||||
<SharedViewForm /> |
||||
</div> |
||||
</NuxtLayout> |
||||
<!-- <NuxtLayout name="empty"> --> |
||||
<SharedViewForm /> |
||||
<!-- </NuxtLayout> --> |
||||
</template> |
||||
|
||||
<style scoped> |
||||
.nc-container { |
||||
height: calc(100% - var(--header-height)); |
||||
flex: 1 1 100%; |
||||
:global(.ant-layout-header) { |
||||
@apply !hidden; |
||||
} |
||||
:global(.nc-main-container) { |
||||
@apply !h-auto; |
||||
} |
||||
</style> |
||||
|
Loading…
Reference in new issue