|
1 | 1 | "use client"; |
2 | 2 |
|
3 | | -import { ArrowLeftIcon, LockIcon, MagnifyingGlassIcon, WarningCircleIcon } from "@phosphor-icons/react"; |
| 3 | +import { ArrowLeftIcon, HouseIcon, LockIcon, MagnifyingGlassIcon, WarningCircleIcon } from "@phosphor-icons/react"; |
| 4 | +import Link from "next/link"; |
4 | 5 | import { useRouter } from "next/navigation"; |
5 | 6 | import { Button } from "@/components/ui/button"; |
| 7 | +import { Card, CardContent } from "@/components/ui/card"; |
| 8 | +import { cn } from "@/lib/utils"; |
6 | 9 |
|
7 | 10 | type WebsiteErrorStateProps = { |
8 | 11 | error: unknown; |
@@ -106,27 +109,12 @@ export function WebsiteErrorState({ |
106 | 109 | const getIcon = () => { |
107 | 110 | switch (type) { |
108 | 111 | case "not_found": |
109 | | - return ( |
110 | | - <MagnifyingGlassIcon |
111 | | - className="size-16 text-primary" |
112 | | - weight="duotone" |
113 | | - /> |
114 | | - ); |
| 112 | + return MagnifyingGlassIcon; |
115 | 113 | case "unauthorized": |
116 | 114 | case "forbidden": |
117 | | - return ( |
118 | | - <LockIcon |
119 | | - className="size-16 text-orange-500 dark:text-orange-400" |
120 | | - weight="duotone" |
121 | | - /> |
122 | | - ); |
| 115 | + return LockIcon; |
123 | 116 | default: |
124 | | - return ( |
125 | | - <WarningCircleIcon |
126 | | - className="size-16 text-destructive" |
127 | | - weight="duotone" |
128 | | - /> |
129 | | - ); |
| 117 | + return WarningCircleIcon; |
130 | 118 | } |
131 | 119 | }; |
132 | 120 |
|
@@ -166,12 +154,14 @@ export function WebsiteErrorState({ |
166 | 154 | if (type === "not_found") { |
167 | 155 | return ( |
168 | 156 | <Button |
169 | | - onClick={() => router.push(isDemoRoute ? "/" : "/websites")} |
170 | | - variant="default" |
171 | | - size="lg" |
| 157 | + asChild |
172 | 158 | className="bg-primary hover:bg-primary/90" |
| 159 | + variant="default" |
173 | 160 | > |
174 | | - {isDemoRoute ? "Go to Homepage" : "Back to Websites"} |
| 161 | + <Link href={isDemoRoute ? "/" : "/websites"}> |
| 162 | + <HouseIcon className="mr-2 size-4" weight="duotone" /> |
| 163 | + Back to Websites |
| 164 | + </Link> |
175 | 165 | </Button> |
176 | 166 | ); |
177 | 167 | } |
@@ -247,50 +237,52 @@ export function WebsiteErrorState({ |
247 | 237 | ); |
248 | 238 | }; |
249 | 239 |
|
| 240 | + const IconComponent = getIcon(); |
| 241 | + const actions = renderActions(); |
| 242 | + |
250 | 243 | return ( |
251 | | - <div className="flex h-screen flex-col items-center justify-center bg-background p-4"> |
252 | | - <div className="flex w-full max-w-md flex-col items-center"> |
253 | | - <div className="mb-6 flex items-center justify-center"> |
254 | | - <div className="relative"> |
255 | | - {getIcon()} |
256 | | - <div className="-z-10 absolute inset-0 rounded-full bg-primary/10 blur-2xl" /> |
| 244 | + <div className="flex min-h-full flex-col items-center justify-center p-4 sm:p-6 lg:p-8"> |
| 245 | + <Card className="flex w-full max-w-md flex-1 flex-col items-center justify-center rounded border-none bg-transparent shadow-none"> |
| 246 | + <CardContent className="flex flex-col items-center justify-center text-center px-6 sm:px-8 lg:px-12 py-12 sm:py-14"> |
| 247 | + <div |
| 248 | + aria-hidden="true" |
| 249 | + className={cn( |
| 250 | + "flex size-12 items-center justify-center rounded-2xl", |
| 251 | + type === "not_found" && "bg-accent", |
| 252 | + (type === "unauthorized" || type === "forbidden") && "bg-orange-500/10", |
| 253 | + type === "unknown" && "bg-destructive/10" |
| 254 | + )} |
| 255 | + role="img" |
| 256 | + > |
| 257 | + <IconComponent |
| 258 | + aria-hidden="true" |
| 259 | + className={cn( |
| 260 | + "size-6", |
| 261 | + type === "not_found" && "text-muted-foreground", |
| 262 | + (type === "unauthorized" || type === "forbidden") && "text-orange-500 dark:text-orange-400", |
| 263 | + type === "unknown" && "text-destructive" |
| 264 | + )} |
| 265 | + size={24} |
| 266 | + weight="fill" |
| 267 | + /> |
257 | 268 | </div> |
258 | | - </div> |
259 | | - |
260 | | - <div className="mb-4 flex items-baseline font-mono"> |
261 | | - {getErrorNumber() |
262 | | - .split("") |
263 | | - .map((digit, i) => ( |
264 | | - <span |
265 | | - key={i} |
266 | | - className="font-bold text-8xl text-primary md:text-9xl" |
267 | | - > |
268 | | - {digit} |
269 | | - </span> |
270 | | - ))} |
271 | | - </div> |
272 | | - |
273 | | - <div className="mb-4 h-px w-16 bg-border" /> |
274 | | - |
275 | | - <h1 className="mb-2 text-center font-bold text-2xl md:text-3xl"> |
276 | | - {getTitle()} |
277 | | - </h1> |
278 | | - |
279 | | - <p className="mb-8 text-center text-muted-foreground text-balance"> |
280 | | - {getDescription()} |
281 | | - </p> |
282 | | - |
283 | | - {renderActions()} |
284 | | - </div> |
285 | 269 |
|
286 | | - <div className="absolute bottom-8 rounded-md border border-accent bg-accent/50 px-4 py-2 font-mono text-muted-foreground text-xs"> |
287 | | - <code>{getErrorCode()}</code> |
288 | | - </div> |
| 270 | + <div className="mt-6 space-y-4 max-w-sm"> |
| 271 | + <h1 className="font-semibold text-foreground text-lg"> |
| 272 | + {getTitle()} |
| 273 | + </h1> |
| 274 | + <p className="text-muted-foreground text-sm leading-relaxed text-balance"> |
| 275 | + {getDescription()} |
| 276 | + </p> |
| 277 | + </div> |
289 | 278 |
|
290 | | - <div className="pointer-events-none absolute inset-0 overflow-hidden opacity-5"> |
291 | | - <div className="-right-24 -top-24 absolute h-96 w-96 rounded-full border-8 border-primary border-dashed" /> |
292 | | - <div className="-left-24 -bottom-24 absolute h-96 w-96 rounded-full border-8 border-primary border-dashed" /> |
293 | | - </div> |
| 279 | + {actions && ( |
| 280 | + <div className="mt-6 flex flex-col items-stretch justify-center gap-3 sm:flex-row sm:items-center"> |
| 281 | + {actions} |
| 282 | + </div> |
| 283 | + )} |
| 284 | + </CardContent> |
| 285 | + </Card> |
294 | 286 | </div> |
295 | 287 | ); |
296 | 288 | } |
|
0 commit comments