import type { Page } from 'playwright'; import { APP_URL, DASHBOARD_PATH } from './config.js'; import type { Storyboard } from './script.js'; export async function installDemoRoutes(page: Page, storyboard: Storyboard) { await Promise.all([stubAiFilters(page, storyboard), stubExport(page)]); } export function dashboardUrl(storyboard: Storyboard): string { const view = storyboard.content.initialMapView; const params = new URLSearchParams({ lat: String(view.lat), lon: String(view.lon), zoom: String(view.zoom), }); // Cold-open filters: applied through the URL so the first frame already // shows a filtered map. Numeric features use name:min:max, enums name:a|b. for (const [name, value] of Object.entries(storyboard.content.initialFilters ?? {})) { const entry = typeof value[0] === 'number' ? `${name}:${value[0]}:${value[1]}` : `${name}:${(value as string[]).join('|')}`; params.append('filter', entry); } for (const tt of storyboard.content.stubbedTravelTimeFilters) { params.append('tt', `${tt.mode}:${tt.slug}:${tt.label}:${tt.min ?? 0}:${tt.max ?? 120}`); } return `${APP_URL}${DASHBOARD_PATH}?${params}`; } async function stubAiFilters(page: Page, storyboard: Storyboard) { await page.route('**/api/ai-filters', async (route) => { await new Promise((r) => setTimeout(r, 120)); await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ filters: storyboard.content.stubbedFilters, travel_time_filters: storyboard.content.stubbedTravelTimeFilters, notes: '', match_count: storyboard.content.matchCount ?? 1247, }), }); }); } async function stubExport(page: Page) { await page.route('**/api/export?**', async (route) => { await new Promise((r) => setTimeout(r, 120)); await route.fulfill({ status: 200, contentType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', headers: { 'content-disposition': 'attachment; filename="perfect-postcode-export.xlsx"', }, body: Buffer.from('Perfect Postcode demo export\n'), }); }); }