130 lines
4.3 KiB
Markdown
130 lines
4.3 KiB
Markdown
# Phase 10: Frontend Shell — shadcn/ui
|
|
|
|
## Setup shadcn/ui
|
|
```bash
|
|
cd frontend
|
|
npx shadcn@latest init
|
|
# Responder: Vite, Zinc, CSS variables, YES to tailwind
|
|
```
|
|
|
|
## Layout principal
|
|
```
|
|
┌────────────────────────────────────────────────┐
|
|
│ TopBar: [☰] [ABE logo] ···· [⌘K Search] [🌙] [👤]│
|
|
├────────┬───────────────────────────────────────┤
|
|
│ │ │
|
|
│ Side- │ Content Area │
|
|
│ bar │ (React Router Outlet) │
|
|
│ │ │
|
|
│ 📊 Dashboard │
|
|
│ 🔍 Explorations │
|
|
│ 🐛 Findings │
|
|
│ 📄 Reports │
|
|
│ ───────── │
|
|
│ ⚙️ Settings │
|
|
│ │ │
|
|
└────────┴───────────────────────────────────────┘
|
|
```
|
|
|
|
## Dark mode (DEFAULT)
|
|
- Usar estrategia class-based: `<html class="dark">`
|
|
- CSS variables de shadcn ya soportan dark mode
|
|
- Toggle en TopBar: sol/luna
|
|
- Persistir en localStorage key 'abe-theme'
|
|
|
|
## Auth flow en frontend
|
|
```
|
|
App monta →
|
|
GET /api/auth/setup-required
|
|
→ si required: mostrar /setup
|
|
→ si no:
|
|
GET /api/auth/me
|
|
→ si 401: redirect /login
|
|
→ si ok: render AppLayout con user data
|
|
```
|
|
|
|
## API client (lib/api.ts)
|
|
```typescript
|
|
const API_URL = import.meta.env.VITE_API_URL || '';
|
|
|
|
export async function apiFetch<T>(path: string, init?: RequestInit): Promise<T> {
|
|
const res = await fetch(`${API_URL}${path}`, {
|
|
...init,
|
|
credentials: 'include',
|
|
headers: { 'Content-Type': 'application/json', ...init?.headers },
|
|
});
|
|
|
|
if (res.status === 401) {
|
|
window.location.href = '/login';
|
|
throw new Error('Unauthorized');
|
|
}
|
|
|
|
if (!res.ok) {
|
|
const body = await res.json().catch(() => ({}));
|
|
throw new Error(body.message || `HTTP ${res.status}`);
|
|
}
|
|
|
|
return res.json();
|
|
}
|
|
```
|
|
|
|
## Routing
|
|
```typescript
|
|
<Routes>
|
|
<Route path="/login" element={<Login />} />
|
|
<Route path="/setup" element={<Setup />} />
|
|
<Route element={<ProtectedRoute><AppLayout /></ProtectedRoute>}>
|
|
<Route path="/" element={<Dashboard />} />
|
|
<Route path="/sessions" element={<SessionList />} />
|
|
<Route path="/sessions/:id" element={<SessionDetail />} />
|
|
<Route path="/findings" element={<FindingsList />} />
|
|
<Route path="/findings/:id" element={<FindingDetail />} />
|
|
<Route path="/reports" element={<Reports />} />
|
|
<Route path="/visual-review" element={<VisualReview />} />
|
|
<Route path="/settings/*" element={<Settings />} />
|
|
</Route>
|
|
</Routes>
|
|
```
|
|
|
|
## Command Palette (⌘K)
|
|
Powered by shadcn Command (cmdk):
|
|
- Buscar por: sessions, findings, settings sections
|
|
- Acciones: "New Exploration", "Generate Report"
|
|
- Keyboard: ⌘K abre, Esc cierra
|
|
|
|
## File structure
|
|
```
|
|
frontend/src/
|
|
├── components/
|
|
│ ├── ui/ # shadcn generados (NO tocar)
|
|
│ ├── layout/
|
|
│ │ ├── AppLayout.tsx
|
|
│ │ ├── AppSidebar.tsx
|
|
│ │ ├── TopBar.tsx
|
|
│ │ ├── CommandPalette.tsx
|
|
│ │ ├── ProtectedRoute.tsx
|
|
│ │ └── ThemeProvider.tsx
|
|
│ └── common/
|
|
│ └── SeverityBadge.tsx
|
|
├── hooks/
|
|
│ ├── useAuth.ts
|
|
│ └── useSocket.ts
|
|
├── lib/
|
|
│ ├── api.ts
|
|
│ ├── queryClient.ts
|
|
│ └── utils.ts # cn() de shadcn
|
|
├── stores/
|
|
│ └── uiStore.ts
|
|
├── pages/
|
|
│ ├── Dashboard.tsx (placeholder "Coming in Phase 11")
|
|
│ ├── Login.tsx
|
|
│ └── Setup.tsx
|
|
├── App.tsx
|
|
└── main.tsx
|
|
```
|
|
|
|
## IMPORTANTE
|
|
- El Dashboard en esta fase puede ser un placeholder que diga "Dashboard — Coming soon"
|
|
- Lo importante es que el shell funcione: login → sidebar → routing → theme
|
|
- NO intentar hacer todo el dashboard aquí — eso es Phase 11
|