first commit

This commit is contained in:
2025-11-23 08:02:54 +01:00
commit afd3881015
52 changed files with 9280 additions and 0 deletions

View File

@ -0,0 +1,67 @@
import { NextResponse } from "next/server";
export interface Excursion {
id: string;
name: string;
type: "tour-lagon" | "plongee" | "4x4";
description: string;
duration: string;
price: number;
available: boolean;
}
const excursions: Excursion[] = [
{
id: "1",
name: "Tour du Lagon de Fakarava",
type: "tour-lagon",
description: "Découvrez les merveilles du lagon de Fakarava avec arrêts snorkeling aux raies et requins. Visite des motus et des spots de plongée exceptionnels.",
duration: "4 heures",
price: 12000,
available: true,
},
{
id: "2",
name: "Plongée à la Passe Sud (Tumakohua)",
type: "plongee",
description: "Expérience unique de plongée à la Passe Sud de Fakarava, réputée pour ses raies mantas et sa faune exceptionnelle. Accessible uniquement par bateau.",
duration: "Journée complète",
price: 15000,
available: true,
},
{
id: "3",
name: "Excursion en vélo vers le Sud",
type: "4x4",
description: "Exploration de l'atoll de Fakarava en vélo le long de la route principale. Découvrez les villages et les points de vue sur le lagon.",
duration: "3 heures",
price: 8000,
available: true,
},
];
export async function GET() {
return NextResponse.json(excursions);
}
export async function POST(request: Request) {
try {
const body = await request.json();
const { excursionId, name, email, phone, date, participants } = body;
// Ici, en production, vous sauvegarderiez la réservation dans une base de données
// Pour l'instant, on simule juste une réponse de succès
return NextResponse.json({
success: true,
message: "Réservation enregistrée avec succès",
reservationId: `RES-${Date.now()}`,
});
} catch (error) {
return NextResponse.json(
{ success: false, message: "Erreur lors de la réservation" },
{ status: 400 }
);
}
}

198
app/api/infos/route.ts Normal file
View File

