fix+upstream: hooks-order crash, ConfirmDialog/PageHeader/skeletons, styled error boundary
Hoists hooks above early return (render-time Navigate); ports finance's ConfirmDialog/PageHeader/loading/states + APC error-copy mapper; removes window.confirm, dead appbar search, and seeded fake notifications; wires toasts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useRef, useState } from "react"
|
||||
|
||||
const SIDEBAR_KEY = "crema.shell.sidebar"
|
||||
import { NavLink, useNavigate } from "react-router"
|
||||
import { NavLink, Navigate, useNavigate } from "react-router"
|
||||
import {
|
||||
Bell,
|
||||
LayoutDashboard,
|
||||
@@ -52,7 +52,6 @@ import {
|
||||
dismissAll,
|
||||
markAllRead,
|
||||
markRead,
|
||||
seedIfEmpty,
|
||||
unreadCount,
|
||||
useNotifications,
|
||||
} from "~/lib/notifications"
|
||||
@@ -66,7 +65,6 @@ import {
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "~/components/ui/dropdown-menu"
|
||||
import { Input } from "~/components/ui/input"
|
||||
import {
|
||||
Sheet,
|
||||
SheetContent,
|
||||
@@ -129,17 +127,10 @@ export function AppShell({
|
||||
),
|
||||
}
|
||||
|
||||
// Protected shell: bounce to /login when there's no session.
|
||||
useEffect(() => {
|
||||
if (typeof window === "undefined") return
|
||||
if (!session) {
|
||||
const next = encodeURIComponent(
|
||||
window.location.pathname + window.location.search,
|
||||
)
|
||||
navigate(`/login?next=${next}`, { replace: true })
|
||||
}
|
||||
}, [session, navigate])
|
||||
if (!session) return null
|
||||
// All hooks must be called unconditionally — declare them BEFORE any
|
||||
// early return so React's render-time hook count is stable. (Previously
|
||||
// `if (!session) return null` sat above these hooks, so the hook count
|
||||
// changed the moment the session flipped → hook-order crash.)
|
||||
const [expanded, setExpanded] = useState<boolean>(() => {
|
||||
if (typeof window === "undefined") return false
|
||||
return localStorage.getItem(SIDEBAR_KEY) === "1"
|
||||
@@ -149,10 +140,20 @@ export function AppShell({
|
||||
}, [expanded])
|
||||
const [mobileOpen, setMobileOpen] = useState(false)
|
||||
const [scriptsOpen, setScriptsOpen] = useState(false)
|
||||
const BrandIcon = brand.icon
|
||||
|
||||
useScriptsHotkey(() => setScriptsOpen(true))
|
||||
|
||||
// Protected shell: redirect to /login when there's no session. Done at
|
||||
// render time (not in an effect) so we don't briefly render a blank
|
||||
// shell during the redirect window.
|
||||
if (!session) {
|
||||
const next =
|
||||
typeof window !== "undefined"
|
||||
? encodeURIComponent(window.location.pathname + window.location.search)
|
||||
: ""
|
||||
return <Navigate to={`/login?next=${next}`} replace />
|
||||
}
|
||||
const BrandIcon = brand.icon
|
||||
|
||||
return (
|
||||
<div
|
||||
data-theme={theme}
|
||||
@@ -289,14 +290,20 @@ export function AppShell({
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
<AppbarTitle>{title}</AppbarTitle>
|
||||
<div className="relative ml-6 hidden md:block">
|
||||
<Search className="pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground" />
|
||||
<Input
|
||||
data-action="appbar-search"
|
||||
placeholder="Search…"
|
||||
className="h-9 w-80 pl-8"
|
||||
/>
|
||||
</div>
|
||||
{/* Honest search affordance: the template has no search index, so
|
||||
this routes to the Assistant — the app's real natural-language
|
||||
surface — rather than being a dead <input>. Forks with a search
|
||||
route should point this at it (and add a ⌘K palette hint). */}
|
||||
<button
|
||||
type="button"
|
||||
data-action="appbar-search"
|
||||
onClick={() => navigate("/assistant")}
|
||||
className="ml-6 hidden h-9 w-80 items-center gap-2 rounded-md border bg-background px-2.5 text-left text-sm text-muted-foreground transition hover:bg-accent/40 md:flex"
|
||||
title="Ask the assistant — it can answer questions and drive the app."
|
||||
>
|
||||
<Search className="size-4" />
|
||||
<span className="flex-1 truncate">Ask the assistant…</span>
|
||||
</button>
|
||||
<AppbarSpacer />
|
||||
<AppbarActions>
|
||||
<Button
|
||||
@@ -459,10 +466,6 @@ function NotificationsBell() {
|
||||
const unread = unreadCount(items)
|
||||
const navigate = useNavigate()
|
||||
|
||||
useEffect(() => {
|
||||
seedIfEmpty()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Popover>
|
||||
<PopoverTrigger
|
||||
|
||||
Reference in New Issue
Block a user