Compare commits

...

20 Commits

Author SHA1 Message Date
681d5da4d1 Amélioration qualité images avec rendu CSS optimisé
- Amélioration rendu CSS des images dans PlaceCard
- Ajout propriétés imageRendering pour meilleure qualité
- Ajout loading='lazy' et fetchPriority='high'
- Script Node.js pour télécharger images haute résolution
- Guide README pour télécharger images Google Maps en HD
- APK rebuild
2025-11-24 13:17:54 +01:00
65ccd2daaf Météo dynamique pour Fakarava avec heure locale
- Intégration API Open-Meteo pour météo en temps réel
- Coordonnées Fakarava (Rotoava): -16.3167, -145.6167
- Affichage heure locale (fuseau horaire Pacific/Tahiti)
- Rafraîchissement automatique toutes les heures
- Gestion des erreurs avec valeurs par défaut
- Icônes dynamiques selon conditions météo
- APK rebuild
2025-11-24 13:10:08 +01:00
7e6b670847 Photo Google Maps pour Tumoana Markets
- Remplacement image temporaire par photo Google Maps
- Image authentique de Tumoana Markets
- APK rebuild
2025-11-23 20:26:40 +01:00
14b7405961 Mise à jour: Tumoana Markets et Snack Elda avec photos Google Maps
- Remplacement Magasin Rotoava → Tumoana Markets
- Photo Google Maps pour Snack Elda
- Mise à jour des coordonnées GPS
- Mise à jour des liens Google Maps
- APK rebuild
2025-11-23 20:22:31 +01:00
980a3dbe0c Intégration des photos réelles Google Maps pour 4 établissements
- Photos Google Maps téléchargées:
   restaurant-requin-dormeur.jpg → Snack du Requin Dormeur
   restaurant-kori-kori.jpg → Snack Kori Kori
   plage-cocotier-pk9.jpg → Plage PK 9 (Cocotier Couché)
   phare-topaka.jpg → Ancien Phare de Topaka

- Images temporaires Unsplash pour les autres lieux
- APK rebuild avec nouvelles photos authentiques
2025-11-23 20:14:48 +01:00
e693713267 Mise à jour des images avec correspondance spécifique aux activités
- Images renommées pour correspondre aux lieux spécifiques
- restaurant-requin-dormeur.jpg → Snack du Requin Dormeur
- restaurant-kori-kori.jpg → Snack Kori Kori
- plage-cocotier-pk9.jpg → Plage PK 9 (Cocotier Couché)
- phare-topaka.jpg → Ancien Phare de Topaka
- passe-nord-snorkeling.jpg → Passe Nord (Garuae)
- sables-roses.jpg → Sables Roses (Passe Sud)
- plage-pension.jpg → Plage de la Pension
- epicerie-rotoava.jpg → Magasin Rotoava
- snack-chez-elda.jpg → Snack Chez Elda
- epicerie-locale.jpg → Autres Petites Épiceries
- APK rebuild avec nouvelles images
2025-11-23 20:04:25 +01:00
3ff1b82b85 Permettre le commit de l'APK pour téléchargement distant 2025-11-23 19:56:01 +01:00
f42b09ebab Build APK avec images incluses (6.3M)
- APK généré avec toutes les images
- Restaurants, plages et épiceries avec photos
- Prêt pour test beta
2025-11-23 19:55:47 +01:00
908bf5f0c2 Ajout d'images pour les cartes de lieux (restaurants, plages, épiceries)
- Téléchargement d'images depuis Unsplash
- Images stockées dans public/images/
- Mise à jour de places.json avec chemins locaux
- Composant PlaceCard affiche maintenant les images dans l'en-tête
- Fallback automatique vers placeholder si image manquante
2025-11-23 19:52:37 +01:00
fa4ef6320a Fix: Logo visible en mode sombre avec dark:invert 2025-11-23 19:40:02 +01:00
45a96f618b Suppression de l'APK admin obsolète 2025-11-23 19:29:09 +01:00
bbfddefc76 Build APK simplifié réussi - Version finale sans admin/WiFi/bungalow 2025-11-23 19:27:15 +01:00
b10d9c515b Simplification complète de l'app - Suppression admin/WiFi/bungalow
- Suppression de toute la partie admin (routes, composants, API)
- Suppression du WiFi et du numéro de bungalow
- Simplification de l'accueil (logo, météo, message statique)
- App 100% statique maintenant
- Redirection simple vers /accueil
- Nettoyage des hooks et types inutilisés
2025-11-23 19:22:34 +01:00
29d6ab3d80 Rebuild APK client avec la logique de détection corrigée 2025-11-23 17:23:36 +01:00
429eb07291 Amélioration de la détection APK client vs admin
- Détection basée sur Capacitor Android + adminPassword
- APK client (sans adminPassword) → redirige vers /accueil
- APK admin (avec adminPassword) → redirige vers /admin/login
- Web avec /admin → admin
- Sinon → client
2025-11-23 17:21:22 +01:00
092d537180 Ajout du script build-apk-admin.sh pour générer l'APK admin
- Script dédié pour l'APK admin (compagnon-admin-debug.apk)
- Configuration Capacitor pour com.pensionmarama.admin
- Exclusion temporaire des routes API pendant le build
- Génération de l'APK admin avec les corrections pour /admin/login
2025-11-23 17:18:34 +01:00
61c1f47409 Corrections pour le build APK admin
- Ajout de updatedAt manquant dans la création de client
- Correction de l'import ThemeProvider (next-themes)
- Correction du size icon dans ThemeToggle
- Exclusion temporaire des routes API pendant le build
- Build APK admin réussi
2025-11-23 17:13:47 +01:00
0247f6ed9d Ajout du message informatif pour le mode hors ligne dans ClientList 2025-11-23 17:01:52 +01:00
ba1433b192 Correction de l'accès à /admin/login dans l'APK admin
- Détection automatique de l'app admin (Capacitor)
- Redirection automatique vers /admin/login si non connecté
- Gestion gracieuse des erreurs API en mode statique
- Message informatif si API non disponible
- Support du mode statique pour l'APK admin
2025-11-23 17:01:31 +01:00
0e485aacee Ajout du mode clair/sombre (dark mode)
- Installation de next-themes
- Création du composant ThemeToggle avec icônes soleil/lune
- Intégration du ThemeProvider dans le layout
- Ajout du toggle dans la navigation mobile et le header admin
- Adaptation des couleurs pour le dark mode (tropical chic)
- Mise à jour des composants UI (Card, Button) pour le dark mode
- Adaptation des composants principaux (Layout, WifiCard, etc.)
2025-11-23 16:53:27 +01:00
232 changed files with 10230 additions and 1568 deletions

4
.gitignore vendored
View File

