spreadlab/web/src/components/ComparisonTable.vue
Justin Visser c935071bd1 web: parity page renders the three-scenario comparison from live data
App.vue fetches the default config, posts it to /api/comparison, and
renders ComparisonTable; the Vite dev server proxies /api to the Go
server so the browser sees one origin. src/lib/api.ts is the only
fetch code and uses exclusively generated types: no shape is defined
on the frontend. Scaffold example components removed.
Engine fix surfaced by the smoke test: a nil Go slice marshals to
JSON null, violating the generated 'educated: number[]' contract;
RunScenario now returns an empty slice instead.
Verified end to end: curl through the Vite proxy returns the golden
99/70/7. Vitest covers the table rendering; type-check, oxlint,
eslint clean. This completes the milestone 2 parity check.
2026-06-10 13:47:01 +02:00

63 lines
1.5 KiB
Vue

<script setup lang="ts">
import type { ComparisonResponse } from '@/types/api'
defineProps<{ comparison: ComparisonResponse }>()
const strategyLabels: Record<string, string> = {
none: 'No program',
random: 'Educate at random',
'most-connected': 'Educate the most connected',
}
</script>
<template>
<table>
<caption>
Same education budget ({{ comparison.config.numEducated }} of
{{ comparison.config.numStudents }} students), different targeting
</caption>
<thead>
<tr>
<th scope="col">Strategy</th>
<th scope="col">Educated</th>
<th scope="col">Reached</th>
<th scope="col">Share of school</th>
<th scope="col">Rounds</th>
</tr>
</thead>
<tbody>
<tr v-for="result in comparison.results" :key="result.strategy">
<th scope="row">{{ strategyLabels[result.strategy] ?? result.strategy }}</th>
<td>{{ result.educated.length }}</td>
<td>{{ result.numReached }} / {{ comparison.config.numStudents }}</td>
<td>{{ Math.round(result.reachedPct) }}%</td>
<td>{{ result.numRounds }}</td>
</tr>
</tbody>
</table>
</template>
<style scoped>
table {
border-collapse: collapse;
margin-top: 1.5rem;
}
caption {
caption-side: top;
text-align: left;
padding-bottom: 0.75rem;
color: var(--color-text);
}
th,
td {
border: 1px solid var(--color-border);
padding: 0.5rem 0.9rem;
text-align: left;
}
thead th {
background: var(--color-background-soft);
}
</style>