Build APK Android fonctionnel - Corrections finales
- Ajout de Java 21 dans Dockerfile pour compatibilité Capacitor - Création de fichiers de types séparés (lib/types/) pour éviter dépendances API routes - Configuration next.config.export.js pour export statique - Exclusion temporaire des routes API pendant le build - Correction configuration Gradle (Java 17/21) - Script build-apk.sh amélioré avec gestion des routes API - APK généré avec succès (4.5MB) dans dist/compagnon-admin-debug.apk Fichiers de types créés: - lib/types/place.ts - lib/types/infos.ts - lib/types/tides.ts - lib/types/excursions.ts - lib/types/sun-times.ts - lib/types/notifications.ts Tous les imports mis à jour pour utiliser les nouveaux fichiers de types.
This commit is contained in:
@ -1,55 +0,0 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { updateClient, deleteClient, loadClients } from "@/lib/admin/client-utils";
|
||||
import { requireAdminAuth } from "@/lib/admin/auth";
|
||||
import { ClientInput } from "@/lib/types/client";
|
||||
|
||||
export async function PUT(
|
||||
request: Request,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
if (!requireAdminAuth(request)) {
|
||||
return NextResponse.json({ error: "Non autorisé" }, { status: 401 });
|
||||
}
|
||||
|
||||
try {
|
||||
const { id } = await params;
|
||||
const body: Partial<ClientInput> = await request.json();
|
||||
const client = updateClient(id, body);
|
||||
|
||||
if (!client) {
|
||||
return NextResponse.json(
|
||||
{ error: "Client non trouvé" },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
return NextResponse.json(client);
|
||||
} catch (error: any) {
|
||||
return NextResponse.json(
|
||||
{ error: error.message || "Erreur lors de la mise à jour" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function DELETE(
|
||||
request: Request,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
if (!requireAdminAuth(request)) {
|
||||
return NextResponse.json({ error: "Non autorisé" }, { status: 401 });
|
||||
}
|
||||
|
||||
const { id } = await params;
|
||||
const deleted = deleteClient(id);
|
||||
|
||||
if (!deleted) {
|
||||
return NextResponse.json(
|
||||
{ error: "Client non trouvé" },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
}
|
||||
|
||||
@ -1,60 +0,0 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import {
|
||||
createClient,
|
||||
loadClients,
|
||||
updateClient,
|
||||
deleteClient,
|
||||
validateEmail,
|
||||
} from "@/lib/admin/client-utils";
|
||||
import { requireAdminAuth } from "@/lib/admin/auth";
|
||||
import { ClientInput } from "@/lib/types/client";
|
||||
|
||||
export async function GET(request: Request) {
|
||||
if (!requireAdminAuth(request)) {
|
||||
return NextResponse.json({ error: "Non autorisé" }, { status: 401 });
|
||||
}
|
||||
|
||||
const clients = loadClients();
|
||||
return NextResponse.json(clients);
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
if (!requireAdminAuth(request)) {
|
||||
return NextResponse.json({ error: "Non autorisé" }, { status: 401 });
|
||||
}
|
||||
|
||||
try {
|
||||
const body: ClientInput = await request.json();
|
||||
|
||||
// Validation
|
||||
if (!body.email || !validateEmail(body.email)) {
|
||||
return NextResponse.json(
|
||||
{ error: "Email invalide" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
if (!body.bungalowNumber) {
|
||||
return NextResponse.json(
|
||||
{ error: "Numéro de bungalow requis" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const client = createClient({
|
||||
email: body.email,
|
||||
bungalowNumber: body.bungalowNumber,
|
||||
wifiName: body.wifiName || "Lagon-WiFi",
|
||||
wifiPassword: body.wifiPassword || "",
|
||||
gerantMessage: body.gerantMessage || "Bienvenue dans notre pension de famille !",
|
||||
});
|
||||
|
||||
return NextResponse.json(client, { status: 201 });
|
||||
} catch (error: any) {
|
||||
return NextResponse.json(
|
||||
{ error: error.message || "Erreur lors de la création du client" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,26 +0,0 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { getClientByToken } from "@/lib/admin/client-utils";
|
||||
|
||||
export async function GET(
|
||||
request: Request,
|
||||
{ params }: { params: Promise<{ token: string }> }
|
||||
) {
|
||||
const { token } = await params;
|
||||
const client = getClientByToken(token);
|
||||
|
||||
if (!client) {
|
||||
return NextResponse.json(
|
||||
{ error: "Token invalide ou expiré" },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
// Retourner uniquement les informations nécessaires (sans le token)
|
||||
return NextResponse.json({
|
||||
bungalowNumber: client.bungalowNumber,
|
||||
wifiName: client.wifiName,
|
||||
wifiPassword: client.wifiPassword,
|
||||
gerantMessage: client.gerantMessage,
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,67 +0,0 @@
|
||||
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 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,191 +0,0 @@
|
||||
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: "🍽️",
|
||||
},
|
||||
|
||||
// 💰 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 });
|
||||
}
|
||||
|
||||
@ -1,76 +0,0 @@
|
||||
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 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,85 +0,0 @@
|
||||
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);
|
||||
}
|
||||
|
||||
@ -1,40 +0,0 @@
|
||||
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);
|
||||
}
|
||||
|
||||
@ -1,44 +0,0 @@
|
||||
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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user