diff --git a/src/features/import-review/ImportReviewCard.tsx b/src/features/import-review/ImportReviewCard.tsx new file mode 100644 index 0000000..6fd1ad3 --- /dev/null +++ b/src/features/import-review/ImportReviewCard.tsx @@ -0,0 +1,128 @@ +import { useQueryClient } from "@tanstack/react-query"; +import { ExternalLink, Trash2 } from "lucide-react"; +import { useState } from "react"; +import { toast } from "sonner"; +import type { ImportReviewDTO } from "@/api/generated/api.schemas.ts"; +import { + useDeleteImportReview, + useResolveImportReview, +} from "@/api/generated/manga-import-review/manga-import-review.ts"; +import { Button } from "@/components/ui/button"; +import { Card } from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; + +interface ImportReviewCardProps { + importReview: ImportReviewDTO; + queryKey: any; +} + +export function ImportReviewCard({ + importReview, + queryKey, +}: ImportReviewCardProps) { + const queryClient = useQueryClient(); + + const [malId, setMalId] = useState(""); + + const { mutate: mutateDeleteImportReview } = useDeleteImportReview({ + mutation: { + onSuccess: () => { + queryClient.invalidateQueries({ queryKey }); + + toast.success("Import review removed successfully"); + }, + }, + }); + + const { + mutate: mutateResolveImportReview, + isPending: isPendingResolveImportReview, + } = useResolveImportReview({ + mutation: { + onSuccess: () => { + queryClient.invalidateQueries({ queryKey }); + toast.success("Import review resolved successfully"); + }, + }, + }); + + const handleResolve = () => { + if (!malId.trim()) { + alert("Please enter a MyAnimeList ID"); + return; + } + + mutateResolveImportReview({ + params: { importReviewId: importReview.id, malId }, + }); + }; + + const handleRemove = () => { + mutateDeleteImportReview({ id: importReview.id }); + }; + + const importDate = new Date(importReview.createdAt).toLocaleDateString(); + + return ( + +
+
+
+

+ {importReview.title} +

+

+ Provider:{" "} + {importReview.providerName} •{" "} + {importDate} +

+
+ {importReview.externalUrl && ( + + + + )} +
+ +
+

{importReview.reason}

+
+ +
+
+ + setMalId(e.target.value)} + className="mt-2" + /> +
+ +
+ + +
+
+
+
+ ); +} diff --git a/src/pages/ImportReview.tsx b/src/pages/ImportReview.tsx new file mode 100644 index 0000000..377e85b --- /dev/null +++ b/src/pages/ImportReview.tsx @@ -0,0 +1,70 @@ +"use client"; + +import { AlertCircle } from "lucide-react"; +import { useEffect } from "react"; +import { useNavigate } from "react-router"; +import { useGetImportReviews } from "@/api/generated/manga-import-review/manga-import-review.ts"; +import { Card } from "@/components/ui/card"; +import { useAuth } from "@/contexts/AuthContext.tsx"; +import { ImportReviewCard } from "@/features/import-review/ImportReviewCard.tsx"; + +export default function ImportReviewPage() { + const navigate = useNavigate(); + const { user, isAuthenticated } = useAuth(); + + const { data: importReviewData, queryKey } = useGetImportReviews(); + + useEffect(() => { + if (!user) { + return; + } + + if (!isAuthenticated) { + navigate("/login"); + } + }, [isAuthenticated, navigate, user]); + + if (!user) { + return null; + } + + return ( +
+
+
+

Import Review

+

+ Review and resolve manga imports by manually matching them with + MyAnimeList entries. +

+
+ + {!importReviewData?.data || importReviewData.data.length === 0 ? ( + + +

+ No Imports to Review +

+

+ All your imports have been processed successfully! +

+
+ ) : ( +
+
+ {importReviewData.data.length} import + {importReviewData.data.length !== 1 ? "s" : ""} to review +
+ {importReviewData.data.map((importReview) => ( + + ))} +
+ )} +
+
+ ); +} diff --git a/src/pages/Router.tsx b/src/pages/Router.tsx index 643410d..2e63c02 100644 --- a/src/pages/Router.tsx +++ b/src/pages/Router.tsx @@ -3,6 +3,7 @@ import { createBrowserRouter } from "react-router"; import { AppLayout } from "@/components/Layout/AppLayout.tsx"; const Home = lazy(() => import("./Home.tsx")); +const ImportReview = lazy(() => import("./ImportReview.tsx")); const Manga = lazy(() => import("./Manga.tsx")); const Chapter = lazy(() => import("./Chapter.tsx")); const Login = lazy(() => import("./Login.tsx")); @@ -28,6 +29,10 @@ export const Router = createBrowserRouter([ path: "/register", element: , }, + { + path: "/import-review", + element: , + }, { path: "/manga", children: [