Import initial : code en production sur Hetzner
Some checks are pending
CI / tests (push) Waiting to run
CI / deploiement (push) Blocked by required conditions

This commit is contained in:
Adam Belkacemi 2026-09-07 18:21:38 +00:00
commit cf6ee64e31
43 changed files with 14404 additions and 0 deletions

94
shared/booking.ts Normal file
View file

@ -0,0 +1,94 @@
import type { Place } from './types'
export interface BookingLink {
url: string
label: string
provider: string
}
/** Normalise un nom de ville pour l'inclure dans une URL slug. */
function slugify(label: string): string {
return label
.normalize('NFD').replace(/\p{Diacritic}/gu, '')
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '')
}
/** Extrait le nom de ville avant la virgule ("Lille, France" -> "Lille"). */
function cityName(label: string): string {
return label.split(',')[0]?.trim() ?? label.trim()
}
/** Format de date que Kombo et CheckMyBus acceptent tous les deux. */
function dateISO(d: Date): string {
return d.toISOString().slice(0, 10)
}
/** Kombo comprend les noms de ville en clair : pas de mapping de gares a maintenir. */
export function buildKomboLink(from: Place, to: Place, date: Date): BookingLink {
const f = encodeURIComponent(cityName(from.label))
const t = encodeURIComponent(cityName(to.label))
const d = dateISO(date)
return {
url: `https://www.kombo.co/fr/app/outward/1/1/18/${d}/${f}/${t}/0/0/results`,
label: 'Reserver sur Kombo',
provider: 'Kombo',
}
}
/**
* CheckMyBus accepte un fallback avec UUID zeros quand on ne connait pas
* son identifiant interne : le nom + les coordonnees suffisent a lancer
* la recherche. Le fragment porte tout, y compris la date.
*/
export function buildCheckMyBusLink(from: Place, to: Place, date: Date): BookingLink {
const zeroId = '00000000-0000-0000-0000-000000000000'
const encodeStop = (p: Place) =>
encodeURIComponent(`${cityName(p.label)}, France`)
+ `%24${p.lat}%2C${p.lon}`
+ `%24${zeroId}%24${zeroId}%24true%24false`
const fromSlug = slugify(cityName(from.label))
const toSlug = slugify(cityName(to.label))
const d = dateISO(date)
const fragment = [
`departureDate=${d}`,
`origin=${encodeStop(from)}`,
`destination=${encodeStop(to)}`,
'sortValue=Relevance',
'sortOrder=ascending',
'adults=1',
].join('&')
return {
url: `https://www.checkmybus.fr/${fromSlug}/${toSlug}?mode=search#${fragment}`,
label: 'Chercher sur CheckMyBus',
provider: 'CheckMyBus',
}
}
/** Chargemap : on ouvre juste la page d'accueil du planificateur. */
export function buildChargemapLink(): BookingLink {
return {
url: 'https://chargemap.com/map',
label: 'Ouvrir Chargemap',
provider: 'Chargemap',
}
}
/** Retourne le lien approprie pour le mode donne, ou null si aucun. */
export function buildBookingLink(
mode: 'train' | 'bus' | 'electric_car' | 'bike',
from: Place,
to: Place,
date: Date,
): BookingLink | null {
switch (mode) {
case 'train': return buildKomboLink(from, to, date)
case 'bus': return buildCheckMyBusLink(from, to, date)
case 'electric_car': return buildChargemapLink()
case 'bike': return null
}
}