Browse Source

fix(gui-v2): shared form view not passing files when submitting

pull/3300/head
braks 2 years ago
parent
commit
40e67573ad
  1. 12
      packages/nc-gui-v2/components/cell/attachment/index.vue
  2. 23
      packages/nc-gui-v2/components/cell/attachment/utils.ts
  3. 62
      packages/nc-gui-v2/composables/useSharedFormViewStore.ts
  4. 28
      packages/nc-gui-v2/composables/useSmartsheetRowStore.ts
  5. 5
      packages/nc-gui-v2/utils/urlUtils.ts

12
packages/nc-gui-v2/components/cell/attachment/index.vue

@ -18,7 +18,7 @@ import {
} from '#imports'
interface Props {
modelValue: string | Record<string, any>[] | null
modelValue?: string | Record<string, any>[] | null
rowIndex?: number
}
@ -50,10 +50,16 @@ const {
selectedImage,
isReadonly,
storedFiles,
} = useProvideAttachmentCell(updateModelValue)
} = useProvideAttachmentCell((val) => {
console.log(val)
updateModelValue(val)
})
const currentCellRef = computed(() =>
isForm.value ? attachmentCellRef.value : cellRefs.value.find((cell) => cell.dataset.key === `${rowIndex}${column.value.id}`),
!rowIndex && isForm.value
? attachmentCellRef.value
: cellRefs.value.find((cell) => cell.dataset.key === `${rowIndex}${column.value.id}`),
)
const { dragging } = useSortable(sortableRef, visibleItems, updateModelValue, isReadonly)

23
packages/nc-gui-v2/components/cell/attachment/utils.ts

