// ======================================== // ProjectOverviewPage Component // ======================================== // Project overview page displaying architecture, tech stack, and components import * as React from 'react'; import { useIntl } from 'react-intl'; import { Code2, Blocks, Component, GitBranch, BarChart3, ScrollText, ClipboardList, Sparkles, Zap, Bug, Wrench, BookOpen, CheckSquare, Lightbulb, BookMarked, ShieldAlert, LayoutGrid, GitCommitHorizontal, } from 'lucide-react'; import { useProjectOverview } from '@/hooks/useProjectOverview'; import type { KeyComponent, DevelopmentIndexEntry, GuidelineEntry, LearningEntry, } from '@/lib/api'; import { Button } from '@/components/ui/Button'; import { Badge } from '@/components/ui/Badge'; import { Card, CardContent } from '@/components/ui/Card'; import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/Tabs'; type DevIndexView = 'category' | 'timeline'; // Helper function to format date (currently unused but kept for future use) // @ts-ignore - kept for potential future use function formatDate(dateString: string | undefined): string { if (!dateString) return '-'; try { const date = new Date(dateString); return date.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric', }); } catch { return '-'; } } /** * ProjectOverviewPage component - Display project architecture and tech stack */ export function ProjectOverviewPage() { const { formatMessage } = useIntl(); const { projectOverview, isLoading, error, refetch } = useProjectOverview(); const [devIndexView, setDevIndexView] = React.useState('category'); // Helper function to format date function formatDate(dateString: string | undefined): string { if (!dateString) return '-'; try { const date = new Date(dateString); return date.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric', }); } catch { return '-'; } } // Define dev index categories (before any conditional logic) const devIndexCategories = [ { key: 'feature', i18nKey: 'projectOverview.devIndex.category.features', icon: Sparkles, color: 'primary' }, { key: 'enhancement', i18nKey: 'projectOverview.devIndex.category.enhancements', icon: Zap, color: 'success' }, { key: 'bugfix', i18nKey: 'projectOverview.devIndex.category.bugfixes', icon: Bug, color: 'destructive' }, { key: 'refactor', i18nKey: 'projectOverview.devIndex.category.refactorings', icon: Wrench, color: 'warning' }, { key: 'docs', i18nKey: 'projectOverview.devIndex.category.documentation', icon: BookOpen, color: 'muted' }, ]; // Collect all entries for timeline (compute before any conditional logic) const allDevEntries = React.useMemo(() => { if (!projectOverview?.developmentIndex) return []; const entries: Array<{ title: string; description?: string; type: string; typeLabel: string; typeIcon: React.ElementType; typeColor: string; date: string; sessionId?: string; sub_feature?: string; tags?: string[]; }> = []; devIndexCategories.forEach((cat) => { (projectOverview.developmentIndex?.[cat.key] || []).forEach((entry: DevelopmentIndexEntry) => { entries.push({ ...entry, type: cat.key, typeLabel: formatMessage({ id: cat.i18nKey }), typeIcon: cat.icon, typeColor: cat.color, date: entry.archivedAt || entry.date || entry.implemented_at || '', }); }); }); return entries.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); }, [projectOverview?.developmentIndex]); // Calculate totals for development index const devIndexTotals = devIndexCategories.reduce((acc, cat) => { acc[cat.key] = (projectOverview?.developmentIndex?.[cat.key] || []).length; return acc; }, {} as Record); const totalEntries = Object.values(devIndexTotals).reduce((sum, count) => sum + count, 0); // Calculate statistics const totalFeatures = devIndexCategories.reduce((sum, cat) => sum + devIndexTotals[cat.key], 0); // Render loading state if (isLoading) { return (
{Array.from({ length: 4 }).map((_, i) => (
))}
); } // Render error state if (error) { return (

{formatMessage({ id: 'common.errors.loadFailed' })}

{error.message}

); } // Render empty state if (!projectOverview) { return (

{formatMessage({ id: 'projectOverview.empty.title' })}