@ -49,5 +49,5 @@ next-env.d.ts
# data storage (ne pas commit les données clients) # data storage (ne pas commit les données clients)
/data/clients.json /data/clients.json
# APK # APK (commenté pour permettre le téléchargement depuis le dépôt)
/dist/*.apk # /dist/*.apk

View File

@ -1,53 +1,37 @@
"use client"; "use client";
import { Suspense } from "react";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import Layout from "@/components/layout/Layout"; import Layout from "@/components/layout/Layout";
import WifiCard from "@/components/accueil/WifiCard";
import Logo from "@/components/Logo"; import Logo from "@/components/Logo";
import { useClientData } from "@/lib/hooks/useClientData"; import { config } from "@/lib/config";
const WeatherWidget = dynamic(() => import("@/components/accueil/WeatherWidget"), { const WeatherWidget = dynamic(() => import("@/components/accueil/WeatherWidget"), {
loading: () => <div className="h-32 bg-gray-100 rounded-2xl animate-pulse" />, loading: () => <div className="h-32 bg-gray-100 dark:bg-gray-800 rounded-2xl animate-pulse" />,
ssr: false, ssr: false,
}); });
function AccueilContent() { export default function AccueilPage() {
const { bungalowNumber, gerantMessage, loading } = useClientData();
if (loading) {
return (
<Layout>
<div className="px-4 py-6 space-y-6">
<div className="h-32 bg-gray-100 rounded-2xl animate-pulse" />
</div>
</Layout>
);
}
return ( return (
<Layout> <Layout>
<div className="px-4 py-6 space-y-6"> <div className="px-4 py-6 space-y-6">
<header className="text-center py-4"> <header className="text-center py-4">
<Logo size={140} className="mb-4" /> <Logo size={140} className="mb-4" />
<h1 className="text-2xl font-bold text-primary mb-2"> <h1 className="text-2xl font-bold text-primary dark:text-primary mb-2">
Ia Ora Na Ia Ora Na
</h1> </h1>
<p className="text-lg text-gray-700"> <p className="text-lg text-gray-700 dark:text-gray-300">
Bienvenue au Bungalow {bungalowNumber} Bienvenue à la Pension Marama
</p> </p>
</header> </header>
<WifiCard />
<WeatherWidget /> <WeatherWidget />
<section className="bg-secondary rounded-2xl p-6"> <section className="bg-secondary dark:bg-primary/20 rounded-2xl p-6">
<h2 className="text-xl font-semibold text-primary mb-3"> <h2 className="text-xl font-semibold text-primary dark:text-primary mb-3">
Le mot du gérant Le mot du gérant
</h2> </h2>
<p className="text-gray-700 leading-relaxed"> <p className="text-gray-700 dark:text-gray-300 leading-relaxed">
{gerantMessage} {config.gerantMessage}
</p> </p>
</section> </section>
</div> </div>
@ -55,17 +39,3 @@ function AccueilContent() {
); );
} }
export default function AccueilPage() {
return (
<Suspense fallback={
<Layout>
<div className="px-4 py-6 space-y-6">
<div className="h-32 bg-gray-100 rounded-2xl animate-pulse" />
</div>
</Layout>
}>
<AccueilContent />
</Suspense>
);
}

View File

@ -1,85 +0,0 @@
"use client";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { Button } from "@/components/ui/button";
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
import Logo from "@/components/Logo";
export default function AdminLoginPage() {
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
const router = useRouter();
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError("");
setLoading(true);
// Vérification simple côté client (la vraie vérification se fait côté serveur)
// Pour l'instant, on stocke le mot de passe dans localStorage
localStorage.setItem("adminPassword", password);
// Test avec une requête API
try {
const response = await fetch("/api/admin/clients", {
headers: {
Authorization: `Bearer ${password}`,
},
});
if (response.ok) {
router.push("/admin");
} else {
setError("Mot de passe incorrect");
localStorage.removeItem("adminPassword");
}
} catch (err) {
setError("Erreur de connexion");
localStorage.removeItem("adminPassword");
} finally {
setLoading(false);
}
};
return (
<div className="min-h-screen bg-background flex items-center justify-center px-4">
<Card className="w-full max-w-md">
<CardHeader className="text-center">
<div className="flex justify-center mb-4">
<Logo size={100} />
</div>
<CardTitle>Administration</CardTitle>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Mot de passe
</label>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full px-4 py-2 border border-gray-300 rounded-xl focus:ring-2 focus:ring-primary focus:border-transparent"
required
/>
</div>
{error && (
<div className="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-xl text-sm">
{error}
</div>
)}
<Button type="submit" disabled={loading} className="w-full">
{loading ? "Connexion..." : "Se connecter"}
</Button>
</form>
</CardContent>
</Card>
</div>
);
}

View File

@ -1,73 +0,0 @@
"use client";
import { useState, useEffect } from "react";
import { useRouter } from "next/navigation";
import AdminLayout from "@/components/admin/AdminLayout";
import ClientForm from "@/components/admin/ClientForm";
import ClientList from "@/components/admin/ClientList";
import { Button } from "@/components/ui/button";
import { Plus } from "lucide-react";
import { Client } from "@/lib/types/client";
export default function AdminPage() {
const [showForm, setShowForm] = useState(false);
const [editingClient, setEditingClient] = useState<Client | undefined>();
const [refreshKey, setRefreshKey] = useState(0);
const router = useRouter();
useEffect(() => {
// Vérifier si l'admin est connecté
const adminPassword = localStorage.getItem("adminPassword");
if (!adminPassword) {
router.push("/admin/login");
}
}, [router]);
const handleNewClient = () => {
setEditingClient(undefined);
setShowForm(true);
};
const handleEdit = (client: Client) => {
setEditingClient(client);
setShowForm(true);
};
const handleSuccess = () => {
setShowForm(false);
setEditingClient(undefined);
setRefreshKey((k) => k + 1);
};
const handleCancel = () => {
setShowForm(false);
setEditingClient(undefined);
};
return (
<AdminLayout>
<div className="space-y-6">
<div className="flex items-center justify-between">
<h2 className="text-2xl font-bold text-primary">Gestion des clients</h2>
{!showForm && (
<Button onClick={handleNewClient}>
<Plus className="h-4 w-4 mr-2" />
Nouveau client
</Button>
)}
</div>
{showForm ? (
<ClientForm
client={editingClient}
onSuccess={handleSuccess}
onCancel={handleCancel}
/>
) : (
<ClientList onEdit={handleEdit} onRefresh={() => setRefreshKey((k) => k + 1)} />
)}
</div>
</AdminLayout>
);
}

View File

@ -1,153 +0,0 @@
import { NextRequest, NextResponse } from "next/server";
import { writeFile, readFile, mkdir } from "fs/promises";
import { existsSync } from "fs";
import path from "path";
import { Client, ClientInput } from "@/lib/types/client";
const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || "admin123";
const DATA_DIR = path.join(process.cwd(), "data");
const CLIENTS_FILE = path.join(DATA_DIR, "clients.json");
function verifyAuth(request: NextRequest): boolean {
const authHeader = request.headers.get("authorization");
if (!authHeader) return false;
const token = authHeader.replace("Bearer ", "");
return token === ADMIN_PASSWORD;
}
async function loadClients(): Promise<Client[]> {
try {
if (!existsSync(CLIENTS_FILE)) {
if (!existsSync(DATA_DIR)) {
await mkdir(DATA_DIR, { recursive: true });
}
await writeFile(CLIENTS_FILE, JSON.stringify([], null, 2));
return [];
}
const data = await readFile(CLIENTS_FILE, "utf-8");
return JSON.parse(data);
} catch (error) {
console.error("Erreur lecture clients:", error);
return [];
}
}
async function saveClients(clients: Client[]): Promise<void> {
try {
if (!existsSync(DATA_DIR)) {
await mkdir(DATA_DIR, { recursive: true });
}
await writeFile(CLIENTS_FILE, JSON.stringify(clients, null, 2));
} catch (error) {
console.error("Erreur sauvegarde clients:", error);
throw error;
}
}
// GET - Récupérer un client par ID
export async function GET(
request: NextRequest,
{ params }: { params: { id: string } }
) {
if (!verifyAuth(request)) {
return NextResponse.json({ error: "Non autorisé" }, { status: 401 });
}
try {
const clients = await loadClients();
const client = clients.find(c => c.id === params.id);
if (!client) {
return NextResponse.json(
{ error: "Client non trouvé" },
{ status: 404 }
);
}
return NextResponse.json(client);
} catch (error) {
console.error("Erreur GET client:", error);
return NextResponse.json(
{ error: "Erreur serveur" },
{ status: 500 }
);
}
}
// PUT - Mettre à jour un client
export async function PUT(
request: NextRequest,
{ params }: { params: { id: string } }
) {
if (!verifyAuth(request)) {
return NextResponse.json({ error: "Non autorisé" }, { status: 401 });
}
try {
const input: ClientInput = await request.json();
const clients = await loadClients();
const clientIndex = clients.findIndex(c => c.id === params.id);
if (clientIndex === -1) {
return NextResponse.json(
{ error: "Client non trouvé" },
{ status: 404 }
);
}
// Mettre à jour le client
const updatedClient: Client = {
...clients[clientIndex],
email: input.email || clients[clientIndex].email,
bungalowNumber: input.bungalowNumber || clients[clientIndex].bungalowNumber,
wifiName: input.wifiName || clients[clientIndex].wifiName,
wifiPassword: input.wifiPassword || clients[clientIndex].wifiPassword,
gerantMessage: input.gerantMessage || clients[clientIndex].gerantMessage,
};
clients[clientIndex] = updatedClient;
await saveClients(clients);
return NextResponse.json(updatedClient);
} catch (error) {
console.error("Erreur PUT client:", error);
return NextResponse.json(
{ error: "Erreur serveur" },
{ status: 500 }
);
}
}
// DELETE - Supprimer un client
export async function DELETE(
request: NextRequest,
{ params }: { params: { id: string } }
) {
if (!verifyAuth(request)) {
return NextResponse.json({ error: "Non autorisé" }, { status: 401 });
}
try {
const clients = await loadClients();
const filteredClients = clients.filter(c => c.id !== params.id);
if (filteredClients.length === clients.length) {
return NextResponse.json(
{ error: "Client non trouvé" },
{ status: 404 }
);
}
await saveClients(filteredClients);
return NextResponse.json({ success: true });
} catch (error) {
console.error("Erreur DELETE client:", error);
return NextResponse.json(
{ error: "Erreur serveur" },
{ status: 500 }
);
}
}

View File

@ -1,131 +0,0 @@
import { NextRequest, NextResponse } from "next/server";
import { writeFile, readFile, mkdir } from "fs/promises";
import { existsSync } from "fs";
import path from "path";
import { Client, ClientInput } from "@/lib/types/client";
// Mot de passe admin (à changer en production via variable d'environnement)
const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || "admin123";
// Chemin vers le fichier de stockage
const DATA_DIR = path.join(process.cwd(), "data");
const CLIENTS_FILE = path.join(DATA_DIR, "clients.json");
// Vérifier l'authentification
function verifyAuth(request: NextRequest): boolean {
const authHeader = request.headers.get("authorization");
if (!authHeader) return false;
const token = authHeader.replace("Bearer ", "");
return token === ADMIN_PASSWORD;
}
// Charger les clients depuis le fichier
async function loadClients(): Promise<Client[]> {
try {
if (!existsSync(CLIENTS_FILE)) {
// Créer le répertoire et le fichier si nécessaire
if (!existsSync(DATA_DIR)) {
await mkdir(DATA_DIR, { recursive: true });
}
await writeFile(CLIENTS_FILE, JSON.stringify([], null, 2));
return [];
}
const data = await readFile(CLIENTS_FILE, "utf-8");
return JSON.parse(data);
} catch (error) {
console.error("Erreur lecture clients:", error);
return [];
}
}
// Sauvegarder les clients dans le fichier
async function saveClients(clients: Client[]): Promise<void> {
try {
if (!existsSync(DATA_DIR)) {
await mkdir(DATA_DIR, { recursive: true });
}
await writeFile(CLIENTS_FILE, JSON.stringify(clients, null, 2));
} catch (error) {
console.error("Erreur sauvegarde clients:", error);
throw error;
}
}
// GET - Récupérer tous les clients
export async function GET(request: NextRequest) {
if (!verifyAuth(request)) {
return NextResponse.json({ error: "Non autorisé" }, { status: 401 });
}
try {
const clients = await loadClients();
return NextResponse.json(clients);
} catch (error) {
console.error("Erreur GET clients:", error);
return NextResponse.json(
{ error: "Erreur serveur" },
{ status: 500 }
);
}
}
// POST - Créer un nouveau client
export async function POST(request: NextRequest) {
if (!verifyAuth(request)) {
return NextResponse.json({ error: "Non autorisé" }, { status: 401 });
}
try {
const input: ClientInput = await request.json();
// Validation
if (!input.email || !input.bungalowNumber) {
return NextResponse.json(
{ error: "Email et numéro de bungalow requis" },
{ status: 400 }
);
}
const clients = await loadClients();
// Vérifier si l'email existe déjà
if (clients.some(c => c.email === input.email)) {
return NextResponse.json(
{ error: "Un client avec cet email existe déjà" },
{ status: 409 }
);
}
// Créer le nouveau client
const newClient: Client = {
id: `client-${Date.now()}`,
token: generateToken(),
email: input.email,
bungalowNumber: input.bungalowNumber,
wifiName: input.wifiName || "Lagon-WiFi",
wifiPassword: input.wifiPassword || "",
gerantMessage: input.gerantMessage || "Bienvenue dans notre pension de famille !",
createdAt: new Date().toISOString(),
};
clients.push(newClient);
await saveClients(clients);
return NextResponse.json(newClient, { status: 201 });
} catch (error) {
console.error("Erreur POST client:", error);
return NextResponse.json(
{ error: "Erreur serveur" },
{ status: 500 }
);
}
}
// Générer un token unique
function generateToken(): string {
return Math.random().toString(36).substring(2, 15) +
Math.random().toString(36).substring(2, 15);
}

View File

@ -1,55 +0,0 @@
import { NextRequest, NextResponse } from "next/server";
import { readFile } from "fs/promises";
import { existsSync } from "fs";
import path from "path";
import { Client } from "@/lib/types/client";
const DATA_DIR = path.join(process.cwd(), "data");
const CLIENTS_FILE = path.join(DATA_DIR, "clients.json");
async function loadClients(): Promise<Client[]> {
try {
if (!existsSync(CLIENTS_FILE)) {
return [];
}
const data = await readFile(CLIENTS_FILE, "utf-8");
return JSON.parse(data);
} catch (error) {
console.error("Erreur lecture clients:", error);
return [];
}
}
// GET - Récupérer les données d'un client par son token
export async function GET(
request: NextRequest,
{ params }: { params: { token: string } }
) {
try {
const clients = await loadClients();
const client = clients.find(c => c.token === params.token);
if (!client) {
return NextResponse.json(
{ error: "Client non trouvé" },
{ status: 404 }
);
}
// Retourner uniquement les données nécessaires (pas le token ni l'ID)
return NextResponse.json({
bungalowNumber: client.bungalowNumber,
wifiName: client.wifiName,
wifiPassword: client.wifiPassword,
gerantMessage: client.gerantMessage,
});
} catch (error) {
console.error("Erreur GET client par token:", error);
return NextResponse.json(
{ error: "Erreur serveur" },
{ status: 500 }
);
}
}

View File

@ -4,10 +4,10 @@
@layer base { @layer base {
body { body {
@apply bg-background; @apply bg-background dark:bg-background-dark;
font-size: 16px; font-size: 16px;
min-height: 100vh; min-height: 100vh;
color: #1f2937; @apply text-foreground dark:text-foreground-dark;
} }
} }

View File

@ -2,6 +2,7 @@ import type { Metadata } from "next";
import { Inter } from "next/font/google"; import { Inter } from "next/font/google";
import "./globals.css"; import "./globals.css";
import PWARegister from "@/components/PWARegister"; import PWARegister from "@/components/PWARegister";
import { ThemeProvider } from "@/components/ThemeProvider";
const inter = Inter({ subsets: ["latin"] }); const inter = Inter({ subsets: ["latin"] });
@ -29,14 +30,21 @@ export default function RootLayout({
children: React.ReactNode; children: React.ReactNode;
}>) { }>) {
return ( return (
<html lang="fr"> <html lang="fr" suppressHydrationWarning>
<head> <head>
<link rel="icon" href="/logo-relais-marama.svg" type="image/svg+xml" /> <link rel="icon" href="/logo-relais-marama.svg" type="image/svg+xml" />
<link rel="apple-touch-icon" href="/logo-relais-marama.svg" /> <link rel="apple-touch-icon" href="/logo-relais-marama.svg" />
</head> </head>
<body className={inter.className}> <body className={inter.className}>
{children} <ThemeProvider
<PWARegister /> attribute="class"
defaultTheme="light"
enableSystem={true}
disableTransitionOnChange={false}
>
{children}
<PWARegister />
</ThemeProvider>
</body> </body>
</html> </html>
); );

View File

@ -11,11 +11,10 @@ export default function Home() {
}, [router]); }, [router]);
return ( return (
<div className="flex items-center justify-center min-h-screen"> <div className="flex items-center justify-center min-h-screen bg-background dark:bg-background-dark">
<div className="text-center"> <div className="text-center">
<p className="text-gray-600">Redirection...</p> <p className="text-gray-600 dark:text-gray-400">Redirection...</p>
</div> </div>
</div> </div>
); );
} }

View File

@ -44,7 +44,7 @@ export default function Logo({ size = 120, className = "" }: LogoProps) {
alt="Relais Marama - Fakarava" alt="Relais Marama - Fakarava"
width={size} width={size}
height={size} height={size}
className="object-contain" className="object-contain dark:invert"
style={{ maxWidth: `${size}px`, maxHeight: `${size}px` }} style={{ maxWidth: `${size}px`, maxHeight: `${size}px` }}
/> />
<p className="text-primary font-semibold mt-2" style={{ fontSize: `${size * 0.15}px` }}> <p className="text-primary font-semibold mt-2" style={{ fontSize: `${size * 0.15}px` }}>

View File

@ -0,0 +1,12 @@
"use client";
import { ThemeProvider as NextThemesProvider } from "next-themes";
import { type ComponentProps } from "react";
export function ThemeProvider({
children,
...props
}: ComponentProps<typeof NextThemesProvider>) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
}

View File

@ -0,0 +1,45 @@
"use client";
import { useTheme } from "next-themes";
import { Moon, Sun } from "lucide-react";
import { Button } from "@/components/ui/button";
import { useEffect, useState } from "react";
export function ThemeToggle() {
const { theme, setTheme } = useTheme();
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
if (!mounted) {
return (
<Button
variant="ghost"
size="sm"
className="h-9 w-9 rounded-full p-0"
aria-label="Changer de thème"
>
<Sun className="h-5 w-5" />
</Button>
);
}
return (
<Button
variant="ghost"
size="sm"
onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
className="h-10 w-10 rounded-full hover:bg-secondary dark:hover:bg-gray-800 p-0"
aria-label="Changer de thème"
>
{theme === "dark" ? (
<Sun className="h-5 w-5 text-primary dark:text-yellow-400" />
) : (
<Moon className="h-5 w-5 text-primary dark:text-blue-300" />
)}
</Button>
);
}

View File

@ -1,35 +1,177 @@
import { Cloud, Sun } from "lucide-react"; "use client";
import { useEffect, useState } from "react";
import { Cloud, Sun, CloudRain, Wind, Droplets, Loader2, Clock } from "lucide-react";
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"; import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
interface WeatherData {
temperature: number;
condition: string;
windSpeed: number;
humidity: number;
weatherCode: number;
}
// Coordonnées de Fakarava (Rotoava)
const FAKARAVA_LAT = -16.3167;
const FAKARAVA_LON = -145.6167;
// Codes météo Open-Meteo vers descriptions
const getWeatherCondition = (code: number): { text: string; icon: React.ReactNode } => {
// Codes Open-Meteo WMO Weather interpretation codes
if (code === 0) {
return { text: "Ciel dégagé", icon: <Sun className="h-16 w-16 text-yellow-400" /> };
} else if (code <= 3) {
return { text: "Partiellement nuageux", icon: <Cloud className="h-16 w-16 text-gray-400" /> };
} else if (code <= 48) {
return { text: "Nuageux", icon: <Cloud className="h-16 w-16 text-gray-500" /> };
} else if (code <= 55) {
return { text: "Brouillard", icon: <Cloud className="h-16 w-16 text-gray-400" /> };
} else if (code <= 67) {
return { text: "Pluie", icon: <CloudRain className="h-16 w-16 text-blue-400" /> };
} else if (code <= 77) {
return { text: "Neige", icon: <CloudRain className="h-16 w-16 text-gray-300" /> };
} else if (code <= 82) {
return { text: "Averses", icon: <CloudRain className="h-16 w-16 text-blue-500" /> };
} else if (code <= 86) {
return { text: "Averses de neige", icon: <CloudRain className="h-16 w-16 text-gray-300" /> };
} else {
return { text: "Orage", icon: <CloudRain className="h-16 w-16 text-purple-500" /> };
}
};
export default function WeatherWidget() { export default function WeatherWidget() {
const [weather, setWeather] = useState<WeatherData | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [localTime, setLocalTime] = useState<string>("");
// Mise à jour de l'heure locale chaque seconde
useEffect(() => {
const updateTime = () => {
const now = new Date();
// Fuseau horaire de Tahiti (UTC-10)
const tahitiTime = new Date(now.toLocaleString("en-US", { timeZone: "Pacific/Tahiti" }));
const hours = tahitiTime.getHours().toString().padStart(2, "0");
const minutes = tahitiTime.getMinutes().toString().padStart(2, "0");
setLocalTime(`${hours}:${minutes}`);
};
updateTime();
const timeInterval = setInterval(updateTime, 1000);
return () => clearInterval(timeInterval);
}, []);
useEffect(() => {
const fetchWeather = async () => {
try {
setLoading(true);
setError(null);
// API Open-Meteo (gratuit, pas de clé API nécessaire)
const response = await fetch(
`https://api.open-meteo.com/v1/forecast?latitude=${FAKARAVA_LAT}&longitude=${FAKARAVA_LON}&current=temperature_2m,relative_humidity_2m,weather_code,wind_speed_10m&wind_speed_unit=kmh&timezone=Pacific/Tahiti`
);
if (!response.ok) {
throw new Error("Erreur lors de la récupération de la météo");
}
const data = await response.json();
const current = data.current;
setWeather({
temperature: Math.round(current.temperature_2m),
condition: getWeatherCondition(current.weather_code).text,
windSpeed: Math.round(current.wind_speed_10m),
humidity: current.relative_humidity_2m,
weatherCode: current.weather_code,
});
} catch (err) {
console.error("Erreur météo:", err);
setError("Impossible de charger la météo");
// Valeurs par défaut en cas d'erreur
setWeather({
temperature: 28,
condition: "Ensoleillé",
windSpeed: 15,
humidity: 75,
weatherCode: 0,
});
} finally {
setLoading(false);
}
};
fetchWeather();
// Rafraîchir toutes les heures
const interval = setInterval(fetchWeather, 3600000);
return () => clearInterval(interval);
}, []);
if (loading) {
return (
<Card className="bg-gradient-to-br from-primary/10 to-secondary">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Sun className="h-6 w-6 text-primary" />
Météo - Fakarava
</CardTitle>
</CardHeader>
<CardContent>
<div className="flex items-center justify-center py-8">
<Loader2 className="h-8 w-8 animate-spin text-primary" />
</div>
</CardContent>
</Card>
);
}
if (!weather) {
return null;
}
const weatherInfo = getWeatherCondition(weather.weatherCode);
return ( return (
<Card className="bg-gradient-to-br from-primary/10 to-secondary"> <Card className="bg-gradient-to-br from-primary/10 to-secondary">
<CardHeader> <CardHeader>
<CardTitle className="flex items-center gap-2"> <CardTitle className="flex items-center justify-between">
<Sun className="h-6 w-6 text-primary" /> <div className="flex items-center gap-2">
Météo <Sun className="h-6 w-6 text-primary" />
Météo - Fakarava
</div>
{localTime && (
<div className="flex items-center gap-1 text-sm font-normal text-gray-600 dark:text-gray-400">
<Clock className="h-4 w-4" />
{localTime}
</div>
)}
</CardTitle> </CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<p className="text-3xl font-bold text-primary">28°C</p> <p className="text-3xl font-bold text-primary">{weather.temperature}°C</p>
<p className="text-gray-600 mt-1">Ensoleillé</p> <p className="text-gray-600 dark:text-gray-300 mt-1">{weather.condition}</p>
</div> {error && (
<div className="text-6xl"> <p className="text-xs text-orange-500 mt-1">Données en cache</p>
<Sun className="h-16 w-16 text-yellow-400" /> )}
</div> </div>
<div className="text-6xl">{weatherInfo.icon}</div>
</div> </div>
<div className="mt-4 flex gap-4 text-sm text-gray-600"> <div className="mt-4 flex gap-4 text-sm text-gray-600 dark:text-gray-400">
<div> <div className="flex items-center gap-1">
<span className="font-semibold">Vent:</span> 15 km/h <Wind className="h-4 w-4" />
<span className="font-semibold">Vent:</span> {weather.windSpeed} km/h
</div> </div>
<div> <div className="flex items-center gap-1">
<span className="font-semibold">Humidité:</span> 75% <Droplets className="h-4 w-4" />
<span className="font-semibold">Humidité:</span> {weather.humidity}%
</div> </div>
</div> </div>
</CardContent> </CardContent>
</Card> </Card>
); );
} }

View File

@ -1,129 +0,0 @@
"use client";
import { useState, useEffect } from "react";
import { Wifi, Copy, Check, AlertCircle } from "lucide-react";
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { useClientData } from "@/lib/hooks/useClientData";
export default function WifiCard() {
const { wifiName, wifiPassword, loading } = useClientData();
const [copied, setCopied] = useState(false);
const [error, setError] = useState<string | null>(null);
// Fonction de copie avec fallback pour les navigateurs qui ne supportent pas l'API Clipboard
const copyToClipboard = async (text: string): Promise<boolean> => {
// Méthode moderne (nécessite HTTPS ou localhost)
if (navigator.clipboard && window.isSecureContext) {
try {
await navigator.clipboard.writeText(text);
return true;
} catch (err) {
console.error("Erreur avec l'API Clipboard:", err);
}
}
// Fallback pour les navigateurs plus anciens ou contextes non sécurisés
try {
const textArea = document.createElement("textarea");
textArea.value = text;
textArea.style.position = "fixed";
textArea.style.left = "-999999px";
textArea.style.top = "-999999px";
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
const successful = document.execCommand("copy");
document.body.removeChild(textArea);
if (successful) {
return true;
} else {
throw new Error("La commande copy a échoué");
}
} catch (err) {
console.error("Erreur avec la méthode fallback:", err);
return false;
}
};
const handleCopyPassword = async () => {
if (!wifiPassword || wifiPassword.trim() === "") {
setError("Le mot de passe WiFi n'est pas disponible");
setTimeout(() => setError(null), 3000);
return;
}
setError(null);
const success = await copyToClipboard(wifiPassword);
if (success) {
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} else {
setError("Impossible de copier. Veuillez sélectionner manuellement le mot de passe ci-dessous.");
setTimeout(() => setError(null), 5000);
}
};
// Afficher le mot de passe en cas d'échec de la copie
const showPasswordFallback = error && error.includes("sélectionner manuellement");
return (
<Card className="bg-white">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Wifi className="h-6 w-6 text-primary" />
Connexion WiFi
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div>
<p className="text-sm text-gray-600 mb-1">Nom du réseau</p>
<p className="text-lg font-semibold text-primary">{wifiName || "Chargement..."}</p>
</div>
{showPasswordFallback && wifiPassword && (
<div className="bg-yellow-50 border border-yellow-200 rounded-xl p-3">
<p className="text-sm text-yellow-800 font-mono select-all">
{wifiPassword}
</p>
</div>
)}
{error && !showPasswordFallback && (
<div className="bg-red-50 border border-red-200 rounded-xl p-3 flex items-start gap-2">
<AlertCircle className="h-5 w-5 text-red-600 flex-shrink-0 mt-0.5" />
<p className="text-sm text-red-800">{error}</p>
</div>
)}
<Button
onClick={handleCopyPassword}
disabled={loading || !wifiPassword}
className="w-full h-14 text-lg"
size="lg"
>
{loading ? (
<>
<Copy className="mr-2 h-5 w-5" />
Chargement...
</>
) : copied ? (
<>
<Check className="mr-2 h-5 w-5" />
Mot de passe copié !
</>
) : (
<>
<Copy className="mr-2 h-5 w-5" />
Copier le mot de passe
</>
)}
</Button>
</CardContent>
</Card>
);
}

View File

@ -1,30 +0,0 @@
"use client";
import { LogOut } from "lucide-react";
import { Button } from "@/components/ui/button";
import { useRouter } from "next/navigation";
export default function AdminLayout({ children }: { children: React.ReactNode }) {
const router = useRouter();
const handleLogout = () => {
localStorage.removeItem("adminPassword");
router.push("/admin/login");
};
return (
<div className="min-h-screen bg-background">
<header className="bg-white border-b border-gray-200 shadow-sm">
<div className="max-w-4xl mx-auto px-4 py-4 flex items-center justify-between">
<h1 className="text-xl font-bold text-primary">Administration</h1>
<Button variant="outline" size="sm" onClick={handleLogout}>
<LogOut className="h-4 w-4 mr-2" />
Déconnexion
</Button>
</div>
</header>
<main className="max-w-4xl mx-auto px-4 py-6">{children}</main>
</div>
);
}

View File

@ -1,264 +0,0 @@
"use client";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
import { Client, ClientInput } from "@/lib/types/client";
import QRCodeDisplay from "./QRCodeDisplay";
import { Copy, Check } from "lucide-react";
interface ClientFormProps {
client?: Client;
onSuccess: () => void;
onCancel: () => void;
}
export default function ClientForm({ client, onSuccess, onCancel }: ClientFormProps) {
const [formData, setFormData] = useState<ClientInput>({
email: client?.email || "",
bungalowNumber: client?.bungalowNumber || "",
wifiName: client?.wifiName || "Lagon-WiFi",
wifiPassword: client?.wifiPassword || "",
gerantMessage: client?.gerantMessage || "Bienvenue dans notre pension de famille !",
});
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [createdClient, setCreatedClient] = useState<Client | null>(client || null);
const [copied, setCopied] = useState(false);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
setError(null);
try {
const adminPassword = localStorage.getItem("adminPassword") || "";
const url = client
? `/api/admin/clients/${client.id}`
: "/api/admin/clients";
const method = client ? "PUT" : "POST";
const response = await fetch(url, {
method,
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${adminPassword}`,
},
body: JSON.stringify(formData),
});
if (!response.ok) {
const data = await response.json();
throw new Error(data.error || "Erreur lors de la sauvegarde");
}
const data = await response.json();
setCreatedClient(data);
onSuccess();
} catch (err: any) {
setError(err.message);
} finally {
setLoading(false);
}
};
const getClientUrl = () => {
if (!createdClient) return "";
const baseUrl = typeof window !== "undefined" ? window.location.origin : "";
return `${baseUrl}/accueil?token=${createdClient.token}`;
};
const handleCopyLink = async () => {
const url = getClientUrl();
try {
await navigator.clipboard.writeText(url);
setCopied(true);
// Alerte pour confirmer
alert(`✅ Lien copié !\n\n${url}\n\nVous pouvez maintenant le coller (Ctrl+V) pour le partager avec votre client.`);
setTimeout(() => setCopied(false), 3000);
} catch (err) {
console.error("Erreur lors de la copie:", err);
// Fallback pour les navigateurs plus anciens
const textArea = document.createElement("textarea");
textArea.value = url;
textArea.style.position = "fixed";
textArea.style.left = "-999999px";
document.body.appendChild(textArea);
textArea.select();
try {
const successful = document.execCommand("copy");
if (successful) {
setCopied(true);
alert(`✅ Lien copié !\n\n${url}\n\nVous pouvez maintenant le coller (Ctrl+V) pour le partager avec votre client.`);
setTimeout(() => setCopied(false), 3000);
} else {
alert(`❌ Copie automatique non supportée.\n\nVeuillez copier manuellement le lien:\n\n${url}`);
}
} catch (e) {
alert(`❌ Copie automatique non supportée.\n\nVeuillez copier manuellement le lien:\n\n${url}`);
}
document.body.removeChild(textArea);
}
};
return (
<Card>
<CardHeader>
<CardTitle>{client ? "Modifier le client" : "Nouveau client"}</CardTitle>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Email *
</label>
<input
type="email"
required
value={formData.email}
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
disabled={!!client}
className="w-full px-4 py-2 border border-gray-300 rounded-xl focus:ring-2 focus:ring-primary focus:border-transparent disabled:bg-gray-100"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Numéro de bungalow *
</label>
<input
type="text"
required
value={formData.bungalowNumber}
onChange={(e) =>
setFormData({ ...formData, bungalowNumber: e.target.value })
}
className="w-full px-4 py-2 border border-gray-300 rounded-xl focus:ring-2 focus:ring-primary focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Nom du WiFi
</label>
<input
type="text"
value={formData.wifiName}
onChange={(e) =>
setFormData({ ...formData, wifiName: e.target.value })
}
className="w-full px-4 py-2 border border-gray-300 rounded-xl focus:ring-2 focus:ring-primary focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Mot de passe WiFi
</label>
<input
type="text"
value={formData.wifiPassword}
onChange={(e) =>
setFormData({ ...formData, wifiPassword: e.target.value })
}
className="w-full px-4 py-2 border border-gray-300 rounded-xl focus:ring-2 focus:ring-primary focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Message du gérant
</label>
<textarea
value={formData.gerantMessage}
onChange={(e) =>
setFormData({ ...formData, gerantMessage: e.target.value })
}
rows={3}
className="w-full px-4 py-2 border border-gray-300 rounded-xl focus:ring-2 focus:ring-primary focus:border-transparent"
/>
</div>
{error && (
<div className="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-xl text-sm">
{error}
</div>
)}
<div className="flex gap-3">
<Button type="button" variant="outline" onClick={onCancel} className="flex-1">
Annuler
</Button>
<Button type="submit" disabled={loading} className="flex-1">
{loading ? "Enregistrement..." : client ? "Modifier" : "Créer"}
</Button>
</div>
</form>
{createdClient && !client && (
<div className="mt-6 pt-6 border-t border-gray-200">
<h3 className="font-semibold text-primary mb-3">Client créé avec succès !</h3>
<div className="space-y-4">
<div>
<p className="text-sm font-medium text-gray-700 mb-2">Lien unique :</p>
<div className="bg-secondary rounded-xl p-4 space-y-3">
<textarea
readOnly
value={getClientUrl()}
onClick={(e) => e.currentTarget.select()}
onFocus={(e) => e.currentTarget.select()}
className="w-full p-3 text-sm font-mono text-primary bg-white border-2 border-primary rounded-lg resize-none"
rows={3}
style={{ cursor: 'text' }}
/>
<div className="flex gap-2">
<Button
size="sm"
onClick={handleCopyLink}
className={`flex-1 ${copied ? "bg-green-600 hover:bg-green-700" : ""}`}
>
{copied ? (
<>
<Check className="h-4 w-4 mr-2" />
Copié !
</>
) : (
<>
<Copy className="h-4 w-4 mr-2" />
Copier
</>
)}
</Button>
<Button
size="sm"
variant="outline"
onClick={() => {
const textarea = document.querySelector('textarea[readonly]') as HTMLTextAreaElement;
if (textarea) {
textarea.select();
}
}}
className="flex-1"
>
Sélectionner
</Button>
</div>
</div>
<p className="text-xs text-gray-500 mt-2">
💡 Cliquez sur le lien pour le sélectionner, puis Ctrl+C pour copier
</p>
</div>
<div>
<p className="text-sm text-gray-600 mb-2">QR Code :</p>
<QRCodeDisplay url={getClientUrl()} />
</div>
</div>
</div>
)}
</CardContent>
</Card>
);
}

View File

@ -1,165 +0,0 @@
"use client";
import { useState, useEffect } from "react";
import { Trash2, Edit, Copy, QrCode } from "lucide-react";
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Client } from "@/lib/types/client";
import QRCodeDisplay from "./QRCodeDisplay";
interface ClientListProps {
onEdit: (client: Client) => void;
onRefresh: () => void;
}
export default function ClientList({ onEdit, onRefresh }: ClientListProps) {
const [clients, setClients] = useState<Client[]>([]);
const [loading, setLoading] = useState(true);
const [selectedClient, setSelectedClient] = useState<Client | null>(null);
const [showQR, setShowQR] = useState<string | null>(null);
useEffect(() => {
fetchClients();
}, []);
const fetchClients = async () => {
try {
const adminPassword = localStorage.getItem("adminPassword") || "";
const response = await fetch("/api/admin/clients", {
headers: {
Authorization: `Bearer ${adminPassword}`,
},
});
if (response.ok) {
const data = await response.json();
setClients(data);
}
} catch (error) {
console.error("Erreur lors du chargement des clients:", error);
} finally {
setLoading(false);
}
};
const handleDelete = async (id: string) => {
if (!confirm("Êtes-vous sûr de vouloir supprimer ce client ?")) {
return;
}
try {
const adminPassword = localStorage.getItem("adminPassword") || "";
const response = await fetch(`/api/admin/clients/${id}`, {
method: "DELETE",
headers: {
Authorization: `Bearer ${adminPassword}`,
},
});
if (response.ok) {
fetchClients();
onRefresh();
}
} catch (error) {
console.error("Erreur lors de la suppression:", error);
}
};
const getClientUrl = (token: string) => {
const baseUrl = typeof window !== "undefined" ? window.location.origin : "";
return `${baseUrl}/accueil?token=${token}`;
};
if (loading) {
return (
<div className="flex items-center justify-center py-8">
<p className="text-gray-600">Chargement...</p>
</div>
);
}
if (clients.length === 0) {
return (
<Card>
<CardContent className="py-8 text-center">
<p className="text-gray-600">Aucun client pour le moment.</p>
</CardContent>
</Card>
);
}
return (
<div className="space-y-4">
{clients.map((client) => (
<Card key={client.id}>
<CardHeader>
<div className="flex items-start justify-between">
<div className="flex-1">
<CardTitle className="text-lg">{client.email}</CardTitle>
<p className="text-sm text-gray-600 mt-1">
Bungalow {client.bungalowNumber}
</p>
</div>
<div className="flex gap-2">
<Button
size="sm"
variant="outline"
onClick={() => {
const url = getClientUrl(client.token);
navigator.clipboard.writeText(url);
}}
>
<Copy className="h-4 w-4" />
</Button>
<Button
size="sm"
variant="outline"
onClick={() =>
setShowQR(showQR === client.id ? null : client.id)
}
>
<QrCode className="h-4 w-4" />
</Button>
<Button
size="sm"
variant="outline"
onClick={() => onEdit(client)}
>
<Edit className="h-4 w-4" />
</Button>
<Button
size="sm"
variant="outline"
onClick={() => handleDelete(client.id)}
>
<Trash2 className="h-4 w-4" />
</Button>
</div>
</div>
</CardHeader>
<CardContent>
<div className="space-y-2 text-sm">
<div>
<span className="font-medium">WiFi:</span> {client.wifiName}
</div>
<div>
<span className="font-medium">Message:</span>{" "}
{client.gerantMessage.substring(0, 50)}
{client.gerantMessage.length > 50 ? "..." : ""}
</div>
<div className="text-xs text-gray-500">
Créé le {new Date(client.createdAt).toLocaleDateString("fr-FR")}
</div>
</div>
{showQR === client.id && (
<div className="mt-4 pt-4 border-t border-gray-200">
<QRCodeDisplay url={getClientUrl(client.token)} size={150} />
</div>
)}
</CardContent>
</Card>
))}
</div>
);
}

View File

@ -1,100 +0,0 @@
"use client";
import { useState } from "react";
import { QRCodeSVG } from "qrcode.react";
import { Button } from "@/components/ui/button";
import { Copy, Check } from "lucide-react";
interface QRCodeDisplayProps {
url: string;
size?: number;
}
export default function QRCodeDisplay({ url, size = 200 }: QRCodeDisplayProps) {
const [copied, setCopied] = useState(false);
const handleCopyLink = async () => {
try {
await navigator.clipboard.writeText(url);
setCopied(true);
alert(`✅ Lien copié !\n\n${url}\n\nVous pouvez maintenant le coller (Ctrl+V) pour le partager avec votre client.`);
setTimeout(() => setCopied(false), 3000);
} catch (err) {
console.error("Erreur lors de la copie:", err);
const textArea = document.createElement("textarea");
textArea.value = url;
textArea.style.position = "fixed";
textArea.style.left = "-999999px";
document.body.appendChild(textArea);
textArea.select();
try {
const successful = document.execCommand("copy");
if (successful) {
setCopied(true);
alert(`✅ Lien copié !\n\n${url}\n\nVous pouvez maintenant le coller (Ctrl+V) pour le partager avec votre client.`);
setTimeout(() => setCopied(false), 3000);
} else {
alert(`Copiez ce lien manuellement:\n\n${url}`);
}
} catch (e) {
alert(`Copiez ce lien manuellement:\n\n${url}`);
}
document.body.removeChild(textArea);
}
};
return (
<div className="flex flex-col items-center gap-4 p-6 bg-white rounded-2xl border border-gray-200">
<QRCodeSVG value={url} size={size} level="H" />
<div className="w-full space-y-3">
<p className="text-sm font-medium text-gray-700">Lien unique :</p>
<textarea
readOnly
value={url}
onClick={(e) => e.currentTarget.select()}
onFocus={(e) => e.currentTarget.select()}
className="w-full p-3 text-sm font-mono text-primary bg-secondary border-2 border-primary rounded-lg resize-none"
rows={3}
style={{ cursor: 'text' }}
/>
<div className="flex gap-2">
<Button
size="sm"
onClick={handleCopyLink}
className={`flex-1 ${copied ? "bg-green-600 hover:bg-green-700" : ""}`}
>
{copied ? (
<>
<Check className="h-4 w-4 mr-2" />
Copié !
</>
) : (
<>
<Copy className="h-4 w-4 mr-2" />
Copier
</>
)}
</Button>
<Button
size="sm"
variant="outline"
onClick={() => {
const textarea = document.querySelector('textarea[readonly]') as HTMLTextAreaElement;
if (textarea) {
textarea.select();
}
}}
className="flex-1"
>
Sélectionner
</Button>
</div>
<p className="text-xs text-gray-500 text-center">
💡 Cliquez sur le lien pour le sélectionner, puis Ctrl+C pour copier
</p>
</div>
</div>
);
}

View File

@ -1,5 +1,6 @@
"use client"; "use client";
import { useState } from "react";
import { MapPin, ExternalLink } from "lucide-react"; import { MapPin, ExternalLink } from "lucide-react";
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"; import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
@ -10,6 +11,9 @@ interface PlaceCardProps {
} }
export default function PlaceCard({ place }: PlaceCardProps) { export default function PlaceCard({ place }: PlaceCardProps) {
const [imageError, setImageError] = useState(false);
const hasValidImage = place.image && place.image !== "";
const handleOpenMaps = () => { const handleOpenMaps = () => {
let url: string; let url: string;
if (place.gmapLink && place.gmapLink !== "LIEN_GOOGLE_MAPS_A_INSERER") { if (place.gmapLink && place.gmapLink !== "LIEN_GOOGLE_MAPS_A_INSERER") {
@ -22,10 +26,24 @@ export default function PlaceCard({ place }: PlaceCardProps) {
return ( return (
<Card className="overflow-hidden"> <Card className="overflow-hidden">
<div className="relative h-48 bg-gradient-to-br from-primary/20 to-secondary"> <div className="relative h-48 bg-gradient-to-br from-primary/20 to-secondary overflow-hidden">
<div className="absolute inset-0 flex items-center justify-center"> {hasValidImage && !imageError ? (
<MapPin className="h-16 w-16 text-primary/30" /> <img
</div> src={place.image}
alt={place.name}
className="w-full h-full object-cover"
style={{
imageRendering: 'auto',
} as React.CSSProperties}
loading="lazy"
fetchPriority="high"
onError={() => setImageError(true)}
/>
) : (
<div className="absolute inset-0 flex items-center justify-center">
<MapPin className="h-16 w-16 text-primary/30" />
</div>
)}
</div> </div>
<CardHeader> <CardHeader>
<div className="flex items-start justify-between gap-2"> <div className="flex items-start justify-between gap-2">

View File

@ -2,7 +2,7 @@ import TabNavigation from "./TabNavigation";
export default function Layout({ children }: { children: React.ReactNode }) { export default function Layout({ children }: { children: React.ReactNode }) {
return ( return (
<div className="min-h-screen bg-background pb-16"> <div className="min-h-screen bg-background dark:bg-background-dark pb-16">
{children} {children}
<TabNavigation /> <TabNavigation />
</div> </div>

View File

@ -4,6 +4,7 @@ import Link from "next/link";
import { usePathname } from "next/navigation"; import { usePathname } from "next/navigation";
import { Home, MapPin, Info, Waves } from "lucide-react"; import { Home, MapPin, Info, Waves } from "lucide-react";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { ThemeToggle } from "@/components/ThemeToggle";
const tabs = [ const tabs = [
{ {
@ -32,7 +33,7 @@ export default function TabNavigation() {
const pathname = usePathname(); const pathname = usePathname();
return ( return (
<nav className="fixed bottom-0 left-0 right-0 z-50 bg-white border-t border-gray-200 shadow-lg"> <nav className="fixed bottom-0 left-0 right-0 z-50 bg-white dark:bg-gray-900 border-t border-gray-200 dark:border-gray-800 shadow-lg">
<div className="flex items-center justify-around h-16 px-2"> <div className="flex items-center justify-around h-16 px-2">
{tabs.map((tab) => { {tabs.map((tab) => {
const Icon = tab.icon; const Icon = tab.icon;
@ -44,8 +45,8 @@ export default function TabNavigation() {
className={cn( className={cn(
"flex flex-col items-center justify-center gap-1 flex-1 h-full rounded-xl transition-colors", "flex flex-col items-center justify-center gap-1 flex-1 h-full rounded-xl transition-colors",
isActive isActive
? "text-primary bg-secondary" ? "text-primary bg-secondary dark:bg-primary/20"
: "text-gray-500 hover:text-primary hover:bg-gray-50" : "text-gray-500 dark:text-gray-400 hover:text-primary dark:hover:text-primary hover:bg-gray-50 dark:hover:bg-gray-800"
)} )}
> >
<Icon className="h-6 w-6" /> <Icon className="h-6 w-6" />
@ -53,6 +54,9 @@ export default function TabNavigation() {
</Link> </Link>
); );
})} })}
<div className="flex items-center justify-center h-full px-2">
<ThemeToggle />
</div>
</div> </div>
</nav> </nav>
); );

View File

@ -7,10 +7,10 @@ const buttonVariants = cva(
{ {
variants: { variants: {
variant: { variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90", default: "bg-primary text-primary-foreground hover:bg-primary/90 dark:bg-primary dark:hover:bg-primary/80",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80", secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 dark:bg-primary/20 dark:text-primary dark:hover:bg-primary/30",
outline: "border-2 border-primary text-primary hover:bg-primary hover:text-white", outline: "border-2 border-primary text-primary hover:bg-primary hover:text-white dark:border-primary dark:text-primary dark:hover:bg-primary dark:hover:text-white",
ghost: "hover:bg-secondary hover:text-secondary-foreground", ghost: "hover:bg-secondary hover:text-secondary-foreground dark:hover:bg-gray-800 dark:hover:text-gray-200",
}, },
size: { size: {
default: "h-12 px-6 py-3", default: "h-12 px-6 py-3",

View File

@ -8,7 +8,7 @@ const Card = React.forwardRef<
<div <div
ref={ref} ref={ref}
className={cn( className={cn(
"rounded-2xl border border-gray-200 bg-white shadow-sm", "rounded-2xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 shadow-sm",
className className
)} )}
{...props} {...props}
@ -46,7 +46,7 @@ const CardDescription = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<p <p
ref={ref} ref={ref}
className={cn("text-sm text-gray-600", className)} className={cn("text-sm text-gray-600 dark:text-gray-400", className)}
{...props} {...props}
/> />
)); ));

5
deploy/.env.example Normal file
View File

@ -0,0 +1,5 @@
# Mot de passe admin
ADMIN_PASSWORD=votre_mot_de_passe_securise
# Port (optionnel, par défaut 3000)
PORT=3000

1
deploy/.next/BUILD_ID Normal file
View File

@ -0,0 +1 @@
3c87ZLiEKCTo7fN65B1s7

View File

@ -0,0 +1,88 @@
{
"pages": {
"/_not-found/page": [
"static/chunks/webpack-272d92e0b8f870de.js",
"static/chunks/fd9d1056-f29a34e8cb4f1ac5.js",
"static/chunks/117-81b684a50b4e5a2c.js",
"static/chunks/main-app-be78d15d42f0ba33.js",
"static/chunks/app/_not-found/page-93c891698b4decca.js"
],
"/layout": [
"static/chunks/webpack-272d92e0b8f870de.js",
"static/chunks/fd9d1056-f29a34e8cb4f1ac5.js",
"static/chunks/117-81b684a50b4e5a2c.js",
"static/chunks/main-app-be78d15d42f0ba33.js",
"static/css/37740839d02c8860.css",
"static/chunks/app/layout-012feff5f2309838.js"
],
"/accueil/page": [
"static/chunks/webpack-272d92e0b8f870de.js",
"static/chunks/fd9d1056-f29a34e8cb4f1ac5.js",
"static/chunks/117-81b684a50b4e5a2c.js",
"static/chunks/main-app-be78d15d42f0ba33.js",
"static/chunks/130-fbdba5ec657d70e2.js",
"static/chunks/876-74e3127f7295a7cc.js",
"static/chunks/app/accueil/page-eeceff806dc9e963.js"
],
"/admin/login/page": [
"static/chunks/webpack-272d92e0b8f870de.js",
"static/chunks/fd9d1056-f29a34e8cb4f1ac5.js",
"static/chunks/117-81b684a50b4e5a2c.js",
"static/chunks/main-app-be78d15d42f0ba33.js",
"static/chunks/130-fbdba5ec657d70e2.js",
"static/chunks/app/admin/login/page-64d4a547eaf512e7.js"
],
"/admin/page": [
"static/chunks/webpack-272d92e0b8f870de.js",
"static/chunks/fd9d1056-f29a34e8cb4f1ac5.js",
"static/chunks/117-81b684a50b4e5a2c.js",
"static/chunks/main-app-be78d15d42f0ba33.js",
"static/chunks/130-fbdba5ec657d70e2.js",
"static/chunks/659-547b5910f7777d12.js",
"static/chunks/app/admin/page-be00d3b5e7fb599b.js"
],
"/explorer/page": [
"static/chunks/webpack-272d92e0b8f870de.js",
"static/chunks/fd9d1056-f29a34e8cb4f1ac5.js",
"static/chunks/117-81b684a50b4e5a2c.js",
"static/chunks/main-app-be78d15d42f0ba33.js",
"static/chunks/130-fbdba5ec657d70e2.js",
"static/chunks/876-74e3127f7295a7cc.js",
"static/chunks/app/explorer/page-94fbb37299b728f0.js"
],
"/infos/page": [
"static/chunks/webpack-272d92e0b8f870de.js",
"static/chunks/fd9d1056-f29a34e8cb4f1ac5.js",
"static/chunks/117-81b684a50b4e5a2c.js",
"static/chunks/main-app-be78d15d42f0ba33.js",
"static/chunks/130-fbdba5ec657d70e2.js",
"static/chunks/876-74e3127f7295a7cc.js",
"static/chunks/app/infos/page-9c23965cb3e698cb.js"
],
"/mana-tracker/page": [
"static/chunks/webpack-272d92e0b8f870de.js",
"static/chunks/fd9d1056-f29a34e8cb4f1ac5.js",
"static/chunks/117-81b684a50b4e5a2c.js",
"static/chunks/main-app-be78d15d42f0ba33.js",
"static/chunks/130-fbdba5ec657d70e2.js",
"static/chunks/876-74e3127f7295a7cc.js",
"static/chunks/app/mana-tracker/page-190454f13d1cd3f9.js"
],
"/offline/page": [
"static/chunks/webpack-272d92e0b8f870de.js",
"static/chunks/fd9d1056-f29a34e8cb4f1ac5.js",
"static/chunks/117-81b684a50b4e5a2c.js",
"static/chunks/main-app-be78d15d42f0ba33.js",
"static/chunks/130-fbdba5ec657d70e2.js",
"static/chunks/876-74e3127f7295a7cc.js",
"static/chunks/app/offline/page-c706c75d3862967d.js"
],
"/page": [
"static/chunks/webpack-272d92e0b8f870de.js",
"static/chunks/fd9d1056-f29a34e8cb4f1ac5.js",
"static/chunks/117-81b684a50b4e5a2c.js",
"static/chunks/main-app-be78d15d42f0ba33.js",
"static/chunks/app/page-66582cb216c486f4.js"
]
}
}

View File

@ -0,0 +1 @@
{"/_not-found/page":"/_not-found","/accueil/page":"/accueil","/admin/login/page":"/admin/login","/admin/page":"/admin","/api/admin/clients/route":"/api/admin/clients","/explorer/page":"/explorer","/infos/page":"/infos","/mana-tracker/page":"/mana-tracker","/offline/page":"/offline","/page":"/"}

View File

@ -0,0 +1,32 @@
{
"polyfillFiles": [
"static/chunks/polyfills-42372ed130431b0a.js"
],
"devFiles": [],
"ampDevFiles": [],
"lowPriorityFiles": [
"static/3c87ZLiEKCTo7fN65B1s7/_buildManifest.js",
"static/3c87ZLiEKCTo7fN65B1s7/_ssgManifest.js"
],
"rootMainFiles": [
"static/chunks/webpack-272d92e0b8f870de.js",
"static/chunks/fd9d1056-f29a34e8cb4f1ac5.js",
"static/chunks/117-81b684a50b4e5a2c.js",
"static/chunks/main-app-be78d15d42f0ba33.js"
],
"pages": {
"/_app": [
"static/chunks/webpack-272d92e0b8f870de.js",
"static/chunks/framework-f66176bb897dc684.js",
"static/chunks/main-98da237dfd0126ae.js",
"static/chunks/pages/_app-72b849fbd24ac258.js"
],
"/_error": [
"static/chunks/webpack-272d92e0b8f870de.js",
"static/chunks/framework-f66176bb897dc684.js",
"static/chunks/main-98da237dfd0126ae.js",
"static/chunks/pages/_error-7ba65e1336b92748.js"
]
},
"ampFirstPages": []
}

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@
{"version":1,"hasExportPathMap":false,"exportTrailingSlash":false,"isNextImageImported":false}

View File

@ -0,0 +1 @@
{"version":1,"images":{"deviceSizes":[640,750,828,1080,1200,1920,2048,3840],"imageSizes":[16,32,48,64,96,128,256,384],"path":"/_next/image","loader":"default","loaderFile":"","domains":[],"disableStaticImages":false,"minimumCacheTTL":60,"formats":["image/avif","image/webp"],"dangerouslyAllowSVG":false,"contentSecurityPolicy":"script-src 'none'; frame-src 'none'; sandbox;","contentDispositionType":"inline","remotePatterns":[],"unoptimized":false,"sizes":[640,750,828,1080,1200,1920,2048,3840,16,32,48,64,96,128,256,384]}}

View File

@ -0,0 +1 @@
{"version":1,"files":["../node_modules/styled-jsx/index.js","../node_modules/styled-jsx/package.json","../node_modules/styled-jsx/dist/index/index.js","../node_modules/react/package.json","../node_modules/react/index.js","../node_modules/client-only/package.json","../node_modules/react/cjs/react.production.min.js","../node_modules/client-only/index.js","../node_modules/styled-jsx/style.js","../node_modules/next/dist/compiled/next-server/server.runtime.prod.js","../node_modules/next/package.json","../node_modules/next/dist/server/body-streams.js","../node_modules/next/dist/lib/constants.js","../node_modules/next/dist/lib/picocolors.js","../node_modules/next/dist/shared/lib/constants.js","../node_modules/next/dist/server/web/utils.js","../node_modules/next/dist/client/components/app-router-headers.js","../node_modules/next/dist/server/lib/trace/tracer.js","../node_modules/next/dist/server/lib/trace/constants.js","../node_modules/next/dist/client/components/static-generation-async-storage.external.js","../node_modules/next/dist/shared/lib/modern-browserslist-target.js","../node_modules/next/dist/client/components/static-generation-async-storage-instance.js","../node_modules/next/dist/shared/lib/runtime-config.external.js","../node_modules/next/dist/compiled/ws/package.json","../node_modules/next/dist/compiled/node-html-parser/package.json","../node_modules/next/dist/compiled/lru-cache/package.json","../node_modules/@swc/helpers/_/_interop_require_default/package.json","../node_modules/next/dist/client/components/async-local-storage.js","../node_modules/next/dist/compiled/ws/index.js","../node_modules/next/dist/compiled/node-html-parser/index.js","../node_modules/next/dist/compiled/lru-cache/index.js","../node_modules/next/dist/compiled/@opentelemetry/api/package.json","../node_modules/@swc/helpers/package.json","../node_modules/next/dist/compiled/jsonwebtoken/package.json","../node_modules/next/dist/shared/lib/error-source.js","../node_modules/@swc/helpers/cjs/_interop_require_default.cjs","../node_modules/next/dist/compiled/@opentelemetry/api/index.js","../node_modules/next/dist/compiled/jsonwebtoken/index.js","../node_modules/next/dist/compiled/debug/package.json","../node_modules/next/dist/lib/semver-noop.js","../node_modules/next/dist/compiled/debug/index.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/amp-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/app-router-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/entrypoints.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/head-manager-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/hooks-client-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/html-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/image-config-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/loadable-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/loadable.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/router-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/server-inserted-html.js","../node_modules/next/dist/server/future/route-modules/app-page/module.compiled.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/amp-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/app-router-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/entrypoints.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/head-manager-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/hooks-client-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/html-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/image-config-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/loadable-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/loadable.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/router-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/server-inserted-html.js","../node_modules/next/dist/server/future/route-modules/pages/module.compiled.js"]}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"type": "commonjs"}

View File

@ -0,0 +1 @@
{"version":4,"routes":{"/infos":{"experimentalBypassFor":[{"type":"header","key":"Next-Action"},{"type":"header","key":"content-type","value":"multipart/form-data;.*"}],"initialRevalidateSeconds":false,"srcRoute":"/infos","dataRoute":"/infos.rsc"},"/mana-tracker":{"experimentalBypassFor":[{"type":"header","key":"Next-Action"},{"type":"header","key":"content-type","value":"multipart/form-data;.*"}],"initialRevalidateSeconds":false,"srcRoute":"/mana-tracker","dataRoute":"/mana-tracker.rsc"},"/offline":{"experimentalBypassFor":[{"type":"header","key":"Next-Action"},{"type":"header","key":"content-type","value":"multipart/form-data;.*"}],"initialRevalidateSeconds":false,"srcRoute":"/offline","dataRoute":"/offline.rsc"},"/":{"experimentalBypassFor":[{"type":"header","key":"Next-Action"},{"type":"header","key":"content-type","value":"multipart/form-data;.*"}],"initialRevalidateSeconds":false,"srcRoute":"/","dataRoute":"/index.rsc"},"/accueil":{"experimentalBypassFor":[{"type":"header","key":"Next-Action"},{"type":"header","key":"content-type","value":"multipart/form-data;.*"}],"initialRevalidateSeconds":false,"srcRoute":"/accueil","dataRoute":"/accueil.rsc"},"/admin":{"experimentalBypassFor":[{"type":"header","key":"Next-Action"},{"type":"header","key":"content-type","value":"multipart/form-data;.*"}],"initialRevalidateSeconds":false,"srcRoute":"/admin","dataRoute":"/admin.rsc"},"/explorer":{"experimentalBypassFor":[{"type":"header","key":"Next-Action"},{"type":"header","key":"content-type","value":"multipart/form-data;.*"}],"initialRevalidateSeconds":false,"srcRoute":"/explorer","dataRoute":"/explorer.rsc"},"/admin/login":{"experimentalBypassFor":[{"type":"header","key":"Next-Action"},{"type":"header","key":"content-type","value":"multipart/form-data;.*"}],"initialRevalidateSeconds":false,"srcRoute":"/admin/login","dataRoute":"/admin/login.rsc"}},"dynamicRoutes":{},"notFoundRoutes":[],"preview":{"previewModeId":"72798059332883bb836619ead11f265d","previewModeSigningKey":"c91fe6a8fb8a6afd08166f7488f47956827aa8492d588a1be69527dd7453feda","previewModeEncryptionKey":"b90ebc3c46fd8e2c88f96f242898610916379d8d4dc5cc0f98f6d0bb8b887095"}}

View File

@ -0,0 +1,14 @@
{
"app/accueil/page.tsx -> @/components/accueil/WeatherWidget": {
"id": 5147,
"files": [
"static/chunks/147.f0c63fadc263cdbf.js"
]
},
"app/explorer/page.tsx -> @/components/explorer/PlaceList": {
"id": 3293,
"files": [
"static/chunks/80.c30ab4050c6d10af.js"
]
}
}

View File

@ -0,0 +1 @@
{"version":1,"config":{"env":{},"webpack":null,"eslint":{"ignoreDuringBuilds":false},"typescript":{"ignoreBuildErrors":false,"tsconfigPath":"tsconfig.json"},"distDir":".next","cleanDistDir":true,"assetPrefix":"","cacheMaxMemorySize":52428800,"configOrigin":"next.config.js","useFileSystemPublicRoutes":true,"generateEtags":true,"pageExtensions":["tsx","ts","jsx","js"],"poweredByHeader":false,"compress":true,"analyticsId":"","images":{"deviceSizes":[640,750,828,1080,1200,1920,2048,3840],"imageSizes":[16,32,48,64,96,128,256,384],"path":"/_next/image","loader":"default","loaderFile":"","domains":[],"disableStaticImages":false,"minimumCacheTTL":60,"formats":["image/avif","image/webp"],"dangerouslyAllowSVG":false,"contentSecurityPolicy":"script-src 'none'; frame-src 'none'; sandbox;","contentDispositionType":"inline","remotePatterns":[],"unoptimized":false},"devIndicators":{"buildActivity":true,"buildActivityPosition":"bottom-right"},"onDemandEntries":{"maxInactiveAge":60000,"pagesBufferLength":5},"amp":{"canonicalBase":""},"basePath":"","sassOptions":{},"trailingSlash":false,"i18n":null,"productionBrowserSourceMaps":false,"optimizeFonts":true,"excludeDefaultMomentLocales":true,"serverRuntimeConfig":{},"publicRuntimeConfig":{},"reactProductionProfiling":false,"reactStrictMode":true,"httpAgentOptions":{"keepAlive":true},"outputFileTracing":true,"staticPageGenerationTimeout":60,"swcMinify":true,"modularizeImports":{"@mui/icons-material":{"transform":"@mui/icons-material/{{member}}"},"lodash":{"transform":"lodash/{{member}}"}},"experimental":{"multiZoneDraftMode":false,"prerenderEarlyExit":false,"serverMinification":true,"serverSourceMaps":false,"linkNoTouchStart":false,"caseSensitiveRoutes":false,"clientRouterFilter":true,"clientRouterFilterRedirects":false,"fetchCacheKeyPrefix":"","middlewarePrefetch":"flexible","optimisticClientCache":true,"manualClientBasePath":false,"cpus":7,"memoryBasedWorkersCount":false,"isrFlushToDisk":true,"workerThreads":false,"optimizeCss":false,"nextScriptWorkers":false,"scrollRestoration":false,"externalDir":false,"disableOptimizedLoading":false,"gzipSize":true,"craCompat":false,"esmExternals":true,"fullySpecified":false,"outputFileTracingRoot":"/home/syoul/Ccompagnon Marama","swcTraceProfiling":false,"forceSwcTransforms":false,"largePageDataBytes":128000,"adjustFontFallbacks":false,"adjustFontFallbacksWithSizeAdjust":false,"typedRoutes":false,"instrumentationHook":false,"bundlePagesExternals":false,"parallelServerCompiles":false,"parallelServerBuildTraces":false,"ppr":false,"missingSuspenseWithCSRBailout":true,"optimizeServerReact":true,"useEarlyImport":false,"staleTimes":{"dynamic":30,"static":300},"optimizePackageImports":["lucide-react","date-fns","lodash-es","ramda","antd","react-bootstrap","ahooks","@ant-design/icons","@headlessui/react","@headlessui-float/react","@heroicons/react/20/solid","@heroicons/react/24/solid","@heroicons/react/24/outline","@visx/visx","@tremor/react","rxjs","@mui/material","@mui/icons-material","recharts","react-use","@material-ui/core","@material-ui/icons","@tabler/icons-react","mui-core","react-icons/ai","react-icons/bi","react-icons/bs","react-icons/cg","react-icons/ci","react-icons/di","react-icons/fa","react-icons/fa6","react-icons/fc","react-icons/fi","react-icons/gi","react-icons/go","react-icons/gr","react-icons/hi","react-icons/hi2","react-icons/im","react-icons/io","react-icons/io5","react-icons/lia","react-icons/lib","react-icons/lu","react-icons/md","react-icons/pi","react-icons/ri","react-icons/rx","react-icons/si","react-icons/sl","react-icons/tb","react-icons/tfi","react-icons/ti","react-icons/vsc","react-icons/wi"],"trustHostHeader":false,"isExperimentalCompile":false},"configFileName":"next.config.js"},"appDir":"/home/syoul/Ccompagnon Marama","relativeAppDir":"","files":[".next/routes-manifest.json",".next/server/pages-manifest.json",".next/build-manifest.json",".next/prerender-manifest.json",".next/server/middleware-manifest.json",".next/server/middleware-build-manifest.js",".next/server/middleware-react-loadable-manifest.js",".next/server/app-paths-manifest.json",".next/app-path-routes-manifest.json",".next/app-build-manifest.json",".next/server/server-reference-manifest.js",".next/server/server-reference-manifest.json",".next/react-loadable-manifest.json",".next/server/font-manifest.json",".next/BUILD_ID",".next/server/next-font-manifest.js",".next/server/next-font-manifest.json"],"ignore":["node_modules/next/dist/compiled/@ampproject/toolbox-optimizer/**/*"]}

View File

@ -0,0 +1,12 @@
{
"/_not-found/page": "app/_not-found/page.js",
"/accueil/page": "app/accueil/page.js",
"/admin/login/page": "app/admin/login/page.js",
"/admin/page": "app/admin/page.js",
"/api/admin/clients/route": "app/api/admin/clients/route.js",
"/explorer/page": "app/explorer/page.js",
"/infos/page": "app/infos/page.js",
"/mana-tracker/page": "app/mana-tracker/page.js",
"/offline/page": "app/offline/page.js",
"/page": "app/page.js"
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,6 @@
{
"status": 404,
"headers": {
"x-next-cache-tags": "_N_T_/layout,_N_T_/_not-found/layout,_N_T_/_not-found/page,_N_T_/_not-found"
}
}

View File

@ -0,0 +1,11 @@
2:I[4707,[],""]
3:I[6423,[],""]
4:I[5495,["185","static/chunks/app/layout-012feff5f2309838.js"],"ThemeProvider"]
9:I[1454,["185","static/chunks/app/layout-012feff5f2309838.js"],"default"]
5:{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"}
6:{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"}
7:{"display":"inline-block"}
8:{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0}
0:["3c87ZLiEKCTo7fN65B1s7",[[["",{"children":["/_not-found",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",{"children":["/_not-found",{"children":["__PAGE__",{},[["$L1",[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],null],null],null]},[null,["$","$L2",null,{"parallelRouterKey":"children","segmentPath":["children","/_not-found","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}]],null]},[[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/37740839d02c8860.css","precedence":"next","crossOrigin":"$undefined"}]],["$","html",null,{"lang":"fr","suppressHydrationWarning":true,"children":[["$","head",null,{"children":[["$","link",null,{"rel":"icon","href":"/logo-relais-marama.svg","type":"image/svg+xml"}],["$","link",null,{"rel":"apple-touch-icon","href":"/logo-relais-marama.svg"}]]}],["$","body",null,{"className":"__className_f367f3","children":["$","$L4",null,{"attribute":"class","defaultTheme":"light","enableSystem":true,"disableTransitionOnChange":false,"children":[["$","$L2",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":"$5","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$6","children":"404"}],["$","div",null,{"style":"$7","children":["$","h2",null,{"style":"$8","children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}],["$","$L9",null,{}]]}]}]]}]],null],null],["$La",["$","meta",null,{"name":"robots","content":"noindex"}]]]]]
a:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"Compagnon du lagon - Pension Marama"}],["$","meta","3",{"name":"description","content":"Votre guide numérique pour votre séjour à Fakarava"}],["$","link","4",{"rel":"manifest","href":"/manifest.json","crossOrigin":"use-credentials"}],["$","meta","5",{"name":"apple-mobile-web-app-capable","content":"yes"}],["$","meta","6",{"name":"apple-mobile-web-app-title","content":"Compagnon du lagon - Pension Marama"}],["$","meta","7",{"name":"apple-mobile-web-app-status-bar-style","content":"default"}],["$","meta","8",{"name":"next-size-adjust"}]]
1:null

View File

@ -0,0 +1 @@
(()=>{var e={};e.id=409,e.ids=[409],e.modules={2934:e=>{"use strict";e.exports=require("next/dist/client/components/action-async-storage.external.js")},4580:e=>{"use strict";e.exports=require("next/dist/client/components/request-async-storage.external.js")},5869:e=>{"use strict";e.exports=require("next/dist/client/components/static-generation-async-storage.external.js")},399:e=>{"use strict";e.exports=require("next/dist/compiled/next-server/app-page.runtime.prod.js")},4390:(e,t,n)=>{"use strict";n.r(t),n.d(t,{GlobalError:()=>i.a,__next_app__:()=>p,originalPathname:()=>c,pages:()=>d,routeModule:()=>f,tree:()=>u}),n(7352),n(5866),n(5481);var r=n(3191),o=n(8716),a=n(7922),i=n.n(a),s=n(5231),l={};for(let e in s)0>["default","tree","pages","GlobalError","originalPathname","__next_app__","routeModule"].indexOf(e)&&(l[e]=()=>s[e]);n.d(t,l);let u=["",{children:["/_not-found",{children:["__PAGE__",{},{page:[()=>Promise.resolve().then(n.t.bind(n,5866,23)),"next/dist/client/components/not-found-error"]}]},{}]},{layout:[()=>Promise.resolve().then(n.bind(n,5481)),"/home/syoul/Ccompagnon Marama/app/layout.tsx"],"not-found":[()=>Promise.resolve().then(n.t.bind(n,5866,23)),"next/dist/client/components/not-found-error"]}],d=[],c="/_not-found/page",p={require:n,loadChunk:()=>Promise.resolve()},f=new r.AppPageRouteModule({definition:{kind:o.x.APP_PAGE,page:"/_not-found/page",pathname:"/_not-found",bundlePath:"",filename:"",appPaths:[]},userland:{loaderTree:u}})},7274:(e,t,n)=>{Promise.resolve().then(n.bind(n,7904)),Promise.resolve().then(n.bind(n,7459))},9401:(e,t,n)=>{Promise.resolve().then(n.t.bind(n,2994,23)),Promise.resolve().then(n.t.bind(n,6114,23)),Promise.resolve().then(n.t.bind(n,9727,23)),Promise.resolve().then(n.t.bind(n,9671,23)),Promise.resolve().then(n.t.bind(n,1868,23)),Promise.resolve().then(n.t.bind(n,4759,23))},7904:(e,t,n)=>{"use strict";function r(){return null}n.d(t,{default:()=>r}),n(7577)},7459:(e,t,n)=>{"use strict";n.d(t,{ThemeProvider:()=>a});var r=n(326),o=n(4831);function a({children:e,...t}){return r.jsx(o.f,{...t,children:e})}},5481:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>d,metadata:()=>u});var r=n(9510),o=n(5384),a=n.n(o);n(7272);var i=n(8570);let s=(0,i.createProxy)(String.raw`/home/syoul/Ccompagnon Marama/components/PWARegister.tsx#default`),l=(0,i.createProxy)(String.raw`/home/syoul/Ccompagnon Marama/components/ThemeProvider.tsx#ThemeProvider`),u={title:"Compagnon du lagon - Pension Marama",description:"Votre guide num\xe9rique pour votre s\xe9jour \xe0 Fakarava",manifest:"/manifest.json",themeColor:"#0E7490",appleWebApp:{capable:!0,statusBarStyle:"default",title:"Compagnon du lagon - Pension Marama"},viewport:{width:"device-width",initialScale:1,maximumScale:1,userScalable:!1}};function d({children:e}){return(0,r.jsxs)("html",{lang:"fr",suppressHydrationWarning:!0,children:[(0,r.jsxs)("head",{children:[r.jsx("link",{rel:"icon",href:"/logo-relais-marama.svg",type:"image/svg+xml"}),r.jsx("link",{rel:"apple-touch-icon",href:"/logo-relais-marama.svg"})]}),r.jsx("body",{className:a().className,children:(0,r.jsxs)(l,{attribute:"class",defaultTheme:"light",enableSystem:!0,disableTransitionOnChange:!1,children:[e,r.jsx(s,{})]})})]})}},6399:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),function(e,t){for(var n in t)Object.defineProperty(e,n,{enumerable:!0,get:t[n]})}(t,{isNotFoundError:function(){return o},notFound:function(){return r}});let n="NEXT_NOT_FOUND";function r(){let e=Error(n);throw e.digest=n,e}function o(e){return"object"==typeof e&&null!==e&&"digest"in e&&e.digest===n}("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&void 0===t.default.__esModule&&(Object.defineProperty(t.default,"__esModule",{value:!0}),Object.assign(t.default,t),e.exports=t.default)},7352:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),function(e,t){for(var n in t)Object.defineProperty(e,n,{enumerable:!0,get:t[n]})}(t,{PARALLEL_ROUTE_DEFAULT_PATH:function(){return o},default:function(){return a}});let r=n(6399),o="next/dist/client/components/parallel-route-default.js";function a(){(0,r.notFound)()}("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&void 0===t.default.__esModule&&(Object.defineProperty(t.default,"__esModule",{value:!0}),Object.assign(t.default,t),e.exports=t.default)},7272:()=>{}};var t=require("../../webpack-runtime.js");t.C(e);var n=e=>t(t.s=e),r=t.X(0,[276,740],()=>n(4390));module.exports=r})();

View File

@ -0,0 +1 @@
{"version":1,"files":["../../../../node_modules/next/dist/client/components/action-async-storage-instance.js","../../../../node_modules/next/dist/client/components/action-async-storage.external.js","../../../../node_modules/next/dist/client/components/async-local-storage.js","../../../../node_modules/next/dist/client/components/request-async-storage-instance.js","../../../../node_modules/next/dist/client/components/request-async-storage.external.js","../../../../node_modules/next/dist/client/components/static-generation-async-storage-instance.js","../../../../node_modules/next/dist/client/components/static-generation-async-storage.external.js","../../../../node_modules/next/dist/compiled/@opentelemetry/api/index.js","../../../../node_modules/next/dist/compiled/@opentelemetry/api/package.json","../../../../node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js","../../../../node_modules/next/dist/server/lib/trace/constants.js","../../../../node_modules/next/dist/server/lib/trace/tracer.js","../../../../node_modules/next/package.json","../../../package.json","../../chunks/276.js","../../chunks/740.js","../../webpack-runtime.js","page_client-reference-manifest.js"]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,5 @@
{
"headers": {
"x-next-cache-tags": "_N_T_/layout,_N_T_/accueil/layout,_N_T_/accueil/page,_N_T_/accueil"
}
}

View File

@ -0,0 +1,9 @@
2:I[9107,[],"ClientPageRoot"]
3:I[8666,["130","static/chunks/130-fbdba5ec657d70e2.js","876","static/chunks/876-74e3127f7295a7cc.js","206","static/chunks/app/accueil/page-eeceff806dc9e963.js"],"default",1]
4:I[4707,[],""]
5:I[6423,[],""]
6:I[5495,["185","static/chunks/app/layout-012feff5f2309838.js"],"ThemeProvider"]
7:I[1454,["185","static/chunks/app/layout-012feff5f2309838.js"],"default"]
0:["3c87ZLiEKCTo7fN65B1s7",[[["",{"children":["accueil",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",{"children":["accueil",{"children":["__PAGE__",{},[["$L1",["$","$L2",null,{"props":{"params":{},"searchParams":{}},"Component":"$3"}],null],null],null]},[null,["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children","accueil","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}]],null]},[[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/37740839d02c8860.css","precedence":"next","crossOrigin":"$undefined"}]],["$","html",null,{"lang":"fr","suppressHydrationWarning":true,"children":[["$","head",null,{"children":[["$","link",null,{"rel":"icon","href":"/logo-relais-marama.svg","type":"image/svg+xml"}],["$","link",null,{"rel":"apple-touch-icon","href":"/logo-relais-marama.svg"}]]}],["$","body",null,{"className":"__className_f367f3","children":["$","$L6",null,{"attribute":"class","defaultTheme":"light","enableSystem":true,"disableTransitionOnChange":false,"children":[["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}],["$","$L7",null,{}]]}]}]]}]],null],null],["$L8",null]]]]
8:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"Compagnon du lagon - Pension Marama"}],["$","meta","3",{"name":"description","content":"Votre guide numérique pour votre séjour à Fakarava"}],["$","link","4",{"rel":"manifest","href":"/manifest.json","crossOrigin":"use-credentials"}],["$","meta","5",{"name":"apple-mobile-web-app-capable","content":"yes"}],["$","meta","6",{"name":"apple-mobile-web-app-title","content":"Compagnon du lagon - Pension Marama"}],["$","meta","7",{"name":"apple-mobile-web-app-status-bar-style","content":"default"}],["$","meta","8",{"name":"next-size-adjust"}]]
1:null

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"version":1,"files":["../../../../node_modules/next/dist/client/components/action-async-storage-instance.js","../../../../node_modules/next/dist/client/components/action-async-storage.external.js","../../../../node_modules/next/dist/client/components/async-local-storage.js","../../../../node_modules/next/dist/client/components/request-async-storage-instance.js","../../../../node_modules/next/dist/client/components/request-async-storage.external.js","../../../../node_modules/next/dist/client/components/static-generation-async-storage-instance.js","../../../../node_modules/next/dist/client/components/static-generation-async-storage.external.js","../../../../node_modules/next/dist/compiled/@opentelemetry/api/index.js","../../../../node_modules/next/dist/compiled/@opentelemetry/api/package.json","../../../../node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js","../../../../node_modules/next/dist/server/lib/trace/constants.js","../../../../node_modules/next/dist/server/lib/trace/tracer.js","../../../../node_modules/next/package.json","../../../../package.json","../../../package.json","../../chunks/276.js","../../chunks/341.js","../../chunks/740.js","../../chunks/87.js","../../webpack-runtime.js","page_client-reference-manifest.js"]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,5 @@
{
"headers": {
"x-next-cache-tags": "_N_T_/layout,_N_T_/admin/layout,_N_T_/admin/page,_N_T_/admin"
}
}

View File

@ -0,0 +1,9 @@
2:I[9107,[],"ClientPageRoot"]
3:I[5176,["130","static/chunks/130-fbdba5ec657d70e2.js","659","static/chunks/659-547b5910f7777d12.js","3","static/chunks/app/admin/page-be00d3b5e7fb599b.js"],"default",1]
4:I[4707,[],""]
5:I[6423,[],""]
6:I[5495,["185","static/chunks/app/layout-012feff5f2309838.js"],"ThemeProvider"]
7:I[1454,["185","static/chunks/app/layout-012feff5f2309838.js"],"default"]
0:["3c87ZLiEKCTo7fN65B1s7",[[["",{"children":["admin",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",{"children":["admin",{"children":["__PAGE__",{},[["$L1",["$","$L2",null,{"props":{"params":{},"searchParams":{}},"Component":"$3"}],null],null],null]},[null,["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children","admin","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}]],null]},[[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/37740839d02c8860.css","precedence":"next","crossOrigin":"$undefined"}]],["$","html",null,{"lang":"fr","suppressHydrationWarning":true,"children":[["$","head",null,{"children":[["$","link",null,{"rel":"icon","href":"/logo-relais-marama.svg","type":"image/svg+xml"}],["$","link",null,{"rel":"apple-touch-icon","href":"/logo-relais-marama.svg"}]]}],["$","body",null,{"className":"__className_f367f3","children":["$","$L6",null,{"attribute":"class","defaultTheme":"light","enableSystem":true,"disableTransitionOnChange":false,"children":[["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}],["$","$L7",null,{}]]}]}]]}]],null],null],["$L8",null]]]]
8:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"Compagnon du lagon - Pension Marama"}],["$","meta","3",{"name":"description","content":"Votre guide numérique pour votre séjour à Fakarava"}],["$","link","4",{"rel":"manifest","href":"/manifest.json","crossOrigin":"use-credentials"}],["$","meta","5",{"name":"apple-mobile-web-app-capable","content":"yes"}],["$","meta","6",{"name":"apple-mobile-web-app-title","content":"Compagnon du lagon - Pension Marama"}],["$","meta","7",{"name":"apple-mobile-web-app-status-bar-style","content":"default"}],["$","meta","8",{"name":"next-size-adjust"}]]
1:null

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,5 @@
{
"headers": {
"x-next-cache-tags": "_N_T_/layout,_N_T_/admin/layout,_N_T_/admin/login/layout,_N_T_/admin/login/page,_N_T_/admin/login"
}
}