@ -0,0 +1,198 @@
import { NextResponse } from "next/server";
export interface FAQItem {
id: string;
question: string;
answer: string;
category?: string;
icon?: string;
}
export interface LexiqueItem {
id: string;
mot: string;
traduction: string;
description: string;
}
const faq: FAQItem[] = [
// Pension - Informations générales
{
id: "pension-1",
question: "Heures de Check-out",
answer: "Le check-out se fait avant 11h00. Merci de libérer votre bungalow à l'heure prévue pour permettre la préparation pour les prochains clients.",
category: "Pension",
icon: "🏠",
},
{
id: "pension-2",
question: "Petit-déjeuner",
answer: "Le petit-déjeuner est servi de 7h30 à 9h30 dans la salle commune. Il comprend des fruits frais, du pain, des confitures locales et des boissons chaudes.",
category: "Pension",
icon: "🍽️",
},
{
id: "pension-3",
question: "Comment utiliser la climatisation",
answer: "La télécommande de la climatisation se trouve sur la table de chevet. Appuyez sur le bouton ON/OFF pour l'activer. La température recommandée est de 24°C pour un confort optimal et une consommation raisonnable.",
category: "Pension",
icon: "❄️",
},
// 💰 Argent, Banque & Paiement
{
id: "argent-1",
question: "Où trouver un distributeur ?",
answer: "Un seul distributeur automatique de billets (DAB) est disponible à Fakarava. Il est situé à l'entrée du Bureau de Poste (OPT) dans le village de Rotoava.",
category: "💰 Argent, Banque & Paiement",
icon: "💰",
},
{
id: "argent-2",
question: "Faut-il prévoir du liquide ?",
answer: "OUI, le liquide (XPF) est ESSENTIEL. De nombreux snacks, petites pensions et activités n'acceptent pas la carte bancaire. Prévoyez de retirer assez à Tahiti ou Papeete avant d'arriver, ou dès votre arrivée à Rotoava.",
category: "💰 Argent, Banque & Paiement",
icon: "💰",
},
{
id: "argent-3",
question: "Les cartes bancaires sont-elles acceptées ?",
answer: "Les hôtels, les grands prestataires de plongée et les magasins principaux acceptent la carte. Les petites roulottes et certains taxis exigent du liquide.",
category: "💰 Argent, Banque & Paiement",
icon: "💰",
},
// 🌐 Communications & Internet
{
id: "internet-1",
question: "Comment avoir du Wifi ?",
answer: "Le Wifi est disponible exclusivement à la pension. Le code est accessible sur la page d'accueil de cette application (onglet Accueil). Attention : le débit est plus lent que sur les grandes îles.",
category: "🌐 Communications & Internet",
icon: "🌐",
},
{
id: "internet-2",
question: "Où acheter une carte SIM ?",
answer: "Vous pouvez acheter une carte SIM locale (Vini ou Vodafone) au Bureau de Poste (OPT) de Rotoava, si vous souhaitez avoir la 4G sur l'atoll (coûteux).",
category: "🌐 Communications & Internet",
icon: "🌐",
},
{
id: "internet-3",
question: "Y a-t-il une couverture mobile ?",
answer: "La couverture 4G est bonne et stable uniquement dans la zone de Rotoava (au Nord) et sur la route principale. Elle est quasi inexistante vers le Sud de l'atoll.",
category: "🌐 Communications & Internet",
icon: "🌐",
},
// 🚲 Transport & Déplacements
{
id: "transport-1",
question: "Quel est le meilleur moyen de se déplacer ?",
answer: "Le vélo est le moyen de transport standard et le plus agréable. Le Nord de l'atoll (Rotoava) est plat et idéal pour le vélo.",
category: "🚲 Transport & Déplacements",
icon: "🚲",
},
{
id: "transport-2",
question: "Comment aller à la Passe Sud (Tumakohua) ?",
answer: "La Passe Sud est à environ 60 km et est accessible uniquement par bateau. Les excursions sont organisées par les prestataires de plongée. Il est impossible d'y aller en voiture ou vélo depuis le Nord.",
category: "🚲 Transport & Déplacements",
icon: "🚲",
},
{
id: "transport-3",
question: "Où louer un vélo ou un scooter ?",
answer: "Des vélos sont disponibles à la location directement à la pension. Contactez le gérant pour plus d'informations.",
category: "🚲 Transport & Déplacements",
icon: "🚲",
},
// 🩹 Santé & Urgences
{
id: "sante-1",
question: "Où trouver un médecin ?",
answer: "Le dispensaire se trouve dans le village de Rotoava. En cas d'urgence grave, contactez le gérant immédiatement.",
category: "🩹 Santé & Urgences",
icon: "🩹",
},
{
id: "sante-2",
question: "Y a-t-il une pharmacie ?",
answer: "Il n'y a pas de pharmacie à Fakarava. Seule l'infirmerie du dispensaire dispose d'une petite réserve de médicaments de base.",
category: "🩹 Santé & Urgences",
icon: "🩹",
},
{
id: "sante-3",
question: "Numéros d'urgence",
answer: "Gérant : Contactez le gérant via l'application ou le numéro fourni à l'arrivée. Dispensaire : Situé à Rotoava. SAMU : 15. Police : 17.",
category: "🩹 Santé & Urgences",
icon: "🩹",
},
// 🌿 Consignes Éco-Lagon
{
id: "eco-1",
question: "Comment protéger le lagon ?",
answer: "Fakarava est une Réserve de Biosphère UNESCO ! Nous vous demandons de ne jamais toucher les coraux (morts ou vivants), de ne pas nourrir les poissons, et de toujours emporter vos déchets avec vous.",
category: "🌿 Consignes Éco-Lagon",
icon: "🌿",
},
{
id: "eco-2",
question: "Consommation d'eau",
answer: "L'eau potable est précieuse ici. Merci de limiter la durée de vos douches et de toujours fermer les robinets après usage.",
category: "🌿 Consignes Éco-Lagon",
icon: "🌿",
},
];
const lexique: LexiqueItem[] = [
{
id: "1",
mot: "Ia Ora Na",
traduction: "Bonjour / Salut",
description: "Salutation traditionnelle tahitienne utilisée pour dire bonjour.",
},
{
id: "2",
mot: "Mauruuru",
traduction: "Merci",
description: "Expression de gratitude en tahitien.",
},
{
id: "3",
mot: "Maeva",
traduction: "Bienvenue",
description: "Mot utilisé pour souhaiter la bienvenue aux visiteurs.",
},
{
id: "4",
mot: "Aita",
traduction: "Non",
description: "Négation en tahitien.",
},
{
id: "5",
mot: "E",
traduction: "Oui",
description: "Affirmation en tahitien.",
},
];
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const type = searchParams.get("type");
if (type === "faq") {
return NextResponse.json(faq);
}
if (type === "lexique") {
return NextResponse.json(lexique);
}
return NextResponse.json({ faq, lexique });
}

View File

@ -0,0 +1,76 @@
import { NextResponse } from "next/server";
export interface Notification {
id: string;
title: string;
message: string;
type: "whale" | "weather" | "excursion" | "info";
timestamp: string;
read: boolean;
}
// Notifications stockées (en production, utiliser une base de données)
let notifications: Notification[] = [
{
id: "1",
title: "Observation de baleines",
message: "Les baleines ont été vues au nord de l'île ce matin !",
type: "whale",
timestamp: new Date().toISOString(),
read: false,
},
];
export async function GET() {
return NextResponse.json(notifications);
}
export async function POST(request: Request) {
try {
const body = await request.json();
const { title, message, type } = body;
const notification: Notification = {
id: `notif-${Date.now()}`,
title,
message,
type: type || "info",
timestamp: new Date().toISOString(),
read: false,
};
notifications.unshift(notification);
// Limiter à 50 notifications
if (notifications.length > 50) {
notifications = notifications.slice(0, 50);
}
return NextResponse.json({ success: true, notification });
} catch (error) {
return NextResponse.json(
{ success: false, message: "Erreur lors de la création de la notification" },
{ status: 400 }
);
}
}
export async function PATCH(request: Request) {
try {
const body = await request.json();
const { id, read } = body;
const notification = notifications.find((n) => n.id === id);
if (notification) {
notification.read = read;
}
return NextResponse.json({ success: true });
} catch (error) {
return NextResponse.json(
{ success: false, message: "Erreur lors de la mise à jour" },
{ status: 400 }
);
}
}

85
app/api/places/route.ts Normal file
View File

@ -0,0 +1,85 @@
import { NextResponse } from "next/server";
import { FAKARAVA_SPOTS } from "@/lib/data/fakarava-spots";
export interface Place {
id: string;
name: string;
category: string;
description: string;
image: string;
location: {
lat: number;
lng: number;
address: string;
};
type?: string;
keywords?: string[];
contact?: string;
gmapLink?: string;
conseil?: string;
horaires?: string;
}
// Coordonnées approximatives de Fakarava (Rotoava)
const FAKARAVA_CENTER = {
lat: -16.3167,
lng: -145.6167,
};
// Convertir les spots de Fakarava en format Place
const convertFakaravaSpots = (): Place[] => {
return FAKARAVA_SPOTS.map((spot, index) => {
// Mapper la catégorie "Restauration" vers "restaurants"
const categoryMap: Record<string, string> = {
"Restauration": "restaurants",
"Plages": "plages",
"Epiceries": "epiceries",
"Activités": "activites",
};
// Déterminer l'image par défaut selon la catégorie
const getDefaultImage = (category: string) => {
if (category === "plages") return "/placeholder-beach.jpg";
if (category === "epiceries") return "/placeholder-store.jpg";
return "/placeholder-restaurant.jpg";
};
return {
id: `fakarava-${index + 1}`,
name: spot.name,
category: categoryMap[spot.category] || spot.category.toLowerCase(),
type: spot.type,
description: spot.description,
keywords: spot.keywords,
contact: spot.contact,
conseil: spot.conseil,
horaires: spot.horaires,
image: getDefaultImage(categoryMap[spot.category] || spot.category.toLowerCase()),
location: {
// Coordonnées approximatives basées sur le PK
lat: FAKARAVA_CENTER.lat + (index * 0.01),
lng: FAKARAVA_CENTER.lng + (index * 0.01),
address: spot.location,
},
gmapLink: spot.gmapLink,
};
});
};
const places: Place[] = [
...convertFakaravaSpots(),
// Ajoutez ici d'autres lieux spécifiques à Fakarava
];
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const category = searchParams.get("category");
let filteredPlaces = places;
if (category && category !== "all") {
filteredPlaces = places.filter((place) => place.category === category);
}
return NextResponse.json(filteredPlaces);
}

