refactor: widen profile to max-w-7xl and remove avatar from header

This commit is contained in:
2026-04-26 11:03:37 -05:00
parent 882827dfa3
commit ae22a65a9f
3 changed files with 122 additions and 105 deletions
+29 -20
View File
@@ -22,28 +22,37 @@ interface ProfilePageClientProps {
export function ProfilePageClient({ profile, recentContributions }: ProfilePageClientProps) { export function ProfilePageClient({ profile, recentContributions }: ProfilePageClientProps) {
return ( return (
<div className="max-w-2xl mx-auto px-4 py-8 space-y-8"> <div className="w-full flex flex-col gap-8 pb-16">
<ProfileHeader <div className="px-4 md:px-[10svw]">
name={profile.name} <div className="max-w-7xl mx-auto">
image={profile.image} <ProfileHeader
role={profile.role} name={profile.name}
verified={profile.verified} role={profile.role}
createdAt={profile.createdAt} verified={profile.verified}
/> createdAt={profile.createdAt}
/>
</div>
</div>
<StatsRow <div className="px-4 md:px-[10svw]">
contributions={profile.contributions} <div className="max-w-7xl mx-auto">
verifiedEntries={profile.verifiedEntries} <StatsRow
reputation={profile.reputation} contributions={profile.contributions}
/> verifiedEntries={profile.verifiedEntries}
reputation={profile.reputation}
/>
</div>
</div>
<div> <div className="px-4 md:px-[10svw]">
<h2 className="text-lg font-semibold mb-4">Recent Contributions</h2> <div className="max-w-7xl mx-auto">
<ContributionList <h2 className="text-lg font-semibold mb-4">Recent Contributions</h2>
entries={recentContributions} <ContributionList
showViewAll entries={recentContributions}
totalCount={profile.contributions} showViewAll
/> totalCount={profile.contributions}
/>
</div>
</div> </div>
</div> </div>
) )
+66 -50
View File
@@ -84,61 +84,77 @@ export default function ProfilePage() {
] ]
return ( return (
<div className="max-w-3xl mx-auto px-4 py-8 space-y-6"> <div className="w-full flex flex-col gap-8 pb-16">
<ProfileHeader {/* Profile header section */}
name={profile.name} <motion.div initial={{ opacity: 0, y: 12 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.4 }} className="px-4 md:px-[10svw]">
image={profile.image} <div className="max-w-7xl mx-auto">
role={profile.role} <ProfileHeader
verified={profile.verified} name={profile.name}
createdAt={profile.createdAt} email={profile.email}
/> role={profile.role}
verified={profile.verified}
createdAt={profile.createdAt}
/>
</div>
</motion.div>
<StatsRow {/* Stats section */}
contributions={profile.contributions} <motion.div initial={{ opacity: 0, y: 12 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.4, delay: 0.1 }} className="px-4 md:px-[10svw]">
verifiedEntries={profile.verifiedEntries} <div className="max-w-7xl mx-auto">
reputation={profile.reputation} <StatsRow
/> contributions={profile.contributions}
verifiedEntries={profile.verifiedEntries}
reputation={profile.reputation}
/>
</div>
</motion.div>
{/* Tabs */} {/* Tabs + content section */}
<div className="flex gap-1 border-b border-border"> <motion.div initial={{ opacity: 0, y: 12 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.4, delay: 0.15 }} className="px-4 md:px-[10svw]">
{tabs.map((tab) => ( <div className="max-w-7xl mx-auto">
<button {/* Tabs */}
key={tab.id} <div className="flex gap-1 border-b border-border">
onClick={() => setActiveTab(tab.id)} {tabs.map((tab) => (
className={`flex items-center gap-2 px-4 py-2 text-sm font-medium transition-colors border-b-2 -mb-px ${ <button
activeTab === tab.id key={tab.id}
? "border-primary text-primary" onClick={() => setActiveTab(tab.id)}
: "border-transparent text-text/50 hover:text-text/70" className={`flex items-center gap-2 px-4 py-2 text-sm font-medium transition-colors border-b-2 -mb-px ${
}`} activeTab === tab.id
? "border-primary text-primary"
: "border-transparent text-text/50 hover:text-text/70"
}`}
>
<tab.icon className="h-4 w-4" />
{tab.label}
</button>
))}
</div>
{/* Tab Content */}
<motion.div
key={activeTab}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.2 }}
className="pt-6"
> >
<tab.icon className="h-4 w-4" /> {activeTab === "overview" && (
{tab.label} <div>
</button> <h2 className="text-lg font-semibold mb-4">Recent Contributions</h2>
))} <ContributionList entries={contributions} />
</div> </div>
)}
{/* Tab Content */} {activeTab === "saved" && <SavedGamesGrid />}
<motion.div
key={activeTab}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.2 }}
>
{activeTab === "overview" && (
<div>
<h2 className="text-lg font-semibold mb-4">Recent Contributions</h2>
<ContributionList entries={contributions} />
</div>
)}
{activeTab === "saved" && <SavedGamesGrid />} {activeTab === "settings" && (
<div className="text-center py-12 text-text/40">
{activeTab === "settings" && ( <Settings className="h-8 w-8 mx-auto mb-2" />
<div className="text-center py-12 text-text/40"> <p>Account settings will appear here</p>
<Settings className="h-8 w-8 mx-auto mb-2" /> </div>
<p>Account settings will appear here</p> )}
</div> </motion.div>
)} </div>
</motion.div> </motion.div>
</div> </div>
) )
+27 -35
View File
@@ -1,24 +1,23 @@
"use client" "use client"
import Image from "next/image" import { Shield, Crown, CheckCircle, Mail } from "lucide-react"
import { User, Shield, Crown, CheckCircle } from "lucide-react"
import { motion } from "motion/react" import { motion } from "motion/react"
interface ProfileHeaderProps { interface ProfileHeaderProps {
name: string name: string
image: string | null email?: string
role: string | null role: string | null
verified: boolean verified: boolean
createdAt: string createdAt: string
} }
const roleConfig: Record<string, { label: string; color: string; icon: typeof User }> = { const roleConfig: Record<string, { label: string; color: string; icon: typeof Crown }> = {
admin: { label: "Admin", color: "bg-yellow-500/20 text-yellow-400 border-yellow-500/30", icon: Crown }, admin: { label: "Admin", color: "bg-yellow-500/20 text-yellow-400 border-yellow-500/30", icon: Crown },
contributor: { label: "Contributor", color: "bg-blue-500/20 text-blue-400 border-blue-500/30", icon: Shield }, contributor: { label: "Contributor", color: "bg-blue-500/20 text-blue-400 border-blue-500/30", icon: Shield },
user: { label: "Member", color: "bg-text/10 text-text/60 border-text/20", icon: User }, user: { label: "Member", color: "bg-text/10 text-text/60 border-text/20", icon: Shield },
} }
export function ProfileHeader({ name, image, role, verified, createdAt }: ProfileHeaderProps) { export function ProfileHeader({ name, email, role, verified, createdAt }: ProfileHeaderProps) {
const config = roleConfig[role || "user"] || roleConfig.user const config = roleConfig[role || "user"] || roleConfig.user
const RoleIcon = config.icon const RoleIcon = config.icon
@@ -31,39 +30,32 @@ export function ProfileHeader({ name, image, role, verified, createdAt }: Profil
<motion.div <motion.div
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
className="flex flex-col sm:flex-row items-center sm:items-start gap-4 sm:gap-6" className="flex flex-col gap-2"
> >
<div className="relative shrink-0"> <div className="flex flex-wrap items-center gap-3">
{image ? ( <h1 className="text-2xl font-bold">{name}</h1>
<Image <span className={`inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium border ${config.color}`}>
src={image} <RoleIcon className="h-3 w-3" />
alt={name} {config.label}
width={96} </span>
height={96}
unoptimized
className="h-24 w-24 rounded-full border-2 border-border"
/>
) : (
<div className="h-24 w-24 rounded-full bg-secondary/50 border-2 border-border flex items-center justify-center">
<User className="h-10 w-10 text-text/40" />
</div>
)}
{verified && ( {verified && (
<div className="absolute -bottom-1 -right-1 h-6 w-6 rounded-full bg-green-500 border-2 border-background flex items-center justify-center"> <span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium bg-green-500/20 text-green-400 border border-green-500/30">
<CheckCircle className="h-4 w-4 text-white" /> <CheckCircle className="h-3 w-3" />
</div> Verified
</span>
)} )}
</div> </div>
<div className="flex items-center gap-3 text-sm text-text/50">
<div className="text-center sm:text-left"> <span>Member since {joinDate}</span>
<div className="flex flex-col sm:flex-row items-center gap-2"> {email && (
<h1 className="text-2xl font-bold">{name}</h1> <>
<span className={`inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium border ${config.color}`}> <span>&middot;</span>
<RoleIcon className="h-3 w-3" /> <span className="flex items-center gap-1">
{config.label} <Mail className="h-3 w-3" />
</span> {email}
</div> </span>
<p className="text-sm text-text/50 mt-1">Member since {joinDate}</p> </>
)}
</div> </div>
</motion.div> </motion.div>
) )