View File

@ -0,0 +1,9 @@
2:I[9107,[],"ClientPageRoot"]
3:I[4507,["130","static/chunks/130-fbdba5ec657d70e2.js","920","static/chunks/app/admin/login/page-64d4a547eaf512e7.js"],"default",1]
4:I[4707,[],""]
5:I[6423,[],""]
6:I[5495,["185","static/chunks/app/layout-012feff5f2309838.js"],"ThemeProvider"]
7:I[1454,["185","static/chunks/app/layout-012feff5f2309838.js"],"default"]
0:["3c87ZLiEKCTo7fN65B1s7",[[["",{"children":["admin",{"children":["login",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",true],["",{"children":["admin",{"children":["login",{"children":["__PAGE__",{},[["$L1",["$","$L2",null,{"props":{"params":{},"searchParams":{}},"Component":"$3"}],null],null],null]},[null,["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children","admin","children","login","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}]],null]},[null,["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children","admin","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}]],null]},[[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/37740839d02c8860.css","precedence":"next","crossOrigin":"$undefined"}]],["$","html",null,{"lang":"fr","suppressHydrationWarning":true,"children":[["$","head",null,{"children":[["$","link",null,{"rel":"icon","href":"/logo-relais-marama.svg","type":"image/svg+xml"}],["$","link",null,{"rel":"apple-touch-icon","href":"/logo-relais-marama.svg"}]]}],["$","body",null,{"className":"__className_f367f3","children":["$","$L6",null,{"attribute":"class","defaultTheme":"light","enableSystem":true,"disableTransitionOnChange":false,"children":[["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}],["$","$L7",null,{}]]}]}]]}]],null],null],["$L8",null]]]]
8:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"Compagnon du lagon - Pension Marama"}],["$","meta","3",{"name":"description","content":"Votre guide numérique pour votre séjour à Fakarava"}],["$","link","4",{"rel":"manifest","href":"/manifest.json","crossOrigin":"use-credentials"}],["$","meta","5",{"name":"apple-mobile-web-app-capable","content":"yes"}],["$","meta","6",{"name":"apple-mobile-web-app-title","content":"Compagnon du lagon - Pension Marama"}],["$","meta","7",{"name":"apple-mobile-web-app-status-bar-style","content":"default"}],["$","meta","8",{"name":"next-size-adjust"}]]
1:null

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"version":1,"files":["../../../../../node_modules/next/dist/client/components/action-async-storage-instance.js","../../../../../node_modules/next/dist/client/components/action-async-storage.external.js","../../../../../node_modules/next/dist/client/components/async-local-storage.js","../../../../../node_modules/next/dist/client/components/request-async-storage-instance.js","../../../../../node_modules/next/dist/client/components/request-async-storage.external.js","../../../../../node_modules/next/dist/client/components/static-generation-async-storage-instance.js","../../../../../node_modules/next/dist/client/components/static-generation-async-storage.external.js","../../../../../node_modules/next/dist/compiled/@opentelemetry/api/index.js","../../../../../node_modules/next/dist/compiled/@opentelemetry/api/package.json","../../../../../node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js","../../../../../node_modules/next/dist/server/lib/trace/constants.js","../../../../../node_modules/next/dist/server/lib/trace/tracer.js","../../../../../node_modules/next/package.json","../../../../../package.json","../../../../package.json","../../../chunks/276.js","../../../chunks/740.js","../../../chunks/87.js","../../../webpack-runtime.js","page_client-reference-manifest.js"]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"version":1,"files":["../../../../node_modules/next/dist/client/components/action-async-storage-instance.js","../../../../node_modules/next/dist/client/components/action-async-storage.external.js","../../../../node_modules/next/dist/client/components/async-local-storage.js","../../../../node_modules/next/dist/client/components/request-async-storage-instance.js","../../../../node_modules/next/dist/client/components/request-async-storage.external.js","../../../../node_modules/next/dist/client/components/static-generation-async-storage-instance.js","../../../../node_modules/next/dist/client/components/static-generation-async-storage.external.js","../../../../node_modules/next/dist/compiled/@opentelemetry/api/index.js","../../../../node_modules/next/dist/compiled/@opentelemetry/api/package.json","../../../../node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js","../../../../node_modules/next/dist/server/lib/trace/constants.js","../../../../node_modules/next/dist/server/lib/trace/tracer.js","../../../../node_modules/next/package.json","../../../../package.json","../../../package.json","../../chunks/276.js","../../chunks/740.js","../../chunks/87.js","../../webpack-runtime.js","page_client-reference-manifest.js"]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"version":1,"files":["../../../../../../data/.gitkeep","../../../../../../data/clients.json","../../../../../../node_modules/next/dist/client/components/action-async-storage-instance.js","../../../../../../node_modules/next/dist/client/components/action-async-storage.external.js","../../../../../../node_modules/next/dist/client/components/async-local-storage.js","../../../../../../node_modules/next/dist/client/components/request-async-storage-instance.js","../../../../../../node_modules/next/dist/client/components/request-async-storage.external.js","../../../../../../node_modules/next/dist/client/components/static-generation-async-storage-instance.js","../../../../../../node_modules/next/dist/client/components/static-generation-async-storage.external.js","../../../../../../node_modules/next/dist/compiled/@opentelemetry/api/index.js","../../../../../../node_modules/next/dist/compiled/@opentelemetry/api/package.json","../../../../../../node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js","../../../../../../node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js","../../../../../../node_modules/next/dist/server/lib/trace/constants.js","../../../../../../node_modules/next/dist/server/lib/trace/tracer.js","../../../../../../node_modules/next/package.json","../../../../../../package.json","../../../../../package.json","../../../../chunks/276.js","../../../../webpack-runtime.js"]}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,5 @@
{
"headers": {
"x-next-cache-tags": "_N_T_/layout,_N_T_/explorer/layout,_N_T_/explorer/page,_N_T_/explorer"
}
}

View File

@ -0,0 +1,9 @@
2:I[9107,[],"ClientPageRoot"]
3:I[9566,["130","static/chunks/130-fbdba5ec657d70e2.js","876","static/chunks/876-74e3127f7295a7cc.js","743","static/chunks/app/explorer/page-94fbb37299b728f0.js"],"default",1]
4:I[4707,[],""]
5:I[6423,[],""]
6:I[5495,["185","static/chunks/app/layout-012feff5f2309838.js"],"ThemeProvider"]
7:I[1454,["185","static/chunks/app/layout-012feff5f2309838.js"],"default"]
0:["3c87ZLiEKCTo7fN65B1s7",[[["",{"children":["explorer",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",{"children":["explorer",{"children":["__PAGE__",{},[["$L1",["$","$L2",null,{"props":{"params":{},"searchParams":{}},"Component":"$3"}],null],null],null]},[null,["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children","explorer","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}]],null]},[[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/37740839d02c8860.css","precedence":"next","crossOrigin":"$undefined"}]],["$","html",null,{"lang":"fr","suppressHydrationWarning":true,"children":[["$","head",null,{"children":[["$","link",null,{"rel":"icon","href":"/logo-relais-marama.svg","type":"image/svg+xml"}],["$","link",null,{"rel":"apple-touch-icon","href":"/logo-relais-marama.svg"}]]}],["$","body",null,{"className":"__className_f367f3","children":["$","$L6",null,{"attribute":"class","defaultTheme":"light","enableSystem":true,"disableTransitionOnChange":false,"children":[["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}],["$","$L7",null,{}]]}]}]]}]],null],null],["$L8",null]]]]
8:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"Compagnon du lagon - Pension Marama"}],["$","meta","3",{"name":"description","content":"Votre guide numérique pour votre séjour à Fakarava"}],["$","link","4",{"rel":"manifest","href":"/manifest.json","crossOrigin":"use-credentials"}],["$","meta","5",{"name":"apple-mobile-web-app-capable","content":"yes"}],["$","meta","6",{"name":"apple-mobile-web-app-title","content":"Compagnon du lagon - Pension Marama"}],["$","meta","7",{"name":"apple-mobile-web-app-status-bar-style","content":"default"}],["$","meta","8",{"name":"next-size-adjust"}]]
1:null

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"version":1,"files":["../../../../node_modules/next/dist/client/components/action-async-storage-instance.js","../../../../node_modules/next/dist/client/components/action-async-storage.external.js","../../../../node_modules/next/dist/client/components/async-local-storage.js","../../../../node_modules/next/dist/client/components/request-async-storage-instance.js","../../../../node_modules/next/dist/client/components/request-async-storage.external.js","../../../../node_modules/next/dist/client/components/static-generation-async-storage-instance.js","../../../../node_modules/next/dist/client/components/static-generation-async-storage.external.js","../../../../node_modules/next/dist/compiled/@opentelemetry/api/index.js","../../../../node_modules/next/dist/compiled/@opentelemetry/api/package.json","../../../../node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js","../../../../node_modules/next/dist/server/lib/trace/constants.js","../../../../node_modules/next/dist/server/lib/trace/tracer.js","../../../../node_modules/next/package.json","../../../../package.json","../../../package.json","../../chunks/276.js","../../chunks/341.js","../../chunks/740.js","../../chunks/87.js","../../chunks/933.js","../../webpack-runtime.js","page_client-reference-manifest.js"]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,5 @@
{
"headers": {
"x-next-cache-tags": "_N_T_/layout,_N_T_/page,_N_T_/"
}
}

