{title}
} + {description && ( +{description}
+ )} + {action &&diff --git a/services/frontend/DESIGN_TOKENS.md b/services/frontend/DESIGN_TOKENS.md
index eabb86c..0cf01ba 100644
--- a/services/frontend/DESIGN_TOKENS.md
+++ b/services/frontend/DESIGN_TOKENS.md
@@ -633,7 +633,7 @@ Framer Motion `AnimatePresence` and `motion` components (used extensively in Mes
## 13. Known Issues & Technical Debt
-### 13.1 Hardcoded Utility Colors — Not Using CSS Variables
+### 13.1 Hardcoded Utility Colors — Not Using CSS Variables — RESOLVED in redesign (all components migrated to CSS vars)
Several components bypass the OKLCH custom property system and use Tailwind's built-in `emerald-*`, `amber-*`, `red-*`, `blue-*`, `orange-*`, `yellow-*`, `violet-*`, `cyan-*`, `purple-*`, `sky-*`, `pink-*` utility classes directly. These will not respond to theme changes.
@@ -692,13 +692,13 @@ const primaryColor = getComputedStyle(document.documentElement)
// Convert OKLCH to hex or use Canvas oklch() if available
```
-### 13.4 `glow-pulse` Keyframe Fragmentation
+### 13.4 `glow-pulse` Keyframe Fragmentation — RESOLVED in T02 (moved to styles.css)
The `glowPulse` keyframes are defined in `tailwind.config.js` but NOT in `styles.css`. The class `animate-glow-pulse` is referenced by `ParticleBackground.tsx`. Under Tailwind 4's CSS-first configuration, keyframes should live in the stylesheet. The config-only definition works but is inconsistent with `bar-pulse`, `shimmer`, `fadeInUp`, and `fadeIn` which are in `styles.css`.
**Fix**: Move `@keyframes glowPulse { ... }` into `styles.css`.
-### 13.5 `shimmer` Duration Mismatch
+### 13.5 `shimmer` Duration Mismatch — RESOLVED in T02 (CSS canonical at 1.5s)
`styles.css`: `animation: shimmer 1.5s ease-in-out infinite`
`tailwind.config.js`: `shimmer: "shimmer 2s linear infinite"` (also uses `linear` vs. `ease-in-out`)
@@ -707,13 +707,13 @@ The CSS class `.animate-shimmer` (used by `Skeleton.tsx`) references the CSS-bas
**Fix**: Align both sources. Pick one canonical definition.
-### 13.6 Z-Index Scale Not Formalized
+### 13.6 Z-Index Scale Not Formalized — RESOLVED (z-index registry established in layout)
No defined z-index scale or Sass/CSS variables. Values jump from 50 to 9999. The chatbot's `z-[9999]` is brittle — any overlay/modal added later risks overlap issues.
**Fix**: Define z-index custom properties: `--z-header: 10`, `--z-overlay: 50`, `--z-modal: 100`, `--z-toast: 50`, etc.
-### 13.7 Toast Container z-index Collision
+### 13.7 Toast Container z-index Collision — RESOLVED in T06 (toast now z-40, positioned top-right)
Toast container (`z-50`) and MobileTabBar (`z-50`) at same z-index. On mobile, toasts could be partially hidden behind the tab bar since toasts use `bottom-4` and the tab bar is `bottom-0`. In practice the toast gap prevents overlap, but this is fragile.
@@ -723,6 +723,14 @@ Toast container (`z-50`) and MobileTabBar (`z-50`) at same z-index. On mobile, t
---
+## 14. Dark Mode
+
+Dark mode is driven by `[data-theme="dark"]` CSS custom property overrides in `styles.css`.
+The theme is managed by `useTheme()` hook (`shared/hooks/useTheme.ts`).
+See the full palette in `docs/superpowers/specs/2026-07-02-bete-frontend-redesign-design.md`.
+
+---
+
## Appendix: File Map
| Token / Concern | Canonical Source |
diff --git a/services/frontend/src/App.tsx b/services/frontend/src/App.tsx
index 4f70ec8..82c5d23 100644
--- a/services/frontend/src/App.tsx
+++ b/services/frontend/src/App.tsx
@@ -1,5 +1,6 @@
-import { useEffect, useMemo, useState } from "react";
+import { useCallback, useEffect, useMemo, useState } from "react";
import type { ActiveSpeaker } from "./entities/voice/types.js";
+import { AuthOverlay } from "./features/auth";
import { DashboardPanel } from "./features/dashboard";
import { LivePanel } from "./features/live";
import { useMediaControl } from "./features/live/hooks/useMediaControl";
@@ -13,6 +14,7 @@ import {
import { getAppConfig } from "./shared/api/client";
import { useAudioPlayback } from "./shared/hooks/useAudioPlayback";
import { useAudioTransmit } from "./shared/hooks/useAudioTransmit";
+import { useTheme } from "./shared/hooks/useTheme";
import { useUIState } from "./shared/hooks/useUIState";
import { MobileTabBar } from "./shared/ui/MobileTabBar";
import { useDashboardSocket } from "./shared/ws/socket";
@@ -20,6 +22,22 @@ import { DashboardLayout } from "./widgets/DashboardLayout";
export default function App() {
const { uiState, patchUIState } = useUIState();
+ const [authenticated, setAuthenticated] = useState(() => {
+ return sessionStorage.getItem("admin-password") !== null;
+ });
+ useTheme();
+
+ const handleAuthenticated = useCallback(() => {
+ setAuthenticated(true);
+ }, []);
+
+ // If not authenticated, show the auth overlay
+ if (!authenticated) {
+ const isPublicDashboard = import.meta.env.VITE_DASHBOARD_IS_PUBLIC === "true";
+ if (!isPublicDashboard) {
+ return
Pending
+
{stats.moderation_overview.processing}
Processing
diff --git a/services/frontend/src/features/dashboard/index.tsx b/services/frontend/src/features/dashboard/index.tsx index f2086ef..bd9f5ba 100644 --- a/services/frontend/src/features/dashboard/index.tsx +++ b/services/frontend/src/features/dashboard/index.tsx @@ -1,5 +1,21 @@ +/* ═══════════════════════════════════════════════════════════════════════════ + * IMPHNEN DashboardPanel — Statistics Hub for Guild Moderation Watcher + * Menampilkan overview komunitas dengan IMPHNEN approachable modernism. + * Tiga tab: Stats (ringkasan), Users (profil pengguna), Channels (kanal). + * ═══════════════════════════════════════════════════════════════════════════ */ + import { useState } from "react"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../shared/ui"; +import { + BarChart3, + Hash, + Users, +} from "lucide-react"; +import { + Tabs, + TabsContent, + TabsList, + TabsTrigger, +} from "../../shared/ui"; import { ChannelProfileDetail } from "./components/ChannelProfileDetail"; import { ChannelSummaryList } from "./components/ChannelSummaryList"; import { DashboardStatsContent } from "./components/DashboardStats"; @@ -82,44 +98,67 @@ export function DashboardPanel() { } return ( -+ Pantau statistik, profil pengguna, dan aktivitas kanal komunitas + IMPHNEN secara real-time. +
+{description}
+ )} + {action &&