import { useState, useEffect } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { useAuth } from '@/hooks/useAuth'; import { userService, type UserProfile, type UserActivity } from '@/services/userService'; import type { Report, CleanupEvent } from '@/types'; import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet'; import { NotificationSettings, type NotificationPreferences } from '@/components/auth'; import 'leaflet/dist/leaflet.css'; import L from 'leaflet'; // Fix Leaflet default marker icon issue import markerIcon2x from 'leaflet/dist/images/marker-icon-2x.png'; import markerIcon from 'leaflet/dist/images/marker-icon.png'; import markerShadow from 'leaflet/dist/images/marker-shadow.png'; delete (L.Icon.Default.prototype as any)._getIconUrl; L.Icon.Default.mergeOptions({ iconUrl: markerIcon, iconRetinaUrl: markerIcon2x, shadowUrl: markerShadow, }); export function UserProfilePage() { const { userId } = useParams<{ userId: string }>(); const { user: currentUser } = useAuth(); const navigate = useNavigate(); const [profile, setProfile] = useState(null); const [reports, setReports] = useState([]); const [events, setEvents] = useState<{ registered: CleanupEvent[]; organized: CleanupEvent[] }>({ registered: [], organized: [] }); const [activity, setActivity] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [activeTab, setActiveTab] = useState<'overview' | 'reports' | 'events' | 'activity' | 'notifications'>('overview'); const [isEditing, setIsEditing] = useState(false); const [editForm, setEditForm] = useState({ firstName: '', lastName: '', username: '', }); const [notificationPreferences, setNotificationPreferences] = useState({ notificationEmail: true, notificationInApp: true, areasOfInterest: [], }); const isOwnProfile = currentUser?.id === userId; useEffect(() => { if (!userId) { setError('User ID is required'); setLoading(false); return; } loadProfileData(); }, [userId]); const loadProfileData = async () => { if (!userId) return; try { setLoading(true); setError(null); const [profileData, reportsData, eventsData, activityData] = await Promise.all([ userService.getProfile(userId), userService.getUserReports(userId), userService.getUserEvents(userId), userService.getUserActivity(userId), ]); setProfile(profileData); setReports(reportsData); // Handle both array and object response formats if (Array.isArray(eventsData)) { setEvents({ registered: eventsData, organized: [] }); } else { setEvents(eventsData); } setActivity(activityData); setEditForm({ firstName: profileData.firstName || '', lastName: profileData.lastName || '', username: profileData.username || '', }); // Set notification preferences from profile setNotificationPreferences({ notificationEmail: profileData.notificationEmail ?? true, notificationInApp: profileData.notificationInApp ?? true, areasOfInterest: profileData.areasOfInterest || [], }); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to load profile'); } finally { setLoading(false); } }; const handleEditProfile = () => { setIsEditing(true); }; const handleCancelEdit = () => { setIsEditing(false); if (profile) { setEditForm({ firstName: profile.firstName || '', lastName: profile.lastName || '', username: profile.username || '', }); } }; const handleSaveProfile = async () => { if (!userId) return; try { await userService.updateProfile(userId, editForm); setIsEditing(false); await loadProfileData(); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to update profile'); } }; const handleSaveNotificationPreferences = async (preferences: NotificationPreferences) => { if (!userId) return; try { await userService.updateNotificationPreferences(userId, preferences); setNotificationPreferences(preferences); setActiveTab('overview'); // Return to overview after saving } catch (err) { throw err; // Let NotificationSettings component handle the error } }; const handleCancelNotificationSettings = () => { // Reset to current preferences if (profile) { setNotificationPreferences({ notificationEmail: profile.notificationEmail ?? true, notificationInApp: profile.notificationInApp ?? true, areasOfInterest: profile.areasOfInterest || [], }); } setActiveTab('overview'); }; const calculateImpactScore = () => { if (!profile) return 0; // Impact score calculation: reports * 10 + events attended * 20 const reportCount = profile.reportCount || 0; const eventCount = profile.eventCount || 0; return reportCount * 10 + eventCount * 20; }; const formatDate = (date: Date) => { return new Date(date).toLocaleDateString('en-IE', { year: 'numeric', month: 'short', day: 'numeric', }); }; if (loading) { return (

Loading profile...

); } if (error || !profile) { return (

{error || 'Profile not found'}

); } return (
{/* Profile Header */}
{profile.avatarUrl && ( {profile.username} )}
{isEditing ? (
setEditForm({ ...editForm, firstName: e.target.value })} placeholder="First name" className="px-3 py-1 border border-gray-300 rounded-md" /> setEditForm({ ...editForm, lastName: e.target.value })} placeholder="Last name" className="px-3 py-1 border border-gray-300 rounded-md" />
setEditForm({ ...editForm, username: e.target.value })} placeholder="Username" className="px-3 py-1 border border-gray-300 rounded-md w-full" />
) : ( <>

{profile.firstName && profile.lastName ? `${profile.firstName} ${profile.lastName}` : profile.username}

@{profile.username}

)}
{isOwnProfile && (
{isEditing ? ( <> ) : ( )}
)}
{/* Statistics */}

Reports Submitted

{profile.reportCount}

Events Attended

{profile.eventCount}

Impact Score

{calculateImpactScore()}

{/* Tabs */}
{activeTab === 'overview' && (

Contribution Map

{reports.length > 0 && reports[0] ? (
{reports.map((report) => (

{report.litterType}

{formatDate(report.createdAt)}

))}
) : (

No reports submitted yet

)}

Recent Activity

{activity.length > 0 ? (
{activity.slice(0, 5).map((item, index) => (
{item.type === 'report' && (
📍
)} {item.type.startsWith('event') && (
🗓️
)}

{item.title}

{item.description && (

{item.description}

)}

{formatDate(item.timestamp)}

))}
) : (

No activity yet

)}
)} {activeTab === 'reports' && (

Submitted Reports

{reports.length > 0 ? (
{reports.map((report) => (
{report.photoUrls.length > 0 && ( Report )}

{report.litterType}

Quantity: {report.quantity}

{report.description && (

{report.description}

)}

{formatDate(report.createdAt)}

))}
) : (

No reports submitted yet

)}
)} {activeTab === 'events' && (
{/* Registered Events */}

Registered Events

{events.registered.length > 0 ? (
{events.registered.map((event) => (

{event.title}

{event.description}

📍 {event.locationName}

🗓️ {formatDate(event.scheduledDate)}

👥 {event.participantCount} participants

{event.litterCollected && (

♻️ {event.litterCollected} kg collected

)}
{event.status}
))}
) : (

No registered events yet

)}
{/* Organized Events */}

Organized Events

{events.organized.length > 0 ? (
{events.organized.map((event) => (

{event.title}

{event.description}

📍 {event.locationName}

🗓️ {formatDate(event.scheduledDate)}

👥 {event.participantCount} participants

{event.litterCollected && (

♻️ {event.litterCollected} kg collected

)}
{event.status}
))}
) : (

No organized events yet

)}
)} {activeTab === 'activity' && (

Activity Timeline

{activity.length > 0 ? (
{activity.map((item, index) => (
{item.type === 'report' && 📍} {item.type === 'event_created' && ➕} {item.type === 'event_attended' && ✅} {item.type === 'event_completed' && 🎉}

{item.title}

{item.description && (

{item.description}

)} {item.location && (

📍 {item.location.locationName || `${item.location.latitude?.toFixed(4)}, ${item.location.longitude?.toFixed(4)}`}

)}

{formatDate(item.timestamp)}

))}
) : (

No activity yet

)}
)} {activeTab === 'notifications' && isOwnProfile && ( )}
); }