fix(ux): surface hidden notices, bound report waits, add root boundaries
Independent Staging Quality Gate / validate (push) Successful in 12m8s
Independent Staging Quality Gate / publish (push) Successful in 14m29s

Framework-level UX fixes found while auditing staging (BUG-216..220).

- chat: route 44 previously discarded composer notices to sonner with
  dedupe, so recovery, cancel and archive feedback is actually visible
  (BUG-216)
- chat: anchor stream auto-scroll to bottom proximity and add a
  jump-to-latest control, so reading history is no longer interrupted
  on every token (BUG-218)
- reports: replace the silent 120s poll cutoff with an explicit
  timed-out state, an 8m budget, stepped backoff and an elapsed
  counter (BUG-217)
- reports: pause polling while the tab is hidden, via a shared hook
- app: add root error, global-error and not-found boundaries (BUG-219)
- admin: add antd SSR style extraction and the React 19 render adapter,
  and move admin-only css out of the global stylesheet (BUG-220)
- membership: run bootstrap fetches concurrently and pause payment
  polling while hidden
- build: configure optimizePackageImports

Verified on top of 2d370f2e: tsc, eslint, next build, and the related
frontend contract suites.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-08-17 13:14:15 +08:00
co-authored by Cursor
parent 2d370f2e9d
commit 9c296f1e3f
23 changed files with 1144 additions and 88 deletions
+45
View File
@@ -0,0 +1,45 @@
"use client";
import { useEffect } from "react";
import { TriangleAlert } from "lucide-react";
import Link from "next/link";
import { Button } from "@/components/ui/button";
export default function RootError({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
console.error("app root error", error.digest ?? error.message);
}, [error]);
return (
<main className="flex min-h-svh flex-col items-center justify-center gap-4 px-6 py-12 text-center">
<TriangleAlert aria-hidden="true" className="size-8 text-destructive" />
<div className="flex flex-col items-center gap-3" role="alert">
<h1 className="text-xl font-semibold text-foreground"></h1>
<p className="max-w-md text-sm text-muted-foreground">
</p>
</div>
{error.digest ? (
<p className="max-w-md text-xs text-muted-foreground">
<code className="font-mono">{error.digest}</code>
</p>
) : null}
<div className="mt-2 flex flex-wrap items-center justify-center gap-3">
<Button type="button" onClick={() => reset()}>
</Button>
<Button render={<Link href="/" />} nativeButton={false} variant="outline">
</Button>
</div>
</main>
);
}