feat(web): add MosaicLogo and MosaicSpinner components (MS15-FE-006)
All checks were successful
ci/woodpecker/push/web Pipeline was successful

- Add MosaicLogo component with 5-element Mosaic brand icon (4 corner
  squares + center circle), using CSS custom properties for theme-aware
  colors and optional rotation animation
- Add MosaicSpinner wrapper that enables spinning and supports optional
  label and full-page overlay modes
- Replace generic CSS spinner in authenticated layout loading state with
  MosaicSpinner for consistent brand identity

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-22 14:34:29 -06:00
parent 2a6d02b46b
commit e615fa80a5
11 changed files with 850 additions and 304 deletions

View File

@@ -4,8 +4,10 @@ import { useEffect } from "react";
import { useRouter } from "next/navigation";
import { useAuth } from "@/lib/auth/auth-context";
import { IS_MOCK_AUTH_MODE } from "@/lib/config";
import { Navigation } from "@/components/layout/Navigation";
import { AppHeader } from "@/components/layout/AppHeader";
import { AppSidebar } from "@/components/layout/AppSidebar";
import { ChatOverlay } from "@/components/chat";
import { MosaicSpinner } from "@/components/ui/MosaicSpinner";
import type { ReactNode } from "react";
export default function AuthenticatedLayout({
@@ -23,11 +25,7 @@ export default function AuthenticatedLayout({
}, [isAuthenticated, isLoading, router]);
if (isLoading) {
return (
<div className="flex min-h-screen items-center justify-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-gray-900"></div>
</div>
);
return <MosaicSpinner size={48} fullPage />;
}
if (!isAuthenticated) {
@@ -35,19 +33,31 @@ export default function AuthenticatedLayout({
}
return (
<div className="min-h-screen bg-gray-50">
<Navigation />
<div className="pt-16">
<div className="app-shell">
{/* Full-width header — grid-column: 1 / -1 via .app-header CSS class */}
<AppHeader />
{/* Sidebar — left column, row 2, via .app-sidebar CSS class */}
<AppSidebar />
{/* Main content — right column, row 2, via .app-main CSS class */}
<main className="app-main" id="main-content">
{IS_MOCK_AUTH_MODE && (
<div
className="border-b border-amber-300 bg-amber-50 px-4 py-2 text-sm text-amber-900"
className="border-b px-4 py-2 text-xs font-medium flex-shrink-0"
style={{
borderColor: "var(--ms-amber-500)",
background: "rgba(245, 158, 11, 0.08)",
color: "var(--ms-amber-400)",
}}
data-testid="mock-auth-banner"
>
Mock Auth Mode (Local Only): Real authentication is bypassed for frontend development.
</div>
)}
{children}
</div>
<div className="flex-1 overflow-y-auto p-5">{children}</div>
</main>
{!IS_MOCK_AUTH_MODE && <ChatOverlay />}
</div>
);