feat(mobile-dashboard): add light/dark toggle, switch accent to red, bigger numbers
Boss phone dashboard (/) improvements: - Add a Sun/Moon theme toggle button next to the refresh button. Uses the existing next-themes provider so the choice stays in sync with the Settings → Mode toggle. - Replace the cyan accent (cyan-400/cyan-500/*) with red (red-400 for chrome, red-500 on tinted backgrounds) for stronger contrast on both light and dark themes. The active today/week/month tab and per-rep/per-driver avatar circles use bg-red-500/15 + text-red-500 for high-contrast highlights. - Bump OrderTotal values from text-xl/font-semibold to text-3xl/font-bold, and MiniMetric values from text-sm/font- semibold to text-lg/font-bold, so the month-of-loaded/claimed/ delivered/invoiced counts are easier to read at a glance.
This commit is contained in:
parent
20304442a7
commit
43cdf02ae5
1 changed files with 47 additions and 19 deletions
|
|
@ -3,7 +3,8 @@
|
|||
import { useState, useEffect, useCallback, useRef } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { toast } from "sonner";
|
||||
import { RefreshCw, X } from "lucide-react";
|
||||
import { RefreshCw, X, Moon, Sun } from "lucide-react";
|
||||
import { useTheme } from "next-themes";
|
||||
import getPocketBase from "@/lib/pocketbase";
|
||||
import type {
|
||||
BossDashboardData,
|
||||
|
|
@ -56,6 +57,19 @@ export default function BossDashboardView() {
|
|||
realtimeStatusRef.current = realtimeStatus;
|
||||
}, [realtimeStatus]);
|
||||
|
||||
// Light/dark toggle — next-themes mounted check avoids SSR/hydration
|
||||
// mismatch (next-themes returns "light" before mount regardless of the
|
||||
// persisted value).
|
||||
const { theme: colorMode, setTheme: setColorMode } = useTheme();
|
||||
const [mounted, setMounted] = useState(false);
|
||||
useEffect(() => {
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||
setMounted(true);
|
||||
}, []);
|
||||
const toggleColorMode = useCallback(() => {
|
||||
setColorMode(colorMode === "dark" ? "light" : "dark");
|
||||
}, [colorMode, setColorMode]);
|
||||
|
||||
const fetchData = useCallback(async () => {
|
||||
// Pre-check auth — if PB has no valid token, redirect via the router so
|
||||
// we preserve the in-memory state of the dashboard (search, tabs, sheet)
|
||||
|
|
@ -225,7 +239,7 @@ export default function BossDashboardView() {
|
|||
<header className="flex items-start justify-between gap-3">
|
||||
<div className="min-w-0">
|
||||
<h1 className="text-xl font-semibold tracking-tight">
|
||||
Good {greeting()}, <span className="text-cyan-400">{firstName}</span>.
|
||||
Good {greeting()}, <span className="text-red-400">{firstName}</span>.
|
||||
</h1>
|
||||
<div className="mt-0.5 flex items-center gap-2 text-xs text-muted-foreground">
|
||||
<span
|
||||
|
|
@ -247,15 +261,29 @@ export default function BossDashboardView() {
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void fetchData()}
|
||||
disabled={loading}
|
||||
aria-label="Refresh"
|
||||
className="grid h-10 w-10 shrink-0 place-items-center rounded-full border border-cyan-500/30 text-cyan-400 active:bg-cyan-500/10 disabled:opacity-50"
|
||||
>
|
||||
<RefreshCw className={`h-4 w-4 ${loading ? "animate-spin" : ""}`} />
|
||||
</button>
|
||||
<div className="flex items-center gap-2 shrink-0">
|
||||
<button
|
||||
type="button"
|
||||
onClick={toggleColorMode}
|
||||
aria-label={mounted && colorMode === "dark" ? "Switch to light mode" : "Switch to dark mode"}
|
||||
className="grid h-10 w-10 place-items-center rounded-full border border-red-500/30 text-red-400 active:bg-red-500/10"
|
||||
>
|
||||
{mounted && colorMode === "dark" ? (
|
||||
<Sun className="h-4 w-4" />
|
||||
) : (
|
||||
<Moon className="h-4 w-4" />
|
||||
)}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void fetchData()}
|
||||
disabled={loading}
|
||||
aria-label="Refresh"
|
||||
className="grid h-10 w-10 shrink-0 place-items-center rounded-full border border-red-500/30 text-red-400 active:bg-red-500/10 disabled:opacity-50"
|
||||
>
|
||||
<RefreshCw className={`h-4 w-4 ${loading ? "animate-spin" : ""}`} />
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Time tab bar */}
|
||||
|
|
@ -269,7 +297,7 @@ export default function BossDashboardView() {
|
|||
onClick={() => selectWindow(w)}
|
||||
className={`flex-1 rounded-lg px-3 py-2 text-sm font-medium capitalize transition-colors ${
|
||||
activeWindow === w
|
||||
? "bg-cyan-500/15 text-cyan-300"
|
||||
? "bg-red-500/15 text-red-500"
|
||||
: "text-muted-foreground hover:text-foreground"
|
||||
}`}
|
||||
>
|
||||
|
|
@ -315,13 +343,13 @@ export default function BossDashboardView() {
|
|||
className="w-full rounded-xl border border-border/60 bg-card p-3 text-left transition-colors hover:bg-card/70 active:bg-card/80 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="grid h-9 w-9 shrink-0 place-items-center rounded-full bg-cyan-500/15 text-cyan-300 text-sm font-semibold">
|
||||
<div className="grid h-9 w-9 shrink-0 place-items-center rounded-full bg-red-500/15 text-red-500 text-sm font-semibold">
|
||||
{(rep.name[0] ?? "?").toUpperCase()}
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="text-sm font-medium truncate">{rep.name}</div>
|
||||
<div className="mt-0.5 grid grid-cols-3 gap-1 text-center">
|
||||
<MiniMetric label="loaded" value={rep.quotesDoneFor} color="text-cyan-400" />
|
||||
<MiniMetric label="loaded" value={rep.quotesDoneFor} color="text-red-400" />
|
||||
<MiniMetric label="claimed" value={rep.quotesLoaded} color="text-emerald-400" />
|
||||
<MiniMetric label="orders" value={rep.ordersPlaced} color="text-orange-400" />
|
||||
</div>
|
||||
|
|
@ -388,7 +416,7 @@ export default function BossDashboardView() {
|
|||
className="flex items-center justify-between gap-3 rounded-xl border border-border/60 bg-card p-3"
|
||||
>
|
||||
<div className="flex items-center gap-3 min-w-0">
|
||||
<div className="grid h-9 w-9 shrink-0 place-items-center rounded-full bg-cyan-500/15 text-cyan-300 text-sm font-semibold">
|
||||
<div className="grid h-9 w-9 shrink-0 place-items-center rounded-full bg-red-500/15 text-red-500 text-sm font-semibold">
|
||||
{(drv.name[0] ?? "?").toUpperCase()}
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
|
|
@ -465,7 +493,7 @@ export default function BossDashboardView() {
|
|||
}}
|
||||
className={`flex-1 rounded-md px-3 py-1.5 text-sm font-medium capitalize transition-colors ${
|
||||
sheetKind === k
|
||||
? "bg-cyan-500/15 text-cyan-300"
|
||||
? "bg-red-500/15 text-red-500"
|
||||
: "text-muted-foreground hover:text-foreground"
|
||||
}`}
|
||||
>
|
||||
|
|
@ -569,7 +597,7 @@ function OrderTotal({
|
|||
<div className="text-[10px] font-bold uppercase tracking-widest text-muted-foreground">
|
||||
{label}
|
||||
</div>
|
||||
<div className={`mt-1 text-xl font-semibold tabular-nums ${color}`}>
|
||||
<div className={`mt-1 text-3xl font-bold tabular-nums ${color}`}>
|
||||
{value.toLocaleString()}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -587,10 +615,10 @@ function MiniMetric({
|
|||
}) {
|
||||
return (
|
||||
<div>
|
||||
<div className={`text-sm font-semibold tabular-nums leading-none ${color}`}>
|
||||
<div className={`text-lg font-bold tabular-nums leading-none ${color}`}>
|
||||
{value.toLocaleString()}
|
||||
</div>
|
||||
<div className="mt-0.5 text-[9px] uppercase tracking-wider text-muted-foreground leading-none">
|
||||
<div className="mt-1 text-[9px] uppercase tracking-wider text-muted-foreground leading-none">
|
||||
{label}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue