RELEASE V0.0.1.2 : Insertion des données de la BDD + Suppression des données mockées

This commit is contained in:
mathis committed 2026-08-26 20:48:16 +02:00
1 parent 3eed2b9cd4
commit 9b7ff6e295
8 files changed
+1372 -757

No files matched your search

+24
View File
@@ -0,0 +1,24 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512">
<defs>
<linearGradient id="sncf-grad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#0066a4"/>
<stop offset="100%" stop-color="#004b7a"/>
</linearGradient>
<filter id="shadow" x="-10%" y="-10%" width="120%" height="120%">
<feDropShadow dx="0" dy="4" stdDeviation="8" flood-color="#000" flood-opacity="0.2"/>
</filter>
</defs>
<rect width="512" height="512" rx="108" fill="url(#sncf-grad)"/>
<!-- SNCF Red Badge -->
<rect x="80" y="80" width="160" height="70" rx="8" fill="#cd0037" filter="url(#shadow)"/>
<text x="160" y="130" fill="#ffffff" font-family="Avenir, sans-serif" font-weight="900" font-size="44" text-anchor="middle" letter-spacing="2">SNCF</text>
<!-- Train Icon -->
<g transform="translate(136, 170)" fill="#ffffff">
<path d="M120 0C53.7 0 0 53.7 0 120v100c0 33.1 26.9 60 60 60v20c0 11 9 20 20 20s20-9 20-20v-20h80v20c0 11 9 20 20 20s20-9 20-20v-20c33.1 0 60-26.9 60-60V120C240 53.7 186.3 0 120 0zm-80 60h160v60H40V60zm30 150c-11 0-20-9-20-20s9-20 20-20 20 9 20 20-9 20-20 20zm100 0c-11 0-20-9-20-20s9-20 20-20 20 9 20 20-9 20-20 20z"/>
</g>
<!-- TER ZOU text / Tag -->
<text x="256" y="440" fill="#ffffff" font-family="Avenir, sans-serif" font-weight="800" font-size="34" text-anchor="middle" letter-spacing="1">FERROVIA RÉGIONS</text>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

