← Terug naar masterplan

Bossuyt Changenotes

Versiegeschiedenis van het plan, de cursus en de service app — met uitleg van elke nieuwe functie.

v1.5 9 april 2026 Staging workflow + server migratie plan
Nieuw docker-compose.staging.yml — Tijdelijke testomgeving

Aparte Docker Compose file die een staging versie opstart op staging-bossuyt.fixassistant.com. Eigen database zodat productiedata nooit geriskeerd wordt. Container herstart niet automatisch — staging is bewust tijdelijk.

💡 Hoe gebruik je dit?

Opstarten: docker compose -f docker-compose.staging.yml up --build -d

Stoppen na testen: docker compose -f docker-compose.staging.yml down

De -f flag vertelt Docker Compose welk bestand te gebruiken. Zonder die flag gebruikt het altijd docker-compose.yml (productie). Zo kunnen beide naast elkaar draaien zonder conflict — andere container naam, andere subdomain, andere Traefik router.

Workflow per sessie (1 dag/week)
# 1. Code schrijven — geen impact op productie

# 2. Staging opstarten en testen
docker compose -f docker-compose.staging.yml up --build -d
# → https://staging-bossuyt.fixassistant.com

# 3. Alles ok? Naar productie
docker compose up --build -d

# 4. Staging opruimen
docker compose -f docker-compose.staging.yml down
Nieuw Fase 5 — Server migratie Hetzner → Bossuyt

Nieuwe fase toegevoegd aan het masterplan (post Week 40): volledige verhuizing van de Hetzner server naar een Bossuyt-eigen server. Inclusief database backup strategie, Docker image inventarisatie, DNS switch procedure en rollback plan.

💡 Waarom is migratie risicovol?

Een server migratie heeft drie gevaarlijke momenten:

1. Database: als de dump niet compleet is of de restore mislukt, verlies je data. Oplossing: restore altijd eerst testen op de nieuwe server vóór de echte migratie.

2. DNS: DNS wijzigingen propageren traag (soms uren). Oplossing: TTL vooraf verlagen naar 5 minuten, zodat een rollback snel effectief is.

3. Environment variables: .env bestanden zijn nooit in git. Oplossing: inventariseer ze expliciet vóór de migratie.

Database backup commando's
# Backup (op oude server)
docker exec bossuyt-db pg_dump -U bossuyt -Fc bossuyt > bossuyt.dump
docker exec keycloak-db pg_dump -U keycloak -Fc keycloak > keycloak.dump

# Overbrengen naar nieuwe server
scp bossuyt.dump user@nieuwe-server:/backups/

# Restore (op nieuwe server)
docker exec -i bossuyt-db pg_restore -U bossuyt -d bossuyt < bossuyt.dump
Nieuw Staging workflow — plan website sectie

Stap-voor-stap staging workflow toegevoegd aan de "Waar starten" sectie van het masterplan: code schrijven → staging bouwen → testen → productie deployen → staging stoppen.

docker-compose.staging.yml Plan: Fase 5 server migratie Plan: staging workflow sectie
v1.4 9 april 2026 Service App fundament
Nieuw types/planning.ts — Planning types

Nieuwe TypeScript types die de plannningslogica beschrijven: WorkOrder (een werkbon met type planned of open), RemovedReason (waarom een technieker een job uit zijn planning haalt), RouteStep (rijtijd tussen twee stops) en TechnicianDayCache (alles wat gecached wordt bij dagstart).

💡 Wat zijn TypeScript types?

Types zijn als afspraken: je beschrijft de vorm van je data zodat TypeScript je waarschuwt als je iets vergeet of een typfout maakt. Het zijn geen echte objecten — ze bestaan enkel tijdens het compileren.

types/planning.ts (fragment)
// RemovedReason is een "union type" — één van deze exacte strings
type RemovedReason =
  | 'cant_finish'        // Lukt niet meer
  | 'missing_material'   // Materiaal ontbreekt
  | 'impossible_timing'  // Onmogelijke timing voor klant
  | 'other'              // Andere (verplicht vrij tekstveld)

