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.
This commit is contained in:
parent
e9f36f509e
commit
c935071bd1
17 changed files with 198 additions and 319 deletions
|
|
@ -1,47 +1,40 @@
|
|||
<script setup lang="ts">
|
||||
import HelloWorld from './components/HelloWorld.vue'
|
||||
import TheWelcome from './components/TheWelcome.vue'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { fetchDefaultConfig, runComparison } from '@/lib/api'
|
||||
import type { ComparisonResponse } from '@/types/api'
|
||||
import ComparisonTable from '@/components/ComparisonTable.vue'
|
||||
|
||||
const comparison = ref<ComparisonResponse | null>(null)
|
||||
const error = ref<string | null>(null)
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const config = await fetchDefaultConfig()
|
||||
comparison.value = await runComparison(config)
|
||||
} catch (cause) {
|
||||
error.value = cause instanceof Error ? cause.message : String(cause)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header>
|
||||
<img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />
|
||||
|
||||
<div class="wrapper">
|
||||
<HelloWorld msg="You did it!" />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<TheWelcome />
|
||||
<h1>spreadlab</h1>
|
||||
<p>
|
||||
How a non-consensual deepfake spreads through a simulated school year
|
||||
group, and what an education program changes. Illustrative, not
|
||||
validated.
|
||||
</p>
|
||||
<p v-if="error" role="alert">Could not reach the spreadlab API: {{ error }}</p>
|
||||
<ComparisonTable v-else-if="comparison" :comparison="comparison" />
|
||||
<p v-else>Running scenarios…</p>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
header {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: block;
|
||||
margin: 0 auto 2rem;
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
header {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
padding-right: calc(var(--section-gap) / 2);
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin: 0 2rem 0 0;
|
||||
}
|
||||
|
||||
header .wrapper {
|
||||
display: flex;
|
||||
place-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
main {
|
||||
max-width: 48rem;
|
||||
margin: 0 auto;
|
||||
padding: 2rem 1rem;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue