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:
@@ -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>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user