Skip to content

Commit ba614c0

Browse files
committed
Start folder upload process
1 parent 7f9d6cf commit ba614c0

File tree

3 files changed

+177
-63
lines changed

3 files changed

+177
-63
lines changed

src/app/apiSlice.ts

+22
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,17 @@ interface AddImageRequest {
3939
};
4040
}
4141

42+
interface ImportImageFolderRequest {
43+
token: string;
44+
body: {
45+
folder: string;
46+
author: string;
47+
author_url?: string;
48+
category: string;
49+
tags: TagMap;
50+
};
51+
}
52+
4253
interface AddImageResponse {
4354
id: number;
4455
url: string;
@@ -178,6 +189,16 @@ export const api = createApi({
178189
body,
179190
}),
180191
}),
192+
importImageFolder: build.mutation<OkResponse, ImportImageFolderRequest>({
193+
query: ({ token, body }) => ({
194+
url: `image/local`,
195+
method: "POST",
196+
headers: {
197+
Authorization: `Bearer ${token}`,
198+
},
199+
body,
200+
}),
201+
}),
181202
deleteUnusedImages: build.mutation<OkResponse, RequestWithToken>({
182203
query: ({ token }) => ({
183204
url: `image/unused`,
@@ -294,6 +315,7 @@ export const {
294315
useGetCategoryQuery,
295316
useReorderCategoriesMutation,
296317
useAddImageMutation,
318+
useImportImageFolderMutation,
297319
useDeleteUnusedImagesMutation,
298320
useAddImageToCategoryMutation,
299321
useDeleteImageFromCategoryMutation,

src/app/utilities.ts

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { SerializedError } from "@reduxjs/toolkit";
2+
import { FetchBaseQueryError } from "@reduxjs/toolkit/query";
3+
4+
interface DrawRefAPIError {
5+
error: string;
6+
}
7+
8+
export function parseError(input: FetchBaseQueryError | SerializedError | undefined): string | undefined {
9+
const fberror = input ? ((input as FetchBaseQueryError).data as DrawRefAPIError).error || String(input) : undefined;
10+
return fberror;
11+
}

src/routes/AdminEditCategory.tsx

+144-63
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ import {
1515
useEditCategoryMutation,
1616
useGetCategoryImagesQuery,
1717
useDeleteImageFromCategoryMutation,
18+
useImportImageFolderMutation,
1819
} from "../app/apiSlice";
1920
import { useUploadImageMutation } from "../app/uploadSlice";
2021
import NotFound from "./NotFound";
2122
import { TagMap } from "../types/drawref";
23+
import { parseError } from "../app/utilities";
2224

2325
type Params = {
2426
categoryId: string;
@@ -39,19 +41,25 @@ function AdminEditCategory() {
3941
error: getCategoryImagesError,
4042
} = useGetCategoryImagesQuery({ category: categoryId, page: 0 });
4143

44+
const [typeOfUpload, setTypeOfUpload] = useState("upload");
4245
const [uploadTags, setUploadTags] = useState<TagMap>({});
4346
const [uploadAuthorName, setUploadAuthorName] = useState("");
4447
const [uploadAuthorUrl, setUploadAuthorUrl] = useState("");
48+
const [importFolder, setImportFolder] = useState("");
4549
const [uploadFiles, setUploadFiles] = useState<File[]>([]);
4650

4751
const [editCategory, { isLoading: isEditingCategory, error: categoryError }] = useEditCategoryMutation();
4852
const [uploadImage, { isLoading: isUploadingImage, error: uploadImageError }] = useUploadImageMutation();
4953
const [addImage, { isLoading: isAddingImage, error: addImageError }] = useAddImageMutation();
5054
const [addImageToCategory, { isLoading: isAddingImageToCategory, error: addImageToCategoryError }] =
5155
useAddImageToCategoryMutation();
56+
const [importImageFolder, { isLoading: isImportingImageFolder, error: importImageFolderError }] =
57+
useImportImageFolderMutation();
5258
const [deleteImageFromCategory] = useDeleteImageFromCategoryMutation();
5359

54-
const errorToShow = categoryError ? "Couldn't edit category" : "";
60+
const isUploadingImages = isUploadingImage || isAddingImage || isAddingImageToCategory || isImportingImageFolder;
61+
62+
const categoryErrorToShow = categoryError ? `Couldn't edit category: ${parseError(categoryError)}` : "";
5563

5664
return (
5765
<>
@@ -67,7 +75,7 @@ function AdminEditCategory() {
6775
coverId={categoryData.cover_id}
6876
coverUrl={categoryData.cover}
6977
tags={categoryData.tags}
70-
error={errorToShow}
78+
error={categoryErrorToShow}
7179
onSubmit={async (data) => {
7280
if (data.name !== "") {
7381
console.log("Editing category:", data);
@@ -108,71 +116,144 @@ function AdminEditCategory() {
108116
onChange={(e) => setUploadAuthorUrl(e.target.value)}
109117
></input>
110118
</div>
111-
<input
112-
type="file"
113-
id="coverImage"
114-
multiple
115-
onChange={async (e) => {
116-
// upload
117-
setUploadFiles((state) => {
118-
if (e.target.files === null) {
119-
return state;
120-
}
121-
return state.concat([...e.target.files]);
122-
});
123-
124-
if (e.target.files === null) {
125-
return;
126-
}
127-
128-
for (const file of [...e.target.files]) {
129-
try {
130-
const fData = new FormData();
131-
fData.append("image", file);
132-
const uploadResult = await uploadImage({ token: user.token, body: fData }).unwrap();
133-
134-
if (uploadResult.path) {
135-
// add image
136-
const addResult = await addImage({
137-
token: user.token,
138-
body: {
139-
path: uploadResult.path,
140-
// author name and url
141-
author: uploadAuthorName,
142-
author_url: uploadAuthorUrl,
143-
},
144-
});
145-
if ("error" in addResult) {
146-
throw addResult.error;
119+
<div className="flex gap-3">
120+
<label htmlFor="typeOfUpload" className="text-lg font-medium">
121+
Image source
122+
</label>
123+
<select
124+
id="typeOfUpload"
125+
className="col-span-2 rounded bg-primary-100 px-1.5 py-1.5 text-sm text-defaultText"
126+
value={typeOfUpload}
127+
onChange={(e) => setTypeOfUpload(e.target.value)}
128+
disabled={isUploadingImages}
129+
>
130+
<option value="upload">Upload</option>
131+
<option value="local">Local folder</option>
132+
</select>
133+
</div>
134+
{typeOfUpload == "local" && (
135+
<div className="box-border flex max-w-full flex-col gap-3 border-[5px] border-primary-700 bg-primary-900 px-3 py-2">
136+
<label htmlFor="localUploadFolder" className="text-lg font-medium">
137+
Folder on the server:
138+
</label>
139+
<input
140+
id="localUploadFolder"
141+
className="col-span-3 -mt-2 rounded px-2 py-1 text-defaultText"
142+
placeholder="/drawref/images/1"
143+
value={importFolder}
144+
onChange={(e) => setImportFolder(e.target.value)}
145+
></input>
146+
<button
147+
type="submit"
148+
className="mx-auto mt-1 rounded bg-secondary-500 px-5 py-1.5 text-sm text-white shadow"
149+
disabled={false}
150+
onClick={() => {
151+
console.log("UPLOADING!");
152+
importImageFolder({
153+
token: user.token,
154+
body: {
155+
folder: importFolder,
156+
author: uploadAuthorName,
157+
author_url: uploadAuthorUrl,
158+
category: categoryId,
159+
tags: uploadTags,
160+
},
161+
});
162+
}}
163+
>
164+
Import
165+
</button>
166+
</div>
167+
)}
168+
{typeOfUpload == "upload" && (
169+
<div className="box-border flex max-w-full flex-col gap-3 border-[5px] border-primary-700 bg-primary-900 px-3 py-2">
170+
<input
171+
type="file"
172+
id="coverImage"
173+
multiple
174+
onChange={async (e) => {
175+
// upload
176+
setUploadFiles((state) => {
177+
if (e.target.files === null) {
178+
return state;
147179
}
180+
return state.concat([...e.target.files]);
181+
});
148182

149-
// add image to category
150-
const addToCatResult = await addImageToCategory({
151-
category: categoryId,
152-
image: addResult.data.id,
153-
token: user.token,
154-
body: {
155-
tags: uploadTags,
156-
},
157-
});
158-
if ("error" in addToCatResult) {
159-
throw addToCatResult.error;
183+
if (e.target.files === null) {
184+
return;
185+
}
186+
187+
for (const file of [...e.target.files]) {
188+
try {
189+
const fData = new FormData();
190+
fData.append("image", file);
191+
const uploadResult = await uploadImage({ token: user.token, body: fData }).unwrap();
192+
193+
if (uploadResult.path) {
194+
// add image
195+
const addResult = await addImage({
196+
token: user.token,
197+
body: {
198+
path: uploadResult.path,
199+
// author name and url
200+
author: uploadAuthorName,
201+
author_url: uploadAuthorUrl,
202+
},
203+
});
204+
if ("error" in addResult) {
205+
throw addResult.error;
206+
}
207+
208+
// add image to category
209+
const addToCatResult = await addImageToCategory({
210+
category: categoryId,
211+
image: addResult.data.id,
212+
token: user.token,
213+
body: {
214+
tags: uploadTags,
215+
},
216+
});
217+
if ("error" in addToCatResult) {
218+
throw addToCatResult.error;
219+
}
220+
}
221+
} catch (err) {
222+
console.error(err);
223+
return;
160224
}
225+
226+
// remove this file
227+
setUploadFiles((state) => {
228+
state = state.filter((f) => f !== file);
229+
return state;
230+
});
161231
}
162-
} catch (err) {
163-
console.error(err);
164-
return;
165-
}
166-
167-
// remove this file
168-
setUploadFiles((state) => {
169-
state = state.filter((f) => f !== file);
170-
return state;
171-
});
172-
}
173-
}}
174-
></input>
175-
<p>{uploadFiles.length}</p>
232+
}}
233+
></input>
234+
<p>Images remaining: {uploadFiles.length}</p>
235+
</div>
236+
)}
237+
{uploadImageError && (
238+
<span className="mx-auto -mb-2 w-auto bg-red-600 px-3 py-1 text-sm">
239+
Coule not upload image: {parseError(uploadImageError)}
240+
</span>
241+
)}
242+
{addImageError && (
243+
<span className="mx-auto -mb-2 w-auto bg-red-600 px-3 py-1 text-sm">
244+
Could not add image: {parseError(addImageError)}
245+
</span>
246+
)}
247+
{addImageToCategoryError && (
248+
<span className="mx-auto -mb-2 w-auto bg-red-600 px-3 py-1 text-sm">
249+
Could not add image to category: {parseError(addImageToCategoryError)}
250+
</span>
251+
)}
252+
{importImageFolderError && (
253+
<span className="mx-auto -mb-2 w-auto bg-red-600 px-3 py-1 text-sm">
254+
Could not import image folder: {parseError(importImageFolderError)}
255+
</span>
256+
)}
176257
</div>
177258
)}
178259
{!isLoadingCategoryImages && (

0 commit comments

Comments
 (0)