diff --git a/src/app/espace-hote/page.tsx b/src/app/espace-hote/page.tsx index d412d73..32f3d03 100644 --- a/src/app/espace-hote/page.tsx +++ b/src/app/espace-hote/page.tsx @@ -1,25 +1,287 @@ import Link from "next/link"; +import { auth } from "@/auth"; import { requireRole } from "@/lib/authorization"; +import { BookingStatus, UserRole } from "@/generated/prisma/enums"; +import { + getHostKpis, + listHostCarbets, + listHostRecentBookings, + isScopeAdmin, +} from "@/lib/host-dashboard"; -export default async function HostPage() { - const session = await requireRole(["OWNER", "ADMIN"]); +import { BookingDecision } from "./_components/BookingDecision"; + +export const dynamic = "force-dynamic"; + +const STATUS_TONES: Record = { + PENDING: "bg-sky-100 text-sky-800 ring-sky-300", + CONFIRMED: "bg-emerald-100 text-emerald-800 ring-emerald-300", + CANCELLED: "bg-rose-100 text-rose-700 ring-rose-300", + COMPLETED: "bg-zinc-100 text-zinc-700 ring-zinc-300", + SUCCEEDED: "bg-emerald-100 text-emerald-800 ring-emerald-300", + REFUNDED: "bg-amber-100 text-amber-800 ring-amber-300", + FAILED: "bg-rose-100 text-rose-700 ring-rose-300", + AUTHORIZED: "bg-indigo-100 text-indigo-800 ring-indigo-300", + DRAFT: "bg-zinc-100 text-zinc-700 ring-zinc-300", + PUBLISHED: "bg-emerald-100 text-emerald-800 ring-emerald-300", + ARCHIVED: "bg-amber-100 text-amber-800 ring-amber-300", +}; + +const STATUS_LABEL: Record = { + PENDING: "En attente", + CONFIRMED: "Confirmée", + CANCELLED: "Annulée", + COMPLETED: "Terminée", + SUCCEEDED: "Payé", + REFUNDED: "Remboursé", + FAILED: "Échec", + AUTHORIZED: "Autorisé", + DRAFT: "Brouillon", + PUBLISHED: "Publié", + ARCHIVED: "Archivé", +}; + +function Badge({ value }: { value: string }) { + const tone = STATUS_TONES[value] ?? STATUS_TONES.PENDING; + return ( + + {STATUS_LABEL[value] ?? value} + + ); +} + +function fmtEur(amount: string, currency: string): string { + const n = Number(amount); + return n.toLocaleString("fr-FR", { style: "currency", currency: currency || "EUR" }); +} + +const dateFmt = new Intl.DateTimeFormat("fr-FR", { + day: "2-digit", + month: "short", + year: "2-digit", +}); + +export default async function HostDashboardPage() { + await requireRole([UserRole.OWNER, UserRole.ADMIN]); + const session = await auth(); + const userId = session!.user.id; + const isAdmin = isScopeAdmin(session?.user?.role); + const scope = { ownerId: userId, isAdmin }; + + const [kpis, recent, carbets] = await Promise.all([ + getHostKpis(scope), + listHostRecentBookings(scope, 12), + listHostCarbets(scope), + ]); + + const pendingBookings = recent.filter((b) => b.status === BookingStatus.PENDING); return ( -
-

Espace hôte

-

- Accès autorisé pour {session.user.email} ({session.user.role}). -

+
+
+
+

Espace hôte

+

+ Bienvenue {session?.user?.name || session?.user?.email}.{" "} + {isAdmin ? "Vue globale (admin)." : "Vue limitée à vos carbets."} +

+
+
+ + + Nouveau carbet + + + Tous mes carbets + +
+
-
- - Gérer mes carbets - -
+
+ + + + 0 ? "warn" : "neutral"} + /> + + +
+ + {kpis.nextArrival ? ( +
+
Prochaine arrivée
+
+ {kpis.nextArrival.tenantName} · {kpis.nextArrival.carbetTitle} +
+
+ {dateFmt.format(kpis.nextArrival.startDate)} +
+
+ ) : null} + + {pendingBookings.length > 0 ? ( +
+

+ Demandes en attente ({pendingBookings.length}) +

+
    + {pendingBookings.map((b) => ( +
  • +
    +
    + {b.tenantName} — {b.carbetTitle} +
    +
    + {dateFmt.format(b.startDate)} → {dateFmt.format(b.endDate)} ·{" "} + {b.guestCount} pers · {fmtEur(b.amount, b.currency)} +
    +
    + +
  • + ))} +
+
+ ) : null} + +
+

+ Mes carbets ({carbets.length}) +

+ {carbets.length === 0 ? ( +
+ Aucun carbet pour l'instant.{" "} + + Créer mon premier carbet + +
+ ) : ( +
+ + + + + + + + + + + + + + + {carbets.map((c) => ( + + + + + + + + + + + ))} + +
TitreFleuve€/nuitCap.MédiasRésasAvisStatut
+ + {c.title} + +
+ /{c.slug} +
+
{c.river} + {Number(c.nightlyPrice).toFixed(0)} + {c.capacity} + {c._count.media} + + {c._count.bookings} + + {c._count.reviews} + + +
+
+ )} +
+ + {recent.length > 0 ? ( +
+

+ Activité récente +

+
+ + + + + + + + + + + + + {recent.map((b) => ( + + + + + + + + + ))} + +
CarbetLocataireSéjourMontantRésaPaiement
{b.carbetTitle}{b.tenantName} + {dateFmt.format(b.startDate)} → {dateFmt.format(b.endDate)} + + {fmtEur(b.amount, b.currency)} + + + + +
+
+
+ ) : null}
); } + +function Kpi({ + label, + value, + tone = "neutral", +}: { + label: string; + value: string; + tone?: "neutral" | "warn"; +}) { + return ( +
+
{label}
+
+ {value} +
+
+ ); +}