Add playful 404 page and enhanced empty states
This commit is contained in:
parent
45608bfdfb
commit
16828b65b0
2 changed files with 124 additions and 34 deletions
|
|
@ -1,43 +1,127 @@
|
|||
"use client"
|
||||
|
||||
import Link from "next/link"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Home, ArrowLeft, Search } from "lucide-react"
|
||||
import { Home, Search, Shuffle, ArrowLeft, Music, Disc3 } from "lucide-react"
|
||||
import { useState, useEffect } from "react"
|
||||
|
||||
const GOOSE_QUOTES = [
|
||||
"Looks like this page flew the coop!",
|
||||
"Honk if you're lost too!",
|
||||
"This page has migrated to parts unknown.",
|
||||
"The jam you seek is not in this location.",
|
||||
"404: Page not found. But hey, at least the vibes are good.",
|
||||
"This URL took a wrong turn at Tumble.",
|
||||
"Flodown? More like FlowGONE.",
|
||||
"Seems this page is still in the Dripfield.",
|
||||
"The set break got a little too long...",
|
||||
"Whoops! Someone put this page in the wrong set.",
|
||||
]
|
||||
|
||||
const SONG_SUGGESTIONS = [
|
||||
{ title: "Tumble", slug: "tumble" },
|
||||
{ title: "Arcadia", slug: "arcadia" },
|
||||
{ title: "Hungersite", slug: "hungersite" },
|
||||
{ title: "Atlas Dogs", slug: "atlas-dogs" },
|
||||
{ title: "Rockdale", slug: "rockdale" },
|
||||
]
|
||||
|
||||
export default function NotFound() {
|
||||
const [quote, setQuote] = useState("")
|
||||
const [suggestion, setSuggestion] = useState(SONG_SUGGESTIONS[0])
|
||||
const [isSpinning, setIsSpinning] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
// Random quote on mount
|
||||
setQuote(GOOSE_QUOTES[Math.floor(Math.random() * GOOSE_QUOTES.length)])
|
||||
setSuggestion(SONG_SUGGESTIONS[Math.floor(Math.random() * SONG_SUGGESTIONS.length)])
|
||||
}, [])
|
||||
|
||||
const shuffleQuote = () => {
|
||||
setIsSpinning(true)
|
||||
setTimeout(() => {
|
||||
setQuote(GOOSE_QUOTES[Math.floor(Math.random() * GOOSE_QUOTES.length)])
|
||||
setSuggestion(SONG_SUGGESTIONS[Math.floor(Math.random() * SONG_SUGGESTIONS.length)])
|
||||
setIsSpinning(false)
|
||||
}, 300)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-[60vh] flex flex-col items-center justify-center text-center px-4">
|
||||
<div className="space-y-6 max-w-md">
|
||||
<div className="text-8xl font-bold text-muted-foreground/30">404</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<h1 className="text-2xl font-bold">Page Not Found</h1>
|
||||
<p className="text-muted-foreground">
|
||||
The page you're looking for doesn't exist or may have been moved.
|
||||
</p>
|
||||
<div className="flex flex-col items-center justify-center min-h-[70vh] text-center px-4">
|
||||
{/* Animated Goose/Music Icon */}
|
||||
<div className="relative mb-6 group cursor-pointer" onClick={shuffleQuote}>
|
||||
<div className={`transition-transform duration-300 ${isSpinning ? 'rotate-180 scale-110' : ''}`}>
|
||||
<Disc3 className="h-32 w-32 text-primary/20 group-hover:text-primary/40 transition-colors" />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-3 justify-center pt-4">
|
||||
<Button asChild variant="default">
|
||||
<Link href="/">
|
||||
<Home className="mr-2 h-4 w-4" />
|
||||
Go Home
|
||||
</Link>
|
||||
</Button>
|
||||
<Button asChild variant="outline">
|
||||
<Link href="/shows">
|
||||
<Search className="mr-2 h-4 w-4" />
|
||||
Browse Shows
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-muted-foreground pt-4">
|
||||
Looking for a specific show? Try the{" "}
|
||||
<Link href="/shows" className="underline hover:text-foreground">
|
||||
shows page
|
||||
</Link>{" "}
|
||||
or use the search.
|
||||
</p>
|
||||
<span className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-6xl group-hover:scale-110 transition-transform">
|
||||
🪿
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* 404 Header */}
|
||||
<div className="mb-4">
|
||||
<span className="text-8xl font-black bg-gradient-to-r from-primary via-purple-500 to-pink-500 bg-clip-text text-transparent">
|
||||
404
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Playful Quote */}
|
||||
<p className="text-xl font-medium mb-2 max-w-md min-h-[2em] transition-opacity duration-300"
|
||||
style={{ opacity: isSpinning ? 0 : 1 }}>
|
||||
{quote}
|
||||
</p>
|
||||
|
||||
<p className="text-muted-foreground text-sm mb-8">
|
||||
Click the goose for a new message ☝️
|
||||
</p>
|
||||
|
||||
{/* Action Buttons */}
|
||||
<div className="flex flex-wrap gap-3 justify-center mb-8">
|
||||
<Link href="/">
|
||||
<Button size="lg" className="gap-2 font-semibold">
|
||||
<Home className="h-4 w-4" />
|
||||
Back to Safety
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href="/shows">
|
||||
<Button variant="outline" size="lg" className="gap-2">
|
||||
<Search className="h-4 w-4" />
|
||||
Find a Show
|
||||
</Button>
|
||||
</Link>
|
||||
<Button variant="ghost" size="lg" className="gap-2" onClick={shuffleQuote}>
|
||||
<Shuffle className="h-4 w-4" />
|
||||
Shuffle Quote
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Song Suggestion Card */}
|
||||
<div className="bg-muted/50 rounded-xl p-6 max-w-sm border border-border/50">
|
||||
<p className="text-sm text-muted-foreground mb-3">
|
||||
While you're here, maybe check out:
|
||||
</p>
|
||||
<Link
|
||||
href={`/songs/${suggestion.slug}`}
|
||||
className="flex items-center gap-3 p-3 rounded-lg bg-background hover:bg-primary/5 border border-border/50 transition-all hover:border-primary/20 group"
|
||||
>
|
||||
<div className="h-10 w-10 rounded-lg bg-primary/10 flex items-center justify-center group-hover:bg-primary/20 transition-colors">
|
||||
<Music className="h-5 w-5 text-primary" />
|
||||
</div>
|
||||
<div className="text-left">
|
||||
<p className="font-semibold group-hover:text-primary transition-colors">{suggestion.title}</p>
|
||||
<p className="text-xs text-muted-foreground">Random song suggestion</p>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Back Link */}
|
||||
<button
|
||||
onClick={() => window.history.back()}
|
||||
className="mt-8 text-sm text-muted-foreground hover:text-primary transition-colors flex items-center gap-1 group"
|
||||
>
|
||||
<ArrowLeft className="h-3 w-3 group-hover:-translate-x-1 transition-transform" />
|
||||
Take me back where I came from
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -243,7 +243,13 @@ export default async function ShowDetailPage({ params }: { params: Promise<{ slu
|
|||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-muted-foreground text-sm">No setlist data available.</p>
|
||||
<div className="flex flex-col items-center justify-center py-8 text-center">
|
||||
<Music2 className="h-12 w-12 text-muted-foreground/30 mb-4" />
|
||||
<p className="text-muted-foreground font-medium">No Setlist Documented</p>
|
||||
<p className="text-sm text-muted-foreground/70 mt-1 max-w-sm">
|
||||
This show's setlist hasn't been added yet. Early Goose shows (2014-2016) often weren't documented.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue