/**
* ProfileHeader Component
*
* Enhanced hero section of user profile with cover image, avatar, badges, and action buttons
* Features prominent photo contribution button and verified badges display
*/
'use client';
import { useState, useEffect } from 'react';
import Image from 'next/image';
import { Button } from '@/components/ui/button';
import { FollowButton } from './FollowButton';
import { SubmitRidingPhoto } from './SubmitRidingPhoto';
import { VerifiedBadges } from './VerifiedBadges';
import { PartyAffiliationPill } from './PartyAffiliationPill';
import {
MapPin,
Link as LinkIcon,
Calendar,
MessageCircle,
Settings,
Camera,
} from 'lucide-react';
import { useAuth } from '@/contexts/AuthContext';
import Link from 'next/link';
import { formatDistanceToNow } from 'date-fns';
import { cn } from '@canadagpt/design-system';
interface RidingPhoto {
id: string;
riding_name: string;
image_url: string;
submitter_username: string | null;
caption: string | null;
}
interface ProfileHeaderProps {
profile: {
id: string;
username: string | null;
display_name: string | null;
avatar_url: string | null;
bio: string | null;
location: string | null;
website_url: string | null;
cover_image_url: string | null;
riding_name?: string | null;
riding_id?: string | null;
created_at: string;
// Verified badge fields
is_verified_mp?: boolean;
is_admin?: boolean;
is_beta_tester?: boolean;
// Party affiliation fields
party_affiliation?: string | null;
party_affiliation_visibility?: 'public' | 'followers' | 'private';
};
isFollowing: boolean;
isOwnProfile: boolean;
canViewPartyAffiliation?: boolean;
onFollowChange?: (isFollowing: boolean) => void;
}
export function ProfileHeader({
profile,
isFollowing,
isOwnProfile,
canViewPartyAffiliation = true,
onFollowChange,
}: ProfileHeaderProps) {
const { user } = useAuth();
const [localIsFollowing, setLocalIsFollowing] = useState(isFollowing);
const [ridingPhoto, setRidingPhoto] = useState<RidingPhoto | null>(null);
const [showSubmitPhoto, setShowSubmitPhoto] = useState(false);
const handleFollowChange = (newIsFollowing: boolean) => {
setLocalIsFollowing(newIsFollowing);
onFollowChange?.(newIsFollowing);
};
// Fetch riding-based cover photo
useEffect(() => {
async function fetchRidingPhoto() {
if (!profile.riding_name) return;
try {
const response = await fetch(
`/api/riding-photos/${encodeURIComponent(profile.riding_name)}`
);
if (response.ok) {
const data = await response.json();
if (data.photo) {
setRidingPhoto(data.photo);
}
}
} catch (error) {
console.error('Error fetching riding photo:', error);
}
}
fetchRidingPhoto();
}, [profile.riding_name]);
const joinedDate = formatDistanceToNow(new Date(profile.created_at), {
addSuffix: true,
});
// Determine which cover image to show
const coverImageUrl = ridingPhoto?.image_url || profile.cover_image_url;
const hasNoCoverPhoto = !coverImageUrl;
return (
<div className="w-full">
{/* Cover Image - Enhanced with responsive heights */}
<div className="relative h-44 sm:h-56 md:h-72 lg:h-80 bg-gradient-to-br from-blue-600 via-purple-600 to-indigo-700">
{coverImageUrl ? (
<Image
src={coverImageUrl}
alt={ridingPhoto ? `${profile.riding_name}` : 'Cover'}
fill
className="object-cover"
priority
/>
) : (
// Enhanced gradient with riding name overlay
<div className="absolute inset-0 flex items-center justify-center bg-gradient-to-br from-blue-600 via-purple-600 to-indigo-700">
{profile.riding_name && (
<div className="text-center px-4">
<span className="text-white/20 text-3xl sm:text-4xl md:text-5xl lg:text-6xl font-bold">
{profile.riding_name}
</span>
{hasNoCoverPhoto && (
<p className="text-white/40 text-sm mt-2">
Be the first to add a photo!
</p>
)}
</div>
)}
</div>
)}
{/* Gradient overlay for better text readability */}
<div className="absolute inset-0 bg-gradient-to-t from-black/40 via-transparent to-transparent" />
{/* Photographer credit for riding photos */}
{ridingPhoto?.submitter_username && (
<div className="absolute bottom-3 left-3 sm:bottom-4 sm:left-4 bg-black/60 backdrop-blur-sm text-white/90 text-xs px-2.5 py-1.5 rounded-md">
Photo by{' '}
<Link
href={`/users/${ridingPhoto.submitter_username}`}
className="text-white hover:underline font-medium"
>
@{ridingPhoto.submitter_username}
</Link>
</div>
)}
{/* Prominent Contribute Photo button - own profile only with riding */}
{isOwnProfile && profile.riding_name && (
<button
onClick={() => setShowSubmitPhoto(true)}
className={cn(
'absolute bottom-4 right-4 sm:bottom-6 sm:right-6',
'flex items-center gap-2',
'px-4 py-2.5 sm:px-5 sm:py-3',
'bg-black/60 hover:bg-black/80',
'text-white text-sm font-medium',
'rounded-lg backdrop-blur-sm',
'transition-all duration-200',
'group shadow-lg hover:shadow-xl'
)}
>
<Camera className="w-5 h-5 group-hover:scale-110 transition-transform" />
<span className="hidden sm:inline">Contribute Photo</span>
<span className="sm:hidden">Photo</span>
</button>
)}
</div>
{/* Submit Riding Photo Modal */}
{profile.riding_name && (
<SubmitRidingPhoto
ridingName={profile.riding_name}
ridingId={profile.riding_id || undefined}
isOpen={showSubmitPhoto}
onClose={() => setShowSubmitPhoto(false)}
onSuccess={() => {
// Could refresh riding photo here
}}
/>
)}
{/* Profile Info Section */}
<div className="px-4 md:px-6 max-w-5xl mx-auto">
<div className="relative flex flex-col gap-4 pb-4 md:flex-row md:items-end md:justify-between">
{/* Avatar - Enhanced responsive sizing */}
<div className="relative -mt-16 sm:-mt-20 md:-mt-24">
<div className="relative h-24 w-24 sm:h-32 sm:w-32 md:h-40 md:w-40 rounded-full border-4 border-background bg-muted shadow-xl">
{profile.avatar_url ? (
<Image
src={profile.avatar_url}
alt={profile.display_name || 'User'}
fill
className="rounded-full object-cover"
/>
) : (
<div className="flex h-full w-full items-center justify-center rounded-full bg-gradient-to-br from-blue-500 to-purple-600 text-3xl sm:text-4xl md:text-5xl font-bold text-white">
{profile.display_name?.[0]?.toUpperCase() || '?'}
</div>
)}
</div>
</div>
{/* Action Buttons */}
<div className="flex gap-2 mt-2 md:mt-0">
{isOwnProfile ? (
<Link href="/settings">
<Button variant="outline" size="default">
<Settings className="mr-2 h-4 w-4" />
Edit profile
</Button>
</Link>
) : (
<>
<FollowButton
targetUserId={profile.id}
targetUsername={profile.username || profile.id}
initialIsFollowing={localIsFollowing}
onFollowChange={handleFollowChange}
/>
<Link href={`/messages?user=${profile.username || profile.id}`}>
<Button variant="outline">
<MessageCircle className="mr-2 h-4 w-4" />
Message
</Button>
</Link>
</>
)}
</div>
</div>
{/* Name, Badges, and Party Affiliation */}
<div className="mt-2">
<div className="flex flex-wrap items-center gap-2">
<h1 className="text-2xl sm:text-3xl font-bold">
{profile.display_name || 'Anonymous User'}
</h1>
<VerifiedBadges
isVerifiedMp={profile.is_verified_mp}
isAdmin={profile.is_admin}
isBetaTester={profile.is_beta_tester}
/>
<PartyAffiliationPill
party={profile.party_affiliation || null}
visibility={profile.party_affiliation_visibility || 'public'}
canView={canViewPartyAffiliation}
/>
</div>
{profile.username && (
<p className="text-sm sm:text-base text-muted-foreground mt-0.5">
@{profile.username}
</p>
)}
</div>
{/* Bio */}
{profile.bio && (
<p className="mt-3 whitespace-pre-wrap text-sm sm:text-base leading-relaxed">
{profile.bio}
</p>
)}
{/* Metadata Row */}
<div className="mt-4 flex flex-wrap gap-x-4 gap-y-2 text-sm text-muted-foreground">
{profile.riding_name && (
<div className="flex items-center gap-1.5">
<MapPin className="h-4 w-4 flex-shrink-0" />
<span className="font-medium">{profile.riding_name}</span>
</div>
)}
{profile.location && !profile.riding_name && (
<div className="flex items-center gap-1.5">
<MapPin className="h-4 w-4 flex-shrink-0" />
<span>{profile.location}</span>
</div>
)}
{profile.website_url && (
<div className="flex items-center gap-1.5">
<LinkIcon className="h-4 w-4 flex-shrink-0" />
<a
href={profile.website_url}
target="_blank"
rel="noopener noreferrer"
className="text-blue-600 hover:underline dark:text-blue-400"
>
{(() => {
try {
return new URL(profile.website_url).hostname;
} catch {
return profile.website_url;
}
})()}
</a>
</div>
)}
<div className="flex items-center gap-1.5">
<Calendar className="h-4 w-4 flex-shrink-0" />
<span>Joined {joinedDate}</span>
</div>
</div>
</div>
</div>
);
}