4.3 KiB
4.3 KiB
Phase 10: Frontend Shell — shadcn/ui
Setup shadcn/ui
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)
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
<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