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:
parent
0904967320
commit
5bdb095235
6 changed files with 177 additions and 19 deletions
|
|
@ -1,25 +1,49 @@
|
||||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||||
import LabelView from '../views/LabelView.vue'
|
|
||||||
|
|
||||||
// Views are lazy-loaded to keep initial bundle small
|
// Lazy-loaded views
|
||||||
const FetchView = () => import('../views/FetchView.vue')
|
const DashboardView = () => import('../views/DashboardView.vue')
|
||||||
const StatsView = () => import('../views/StatsView.vue')
|
const LabelView = () => import('../views/LabelView.vue')
|
||||||
const BenchmarkView = () => import('../views/BenchmarkView.vue')
|
const FetchView = () => import('../views/FetchView.vue')
|
||||||
const SettingsView = () => import('../views/SettingsView.vue')
|
const CorrectionsView = () => import('../views/CorrectionsView.vue')
|
||||||
const CorrectionsView = () => import('../views/CorrectionsView.vue')
|
const ImitateView = () => import('../views/ImitateView.vue')
|
||||||
const ModelsView = () => import('../views/ModelsView.vue')
|
const BenchmarkView = () => import('../views/BenchmarkView.vue')
|
||||||
const ImitateView = () => import('../views/ImitateView.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({
|
export const router = createRouter({
|
||||||
history: createWebHashHistory(),
|
history: createWebHashHistory(),
|
||||||
routes: [
|
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' } },
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
|
|
|
||||||
94
web/src/router/router.test.ts
Normal file
94
web/src/router/router.test.ts
Normal 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()
|
||||||
|
})
|
||||||
|
})
|
||||||
10
web/src/views/CompareView.vue
Normal file
10
web/src/views/CompareView.vue
Normal 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>
|
||||||
10
web/src/views/DashboardView.vue
Normal file
10
web/src/views/DashboardView.vue
Normal 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>
|
||||||
10
web/src/views/TrainJobsView.vue
Normal file
10
web/src/views/TrainJobsView.vue
Normal 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>
|
||||||
10
web/src/views/TrainResultsView.vue
Normal file
10
web/src/views/TrainResultsView.vue
Normal 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>
|
||||||
Loading…
Reference in a new issue