-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathApp.tsx
More file actions
134 lines (117 loc) · 5.29 KB
/
App.tsx
File metadata and controls
134 lines (117 loc) · 5.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import React, { useState } from 'react';
import RepoInput from './components/RepoInput';
import Dashboard from './components/Dashboard';
import CodeViewer from './components/CodeViewer';
import { RepoInfo, FileAnalysis, AppState } from './types';
import { fetchRepoTree, fetchFileContent } from './services/githubService';
import { analyzeFile } from './services/complexityService';
import { Loader2 } from 'lucide-react';
const App: React.FC = () => {
const [appState, setAppState] = useState<AppState>(AppState.IDLE);
const [repoInfo, setRepoInfo] = useState<RepoInfo | null>(null);
const [files, setFiles] = useState<FileAnalysis[]>([]);
const [selectedFile, setSelectedFile] = useState<FileAnalysis | null>(null);
const [loadingStatus, setLoadingStatus] = useState('');
const [error, setError] = useState<string | null>(null);
const handleAnalyze = async (repo: RepoInfo, token?: string) => {
try {
setError(null);
setAppState(AppState.FETCHING_TREE);
setRepoInfo(repo);
setLoadingStatus('Connecting to GitHub...');
const tree = await fetchRepoTree(repo, token);
if (tree.length === 0) {
throw new Error('No supported source files (JS/TS) found in this repository.');
}
setAppState(AppState.FETCHING_FILES);
setLoadingStatus(`Found ${tree.length} potential files. Analyzing top 15 largest modules...`);
// Sort by size descending to get the "meatiest" files, but limit to 15 to respect rate limits/performance
const filesToAnalyze = tree
.sort((a: any, b: any) => b.size - a.size)
.slice(0, 15);
const analyzedFiles: FileAnalysis[] = [];
for (const node of filesToAnalyze) {
setLoadingStatus(`Fetching & Analyzing: ${node.path}`);
try {
const content = await fetchFileContent(node.url, token);
if (content) {
const metrics = analyzeFile(node.path, content);
analyzedFiles.push({
fileName: node.path.split('/').pop() || node.path,
path: node.path,
content,
language: node.path.split('.').pop() || 'js',
size: node.size,
metrics
});
}
} catch (err) {
console.warn(`Skipping file ${node.path} due to download error`);
}
}
if (analyzedFiles.length === 0) {
throw new Error("Failed to download any file contents. Check your connection or token permissions.");
}
setFiles(analyzedFiles);
setAppState(AppState.RESULTS);
} catch (error: any) {
console.error(error);
setError(error.message || "An unexpected error occurred");
setAppState(AppState.IDLE);
}
};
return (
<div className="min-h-screen bg-slate-50 text-slate-900 font-sans p-4 md:p-8">
{appState === AppState.IDLE && (
<div className="h-screen flex flex-col items-center justify-center -mt-20">
<RepoInput
onAnalyze={handleAnalyze}
loading={false}
errorMessage={error}
/>
</div>
)}
{(appState === AppState.FETCHING_TREE || appState === AppState.FETCHING_FILES) && (
<div className="h-screen flex flex-col items-center justify-center -mt-20">
<div className="bg-white p-8 rounded-2xl shadow-xl text-center max-w-md w-full">
<div className="relative w-20 h-20 mx-auto mb-6">
<div className="absolute inset-0 border-4 border-indigo-100 rounded-full"></div>
<div className="absolute inset-0 border-4 border-indigo-600 rounded-full border-t-transparent animate-spin"></div>
<Loader2 className="absolute inset-0 m-auto w-8 h-8 text-indigo-600 animate-pulse" />
</div>
<h3 className="text-xl font-bold text-slate-800 mb-2">Analyzing Repository</h3>
<p className="text-slate-500 text-sm mb-6">{loadingStatus}</p>
<div className="w-full bg-slate-100 rounded-full h-2 overflow-hidden">
<div className="h-full bg-indigo-500 animate-progress origin-left w-full"></div>
</div>
</div>
</div>
)}
{appState === AppState.RESULTS && !selectedFile && (
<div className="max-w-7xl mx-auto h-[calc(100vh-4rem)]">
<div className="flex items-center justify-between mb-6">
<div>
<h1 className="text-2xl font-bold text-slate-900">
{repoInfo?.owner} / {repoInfo?.name}
</h1>
<p className="text-slate-500 text-sm">Analysis Results</p>
</div>
<button
onClick={() => setAppState(AppState.IDLE)}
className="text-sm text-indigo-600 hover:text-indigo-800 font-medium"
>
Analyze another repo
</button>
</div>
<Dashboard files={files} onSelectFile={setSelectedFile} />
</div>
)}
{appState === AppState.RESULTS && selectedFile && (
<div className="max-w-7xl mx-auto h-[calc(100vh-4rem)]">
<CodeViewer file={selectedFile} onBack={() => setSelectedFile(null)} />
</div>
)}
</div>
);
};
export default App;