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) {
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
name={profile.name}
image={profile.image}
role={profile.role}
verified={profile.verified}
createdAt={profile.createdAt}
/>
</div>
</div>
<div className="px-4 md:px-[10svw]">
<div className="max-w-7xl mx-auto">
<StatsRow
contributions={profile.contributions}
verifiedEntries={profile.verifiedEntries}
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>
<ContributionList
entries={recentContributions}
@@ -46,5 +54,6 @@ export function ProfilePageClient({ profile, recentContributions }: ProfilePageC
/>
</div>
</div>
</div>
)
}
+18 -2
View File
@@ -84,21 +84,34 @@ export default function ProfilePage() {
]
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
name={profile.name}
image={profile.image}
email={profile.email}
role={profile.role}
verified={profile.verified}
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
contributions={profile.contributions}
verifiedEntries={profile.verifiedEntries}
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 */}
<div className="flex gap-1 border-b border-border">
{tabs.map((tab) => (
@@ -123,6 +136,7 @@ export default function ProfilePage() {
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.2 }}
className="pt-6"
>
{activeTab === "overview" && (
<div>
@@ -141,5 +155,7 @@ export default function ProfilePage() {
)}
</motion.div>
</div>
</motion.div>
</div>
)
}
+24 -32
View File
@@ -1,24 +1,23 @@
"use client"
import Image from "next/image"
import { User, Shield, Crown, CheckCircle } from "lucide-react"
import { Shield, Crown, CheckCircle, Mail } from "lucide-react"
import { motion } from "motion/react"
interface ProfileHeaderProps {
name: string
image: string | null
email?: string
role: string | null
verified: boolean
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 },
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 RoleIcon = config.icon
@@ -31,39 +30,32 @@ export function ProfileHeader({ name, image, role, verified, createdAt }: Profil
<motion.div
initial={{ opacity: 0, y: 20 }}
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">
{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">
<div className="flex flex-wrap items-center gap-3">
<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}`}>
<RoleIcon className="h-3 w-3" />
{config.label}
</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>
<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>
</motion.div>
)