View File

@ -0,0 +1,9 @@
2:I[9107,[],"ClientPageRoot"]
3:I[7340,["931","static/chunks/app/page-66582cb216c486f4.js"],"default",1]
4:I[5495,["185","static/chunks/app/layout-012feff5f2309838.js"],"ThemeProvider"]
5:I[4707,[],""]
6:I[6423,[],""]
7:I[1454,["185","static/chunks/app/layout-012feff5f2309838.js"],"default"]
0:["3c87ZLiEKCTo7fN65B1s7",[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",{"children":["__PAGE__",{},[["$L1",["$","$L2",null,{"props":{"params":{},"searchParams":{}},"Component":"$3"}],null],null],null]},[[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/37740839d02c8860.css","precedence":"next","crossOrigin":"$undefined"}]],["$","html",null,{"lang":"fr","suppressHydrationWarning":true,"children":[["$","head",null,{"children":[["$","link",null,{"rel":"icon","href":"/logo-relais-marama.svg","type":"image/svg+xml"}],["$","link",null,{"rel":"apple-touch-icon","href":"/logo-relais-marama.svg"}]]}],["$","body",null,{"className":"__className_f367f3","children":["$","$L4",null,{"attribute":"class","defaultTheme":"light","enableSystem":true,"disableTransitionOnChange":false,"children":[["$","$L5",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}],["$","$L7",null,{}]]}]}]]}]],null],null],["$L8",null]]]]
8:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"Compagnon du lagon - Pension Marama"}],["$","meta","3",{"name":"description","content":"Votre guide numérique pour votre séjour à Fakarava"}],["$","link","4",{"rel":"manifest","href":"/manifest.json","crossOrigin":"use-credentials"}],["$","meta","5",{"name":"apple-mobile-web-app-capable","content":"yes"}],["$","meta","6",{"name":"apple-mobile-web-app-title","content":"Compagnon du lagon - Pension Marama"}],["$","meta","7",{"name":"apple-mobile-web-app-status-bar-style","content":"default"}],["$","meta","8",{"name":"next-size-adjust"}]]
1:null

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,5 @@
{
"headers": {
"x-next-cache-tags": "_N_T_/layout,_N_T_/infos/layout,_N_T_/infos/page,_N_T_/infos"
}
}

