Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 26 additions & 37 deletions apps/web/src/app/[username]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import Link from 'next/link'
import { useTheme } from 'next-themes'
import { GitHubCalendar } from 'react-github-calendar'
import { Logo } from '@/components/logo'
import { MdtStatusPage } from '@/components/mdt-status-page'
import { TECH_CATALOG } from '@/components/tech-stack-picker'

type PublicProfile = {
Expand Down Expand Up @@ -230,43 +231,31 @@ export default function PublicProfilePage() {
/* ── Error / 404 state ── */
if (error || !profile) {
return (
<div className="min-h-screen flex flex-col items-center justify-center bg-background relative">
{/* Ambient glow */}
<div className="pointer-events-none fixed inset-0 -z-10">
<div className="absolute top-1/3 left-1/2 -translate-x-1/2 w-[60%] h-[40%] bg-[radial-gradient(ellipse_at_center,hsl(var(--destructive)/0.06),transparent_60%)] blur-3xl" />
</div>

<div className="relative z-10 flex flex-col items-center gap-8 p-8">
<Logo size={36} showText />
<div className="text-center space-y-3">
<h1 className="text-7xl md:text-8xl font-black tracking-tighter text-foreground/10 select-none">
404
</h1>
<p className="text-lg text-muted-foreground font-medium">
This profile doesn&apos;t exist yet.
</p>
<p className="text-sm text-muted-foreground/60 max-w-md">
The username <span className="font-mono text-foreground/50">@{username}</span> hasn&apos;t been claimed.
<br />Create your own developer profile for free.
</p>
</div>
<div className="flex gap-3">
<Link
href="/"
className="inline-flex items-center gap-2 text-sm font-medium px-5 py-2.5 rounded-full bg-primary text-primary-foreground hover:opacity-90 transition-opacity shadow-lg shadow-primary/20"
>
<Sparkles className="h-4 w-4" />
Create Profile
</Link>
<Link
href="/"
className="inline-flex items-center gap-2 text-sm font-medium px-5 py-2.5 rounded-full border border-border hover:bg-muted/50 transition-colors text-muted-foreground hover:text-foreground"
>
Explore Tools
</Link>
</div>
</div>
</div>
<MdtStatusPage
code="404"
kicker="Profile not found"
title="This profile doesn't exist yet."
description={`@${username} hasn't been claimed. Grab the handle and create your own developer profile for free.`}
diagnostics={[
'looking up developer profile…',
`GET /@${username} → 404 NOT_CLAIMED`,
'hint: this handle is available',
]}
>
<Link
href="/login"
className="mdt-btn-grad inline-flex h-12 items-center justify-center rounded-full px-7 text-sm font-medium"
>
<Sparkles className="mr-2 h-4 w-4" />
Create Profile
</Link>
<Link
href="/tools"
className="inline-flex h-12 items-center justify-center rounded-full border border-border/60 bg-background/60 px-7 text-sm font-medium text-foreground transition-all hover:bg-muted hover:scale-[1.03] active:scale-[0.98]"
>
Explore Tools
</Link>
</MdtStatusPage>
)
}

Expand Down
62 changes: 26 additions & 36 deletions apps/web/src/app/[username]/public-profile-client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import { GitHubCalendar } from 'react-github-calendar'
import { toast } from 'sonner'
import { Logo } from '@/components/logo'
import { TECH_CATALOG } from '@/components/tech-stack-picker'
import { MdtStatusPage } from '@/components/mdt-status-page'
import { cn } from '@/lib/utils'

/* ──────────────────────────────────────────────────────────────────────── */
Expand Down Expand Up @@ -788,42 +789,31 @@ export default function PublicProfileClient({ username: usernameParam }: { usern

if (error || !profile) {
return (
<div className="min-h-screen flex flex-col items-center justify-center bg-background relative">
<div className="pointer-events-none fixed inset-0 -z-10">
<div className="absolute top-1/3 left-1/2 -translate-x-1/2 w-[60%] h-[40%] bg-[radial-gradient(ellipse_at_center,hsl(var(--destructive)/0.06),transparent_60%)] blur-3xl" />
</div>
<div className="relative z-10 flex flex-col items-center gap-8 p-8">
<Logo size={36} showText />
<div className="text-center space-y-3">
<h1 className="text-7xl md:text-8xl font-black tracking-tighter text-foreground/10 select-none">
404
</h1>
<p className="text-lg text-muted-foreground font-medium">
This profile doesn&apos;t exist yet.
</p>
<p className="text-sm text-muted-foreground/60 max-w-md">
The username <span className="font-mono text-foreground/50">@{usernameSegment}</span> hasn&apos;t been claimed.
<br />
Create your own developer profile for free.
</p>
</div>
<div className="flex gap-3">
<Link
href="/"
className="inline-flex items-center gap-2 text-sm font-medium px-5 py-2.5 rounded-full bg-primary text-primary-foreground hover:opacity-90 transition-opacity shadow-lg shadow-primary/20 cursor-pointer"
>
<Sparkles className="h-4 w-4" />
Create Profile
</Link>
<Link
href="/"
className="inline-flex items-center gap-2 text-sm font-medium px-5 py-2.5 rounded-full border border-border hover:bg-muted/50 transition-colors text-muted-foreground hover:text-foreground cursor-pointer"
>
Explore Tools
</Link>
</div>
</div>
</div>
<MdtStatusPage
code="404"
kicker="Profile not found"
title="This profile doesn't exist yet."
description={`@${usernameSegment} hasn't been claimed. Grab the handle and create your own developer profile for free.`}
diagnostics={[
'looking up developer profile…',
`GET /@${usernameSegment} → 404 NOT_CLAIMED`,
'hint: this handle is available',
]}
>
<Link
href="/login"
className="mdt-btn-grad inline-flex h-12 items-center justify-center rounded-full px-7 text-sm font-medium"
>
<Sparkles className="mr-2 h-4 w-4" />
Create Profile
</Link>
<Link
href="/tools"
className="inline-flex h-12 items-center justify-center rounded-full border border-border/60 bg-background/60 px-7 text-sm font-medium text-foreground transition-all hover:bg-muted hover:scale-[1.03] active:scale-[0.98]"
>
Explore Tools
</Link>
</MdtStatusPage>
)
}

Expand Down
47 changes: 47 additions & 0 deletions apps/web/src/app/error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
'use client'

import Link from 'next/link'
import { Home, RotateCw } from 'lucide-react'
import { MdtStatusPage } from '@/components/mdt-status-page'

export default function Error({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
return (
<MdtStatusPage
code="500"
kicker="Something broke"
title="Our end hit an unexpected error."
description={
error.digest
? `Something went wrong on our side. Reference: ${error.digest}`
: 'Something went wrong on our side. Try again, or head back home.'
}
diagnostics={[
'unhandled exception caught',
`trace: ${error.digest ?? 'no digest available'}`,
'action: retry or return home',
]}
>
<button
type="button"
onClick={reset}
className="mdt-btn-grad inline-flex h-12 items-center justify-center rounded-full px-7 text-sm font-medium"
>
<RotateCw className="mr-2 h-4 w-4" />
Try again
</button>
<Link
href="/"
className="inline-flex h-12 items-center justify-center rounded-full border border-border/60 bg-background/60 px-7 text-sm font-medium text-foreground transition-all hover:bg-muted hover:scale-[1.03] active:scale-[0.98]"
>
<Home className="mr-2 h-4 w-4" />
Back home
</Link>
</MdtStatusPage>
)
}
42 changes: 42 additions & 0 deletions apps/web/src/app/global-error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
'use client'

import { GeistSans } from 'geist/font/sans'
import { GeistMono } from 'geist/font/mono'
import { RotateCw } from 'lucide-react'
import { MdtStatusPage } from '@/components/mdt-status-page'
import './globals.css'

// Catches errors thrown in the root layout itself — must render its own <html>/<body>.
export default function GlobalError({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
return (
<html lang="en" className={`${GeistSans.variable} ${GeistMono.variable}`}>
<body>
<MdtStatusPage
code="500"
kicker="Something broke"
title="The app failed to load."
description={
error.digest
? `A critical error occurred. Reference: ${error.digest}`
: 'A critical error occurred while loading MyDevTools. Try reloading.'
}
>
<button
type="button"
onClick={reset}
className="mdt-btn-grad inline-flex h-12 items-center justify-center rounded-full px-7 text-sm font-medium"
>
<RotateCw className="mr-2 h-4 w-4" />
Reload
</button>
</MdtStatusPage>
</body>
</html>
)
}
21 changes: 21 additions & 0 deletions apps/web/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -1525,6 +1525,27 @@ html { overflow-x: hidden; }
position: absolute; inset: 0; pointer-events: none; opacity: .28; mix-blend-mode: soft-light;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
/* CRT scanlines — status pages (subtle, matches the boot loader) */
.mdt-status-scanlines {
pointer-events: none; opacity: .5;
background-image: repeating-linear-gradient(
to bottom,
rgba(255, 255, 255, 0.03) 0px,
rgba(255, 255, 255, 0.03) 1px,
transparent 1px,
transparent 3px
);
mask-image: radial-gradient(ellipse 80% 60% at 50% 45%, #000 30%, transparent 100%);
-webkit-mask-image: radial-gradient(ellipse 80% 60% at 50% 45%, #000 30%, transparent 100%);
}
@media (prefers-reduced-motion: no-preference) {
.mdt-status-scanlines { animation: mdt-scanline-roll 8s linear infinite; }
}
@keyframes mdt-scanline-roll {
from { background-position: 0 0; }
to { background-position: 0 6px; }
}

/* content stacks above the backdrop naturally; backdrop sits at z-index:-1.
(No blanket position rule — it would override fixed/sticky children.) */

Expand Down
Loading