feat: wire build/test infra, trips API, and enriched journey stops

- Add tsconfig.json (server) + client/tsconfig.{json,app.json,node.json}
  so typecheck and tsc -b actually work.
- Fix npm test to run Playwright (was running vitest on Playwright specs);
  typecheck now covers both server and client.
- Mount routes before app.listen, add error handler, mount optional
  @tonycodes/auth-express middleware when AUTH_SECRET is set.
- Add /api/trips (GET/POST/PATCH/DELETE) backed by an in-memory store
  that gracefully degrades when DATABASE_URL is unset.
- Add prisma/seed.ts skeleton and server/types/express.d.ts for req.auth.
- Rewrite Grok prompt for combo-aware planning: charge+eat,
  stay+destination-charging, eat+viewpoint, etc., with amenities,
  cuisine, priceLevel, duration, day titles and trip highlights.
- Extend Stop schema + normalization to preserve all enrichment fields.
- New StopCard component renders combo pill, description, meta row
  (charge / stop / battery / cuisine / £-level) and amenity icons;
  map popups show the same enriched detail; timeline gains day titles
  and a HIGHLIGHTS sidebar.
- Fix server TS errors (vehicle accepted as string | {name,rangeKm},
  JSON parse results typed).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-19 10:32:53 +01:00
parent d516e93323
commit 89b24d4c34
24 changed files with 1263 additions and 243 deletions
+62
View File
@@ -0,0 +1,62 @@
/**
* Tesla Roadtrip seed
*
* Generates a small demo trip so the API has something to return after a fresh setup.
* Requires DATABASE_URL and a Prisma client generated from prisma/schema.prisma.
*
* Run: npm run db:seed
*/
import { PrismaClient } from './generated/prisma/index.js';
const prisma = new PrismaClient();
async function main() {
const demoUserId = 'demo-user';
const existing = await prisma.trip.findFirst({ where: { userId: demoUserId } });
if (existing) {
console.log('Seed skipped — demo trip already exists:', existing.id);
return;
}
const trip = await prisma.trip.create({
data: {
userId: demoUserId,
title: 'London → Edinburgh (demo)',
vehicleModel: 'Model Y Long Range',
rangeMi: 320,
status: 'planning',
isPublic: false,
itinerary: {
days: [
{
day: 1,
stops: [
{ id: 'london', name: 'London', type: 'custom', lat: 51.5074, lng: -0.1278, day: 1, order: 1 },
{ id: 'mk-sc', name: 'Milton Keynes Supercharger', type: 'supercharger', lat: 52.0406, lng: -0.7594, day: 1, order: 2, chargeMinutes: 25 },
{ id: 'leeds-sc', name: 'Leeds Supercharger', type: 'supercharger', lat: 53.8008, lng: -1.5491, day: 1, order: 3, chargeMinutes: 30 },
],
},
{
day: 2,
stops: [
{ id: 'edi', name: 'Edinburgh', type: 'attraction', lat: 55.9533, lng: -3.1883, day: 2, order: 1 },
],
},
],
summary: { totalDistanceKm: 670, estDriveHours: 8, estChargeHours: 1.2, superchargers: 2, hotels: 0 },
},
},
});
console.log('Seed created demo trip:', trip.id);
}
main()
.catch((err) => {
console.error('Seed failed:', err);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});