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
+12 -3
View File
@@ -22,22 +22,30 @@ 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">
<div className="px-4 md:px-[10svw]">
<div className="max-w-7xl mx-auto">
<ProfileHeader <ProfileHeader
name={profile.name} name={profile.name}
image={profile.image}
role={profile.role} role={profile.role}
verified={profile.verified} verified={profile.verified}
createdAt={profile.createdAt} createdAt={profile.createdAt}
/> />
</div>
</div>
<div className="px-4 md:px-[10svw]">
<div className="max-w-7xl mx-auto">
<StatsRow <StatsRow
contributions={profile.contributions} contributions={profile.contributions}
verifiedEntries={profile.verifiedEntries} verifiedEntries={profile.verifiedEntries}
reputation={profile.reputation} reputation={profile.reputation}
/> />
</div>
</div>
<div> <div className="px-4 md:px-[10svw]">
<div className="max-w-7xl mx-auto">
<h2 className="text-lg font-semibold mb-4">Recent Contributions</h2> <h2 className="text-lg font-semibold mb-4">Recent Contributions</h2>
<ContributionList <ContributionList
entries={recentContributions} entries={recentContributions}
@@ -46,5 +54,6 @@ export function ProfilePageClient({ profile, recentContributions }: ProfilePageC
/> />
</div> </div>
</div> </div>
</div>
) )
} }
+18 -2
View File
@@ -84,21 +84,34 @@ 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">
{/* Profile header section */}
<motion.div initial={{ opacity: 0, y: 12 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.4 }} className="px-4 md:px-[10svw]">
<div className="max-w-7xl mx-auto">
<ProfileHeader <ProfileHeader
name={profile.name} name={profile.name}
image={profile.image} email={profile.email}
role={profile.role} role={profile.role}
verified={profile.verified} verified={profile.verified}
createdAt={profile.createdAt} createdAt={profile.createdAt}
/> />
</div>
</motion.div>
{/* Stats section */}
<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]">
<div className="max-w-7xl mx-auto">
<StatsRow <StatsRow
contributions={profile.contributions} contributions={profile.contributions}
verifiedEntries={profile.verifiedEntries} verifiedEntries={profile.verifiedEntries}
reputation={profile.reputation} reputation={profile.reputation}
/> />
</div>
</motion.div>
{/* Tabs + content section */}
<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]">
<div className="max-w-7xl mx-auto">
{/* Tabs */} {/* Tabs */}
<div className="flex gap-1 border-b border-border"> <div className="flex gap-1 border-b border-border">
{tabs.map((tab) => ( {tabs.map((tab) => (
@@ -123,6 +136,7 @@ export default function ProfilePage() {
initial={{ opacity: 0, y: 10 }} initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.2 }} transition={{ duration: 0.2 }}
className="pt-6"
> >
{activeTab === "overview" && ( {activeTab === "overview" && (
<div> <div>
@@ -141,5 +155,7 @@ export default function ProfilePage() {
)} )}
</motion.div> </motion.div>
</div> </div>
</motion.div>
</div>
) )
} }
+24 -32
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 ? (
<Image
src={image}
alt={name}
width={96}
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 && (
<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">
<CheckCircle className="h-4 w-4 text-white" />
</div>
)}
</div>
<div className="text-center sm:text-left">
<div className="flex flex-col sm:flex-row items-center gap-2">
<h1 className="text-2xl font-bold">{name}</h1> <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 className={`inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium border ${config.color}`}>
<RoleIcon className="h-3 w-3" /> <RoleIcon className="h-3 w-3" />
{config.label} {config.label}
</span> </span>
{verified && (
<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-3 w-3" />
Verified
</span>
)}
</div> </div>
<p className="text-sm text-text/50 mt-1">Member since {joinDate}</p> <div className="flex items-center gap-3 text-sm text-text/50">
<span>Member since {joinDate}</span>
{email && (
<>
<span>&middot;</span>
<span className="flex items-center gap-1">
<Mail className="h-3 w-3" />
{email}
</span>
</>
)}
</div> </div>
</motion.div> </motion.div>
) )