fix: handle events structure and prevent duplicate keys

- Update events state to match backend response structure (registered/organized)
- Display registered and organized events in separate sections
- Use unique keys for all event mappings (registered-id, organized-id)
- Fix duplicate key errors in activity timeline overview section

This resolves all duplicate React key warnings
This commit is contained in:
2025-11-16 21:48:45 +00:00
parent ad06833842
commit cc7298bc56

View File

@@ -26,7 +26,7 @@ export function UserProfilePage() {
const [profile, setProfile] = useState<UserProfile | null>(null);
const [reports, setReports] = useState<Report[]>([]);
const [events, setEvents] = useState<CleanupEvent[]>([]);
const [events, setEvents] = useState<{ registered: CleanupEvent[]; organized: CleanupEvent[] }>({ registered: [], organized: [] });
const [activity, setActivity] = useState<UserActivity[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
@@ -310,8 +310,8 @@ export function UserProfilePage() {
<h2 className="text-xl font-semibold mb-4">Recent Activity</h2>
{activity.length > 0 ? (
<div className="space-y-3">
{activity.slice(0, 5).map((item) => (
<div key={item.id} className="flex items-start space-x-3 p-3 bg-gray-50 rounded-lg">
{activity.slice(0, 5).map((item, index) => (
<div key={`overview-${item.type}-${item.id}-${index}`} className="flex items-start space-x-3 p-3 bg-gray-50 rounded-lg">
<div className="flex-shrink-0">
{item.type === 'report' && (
<div className="h-10 w-10 bg-blue-100 rounded-full flex items-center justify-center">
@@ -373,49 +373,98 @@ export function UserProfilePage() {
)}
{activeTab === 'events' && (
<div>
<h2 className="text-xl font-semibold mb-4">Events</h2>
{events.length > 0 ? (
<div className="space-y-4">
{events.map((event) => (
<div key={event.id} className="bg-gray-50 rounded-lg p-4">
<div className="flex justify-between items-start">
<div>
<h3 className="font-semibold text-lg">{event.title}</h3>
<p className="text-sm text-gray-600 mt-1">{event.description}</p>
<p className="text-sm text-gray-500 mt-2">
📍 {event.locationName}
</p>
<p className="text-sm text-gray-500">
🗓️ {formatDate(event.scheduledDate)}
</p>
<p className="text-sm text-gray-500">
👥 {event.participantCount} participants
</p>
{event.litterCollected && (
<p className="text-sm text-green-600 font-medium mt-2">
♻️ {event.litterCollected} kg collected
<div className="space-y-6">
{/* Registered Events */}
<div>
<h2 className="text-xl font-semibold mb-4">Registered Events</h2>
{events.registered.length > 0 ? (
<div className="space-y-4">
{events.registered.map((event) => (
<div key={`registered-${event.id}`} className="bg-gray-50 rounded-lg p-4">
<div className="flex justify-between items-start">
<div>
<h3 className="font-semibold text-lg">{event.title}</h3>
<p className="text-sm text-gray-600 mt-1">{event.description}</p>
<p className="text-sm text-gray-500 mt-2">
📍 {event.locationName}
</p>
)}
<p className="text-sm text-gray-500">
🗓️ {formatDate(event.scheduledDate)}
</p>
<p className="text-sm text-gray-500">
👥 {event.participantCount} participants
</p>
{event.litterCollected && (
<p className="text-sm text-green-600 font-medium mt-2">
♻️ {event.litterCollected} kg collected
</p>
)}
</div>
<span
className={`px-3 py-1 rounded-full text-xs font-medium ${
event.status === 'completed'
? 'bg-green-100 text-green-800'
: event.status === 'upcoming'
? 'bg-blue-100 text-blue-800'
: 'bg-gray-100 text-gray-800'
}`}
>
{event.status}
</span>
</div>
<span
className={`px-3 py-1 rounded-full text-xs font-medium ${
event.status === 'completed'
? 'bg-green-100 text-green-800'
: event.status === 'upcoming'
? 'bg-blue-100 text-blue-800'
: 'bg-gray-100 text-gray-800'
}`}
>
{event.status}
</span>
</div>
</div>
))}
</div>
) : (
<p className="text-gray-500">No events yet</p>
)}
))}
</div>
) : (
<p className="text-gray-500">No registered events yet</p>
)}
</div>
{/* Organized Events */}
<div>
<h2 className="text-xl font-semibold mb-4">Organized Events</h2>
{events.organized.length > 0 ? (
<div className="space-y-4">
{events.organized.map((event) => (
<div key={`organized-${event.id}`} className="bg-gray-50 rounded-lg p-4">
<div className="flex justify-between items-start">
<div>
<h3 className="font-semibold text-lg">{event.title}</h3>
<p className="text-sm text-gray-600 mt-1">{event.description}</p>
<p className="text-sm text-gray-500 mt-2">
📍 {event.locationName}
</p>
<p className="text-sm text-gray-500">
🗓️ {formatDate(event.scheduledDate)}
</p>
<p className="text-sm text-gray-500">
👥 {event.participantCount} participants
</p>
{event.litterCollected && (
<p className="text-sm text-green-600 font-medium mt-2">
♻️ {event.litterCollected} kg collected
</p>
)}
</div>
<span
className={`px-3 py-1 rounded-full text-xs font-medium ${
event.status === 'completed'
? 'bg-green-100 text-green-800'
: event.status === 'upcoming'
? 'bg-blue-100 text-blue-800'
: 'bg-gray-100 text-gray-800'
}`}
>
{event.status}
</span>
</div>
</div>
))}
</div>
) : (
<p className="text-gray-500">No organized events yet</p>
)}
</div>
</div>
)}