// Als je nu ergens removedReason = 'verkeerd' schrijft → TypeScript fout
// Als je removedReason vergeet in te vullen → TypeScript fout
Nieuw lib/routing/ — Swappable routing service

Een IRoutingService interface plus een OrsRoutingService implementatie. De interface definieert wat de service doet, de implementatie definieert hoe. Later swap je ORS naar TomTom zonder de rest van de app te wijzigen.

💡 Wat is een interface?

Een interface is een contract. Het zegt: "alles wat dit interface implementeert, moet deze functies hebben." De OrsRoutingService tekent dit contract en levert de echte code. Morgen maak je TomTomRoutingService — zelfde contract, andere code.

lib/routing/IRoutingService.ts
// Het contract — geen implementatie, enkel de signatuur
export interface IRoutingService {
  getETA(from: Coordinates, to: Coordinates): Promise<RouteResult>
}

// ORS tekent het contract
export class OrsRoutingService implements IRoutingService {
  async getETA(from, to) { /* echte ORS code */ }
}

// Gebruik: je kent enkel het contract — niet welke provider
const eta = await routingService.getETA(depot, klant)
// ↑ werkt met ORS én TomTom zonder aanpassing
Nieuw lib/idb.ts — Offline database

De browser-database van de Service App. Slaat interventies, werkbon-formulierdata, offline acties en sync-metadata op zodat alles werkt zonder internet.

💡 Wat is IndexedDB?

IndexedDB is een database ingebouwd in elke browser. Je kan er grote hoeveelheden data in opslaan — ook als je offline bent. We gebruiken het idb pakket dat de moeilijke native API omzet naar gewone async functies.

4 stores (tabellen):

lib/idb.ts — stores overzicht
// interventions — de werkbonnen van de dag (planned + open)
// werkbonnen    — ingevulde formulieren per werkbon
// pendingWrites — acties gedaan offline, wachten op sync
// dayMeta       — wanneer was de laatste sync?

// Voorbeeld: werkbon opslaan (elke keystroke)
await saveWerkbon({
  interventionId: 'int-42',
  parts: [{ articleId: 'art-1', qty: 2 }],
  notes: 'Filter vervangen',
  followUpRequired: false,
  // ...
})
// → opgeslagen in IndexedDB, overleeft refresh en offline

Transaction: wanneer we de dagcache vervangen, gebruiken we een transaction. Dat garandeert dat óf alles lukt óf niets verandert — nooit halfweg.

Nieuw lib/sync.ts — Dagstart synchronisatie

Haalt elke ochtend max 10 werkbonnen op (6 planned + 4 open pool) en slaat ze offline op. Vraagt ook rijtijden op voor de geplande stops. Als de sync mislukt door geen internet — geen probleem, gisteren's cache blijft bruikbaar.

💡 Hoe werkt de sync flow?
lib/sync.ts — flow
// 1. Controleer of sync nodig is (slechts 1x per dag)
const nodig = await shouldSync(technicianId)
if (!nodig) return  // cache is nog geldig

// 2. Haal data op van de server
const res = await fetch(`/api/sync/today?technicianId=...`)
const { planned, open } = await res.json()

// 3. Beperk tot max 6 planned + 4 open
const beperkt = [...planned.slice(0, 6), ...open.slice(0, 4)]

// 4. Sla op in IndexedDB (vervangt vorige dag)
await cacheInterventions(beperkt)

// 5. Haal rijtijden op (ORS matrix call)
const route = await fetchDailyRoute(planned)

// 6. Klaar — app werkt nu volledig offline

Pending writes: als je offline een status wijzigt, gaat die actie in de pendingWrites store. Zodra je terug online bent roept syncPendingWrites() ze allemaal op de server — in volgorde.

Nieuw app/api/route/ — ORS routing endpoints

Twee Next.js API routes: /api/route voor een enkele hop, /api/route/daily voor de volledige dagroute. De ORS API key zit server-side — nooit zichtbaar in de browser. Werkt ook zonder key (geeft mock-data terug in development).

💡 Waarom een backend endpoint?

Je kan ORS niet rechtstreeks vanuit de browser aanroepen met je API key — dan is de key zichtbaar voor iedereen. De Next.js route handler draait op de server, leest de key uit process.env.ORS_API_KEY (een geheime omgevingsvariabele) en stuurt de route terug naar de browser.

app/api/route/route.ts — kern
// Server-side: key is veilig
const routingService = new OrsRoutingService(
  process.env.ORS_API_KEY ?? ''  // uit .env, nooit in de code
)

export async function POST(req: NextRequest) {
  const { from, to } = await req.json()
  const result = await routingService.getETA(from, to)
  return NextResponse.json(result)
  // → { distanceKm: 12.4, travelMinutes: 22, provider: 'ors' }
}
types/planning.ts lib/routing/IRoutingService.ts lib/routing/OrsRoutingService.ts lib/idb.ts lib/sync.ts app/api/route/route.ts app/api/route/daily/route.ts
v1.3 8 april 2026 Planning logica + routing service in plan & cursus
Nieuw Planningssysteem — planned/open split

De Service App krijgt 10 gecachte werkbonnen per dag: max 6 geplande items (dispatcher-assigned met deadline) en max 4 open pool items (technieker kiest zelf). Technieker kan een gepland item verwijderen met een reden. Drag & drop om volgorde aan te passen. Optionele collega-view.

Nieuw Routing service abstractie — ORS fase 1

Provider-agnostisch routingService.getETA(from, to). Fase 1: OpenRouteService (gratis, account.heigit.org). Fase 2: TomTom swap met traffic-aware ETA — enkel server.ts wijzigt. UI toont disclaimer "zonder file-rekening" op basis van provider veld in response.

Nieuw Core API Cursus — Les 8: Routing service

Nieuwe les in de cursus op /coreapi: IRoutingService interface, ORS implementatie, dependency injection, beide endpoints, en UI disclaimer logica.

Nieuw MD bestanden — spec & code documentatie

service-app-planning.md en routing-service.md aangemaakt — volledige TypeScript code en uitleg voor toekomstige sessies.

v1.2 8 april 2026 Core API cursus (8 lessen)
Nieuw /coreapi — Stap-voor-stap cursus

Aparte pagina op /coreapi met 8 lessen: Project setup → Fastify → Docker + PostgreSQL → Drizzle schema → Keycloak JWT → Endpoints → NAV sync → Routing service. Sidebar navigatie, highlight.js syntax highlighting, callout boxes, progress bar.

Verbeterd /coreapi — Mobile-friendly

Vaste top bar op mobiel met huidige les-titel en hamburger menu. Code blocks scrollen horizontaal. Kleinere typografie op kleine schermen. Prev/next navigatie stapelt verticaal.

v1.1 8 april 2026 Mobile-friendly plan website
Nieuw Full / Compact view toggle

Pill-knop in de sticky nav: 🖥 Full (brede layout, Gantt chart) en 📱 Compact (mobiele layout, fase-progress bars, gestapelde kolommen). Auto-detecteert telefoon bij eerste bezoek. Keuze opgeslagen in localStorage.

Verbeterd Architectuurdiagram — compact versie

In compact mode: gestapelde versie van het architectuurdiagram i.p.v. het brede 5-koloms grid.

Verbeterd Gantt → Fase progress bars

De 40-koloms Gantt chart is onleesbaar op mobiel. In compact mode vervangen door 4 gekleurde progress bars per fase met week-indicaties.

v1.0 6–7 april 2026 Initieel masterplan
Nieuw Plan website — volledig masterplan

Gebaseerd op Gemini brainstorm sessie, uitgewerkt met Claude + OMC. 40 weken (1 dag/week), 4 fases: Foundation → NAV Bridge → Admin Portal → App Modularisatie. Gantt chart, architectuurdiagram, tech stack, risico register, analyse sectie.

Nieuw Docker + Traefik deployment

nginx:alpine container geserveerd via Traefik op plan.bossuyt.fixassistant.com. Directory volume mount zodat bewerkingen direct zichtbaar zijn zonder container restart.

Nieuw PWA offline-first strategie

Service App strategie: 10 werkbonnen cachen bij dagstart (later verhoogd van 5), klant/toesteldata ad-hoc + gecached, wagenstock (~1000 lijnen) enkel bij verbinding, Service Worker als offline fallback.