@ -23,10 +23,11 @@ import MdiFilePowerpointBox from '~icons/mdi/file-powerpoint-box'
import MdiFileExcelOutline from '~icons/mdi/file-excel-outline'
import IcOutlineInsertDriveFile from '~icons/ic/outline-insert-drive-file'
interface AttachmentProps {
interface AttachmentProps extends File {
data?: any
file: File
title: string
mimetype: string
}
export const [useProvideAttachmentCell, useAttachmentCell] = useInjectionState(
@ -44,11 +45,8 @@ export const [useProvideAttachmentCell, useAttachmentCell] = useInjectionState(
const editEnabled = inject(EditModeInj, ref(false))
/** keep user selected files data (in base encoded string format) and meta details */
const storedFilesData = ref<{ title: string; file: File }[]>([])
/** keep user selected File object */
const storedFiles = ref<{ title: string; file: File }[]>([])
const storedFiles = ref<AttachmentProps[]>([])
const attachments = ref<File[]>([])
@ -60,15 +58,14 @@ export const [useProvideAttachmentCell, useAttachmentCell] = useInjectionState(
const { api, isLoading } = useApi()
const { files, open, reset } = useFileDialog()
const { files, open } = useFileDialog()
/** remove a file from our stored attachments (either locally stored or saved ones) */
function removeFile(i: number) {
if (isPublic.value) {
storedFilesData.value.splice(i, 1)
storedFiles.value.splice(i, 1)
updateModelValue(storedFilesData.value.map((storedFile) => storedFile.file))
updateModelValue(storedFiles.value.map((stored) => stored.file))
} else {
attachments.value.splice(i, 1)
@ -86,9 +83,8 @@ export const [useProvideAttachmentCell, useAttachmentCell] = useInjectionState(
Array.from(selectedFiles).map(
(file) =>
new Promise<AttachmentProps>((resolve) => {
const res: any = { ...file, file, title: file.name }
const res: AttachmentProps = { ...file, file, title: file.name, mimetype: file.type }
console.log(res)
if (isImage(file.name, (<any>file).mimetype ?? file.type)) {
const reader = new FileReader()
@ -111,9 +107,7 @@ export const [useProvideAttachmentCell, useAttachmentCell] = useInjectionState(
)),
)
reset()
return updateModelValue(storedFiles.value.map((next) => next.file))
return updateModelValue(storedFiles.value.map((stored) => stored.file))
}
const newAttachments = []
@ -136,8 +130,6 @@ export const [useProvideAttachmentCell, useAttachmentCell] = useInjectionState(
}
}
reset()
updateModelValue([...attachments.value, ...newAttachments])
}
@ -176,7 +168,6 @@ export const [useProvideAttachmentCell, useAttachmentCell] = useInjectionState(
return {
attachments,
storedFilesData,
visibleItems,
isPublic,
isPublicGrid,

62
packages/nc-gui-v2/composables/useSharedFormViewStore.ts

@ -1,12 +1,21 @@
import useVuelidate from '@vuelidate/core'
import { minLength, required } from '@vuelidate/validators'
import type { Ref } from 'vue'
import { message } from 'ant-design-vue'
import type { ColumnType, FormType, LinkToAnotherRecordType, TableType, ViewType } from 'nocodb-sdk'
import { ErrorMessages, RelationTypes, UITypes, isVirtualCol } from 'nocodb-sdk'
import type { Ref } from 'vue'
import { SharedViewPasswordInj } from '~/context'
import { extractSdkResponseErrorMsg } from '~/utils'
import { useInjectionState, useMetas } from '#imports'
import {
SharedViewPasswordInj,
computed,
extractSdkResponseErrorMsg,
provide,
ref,
useApi,
useInjectionState,
useMetas,
useProvideSmartsheetRowStore,
watch,
} from '#imports'
const [useProvideSharedFormStore, useSharedFormStore] = useInjectionState((sharedViewId: string) => {
const progress = ref(false)
@ -23,8 +32,10 @@ const [useProvideSharedFormStore, useSharedFormStore] = useInjectionState((share
const meta = ref<TableType>()
const columns = ref<(ColumnType & { required?: boolean; show?: boolean })[]>()
const { $api } = useNuxtApp()
const { api, isLoading } = useApi()
const { metas, setMeta } = useMetas()
const formState = ref({})
const { state: additionalState } = useProvideSmartsheetRowStore(
@ -37,11 +48,11 @@ const [useProvideSharedFormStore, useSharedFormStore] = useInjectionState((share
)
const formColumns = computed(() =>
columns?.value?.filter((c) => c.show)?.filter((col) => !isVirtualCol(col) || col.uidt === UITypes.LinkToAnotherRecord),
columns.value?.filter((c) => c.show).filter((col) => !isVirtualCol(col) || col.uidt === UITypes.LinkToAnotherRecord),
)
const loadSharedView = async () => {
try {
const viewMeta = await $api.public.sharedViewMetaGet(sharedViewId, {
const viewMeta: Record<string, any> = await api.public.sharedViewMetaGet(sharedViewId, {
headers: {
'xc-password': password.value,
},
@ -54,9 +65,10 @@ const [useProvideSharedFormStore, useSharedFormStore] = useInjectionState((share
meta.value = viewMeta.model
columns.value = viewMeta.model?.columns
setMeta(viewMeta.model)
await 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) {
@ -68,11 +80,14 @@ const [useProvideSharedFormStore, useSharedFormStore] = useInjectionState((share
}
const validators = computed(() => {
const obj: any = {
const obj: Record<string, Record<string, any>> = {
localState: {},
virtual: {},
}
for (const column of formColumns?.value ?? []) {
if (!formColumns.value) return obj
for (const column of formColumns.value) {
if (
!isVirtualCol(column) &&
((column.rqd && !column.cdf) || (column.pk && !(column.ai || column.cdf)) || (column as any).required)
@ -103,7 +118,7 @@ const [useProvideSharedFormStore, useSharedFormStore] = useInjectionState((share
const v$ = useVuelidate(
validators,
computed(() => ({ localState: formState?.value, virtual: additionalState?.value })),
computed(() => ({ localState: formState.value, virtual: additionalState.value })),
)
const submitForm = async () => {
@ -113,18 +128,20 @@ const [useProvideSharedFormStore, useSharedFormStore] = useInjectionState((share
}
progress.value = true
const data: Record<string, any> = { ...(formState?.value ?? {}), ...(additionalState?.value || {}) }
const data: Record<string, any> = { ...formState.value, ...additionalState.value }
const attachment: Record<string, any> = {}
for (const col of metas?.value?.[sharedView?.value?.fk_model_id as string]?.columns ?? []) {
/** find attachments in form data */
for (const col of metas.value?.[sharedView.value?.fk_model_id as string]?.columns) {
if (col.uidt === UITypes.Attachment) {
attachment[`_${col.title}`] = data[col.title!]
delete data[col.title!]
console.log(data[col.title!])
attachment[`_${col.title}`] = data[col.title].map((item: { file: File }) => item.file)
}
}
await $api.public.dataCreate(
sharedView?.value?.uuid as string,
await api.public.dataCreate(
(sharedView.value as any)?.uuid as string,
{
data,
...attachment,
@ -139,7 +156,7 @@ const [useProvideSharedFormStore, useSharedFormStore] = useInjectionState((share
submitted.value = true
progress.value = false
await message.success(sharedFormView.value?.success_msg || 'Saved successfully.')
await message.success(sharedFormView.value?.sucess_msg || 'Saved successfully.')
} catch (e: any) {
console.log(e)
await message.error(await extractSdkResponseErrorMsg(e))
@ -148,13 +165,15 @@ const [useProvideSharedFormStore, useSharedFormStore] = useInjectionState((share
}
/** reset form if show_blank_form is true */
watch(submitted, (nextVal: boolean) => {
if (nextVal && sharedFormView.value?.show_blank_form) {
watch(submitted, (nextVal) => {
if (nextVal && (sharedFormView.value as any)?.show_blank_form) {
secondsRemain.value = 5
const intvl = setInterval(() => {
secondsRemain.value = secondsRemain.value - 1
if (secondsRemain.value < 0) {
submitted.value = false
clearInterval(intvl)
}
}, 1000)
@ -185,6 +204,7 @@ const [useProvideSharedFormStore, useSharedFormStore] = useInjectionState((share
submitted,
secondsRemain,
passwordDlg,
isLoading,
}
}, 'expanded-form-store')
@ -192,6 +212,8 @@ export { useProvideSharedFormStore }
export function useSharedFormStoreOrThrow() {
const sharedFormStore = useSharedFormStore()
if (sharedFormStore == null) throw new Error('Please call `useProvideSharedFormStore` on the appropriate parent component')
return sharedFormStore
}

28
packages/nc-gui-v2/composables/useSmartsheetRowStore.ts

@ -2,27 +2,41 @@ import { message } from 'ant-design-vue'
import { UITypes } from 'nocodb-sdk'
import type { ColumnType, LinkToAnotherRecordType, RelationTypes, TableType } from 'nocodb-sdk'
import type { Ref } from 'vue'
import type { MaybeRef } from '@vueuse/core'
import type { Row } from './useViewData'
import { useInjectionState, useMetas, useNuxtApp, useProject, useVirtualCell } from '#imports'
import { NOCO } from '~/lib'
import { deepCompare, extractPkFromRow, extractSdkResponseErrorMsg } from '~/utils'
import {
NOCO,
computed,
deepCompare,
extractPkFromRow,
extractSdkResponseErrorMsg,
ref,
unref,
useInjectionState,
useMetas,
useNuxtApp,
useProject,
useVirtualCell,
} from '#imports'
const [useProvideSmartsheetRowStore, useSmartsheetRowStore] = useInjectionState((meta: Ref<TableType>, row: Ref<Row>) => {
const [useProvideSmartsheetRowStore, useSmartsheetRowStore] = useInjectionState((meta: Ref<TableType>, row: MaybeRef<Row>) => {
const { $api } = useNuxtApp()
const { project } = useProject()
const { metas } = useMetas()
// state
const state = ref<Record<string, Record<string, any> | Record<string, any>[] | null>>({})
// getters
const isNew = computed(() => row.value?.rowMeta?.new ?? false)
const isNew = computed(() => unref(row).rowMeta?.new ?? false)
// actions
const addLTARRef = async (value: Record<string, any>, column: ColumnType) => {
const { isHm, isMm, isBt } = $(useVirtualCell(ref(column)))
if (isHm || isMm) {
state.value[column.title!] = state.value[column.title!] || []
if (!state.value[column.title!]) state.value[column.title!] = []
if (state.value[column.title!]!.find((ln: Record<string, any>) => deepCompare(ln, value))) {
return message.info('This value is already in the list')
@ -106,6 +120,8 @@ export { useProvideSmartsheetRowStore }
export function useSmartsheetRowStoreOrThrow() {
const smartsheetRowStore = useSmartsheetRowStore()
if (smartsheetRowStore == null) throw new Error('Please call `useSmartsheetRowStore` on the appropriate parent component')
return smartsheetRowStore
}

5
packages/nc-gui-v2/utils/urlUtils.ts

@ -21,10 +21,11 @@ export const replaceUrlsWithLink = (text: string): boolean | string => {
export const isValidURL = (str: string) => {
const pattern =
/^(?:(?:https?|ftp):\/\/)?(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00A1-\uFFFF0-9]-*)*[a-z\u00A1-\uFFFF0-9]+)(?:\.(?:[a-z\u00A1-\uFFFF0-9]-*)*[a-z\u00A1-\uFFFF0-9]+)*(?:\.(?:[a-z\u00A1-\uFFFF]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/i
return !!pattern.test(str)
return pattern.test(str)
}
export const openLink = (path: string, baseURL: string, target = '_blank') => {
export const openLink = (path: string, baseURL?: string, target = '_blank') => {
const url = new URL(path, baseURL)
window.open(url.href, target)
}

Loading…
Cancel
Save