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 [profile, setProfile] = useState<UserProfile | null>(null);
|
||||||
const [reports, setReports] = useState<Report[]>([]);
|
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 [activity, setActivity] = useState<UserActivity[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
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>
|
<h2 className="text-xl font-semibold mb-4">Recent Activity</h2>
|
||||||
{activity.length > 0 ? (
|
{activity.length > 0 ? (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{activity.slice(0, 5).map((item) => (
|
{activity.slice(0, 5).map((item, index) => (
|
||||||
<div key={item.id} className="flex items-start space-x-3 p-3 bg-gray-50 rounded-lg">
|
<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">
|
<div className="flex-shrink-0">
|
||||||
{item.type === 'report' && (
|
{item.type === 'report' && (
|
||||||
<div className="h-10 w-10 bg-blue-100 rounded-full flex items-center justify-center">
|
<div className="h-10 w-10 bg-blue-100 rounded-full flex items-center justify-center">
|
||||||
@@ -373,12 +373,14 @@ export function UserProfilePage() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{activeTab === 'events' && (
|
{activeTab === 'events' && (
|
||||||
|
<div className="space-y-6">
|
||||||
|
{/* Registered Events */}
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl font-semibold mb-4">Events</h2>
|
<h2 className="text-xl font-semibold mb-4">Registered Events</h2>
|
||||||
{events.length > 0 ? (
|
{events.registered.length > 0 ? (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{events.map((event) => (
|
{events.registered.map((event) => (
|
||||||
<div key={event.id} className="bg-gray-50 rounded-lg p-4">
|
<div key={`registered-${event.id}`} className="bg-gray-50 rounded-lg p-4">
|
||||||
<div className="flex justify-between items-start">
|
<div className="flex justify-between items-start">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-semibold text-lg">{event.title}</h3>
|
<h3 className="font-semibold text-lg">{event.title}</h3>
|
||||||
@@ -414,9 +416,56 @@ export function UserProfilePage() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<p className="text-gray-500">No events yet</p>
|
<p className="text-gray-500">No registered events yet</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</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>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{activeTab === 'activity' && (
|
{activeTab === 'activity' && (
|
||||||
|
|||||||
Reference in New Issue
Block a user