|
1 | | -import { useState, useEffect } from 'react' |
2 | | -import { Toaster } from '@/components/ui/sonner' |
3 | | -import { toast } from 'sonner' |
4 | | -import { useAuth } from '../contexts/AuthContext' |
5 | | -import { RepoList } from './RepoList' |
6 | | -import { AddRepoForm } from './AddRepoForm' |
7 | | -import { SearchPanel } from './SearchPanel' |
8 | | -import { DependencyGraph } from './DependencyGraph' |
9 | | -import { RepoOverview } from './RepoOverview' |
10 | | -import { StyleInsights } from './StyleInsights' |
11 | | -import { ImpactAnalyzer } from './ImpactAnalyzer' |
12 | | -import { PerformanceDashboard } from './PerformanceDashboard' |
13 | | -import { UserNav } from './UserNav' |
14 | | -import type { Repository } from '../types' |
15 | | -import { API_URL } from '../config/api' |
16 | | - |
17 | | -type RepoTab = 'overview' | 'search' | 'dependencies' | 'insights' | 'impact' |
| 1 | +import { DashboardLayout } from './dashboard/DashboardLayout' |
| 2 | +import { DashboardHome } from './dashboard/DashboardHome' |
18 | 3 |
|
19 | 4 | export function Dashboard() { |
20 | | - const { session } = useAuth() |
21 | | - const [repos, setRepos] = useState<Repository[]>([]) |
22 | | - const [selectedRepo, setSelectedRepo] = useState<string | null>(null) |
23 | | - const [activeTab, setActiveTab] = useState<RepoTab>('overview') |
24 | | - const [loading, setLoading] = useState(false) |
25 | | - const [showPerformance, setShowPerformance] = useState(false) |
26 | | - |
27 | | - const fetchRepos = async () => { |
28 | | - if (!session?.access_token) return |
29 | | - |
30 | | - try { |
31 | | - const response = await fetch(`${API_URL}/api/repos`, { |
32 | | - headers: { 'Authorization': `Bearer ${session.access_token}` } |
33 | | - }) |
34 | | - const data = await response.json() |
35 | | - setRepos(data.repositories || []) |
36 | | - } catch (error) { |
37 | | - console.error('Error fetching repos:', error) |
38 | | - } |
39 | | - } |
40 | | - |
41 | | - useEffect(() => { |
42 | | - fetchRepos() |
43 | | - const interval = setInterval(fetchRepos, 30000) |
44 | | - return () => clearInterval(interval) |
45 | | - }, []) |
46 | | - |
47 | | - const handleAddRepo = async (gitUrl: string, branch: string) => { |
48 | | - try { |
49 | | - setLoading(true) |
50 | | - const name = gitUrl.split('/').pop()?.replace('.git', '') || 'unknown' |
51 | | - |
52 | | - const response = await fetch(`${API_URL}/api/repos`, { |
53 | | - method: 'POST', |
54 | | - headers: { |
55 | | - 'Authorization': `Bearer ${session.access_token}`, |
56 | | - 'Content-Type': 'application/json' |
57 | | - }, |
58 | | - body: JSON.stringify({ name, git_url: gitUrl, branch }) |
59 | | - }) |
60 | | - |
61 | | - const data = await response.json() |
62 | | - |
63 | | - await fetch(`${API_URL}/api/repos/${data.repo_id}/index`, { |
64 | | - method: 'POST', |
65 | | - headers: { 'Authorization': `Bearer ${session.access_token}` } |
66 | | - }) |
67 | | - |
68 | | - await fetchRepos() |
69 | | - toast.success('Repository added!', { |
70 | | - description: `${name} is now being indexed` |
71 | | - }) |
72 | | - } catch (error) { |
73 | | - console.error('Error adding repo:', error) |
74 | | - toast.error('Failed to add repository', { |
75 | | - description: 'Please check the Git URL and try again' |
76 | | - }) |
77 | | - } finally { |
78 | | - setLoading(false) |
79 | | - } |
80 | | - } |
81 | | - |
82 | | - const handleReindex = async () => { |
83 | | - if (!selectedRepo) return |
84 | | - |
85 | | - try { |
86 | | - setLoading(true) |
87 | | - await fetch(`${API_URL}/api/repos/${selectedRepo}/index`, { |
88 | | - method: 'POST', |
89 | | - headers: { 'Authorization': `Bearer ${session.access_token}` } |
90 | | - }) |
91 | | - await fetchRepos() |
92 | | - // RepoOverview component will show the toast and progress |
93 | | - } catch (error) { |
94 | | - toast.error('Re-indexing failed', { |
95 | | - description: 'Please check the console for details' |
96 | | - }) |
97 | | - } finally { |
98 | | - setLoading(false) |
99 | | - } |
100 | | - } |
101 | | - |
102 | | - const selectedRepoData = repos.find(r => r.id === selectedRepo) |
103 | | - const isRepoView = selectedRepo && selectedRepoData |
104 | | - |
105 | 5 | return ( |
106 | | - <div className="min-h-screen bg-gray-50"> |
107 | | - <nav className="bg-white border-b border-gray-200"> |
108 | | - <div className="max-w-7xl mx-auto px-6"> |
109 | | - <div className="flex items-center justify-between h-16"> |
110 | | - <div className="flex items-center gap-2"> |
111 | | - <div className="w-8 h-8 rounded-lg bg-blue-600 flex items-center justify-center"> |
112 | | - <span className="text-white font-bold text-sm">CI</span> |
113 | | - </div> |
114 | | - <span className="font-semibold text-gray-900">CodeIntel</span> |
115 | | - <span className="text-xs text-gray-400 ml-2">MCP Server</span> |
116 | | - </div> |
117 | | - |
118 | | - <div className="flex items-center gap-4"> |
119 | | - <button |
120 | | - onClick={() => setShowPerformance(!showPerformance)} |
121 | | - className={`text-xs px-3 py-1.5 rounded-md transition-colors ${ |
122 | | - showPerformance |
123 | | - ? 'bg-blue-100 text-blue-700 font-medium' |
124 | | - : 'text-gray-600 hover:bg-gray-100' |
125 | | - }`} |
126 | | - > |
127 | | - 📊 Performance |
128 | | - </button> |
129 | | - <div className="flex items-center gap-2 text-xs text-gray-500"> |
130 | | - <div className="w-2 h-2 rounded-full bg-green-500" /> |
131 | | - <span>API Connected</span> |
132 | | - </div> |
133 | | - <div className="text-xs text-gray-400 border-l border-gray-200 pl-4"> |
134 | | - {repos.length} repos • {repos.filter(r => r.status === 'indexed').length} indexed |
135 | | - </div> |
136 | | - <div className="border-l border-gray-200 pl-4"> |
137 | | - <UserNav /> |
138 | | - </div> |
139 | | - </div> |
140 | | - </div> |
141 | | - </div> |
142 | | - </nav> |
143 | | - |
144 | | - <main className="max-w-7xl mx-auto px-6 py-8"> |
145 | | - {/* Performance Dashboard Overlay */} |
146 | | - {showPerformance && ( |
147 | | - <div className="mb-6"> |
148 | | - <PerformanceDashboard apiUrl={API_URL} apiKey={session.access_token} /> |
149 | | - </div> |
150 | | - )} |
151 | | - |
152 | | - {!isRepoView && ( |
153 | | - <div className="space-y-6"> |
154 | | - <div className="flex items-center justify-between"> |
155 | | - <div> |
156 | | - <h1 className="text-2xl font-semibold text-gray-900">Repositories</h1> |
157 | | - <p className="text-sm text-gray-600 mt-1">Manage and index your codebases</p> |
158 | | - </div> |
159 | | - <AddRepoForm onAdd={handleAddRepo} loading={loading} /> |
160 | | - </div> |
161 | | - |
162 | | - <RepoList |
163 | | - repos={repos} |
164 | | - selectedRepo={selectedRepo} |
165 | | - onSelect={(id) => { |
166 | | - setSelectedRepo(id) |
167 | | - setActiveTab('overview') |
168 | | - }} |
169 | | - /> |
170 | | - </div> |
171 | | - )} |
172 | | - |
173 | | - {isRepoView && ( |
174 | | - <div> |
175 | | - <div className="mb-6"> |
176 | | - <button |
177 | | - onClick={() => { |
178 | | - setSelectedRepo(null) |
179 | | - setActiveTab('overview') |
180 | | - }} |
181 | | - className="flex items-center gap-2 text-sm text-gray-600 hover:text-gray-900 mb-4" |
182 | | - > |
183 | | - <span>←</span> |
184 | | - <span>Back to Repositories</span> |
185 | | - </button> |
186 | | - |
187 | | - <div className="flex items-center justify-between"> |
188 | | - <div> |
189 | | - <h1 className="text-2xl font-semibold text-gray-900">{selectedRepoData.name}</h1> |
190 | | - <p className="text-sm text-gray-600 mt-1 font-mono">{selectedRepoData.git_url}</p> |
191 | | - </div> |
192 | | - |
193 | | - {selectedRepoData.status === 'indexed' && ( |
194 | | - <span className="badge-success">✓ Indexed</span> |
195 | | - )} |
196 | | - </div> |
197 | | - </div> |
198 | | - |
199 | | - <div className="border-b border-gray-200 mb-6"> |
200 | | - <div className="flex gap-6 overflow-x-auto"> |
201 | | - <button |
202 | | - onClick={() => setActiveTab('overview')} |
203 | | - className={`pb-3 border-b-2 transition-colors whitespace-nowrap ${ |
204 | | - activeTab === 'overview' |
205 | | - ? 'border-blue-600 text-gray-900 font-medium' |
206 | | - : 'border-transparent text-gray-600 hover:text-gray-900' |
207 | | - }`} |
208 | | - > |
209 | | - Overview |
210 | | - </button> |
211 | | - <button |
212 | | - onClick={() => setActiveTab('search')} |
213 | | - className={`pb-3 border-b-2 transition-colors whitespace-nowrap ${ |
214 | | - activeTab === 'search' |
215 | | - ? 'border-blue-600 text-gray-900 font-medium' |
216 | | - : 'border-transparent text-gray-600 hover:text-gray-900' |
217 | | - }`} |
218 | | - > |
219 | | - Search |
220 | | - </button> |
221 | | - <button |
222 | | - onClick={() => setActiveTab('dependencies')} |
223 | | - className={`pb-3 border-b-2 transition-colors whitespace-nowrap ${ |
224 | | - activeTab === 'dependencies' |
225 | | - ? 'border-blue-600 text-gray-900 font-medium' |
226 | | - : 'border-transparent text-gray-600 hover:text-gray-900' |
227 | | - }`} |
228 | | - > |
229 | | - Dependencies |
230 | | - </button> |
231 | | - <button |
232 | | - onClick={() => setActiveTab('insights')} |
233 | | - className={`pb-3 border-b-2 transition-colors whitespace-nowrap ${ |
234 | | - activeTab === 'insights' |
235 | | - ? 'border-blue-600 text-gray-900 font-medium' |
236 | | - : 'border-transparent text-gray-600 hover:text-gray-900' |
237 | | - }`} |
238 | | - > |
239 | | - Code Style |
240 | | - </button> |
241 | | - <button |
242 | | - onClick={() => setActiveTab('impact')} |
243 | | - className={`pb-3 border-b-2 transition-colors whitespace-nowrap ${ |
244 | | - activeTab === 'impact' |
245 | | - ? 'border-blue-600 text-gray-900 font-medium' |
246 | | - : 'border-transparent text-gray-600 hover:text-gray-900' |
247 | | - }`} |
248 | | - > |
249 | | - Impact |
250 | | - </button> |
251 | | - </div> |
252 | | - </div> |
253 | | - |
254 | | - {activeTab === 'overview' && ( |
255 | | - <RepoOverview |
256 | | - repo={selectedRepoData} |
257 | | - onReindex={handleReindex} |
258 | | - apiUrl={API_URL} |
259 | | - apiKey={session.access_token} |
260 | | - /> |
261 | | - )} |
262 | | - |
263 | | - {activeTab === 'search' && ( |
264 | | - <SearchPanel repoId={selectedRepo} apiUrl={API_URL} apiKey={session.access_token} /> |
265 | | - )} |
266 | | - |
267 | | - {activeTab === 'dependencies' && ( |
268 | | - <DependencyGraph repoId={selectedRepo} apiUrl={API_URL} apiKey={session.access_token} /> |
269 | | - )} |
270 | | - |
271 | | - {activeTab === 'insights' && ( |
272 | | - <StyleInsights repoId={selectedRepo} apiUrl={API_URL} apiKey={session.access_token} /> |
273 | | - )} |
274 | | - |
275 | | - {activeTab === 'impact' && ( |
276 | | - <ImpactAnalyzer repoId={selectedRepo} apiUrl={API_URL} apiKey={session.access_token} /> |
277 | | - )} |
278 | | - </div> |
279 | | - )} |
280 | | - </main> |
281 | | - <Toaster /> |
282 | | - </div> |
| 6 | + <DashboardLayout> |
| 7 | + <DashboardHome /> |
| 8 | + </DashboardLayout> |
283 | 9 | ) |
284 | 10 | } |
0 commit comments