mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 22:02:16 +00:00
Promote the register page's submit button to the 44 px tap-target floor (size="lg" + min-h-11 + w-full) so the primary action stays finger-tap reachable on a narrow viewport. Add a new branded not-found.tsx page. The previous implicit fallback was Next.js's bare framework text; this introduces a lighter wrapper than ErrorDetails (404 is not an application error) — Logo, headline, explanatory paragraph, and a single "Back to dashboard" link. The wrapper carries min-h-dvh and safe-area-inset-top so the panel anchors centred under iOS Safari's animated URL bar and stays clear of the notch. Apply the same dvh + safe-area pattern on the root-layout global error boundary and lift its inline button heights to 44 px. Wrap the ErrorDetails panel in a min-h-dvh flex column so server-error pages also centre instead of hugging the notch.
118 lines
3.6 KiB
TypeScript
118 lines
3.6 KiB
TypeScript
"use client";
|
|
|
|
/**
|
|
* Root-level error boundary. Renders when even the root layout itself fails,
|
|
* so it cannot rely on any providers (i18n, query, auth). Keep it static,
|
|
* in English, and self-contained.
|
|
*/
|
|
export default function GlobalError({
|
|
error,
|
|
reset,
|
|
}: {
|
|
error: Error & { digest?: string };
|
|
reset: () => void;
|
|
}) {
|
|
async function handleCopy() {
|
|
// Strip query string — OAuth callback routes carry sensitive tokens.
|
|
const loc = typeof window !== "undefined" ? window.location : null;
|
|
const payload = {
|
|
message: error.message,
|
|
digest: error.digest,
|
|
name: error.name,
|
|
stack: error.stack?.split("\n").slice(0, 10).join("\n"),
|
|
urlPath: loc?.pathname ?? null,
|
|
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : null,
|
|
timestamp: new Date().toISOString(),
|
|
};
|
|
const text = JSON.stringify(payload, null, 2);
|
|
try {
|
|
await navigator.clipboard.writeText(text);
|
|
} catch {
|
|
window.prompt("Copy details", text);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<html lang="en">
|
|
<body
|
|
style={{
|
|
fontFamily: "system-ui, sans-serif",
|
|
backgroundColor: "#1e1f29",
|
|
color: "#f8f8f2",
|
|
margin: 0,
|
|
// v1.4.27 MB6 — `100dvh` follows the dynamic viewport on
|
|
// iOS Safari (URL-bar collapse) so the wrapper still
|
|
// anchors centred when the browser chrome animates in or
|
|
// out. `safe-area-inset-top` keeps the headline out from
|
|
// under the notch / Dynamic Island.
|
|
minHeight: "100dvh",
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
padding:
|
|
"calc(env(safe-area-inset-top, 0px) + 24px) 24px 24px 24px",
|
|
}}
|
|
>
|
|
<div style={{ maxWidth: 560 }}>
|
|
<h1 style={{ fontSize: 20, marginBottom: 12 }}>
|
|
Something went wrong
|
|
</h1>
|
|
<p style={{ color: "#d0d0d0", marginBottom: 16, fontSize: 14 }}>
|
|
A critical error occurred. You can retry or copy the details for
|
|
support.
|
|
</p>
|
|
<pre
|
|
style={{
|
|
background: "#282a36",
|
|
padding: 12,
|
|
borderRadius: 6,
|
|
fontSize: 12,
|
|
overflow: "auto",
|
|
marginBottom: 16,
|
|
border: "1px solid #44475a",
|
|
}}
|
|
>
|
|
{error.message || "Unknown error"}
|
|
</pre>
|
|
<div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
|
|
<button
|
|
onClick={reset}
|
|
style={{
|
|
background: "#bd93f9",
|
|
color: "#282a36",
|
|
border: "none",
|
|
borderRadius: 6,
|
|
// v1.4.27 MB6 — 44 px minimum height matches the
|
|
// app-wide tap-target floor; the larger inline padding
|
|
// keeps the labels comfortably finger-tap-sized.
|
|
minHeight: 44,
|
|
padding: "10px 16px",
|
|
cursor: "pointer",
|
|
fontSize: 14,
|
|
fontWeight: 500,
|
|
}}
|
|
>
|
|
Retry
|
|
</button>
|
|
<button
|
|
onClick={handleCopy}
|
|
style={{
|
|
background: "transparent",
|
|
color: "#f8f8f2",
|
|
border: "1px solid #44475a",
|
|
borderRadius: 6,
|
|
minHeight: 44,
|
|
padding: "10px 16px",
|
|
cursor: "pointer",
|
|
fontSize: 14,
|
|
}}
|
|
>
|
|
Copy details
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
);
|
|
}
|