From e98c7a7a5518f0f7f1a3ecafc49fad9f267b3a8d Mon Sep 17 00:00:00 2001 From: Michael Sacone Date: Wed, 16 Oct 2024 13:23:08 +0200 Subject: [PATCH] Add Metadata selection before file upload --- components.d.ts | 18 -------- package.json | 2 +- pnpm-lock.yaml | 11 +++-- src/services/RabbitHoleService.ts | 7 ++- src/views/HomeView.vue | 72 ++++++++++++++++++++++++++++++- 5 files changed, 82 insertions(+), 28 deletions(-) diff --git a/components.d.ts b/components.d.ts index 11e9065..1c0092b 100644 --- a/components.d.ts +++ b/components.d.ts @@ -14,7 +14,6 @@ declare module 'vue' { DynamicForm: typeof import('./src/components/DynamicForm.vue')['default'] ErrorBox: typeof import('./src/components/ErrorBox.vue')['default'] Header: typeof import('./src/components/Header.vue')['default'] - HeroiconsAdjustmentsVertical: typeof import('~icons/heroicons/adjustments-vertical')['default'] HeroiconsArrowDown20Solid: typeof import('~icons/heroicons/arrow-down20-solid')['default'] HeroiconsArrowPath: typeof import('~icons/heroicons/arrow-path')['default'] HeroiconsArrowRightOnRectangle: typeof import('~icons/heroicons/arrow-right-on-rectangle')['default'] @@ -22,15 +21,10 @@ declare module 'vue' { HeroiconsBoltSolid: typeof import('~icons/heroicons/bolt-solid')['default'] HeroiconsChevronUpDown20Solid: typeof import('~icons/heroicons/chevron-up-down20-solid')['default'] HeroiconsClipboard: typeof import('~icons/heroicons/clipboard')['default'] - HeroiconsCloudArrowDownSolid: typeof import('~icons/heroicons/cloud-arrow-down-solid')['default'] HeroiconsCog6Tooth20Solid: typeof import('~icons/heroicons/cog6-tooth20-solid')['default'] HeroiconsDocumentTextSolid: typeof import('~icons/heroicons/document-text-solid')['default'] HeroiconsGlobeAlt: typeof import('~icons/heroicons/globe-alt')['default'] HeroiconsHome20Solid: typeof import('~icons/heroicons/home20-solid')['default'] - HeroiconsInformationCircleSolid: typeof import('~icons/heroicons/information-circle-solid')['default'] - HeroiconsLink20Solid: typeof import('~icons/heroicons/link20-solid')['default'] - HeroiconsLogout: typeof import('~icons/heroicons/logout')['default'] - HeroiconsLogoutSolid: typeof import('~icons/heroicons/logout-solid')['default'] HeroiconsMagnifyingGlass20Solid: typeof import('~icons/heroicons/magnifying-glass20-solid')['default'] HeroiconsMicrophoneSolid: typeof import('~icons/heroicons/microphone-solid')['default'] HeroiconsMoonSolid: typeof import('~icons/heroicons/moon-solid')['default'] @@ -53,36 +47,24 @@ declare module 'vue' { ModalBox: typeof import('./src/components/ModalBox.vue')['default'] NotificationStack: typeof import('./src/components/NotificationStack.vue')['default'] Pagination: typeof import('./src/components/Pagination.vue')['default'] - PhArrowCounterClockwiseBold: typeof import('~icons/ph/arrow-counter-clockwise-bold')['default'] PhBrainFill: typeof import('~icons/ph/brain-fill')['default'] - PhCaretLeftFill: typeof import('~icons/ph/caret-left-fill')['default'] - PhCaretRightFill: typeof import('~icons/ph/caret-right-fill')['default'] PhChatCenteredDots: typeof import('~icons/ph/chat-centered-dots')['default'] PhChats: typeof import('~icons/ph/chats')['default'] PhExportBold: typeof import('~icons/ph/export-bold')['default'] PhFileFill: typeof import('~icons/ph/file-fill')['default'] PhFiles: typeof import('~icons/ph/files')['default'] - PhFloppyDiskBold: typeof import('~icons/ph/floppy-disk-bold')['default'] - PhInfo: typeof import('~icons/ph/info')['default'] - PhLightbulbFilamentFill: typeof import('~icons/ph/lightbulb-filament-fill')['default'] PhListMagnifyingGlass: typeof import('~icons/ph/list-magnifying-glass')['default'] PhNut: typeof import('~icons/ph/nut')['default'] - PhPencilFill: typeof import('~icons/ph/pencil-fill')['default'] PhPlugFill: typeof import('~icons/ph/plug-fill')['default'] - PhPlus: typeof import('~icons/ph/plus')['default'] PhQuestionMark: typeof import('~icons/ph/question-mark')['default'] PhTextbox: typeof import('~icons/ph/textbox')['default'] PhToolbox: typeof import('~icons/ph/toolbox')['default'] - PhTrashFill: typeof import('~icons/ph/trash-fill')['default'] - PhUser: typeof import('~icons/ph/user')['default'] - PhUserFill: typeof import('~icons/ph/user-fill')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] SelectBox: typeof import('./src/components/SelectBox.vue')['default'] SidePanel: typeof import('./src/components/SidePanel.vue')['default'] TransitionChild: typeof import('@headlessui/vue')['TransitionChild'] TransitionRoot: typeof import('@headlessui/vue')['TransitionRoot'] - UseImage: typeof import('@vueuse/components')['UseImage'] UserDropdown: typeof import('./src/components/UserDropdown.vue')['default'] } } diff --git a/package.json b/package.json index 4841f20..237304d 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "animate.css": "^4.1.1", "apexcharts": "^3.54.1", "axios": "^1.7.7", - "ccat-api": "github:cheshire-cat-ai/api-client-ts#develop", + "ccat-api": "^0.11.3", "daisyui": "^4.12.13", "highlight.js": "^11.10.0", "jwt-decode": "^4.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8be9d51..526e045 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,8 +48,8 @@ importers: specifier: ^1.7.7 version: 1.7.7 ccat-api: - specifier: github:cheshire-cat-ai/api-client-ts#develop - version: https://codeload.github.com/cheshire-cat-ai/api-client-ts/tar.gz/0d9712fcb1249352f01e19758cdfe40eca114ff3 + specifier: ^0.11.3 + version: 0.11.3 daisyui: specifier: ^4.12.13 version: 4.12.13(postcss@8.4.47) @@ -993,9 +993,8 @@ packages: caniuse-lite@1.0.30001668: resolution: {integrity: sha512-nWLrdxqCdblixUO+27JtGJJE/txpJlyUy5YN1u53wLZkP0emYCo5zgS6QYft7VUYR42LGgi/S5hdLZTrnyIddw==} - ccat-api@https://codeload.github.com/cheshire-cat-ai/api-client-ts/tar.gz/0d9712fcb1249352f01e19758cdfe40eca114ff3: - resolution: {tarball: https://codeload.github.com/cheshire-cat-ai/api-client-ts/tar.gz/0d9712fcb1249352f01e19758cdfe40eca114ff3} - version: 0.11.2 + ccat-api@0.11.3: + resolution: {integrity: sha512-avn5zqJbA/6aiI71UhnvoTfxt3og8kWq8+HDhoznBOo9TNJcfZXFu8j+/H1WRBSh3FlkLGc99xjS1j523as8mw==} chai@5.1.1: resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==} @@ -3486,7 +3485,7 @@ snapshots: caniuse-lite@1.0.30001668: {} - ccat-api@https://codeload.github.com/cheshire-cat-ai/api-client-ts/tar.gz/0d9712fcb1249352f01e19758cdfe40eca114ff3: + ccat-api@0.11.3: dependencies: '@types/ws': 8.5.12 axios: 1.7.7 diff --git a/src/services/RabbitHoleService.ts b/src/services/RabbitHoleService.ts index d180be0..1579929 100644 --- a/src/services/RabbitHoleService.ts +++ b/src/services/RabbitHoleService.ts @@ -5,9 +5,12 @@ import { apiClient, tryRequest } from '@services/ApiService' * Meaning this service sends files to the backend. */ const RabbitHoleService = Object.freeze({ - sendFile: async (file: File) => { + sendFile: async (file: File, metadata?: Record) => { return await tryRequest( - apiClient?.api?.rabbitHole.uploadFile({ file }), + apiClient?.api?.rabbitHole.uploadFile({ + file, + metadata: JSON.stringify(metadata), + }), `File ${file.name} successfully sent down the rabbit hole!`, 'Unable to send the file to the rabbit hole!', 'Sending a file to the rabbit hole', diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index c636dd5..f2dc8b6 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -4,6 +4,7 @@ import { useMessages } from '@stores/useMessages' import { useMemory } from '@stores/useMemory' import ModalBox from '@components/ModalBox.vue' import { capitalize } from 'lodash' +import RabbitHoleService from '@services/RabbitHoleService' const route = useRoute() const messagesStore = useMessages() @@ -14,6 +15,51 @@ const userMessage = ref(''), insertedURL = ref(''), isScrollable = ref(false), isTwoLines = ref(false) + +/** + * File Upload w/ Metadata Management + * **/ + +const uploadFileDialog = useFileDialog() + +// User selects a file and then the Metadata Modal opens up +const selectedFiles = ref([]) +uploadFileDialog.onChange((files) => { + selectedFiles!.value = files + fileMetadata.value[0].value = selectedFiles.value[0].name + boxUploadFile.value?.toggleModal() +}) + +const fileMetadata = ref<{key: string, value: string}[]>([ + { key: 'source', value: 'filename.pdf'}, +]) + +const addMetadata = () => { + fileMetadata.value.push( + { key: '', value: '' } + ) +} +const removeMetadata = (index: number) => { + fileMetadata.value.splice(index, 1) +} + +const boxUploadFile = ref>() + +const dispatchFiles = async () => { + boxUploadFile.value?.toggleModal() + const json: Record = {}; + // turn metadata into Json + for (const md of fileMetadata.value) + json[md.key] = md.value + for (const file of selectedFiles.value) + await RabbitHoleService.sendFile(file, json) + uploadFileDialog.reset() +} + +/** + * End Custom Metadata Management + * */ + const boxUploadURL = ref>() const { textarea: textArea } = useTextareaAutosize({ @@ -123,6 +169,7 @@ useEventListener(document, 'scroll', () => { isScrollable.value = doc.scrollHeight > doc.clientHeight + doc.scrollTop }) + /** * Dispatches the inserted url to the RabbitHole service and closes the modal. */ @@ -281,7 +328,7 @@ const scrollToBottom = () => { + + +
+

File Upload

+

Selected file(s): {{ `${selectedFiles.length} ${selectedFiles.length === 1 ? 'file' : 'files'}` }}

+
  • + {{ file.name }} +
  • +

    Add metadata to file:

    +
    + + + + + +
    + + + +
    + +
    +