feat: restructure router into /data/* /eval/* /train/* domains with backward-compat redirects

- Export named `routes` array from router/index.ts for testability
- Move label/fetch/corrections/imitate under /data/* namespace
- Move benchmark/compare under /eval/* namespace
- Add /train/jobs and /train/results under /train/* namespace
- Add / -> DashboardView and /fleet -> ModelsView (replaces old / -> LabelView)
- Add backward-compat redirects for all old flat paths (/benchmark, /models, /stats, /label, /fetch, /corrections, /imitate)
- Add stub views for DashboardView, CompareView, TrainJobsView, TrainResultsView (implemented in later tasks)
- Add router.test.ts: 16 tests covering route structure and redirect targets
This commit is contained in:
pyr0ball 2026-05-02 13:00:04 -07:00
parent 0904967320
commit 5bdb095235
6 changed files with 177 additions and 19 deletions

View file

@ -1,25 +1,49 @@
import { createRouter, createWebHashHistory } from 'vue-router'
import LabelView from '../views/LabelView.vue'
// Views are lazy-loaded to keep initial bundle small
const FetchView = () => import('../views/FetchView.vue')
const StatsView = () => import('../views/StatsView.vue')
const BenchmarkView = () => import('../views/BenchmarkView.vue')
const SettingsView = () => import('../views/SettingsView.vue')
const CorrectionsView = () => import('../views/CorrectionsView.vue')
const ModelsView = () => import('../views/ModelsView.vue')
const ImitateView = () => import('../views/ImitateView.vue')
// Lazy-loaded views
const DashboardView = () => import('../views/DashboardView.vue')
const LabelView = () => import('../views/LabelView.vue')
const FetchView = () => import('../views/FetchView.vue')
const CorrectionsView = () => import('../views/CorrectionsView.vue')
const ImitateView = () => import('../views/ImitateView.vue')
const BenchmarkView = () => import('../views/BenchmarkView.vue')
const CompareView = () => import('../views/CompareView.vue')
const TrainJobsView = () => import('../views/TrainJobsView.vue')
const TrainResultsView = () => import('../views/TrainResultsView.vue')
const ModelsView = () => import('../views/ModelsView.vue')
const SettingsView = () => import('../views/SettingsView.vue')
export const routes = [
// ── Top-level ────────────────────────────────────────────
{ path: '/', component: DashboardView, meta: { title: 'Dashboard' } },
{ path: '/fleet', component: ModelsView, meta: { title: 'Fleet' } },
{ path: '/settings', component: SettingsView, meta: { title: 'Settings' } },
// ── Data domain ──────────────────────────────────────────
{ path: '/data/label', component: LabelView, meta: { title: 'Label' } },
{ path: '/data/fetch', component: FetchView, meta: { title: 'Fetch' } },
{ path: '/data/corrections', component: CorrectionsView, meta: { title: 'Corrections' } },
{ path: '/data/imitate', component: ImitateView, meta: { title: 'Imitate' } },
// ── Eval domain ──────────────────────────────────────────
{ path: '/eval/benchmark', component: BenchmarkView, meta: { title: 'Benchmark' } },
{ path: '/eval/compare', component: CompareView, meta: { title: 'Compare' } },
// ── Train domain ─────────────────────────────────────────
{ path: '/train/jobs', component: TrainJobsView, meta: { title: 'Training Jobs' } },
{ path: '/train/results', component: TrainResultsView, meta: { title: 'Training Results' } },
// ── Backward-compat redirects ────────────────────────────
{ path: '/benchmark', redirect: '/eval/benchmark' },
{ path: '/models', redirect: '/fleet' },
{ path: '/stats', redirect: '/' },
{ path: '/label', redirect: '/data/label' },
{ path: '/fetch', redirect: '/data/fetch' },
{ path: '/corrections', redirect: '/data/corrections' },
{ path: '/imitate', redirect: '/data/imitate' },
]
export const router = createRouter({
history: createWebHashHistory(),
routes: [
{ path: '/', component: LabelView, meta: { title: 'Label' } },
{ path: '/fetch', component: FetchView, meta: { title: 'Fetch' } },
{ path: '/stats', component: StatsView, meta: { title: 'Stats' } },
{ path: '/benchmark', component: BenchmarkView, meta: { title: 'Benchmark' } },
{ path: '/models', component: ModelsView, meta: { title: 'Models' } },
{ path: '/imitate', component: ImitateView, meta: { title: 'Imitate' } },
{ path: '/corrections', component: CorrectionsView, meta: { title: 'Corrections' } },
{ path: '/settings', component: SettingsView, meta: { title: 'Settings' } },
],
routes,
})

View file

@ -0,0 +1,94 @@
import { describe, it, expect } from 'vitest'
import { createRouter, createWebHashHistory } from 'vue-router'
// Import the raw routes array so we can test structure without mounting App
import { routes } from './index'
describe('router routes', () => {
it('exports a routes array', () => {
expect(Array.isArray(routes)).toBe(true)
})
it('has / pointing to DashboardView', () => {
const root = routes.find(r => r.path === '/')
expect(root).toBeDefined()
// Component should be async (lazy) or have a name
expect(root?.component).toBeDefined()
})
it('has /fleet route', () => {
const r = routes.find(r => r.path === '/fleet')
expect(r).toBeDefined()
})
it('has /data/label route', () => {
const r = routes.find(r => r.path === '/data/label')
expect(r).toBeDefined()
})
it('has /data/fetch route', () => {
const r = routes.find(r => r.path === '/data/fetch')
expect(r).toBeDefined()
})
it('has /data/corrections route', () => {
const r = routes.find(r => r.path === '/data/corrections')
expect(r).toBeDefined()
})
it('has /data/imitate route', () => {
const r = routes.find(r => r.path === '/data/imitate')
expect(r).toBeDefined()
})
it('has /eval/benchmark route', () => {
const r = routes.find(r => r.path === '/eval/benchmark')
expect(r).toBeDefined()
})
it('has /eval/compare route', () => {
const r = routes.find(r => r.path === '/eval/compare')
expect(r).toBeDefined()
})
it('has /train/jobs route', () => {
const r = routes.find(r => r.path === '/train/jobs')
expect(r).toBeDefined()
})
it('has /train/results route', () => {
const r = routes.find(r => r.path === '/train/results')
expect(r).toBeDefined()
})
it('has /settings route', () => {
const r = routes.find(r => r.path === '/settings')
expect(r).toBeDefined()
})
it('has backward-compat redirect from /benchmark to /eval/benchmark', () => {
const r = routes.find(r => r.path === '/benchmark')
expect(r).toBeDefined()
expect((r as { redirect?: string }).redirect).toBe('/eval/benchmark')
})
it('has backward-compat redirect from /models to /fleet', () => {
const r = routes.find(r => r.path === '/models')
expect(r).toBeDefined()
expect((r as { redirect?: string }).redirect).toBe('/fleet')
})
it('has backward-compat redirect from /stats to /', () => {
const r = routes.find(r => r.path === '/stats')
expect(r).toBeDefined()
expect((r as { redirect?: string }).redirect).toBe('/')
})
it('can create a functional router instance', () => {
const router = createRouter({
history: createWebHashHistory(),
routes,
})
expect(router).toBeDefined()
})
})

View file

@ -0,0 +1,10 @@
<template>
<div class="view-placeholder">
<h2>CompareView</h2>
<p>Coming soon.</p>
</div>
</template>
<script setup lang="ts">
// Stub will be implemented in a later task
</script>

View file

@ -0,0 +1,10 @@
<template>
<div class="view-placeholder">
<h2>DashboardView</h2>
<p>Coming soon.</p>
</div>
</template>
<script setup lang="ts">
// Stub will be implemented in a later task
</script>

View file

@ -0,0 +1,10 @@
<template>
<div class="view-placeholder">
<h2>TrainJobsView</h2>
<p>Coming soon.</p>
</div>
</template>
<script setup lang="ts">
// Stub will be implemented in a later task
</script>

View file

@ -0,0 +1,10 @@
<template>
<div class="view-placeholder">
<h2>TrainResultsView</h2>
<p>Coming soon.</p>
</div>
</template>
<script setup lang="ts">
// Stub will be implemented in a later task
</script>