diff --git a/packages/nc-gui-v2/components/cell/Attachment.vue b/packages/nc-gui-v2/components/cell/Attachment.vue deleted file mode 100644 index 6047bca67c..0000000000 --- a/packages/nc-gui-v2/components/cell/Attachment.vue +++ /dev/null @@ -1,207 +0,0 @@ - - - - - - - Drop here - - - - - - - - Click or drop a file into cell - - - - - Add file(s) - - - - - - - - - {{ item.title }} - - - - - - - - - - - - - - - - View attachments - - - - - - - diff --git a/packages/nc-gui-v2/components/cell/attachment/Modal.vue b/packages/nc-gui-v2/components/cell/attachment/Modal.vue new file mode 100644 index 0000000000..8567cd7bb0 --- /dev/null +++ b/packages/nc-gui-v2/components/cell/attachment/Modal.vue @@ -0,0 +1,131 @@ + + + + + + + + + Attach File + + + + Viewing Attachments of + {{ column.title }} + + + + + + + + + Remove File + + + + + + Download file + + + + + + + + + + + + + + + + + {{ item.title }} + + + + + + + diff --git a/packages/nc-gui-v2/components/cell/attachment/index.vue b/packages/nc-gui-v2/components/cell/attachment/index.vue new file mode 100644 index 0000000000..594558f815 --- /dev/null +++ b/packages/nc-gui-v2/components/cell/attachment/index.vue @@ -0,0 +1,132 @@ + + + + + + + Drop here + + + + + + + + + Click or drop a file into cell + + + + + Add file(s) + + + + + + + + + + {{ item.title }} + + + + + + + + + + + + + + + + View attachments + + + + + + + + + + diff --git a/packages/nc-gui-v2/components/cell/attachment/utils.ts b/packages/nc-gui-v2/components/cell/attachment/utils.ts new file mode 100644 index 0000000000..c98b0a53d0 --- /dev/null +++ b/packages/nc-gui-v2/components/cell/attachment/utils.ts @@ -0,0 +1,135 @@ +import { notification } from 'ant-design-vue' +import { computed, createEventHook, inject, ref, useApi, useFileDialog, useInjectionState, useProject, watch } from '#imports' +import { ColumnInj, EditModeInj, MetaInj } from '~/context' +import { isImage } from '~/utils' +import { NOCO } from '~/lib' +import MdiPdfBox from '~icons/mdi/pdf-box' +import MdiFileWordOutline from '~icons/mdi/file-word-outline' +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' + +export const [useProvideAttachmentCell, useAttachmentCell] = useInjectionState(() => { + const isPublicForm = inject('isPublicForm', false) + + const isForm = inject('isForm', false) + + // todo: replace placeholder var + const isPublicGrid = $ref(false) + + const meta = inject(MetaInj)! + + const column = inject(ColumnInj)! + + const editEnabled = inject(EditModeInj, ref(false)) + + const storedFiles = ref<{ title: string; file: File }[]>([]) + + const attachments = ref([]) + + const modalVisible = ref(false) + + const { project } = useProject() + + const { api, isLoading } = useApi() + + const { files, open } = useFileDialog() + + const fileRemovedHook = createEventHook[]>() + + const fileAddedHook = createEventHook() + + function removeFile(i: number) { + if (isPublicForm) { + storedFiles.value.splice(i, 1) + + fileRemovedHook.trigger(storedFiles.value.map((storedFile) => storedFile.file)) + } else { + attachments.value.splice(i, 1) + fileRemovedHook.trigger(attachments.value) + } + } + + async function onFileSelect(selectedFiles: FileList | File[]) { + if (!selectedFiles.length || isPublicGrid) return + + if (isPublicForm) { + storedFiles.value.push( + ...Array.from(selectedFiles).map((file) => { + const res = { file, title: file.name } + if (isImage(file.name, (file as any).mimetype)) { + const reader = new FileReader() + reader.readAsDataURL(file) + } + return res + }), + ) + + return fileAddedHook.trigger(storedFiles.value.map((storedFile) => storedFile.file)) + } + + const newAttachments = [] + + for (const file of selectedFiles) { + try { + const data = await api.storage.upload( + { + path: [NOCO, project.value.title, meta.value.title, column.title].join('/'), + }, + { + files: file, + json: '{}', + }, + ) + + newAttachments.push(...data) + } catch (e: any) { + notification.error({ + message: e.message || 'Some internal error occurred', + }) + } + } + + fileAddedHook.trigger([...attachments.value, ...newAttachments]) + } + + const FileIcon = (icon: string) => { + switch (icon) { + case 'mdi-pdf-box': + return MdiPdfBox + case 'mdi-file-word-outline': + return MdiFileWordOutline + case 'mdi-file-powerpoint-box': + return MdiFilePowerpointBox + case 'mdi-file-excel-outline': + return MdiFileExcelOutline + default: + return IcOutlineInsertDriveFile + } + } + + const visibleItems = computed(() => (isPublicForm ? storedFiles.value : attachments.value) || []) + + watch(files, (nextFiles) => nextFiles && onFileSelect(nextFiles)) + + return { + attachments, + storedFiles, + visibleItems, + isPublicForm, + isForm, + isPublicGrid, + meta, + column, + editEnabled, + isLoading, + api, + open, + onFileSelect, + modalVisible, + FileIcon, + fileRemovedHook, + fileAddedHook, + removeFile, + } +}, 'attachmentCell') diff --git a/packages/nc-gui-v2/package-lock.json b/packages/nc-gui-v2/package-lock.json index a240b8d76f..6c5b48eacf 100644 --- a/packages/nc-gui-v2/package-lock.json +++ b/packages/nc-gui-v2/package-lock.json @@ -34,6 +34,7 @@ "@iconify-json/ri": "^1.1.3", "@intlify/vite-plugin-vue-i18n": "^4.0.0", "@types/axios": "^0.14.0", + "@types/file-saver": "^2.0.5", "@types/papaparse": "^5.3.2", "@types/sortablejs": "^1.13.0", "@vitejs/plugin-vue": "^2.3.3", @@ -2247,6 +2248,12 @@ "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", "dev": true }, + "node_modules/@types/file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ==", + "dev": true + }, "node_modules/@types/form-data": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz", @@ -16058,6 +16065,12 @@ "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", "dev": true }, + "@types/file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ==", + "dev": true + }, "@types/form-data": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz", diff --git a/packages/nc-gui-v2/package.json b/packages/nc-gui-v2/package.json index 778fe37a9c..64531471c0 100644 --- a/packages/nc-gui-v2/package.json +++ b/packages/nc-gui-v2/package.json @@ -40,6 +40,7 @@ "@iconify-json/ri": "^1.1.3", "@intlify/vite-plugin-vue-i18n": "^4.0.0", "@types/axios": "^0.14.0", + "@types/file-saver": "^2.0.5", "@types/papaparse": "^5.3.2", "@types/sortablejs": "^1.13.0", "@vitejs/plugin-vue": "^2.3.3",