wulf-pulse/app/layout.tsx
lorentz bee35e0260 fix(auth): reduce mobile sign-in friction (PWA + auto-redirect)
Three independent changes that together stop the mobile re-auth churn:

- app/layout.tsx: add appleWebApp metadata so iOS "Add to Home Screen"
  launches Pulse in true standalone mode (own cookie jar, persists
  across Safari memory pressure)
- components/auth/sign-in-form.tsx: when /auth/sign-in mounts and
  ?callbackUrl starts with /mobile, auto-call authClient.signIn.social
  for Microsoft. With an active M365 browser session this redirect is
  silent — the user lands on /mobile/* with no tap.
- app/auth/sign-in/page.tsx: wrap SignInForm in <Suspense> (required
  by Next.js 16 because SignInForm now uses useSearchParams)

Pairs with operator-side env bump SESSION_TIMEOUT_SECONDS=2592000
(30 days, .env files are gitignored — applied on the running container
via docker compose up -d --force-recreate app).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 07:05:52 -04:00

84 lines
2.6 KiB
TypeScript

import type { Metadata, Viewport } from "next";
import { IBM_Plex_Sans, IBM_Plex_Mono } from "next/font/google";
import "./globals.css";
import { ThemeProvider } from "@/components/theme-provider";
import { AppNavigation } from "@/components/navigation/app-navigation";
import { CommandPalette } from "@/components/navigation/command-palette";
import { TaglineFooter } from "@/components/branding/tagline-footer";
import { Toaster } from "sonner";
import { AuthProvider } from "@/components/auth/auth-provider";
// IBM Plex Sans replaces the brand-mandated Helvetica/Arial. The 2013
// standards guide called for Helvetica Bold for headers and Helvetica
// Light for the tagline; Plex Sans honors the spirit (clean engineered
// sans) while loading reliably from Google Fonts. Weight 300 covers the
// "Light" usage in the tagline footer.
const plexSans = IBM_Plex_Sans({
subsets: ["latin"],
weight: ["300", "400", "500", "600", "700"],
variable: "--font-plex-sans",
});
const plexMono = IBM_Plex_Mono({
subsets: ["latin"],
weight: ["400", "500", "600"],
variable: "--font-plex-mono",
});
export const metadata: Metadata = {
title: "Pulse · Operations console",
description: "Wulf Consulting operations console — tickets, RMM, IT Glue, backups, and analytics in one place.",
manifest: "/manifest.json",
icons: {
icon: [
{ url: "/favicon.png", sizes: "any" },
{ url: "/wulff-logo.png", sizes: "32x32", type: "image/png" },
],
shortcut: "/favicon.png",
apple: "/wulff-logo.png",
},
appleWebApp: {
capable: true,
statusBarStyle: "default",
title: "Pulse",
},
};
export const viewport: Viewport = {
width: "device-width",
initialScale: 1,
viewportFit: "cover",
themeColor: [
{ media: "(prefers-color-scheme: light)", color: "#FFFFFF" },
{ media: "(prefers-color-scheme: dark)", color: "#0A0A0A" },
],
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en" suppressHydrationWarning className={`${plexSans.variable} ${plexMono.variable}`}>
<body className="font-sans antialiased">
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
<AuthProvider>
<div className="min-h-screen bg-background flex flex-col">
<AppNavigation />
<main className="flex-1">{children}</main>
<TaglineFooter />
</div>
<CommandPalette />
</AuthProvider>
<Toaster position="top-right" richColors />
</ThemeProvider>
</body>
</html>
);
}