feat!: complete visual redesign → 1.0.0
BREAKING: version bump to 1.0.0 New design system: - Emerald/teal color palette with semantic tokens (success, warning, destructive) - Light/dark mode toggle with localStorage persistence and anti-FOUC - CSS custom properties via Tailwind darkMode: 'class' - Zero new dependencies — all primitives hand-rolled Foundation: - 70+ inline SVG icons (no external icon library) - ThemeProvider + useTheme hook - cn() utility for conditional classes UI primitives (web/src/components/ui/): - Button (6 variants, 4 sizes) - Input, Label, Card with slots, Badge (6 variants + sizes) - Switch (animated), Checkbox, Separator - Skeleton, Spinner, EmptyState - Toast system with auto-dismiss (success/error/warning/info) - Dialog with focus trap + escape/click-outside - ConfirmDialog replacing native confirm() - DropdownMenu with keyboard support Layout & navigation: - Sidebar with icon+label nav, active indicator, responsive drawer on mobile - Topbar for <lg viewports with hamburger - PageHeader with title/description/actions pattern - UserMenu with avatar + dropdown (change password, logout) - DirtyBanner redesigned with semantic styling Pages redesigned: - Login: centered card with gradient background - Dashboard: 4 KPI tiles, disk usage bars, services grid, recent activity - Users: table with search, empty state, edit modal - Files: breadcrumbs, drag-drop upload zone, SVG file icons, rename/delete - Samba: card list with badges, edit modal with PathField - NFS: card list with client chips, client editor - Storage: tabs (Mover/SnapRAID/Jobs), real-time job log streaming - Log: timeline grouped by date with filters - Settings: watched mounts, import actions, system info - NotFound: friendly 404 page Polish: - Toast notifications replace all native alert()/confirm() - Tailwind animations (dialog-enter, toast-enter/leave, dropdown-enter) - Custom scrollbar styling - Focus-visible rings - Skeleton loading states across all pages
This commit is contained in:
+12
-2
@@ -12,6 +12,7 @@ import Log from "./pages/Log";
|
||||
import Settings from "./pages/Settings";
|
||||
import Files from "./pages/Files";
|
||||
import Storage from "./pages/Storage";
|
||||
import NotFound from "./pages/NotFound";
|
||||
|
||||
type AuthState = { loading: boolean; authenticated: boolean; username: string };
|
||||
|
||||
@@ -26,7 +27,16 @@ export default function App() {
|
||||
}, []);
|
||||
|
||||
if (auth.loading) {
|
||||
return <div className="flex min-h-screen items-center justify-center text-slate-400">Cargando...</div>;
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center">
|
||||
<div className="flex items-center gap-3 text-muted-foreground">
|
||||
<svg className="animate-spin" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M21 12a9 9 0 1 1-6.219-8.56" />
|
||||
</svg>
|
||||
<span>Cargando…</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!auth.authenticated) {
|
||||
@@ -60,7 +70,7 @@ export default function App() {
|
||||
<Route path="/storage" element={<Storage />} />
|
||||
<Route path="/log" element={<Log />} />
|
||||
<Route path="/settings" element={<Settings />} />
|
||||
<Route path="*" element={<Navigate to="/" replace />} />
|
||||
<Route path="*" element={<NotFound />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
</DirtyProvider>
|
||||
|
||||
Reference in New Issue
Block a user