119 lines
4 KiB
TypeScript
119 lines
4 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useTransition } from "react";
|
|
import { FormField, inputCls, textareaCls } from "@/components/admin/FormField";
|
|
|
|
type Props = {
|
|
initial?: {
|
|
name?: string;
|
|
contactEmail?: string | null;
|
|
contactPhone?: string | null;
|
|
rivers?: string[];
|
|
pricingNote?: string | null;
|
|
description?: string | null;
|
|
active?: boolean;
|
|
};
|
|
action: (fd: FormData) => Promise<{ ok: false; error: string } | { ok: true } | undefined>;
|
|
submitLabel?: string;
|
|
};
|
|
|
|
export function ProviderForm({ initial = {}, action, submitLabel = "Enregistrer" }: Props) {
|
|
const [pending, startTransition] = useTransition();
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [success, setSuccess] = useState<string | null>(null);
|
|
|
|
function onSubmit(formData: FormData) {
|
|
setError(null);
|
|
setSuccess(null);
|
|
startTransition(async () => {
|
|
const res = await action(formData);
|
|
if (res && res.ok === false) setError(res.error);
|
|
else if (res && res.ok === true) setSuccess("Prestataire enregistré.");
|
|
});
|
|
}
|
|
|
|
return (
|
|
<form action={onSubmit} className="space-y-4">
|
|
<fieldset disabled={pending} className="space-y-4">
|
|
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
|
<FormField label="Nom" required>
|
|
<input name="name" defaultValue={initial.name ?? ""} required maxLength={200} className={inputCls} />
|
|
</FormField>
|
|
<FormField label="Email de contact">
|
|
<input
|
|
name="contactEmail"
|
|
type="email"
|
|
defaultValue={initial.contactEmail ?? ""}
|
|
maxLength={200}
|
|
className={inputCls}
|
|
/>
|
|
</FormField>
|
|
<FormField label="Téléphone de contact">
|
|
<input
|
|
name="contactPhone"
|
|
defaultValue={initial.contactPhone ?? ""}
|
|
maxLength={50}
|
|
className={inputCls}
|
|
/>
|
|
</FormField>
|
|
<FormField label="Statut">
|
|
<label className="flex items-center gap-2 px-1 py-2 text-sm">
|
|
<input
|
|
type="checkbox"
|
|
name="active"
|
|
defaultChecked={initial.active ?? true}
|
|
className="h-4 w-4 rounded border-zinc-300"
|
|
/>
|
|
Prestataire actif (sélectionnable sur un carbet)
|
|
</label>
|
|
</FormField>
|
|
</div>
|
|
|
|
<FormField label="Fleuves desservis" required hint="Séparer par virgule, point-virgule ou retour à la ligne.">
|
|
<input
|
|
name="rivers"
|
|
defaultValue={(initial.rivers ?? []).join(", ")}
|
|
placeholder="Maroni, Approuague, Oyapock"
|
|
className={inputCls}
|
|
/>
|
|
</FormField>
|
|
|
|
<FormField label="Tarification" hint="Note libre — fourchette de prix, conditions, durées.">
|
|
<textarea
|
|
name="pricingNote"
|
|
rows={3}
|
|
defaultValue={initial.pricingNote ?? ""}
|
|
maxLength={2000}
|
|
className={textareaCls}
|
|
/>
|
|
</FormField>
|
|
|
|
<FormField label="Description" hint="Présentation, langues parlées, prestations annexes.">
|
|
<textarea
|
|
name="description"
|
|
rows={4}
|
|
defaultValue={initial.description ?? ""}
|
|
maxLength={5000}
|
|
className={textareaCls}
|
|
/>
|
|
</FormField>
|
|
|
|
{error ? (
|
|
<div className="rounded border border-rose-200 bg-rose-50 px-3 py-2 text-sm text-rose-700">{error}</div>
|
|
) : null}
|
|
{success ? (
|
|
<div className="rounded border border-emerald-200 bg-emerald-50 px-3 py-2 text-sm text-emerald-800">{success}</div>
|
|
) : null}
|
|
|
|
<div className="flex items-center justify-end gap-2">
|
|
<button
|
|
type="submit"
|
|
className="rounded-md bg-zinc-900 px-5 py-2 text-sm font-semibold text-white hover:bg-zinc-800 disabled:opacity-50"
|
|
>
|
|
{pending ? "Enregistrement…" : submitLabel}
|
|
</button>
|
|
</div>
|
|
</fieldset>
|
|
</form>
|
|
);
|
|
}
|