fediversion/frontend/app/welcome/page.tsx
fullsizemalt 60456c4737
Some checks failed
Deploy Fediversion / deploy (push) Failing after 1s
feat(frontend): Enforce strict mode and refactor pages
2025-12-30 22:29:16 -08:00

241 lines
11 KiB
TypeScript

"use client"
import { useState } from "react"
import { motion, AnimatePresence } from "framer-motion"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Card, CardContent, CardFooter, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Switch } from "@/components/ui/switch"
import { PartyPopper, UserCircle, Settings, CheckCircle2, ArrowRight, ArrowLeft } from "lucide-react"
import { useRouter } from "next/navigation"
import { getApiUrl } from "@/lib/api-config"
const STEPS = [
{
id: "intro",
title: "Welcome to Fediversion",
description: "Let's get you set up to discover and track your favorite shows.",
icon: PartyPopper
},
{
id: "profile",
title: "Create Your Profile",
description: "How should we call you in the community?",
icon: UserCircle
},
{
id: "preferences",
title: "Customize Experience",
description: "Choose how you want to interact with the platform.",
icon: Settings
},
{
id: "finish",
title: "You're All Set!",
description: "Ready to dive in?",
icon: CheckCircle2
}
]
export default function WelcomePage() {
const router = useRouter()
const [currentStep, setCurrentStep] = useState(0)
const [loading, setLoading] = useState(false)
// Form State
const [username, setUsername] = useState("")
const [displayName, setDisplayName] = useState("")
const [wikiMode, setWikiMode] = useState(false)
const [publicProfile, setPublicProfile] = useState(true)
const handleNext = async () => {
if (currentStep === STEPS.length - 1) {
router.push("/profile") // Redirect to Dashboard
return
}
// Validation & Saving per step
if (currentStep === 1) {
if (!username) return alert("Username required")
setLoading(true)
try {
const token = localStorage.getItem("token")
const res = await fetch(`${getApiUrl()}/users/me`, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`
},
body: JSON.stringify({
username,
display_name: displayName || username
})
})
if (!res.ok) throw new Error("Failed to save profile")
} catch (err) {
console.error(err)
alert("Failed to save profile. Username might be taken.")
setLoading(false)
return
}
setLoading(false)
} else if (currentStep === 2) {
setLoading(true)
try {
const token = localStorage.getItem("token")
const res = await fetch(`${getApiUrl()}/users/me/preferences`, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`
},
body: JSON.stringify({
wiki_mode: wikiMode,
// If wikiMode is true, hide social stuff
show_comments: !wikiMode,
show_ratings: !wikiMode
})
})
if (!res.ok) throw new Error("Failed to save preferences")
} catch (err) {
console.error(err)
setLoading(false)
return
}
setLoading(false)
}
setCurrentStep(prev => prev + 1)
}
const handleBack = () => {
setCurrentStep(prev => Math.max(0, prev - 1))
}
const StepIcon = STEPS[currentStep].icon
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50/50 dark:bg-zinc-950 p-4">
<Card className="w-full max-w-md shadow-xl border-t-4 border-t-primary">
<CardHeader className="text-center pb-2">
<div className="mx-auto w-12 h-12 bg-primary/10 rounded-full flex items-center justify-center mb-4 text-primary">
<StepIcon className="w-6 h-6" />
</div>
<CardTitle className="text-2xl">{STEPS[currentStep].title}</CardTitle>
<CardDescription>{STEPS[currentStep].description}</CardDescription>
</CardHeader>
<CardContent className="py-6">
<AnimatePresence mode="wait">
<motion.div
key={currentStep}
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.2 }}
>
{currentStep === 0 && (
<div className="text-center space-y-4">
<p className="text-sm text-muted-foreground">
We're glad you're here. This quick setup will help us personalize your experience.
</p>
</div>
)}
{currentStep === 1 && (
<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="username">Username <span className="text-red-500">*</span></Label>
<Input
id="username"
placeholder="e.g. phantastic_fan"
value={username}
onChange={e => setUsername(e.target.value)}
/>
<p className="text-xs text-muted-foreground">Unique handle for mentions and profile URL.</p>
</div>
<div className="space-y-2">
<Label htmlFor="displayName">Display Name</Label>
<Input
id="displayName"
placeholder="e.g. Phish Fan 99"
value={displayName}
onChange={e => setDisplayName(e.target.value)}
/>
</div>
</div>
)}
{currentStep === 2 && (
<div className="space-y-6">
<div className="flex items-center justify-between space-x-2">
<div className="space-y-0.5">
<Label htmlFor="wiki-mode" className="text-base">Wiki Mode</Label>
<p className="text-xs text-muted-foreground">
Hide all social features (comments, ratings, leaderboards). Just data.
</p>
</div>
<Switch
id="wiki-mode"
checked={wikiMode}
onCheckedChange={(checked) => setWikiMode(checked)}
/>
</div>
<div className="flex items-center justify-between space-x-2 opacity-80 pointer-events-none">
<div className="space-y-0.5">
<Label className="text-base">Public Profile</Label>
<p className="text-xs text-muted-foreground">
Allow others to see your stats and attendence.
</p>
</div>
<Switch checked={publicProfile} />
</div>
<p className="text-xs text-center text-muted-foreground pt-4">You can change these later in settings.</p>
</div>
)}
{currentStep === 3 && (
<div className="text-center space-y-4">
<p>Your profile is ready!</p>
<div className="p-4 bg-secondary/50 rounded-lg text-sm">
<p className="font-semibold">@{username}</p>
<p>{wikiMode ? "Wiki Mode: ON" : "Community Mode: ON"}</p>
</div>
</div>
)}
</motion.div>
</AnimatePresence>
</CardContent>
<CardFooter className="flex justify-between">
<Button
variant="ghost"
onClick={handleBack}
disabled={currentStep === 0 || loading}
className={currentStep === 0 ? "invisible" : ""}
>
<ArrowLeft className="w-4 h-4 mr-2" /> Back
</Button>
<Button onClick={handleNext} disabled={loading}>
{loading ? "Saving..." : currentStep === STEPS.length - 1 ? "Get Started" : "Next"}
{currentStep !== STEPS.length - 1 && <ArrowRight className="w-4 h-4 ml-2" />}
</Button>
</CardFooter>
</Card>
{/* Progress Dots */}
<div className="absolute bottom-8 flex gap-2">
{STEPS.map((_, index) => (
<div
key={index}
className={`w-2 h-2 rounded-full transition-colors ${index === currentStep ? "bg-primary" : "bg-primary/20"}`}
/>
))}
</div>
</div>
)
}