Files
FabledCurator/frontend/src/router.js
T
bvandeusen 9e262cc5f0
CI / lint (push) Successful in 3s
CI / backend-lint-and-test (push) Successful in 25s
CI / frontend-build (push) Successful in 25s
CI / integration (push) Successful in 3m4s
feat(series): Add-to-series control + Series browse view + nav (FC-6.2)
Completes FC-6.2 with the UI.

- PostSeriesMenu: a "Series ▾" control on each post card — "New series from
  this post" (promote → navigates to manage) and "Add to existing series…"
  (dialog with a browsable picker loaded from GET /api/series, client-side
  filtered — avoids the empty-autocomplete #712 trap).
- SeriesView (/series): a top-level Series browse grid — cover, name, artist,
  chapter/page counts, gap badge; sort recent|name|size; cards → manage/read.
  meta.title adds it to the nav automatically (peer of Posts).
- seriesBrowse store for the list.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 18:38:37 -04:00

76 lines
3.7 KiB
JavaScript

import { createRouter, createWebHistory, createMemoryHistory } from 'vue-router'
import SettingsView from './views/SettingsView.vue'
import GalleryView from './views/GalleryView.vue'
import ShowcaseView from './views/ShowcaseView.vue'
import TagsView from './views/TagsView.vue'
import ArtistView from './views/ArtistView.vue'
import SeriesView from './views/SeriesView.vue'
import SeriesManageView from './views/SeriesManageView.vue'
import SeriesReaderView from './views/SeriesReaderView.vue'
import SubscriptionsView from './views/SubscriptionsView.vue'
import PostsView from './views/PostsView.vue'
import ArtistsView from './views/ArtistsView.vue'
// The application's front door. `/` redirects here. Changing the front door
// is a one-line edit (e.g. '/gallery' or '/tags').
export const FRONT_DOOR = '/showcase'
const routes = [
// Root is a redirect only — no meta.title so it stays out of the nav.
{ path: '/', redirect: FRONT_DOOR },
// FC-2: image backbone
{ path: '/showcase', name: 'showcase', component: ShowcaseView, meta: { title: 'Showcase' } },
{ path: '/gallery', name: 'gallery', component: GalleryView, meta: { title: 'Gallery' } },
{ path: '/artists', name: 'artists', component: ArtistsView, meta: { title: 'Artists' } },
{ path: '/tags', name: 'tags', component: TagsView, meta: { title: 'Tags' } },
// Artist detail — no meta.title (reached by clicking an artist, not nav).
{ path: '/artist/:slug', name: 'artist', component: ArtistView },
// Series browse — a nav entry (meta.title). Peer of Posts.
{ path: '/series', name: 'series', component: SeriesView, meta: { title: 'Series' } },
// Series management — no meta.title (reached from a series card/tag).
{ path: '/series/:tagId', name: 'series-manage', component: SeriesManageView },
// Series reader — immersive (no top nav, no meta.title).
{ path: '/series/:tagId/read', name: 'series-read', component: SeriesReaderView, meta: { immersive: true } },
{ path: '/settings', name: 'settings', component: SettingsView, meta: { title: 'Settings' } },
// FC-3: subscription backbone
// /credentials and /downloads were folded into /subscriptions as subtabs
// 2026-05-27 (?tab=settings and ?tab=downloads). The hub view owns the
// whole download pipeline domain.
{ path: '/posts', name: 'posts', component: PostsView, meta: { title: 'Posts' } },
{ path: '/subscriptions', name: 'subscriptions', component: SubscriptionsView, meta: { title: 'Subscriptions' } },
// Bookmark/back-button safety net for the routes that got folded in
// (no meta.title — stay out of TopNav).
{ path: '/credentials', redirect: '/subscriptions?tab=settings' },
{ path: '/downloads', redirect: '/subscriptions?tab=downloads' }
]
// Browser uses HTML5 history; non-browser (Vitest/SSR) falls back to memory
// history so the module is importable without `window`.
const history =
typeof window !== 'undefined' ? createWebHistory() : createMemoryHistory()
const router = createRouter({
history,
routes
})
const DEFAULT_TITLE = 'FabledCurator'
// Keep the tab title in sync with the route on EVERY navigation. List routes set
// it from meta.title; detail routes (artist, series) have no meta.title, so they
// reset to the default here and then overwrite it with their own dynamic title
// (e.g. ArtistView sets "<artist> — FabledCurator"). Without this reset the
// previous view's dynamic title stuck around — an artist name showing on the
// Showcase tab, operator-flagged 2026-06-07.
router.afterEach((to) => {
if (typeof document === 'undefined') return
document.title = to.meta?.title
? `${to.meta.title}${DEFAULT_TITLE}`
: DEFAULT_TITLE
})
export default router