SkateSpots/src/components/ErrorState.tsx
SkateSpots 087f490acc refactor(layout): hoist Layout into the router so the chrome mounts once
- AppRouter wraps every route (except /amber-callback and the 404) in ONE
  <Route element={<Layout/>}> with an <Outlet/>; the chrome now persists across
  navigation instead of being torn down and rebuilt on every page.
- Layout: children optional (falls back to <Outlet/>); the map is full-bleed via
  a route check (isFullBleed) instead of a per-page noPadding prop.
- All 59 pages + CraftProfileForm/ErrorState/NewsletterIssueView now render
  fragments instead of <Layout> (structure-preserving), imports dropped.

Removes the whole remount class: chrome no longer re-runs its hooks per
navigation, Sasquatch chat state survives navigation, and the header can't
re-open Amber on every page change.
2026-09-27 13:41:42 +07:00

54 lines
1.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { AlertTriangle, RefreshCw } from 'lucide-react';
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';
/**
* Shared failure state for data-driven pages.
*
* Previously these pages rendered "404 Not Found" (or an empty state) when a
* relay query failed, which told users the content had been deleted — false and
* unactionable. This keeps the failure honest and gives a retry path.
*/
export function ErrorState({
title = 'Couldn’t load this',
description = 'The network request failed. Check your connection and try again.',
onRetry,
className,
}: {
title?: string;
description?: string;
onRetry?: () => void;
className?: string;
}) {
return (
<Card className={cn('border-2 border-dashed border-red-500/30 bg-black/50', className)}>
<CardContent className="py-12 text-center">
<AlertTriangle className="mx-auto mb-4 h-12 w-12 text-red-500/60" />
<p className="mb-1 font-bold uppercase tracking-wide text-white">{title}</p>
<p className="mx-auto mb-5 max-w-sm text-sm text-gray-400">{description}</p>
{onRetry && (
<Button onClick={onRetry} className="gap-2 bg-white/10 font-bold uppercase hover:bg-white/20">
<RefreshCw className="h-4 w-4" />
Try again
</Button>
)}
</CardContent>
</Card>
);
}
/** Full-page failure state (with app chrome) for route-level data errors. */
export function PageError(props: {
title?: string;
description?: string;
onRetry?: () => void;
}) {
return (
<>
<div className="container mx-auto max-w-4xl px-4 py-8">
<ErrorState {...props} />
</div>
</>
);
}