Export reports
Select a date range to download a CSV of matching reports.
/* @component-map * App β Main container, tab navigation, map click state [app.jsx] * MapView β Interactive map with incident markers and location [components/MapView.jsx] * ReportForm β Modal form for submitting new incident reports [components/ReportForm.jsx] * ReportsList β List view of all reported incidents with filtering [components/ReportsList.jsx] * @end-component-map */ // DEPLOY_CONFIG: {"cron": [{"name": "daily_incident_summary_broadcast", "schedule": "0 9 * * *", "action": "event", "config": {"event_type": "generate_daily_incident_summary", "payload": {"source": "incident_reports", "range": "yesterday", "ai_summary": true, "broadcast": true}}}], "server_config": {"secrets": [{"name": "AI_API_KEY", "description": "API key for generating daily incident report summaries"}]}} import { useEffect, useMemo, useRef, useState } from 'react'; import { useCollection, useIdentity, usePresence } from 'deplixo'; import { exportCSV, playSound } from '@deplixo/sdk'; import { MapView } from './components/MapView.jsx'; import { ReportForm } from './components/ReportForm.jsx'; import { ReportsList } from './components/ReportsList.jsx'; function App() { const [activeTab, setActiveTab] = useState('map'); const [reportCoords, setReportCoords] = useState(null); const [mapCenter, setMapCenter] = useState(null); const [nearbyReportIds, setNearbyReportIds] = useState(() => new Set()); const [exportStartDate, setExportStartDate] = useState(''); const [exportEndDate, setExportEndDate] = useState(''); const [exportMessage, setExportMessage] = useState(''); const lastAlertRef = useRef(0); const seenReportIdsRef = useRef(new Set()); const identityHook = typeof useIdentity === 'function' ? useIdentity() : { user: null, loading: false }; const identityUser = identityHook?.user ?? null; const identityLoading = identityHook?.loading ?? false; const collectionHook = typeof useCollection === 'function' ? useCollection('incidentReports', { personal: true }) : { items: [], loading: false, add: async () => {} }; const reports = collectionHook?.items ?? []; const reportsLoading = collectionHook?.loading ?? false; const addReport = collectionHook?.add ?? (async () => {}); const currentUser = identityUser || null; const presenceUserData = useMemo(() => { const userId = currentUser?.id || currentUser?.userId || currentUser?.email || 'unknown'; const userName = currentUser?.name || currentUser?.displayName || currentUser?.username || currentUser?.email || 'Anonymous'; return { id: userId, name: userName, status: activeTab === 'map' ? 'online' : 'away', watching: activeTab === 'map', view: 'map', }; }, [currentUser, activeTab]); const presenceHook = typeof usePresence === 'function' ? usePresence(presenceUserData) : { users: [] }; const presenceUsers = presenceHook?.users ?? []; const mapViewers = presenceUsers.filter((u) => u?.watching === true || u?.view === 'map'); const neighborCount = mapViewers.length; const proximityBase = reportCoords || mapCenter; const isReportNearby = (report, base) => { if (!report || !base) return false; const lat = Number(report?.lat); const lng = Number(report?.lng); const baseLat = Number(base?.lat); const baseLng = Number(base?.lng); if (![lat, lng, baseLat, baseLng].every(Number.isFinite)) return false; const latDiff = Math.abs(lat - baseLat); const lngDiff = Math.abs(lng - baseLng); return latDiff <= 0.02 && lngDiff <= 0.02; }; useEffect(() => { if (!reports?.length || !proximityBase) return; const currentReportIds = new Set(); let hasNewNearbyReport = false; reports.forEach((report) => { const reportId = report?.id || report?._id || report?.createdAt || `${report?.lat}-${report?.lng}-${report?.description || ''}`; if (reportId) currentReportIds.add(reportId); const isNearby = isReportNearby(report, proximityBase); if (isNearby) { setNearbyReportIds((prev) => { if (prev.has(reportId)) return prev; const next = new Set(prev); next.add(reportId); return next; }); } const isNew = reportId && !seenReportIdsRef.current.has(reportId); if (isNew) { seenReportIdsRef.current.add(reportId); if (isNearby) hasNewNearbyReport = true; } }); const now = Date.now(); const throttleMs = 15000; if (hasNewNearbyReport && now - lastAlertRef.current > throttleMs) { lastAlertRef.current = now; if (typeof playSound === 'function') playSound('@ding'); } return () => { currentReportIds.forEach((id) => { if (!seenReportIdsRef.current.has(id)) { seenReportIdsRef.current.add(id); } }); }; }, [reports, proximityBase]); const handleMapTap = (lat, lng) => { const nextCoords = { lat, lng }; setReportCoords(nextCoords); setMapCenter(nextCoords); }; const handleMapCenterChange = (center) => { if (center && Number.isFinite(Number(center.lat)) && Number.isFinite(Number(center.lng))) { setMapCenter({ lat: Number(center.lat), lng: Number(center.lng) }); } }; const handleCloseForm = () => { setReportCoords(null); }; const handleSubmitReport = async (reportData) => { const payload = { ...reportData, poster: currentUser ? { id: currentUser.id || currentUser.userId || currentUser.email || 'unknown', name: currentUser.name || currentUser.displayName || currentUser.username || currentUser.email || 'Anonymous', email: currentUser.email || '', } : { id: 'unknown', name: 'Anonymous', email: '', }, createdAt: new Date().toISOString(), }; await addReport(payload); handleCloseForm(); if (typeof playSound === 'function') playSound('@success'); }; const normalizeReportDate = (report) => { const raw = report?.createdAt || report?.date || report?.timestamp || report?._createdAt; const date = raw ? new Date(raw) : null; return date && !Number.isNaN(date.getTime()) ? date : null; }; const handleExportCSV = () => { if (!reports?.length) { setExportMessage('No reports available to export.'); return; } const start = exportStartDate ? new Date(`${exportStartDate}T00:00:00`) : null; const end = exportEndDate ? new Date(`${exportEndDate}T23:59:59.999`) : null; if (start && Number.isNaN(start.getTime())) { setExportMessage('Please enter a valid start date.'); return; } if (end && Number.isNaN(end.getTime())) { setExportMessage('Please enter a valid end date.'); return; } if (start && end && start > end) { setExportMessage('Start date must be on or before end date.'); return; } const filtered = reports.filter((report) => { const reportDate = normalizeReportDate(report); if (!reportDate) return false; if (start && reportDate < start) return false; if (end && reportDate > end) return false; return true; }); const csvRows = filtered.map((report) => ({ id: report?.id || report?._id || '', createdAt: report?.createdAt || report?.date || report?.timestamp || '', date: normalizeReportDate(report)?.toISOString() || '', type: report?.type || report?.incidentType || '', description: report?.description || '', lat: report?.lat ?? '', lng: report?.lng ?? '', posterName: report?.poster?.name || '', posterEmail: report?.poster?.email || '', })); const startLabel = exportStartDate || 'all'; const endLabel = exportEndDate || 'all'; if (typeof exportCSV === 'function') { exportCSV(csvRows, { filename: `incident-reports-${startLabel}-to-${endLabel}.csv`, columns: ['id', 'createdAt', 'date', 'type', 'description', 'lat', 'lng', 'posterName', 'posterEmail'], }); } setExportMessage(`Exported ${filtered.length} report${filtered.length === 1 ? '' : 's'}.`); }; return (
Select a date range to download a CSV of matching reports.