frontend/src/features/home/components/ProviderImportDialog.tsx

191 lines
4.7 KiB
TypeScript

import { zodResolver } from "@hookform/resolvers/zod";
import { useCallback } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
import { useImportFromProvider } from "@/api/generated/manga-import/manga-import.ts";
import { useGetProviders } from "@/api/generated/provider/provider.ts";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog.tsx";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form.tsx";
import { Input } from "@/components/ui/input";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select.tsx";
interface ProviderImportDialogProps {
dialogOpen: boolean;
onDialogOpenChange: (open: boolean) => void;
}
export const ProviderImportDialog = ({
dialogOpen,
onDialogOpenChange,
}: ProviderImportDialogProps) => {
const formSchema = z.object({
value: z.string().min(1, "Please enter an ID or URL."),
providerId: z.string(),
myAnimeListId: z.string().optional(),
});
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
value: "",
providerId: "",
myAnimeListId: undefined,
},
});
const { data: providerData, isFetching: isFetchingProviders } =
useGetProviders({ manualImport: true });
const { mutate: importFromProvider, isPending: isPendingImportFromProvider } =
useImportFromProvider({
mutation: {
onSuccess: () => {
form.reset();
onDialogOpenChange(false);
toast.success("Manga imported successfully!");
},
},
});
const handleSubmit = useCallback(
(data: z.output<typeof formSchema>) => {
importFromProvider({
providerId: Number(data.providerId),
data: { id: data.value, metadataId: data.myAnimeListId },
});
},
[importFromProvider],
);
return (
<Dialog open={dialogOpen} onOpenChange={onDialogOpenChange}>
<DialogContent>
<DialogHeader>
<DialogTitle>Import from Provider</DialogTitle>
<DialogDescription>
Enter a Provider manga URL or ID to import it to your library.
</DialogDescription>
</DialogHeader>
<Form {...form}>
<form
id="importForm"
onSubmit={form.handleSubmit(handleSubmit)}
className="space-y-4"
>
<FormField
control={form.control}
name="providerId"
render={({ field }) => (
<FormItem>
<FormLabel>Provider</FormLabel>
<FormControl>
<Select
onValueChange={field.onChange}
defaultValue={field.value}
{...field}
disabled={isFetchingProviders}
required
>
<SelectTrigger className="w-[180px]">
<SelectValue placeholder="Select a provider" />
</SelectTrigger>
<SelectContent>
{providerData?.data?.providers?.map((provider) => (
<SelectItem
key={provider.id}
value={provider.id.toString()}
>
{provider.name}
</SelectItem>
))}
</SelectContent>
</Select>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="value"
render={({ field }) => (
<FormItem>
<FormLabel>URL or ID</FormLabel>
<FormControl>
<Input
placeholder="e.g., https://mangadex.org/title/..."
disabled={isPendingImportFromProvider}
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="myAnimeListId"
render={({ field }) => (
<FormItem>
<FormLabel>MyAnimeList ID (Optional)</FormLabel>
<FormControl>
<Input
placeholder="e.g., 13 (for One Piece)"
disabled={isPendingImportFromProvider}
{...field}
/>
</FormControl>
<FormDescription>
Optionally link this manga to a MyAnimeList entry for better
precision on metadata fetching.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
</form>
</Form>
<DialogFooter>
<Button
type="button"
variant="outline"
onClick={() => onDialogOpenChange(false)}
>
Cancel
</Button>
<Button
type="submit"
disabled={isPendingImportFromProvider || isFetchingProviders}
form="importForm"
>
Import
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
};