View File

@ -0,0 +1,15 @@
2:"$Sreact.suspense"
3:I[49,["130","static/chunks/130-fbdba5ec657d70e2.js","876","static/chunks/876-74e3127f7295a7cc.js","767","static/chunks/app/infos/page-9c23965cb3e698cb.js"],"PreloadCss"]
6:I[1110,["130","static/chunks/130-fbdba5ec657d70e2.js","876","static/chunks/876-74e3127f7295a7cc.js","767","static/chunks/app/infos/page-9c23965cb3e698cb.js"],"default"]
7:I[9673,["130","static/chunks/130-fbdba5ec657d70e2.js","876","static/chunks/876-74e3127f7295a7cc.js","767","static/chunks/app/infos/page-9c23965cb3e698cb.js"],"default"]
8:I[4707,[],""]
9:I[6423,[],""]
a:I[5495,["185","static/chunks/app/layout-012feff5f2309838.js"],"ThemeProvider"]
b:I[1454,["185","static/chunks/app/layout-012feff5f2309838.js"],"default"]
0:["3c87ZLiEKCTo7fN65B1s7",[[["",{"children":["infos",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",{"children":["infos",{"children":["__PAGE__",{},[["$L1",["$","div",null,{"className":"min-h-screen bg-background dark:bg-background-dark pb-16","children":[["$","div",null,{"className":"px-4 py-6 space-y-8","children":[["$","header",null,{"children":[["$","h1",null,{"className":"text-2xl font-bold text-primary mb-2","children":"Infos Pratiques"}],["$","p",null,{"className":"text-gray-600","children":"Tout ce que vous devez savoir pour votre séjour"}]]}],["$","section",null,{"children":[["$","h2",null,{"className":"text-xl font-semibold text-primary mb-4","children":"Questions Fréquentes"}],["$","$2",null,{"fallback":["$","div",null,{"className":"h-64 bg-gray-100 rounded-2xl animate-pulse"}],"children":[["$","$L3",null,{"moduleIds":["app/infos/page.tsx -> @/components/infos/FAQAccordion"]}],"$L4"]}]]}],["$","section",null,{"children":["$","$2",null,{"fallback":["$","div",null,{"className":"h-48 bg-gray-100 rounded-2xl animate-pulse"}],"children":[["$","$L3",null,{"moduleIds":["app/infos/page.tsx -> @/components/infos/LexiqueSection"]}],"$L5"]}]}],["$","section",null,{"children":["$","$L6",null,{}]}]]}],["$","$L7",null,{}]]}],null],null],null]},[null,["$","$L8",null,{"parallelRouterKey":"children","segmentPath":["children","infos","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L9",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}]],null]},[[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/37740839d02c8860.css","precedence":"next","crossOrigin":"$undefined"}]],["$","html",null,{"lang":"fr","suppressHydrationWarning":true,"children":[["$","head",null,{"children":[["$","link",null,{"rel":"icon","href":"/logo-relais-marama.svg","type":"image/svg+xml"}],["$","link",null,{"rel":"apple-touch-icon","href":"/logo-relais-marama.svg"}]]}],["$","body",null,{"className":"__className_f367f3","children":["$","$La",null,{"attribute":"class","defaultTheme":"light","enableSystem":true,"disableTransitionOnChange":false,"children":[["$","$L8",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L9",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}],["$","$Lb",null,{}]]}]}]]}]],null],null],["$Lc",null]]]]
d:I[646,["130","static/chunks/130-fbdba5ec657d70e2.js","876","static/chunks/876-74e3127f7295a7cc.js","767","static/chunks/app/infos/page-9c23965cb3e698cb.js"],"default"]
e:I[9795,["130","static/chunks/130-fbdba5ec657d70e2.js","876","static/chunks/876-74e3127f7295a7cc.js","767","static/chunks/app/infos/page-9c23965cb3e698cb.js"],"default"]
4:["$","$Ld",null,{}]
5:["$","$Le",null,{}]
c:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"Compagnon du lagon - Pension Marama"}],["$","meta","3",{"name":"description","content":"Votre guide numérique pour votre séjour à Fakarava"}],["$","link","4",{"rel":"manifest","href":"/manifest.json","crossOrigin":"use-credentials"}],["$","meta","5",{"name":"apple-mobile-web-app-capable","content":"yes"}],["$","meta","6",{"name":"apple-mobile-web-app-title","content":"Compagnon du lagon - Pension Marama"}],["$","meta","7",{"name":"apple-mobile-web-app-status-bar-style","content":"default"}],["$","meta","8",{"name":"next-size-adjust"}]]
1:null

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"version":1,"files":["../../../../components/infos/FAQAccordion.tsx","../../../../components/infos/LexiqueSection.tsx","../../../../node_modules/next/dist/client/components/action-async-storage-instance.js","../../../../node_modules/next/dist/client/components/action-async-storage.external.js","../../../../node_modules/next/dist/client/components/async-local-storage.js","../../../../node_modules/next/dist/client/components/request-async-storage-instance.js","../../../../node_modules/next/dist/client/components/request-async-storage.external.js","../../../../node_modules/next/dist/client/components/static-generation-async-storage-instance.js","../../../../node_modules/next/dist/client/components/static-generation-async-storage.external.js","../../../../node_modules/next/dist/compiled/@opentelemetry/api/index.js","../../../../node_modules/next/dist/compiled/@opentelemetry/api/package.json","../../../../node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js","../../../../node_modules/next/dist/server/lib/trace/constants.js","../../../../node_modules/next/dist/server/lib/trace/tracer.js","../../../../node_modules/next/package.json","../../../../package.json","../../../package.json","../../chunks/174.js","../../chunks/276.js","../../chunks/341.js","../../chunks/698.js","../../chunks/740.js","../../chunks/87.js","../../chunks/99.js","../../webpack-runtime.js","page_client-reference-manifest.js"]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,5 @@
{
"headers": {
"x-next-cache-tags": "_N_T_/layout,_N_T_/mana-tracker/layout,_N_T_/mana-tracker/page,_N_T_/mana-tracker"
}
}

View File

@ -0,0 +1,16 @@
2:I[7849,["130","static/chunks/130-fbdba5ec657d70e2.js","876","static/chunks/876-74e3127f7295a7cc.js","347","static/chunks/app/mana-tracker/page-190454f13d1cd3f9.js"],"default"]
3:I[1556,["130","static/chunks/130-fbdba5ec657d70e2.js","876","static/chunks/876-74e3127f7295a7cc.js","347","static/chunks/app/mana-tracker/page-190454f13d1cd3f9.js"],"default"]
4:"$Sreact.suspense"
5:I[49,["130","static/chunks/130-fbdba5ec657d70e2.js","876","static/chunks/876-74e3127f7295a7cc.js","347","static/chunks/app/mana-tracker/page-190454f13d1cd3f9.js"],"PreloadCss"]
8:I[9673,["130","static/chunks/130-fbdba5ec657d70e2.js","876","static/chunks/876-74e3127f7295a7cc.js","347","static/chunks/app/mana-tracker/page-190454f13d1cd3f9.js"],"default"]
9:I[4707,[],""]
a:I[6423,[],""]
b:I[5495,["185","static/chunks/app/layout-012feff5f2309838.js"],"ThemeProvider"]
c:I[1454,["185","static/chunks/app/layout-012feff5f2309838.js"],"default"]
0:["3c87ZLiEKCTo7fN65B1s7",[[["",{"children":["mana-tracker",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",{"children":["mana-tracker",{"children":["__PAGE__",{},[["$L1",["$","div",null,{"className":"min-h-screen bg-background dark:bg-background-dark pb-16","children":[["$","div",null,{"className":"px-4 py-6 space-y-6","children":[["$","header",null,{"children":[["$","h1",null,{"className":"text-2xl font-bold text-primary mb-2","children":"Mana Tracker"}],["$","p",null,{"className":"text-gray-600","children":"Activités & Météo - Tout dépend de la mer et du soleil"}]]}],["$","$L2",null,{}],["$","$L3",null,{}],["$","$4",null,{"fallback":["$","div",null,{"className":"h-64 bg-gray-100 rounded-2xl animate-pulse"}],"children":[["$","$L5",null,{"moduleIds":["app/mana-tracker/page.tsx -> @/components/mana-tracker/ExcursionBooking"]}],"$L6"]}],["$","$4",null,{"fallback":["$","div",null,{"className":"h-48 bg-gray-100 rounded-2xl animate-pulse"}],"children":[["$","$L5",null,{"moduleIds":["app/mana-tracker/page.tsx -> @/components/mana-tracker/PushNotificationManager"]}],"$L7"]}]]}],["$","$L8",null,{}]]}],null],null],null]},[null,["$","$L9",null,{"parallelRouterKey":"children","segmentPath":["children","mana-tracker","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$La",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}]],null]},[[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/37740839d02c8860.css","precedence":"next","crossOrigin":"$undefined"}]],["$","html",null,{"lang":"fr","suppressHydrationWarning":true,"children":[["$","head",null,{"children":[["$","link",null,{"rel":"icon","href":"/logo-relais-marama.svg","type":"image/svg+xml"}],["$","link",null,{"rel":"apple-touch-icon","href":"/logo-relais-marama.svg"}]]}],["$","body",null,{"className":"__className_f367f3","children":["$","$Lb",null,{"attribute":"class","defaultTheme":"light","enableSystem":true,"disableTransitionOnChange":false,"children":[["$","$L9",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$La",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}],["$","$Lc",null,{}]]}]}]]}]],null],null],["$Ld",null]]]]
e:I[4636,["130","static/chunks/130-fbdba5ec657d70e2.js","876","static/chunks/876-74e3127f7295a7cc.js","347","static/chunks/app/mana-tracker/page-190454f13d1cd3f9.js"],"default"]
f:I[845,["130","static/chunks/130-fbdba5ec657d70e2.js","876","static/chunks/876-74e3127f7295a7cc.js","347","static/chunks/app/mana-tracker/page-190454f13d1cd3f9.js"],"default"]
6:["$","$Le",null,{}]
7:["$","$Lf",null,{}]
d:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"Compagnon du lagon - Pension Marama"}],["$","meta","3",{"name":"description","content":"Votre guide numérique pour votre séjour à Fakarava"}],["$","link","4",{"rel":"manifest","href":"/manifest.json","crossOrigin":"use-credentials"}],["$","meta","5",{"name":"apple-mobile-web-app-capable","content":"yes"}],["$","meta","6",{"name":"apple-mobile-web-app-title","content":"Compagnon du lagon - Pension Marama"}],["$","meta","7",{"name":"apple-mobile-web-app-status-bar-style","content":"default"}],["$","meta","8",{"name":"next-size-adjust"}]]
1:null

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"version":1,"files":["../../../../components/mana-tracker/ExcursionBooking.tsx","../../../../components/mana-tracker/PushNotificationManager.tsx","../../../../node_modules/next/dist/client/components/action-async-storage-instance.js","../../../../node_modules/next/dist/client/components/action-async-storage.external.js","../../../../node_modules/next/dist/client/components/async-local-storage.js","../../../../node_modules/next/dist/client/components/request-async-storage-instance.js","../../../../node_modules/next/dist/client/components/request-async-storage.external.js","../../../../node_modules/next/dist/client/components/static-generation-async-storage-instance.js","../../../../node_modules/next/dist/client/components/static-generation-async-storage.external.js","../../../../node_modules/next/dist/compiled/@opentelemetry/api/index.js","../../../../node_modules/next/dist/compiled/@opentelemetry/api/package.json","../../../../node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js","../../../../node_modules/next/dist/server/lib/trace/constants.js","../../../../node_modules/next/dist/server/lib/trace/tracer.js","../../../../node_modules/next/package.json","../../../../package.json","../../../package.json","../../chunks/174.js","../../chunks/180.js","../../chunks/207.js","../../chunks/276.js","../../chunks/341.js","../../chunks/740.js","../../chunks/87.js","../../webpack-runtime.js","page_client-reference-manifest.js"]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,5 @@
{
"headers": {
"x-next-cache-tags": "_N_T_/layout,_N_T_/offline/layout,_N_T_/offline/page,_N_T_/offline"
}
}

View File

@ -0,0 +1,8 @@
2:I[9673,["130","static/chunks/130-fbdba5ec657d70e2.js","876","static/chunks/876-74e3127f7295a7cc.js","200","static/chunks/app/offline/page-c706c75d3862967d.js"],"default"]
3:I[4707,[],""]
4:I[6423,[],""]
5:I[5495,["185","static/chunks/app/layout-012feff5f2309838.js"],"ThemeProvider"]
6:I[1454,["185","static/chunks/app/layout-012feff5f2309838.js"],"default"]
0:["3c87ZLiEKCTo7fN65B1s7",[[["",{"children":["offline",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",{"children":["offline",{"children":["__PAGE__",{},[["$L1",["$","div",null,{"className":"min-h-screen bg-background dark:bg-background-dark pb-16","children":[["$","div",null,{"className":"flex flex-col items-center justify-center min-h-[60vh] px-4 text-center","children":[["$","h1",null,{"className":"text-2xl font-bold text-primary mb-4","children":"Mode hors ligne"}],["$","p",null,{"className":"text-gray-600 mb-6","children":"Vous êtes actuellement hors ligne. Certaines fonctionnalités peuvent être limitées."}],["$","p",null,{"className":"text-sm text-gray-500","children":"Les données mises en cache restent disponibles."}]]}],["$","$L2",null,{}]]}],null],null],null]},[null,["$","$L3",null,{"parallelRouterKey":"children","segmentPath":["children","offline","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}]],null]},[[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/37740839d02c8860.css","precedence":"next","crossOrigin":"$undefined"}]],["$","html",null,{"lang":"fr","suppressHydrationWarning":true,"children":[["$","head",null,{"children":[["$","link",null,{"rel":"icon","href":"/logo-relais-marama.svg","type":"image/svg+xml"}],["$","link",null,{"rel":"apple-touch-icon","href":"/logo-relais-marama.svg"}]]}],["$","body",null,{"className":"__className_f367f3","children":["$","$L5",null,{"attribute":"class","defaultTheme":"light","enableSystem":true,"disableTransitionOnChange":false,"children":[["$","$L3",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}],["$","$L6",null,{}]]}]}]]}]],null],null],["$L7",null]]]]
7:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"Compagnon du lagon - Pension Marama"}],["$","meta","3",{"name":"description","content":"Votre guide numérique pour votre séjour à Fakarava"}],["$","link","4",{"rel":"manifest","href":"/manifest.json","crossOrigin":"use-credentials"}],["$","meta","5",{"name":"apple-mobile-web-app-capable","content":"yes"}],["$","meta","6",{"name":"apple-mobile-web-app-title","content":"Compagnon du lagon - Pension Marama"}],["$","meta","7",{"name":"apple-mobile-web-app-status-bar-style","content":"default"}],["$","meta","8",{"name":"next-size-adjust"}]]
1:null

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"version":1,"files":["../../../../node_modules/next/dist/client/components/action-async-storage-instance.js","../../../../node_modules/next/dist/client/components/action-async-storage.external.js","../../../../node_modules/next/dist/client/components/async-local-storage.js","../../../../node_modules/next/dist/client/components/request-async-storage-instance.js","../../../../node_modules/next/dist/client/components/request-async-storage.external.js","../../../../node_modules/next/dist/client/components/static-generation-async-storage-instance.js","../../../../node_modules/next/dist/client/components/static-generation-async-storage.external.js","../../../../node_modules/next/dist/compiled/@opentelemetry/api/index.js","../../../../node_modules/next/dist/compiled/@opentelemetry/api/package.json","../../../../node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js","../../../../node_modules/next/dist/server/lib/trace/constants.js","../../../../node_modules/next/dist/server/lib/trace/tracer.js","../../../../node_modules/next/package.json","../../../../package.json","../../../package.json","../../chunks/276.js","../../chunks/341.js","../../chunks/740.js","../../chunks/87.js","../../webpack-runtime.js","page_client-reference-manifest.js"]}

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More