karbe/src/app/admin/page.tsx
Claude Integration bcb93c6b29 feat(admin): shell admin + dashboard KPI + recherche ⌘K (Sprint 1)
Layout admin :
- src/app/admin/layout.tsx : route protégée requireRole(ADMIN), sidebar + topbar + breadcrumbs, data-admin sur racine pour theme sobre indépendant du theme public
- Sidebar : 12 sections groupées (Vue d'ensemble, Catalogue, Activité, Membres, Contenu, Système), highlight de la route courante
- TopBar : prompt ⌘K, lien vers site public, email admin
- Breadcrumbs : auto depuis pathname
- CommandPalette : ⌘K / Ctrl K, navigation ↑↓ + Entrée, recherche live debounced 150ms

Dashboard :
- 7 KPI cards avec tone neutral/ok/warn/info (réservations semaine, confirmées 30j, revenus reversés, occupation, nouveaux users, carbets publiés, avis à modérer)
- Section raccourcis fréquents

Theme admin :
- globals.css : [data-admin] override le background+font, neutralise les borders sépia/papier teinté du theme aquarelle, garantit lisibilité permanente

Recherche globale :
- lib/admin/search.ts : query parallèle sur Carbet, User, Booking, ContentPage, PirogueProvider (5 résultats par catégorie, LIKE insensitive)
- api/admin/search?q=… route handler avec requireRole

KPI :
- lib/admin/kpis.ts : 7 métriques live (cache 0), Promise.all, helper formatEur

Pas de dépendance externe ajoutée (cmdk, shadcn) — composants custom Tailwind pour rester léger.
2026-05-31 18:21:50 +00:00

102 lines
3.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { formatEur, getAdminKpis } from "@/lib/admin/kpis";
import { KPICard } from "@/components/admin/KPICard";
export const dynamic = "force-dynamic";
export default async function AdminDashboard() {
const kpis = await getAdminKpis();
return (
<div className="mx-auto max-w-6xl">
<header className="mb-6 mt-2">
<h1 className="text-2xl font-semibold text-zinc-900">Tableau de bord</h1>
<p className="mt-1 text-sm text-zinc-500">
Vue d&apos;ensemble de l&apos;activité Karbé. Données live (cache 0).
</p>
</header>
<section className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
<KPICard
label="Réservations cette semaine"
value={kpis.bookingsThisWeek}
hint="Toutes statuts confondus, démarrage dans la semaine en cours."
tone="info"
/>
<KPICard
label="Réservations confirmées · 30 j"
value={kpis.bookingsConfirmed30d}
hint="CONFIRMED + paiement SUCCEEDED, démarrage J-30."
tone="ok"
/>
<KPICard
label="Revenus reversés · 30 j"
value={formatEur(kpis.revenue30dCents)}
hint="Somme des montants confirmés (reversement loueurs)."
tone="ok"
/>
<KPICard
label="Occupation moyenne · 30 j"
value={`${kpis.occupancyPct} %`}
hint="Nuits réservées / (carbets publiés × 30)."
tone={kpis.occupancyPct > 50 ? "ok" : "neutral"}
/>
<KPICard
label="Nouveaux comptes · 30 j"
value={kpis.newUsers30d}
hint="Inscriptions tous rôles confondus."
tone="info"
/>
<KPICard
label="Carbets publiés"
value={kpis.publishedCarbets}
hint="Catalogue actif (status PUBLISHED)."
tone="neutral"
/>
<KPICard
label="Avis à modérer"
value={kpis.reviewsToModerate}
hint="Aucune réponse de l&apos;hôte enregistrée."
tone={kpis.reviewsToModerate > 5 ? "warn" : "neutral"}
/>
</section>
<section className="mt-10 rounded-lg border border-zinc-200 bg-white p-5 shadow-sm">
<h2 className="text-sm font-semibold uppercase tracking-wider text-zinc-500">
Raccourcis fréquents
</h2>
<ul className="mt-3 grid grid-cols-1 gap-2 text-sm sm:grid-cols-2 lg:grid-cols-3">
<li>
<a href="/admin/carbets" className="block rounded border border-zinc-200 px-3 py-2 hover:border-zinc-300 hover:bg-zinc-50">
Gérer les carbets
</a>
</li>
<li>
<a href="/admin/bookings" className="block rounded border border-zinc-200 px-3 py-2 hover:border-zinc-300 hover:bg-zinc-50">
Voir les réservations
</a>
</li>
<li>
<a href="/admin/content-pages" className="block rounded border border-zinc-200 px-3 py-2 hover:border-zinc-300 hover:bg-zinc-50">
Éditer les pages
</a>
</li>
<li>
<a href="/admin/plugins" className="block rounded border border-zinc-200 px-3 py-2 hover:border-zinc-300 hover:bg-zinc-50">
Activer / désactiver des plugins
</a>
</li>
<li>
<a href="/admin/users" className="block rounded border border-zinc-200 px-3 py-2 hover:border-zinc-300 hover:bg-zinc-50">
Modérer les utilisateurs
</a>
</li>
<li>
<a href="/admin/settings" className="block rounded border border-zinc-200 px-3 py-2 hover:border-zinc-300 hover:bg-zinc-50">
Paramètres
</a>
</li>
</ul>
</section>
</div>
);
}