135 lines
3.7 KiB
TypeScript
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>
|
|
);
|
|
}
|