frontend/components/failed-import-card.tsx

135 lines
3.7 KiB
TypeScript

"use client";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Card } from "@/components/ui/card";
import { useState } from "react";
import { ExternalLink, Trash2 } from "lucide-react";
import {
ImportReviewDTO,
useDeleteImportReview,
useResolveImportReview,
} from "@/api/mangamochi";
import { toast } from "sonner";
import { useQueryClient } from "@tanstack/react-query";
interface FailedImportCardProps {
failedImport: ImportReviewDTO;
queryKey: any;
}
export function FailedImportCard({
failedImport,
queryKey,
}: FailedImportCardProps) {
const queryClient = useQueryClient();
const [malId, setMalId] = useState("");
const {
mutate: mutateDeleteImportReview,
isPending: isPendingDeleteImportReview,
} = 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: failedImport.id, malId },
});
};
const handleRemove = () => {
mutateDeleteImportReview({ id: failedImport.id });
};
const importDate = new Date(failedImport.createdAt).toLocaleDateString();
return (
<Card className="p-4">
<div className="space-y-4">
<div className="flex items-start justify-between">
<div className="flex-1">
<h3 className="font-semibold text-foreground">
{failedImport.title}
</h3>
<p className="text-sm text-muted-foreground">
Provider:{" "}
<span className="capitalize">{failedImport.providerName}</span> {" "}
{importDate}
</p>
</div>
{failedImport.externalUrl && (
<a
href={failedImport.externalUrl}
target="_blank"
rel="noopener noreferrer"
className="ml-2 text-primary hover:underline"
>
<ExternalLink className="h-4 w-4" />
</a>
)}
</div>
<div className="rounded-md bg-destructive/10 p-3">
<p className="text-sm text-destructive">{failedImport.reason}</p>
</div>
<div className="space-y-3">
<div>
<label className="text-sm font-medium">MyAnimeList Manga ID</label>
<Input
placeholder="Enter MAL ID to match manually"
value={malId}
onChange={(e) => setMalId(e.target.value)}
className="mt-2"
type="number"
/>
</div>
<div className="flex gap-2">
<Button
onClick={handleResolve}
disabled={isPendingResolveImportReview || !malId.trim()}
className="flex-1"
>
{isPendingResolveImportReview ? "Resolving..." : "Resolve Import"}
</Button>
<Button
onClick={handleRemove}
variant="outline"
size="icon"
className="text-destructive hover:text-destructive bg-transparent"
>
<Trash2 className="h-4 w-4" />
</Button>
</div>
</div>
</div>
</Card>
);
}