From f6f0f714a1326ff61054ff757cf0064f75ba908c Mon Sep 17 00:00:00 2001 From: Devanshu Rajesh Chicholikar Date: Fri, 5 Dec 2025 23:19:11 -0500 Subject: [PATCH] feat(frontend): Repo Detail Pages Dark Theme (#35) - RepoOverview: Dark stat cards, gradient Quick Guide section - SearchPanel: Dark input, dark result cards with syntax highlighting - DependencyGraph: Dark stat cards, dark controls, styled ReactFlow - StyleInsights: Dark cards for naming conventions, imports, patterns - ImpactAnalyzer: Dark form, risk badges, impact cards All components now use consistent #0a0a0c/#111113 backgrounds with white/10 borders matching the dashboard design system. --- frontend/src/components/DependencyGraph.tsx | 88 +++++++++---------- frontend/src/components/ImpactAnalyzer.tsx | 76 ++++++++--------- frontend/src/components/RepoOverview.tsx | 91 ++++++++++---------- frontend/src/components/SearchPanel.tsx | 50 +++++------ frontend/src/components/StyleInsights.tsx | 95 ++++++++++----------- 5 files changed, 202 insertions(+), 198 deletions(-) diff --git a/frontend/src/components/DependencyGraph.tsx b/frontend/src/components/DependencyGraph.tsx index 16304bf..68fcb78 100644 --- a/frontend/src/components/DependencyGraph.tsx +++ b/frontend/src/components/DependencyGraph.tsx @@ -100,7 +100,6 @@ export function DependencyGraph({ repoId, apiUrl, apiKey }: DependencyGraphProps const data = await response.json() - // Convert to React Flow format const flowNodes: Node[] = data.nodes.map((node: any) => { const fileName = node.label || node.id.split('/').pop() const fullPath = node.id @@ -145,10 +144,10 @@ export function DependencyGraph({ repoId, apiUrl, apiKey }: DependencyGraphProps source: edge.source, target: edge.target, animated: false, - style: { stroke: '#94a3b8', strokeWidth: 1.5 }, + style: { stroke: '#4b5563', strokeWidth: 1.5 }, markerEnd: { type: MarkerType.ArrowClosed, - color: '#94a3b8', + color: '#4b5563', }, })) @@ -172,7 +171,6 @@ export function DependencyGraph({ repoId, apiUrl, apiKey }: DependencyGraphProps const handleNodeClick = useCallback((event: any, node: Node) => { setHighlightedNode(node.id) - // Highlight connected nodes const connectedNodeIds = new Set() connectedNodeIds.add(node.id) @@ -232,67 +230,67 @@ export function DependencyGraph({ repoId, apiUrl, apiKey }: DependencyGraphProps if (loading) { return ( -
-
-

Building dependency graph...

+
+
+

Building dependency graph...

) } return ( -
- {/* Metrics + Controls */} +
+ {/* Metrics */}
-
-
Total Files
-
{allNodes.length}
+
+
Total Files
+
{allNodes.length}
-
-
Dependencies
-
{edges.length}
+
+
Dependencies
+
{edges.length}
-
-
Avg per File
-
+
+
Avg per File
+
{metrics?.avg_dependencies?.toFixed(1) || 0}
-
-
Showing
-
{nodes.length}
+
+
Showing
+
{nodes.length}
{/* Filter Controls */} -
+
- + setMinDeps(Number(e.target.value))} - className="w-32" + className="w-32 accent-blue-500" /> - {minDeps} + {minDeps}
{highlightedNode && ( @@ -302,15 +300,15 @@ export function DependencyGraph({ repoId, apiUrl, apiKey }: DependencyGraphProps {/* Most Critical Files */} {metrics?.most_critical_files && metrics.most_critical_files.length > 0 && ( -
-

Most Critical Files

+
+

Most Critical Files

{metrics.most_critical_files.slice(0, 5).map((item: any, idx: number) => (
- + {item.file.split('/').slice(-2).join('/')} - + {item.dependents} dependents
@@ -320,7 +318,7 @@ export function DependencyGraph({ repoId, apiUrl, apiKey }: DependencyGraphProps )} {/* Graph Visualization */} -
+
- - + + { const style = node.style as any return style?.background || '#6b7280' }} - maskColor="rgba(0, 0, 0, 0.1)" + maskColor="rgba(0, 0, 0, 0.5)" + className="!bg-[#111113] !border-white/10" />
-
-

Graph Legend

+ {/* Legend */} +
+

Graph Legend

- Python + Python
- TypeScript + TypeScript
- JavaScript + JavaScript
-
- Dependency +
+ Dependency
-
+
💡 Click any node to highlight its dependencies • Drag to pan • Scroll to zoom
diff --git a/frontend/src/components/ImpactAnalyzer.tsx b/frontend/src/components/ImpactAnalyzer.tsx index 376532f..fade8f6 100644 --- a/frontend/src/components/ImpactAnalyzer.tsx +++ b/frontend/src/components/ImpactAnalyzer.tsx @@ -60,21 +60,21 @@ export function ImpactAnalyzer({ repoId, apiUrl, apiKey }: ImpactAnalyzerProps) const getRiskColor = (risk: string) => { switch (risk) { - case 'high': return 'text-red-600 bg-red-50 border-red-200' - case 'medium': return 'text-amber-600 bg-amber-50 border-amber-200' - case 'low': return 'text-green-600 bg-green-50 border-green-200' - default: return 'text-gray-600 bg-gray-50 border-gray-200' + case 'high': return 'text-red-400 bg-red-500/10 border-red-500/20' + case 'medium': return 'text-yellow-400 bg-yellow-500/10 border-yellow-500/20' + case 'low': return 'text-green-400 bg-green-500/10 border-green-500/20' + default: return 'text-gray-400 bg-white/5 border-white/10' } } return ( -
+
{/* Input Form */} -
-

Analyze Change Impact

+
+

Analyze Change Impact

-
{/* Quick Guide */} -
-

💡 Quick Guide

-
    -
  • Search tab - Find code by meaning, not keywords
  • -
  • Dependencies tab - Visualize code architecture
  • -
  • Code Style tab - Analyze team coding patterns
  • -
  • Impact tab - See what breaks when you change a file
  • +
    +

    💡 Quick Guide

    +
      +
    • Search tab - Find code by meaning, not keywords
    • +
    • Dependencies tab - Visualize code architecture
    • +
    • Code Style tab - Analyze team coding patterns
    • +
    • Impact tab - See what breaks when you change a file
    • • Use with Claude Desktop via MCP for AI-powered code understanding
    diff --git a/frontend/src/components/SearchPanel.tsx b/frontend/src/components/SearchPanel.tsx index d06facd..826ccea 100644 --- a/frontend/src/components/SearchPanel.tsx +++ b/frontend/src/components/SearchPanel.tsx @@ -53,9 +53,9 @@ export function SearchPanel({ repoId, apiUrl, apiKey }: SearchPanelProps) { } return ( -
    +
    {/* Search */} -
    +
    setQuery(e.target.value)} placeholder="e.g., authentication middleware, React hooks, database queries..." - className="input flex-1" + className="flex-1 px-4 py-3 bg-white/5 border border-white/10 rounded-xl text-white placeholder:text-gray-500 focus:outline-none focus:border-blue-500/50 focus:ring-1 focus:ring-blue-500/20 transition-all" disabled={loading} autoFocus />
    -

    +

    Powered by semantic embeddings - finds code by meaning, not just keywords

    {searchTime !== null && ( -
    +
    - {results.length} results + {results.length} results - + - {searchTime}ms + {searchTime}ms {cached && ( <> - - + + ⚡ Cached @@ -104,15 +104,15 @@ export function SearchPanel({ repoId, apiUrl, apiKey }: SearchPanelProps) { {/* Results */}
    {results.map((result, idx) => ( -
    +
    {/* Header */}
    -

    +

    {result.name}

    - + {result.type.replace('_', ' ')}
    @@ -124,7 +124,7 @@ export function SearchPanel({ repoId, apiUrl, apiKey }: SearchPanelProps) {
    Match
    -
    +
    {(result.score * 100).toFixed(0)}%
    @@ -132,8 +132,9 @@ export function SearchPanel({ repoId, apiUrl, apiKey }: SearchPanelProps) { onClick={(e) => { e.stopPropagation() navigator.clipboard.writeText(result.code) + toast.success('Code copied!') }} - className="btn-ghost px-3 py-1.5 opacity-0 group-hover:opacity-100 transition-opacity" + className="px-3 py-1.5 text-sm text-gray-400 hover:text-white bg-white/5 hover:bg-white/10 rounded-lg opacity-0 group-hover:opacity-100 transition-all" title="Copy code" > Copy @@ -142,7 +143,7 @@ export function SearchPanel({ repoId, apiUrl, apiKey }: SearchPanelProps) {
    {/* Code with Syntax Highlighting */} -
    +
    - + {result.language}
    @@ -170,8 +172,8 @@ export function SearchPanel({ repoId, apiUrl, apiKey }: SearchPanelProps) { Lines {result.line_start}–{result.line_end} - - + + {result.file_path}
    @@ -181,12 +183,12 @@ export function SearchPanel({ repoId, apiUrl, apiKey }: SearchPanelProps) { {/* Empty State */} {results.length === 0 && query && !loading && ( -
    -
    +
    +
    🔍
    -

    No results found

    -

    +

    No results found

    +

    Try a different query or check if the repository is fully indexed

    diff --git a/frontend/src/components/StyleInsights.tsx b/frontend/src/components/StyleInsights.tsx index f3a458b..514e12e 100644 --- a/frontend/src/components/StyleInsights.tsx +++ b/frontend/src/components/StyleInsights.tsx @@ -21,7 +21,6 @@ export function StyleInsights({ repoId, apiUrl, apiKey }: StyleInsightsProps) { headers: { 'Authorization': `Bearer ${apiKey}` } }) const result = await response.json() - console.log('Style data:', result) setData(result) } catch (error) { console.error('Error loading style data:', error) @@ -32,9 +31,9 @@ export function StyleInsights({ repoId, apiUrl, apiKey }: StyleInsightsProps) { if (loading) { return ( -
    -
    -

    Analyzing code style patterns...

    +
    +
    +

    Analyzing code style patterns...

    ) } @@ -42,33 +41,33 @@ export function StyleInsights({ repoId, apiUrl, apiKey }: StyleInsightsProps) { if (!data) return null return ( -
    +
    {/* Summary Cards */}
    -
    -
    Files Analyzed
    -
    +
    +
    Files Analyzed
    +
    {data.summary?.total_files_analyzed || 0}
    -
    -
    Functions
    -
    +
    +
    Functions
    +
    {data.summary?.total_functions || 0}
    -
    -
    Async Adoption
    -
    +
    +
    Async Adoption
    +
    {data.summary?.async_adoption || '0%'}
    -
    -
    Type Hints
    -
    +
    +
    Type Hints
    +
    {data.summary?.type_hints_usage || '0%'}
    @@ -76,20 +75,20 @@ export function StyleInsights({ repoId, apiUrl, apiKey }: StyleInsightsProps) { {/* Naming Conventions */}
    -
    -

    Function Naming

    +
    +

    Function Naming

    {data.naming_conventions?.functions && Object.entries(data.naming_conventions.functions).map(([convention, info]: any) => (
    - + {convention}
    - {info.count} - + {info.count} + {info.percentage}
    @@ -99,20 +98,20 @@ export function StyleInsights({ repoId, apiUrl, apiKey }: StyleInsightsProps) {
    -
    -

    Class Naming

    +
    +

    Class Naming

    {data.naming_conventions?.classes && Object.entries(data.naming_conventions.classes).map(([convention, info]: any) => (
    - + {convention}
    - {info.count} - + {info.count} + {info.percentage}
    @@ -124,39 +123,39 @@ export function StyleInsights({ repoId, apiUrl, apiKey }: StyleInsightsProps) {
    {/* Top Imports */} -
    -

    Most Common Imports

    +
    +

    Most Common Imports

    {data.top_imports?.slice(0, 10).map((item: any, idx: number) => (
    - + {item.module} - {item.count}× + {item.count}×
    ))}
    {/* Patterns */} -
    -

    Code Patterns

    +
    +

    Code Patterns

    -
    - Async/Await Usage +
    + Async/Await Usage
    - {data.patterns?.async_usage} - + {data.patterns?.async_usage} + {data.patterns?.async_percentage?.toFixed(0)}%
    -
    - Type Annotations +
    + Type Annotations
    - {data.patterns?.type_annotations} - + {data.patterns?.type_annotations} + {data.patterns?.typed_percentage?.toFixed(0)}%
    @@ -165,20 +164,20 @@ export function StyleInsights({ repoId, apiUrl, apiKey }: StyleInsightsProps) {
    {/* Language Distribution */} -
    -

    Language Distribution

    -
    +
    +

    Language Distribution

    +
    {data.language_distribution && Object.entries(data.language_distribution).map(([lang, info]: any) => (
    - {lang} -
    + {lang} +
    - {info.percentage} + {info.percentage}
    )) }