diff --git a/app/components/file-viewer.module.css b/app/components/file-viewer.module.css index 1d2d766f..57a04e4c 100644 --- a/app/components/file-viewer.module.css +++ b/app/components/file-viewer.module.css @@ -76,3 +76,24 @@ display: flex; flex-direction: column; } + +.dropzone { + border: 2px dashed #cccccc; + border-radius: 8px; + padding: 20px; + text-align: center; + cursor: pointer; + width: 100%; + max-width: 600px; + background-color: #fff; + transition: border-color 0.2s; +} + +.dropzone p { + margin: 0; + color: #666666; +} + +.dropzone:hover { + border-color: #888888; +} \ No newline at end of file diff --git a/app/components/file-viewer.tsx b/app/components/file-viewer.tsx index b91db891..62c6964c 100644 --- a/app/components/file-viewer.tsx +++ b/app/components/file-viewer.tsx @@ -1,4 +1,5 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useCallback } from "react"; +import { useDropzone } from "react-dropzone"; import styles from "./file-viewer.module.css"; const TrashIcon = () => ( @@ -42,17 +43,25 @@ const FileViewer = () => { method: "DELETE", body: JSON.stringify({ fileId }), }); + fetchFiles(); }; - const handleFileUpload = async (event) => { - const data = new FormData(); - if (event.target.files.length < 0) return; - data.append("file", event.target.files[0]); - await fetch("/api/assistants/files", { - method: "POST", - body: data, - }); - }; + const onDrop = useCallback(async (acceptedFiles) => { + for (const file of acceptedFiles) { + const data = new FormData(); + data.append("file", file); + await fetch("/api/assistants/files", { + method: "POST", + body: data, + }); + } + fetchFiles(); + }, []); + + const { getRootProps, getInputProps } = useDropzone({ + onDrop, + multiple: true, + }); return ( <div className={styles.fileViewer}> @@ -78,20 +87,13 @@ const FileViewer = () => { )} </div> <div className={styles.fileUploadContainer}> - <label htmlFor="file-upload" className={styles.fileUploadBtn}> - Attach files - </label> - <input - type="file" - id="file-upload" - name="file-upload" - className={styles.fileUploadInput} - multiple - onChange={handleFileUpload} - /> + <div {...getRootProps({ className: styles.dropzone })}> + <input {...getInputProps()} /> + <p>Drag your files here or click to upload</p> + </div> </div> </div> ); }; -export default FileViewer; +export default FileViewer; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 01c4e100..8e94d1a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "openai": "^4.46.0", "react": "^18", "react-dom": "^18", + "react-dropzone": "^14.2.3", "react-markdown": "^9.0.1" }, "devDependencies": { @@ -278,6 +279,14 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, + "node_modules/attr-accept": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz", + "integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==", + "engines": { + "node": ">=4" + } + }, "node_modules/bail": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", @@ -475,6 +484,17 @@ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, + "node_modules/file-selector": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.6.0.tgz", + "integrity": "sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw==", + "dependencies": { + "tslib": "^2.4.0" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", @@ -1343,6 +1363,14 @@ } } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/openai": { "version": "4.46.0", "resolved": "https://registry.npmjs.org/openai/-/openai-4.46.0.tgz", @@ -1425,6 +1453,16 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, "node_modules/property-information": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", @@ -1457,6 +1495,27 @@ "react": "^18.2.0" } }, + "node_modules/react-dropzone": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.2.3.tgz", + "integrity": "sha512-O3om8I+PkFKbxCukfIR3QAGftYXDZfOE2N1mr/7qebQJHs7U+/RSL/9xomJNpRg9kM5h9soQSdf0Gc7OHF5Fug==", + "dependencies": { + "attr-accept": "^2.2.2", + "file-selector": "^0.6.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">= 10.13" + }, + "peerDependencies": { + "react": ">= 16.8 || 18.0.0" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, "node_modules/react-markdown": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.0.1.tgz", diff --git a/package.json b/package.json index 59ab54f5..92a1f7d4 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "openai": "^4.46.0", "react": "^18", "react-dom": "^18", - "react-markdown": "^9.0.1" + "react-markdown": "^9.0.1", + "react-dropzone": "^14.2.3" }, "devDependencies": { "@types/node": "20.12.7",