feat(forms): 4 critères opérationnels dans formulaires admin + espace hôte
All checks were successful
CI / test (pull_request) Successful in 2m16s
All checks were successful
CI / test (pull_request) Successful in 2m16s
This commit is contained in:
parent
1f8250ad7e
commit
4901bb950e
6 changed files with 228 additions and 2 deletions
|
|
@ -94,6 +94,10 @@ export default async function EditCarbetPage({ params }: PageProps) {
|
|||
capacity: carbet.capacity,
|
||||
nightlyPrice: carbet.nightlyPrice.toString(),
|
||||
accessType: carbet.accessType,
|
||||
roadAccess: carbet.roadAccess,
|
||||
electricity: carbet.electricity,
|
||||
gsmAtCarbet: carbet.gsmAtCarbet,
|
||||
gsmExitDistanceKm: carbet.gsmExitDistanceKm !== null ? carbet.gsmExitDistanceKm.toString() : null,
|
||||
roadAccessNote: carbet.roadAccessNote,
|
||||
pirogueDurationMin: carbet.pirogueDurationMin,
|
||||
minStayNights: carbet.minStayNights,
|
||||
|
|
|
|||
|
|
@ -20,6 +20,10 @@ export type CarbetFormInitial = {
|
|||
capacity?: number;
|
||||
nightlyPrice?: number | string;
|
||||
accessType?: string;
|
||||
roadAccess?: string | null;
|
||||
electricity?: string | null;
|
||||
gsmAtCarbet?: boolean;
|
||||
gsmExitDistanceKm?: number | string | null;
|
||||
roadAccessNote?: string | null;
|
||||
pirogueDurationMin?: number | null;
|
||||
minStayNights?: number | null;
|
||||
|
|
@ -189,6 +193,63 @@ export function CarbetForm({ initial = {}, owners, providers, action, submitLabe
|
|||
</div>
|
||||
</section>
|
||||
|
||||
{/* Critères opérationnels */}
|
||||
<section className="rounded-lg border border-zinc-200 bg-white p-5 shadow-sm">
|
||||
<h2 className="mb-1 text-sm font-semibold uppercase tracking-wider text-zinc-500">
|
||||
Critères opérationnels
|
||||
</h2>
|
||||
<p className="mb-4 text-xs text-zinc-500">
|
||||
Les 4 dealbreakers d'un séjour en carbet guyanais. Indispensable pour les filtres recherche.
|
||||
</p>
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||
<FormField label="🛣️ Accès route" hint="Praticabilité de l'accès depuis la route">
|
||||
<select name="roadAccess" defaultValue={initial.roadAccess ?? ""} className={selectCls}>
|
||||
<option value="">— non précisé —</option>
|
||||
<option value="ALL_YEAR">🛣️ Toute saison</option>
|
||||
<option value="DRY_SEASON_ONLY">🟠 Saison sèche uniquement</option>
|
||||
<option value="NONE">🛶 Pirogue uniquement</option>
|
||||
</select>
|
||||
</FormField>
|
||||
|
||||
<FormField label="⚡ Électricité" hint="Comment est alimenté le carbet ?">
|
||||
<select name="electricity" defaultValue={initial.electricity ?? ""} className={selectCls}>
|
||||
<option value="">— non précisé —</option>
|
||||
<option value="EDF">⚡ EDF / raccordé réseau</option>
|
||||
<option value="GENERATOR_READY">🔌 Préinstallation groupe électrogène</option>
|
||||
<option value="SOLAR">☀️ Solaire</option>
|
||||
<option value="NONE">🕯️ Aucune électricité</option>
|
||||
</select>
|
||||
</FormField>
|
||||
|
||||
<FormField label="📶 Réseau GSM au carbet" hint="Téléphone capte directement sur place ?">
|
||||
<select
|
||||
name="gsmAtCarbet"
|
||||
defaultValue={initial.gsmAtCarbet ? "yes" : "no"}
|
||||
className={selectCls}
|
||||
>
|
||||
<option value="yes">✅ Oui, signal au carbet</option>
|
||||
<option value="no">❌ Non, zone sans réseau</option>
|
||||
</select>
|
||||
</FormField>
|
||||
|
||||
<FormField
|
||||
label="📵 Distance pour atteindre le réseau (km)"
|
||||
hint="Si pas de réseau au carbet — sinon laisser vide"
|
||||
>
|
||||
<input
|
||||
name="gsmExitDistanceKm"
|
||||
type="number"
|
||||
min={0}
|
||||
max={50}
|
||||
step="0.1"
|
||||
defaultValue={initial.gsmExitDistanceKm?.toString() ?? ""}
|
||||
placeholder="ex. 1.5"
|
||||
className={inputCls}
|
||||
/>
|
||||
</FormField>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Séjour & tarif */}
|
||||
<section className="rounded-lg border border-zinc-200 bg-white p-5 shadow-sm">
|
||||
<h2 className="mb-4 text-sm font-semibold uppercase tracking-wider text-zinc-500">Séjour & tarif</h2>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@ import { prisma } from "@/lib/prisma";
|
|||
import {
|
||||
AccessType,
|
||||
CarbetStatus,
|
||||
Electricity,
|
||||
MediaType,
|
||||
RoadAccess,
|
||||
TransportMode,
|
||||
UserRole,
|
||||
} from "@/generated/prisma/enums";
|
||||
|
|
@ -29,6 +31,16 @@ const baseCarbetSchema = z.object({
|
|||
capacity: z.coerce.number().int().min(1).max(100),
|
||||
nightlyPrice: z.coerce.number().min(0).max(100000),
|
||||
accessType: z.enum([AccessType.ROAD_AND_RIVER, AccessType.RIVER_ONLY]),
|
||||
roadAccess: z
|
||||
.enum([RoadAccess.NONE, RoadAccess.DRY_SEASON_ONLY, RoadAccess.ALL_YEAR])
|
||||
.optional()
|
||||
.nullable(),
|
||||
electricity: z
|
||||
.enum([Electricity.NONE, Electricity.SOLAR, Electricity.GENERATOR_READY, Electricity.EDF])
|
||||
.optional()
|
||||
.nullable(),
|
||||
gsmAtCarbet: z.preprocess((v) => v === "yes" || v === true, z.boolean()),
|
||||
gsmExitDistanceKm: z.coerce.number().min(0).max(50).optional().nullable(),
|
||||
roadAccessNote: z.string().trim().max(1000).optional().nullable(),
|
||||
pirogueDurationMin: z.coerce.number().int().min(0).max(1440).optional().nullable(),
|
||||
minStayNights: z.coerce.number().int().min(1).max(365).optional().nullable(),
|
||||
|
|
@ -53,9 +65,11 @@ function parseFromFormData(fd: FormData) {
|
|||
if (typeof v === "string") obj[k] = v;
|
||||
}
|
||||
// Normalise les champs optionnels nullables
|
||||
["roadAccessNote", "pirogueDurationMin", "minStayNights", "maxStayNights", "minCapacity", "transportMode", "pirogueProviderId"].forEach(
|
||||
["roadAccessNote", "pirogueDurationMin", "minStayNights", "maxStayNights", "minCapacity", "transportMode", "pirogueProviderId", "roadAccess", "electricity", "gsmExitDistanceKm"].forEach(
|
||||
(k) => (obj[k] = normalizeNullable(obj[k] as string | null | undefined)),
|
||||
);
|
||||
// gsmAtCarbet : si pas posté, on garde la valeur (sera traité par preprocess Zod)
|
||||
if (!("gsmAtCarbet" in obj)) obj.gsmAtCarbet = "no";
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue