import type { RouteLocationNormalized } from 'vue-router' import { useProjectsStore } from '@/stores/projects' import { episodeService } from '@/services/episode' import { shotService } from '@/services/shot' export interface BreadcrumbItem { label: string href?: string isActive?: boolean /** True for the crumb representing the current project tab (Overview/Shots/Assets/...), so the header can render a tab-switcher dropdown on it. */ isTabCrumb?: boolean /** True for the crumb representing the current project name, so the header can render a project-switcher dropdown on it. */ isProjectCrumb?: boolean } export class BreadcrumbService { static async generateBreadcrumbs(route: RouteLocationNormalized): Promise { const pathSegments = route.path.split('/').filter(Boolean) const crumbs: BreadcrumbItem[] = [{ label: 'Home', href: '/' }] // Handle different route patterns if (pathSegments[0] === 'projects' && pathSegments[1]) { const projectId = parseInt(pathSegments[1]) if (!isNaN(projectId)) { // Get project information - initialize store here when needed const projectsStore = useProjectsStore() let project = projectsStore.getProjectById(projectId) if (!project) { try { project = await projectsStore.getProject(projectId) } catch (error) { console.error('Failed to load project for breadcrumbs:', error) } } // Add project breadcrumb crumbs.push({ label: project ? project.name : `Project ${projectId}`, href: `/projects/${projectId}`, isProjectCrumb: true }) // Handle tab-based navigation const tab = route.meta?.tab as string const tabLabel = route.meta?.tabLabel as string || this.getTabLabel(tab) if (tab) { // For shots tab with episode context if (tab === 'shots' && route.params.episodeId) { crumbs.push({ label: tabLabel, href: `/projects/${projectId}/shots`, isTabCrumb: true }) // Add episode context const episodeId = parseInt(route.params.episodeId as string) if (!isNaN(episodeId)) { try { const episodes = await episodeService.getProjectEpisodes(projectId) const episode = episodes.find(ep => ep.id === episodeId) crumbs.push({ label: episode ? episode.name : `Episode ${episodeId}`, isActive: true }) } catch (error) { console.error('Failed to load episode for breadcrumbs:', error) crumbs.push({ label: `Episode ${episodeId}`, isActive: true }) } } } else { // Regular tab navigation (Overview included, so the trail always reads Home > Project > Tab) crumbs.push({ label: tabLabel, isActive: true, isTabCrumb: true }) } } // Handle shot detail page (path: /projects/:projectId/shots/:shotId) if (pathSegments[2] === 'shots' && pathSegments[3]) { const shotId = parseInt(pathSegments[3]) if (!isNaN(shotId)) { try { const shot = await shotService.getShot(shotId) // Replace the last item (Shots) with Shots > ShotName if (crumbs.length > 1) { crumbs[crumbs.length - 1] = { label: 'Shots', href: `/projects/${projectId}/shots`, isTabCrumb: true } } crumbs.push({ label: shot.name, isActive: true }) } catch (error) { console.error('Failed to load shot for breadcrumbs:', error) // Keep the default behavior - show shot ID } } } } } else { // Handle other routes let currentPath = '' pathSegments.forEach((segment, index) => { currentPath += `/${segment}` const isLast = index === pathSegments.length - 1 // Format segment label const label = this.formatSegmentLabel(segment) crumbs.push({ label, href: isLast ? undefined : currentPath, isActive: isLast }) }) } return crumbs } private static getTabLabel(tab: string): string { switch (tab) { case 'overview': return 'Overview' case 'shots': return 'Shots' case 'assets': return 'Assets' case 'technical-specs': return 'Technical Specs' default: return tab.charAt(0).toUpperCase() + tab.slice(1).replace(/-/g, ' ') } } private static formatSegmentLabel(segment: string): string { // Handle special cases if (segment === 'api-keys') return 'API Keys' if (segment === 'technical-specs') return 'Technical Specs' // Default formatting return segment.charAt(0).toUpperCase() + segment.slice(1).replace(/-/g, ' ') } }