+36
View File
@@ -0,0 +1,36 @@
{
"name": "FERROVIA - Sites TER Régionaux",
"short_name": "FERROVIA",
"description": "Consultez les horaires, l'info trafic et achetez vos billets TER dans toutes les régions de France.",
"start_url": "/",
"id": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#0066a4",
"orientation": "portrait-primary",
"icons": [
{
"src": "/icon.svg",
"sizes": "any",
"type": "image/svg+xml",
"purpose": "any maskable"
},
{
"src": "/icon.svg",
"sizes": "192x192",
"type": "image/svg+xml"
},
{
"src": "/icon.svg",
"sizes": "512x512",
"type": "image/svg+xml"
}
],
"categories": [
"travel",
"transportation"
],
"lang": "fr-FR",
"dir": "ltr",
"prefer_related_applications": false
}
+48
View File
@@ -0,0 +1,48 @@
const CACHE_NAME = 'ferrovia-pwa-v2';
self.addEventListener('install', (event) => {
self.skipWaiting();
});
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((keys) => {
return Promise.all(
keys.map((key) => {
if (key !== CACHE_NAME) {
return caches.delete(key);
}
})
);
}).then(() => self.clients.claim())
);
});
// Network-First strategy: Always try network first to guarantee live updates
self.addEventListener('fetch', (event) => {
if (event.request.method !== 'GET') return;
const url = new URL(event.request.url);
// Do not cache API or Supabase requests
if (url.pathname.startsWith('/api') || url.hostname.includes('supabase')) {
return;
}
event.respondWith(
fetch(event.request)
.then((networkResponse) => {
if (networkResponse && networkResponse.status === 200 && networkResponse.type === 'basic') {
const responseToCache = networkResponse.clone();
caches.open(CACHE_NAME).then((cache) => {
cache.put(event.request, responseToCache);
});
}
return networkResponse;
})
.catch(() => {
return caches.match(event.request);
})
);
});
+80 -9
View File
@@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { supabase } from '@/lib/supabase'; import { supabase } from '@/lib/supabase';
import RegionContent from '@/components/RegionContent'; import RegionContent, { InfoTraficItem, ActualiteItem, HoraireItem } from '@/components/RegionContent';
interface RegionData { interface RegionData {
nom_site: string; nom_site: string;
@@ -8,12 +8,8 @@ interface RegionData {
description_site: string; description_site: string;
} }
interface GeneralParams { export const dynamic = 'force-dynamic';
region: string; export const revalidate = 0;
regions: Record<string, RegionData>;
}
export const revalidate = 60; // Revalidate every 60s
export default async function RegionPage({ export default async function RegionPage({
params, params,
@@ -22,7 +18,8 @@ export default async function RegionPage({
}) { }) {
const { region } = await params; const { region } = await params;
const { data, error } = await supabase // 1. Fetch general settings
const { data } = await supabase
.from('parametres') .from('parametres')
.select('valeur') .select('valeur')
.eq('cle', 'general') .eq('cle', 'general')
@@ -69,5 +66,79 @@ export default async function RegionPage({
}; };
} }
return <RegionContent regionSlug={region} regionData={regionData} />; // 2. Fetch info trafic from Supabase table "info_trafic"
let infoTraficList: InfoTraficItem[] = [];
try {
const { data: trafficData } = await supabase
.from('info_trafic')
.select('*')
.eq('actif', true)
.is('deleted_at', null)
.order('created_at', { ascending: false });
if (trafficData) {
infoTraficList = trafficData as InfoTraficItem[];
}
} catch (e) {
console.error('Error fetching info_trafic:', e);
}
// 3. Fetch actualites from Supabase table "actualites"
let actualitesList: ActualiteItem[] = [];
try {
const { data: actusData } = await supabase
.from('actualites')
.select('*')
.eq('actif', true)
.is('deleted_at', null)
.order('date_publication', { ascending: false })
.order('created_at', { ascending: false });
if (actusData) {
actualitesList = actusData as ActualiteItem[];
}
} catch (e) {
console.error('Error fetching actualites:', e);
}
// 4. Fetch gares and horaires from Supabase table "horaires"
let horairesList: HoraireItem[] = [];
try {
const { data: garesData } = await supabase
.from('gares')
.select('id, nom');
const garesMap: Record<string, string> = {};
if (garesData) {
garesData.forEach((g: any) => {
garesMap[g.id] = g.nom;
});
}
const { data: rawHoraires } = await supabase
.from('horaires')
.select('*')
.eq('actif', true)
.is('deleted_at', null)
.order('heure_depart', { ascending: true });
if (rawHoraires) {
horairesList = rawHoraires.map((h: any) => ({
...h,
gare_depart_nom: garesMap[h.gare_depart_id] || '',
gare_arrivee_nom: garesMap[h.gare_arrivee_id] || '',
})) as HoraireItem[];
}
} catch (e) {
console.error('Error fetching horaires:', e);
}
return (
<RegionContent
regionSlug={region}
regionData={regionData}
initialInfoTrafic={infoTraficList}
initialActualites={actualitesList}
initialHoraires={horairesList}
/>
);
} }
+24
View File
@@ -145,3 +145,27 @@ body {
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slideInLeftDrawer {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0);
}
}
.animate-fadeIn {
animation: fadeIn 0.2s ease-out forwards;
}
.animate-slideInLeftDrawer {
animation: slideInLeftDrawer 0.28s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
+51 -10
View File
@@ -1,6 +1,7 @@
import type { Metadata } from "next"; import type { Metadata, Viewport } from "next";
import { Geist, Geist_Mono } from "next/font/google"; import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css"; import "./globals.css";
const geistSans = Geist({ const geistSans = Geist({
variable: "--font-geist-sans", variable: "--font-geist-sans",
subsets: ["latin"], subsets: ["latin"],
@@ -12,20 +13,60 @@ const geistMono = Geist_Mono({
}); });
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Create Next App", title: "FERROVIA - Sites TER Régionaux",
description: "Generated by create next app", description: "Consultez les horaires, l'info trafic et achetez vos billets TER dans toutes les régions de France.",
manifest: "/manifest.json",
appleWebApp: {
capable: true,
statusBarStyle: "default",
title: "FERROVIA",
},
icons: {
icon: "/icon.svg",
apple: "/icon.svg",
},
}; };
export default function RootLayout({ children }: { children: React.ReactNode })
{
return (
export const viewport: Viewport = {
themeColor: "#0066a4",
width: "device-width",
initialScale: 1,
maximumScale: 1,
userScalable: false,
viewportFit: "cover",
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html <html
lang="en" lang="fr"
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`} className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
> >
<body className="sncf-voyageurs">{children} <head>
<link rel="manifest" href="/manifest.json" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
<meta name="apple-mobile-web-app-title" content="FERROVIA" />
<link rel="apple-touch-icon" href="/icon.svg" />
</head>
<body className="sncf-voyageurs min-h-full">
{children}
<script
dangerouslySetInnerHTML={{
__html: `
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js').then(function(reg) {
reg.update();
}).catch(function(err) {
console.log('ServiceWorker registration failed: ', err);
});
});
}
`,
}}
/>
</body> </body>
</html> </html>
); );
+11 -18
View File
@@ -1,31 +1,24 @@
import React from 'react'; import { redirect } from 'next/navigation';
import { supabase } from '@/lib/supabase'; import { supabase } from '@/lib/supabase';
import HomeContent from '@/components/HomeContent';
interface RegionData { export const dynamic = 'force-dynamic';
nom_site: string; export const revalidate = 0;
logo_header_url: string;
description_site: string;
}
interface GeneralParams {
region: string;
regions: Record<string, RegionData>;
}
export const revalidate = 60; // Revalidate every 60s
export default async function HomePage() { export default async function HomePage() {
const { data, error } = await supabase const { data } = await supabase
.from('parametres') .from('parametres')
.select('valeur') .select('valeur')
.eq('cle', 'general') .eq('cle', 'general')
.single(); .single();
let general: GeneralParams | null = null; let targetRegion = 'bourgogne-franche-comte';
if (data && data.valeur) { if (data && data.valeur) {
general = data.valeur as GeneralParams; const val = data.valeur as any;
if (val.region) {
targetRegion = val.region;
}
} }
return <HomeContent general={general} />; redirect(`/${targetRegion}`);
} }
File diff suppressed because it is too large. Load diff