From 5ddb122b8830010ef8c287a09b5b44fb88475c0d Mon Sep 17 00:00:00 2001 From: Rodrigo Verdiani Date: Sun, 14 Dec 2025 16:18:18 -0300 Subject: [PATCH] feat(manga): add import file button to manga page --- src/api/generated/api.schemas.ts | 1 + src/features/manga/ManualImportDialog.tsx | 149 ++++++++++++++++++++++ src/pages/Manga.tsx | 26 +++- 3 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 src/features/manga/ManualImportDialog.tsx diff --git a/src/api/generated/api.schemas.ts b/src/api/generated/api.schemas.ts index 2e198e7..237a493 100644 --- a/src/api/generated/api.schemas.ts +++ b/src/api/generated/api.schemas.ts @@ -150,6 +150,7 @@ export interface DefaultResponseDTOMangaDTO { export interface MangaDTO { id: number; + malId: number; /** @minLength 1 */ title: string; coverImageKey?: string; diff --git a/src/features/manga/ManualImportDialog.tsx b/src/features/manga/ManualImportDialog.tsx new file mode 100644 index 0000000..58bde0a --- /dev/null +++ b/src/features/manga/ManualImportDialog.tsx @@ -0,0 +1,149 @@ +import { useQueryClient } from "@tanstack/react-query"; +import { FileUp } from "lucide-react"; +import type React from "react"; +import { useState } from "react"; +import { toast } from "sonner"; +import { useImportMultipleFiles } from "@/api/generated/manga-import/manga-import.ts"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; + +interface ManualImportDialogProps { + malId: number; + mangaTitle: string; + dialogOpen: boolean; + onOpenChange: (open: boolean) => void; + queryKey: any; +} + +export const ManualImportDialog = ({ + dialogOpen, + onOpenChange, + malId, + mangaTitle, + queryKey, +}: ManualImportDialogProps) => { + const [dragActive, setDragActive] = useState(false); + const [files, setFiles] = useState(null); + + const queryClient = useQueryClient(); + + const { mutate, isPending } = useImportMultipleFiles({ + mutation: { + onSuccess: () => { + setFiles(null); + onOpenChange(false); + toast.success("Files imported successfully!"); + queryClient.invalidateQueries(queryKey); + }, + onError: () => toast.error("Failed to import files."), + }, + }); + + const handleFileImport = () => { + if (!files) { + alert("Please select one or more files to upload"); + return; + } + + mutate({ data: { malId: malId.toString(), files } }); + }; + + const handleDrag = (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + if (e.type === "dragenter" || e.type === "dragover") { + setDragActive(true); + } else if (e.type === "dragleave") { + setDragActive(false); + } + }; + + const handleDrop = (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + setDragActive(false); + + if (e.dataTransfer.files) { + setFiles(Array.from(e.dataTransfer.files)); + } + }; + + const handleFileSelect = (e: React.ChangeEvent) => { + if (e.target.files) { + setFiles(Array.from(e.target.files)); + } + }; + + return ( + + + + Upload Files + + Upload one or more files to {mangaTitle}. + + +
+ +
+ + +
+
+ + + + +
+
+ ); +}; diff --git a/src/pages/Manga.tsx b/src/pages/Manga.tsx index 08826a5..57739c4 100644 --- a/src/pages/Manga.tsx +++ b/src/pages/Manga.tsx @@ -7,6 +7,7 @@ import { Calendar, ChevronDown, Database, + FileUp, Heart, Star, } from "lucide-react"; @@ -35,6 +36,7 @@ import { } from "@/components/ui/collapsible.tsx"; import { useAuth } from "@/contexts/AuthContext.tsx"; import { MangaChapter } from "@/features/manga/MangaChapter.tsx"; +import { ManualImportDialog } from "@/features/manga/ManualImportDialog.tsx"; import { formatToTwoDigitsDateRange } from "@/utils/dateFormatter.ts"; const Manga = () => { @@ -45,6 +47,8 @@ const Manga = () => { const queryClient = useQueryClient(); + const [isUploadFilesDialogOpen, setIsUploadFilesDialogOpen] = useState(false); + const { data: mangaData, queryKey } = useGetManga(mangaId); const { mutate, isPending: fetchPending } = useFetchMangaChapters({ @@ -165,7 +169,19 @@ const Manga = () => {

MangaMochi

- +
+ {isAuthenticated && ( + + )} + +
@@ -425,6 +441,14 @@ const Manga = () => { + + ); };