feat(p0): prix/nuit + booking form public + /inscription + /reservations/[id]

This commit is contained in:
Claude Integration 2026-06-01 01:34:00 +00:00
parent f09a680059
commit e79b6dd141
15 changed files with 590 additions and 9 deletions

View file

@ -87,6 +87,7 @@ export default async function EditCarbetPage({ params }: PageProps) {
latitude: carbet.latitude.toString(),
longitude: carbet.longitude.toString(),
capacity: carbet.capacity,
nightlyPrice: carbet.nightlyPrice.toString(),
accessType: carbet.accessType,
roadAccessNote: carbet.roadAccessNote,
pirogueDurationMin: carbet.pirogueDurationMin,

View file

@ -18,6 +18,7 @@ export type CarbetFormInitial = {
latitude?: number | string;
longitude?: number | string;
capacity?: number;
nightlyPrice?: number | string;
accessType?: string;
roadAccessNote?: string | null;
pirogueDurationMin?: number | null;
@ -188,9 +189,9 @@ export function CarbetForm({ initial = {}, owners, providers, action, submitLabe
</div>
</section>
{/* Séjour */}
{/* 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</h2>
<h2 className="mb-4 text-sm font-semibold uppercase tracking-wider text-zinc-500">Séjour &amp; tarif</h2>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-4">
<FormField label="Capacité" required hint="Voyageurs max">
<input
@ -203,6 +204,17 @@ export function CarbetForm({ initial = {}, owners, providers, action, submitLabe
required
/>
</FormField>
<FormField label="Prix / nuit (€)" required hint="Pour le carbet entier.">
<input
name="nightlyPrice"
type="number"
min={0}
step="0.01"
defaultValue={initial.nightlyPrice?.toString() ?? ""}
className={inputCls}
required
/>
</FormField>
<FormField label="Capacité min recommandée" hint="Facultatif">
<input
name="minCapacity"

View file

@ -27,6 +27,7 @@ const baseCarbetSchema = z.object({
latitude: z.coerce.number().min(-90).max(90),
longitude: z.coerce.number().min(-180).max(180),
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]),
roadAccessNote: z.string().trim().max(1000).optional().nullable(),
pirogueDurationMin: z.coerce.number().int().min(0).max(1440).optional().nullable(),

View file

@ -99,6 +99,7 @@ export default async function CarbetsAdminPage({ searchParams }: PageProps) {
<th className="px-4 py-2 text-left font-semibold">Fleuve</th>
<th className="px-4 py-2 text-left font-semibold">Accès</th>
<th className="px-4 py-2 text-right font-semibold">Cap.</th>
<th className="px-4 py-2 text-right font-semibold">/nuit</th>
<th className="px-4 py-2 text-right font-semibold">Médias</th>
<th className="px-4 py-2 text-right font-semibold">Résas</th>
<th className="px-4 py-2 text-left font-semibold">Propriétaire</th>
@ -109,7 +110,7 @@ export default async function CarbetsAdminPage({ searchParams }: PageProps) {
<tbody className="divide-y divide-zinc-100">
{carbets.length === 0 ? (
<tr>
<td colSpan={9} className="px-4 py-8 text-center text-sm text-zinc-500">
<td colSpan={10} className="px-4 py-8 text-center text-sm text-zinc-500">
Aucun carbet ne correspond aux filtres.
</td>
</tr>
@ -129,6 +130,7 @@ export default async function CarbetsAdminPage({ searchParams }: PageProps) {
{c.accessType === AccessType.RIVER_ONLY ? "🛶 Fleuve" : "🛣️ Route+fleuve"}
</td>
<td className="px-4 py-2 text-right font-mono text-zinc-700">{c.capacity}</td>
<td className="px-4 py-2 text-right font-mono text-zinc-700">{Number(c.nightlyPrice).toFixed(0)}</td>
<td className="px-4 py-2 text-right font-mono text-zinc-700">{c.mediaCount}</td>
<td className="px-4 py-2 text-right font-mono text-zinc-700">{c.bookingsCount}</td>
<td className="px-4 py-2 text-zinc-700">{c.ownerName}</td>