Skip to content

Commit 0fe47fe

Browse files
feat: upload multiple files (#116)
* feat: add property 'multiple' to file input * feat: sends multiple documents or images to api * fix: show icon for each file being uploaded * fix: refresh page after uploading all files * chore: add semicolons * fix: cards overflow * chore: add EOF * fix: prevent file name overflow in card Co-authored-by: Kevin Nielsen <keveran@gmail.com> * chore: fix indentation --------- Co-authored-by: Kevin Nielsen <keveran@gmail.com>
1 parent e8e97aa commit 0fe47fe

File tree

5 files changed

+89
-51
lines changed

5 files changed

+89
-51
lines changed

ui/src/components/docs-input.tsx

+15-17
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,35 @@
11
import { useState } from "react";
2-
import { FileText } from "lucide-react";
2+
import UploadPreview from "./upload-preview";
33

44
const DocsInput: React.FC<{
55
fileRef: React.RefObject<HTMLInputElement>;
66
}> = ({ fileRef }) => {
7-
const [fileName, setFileName] = useState<undefined | string>(undefined);
7+
const [fileNames, setFileNames] = useState<string[]>([]);
88

9-
const getFileName = () => {
9+
const getFileNames = () => {
1010
if (fileRef.current?.files != null) {
11-
setFileName(fileRef.current.files[0].name);
11+
let names = [];
12+
13+
for (let file of fileRef.current.files) {
14+
names.push(file.name);
15+
}
16+
17+
setFileNames(names);
1218
}
1319
};
1420

1521
return (
16-
<div className="my-8 flex flex-col justify-center items-center h-full">
17-
<div className="w-full h-full mb-4 flex justify-center items-center">
18-
{fileName ? (
19-
<div className="border rounded-lg w-64 min-h-[264px] max-w-[256px] flex flex-col overflow-hidden justify-center items-center">
20-
<FileText size="128" />
21-
{fileName}
22-
</div>
23-
) : (
24-
<div className="border rounded-lg w-64 min-h-[264px] max-w-[256px] flex justify-center items-center">
25-
<FileText size="128" />
26-
</div>
27-
)}
22+
<div className="my-8 flex max-h-[691px] flex-col justify-center items-center h-full">
23+
<div className="w-full h-full mb-4 flex justify-center items-center overflow-hidden">
24+
<UploadPreview fileNames={fileNames} type="docs"/>
2825
</div>
2926
<label htmlFor="document" className="flex flex-col">
3027
Select Document
3128
<input
32-
onChange={getFileName}
29+
onChange={getFileNames}
3330
type="file"
3431
accept="text/plain, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.openxmlformats-officedocument.presentationml.presentation, application/pdf, application/rtf, application/x-freearc"
32+
multiple
3533
name="document"
3634
id="document"
3735
aria-label="Select document"

ui/src/components/image-input.tsx

+15-16
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,36 @@
1-
import { FileImage } from "lucide-react";
21
import { useState } from "react";
2+
import UploadPreview from "./upload-preview";
33

44
const ImageInput: React.FC<{
55
fileRef: React.RefObject<HTMLInputElement>;
66
}> = ({ fileRef }) => {
7-
const [fileName, setFileName] = useState<undefined | string>(undefined);
7+
const [fileNames, setFileNames] = useState<string[]>([]);
88

9-
const getFileName = () => {
9+
const getFileNames = () => {
1010
if (fileRef.current?.files != null) {
11-
setFileName(fileRef.current.files[0].name);
11+
let names = [];
12+
13+
for (let file of fileRef.current.files) {
14+
names.push(file.name);
15+
}
16+
17+
setFileNames(names);
1218
}
1319
};
1420

21+
1522
return (
16-
<div className="my-8 flex flex-col justify-center items-center h-full">
23+
<div className="my-8 max-h-[691px] flex flex-col justify-center items-center h-full">
1724
<div className="w-full h-full mb-4 flex justify-center items-center">
18-
{fileName ? (
19-
<div className="border rounded-lg w-64 min-h-[264px] max-w-[256px] flex flex-col justify-center overflow-hidden items-center">
20-
<FileImage size="128" />
21-
{fileName}
22-
</div>
23-
) : (
24-
<div className="border rounded-lg w-64 min-h-[264px] max-w-[256px] flex justify-center items-center">
25-
<FileImage size="128" />
26-
</div>
27-
)}
25+
<UploadPreview fileNames={fileNames} type="images"/>
2826
</div>
2927
<label htmlFor="image" className="flex flex-col">
3028
Select Image
3129
<input
32-
onChange={getFileName}
30+
onChange={getFileNames}
3331
type="file"
3432
accept="image/jpeg, image/png, image/jpg, image/webp, image/gif, image/bmp"
33+
multiple
3534
name="image"
3635
id="image"
3736
aria-label="Select image"

ui/src/components/upload-preview.tsx

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { FileText } from "lucide-react";
2+
import { FileImage } from "lucide-react";
3+
4+
const UploadPreview = ({fileNames, type}: {fileNames: string[], type: string}) => {
5+
if (fileNames.length >= 7) {
6+
return (
7+
<ul className="list-columns">
8+
{fileNames.map(fileName => (
9+
<li>{fileName}</li>
10+
))}
11+
</ul>
12+
);
13+
} else if (fileNames.length > 0) {
14+
return (
15+
<section className="flex flex-row overflow-hidden">
16+
{fileNames.map(fileName => (
17+
<div className="border rounded-lg w-64 min-h-[264px] max-w-[256px] flex flex-col overflow-hidden justify-center items-center">
18+
{type == "docs" ? <FileText size="128" /> : <FileImage size="128" />}
19+
<p className="w-full px-2 truncate text-center">{fileName}</p>
20+
</div>
21+
))}
22+
</section>
23+
);
24+
} else {
25+
return (
26+
<div className="border rounded-lg w-64 min-h-[264px] max-w-[256px] flex justify-center items-center">
27+
{type == "docs" ? <FileText size="128" /> : <FileImage size="128" />}
28+
</div>
29+
);
30+
}
31+
};
32+
33+
export default UploadPreview;

ui/src/components/upload.tsx

+21-17
Original file line numberDiff line numberDiff line change
@@ -23,29 +23,33 @@ const Upload = () => {
2323

2424
const file = useRef<HTMLInputElement>(null);
2525

26-
const uploadFile = () => {
26+
const uploadFile = async () => {
2727
const files = file.current?.files;
2828

2929
if (files !== null && files !== undefined) {
3030
toast.loading("Uploading...");
31-
const toUpload = files[0];
32-
const form = new FormData();
3331
const type = tab === "docs" ? "doc" : "image";
34-
form.append(type, toUpload);
35-
axios
36-
.post<{ url: string }>(`/api/cdn/upload/${type}`, form)
37-
.then((res) => {
38-
if (res.status === 200) {
32+
33+
for (let file of files) {
34+
const form = new FormData();
35+
form.append(type, file);
36+
37+
await axios
38+
.post<{ url: string }>(`/api/cdn/upload/${type}`, form)
39+
.then((res) => {
40+
if (res.status === 200) {
41+
toast.dismiss();
42+
toast.success("Successfully uploaded file!");
43+
getSize(setSize, setLoading);
44+
}
45+
})
46+
.catch((err: Error) => {
3947
toast.dismiss();
40-
toast.success("Successfully uploaded file!");
41-
getSize(setSize, setLoading);
42-
location.reload();
43-
}
44-
})
45-
.catch((err: Error) => {
46-
toast.dismiss();
47-
toast.error(err.message);
48-
});
48+
toast.error(err.message);
49+
});
50+
}
51+
52+
location.reload();
4953
}
5054
};
5155

ui/src/index.css

+5-1
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,8 @@
9999
/* Show the tooltip text when you mouse over the tooltip container */
100100
.tooltip:hover .tooltiptext {
101101
visibility: visible;
102-
}
102+
}
103+
104+
.list-columns {
105+
columns: 4;
106+
}

0 commit comments

Comments
 (0)