Files
diyaa.de/components/admin/melody-form.tsx
T

152 lines
7.6 KiB
TypeScript

"use client";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import AdminForm from "@/components/admin/admin-form";
import FileUpload from "@/components/admin/file-upload";
import { createMelody, updateMelody } from "@/app/admin/(protected)/melodies/actions";
import { melodySchema, type MelodyInput } from "@/lib/validations/melody";
type CategoryOption = { id: string; nameAr: string; nameEn: string };
type MelodyFormProps = {
mode: "create" | "edit";
melodyId?: string;
categories: CategoryOption[];
defaultValues: MelodyInput;
};
const statusLabels: Record<MelodyInput["status"], string> = {
DRAFT: "Draft",
PUBLISHED: "Published",
ARCHIVED: "Archived",
};
export default function MelodyForm({ mode, melodyId, categories, defaultValues }: MelodyFormProps) {
const router = useRouter();
const [serverError, setServerError] = useState<string | null>(null);
const form = useForm<MelodyInput>({ resolver: zodResolver(melodySchema), defaultValues });
const onSubmit = async (values: MelodyInput) => {
setServerError(null);
const result = mode === "create" ? await createMelody(values) : await updateMelody(melodyId ?? "", values);
if (result.error) {
setServerError(result.error);
return;
}
router.push("/admin/melodies");
router.refresh();
};
return (
<AdminForm
form={form}
onSubmit={onSubmit}
submitLabel={mode === "create" ? "Create melody" : "Save changes"}
submittingLabel="Saving…"
className="max-w-3xl"
>
<div className="grid gap-5 md:grid-cols-2">
<FormField control={form.control} name="status" render={({ field }) => (
<FormItem>
<FormLabel>Status</FormLabel>
<Select value={field.value} onValueChange={field.onChange}>
<FormControl><SelectTrigger><SelectValue placeholder="Choose a status" /></SelectTrigger></FormControl>
<SelectContent>{Object.entries(statusLabels).map(([value, label]) => <SelectItem key={value} value={value}>{label}</SelectItem>)}</SelectContent>
</Select>
<FormMessage />
</FormItem>
)} />
<FormField control={form.control} name="categoryId" render={({ field }) => (
<FormItem>
<FormLabel>Melody category</FormLabel>
<Select value={field.value || "__none"} onValueChange={(value) => field.onChange(value === "__none" ? "" : value)}>
<FormControl><SelectTrigger><SelectValue placeholder="Choose a category" /></SelectTrigger></FormControl>
<SelectContent>
<SelectItem value="__none">Choose a category</SelectItem>
{categories.map((category) => <SelectItem key={category.id} value={category.id}>{category.nameEn} · {category.nameAr}</SelectItem>)}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)} />
</div>
<FormField control={form.control} name="slug" render={({ field }) => (
<FormItem><FormLabel>Slug</FormLabel><FormControl><Input placeholder="melody-slug" {...field} /></FormControl><FormMessage /></FormItem>
)} />
<div className="grid gap-5 md:grid-cols-2">
<FormField control={form.control} name="titleEn" render={({ field }) => (
<FormItem><FormLabel>English title</FormLabel><FormControl><Input {...field} /></FormControl><FormMessage /></FormItem>
)} />
<FormField control={form.control} name="titleAr" render={({ field }) => (
<FormItem><FormLabel>Arabic title</FormLabel><FormControl><Input dir="rtl" {...field} /></FormControl><FormMessage /></FormItem>
)} />
</div>
<div className="grid gap-5 md:grid-cols-2">
<FormField control={form.control} name="descEn" render={({ field }) => (
<FormItem><FormLabel>English description</FormLabel><FormControl><textarea className="min-h-32 w-full rounded-md border border-input bg-background px-3 py-2 text-sm" {...field} value={field.value ?? ""} /></FormControl><FormMessage /></FormItem>
)} />
<FormField control={form.control} name="descAr" render={({ field }) => (
<FormItem><FormLabel>Arabic description</FormLabel><FormControl><textarea dir="rtl" className="min-h-32 w-full rounded-md border border-input bg-background px-3 py-2 text-sm" {...field} value={field.value ?? ""} /></FormControl><FormMessage /></FormItem>
)} />
</div>
<FormField control={form.control} name="audioFile" render={({ field }) => (
<FormItem>
<FormLabel>Audio file path</FormLabel>
<FormControl><Input placeholder="/api/uploads/audios/..." {...field} /></FormControl>
<FormMessage />
<FileUpload id="melody-audio-file" kind="audio" label="Upload audio file" onUploaded={(files) => field.onChange(files[0]?.url ?? field.value)} />
</FormItem>
)} />
<div className="grid gap-5 md:grid-cols-2">
<FormField control={form.control} name="coverImage" render={({ field }) => (
<FormItem>
<FormLabel>Cover image path</FormLabel>
<FormControl><Input placeholder="/api/uploads/images/..." {...field} value={field.value ?? ""} /></FormControl>
<FormMessage />
<FileUpload id="melody-cover-image" kind="image" label="Upload cover image" onUploaded={(files) => field.onChange(files[0]?.url ?? field.value ?? "")} />
</FormItem>
)} />
<FormField control={form.control} name="durationSec" render={({ field }) => (
<FormItem>
<FormLabel>Duration (seconds)</FormLabel>
<FormControl><Input type="number" min={0} max={86400} value={field.value ?? ""} onChange={(event) => field.onChange(event.target.value ? event.target.valueAsNumber : null)} /></FormControl>
<FormMessage />
</FormItem>
)} />
</div>
<div className="grid gap-3 md:grid-cols-2">
<FormField control={form.control} name="isDownloadable" render={({ field }) => (
<FormItem className="flex items-center gap-3 rounded-md border border-border p-3">
<FormControl><Input type="checkbox" checked={field.value} onChange={(event) => field.onChange(event.target.checked)} className="h-4 w-4" /></FormControl>
<FormLabel className="m-0">Allow audio download</FormLabel>
</FormItem>
)} />
<FormField control={form.control} name="isFeatured" render={({ field }) => (
<FormItem className="flex items-center gap-3 rounded-md border border-border p-3">
<FormControl><Input type="checkbox" checked={field.value} onChange={(event) => field.onChange(event.target.checked)} className="h-4 w-4" /></FormControl>
<FormLabel className="m-0">Feature this melody</FormLabel>
</FormItem>
)} />
</div>
<FormField control={form.control} name="sortOrder" render={({ field }) => (
<FormItem><FormLabel>Sort order</FormLabel><FormControl><Input type="number" min={0} max={9999} value={field.value} onChange={(event) => field.onChange(event.target.valueAsNumber || 0)} /></FormControl><FormMessage /></FormItem>
)} />
{serverError ? <p className="text-sm font-medium text-destructive">{serverError}</p> : null}
</AdminForm>
);
}