87 lines
3.9 KiB
TypeScript
87 lines
3.9 KiB
TypeScript
"use client";
|
|
|
|
import { useForm } from "react-hook-form";
|
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
import { assetSchema, type AssetFormValues } from "@/lib/validations";
|
|
import type { Resolver } from "react-hook-form";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Textarea } from "@/components/ui/textarea";
|
|
import { Label } from "@/components/ui/label";
|
|
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
|
import { Loader2 } from "lucide-react";
|
|
import { ASSET_TYPES } from "@/lib/constants";
|
|
|
|
interface Props {
|
|
open: boolean;
|
|
onOpenChange: (v: boolean) => void;
|
|
onSubmit: (data: AssetFormValues) => Promise<void>;
|
|
initialData?: Partial<AssetFormValues>;
|
|
}
|
|
|
|
export function AssetDialog({ open, onOpenChange, onSubmit, initialData }: Props) {
|
|
const {
|
|
register,
|
|
handleSubmit,
|
|
setValue,
|
|
reset,
|
|
formState: { errors, isSubmitting },
|
|
} = useForm<AssetFormValues>({
|
|
resolver: zodResolver(assetSchema) as Resolver<AssetFormValues>,
|
|
defaultValues: { complexityMultiplier: 1, ...initialData },
|
|
});
|
|
|
|
const handleClose = () => { reset(); onOpenChange(false); };
|
|
const submit = async (data: AssetFormValues) => { await onSubmit(data); reset(); };
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={handleClose}>
|
|
<DialogContent className="bg-zinc-900 border-zinc-800 max-w-lg">
|
|
<DialogHeader>
|
|
<DialogTitle>{initialData ? "Edit Asset" : "Add Asset"}</DialogTitle>
|
|
</DialogHeader>
|
|
<form onSubmit={handleSubmit(submit as Parameters<typeof handleSubmit>[0])} className="space-y-4">
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div className="space-y-1.5 col-span-2">
|
|
<Label>Asset Name *</Label>
|
|
<Input {...register("assetName")} className="bg-zinc-800 border-zinc-700" placeholder="e.g. Hero Character" />
|
|
{errors.assetName && <p className="text-red-400 text-xs">{errors.assetName.message}</p>}
|
|
</div>
|
|
<div className="space-y-1.5">
|
|
<Label>Asset Type</Label>
|
|
<Select onValueChange={(v) => setValue("assetType", v ?? undefined)} defaultValue={initialData?.assetType ?? undefined}>
|
|
<SelectTrigger className="bg-zinc-800 border-zinc-700">
|
|
<SelectValue placeholder="Select type" />
|
|
</SelectTrigger>
|
|
<SelectContent className="bg-zinc-900 border-zinc-700">
|
|
{ASSET_TYPES.map((t) => <SelectItem key={t} value={t}>{t}</SelectItem>)}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<div className="space-y-1.5">
|
|
<Label>Complexity Multiplier</Label>
|
|
<Input {...register("complexityMultiplier")} type="number" step="0.1" min="0.1" max="10" className="bg-zinc-800 border-zinc-700" />
|
|
</div>
|
|
</div>
|
|
<div className="space-y-1.5">
|
|
<Label>Description</Label>
|
|
<Input {...register("description")} className="bg-zinc-800 border-zinc-700" />
|
|
</div>
|
|
<div className="space-y-1.5">
|
|
<Label>Notes</Label>
|
|
<Textarea {...register("notes")} className="bg-zinc-800 border-zinc-700" rows={2} />
|
|
</div>
|
|
<div className="flex gap-3 justify-end pt-2">
|
|
<Button type="button" variant="outline" className="border-zinc-700" onClick={handleClose}>Cancel</Button>
|
|
<Button type="submit" disabled={isSubmitting} className="bg-amber-500 hover:bg-amber-400 text-black font-semibold">
|
|
{isSubmitting && <Loader2 className="h-4 w-4 mr-2 animate-spin" />}
|
|
{initialData ? "Save" : "Add Asset"}
|
|
</Button>
|
|
</div>
|
|
</form>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|