{formatMessage({ id: 'projectOverview.empty.message' })}

); } const { technologyStack, architecture, keyComponents, developmentIndex, guidelines, metadata } = projectOverview; return (
{/* Project Header */}

{projectOverview.projectName}

{projectOverview.description || formatMessage({ id: 'projectOverview.noDescription' })}

{formatMessage({ id: 'projectOverview.header.initialized' })}:{' '} {formatDate(projectOverview.initializedAt)}
{metadata?.analysis_mode && (
{metadata.analysis_mode}
)}
{/* Technology Stack */}

{formatMessage({ id: 'projectOverview.techStack.title' })}

{/* Languages */}

{formatMessage({ id: 'projectOverview.techStack.languages' })}

{technologyStack?.languages && technologyStack.languages.length > 0 ? ( technologyStack.languages.map((lang: { name: string; file_count: number; primary?: boolean }) => (
{lang.name} {lang.file_count} files {lang.primary && ( {formatMessage({ id: 'projectOverview.techStack.primary' })} )}
)) ) : ( {formatMessage({ id: 'projectOverview.techStack.noLanguages' })} )}
{/* Frameworks */}

{formatMessage({ id: 'projectOverview.techStack.frameworks' })}

{technologyStack?.frameworks && technologyStack.frameworks.length > 0 ? ( technologyStack.frameworks.map((fw: string) => ( {fw} )) ) : ( {formatMessage({ id: 'projectOverview.techStack.noFrameworks' })} )}
{/* Build Tools */} {technologyStack?.build_tools && technologyStack.build_tools.length > 0 && (

{formatMessage({ id: 'projectOverview.techStack.buildTools' })}

{technologyStack.build_tools.map((tool: string) => ( {tool} ))}
)} {/* Test Frameworks */} {technologyStack?.test_frameworks && technologyStack.test_frameworks.length > 0 && (

{formatMessage({ id: 'projectOverview.techStack.testFrameworks' })}

{technologyStack.test_frameworks.map((fw: string) => ( {fw} ))}
)}
{/* Architecture */} {architecture && (

{formatMessage({ id: 'projectOverview.architecture.title' })}

{/* Style */}

{formatMessage({ id: 'projectOverview.architecture.style' })}

{architecture.style}
{/* Layers */} {architecture.layers && architecture.layers.length > 0 && (

{formatMessage({ id: 'projectOverview.architecture.layers' })}

{architecture.layers.map((layer: string) => ( {layer} ))}
)} {/* Patterns */} {architecture.patterns && architecture.patterns.length > 0 && (

{formatMessage({ id: 'projectOverview.architecture.patterns' })}

{architecture.patterns.map((pattern: string) => ( {pattern} ))}
)}
)} {/* Key Components */} {keyComponents && keyComponents.length > 0 && (

{formatMessage({ id: 'projectOverview.components.title' })}

{keyComponents.map((comp: KeyComponent) => { const importance = comp.importance || 'low'; const importanceColors: Record = { high: 'border-l-4 border-l-destructive bg-destructive/5', medium: 'border-l-4 border-l-warning bg-warning/5', low: 'border-l-4 border-l-muted-foreground bg-muted', }; const importanceBadges: Record = { high: ( {formatMessage({ id: 'projectOverview.components.importance.high' })} ), medium: ( {formatMessage({ id: 'projectOverview.components.importance.medium' })} ), low: ( {formatMessage({ id: 'projectOverview.components.importance.low' })} ), }; return (

{comp.name}

{importanceBadges[importance]}
{comp.description && (

{comp.description}

)} {comp.responsibility && comp.responsibility.length > 0 && (
    {comp.responsibility.map((resp: string, i: number) => (
  • {resp}
  • ))}
)}
); })}
)} {/* Development Index */} {developmentIndex && totalEntries > 0 && (

{formatMessage({ id: 'projectOverview.devIndex.title' })}

{devIndexCategories.map((cat) => { const count = devIndexTotals[cat.key]; if (count === 0) return null; const Icon = cat.icon; return ( {count} ); })}
setDevIndexView(v as DevIndexView)}>
{formatMessage({ id: 'projectOverview.devIndex.categories' })} {formatMessage({ id: 'projectOverview.devIndex.timeline' })}
{devIndexCategories.map((cat) => { const entries = developmentIndex?.[cat.key] || []; if (entries.length === 0) return null; const Icon = cat.icon; return (

{formatMessage({ id: cat.i18nKey })} {entries.length}

{entries.slice(0, 5).map((entry: DevelopmentIndexEntry & { type?: string; typeLabel?: string; typeIcon?: React.ElementType; typeColor?: string; date?: string }, i: number) => (
{entry.title}
{formatDate(entry.archivedAt || entry.date || entry.implemented_at)}
{entry.description && (

{entry.description}

)}
{entry.sessionId && ( {entry.sessionId} )} {entry.sub_feature && ( {entry.sub_feature} )} {entry.status && ( {entry.status} )}
))} {entries.length > 5 && (
... and {entries.length - 5} more
)}
); })}
{allDevEntries.slice(0, 20).map((entry, i) => { const Icon = entry.typeIcon; return (
{i < Math.min(allDevEntries.length, 20) - 1 && (
)}
{entry.typeLabel}
{entry.title}
{formatDate(entry.date)}
{entry.description && (

{entry.description}

)}
{entry.sessionId && ( {entry.sessionId} )} {entry.sub_feature && ( {entry.sub_feature} )} {entry.tags && entry.tags.slice(0, 3).map((tag) => ( {tag} ))}
); })} {allDevEntries.length > 20 && (
... and {allDevEntries.length - 20} more entries
)}
)} {/* Guidelines */} {guidelines && (

{formatMessage({ id: 'projectOverview.guidelines.title' })}

{/* Conventions */} {guidelines.conventions && (

{formatMessage({ id: 'projectOverview.guidelines.conventions' })}

{Object.entries(guidelines.conventions).slice(0, 4).map(([key, items]) => { const itemList = Array.isArray(items) ? items : []; if (itemList.length === 0) return null; return (
{itemList.slice(0, 3).map((item: string, i: number) => (
{key} {item as string}
))}
); })}
)} {/* Constraints */} {guidelines.constraints && (

{formatMessage({ id: 'projectOverview.guidelines.constraints' })}

{Object.entries(guidelines.constraints).slice(0, 4).map(([key, items]) => { const itemList = Array.isArray(items) ? items : []; if (itemList.length === 0) return null; return (
{itemList.slice(0, 3).map((item: string, i: number) => (
{key} {item as string}
))}
); })}
)} {/* Quality Rules */} {guidelines.quality_rules && guidelines.quality_rules.length > 0 && (

{formatMessage({ id: 'projectOverview.guidelines.qualityRules' })}

{guidelines.quality_rules.slice(0, 5).map((rule: GuidelineEntry, i: number) => (
{rule.rule} {rule.enforced_by && ( {rule.enforced_by} )}
{formatMessage({ id: 'projectOverview.guidelines.scope' })}: {rule.scope}
))}
)} {/* Learnings */} {guidelines.learnings && guidelines.learnings.length > 0 && (

{formatMessage({ id: 'projectOverview.guidelines.learnings' })}

{guidelines.learnings.slice(0, 5).map((learning: LearningEntry, i: number) => (
{learning.insight} {formatDate(learning.date)}
{learning.category && ( {learning.category} )} {learning.session_id && ( {learning.session_id} )}
{learning.context && (

{learning.context}

)}
))}
)}
)} {/* Statistics */}

{formatMessage({ id: 'projectOverview.stats.title' })}

{totalFeatures}
{formatMessage({ id: 'projectOverview.stats.totalFeatures' })}
{formatMessage({ id: 'projectOverview.stats.lastUpdated' })}
{allDevEntries.length > 0 ? formatDate(allDevEntries[0].date) : '-'}
); } export default ProjectOverviewPage;