Twan07 commited on
Commit
0195b42
·
verified ·
1 Parent(s): 15b05cc

Update frontend/hooks/useFileUpload.ts

Browse files
Files changed (1) hide show
  1. frontend/hooks/useFileUpload.ts +79 -81
frontend/hooks/useFileUpload.ts CHANGED
@@ -1,11 +1,12 @@
1
- import { useState, useCallback } from "react";
2
 
3
  /* ================= TYPES ================= */
4
 
5
- export interface UploadItem {
6
  id: string;
7
  file: File;
8
- progress: number; // 0 → 100
 
9
  status: "idle" | "uploading" | "done" | "error";
10
  url?: string;
11
  error?: string;
@@ -14,111 +15,108 @@ export interface UploadItem {
14
  /* ================= HOOK ================= */
15
 
16
  export function useFileUpload() {
17
- const [uploads, setUploads] = useState<UploadItem[]>([]);
 
18
 
19
- /* ---------- ADD FILE ---------- */
20
- const addFiles = useCallback((files: FileList | File[]) => {
21
- const list = Array.from(files).map((file) => ({
22
  id: crypto.randomUUID(),
23
  file,
 
24
  progress: 0,
25
- status: "idle" as const,
26
  }));
27
 
28
- setUploads((prev) => [...prev, ...list]);
29
  }, []);
30
 
31
- /* ---------- UPLOAD SINGLE FILE ---------- */
32
- const uploadFile = useCallback(async (item: UploadItem) => {
33
- const form = new FormData();
34
- form.append("file", item.file);
35
 
36
- setUploads((prev) =>
37
- prev.map((u) =>
38
- u.id === item.id
39
- ? { ...u, status: "uploading", progress: 0 }
40
- : u
41
  )
42
  );
 
43
 
44
- try {
 
 
 
 
 
 
45
  const xhr = new XMLHttpRequest();
46
  xhr.open("POST", "/api/upload");
47
 
48
  xhr.upload.onprogress = (e) => {
49
  if (!e.lengthComputable) return;
50
  const percent = Math.round((e.loaded / e.total) * 100);
51
- setUploads((prev) =>
52
- prev.map((u) =>
53
- u.id === item.id ? { ...u, progress: percent } : u
 
 
 
54
  )
55
  );
56
  };
57
 
58
- const result: any = await new Promise((resolve, reject) => {
59
- xhr.onload = () => {
60
- if (xhr.status >= 200 && xhr.status < 300) {
61
- resolve(JSON.parse(xhr.responseText));
62
- } else {
63
- reject(new Error("Upload failed"));
64
- }
65
- };
66
- xhr.onerror = () => reject(new Error("Network error"));
67
- xhr.send(form);
68
- });
69
-
70
- setUploads((prev) =>
71
- prev.map((u) =>
72
- u.id === item.id
73
- ? {
74
- ...u,
75
- status: "done",
76
- progress: 100,
77
- url: result.url,
78
- }
79
- : u
80
- )
81
- );
82
- } catch (err: any) {
83
- setUploads((prev) =>
84
- prev.map((u) =>
85
- u.id === item.id
86
- ? {
87
- ...u,
88
- status: "error",
89
- error: err.message || "Upload error",
90
- }
91
- : u
92
- )
93
- );
94
- }
95
  }, []);
96
 
97
- /* ---------- UPLOAD ALL ---------- */
98
- const uploadAll = useCallback(async () => {
99
- for (const item of uploads) {
100
- if (item.status === "idle") {
101
- await uploadFile(item);
 
 
 
 
 
102
  }
 
 
103
  }
104
- }, [uploads, uploadFile]);
105
-
106
- /* ---------- REMOVE FILE ---------- */
107
- const remove = useCallback((id: string) => {
108
- setUploads((prev) => prev.filter((u) => u.id !== id));
109
- }, []);
110
-
111
- /* ---------- RESET ---------- */
112
- const reset = useCallback(() => {
113
- setUploads([]);
114
- }, []);
115
 
 
116
  return {
117
- uploads,
 
118
  addFiles,
119
- uploadFile,
120
- uploadAll,
121
- remove,
122
- reset,
123
  };
124
- }
 
1
+ import { useCallback, useMemo, useState } from "react";
2
 
3
  /* ================= TYPES ================= */
4
 
5
+ export interface FileItem {
6
  id: string;
7
  file: File;
8
+ path: string;
9
+ progress: number;
10
  status: "idle" | "uploading" | "done" | "error";
11
  url?: string;
12
  error?: string;
 
15
  /* ================= HOOK ================= */
16
 
17
  export function useFileUpload() {
18
+ const [files, setFiles] = useState<FileItem[]>([]);
19
+ const [isUploading, setIsUploading] = useState(false);
20
 
21
+ /* ---------- ADD FILES ---------- */
22
+ const addFiles = useCallback((input: FileList | File[]) => {
23
+ const list = Array.from(input).map<FileItem>((file) => ({
24
  id: crypto.randomUUID(),
25
  file,
26
+ path: file.name, // mặc định
27
  progress: 0,
28
+ status: "idle",
29
  }));
30
 
31
+ setFiles((prev) => [...prev, ...list]);
32
  }, []);
33
 
34
+ /* ---------- REMOVE FILE ---------- */
35
+ const removeFile = useCallback((id: string) => {
36
+ setFiles((prev) => prev.filter((f) => f.id !== id));
37
+ }, []);
38
 
39
+ /* ---------- UPDATE PATH ---------- */
40
+ const updateFilePath = useCallback((id: string, path: string) => {
41
+ setFiles((prev) =>
42
+ prev.map((f) =>
43
+ f.id === id ? { ...f, path } : f
44
  )
45
  );
46
+ }, []);
47
 
48
+ /* ---------- UPLOAD SINGLE ---------- */
49
+ const uploadOne = useCallback(async (item: FileItem) => {
50
+ const form = new FormData();
51
+ form.append("file", item.file);
52
+ form.append("path", item.path);
53
+
54
+ return new Promise<void>((resolve, reject) => {
55
  const xhr = new XMLHttpRequest();
56
  xhr.open("POST", "/api/upload");
57
 
58
  xhr.upload.onprogress = (e) => {
59
  if (!e.lengthComputable) return;
60
  const percent = Math.round((e.loaded / e.total) * 100);
61
+
62
+ setFiles((prev) =>
63
+ prev.map((f) =>
64
+ f.id === item.id
65
+ ? { ...f, progress: percent, status: "uploading" }
66
+ : f
67
  )
68
  );
69
  };
70
 
71
+ xhr.onload = () => {
72
+ if (xhr.status >= 200 && xhr.status < 300) {
73
+ const res = JSON.parse(xhr.responseText);
74
+ setFiles((prev) =>
75
+ prev.map((f) =>
76
+ f.id === item.id
77
+ ? {
78
+ ...f,
79
+ progress: 100,
80
+ status: "done",
81
+ url: res.url,
82
+ }
83
+ : f
84
+ )
85
+ );
86
+ resolve();
87
+ } else {
88
+ reject(new Error("Upload failed"));
89
+ }
90
+ };
91
+
92
+ xhr.onerror = () => reject(new Error("Network error"));
93
+ xhr.send(form);
94
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  }, []);
96
 
97
+ /* ---------- START UPLOAD (App.tsx dùng) ---------- */
98
+ const startUpload = useCallback(async () => {
99
+ if (isUploading) return;
100
+ setIsUploading(true);
101
+
102
+ try {
103
+ for (const f of files) {
104
+ if (f.status === "idle") {
105
+ await uploadOne(f);
106
+ }
107
  }
108
+ } finally {
109
+ setIsUploading(false);
110
  }
111
+ }, [files, uploadOne, isUploading]);
 
 
 
 
 
 
 
 
 
 
112
 
113
+ /* ---------- RETURN (KHỚP App.tsx) ---------- */
114
  return {
115
+ files, // ✅ App.tsx dùng
116
+ isUploading, // ✅ App.tsx dùng
117
  addFiles,
118
+ removeFile, // ✅ App.tsx dùng
119
+ updateFilePath, // ✅ App.tsx dùng
120
+ startUpload, // ✅ App.tsx dùng
 
121
  };
122
+ }