diff --git a/.kiro/specs/beach-litter-mapping/tasks.md b/.kiro/specs/beach-litter-mapping/tasks.md
index ac7c9e9..6e0c300 100644
--- a/.kiro/specs/beach-litter-mapping/tasks.md
+++ b/.kiro/specs/beach-litter-mapping/tasks.md
@@ -145,7 +145,7 @@
## Cleanup Tracking System
-- [ ] 18. Implement "Mark as Cleaned" functionality
+- [-] 18. Implement "Mark as Cleaned" functionality
- Add `cleaned_at` timestamp field to reports table
- Add `cleaned_by_user_id` field to track who marked it as cleaned
- Create POST /api/reports/:id/mark-cleaned backend endpoint
diff --git a/src/components/map/MapFilters.tsx b/src/components/map/MapFilters.tsx
index fb06744..ddf0481 100644
--- a/src/components/map/MapFilters.tsx
+++ b/src/components/map/MapFilters.tsx
@@ -218,6 +218,28 @@ export function MapFilters({
)}
+ {/* Show Cleaned Reports Toggle */}
+
+
+
+
+
{/* Hotspot Legend */}
{showHotspots && onToggleHotspots && (
diff --git a/src/components/map/MapView.tsx b/src/components/map/MapView.tsx
index a6c71c5..b9edf57 100644
--- a/src/components/map/MapView.tsx
+++ b/src/components/map/MapView.tsx
@@ -152,6 +152,11 @@ export function MapView({
}
}
+ // Filter by cleaned status
+ if (filters.showCleaned === false && report.cleanedAt) {
+ return false;
+ }
+
return true;
});
};
@@ -181,7 +186,8 @@ export function MapView({
// Add report markers
filteredReports.forEach((report) => {
- const icon = createCustomMarkerIcon(report.litterType, report.quantity);
+ const isCleaned = !!report.cleanedAt;
+ const icon = createCustomMarkerIcon(report.litterType, report.quantity, isCleaned);
const marker = L.marker([report.latitude, report.longitude], { icon });
// Add popup with report details
@@ -204,7 +210,12 @@ export function MapView({
${getLitterTypeLabel(report.litterType)}
Quantity: ${report.quantity}
Status: ${report.verificationStatus}
-
Date: ${new Date(report.createdAt).toLocaleDateString()}
+ ${isCleaned && report.cleanedAt ? `
+
✓ Cleaned up
+
Cleaned: ${new Date(report.cleanedAt).toLocaleDateString()}
+ ` : `
+
Date: ${new Date(report.createdAt).toLocaleDateString()}
+ `}
${report.description ? `
${report.description}
` : ''}
`);
@@ -503,7 +514,7 @@ export function MapView({
}
// Helper function to create custom marker icons based on litter type
-function createCustomMarkerIcon(litterType: string, quantity: string): L.DivIcon {
+function createCustomMarkerIcon(litterType: string, quantity: string, isCleaned: boolean = false): L.DivIcon {
const colors = {
plastic: '#3B82F6', // blue
metal: '#6B7280', // gray
@@ -520,7 +531,8 @@ function createCustomMarkerIcon(litterType: string, quantity: string): L.DivIcon
severe: 36,
};
- const color = colors[litterType as keyof typeof colors] || colors.other;
+ // Use green color for cleaned reports, otherwise use litter type color
+ const color = isCleaned ? '#10B981' : (colors[litterType as keyof typeof colors] || colors.other);
const size = sizes[quantity as keyof typeof sizes] || sizes.moderate;
return L.divIcon({
@@ -534,6 +546,7 @@ function createCustomMarkerIcon(litterType: string, quantity: string): L.DivIcon
border-radius: 50% 50% 50% 0;
transform: rotate(-45deg);
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
+ ${isCleaned ? 'opacity: 0.7;' : ''}
">
- ${getLitterTypeIcon(litterType)}
+ ${isCleaned ? '✓' : getLitterTypeIcon(litterType)}
`,
diff --git a/src/components/report/ReportDetailModal.tsx b/src/components/report/ReportDetailModal.tsx
index 69ef6f1..b39213a 100644
--- a/src/components/report/ReportDetailModal.tsx
+++ b/src/components/report/ReportDetailModal.tsx
@@ -211,6 +211,26 @@ export function ReportDetailModal({ report, isOpen, onClose, onVerify, onDispute
)}
+ {/* Cleaned Status */}
+ {report.cleanedAt && (
+
+
+
+ This litter was cleaned up on {format(new Date(report.cleanedAt), 'PPP')}
+
+ {report.cleanedByUserId && (
+
+ Marked as cleaned by a community member
+
+ )}
+
+ )}
+
{/* Description */}
{report.description && (
@@ -358,7 +378,7 @@ export function ReportDetailModal({ report, isOpen, onClose, onVerify, onDispute
{/* Action Buttons */}
- {(onVerify || onDispute || onMarkCleaned) && (
+ {(onVerify || onDispute || onMarkCleaned) && !report.cleanedAt && (
{(onVerify || onDispute) && (
@@ -398,7 +418,7 @@ export function ReportDetailModal({ report, isOpen, onClose, onVerify, onDispute
- Cleaned Up
+ Mark as Cleaned
)}
diff --git a/src/types/index.ts b/src/types/index.ts
index fd49fc8..3c4a119 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -141,6 +141,7 @@ export interface FilterState {
litterTypes?: LitterType[];
quantities?: QuantityLevel[];
verificationStatus?: VerificationStatus[];
+ showCleaned?: boolean;
}
// Analytics types