diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index f260320..9edae7b 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -1,6 +1,8 @@ import { useState } from "react"; import { AuthProvider, useAuth, type AuthUser } from "./state/AuthContext"; import { ShopProvider } from "./state/ShopContext"; +import { ThemeProvider } from "./state/ThemeContext"; +import { ToastProvider } from "./state/ToastContext"; import { CounterPage } from "./pages/CounterPage"; import { HeroPage } from "./pages/HeroPage"; import { ProductsPage } from "./pages/ProductsPage"; @@ -8,6 +10,8 @@ import { CheckoutPage } from "./pages/CheckoutPage"; import { StaffPage } from "./pages/StaffPage"; import { DashboardPage } from "./pages/DashboardPage"; import { CounterPicker } from "./components/CounterPicker"; +import { ThemeToggle } from "./components/ThemeToggle"; +import { ToastContainer } from "./components/ToastContainer"; function useQueryParam(name: string, fallback: string): string { return new URLSearchParams(window.location.search).get(name) ?? fallback; @@ -65,7 +69,11 @@ function AuthedApp({ shopId, role }: AuthedAppProps) { {link.label} @@ -84,9 +92,10 @@ function TopBar({ user, onSignOut }: { user: AuthUser; onSignOut: () => void }) return (
{user.email} - +
); } @@ -95,7 +104,9 @@ function AppShell() { const { status, user, signOut } = useAuth(); if (status === "loading") { - return
Loading…
; + return ( +
Loading…
+ ); } if (status === "signed_out" || !user?.shopId) { @@ -112,8 +123,13 @@ function AppShell() { export function App() { return ( - - - + + + + + + + + ); } diff --git a/apps/web/src/components/AttributionBadge.tsx b/apps/web/src/components/AttributionBadge.tsx index 6046304..9b00d78 100644 --- a/apps/web/src/components/AttributionBadge.tsx +++ b/apps/web/src/components/AttributionBadge.tsx @@ -1,9 +1,9 @@ const COLORS = [ - "bg-sky-100 text-sky-800", - "bg-emerald-100 text-emerald-800", - "bg-amber-100 text-amber-800", - "bg-violet-100 text-violet-800", - "bg-rose-100 text-rose-800", + "bg-sky-100 text-sky-800 dark:bg-sky-950 dark:text-sky-300", + "bg-emerald-100 text-emerald-800 dark:bg-emerald-950 dark:text-emerald-300", + "bg-amber-100 text-amber-800 dark:bg-amber-950 dark:text-amber-300", + "bg-violet-100 text-violet-800 dark:bg-violet-950 dark:text-violet-300", + "bg-rose-100 text-rose-800 dark:bg-rose-950 dark:text-rose-300", ]; /** Deterministic so the same counter always renders the same color, without a shared color registry. */ diff --git a/apps/web/src/components/AuditLogView.tsx b/apps/web/src/components/AuditLogView.tsx index ab9e0a8..33ffff3 100644 --- a/apps/web/src/components/AuditLogView.tsx +++ b/apps/web/src/components/AuditLogView.tsx @@ -2,6 +2,7 @@ import { useEffect, useState } from "react"; import type { AuditHistoryEntry } from "@stocksync/core"; import { getAudit } from "../api/client"; import { VectorClockExplainer } from "./VectorClockExplainer"; +import { Skeleton } from "./Skeleton"; import { AlertTriangleIcon } from "./icons"; export interface AuditLogViewProps { @@ -38,32 +39,40 @@ export function AuditLogView({ itemId, shopId }: AuditLogViewProps) { if (error) return ( -

+

Couldn't load history for {itemId}.

); if (!history) return ( -

- - Loading history… -

+
+ {[0, 1, 2].map((i) => ( +
+ + +
+ ))} +
); - if (history.length === 0) return

No history yet for {itemId}.

; + if (history.length === 0) return

No history yet for {itemId}.

; return ( -
    +
      {history.map((entry, index) => (
    1. - {entry.action} - {entry.counter_id} - {new Date(entry.timestamp).toLocaleTimeString()} + {entry.action} + + {entry.counter_id} + + {new Date(entry.timestamp).toLocaleTimeString()}
      -

      {formatDetails(entry.details)}

      +

      {formatDetails(entry.details)}

      - Why? + + Why? +
    2. diff --git a/apps/web/src/components/AuthPanel.tsx b/apps/web/src/components/AuthPanel.tsx index 80848d0..d72f186 100644 --- a/apps/web/src/components/AuthPanel.tsx +++ b/apps/web/src/components/AuthPanel.tsx @@ -4,9 +4,11 @@ import { ArrowRightIcon } from "./icons"; type Mode = "login" | "signup" | "confirm" | "new_password"; -const INPUT_CLASS = "w-full rounded-md border border-slate-300 px-3 py-2 text-sm"; +const INPUT_CLASS = + "w-full rounded-md border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 placeholder:text-slate-400 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-100 dark:placeholder:text-slate-500"; const SUBMIT_CLASS = "flex w-full items-center justify-center gap-1.5 rounded-md bg-indigo-600 px-4 py-2.5 text-sm font-semibold text-white transition-colors hover:bg-indigo-700 disabled:opacity-50"; +const LINK_CLASS = "mt-4 text-sm text-slate-500 underline decoration-dotted hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200"; /** * The app's real front door for identity — replaces the old shop_id/ @@ -89,12 +91,16 @@ export function AuthPanel() { } return ( -
      +
      {mode === "login" && ( <> -

      Sign in

      -

      Enter your shop's account to get to your counter.

      - {info &&

      {info}

      } +

      Sign in

      +

      Enter your shop's account to get to your counter.

      + {info && ( +

      + {info} +

      + )}
      - {error &&

      {error}

      } + {error &&

      {error}

      } @@ -125,7 +131,7 @@ export function AuthPanel() { setMode("signup"); setError(""); }} - className="mt-4 text-sm text-slate-500 underline decoration-dotted hover:text-slate-700" + className={LINK_CLASS} > New shop? Create an account instead @@ -134,8 +140,8 @@ export function AuthPanel() { {mode === "signup" && ( <> -

      Set up your shop

      -

      You'll be the owner — invite your staff once you're in.

      +

      Set up your shop

      +

      You'll be the owner — invite your staff once you're in.

      - {error &&

      {error}

      } + {error &&

      {error}

      } @@ -174,7 +180,7 @@ export function AuthPanel() { setMode("login"); setError(""); }} - className="mt-4 text-sm text-slate-500 underline decoration-dotted hover:text-slate-700" + className={LINK_CLASS} > Already have an account? Sign in @@ -183,8 +189,8 @@ export function AuthPanel() { {mode === "confirm" && ( <> -

      Check your email

      -

      Enter the confirmation code we sent to {email}.

      +

      Check your email

      +

      Enter the confirmation code we sent to {email}.

      - {error &&

      {error}

      } + {error &&

      {error}

      } @@ -202,7 +208,7 @@ export function AuthPanel() { @@ -211,8 +217,8 @@ export function AuthPanel() { {mode === "new_password" && ( <> -

      Choose a password

      -

      First sign-in on an invited account — set your own password to continue.

      +

      Choose a password

      +

      First sign-in on an invited account — set your own password to continue.

      - {error &&

      {error}

      } + {error &&

      {error}

      } diff --git a/apps/web/src/components/BarcodeScanButton.tsx b/apps/web/src/components/BarcodeScanButton.tsx index c6822f7..987c1d3 100644 --- a/apps/web/src/components/BarcodeScanButton.tsx +++ b/apps/web/src/components/BarcodeScanButton.tsx @@ -66,12 +66,12 @@ export function BarcodeScanButton({ items, onResolved }: BarcodeScanButtonProps) if (state === "scanning") { return ( -
      +