Implement real API clients and update types

Replace mock data in geocoding, HAFAS, and bike routing clients with
actual implementations using fetch and appropriate interfaces. Update
typescript definitions to match the new data structures, including
Journey, Station, and BikeRoute. Add vitest and related testing
dependencies, replacing the placeholder test script with actual tests
for the new client logic. Refactor calendar and countdown utilities to
use the new types and remove unused files.

Implement real API clients and update types

Replace mock data in HafasClient, GeocodingClient, and
BikeRoutingClient with actual fetch implementations. Update
types to match API responses and add Vitest tests.
This commit is contained in:
2026-05-09 11:55:20 +02:00
parent b131e3c69a
commit 3b87b8c4e5
18 changed files with 2486 additions and 390 deletions
+63 -46
View File
@@ -103,11 +103,10 @@ Each event card shows both travel modes side by side:
```
oebb_planner/
├── package.json
├── next.config.js
├── next.config.ts
├── tsconfig.json
├── tailwind.config.ts
├── postcss.config.mjs
├── vitest.config.ts
├── postcss.config.mjs ← Tailwind v4 uses @tailwindcss/postcss; no tailwind.config.ts needed
├── vitest.config.ts ← must exist before Phase 7; deferred from Phase 1
├── src/
│ ├── app/
│ │ ├── layout.tsx ← root layout (fonts, providers, navbar)
@@ -161,27 +160,31 @@ oebb_planner/
│ │ ├── useCalendar.ts ← calendar import logic
│ │ └── useEventsStore.ts ← shared events state (NEW)
│ ├── lib/
│ │ ├── hafas.ts ← HAFAS API client + types
│ │ ├── calendar.ts ← ICS helpers (extractEvents, cleanLocation)
│ │ ├── countdown.ts leaveBy, countdownInfo
│ │ ├── formatting.ts ← fmtTime, delayColor
│ │ ├── demo.ts ← demo journey generator
│ │ ├── constants.ts WALK_MINS, RED, SAMPLE_EVENTS, HAFAS_BASE
│ │ ├── geocode.ts ← Nominatim client (NEW)
│ │ ── bike-route.ts ← OSRM client (NEW)
│ │ ├── hafas-client.ts ← HAFAS API client (class HafasClient)
│ │ ├── calendar-utils.ts ← ICS helpers (extractEvents, cleanLocation)
│ │ ├── countdown-utils.tscalculateCountdown, leaveBy helpers
│ │ ├── formatting.ts ← formatTime, formatDate, formatDateTime, formatDuration, formatDistance
│ │ ├── demo.ts ← demo journey generator
│ │ ├── constants.ts ← HAFAS_URL, NOMINATIM_URL, OSRM_URL, HAFAS_TIMEOUT_MS, DEFAULT_DAYS, APP_VERSION
│ │ ├── status-utils.ts ← StatusUtils.checkServerStatus() (used by useServerHealth)
│ │ ── live-status-utils.ts ← LiveStatusUtils.getLiveStatus() (stub; live data TBD)
│ │ ├── geocoding-client.ts ← Nominatim client (NEW)
│ │ ├── bike-routing-client.ts ← OSRM client (NEW)
│ │ └── index.ts ← re-exports all lib modules
│ └── types/
│ └── index.ts ← all TypeScript interfaces
├── __tests__/
│ ├── lib/
│ ├── calendar.test.ts
│ ├── countdown.test.ts
│ └── bike-route.test.ts ← NEW
│ └── api/
│ ├── hafas.test.ts
├── calendar.test.ts
├── geocode.test.ts ← NEW
├── bike-route.test.ts ← NEW
└── health.test.ts
├── src/lib/__tests__/ ← unit tests live alongside lib, not at repo root
│ ├── hafas-client.test.ts
│ ├── geocoding-client.test.ts
│ ├── calendar-utils.test.ts
├── countdown-utils.test.ts
│ └── bike-routing-client.test.ts ← NEW
├── src/app/api/__tests__/ ← API route tests
├── hafas.test.ts
├── calendar.test.ts
├── geocode.test.ts ← NEW
├── bike-route.test.ts ← NEW
│ └── health.test.ts
├── public/
│ ├── favicon.ico
│ └── robots.txt
@@ -335,7 +338,7 @@ type CalStatus = null | "loading" | "ok" | "error";
|----------|--------|-----------|
| Framework | Next.js 15 App Router | Modern standard; API routes are serverless-compatible |
| Language | TypeScript (strict) | Type safety across frontend and backend |
| Styling | Tailwind CSS | Replaces 700 lines of inline styles; consistent theming |
| Styling | Tailwind CSS v4 | Replaces 700 lines of inline styles; v4 uses CSS-native config, no `tailwind.config.ts` |
| State | React hooks + Context | No Redux needed for this scale |
| API proxy | Next.js Route Handlers | Same logic as Express, no Express dependency |
| ICS parsing | Keep `node-ical` | Already works, well-tested |
@@ -360,11 +363,11 @@ type CalStatus = null | "loading" | "ok" | "error";
### Phase 2 — Types + Library Layer (~2 hours)
7. Define TypeScript types in `src/types/index.ts`
8. Port `lib/hafas.ts`HAFAS API client functions with proper types
9. Port `lib/calendar.ts``extractEvents` + `cleanLocation` (reusable in API routes AND tests)
10. Port `lib/countdown.ts`, `lib/formatting.ts`, `lib/constants.ts`, `lib/demo.ts`
11. Create `lib/geocode.ts` — Nominatim client (NEW)
12. Create `lib/bike-route.ts` — OSRM client (NEW)
8. Port `lib/hafas-client.ts``HafasClient` class with `searchStation()` and `fetchJourneys()`
9. Port `lib/calendar-utils.ts``extractEvents()` + `cleanLocation()` (reusable in API routes AND tests)
10. Port `lib/countdown-utils.ts`, `lib/formatting.ts`, `lib/constants.ts`, `lib/demo.ts`, `lib/status-utils.ts`, `lib/live-status-utils.ts`
11. Create `lib/geocoding-client.ts` — Nominatim client (NEW)
12. Create `lib/bike-routing-client.ts` — OSRM client (NEW)
### Phase 3 — API Routes (~45 min)
@@ -411,10 +414,12 @@ type CalStatus = null | "loading" | "ok" | "error";
### Phase 7 — Tests (~2 hours)
44. Migrate `server/__tests__/*.test.js``__tests__/api/*.test.ts`
45. Add unit tests for `lib/calendar.ts`, `lib/countdown.ts`
46. Add API tests for `geocode` and `bike-route` (NEW)
47. Add component smoke tests with `@testing-library/react`
> Install test dependencies first: `npm install -D vitest @vitejs/plugin-react @testing-library/react @testing-library/jest-dom jsdom` and create `vitest.config.ts`. Update the `test` script in `package.json` to `vitest`.
44. Migrate `server/__tests__/*.test.js``src/app/api/__tests__/*.test.ts`
45. Add unit tests for `src/lib/calendar-utils.ts`, `src/lib/countdown-utils.ts` in `src/lib/__tests__/`
46. Add API tests for `geocode` and `bike-route` in `src/app/api/__tests__/` (NEW)
47. Add component smoke tests with `@testing-library/react` _(optional)_
### Phase 8 — Cleanup (~30 min)
@@ -428,33 +433,43 @@ type CalStatus = null | "loading" | "ok" | "error";
## 8. Dependencies
### Runtime
### Runtime (installed)
```json
{
"dependencies": {
"next": "^15.0.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"next": "16.2.6",
"react": "19.2.4",
"react-dom": "19.2.4",
"node-ical": "^0.18.0"
}
}
```
### Development
### Development (installed)
```json
{
"devDependencies": {
"typescript": "^5",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"tailwindcss": "^4",
"@tailwindcss/postcss": "^4",
"eslint": "^9",
"eslint-config-next": "16.2.6"
}
}
```
### To be installed before Phase 7 (tests)
```json
{
"devDependencies": {
"typescript": "^5.6.0",
"@types/node": "^22.0.0",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@types/node-ical": "^0.18.0",
"tailwindcss": "^3.4.0",
"postcss": "^8.4.0",
"autoprefixer": "^10.4.0",
"vitest": "^2.0.0",
"@vitejs/plugin-react": "^4.0.0",
"@testing-library/react": "^16.0.0",
"@testing-library/jest-dom": "^6.0.0",
"jsdom": "^25.0.0"
@@ -462,6 +477,8 @@ type CalStatus = null | "loading" | "ok" | "error";
}
```
> Note: Tailwind v4 no longer requires `autoprefixer` or a separate `tailwind.config.ts` — configuration is done via CSS and `@tailwindcss/postcss`.
---
## 9. Environment Variables