View File

@ -0,0 +1,40 @@
import { NextResponse } from "next/server";
export interface SunTimes {
date: string;
sunrise: string;
sunset: string;
}
// Calcul approximatif du lever/coucher du soleil pour Fakarava
// Coordonnées de Fakarava (Rotoava) : -16.3167, -145.6167
// En production, utiliser une API comme https://sunrise-sunset.org/api
const calculateSunTimes = (date: Date, lat: number = -16.3167, lng: number = -145.6167): SunTimes => {
// Calcul simplifié (formule approximative)
const dayOfYear = Math.floor((date.getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000);
const declination = 23.45 * Math.sin((360 * (284 + dayOfYear) / 365) * Math.PI / 180);
const hourAngle = Math.acos(-Math.tan(lat * Math.PI / 180) * Math.tan(declination * Math.PI / 180)) * 180 / Math.PI;
const sunriseHour = 12 - hourAngle / 15 - (lng / 15);
const sunsetHour = 12 + hourAngle / 15 - (lng / 15);
const formatTime = (hour: number) => {
const h = Math.floor(hour);
const m = Math.floor((hour - h) * 60);
return `${h.toString().padStart(2, "0")}:${m.toString().padStart(2, "0")}`;
};
return {
date: date.toISOString().split("T")[0],
sunrise: formatTime(sunriseHour),
sunset: formatTime(sunsetHour),
};
};
export async function GET() {
const today = new Date();
const sunTimes = calculateSunTimes(today);
return NextResponse.json(sunTimes);
}

44
app/api/tides/route.ts Normal file
View File

@ -0,0 +1,44 @@
import { NextResponse } from "next/server";
export interface TideData {
date: string;
highTide: { time: string; height: number };
lowTide: { time: string; height: number };
}
// Données de marées pour les 7 prochains jours
// En production, utiliser une API réelle comme https://www.tide-forecast.com/api
const generateTideData = (): TideData[] => {
const tides: TideData[] = [];
const today = new Date();
for (let i = 0; i < 7; i++) {
const date = new Date(today);
date.setDate(today.getDate() + i);
// Simulation de données de marées (à remplacer par une vraie API)
const dayOffset = i;
const highTideHour = (6 + dayOffset * 0.8) % 24;
const lowTideHour = (12 + dayOffset * 0.8) % 24;
tides.push({
date: date.toISOString().split("T")[0],
highTide: {
time: `${Math.floor(highTideHour).toString().padStart(2, "0")}:${Math.floor((highTideHour % 1) * 60).toString().padStart(2, "0")}`,
height: 1.2 + Math.random() * 0.3,
},
lowTide: {
time: `${Math.floor(lowTideHour).toString().padStart(2, "0")}:${Math.floor((lowTideHour % 1) * 60).toString().padStart(2, "0")}`,
height: 0.3 + Math.random() * 0.2,
},
});
}
return tides;
};
export async function GET() {
const tides = generateTideData();
return NextResponse.json(tides);
}