@burdenoff/microfe-ecoimpacthub 2026.704.3 → 2026.704.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ecoimpacthub/pages/DashboardPage.js +345 -189
- package/dist/ecoimpacthub/pages/DashboardPage.js.map +1 -1
- package/dist/ecoimpacthub/pages/_shared/ConservationBadge.js +58 -0
- package/dist/ecoimpacthub/pages/_shared/ConservationBadge.js.map +1 -0
- package/dist/ecoimpacthub/pages/_shared/EvidenceThumb.js +70 -0
- package/dist/ecoimpacthub/pages/_shared/EvidenceThumb.js.map +1 -0
- package/dist/ecoimpacthub/pages/_shared/MethodBadge.js +51 -0
- package/dist/ecoimpacthub/pages/_shared/MethodBadge.js.map +1 -0
- package/dist/ecoimpacthub/pages/_shared/TaxonChip.js +29 -0
- package/dist/ecoimpacthub/pages/_shared/TaxonChip.js.map +1 -0
- package/dist/ecoimpacthub/pages/badges.js +71 -0
- package/dist/ecoimpacthub/pages/badges.js.map +1 -0
- package/dist/ecoimpacthub/pages/carbon/CarbonPage.js +341 -300
- package/dist/ecoimpacthub/pages/carbon/CarbonPage.js.map +1 -1
- package/dist/ecoimpacthub/pages/carbonVisuals.js +89 -0
- package/dist/ecoimpacthub/pages/carbonVisuals.js.map +1 -0
- package/dist/ecoimpacthub/pages/habitat/HabitatProjectDetailPage.js +252 -218
- package/dist/ecoimpacthub/pages/habitat/HabitatProjectDetailPage.js.map +1 -1
- package/dist/ecoimpacthub/pages/habitat/HabitatProjectsPage.js +15 -12
- package/dist/ecoimpacthub/pages/habitat/HabitatProjectsPage.js.map +1 -1
- package/dist/ecoimpacthub/pages/habitat/LifecycleProgress.js +168 -0
- package/dist/ecoimpacthub/pages/habitat/LifecycleProgress.js.map +1 -0
- package/dist/ecoimpacthub/pages/monitoring/MonitoringPage.js +30 -17
- package/dist/ecoimpacthub/pages/monitoring/MonitoringPage.js.map +1 -1
- package/dist/ecoimpacthub/pages/observations/ObservationsPage.js +236 -219
- package/dist/ecoimpacthub/pages/observations/ObservationsPage.js.map +1 -1
- package/dist/ecoimpacthub/pages/reports/ReportDetailPage.js +248 -212
- package/dist/ecoimpacthub/pages/reports/ReportDetailPage.js.map +1 -1
- package/dist/ecoimpacthub/pages/reports/ReportsPage.js +119 -118
- package/dist/ecoimpacthub/pages/reports/ReportsPage.js.map +1 -1
- package/dist/ecoimpacthub/pages/sites/SiteDetailPage.js +498 -370
- package/dist/ecoimpacthub/pages/sites/SiteDetailPage.js.map +1 -1
- package/dist/ecoimpacthub/pages/sites/SitesPage.js +109 -99
- package/dist/ecoimpacthub/pages/sites/SitesPage.js.map +1 -1
- package/dist/ecoimpacthub/pages/sites/siteTypeVisuals.js +64 -0
- package/dist/ecoimpacthub/pages/sites/siteTypeVisuals.js.map +1 -0
- package/dist/ecoimpacthub/pages/species/SpeciesDetailPage.js +275 -259
- package/dist/ecoimpacthub/pages/species/SpeciesDetailPage.js.map +1 -1
- package/dist/ecoimpacthub/pages/species/SpeciesPage.js +235 -177
- package/dist/ecoimpacthub/pages/species/SpeciesPage.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/CartesianGrid.js +305 -0
- package/dist/node_modules/recharts/es6/cartesian/CartesianGrid.js.map +1 -0
- package/dist/node_modules/recharts/es6/context/chartLayoutContext.js +44 -33
- package/dist/node_modules/recharts/es6/context/chartLayoutContext.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/ChartUtils.js +31 -23
- package/dist/node_modules/recharts/es6/util/ChartUtils.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SiteDetailPage.js","names":[],"sources":["../../../../src/ecoimpacthub/pages/sites/SiteDetailPage.tsx"],"sourcesContent":["/**\n * Site detail — sensors, alert thresholds, recent measurements & site actions.\n * @module ecoimpacthub/pages/sites\n *\n * Reads `ecoSite` (+ nested sensors/thresholds) and `ecoListMeasurements`;\n * writes `ecoCreateSensor`, `ecoSetSensorStatus`, `ecoCreateThreshold`,\n * `ecoSetThresholdActive`, `ecoRecordMeasurement`, `ecoArchiveSite`.\n */\n\nimport { useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { ArrowLeft, Plus, Radio, Gauge, Activity, Archive } from 'lucide-react';\nimport {\n Button,\n Input,\n Label,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n GlassCard,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { DataTable, type Column } from '../../components/DataTable';\nimport { LoadingView, ErrorView, Section } from '../../components/StateViews';\nimport { EmptyState } from '../../components/EmptyState';\nimport { EcoTags } from '../../components/EcoTags';\nimport { useEcoImpactHub } from '../../context';\nimport {\n useEcoSite,\n useEcoMeasurements,\n useEcoCreateSensor,\n useEcoSetSensorStatus,\n useEcoCreateThreshold,\n useEcoSetThresholdActive,\n useEcoRecordMeasurement,\n useEcoArchiveSite,\n} from '../../hooks';\nimport {\n formatDateTime,\n formatEnumLabel,\n formatNumber,\n formatCoords,\n} from '../../utils/formatters';\nimport type {\n EcoSensor,\n EcoSensorKind,\n EcoSensorStatus,\n EcoAlertThreshold,\n EcoMeasurement,\n EcoThresholdDirection,\n EcoThresholdSeverity,\n} from '../../types';\n\nconst SENSOR_KINDS: EcoSensorKind[] = [\n 'AIR_QUALITY',\n 'WATER_QUALITY',\n 'WEATHER',\n 'SOIL',\n 'ENERGY_METER',\n 'FLOW',\n 'ACOUSTIC',\n 'CAMERA_TRAP',\n 'MANUAL',\n];\nconst SENSOR_STATUSES: EcoSensorStatus[] = [\n 'ONLINE',\n 'OFFLINE',\n 'DEGRADED',\n 'MAINTENANCE',\n 'RETIRED',\n];\nconst THRESHOLD_DIRECTIONS: EcoThresholdDirection[] = ['ABOVE', 'BELOW', 'OUTSIDE_RANGE'];\nconst THRESHOLD_SEVERITIES: EcoThresholdSeverity[] = ['INFO', 'WARNING', 'CRITICAL'];\n\nexport function SiteDetailPage() {\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useEcoImpactHub();\n const { data: site, isLoading, isError, refetch } = useEcoSite(id);\n const measurements = useEcoMeasurements({ siteId: id, pagination: { limit: 10, offset: 0 } });\n\n const createSensor = useEcoCreateSensor();\n const setSensorStatus = useEcoSetSensorStatus();\n const createThreshold = useEcoCreateThreshold();\n const setThresholdActive = useEcoSetThresholdActive();\n const recordMeasurement = useEcoRecordMeasurement();\n const archiveSite = useEcoArchiveSite();\n\n const [sensorOpen, setSensorOpen] = useState(false);\n const [sensorForm, setSensorForm] = useState({\n name: '',\n kind: 'AIR_QUALITY' as EcoSensorKind,\n unit: '',\n hardwareId: '',\n });\n const [thresholdOpen, setThresholdOpen] = useState(false);\n const [thresholdForm, setThresholdForm] = useState({\n parameter: '',\n unit: '',\n direction: 'ABOVE' as EcoThresholdDirection,\n severity: 'WARNING' as EcoThresholdSeverity,\n limitValue: '',\n minValue: '',\n maxValue: '',\n });\n const [measureOpen, setMeasureOpen] = useState(false);\n const [measureForm, setMeasureForm] = useState({\n parameter: '',\n value: '',\n unit: '',\n sensorId: '',\n });\n const [error, setError] = useState<string | null>(null);\n\n if (isLoading) {\n return (\n <PageLayout title=\"Site\">\n <LoadingView />\n </PageLayout>\n );\n }\n if (isError || !site) {\n return (\n <PageLayout title=\"Site\">\n <ErrorView message=\"This site could not be loaded.\" onRetry={() => refetch()} />\n </PageLayout>\n );\n }\n\n const sensors = site.sensors ?? [];\n const thresholds = site.thresholds ?? [];\n const measurementRows = measurements.data?.items ?? [];\n\n const handleAddSensor = async () => {\n if (!id || !sensorForm.name.trim()) {\n setError('Sensor name is required.');\n return;\n }\n setError(null);\n try {\n await createSensor.mutateAsync({\n name: sensorForm.name.trim(),\n siteId: id,\n kind: sensorForm.kind,\n unit: sensorForm.unit.trim() || null,\n hardwareId: sensorForm.hardwareId.trim() || null,\n });\n setSensorOpen(false);\n setSensorForm({ name: '', kind: 'AIR_QUALITY', unit: '', hardwareId: '' });\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to add sensor.');\n }\n };\n\n const handleAddThreshold = async () => {\n if (!id || !thresholdForm.parameter.trim() || !thresholdForm.unit.trim()) {\n setError('Parameter and unit are required.');\n return;\n }\n setError(null);\n try {\n await createThreshold.mutateAsync({\n siteId: id,\n parameter: thresholdForm.parameter.trim(),\n unit: thresholdForm.unit.trim(),\n direction: thresholdForm.direction,\n severity: thresholdForm.severity,\n limitValue: thresholdForm.limitValue ? Number(thresholdForm.limitValue) : null,\n minValue: thresholdForm.minValue ? Number(thresholdForm.minValue) : null,\n maxValue: thresholdForm.maxValue ? Number(thresholdForm.maxValue) : null,\n });\n setThresholdOpen(false);\n setThresholdForm({\n parameter: '',\n unit: '',\n direction: 'ABOVE',\n severity: 'WARNING',\n limitValue: '',\n minValue: '',\n maxValue: '',\n });\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to add threshold.');\n }\n };\n\n const handleRecordMeasurement = async () => {\n if (!id || !measureForm.parameter.trim() || !measureForm.unit.trim() || !measureForm.value) {\n setError('Parameter, value and unit are required.');\n return;\n }\n setError(null);\n try {\n await recordMeasurement.mutateAsync({\n siteId: id,\n parameter: measureForm.parameter.trim(),\n value: Number(measureForm.value),\n unit: measureForm.unit.trim(),\n recordedAt: new Date().toISOString(),\n sensorId: measureForm.sensorId || null,\n source: 'MANUAL',\n });\n setMeasureOpen(false);\n setMeasureForm({ parameter: '', value: '', unit: '', sensorId: '' });\n measurements.refetch();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to record measurement.');\n }\n };\n\n const handleArchive = async () => {\n if (!id) return;\n setError(null);\n try {\n await archiveSite.mutateAsync(id);\n navigateTo('sites');\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to archive site.');\n }\n };\n\n const sensorColumns: Column<EcoSensor>[] = [\n {\n key: 'name',\n header: 'Sensor',\n render: (s) => (\n <div>\n <p className=\"font-medium text-text-primary\">{s.name}</p>\n <p className=\"text-xs text-text-tertiary\">{formatEnumLabel(s.kind)}</p>\n </div>\n ),\n },\n {\n key: 'unit',\n header: 'Unit',\n hideOnMobile: true,\n render: (s) => <span className=\"text-text-secondary\">{s.unit ?? '—'}</span>,\n },\n { key: 'status', header: 'Status', render: (s) => <StatusBadge status={s.status} /> },\n {\n key: 'action',\n header: '',\n align: 'right',\n render: (s) => (\n <Select\n value={s.status}\n onValueChange={(v) => setSensorStatus.mutate({ id: s.id, status: v as EcoSensorStatus })}\n >\n <SelectTrigger className=\"h-8 w-36\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {SENSOR_STATUSES.map((st) => (\n <SelectItem key={st} value={st}>\n {formatEnumLabel(st)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n ),\n },\n ];\n\n const thresholdColumns: Column<EcoAlertThreshold>[] = [\n {\n key: 'parameter',\n header: 'Parameter',\n render: (t) => <span className=\"font-medium text-text-primary\">{t.parameter}</span>,\n },\n {\n key: 'rule',\n header: 'Rule',\n render: (t) => (\n <span className=\"text-text-secondary\">\n {t.direction === 'OUTSIDE_RANGE'\n ? `${formatNumber(t.minValue)}–${formatNumber(t.maxValue)} ${t.unit}`\n : `${formatEnumLabel(t.direction)} ${formatNumber(t.limitValue)} ${t.unit}`}\n </span>\n ),\n },\n { key: 'severity', header: 'Severity', render: (t) => <StatusBadge status={t.severity} /> },\n {\n key: 'active',\n header: 'Active',\n align: 'right',\n render: (t) => (\n <Button\n size=\"sm\"\n variant={t.isActive ? 'outline' : 'ghost'}\n onClick={() => setThresholdActive.mutate({ id: t.id, isActive: !t.isActive })}\n >\n {t.isActive ? 'Active' : 'Inactive'}\n </Button>\n ),\n },\n ];\n\n const measurementColumns: Column<EcoMeasurement>[] = [\n {\n key: 'parameter',\n header: 'Parameter',\n render: (m) => <span className=\"font-medium text-text-primary\">{m.parameter}</span>,\n },\n {\n key: 'value',\n header: 'Value',\n align: 'right',\n render: (m) => (\n <span className=\"tabular-nums text-text-primary\">\n {formatNumber(m.value)} {m.unit}\n </span>\n ),\n },\n {\n key: 'source',\n header: 'Source',\n hideOnMobile: true,\n render: (m) => <span className=\"text-text-secondary\">{formatEnumLabel(m.source)}</span>,\n },\n {\n key: 'breach',\n header: 'Status',\n render: (m) => (m.isBreach ? <StatusBadge status=\"BREACH\" /> : <StatusBadge status=\"OK\" />),\n },\n {\n key: 'recorded',\n header: 'Recorded',\n align: 'right',\n hideOnMobile: true,\n render: (m) => <span className=\"text-text-tertiary\">{formatDateTime(m.recordedAt)}</span>,\n },\n ];\n\n return (\n <PageLayout\n title={site.name}\n description={[site.code, formatEnumLabel(site.type)].filter(Boolean).join(' · ')}\n actions={\n <>\n <StatusBadge status={site.status} />\n <Button variant=\"ghost\" onClick={() => navigateTo('sites')}>\n <ArrowLeft className=\"mr-1.5 h-4 w-4\" /> Back\n </Button>\n </>\n }\n >\n {/* Overview */}\n <div className=\"grid grid-cols-1 gap-4 lg:grid-cols-3\">\n <GlassCard treatment=\"glass\" className=\"p-6 lg:col-span-2\">\n <dl className=\"grid grid-cols-2 gap-x-6 gap-y-4 sm:grid-cols-3\">\n <div>\n <dt className=\"text-xs text-text-tertiary\">Type</dt>\n <dd className=\"mt-0.5 text-sm text-text-primary\">{formatEnumLabel(site.type)}</dd>\n </div>\n <div>\n <dt className=\"text-xs text-text-tertiary\">Region</dt>\n <dd className=\"mt-0.5 text-sm text-text-primary\">\n {[site.region, site.country].filter(Boolean).join(', ') || '—'}\n </dd>\n </div>\n <div>\n <dt className=\"text-xs text-text-tertiary\">Area</dt>\n <dd className=\"mt-0.5 text-sm text-text-primary tabular-nums\">\n {site.areaHectares != null ? `${formatNumber(site.areaHectares)} ha` : '—'}\n </dd>\n </div>\n <div>\n <dt className=\"text-xs text-text-tertiary\">Coordinates</dt>\n <dd className=\"mt-0.5 text-sm text-text-primary tabular-nums\">\n {formatCoords(site.latitude, site.longitude)}\n </dd>\n </div>\n <div>\n <dt className=\"text-xs text-text-tertiary\">Sensors</dt>\n <dd className=\"mt-0.5 text-sm text-text-primary tabular-nums\">{sensors.length}</dd>\n </div>\n <div>\n <dt className=\"text-xs text-text-tertiary\">Thresholds</dt>\n <dd className=\"mt-0.5 text-sm text-text-primary tabular-nums\">{thresholds.length}</dd>\n </div>\n </dl>\n {site.description && (\n <p className=\"mt-5 rounded-lg bg-bg-sunken/60 p-3 text-sm text-text-secondary\">\n {site.description}\n </p>\n )}\n <div className=\"mt-5 border-t border-border-seam pt-4\">\n <EcoTags entityType=\"ecoimpacthub-site\" entityId={site.id} />\n </div>\n </GlassCard>\n\n <GlassCard treatment=\"glass\" className=\"flex flex-col gap-3 p-6\">\n <p className=\"text-xs text-text-tertiary\">Quick actions</p>\n <Button onClick={() => setMeasureOpen(true)}>\n <Activity className=\"mr-1.5 h-4 w-4\" /> Record measurement\n </Button>\n <Button variant=\"outline\" onClick={() => setSensorOpen(true)}>\n <Radio className=\"mr-1.5 h-4 w-4\" /> Add sensor\n </Button>\n <Button variant=\"outline\" onClick={() => setThresholdOpen(true)}>\n <Gauge className=\"mr-1.5 h-4 w-4\" /> Add threshold\n </Button>\n {site.status !== 'ARCHIVED' && (\n <Button\n variant=\"ghost\"\n onClick={handleArchive}\n disabled={archiveSite.isPending}\n className=\"text-status-error-text hover:text-status-error-text\"\n >\n <Archive className=\"mr-1.5 h-4 w-4\" /> Archive site\n </Button>\n )}\n </GlassCard>\n </div>\n\n {error && <p className=\"mt-4 text-sm text-status-error-text\">{error}</p>}\n\n {/* Sensors */}\n <Section\n title=\"Sensors\"\n description=\"Instruments streaming or recording readings for this site.\"\n action={\n <Button size=\"sm\" onClick={() => setSensorOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Add sensor\n </Button>\n }\n >\n {sensors.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No sensors\"\n description=\"Add a sensor to begin monitoring.\"\n />\n ) : (\n <DataTable columns={sensorColumns} rows={sensors} rowKey={(s) => s.id} />\n )}\n </Section>\n\n {/* Thresholds */}\n <Section\n title=\"Alert thresholds\"\n description=\"Rules that flag measurements as breaches when values drift out of range.\"\n action={\n <Button size=\"sm\" onClick={() => setThresholdOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Add threshold\n </Button>\n }\n >\n {thresholds.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No thresholds\"\n description=\"Define a threshold to flag breaches.\"\n />\n ) : (\n <DataTable columns={thresholdColumns} rows={thresholds} rowKey={(t) => t.id} />\n )}\n </Section>\n\n {/* Recent measurements */}\n <Section title=\"Recent measurements\" description=\"The latest readings recorded at this site.\">\n {measurementRows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No measurements yet\"\n description=\"Record a measurement to see it here.\"\n />\n ) : (\n <DataTable columns={measurementColumns} rows={measurementRows} rowKey={(m) => m.id} />\n )}\n </Section>\n\n {/* Add sensor dialog */}\n <ResponsiveDialog open={sensorOpen} onOpenChange={setSensorOpen}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Add sensor</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Register a sensor at {site.name}.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"grid gap-4 py-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"sensorName\">Name *</Label>\n <Input\n id=\"sensorName\"\n value={sensorForm.name}\n onChange={(e) => setSensorForm({ ...sensorForm, name: e.target.value })}\n placeholder=\"North gate PM2.5\"\n />\n </div>\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"sensorKind\">Kind</Label>\n <Select\n value={sensorForm.kind}\n onValueChange={(v) => setSensorForm({ ...sensorForm, kind: v as EcoSensorKind })}\n >\n <SelectTrigger id=\"sensorKind\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {SENSOR_KINDS.map((k) => (\n <SelectItem key={k} value={k}>\n {formatEnumLabel(k)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"sensorUnit\">Unit</Label>\n <Input\n id=\"sensorUnit\"\n value={sensorForm.unit}\n onChange={(e) => setSensorForm({ ...sensorForm, unit: e.target.value })}\n placeholder=\"µg/m³\"\n />\n </div>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"sensorHw\">Hardware ID</Label>\n <Input\n id=\"sensorHw\"\n value={sensorForm.hardwareId}\n onChange={(e) => setSensorForm({ ...sensorForm, hardwareId: e.target.value })}\n placeholder=\"Optional device identifier\"\n />\n </div>\n </div>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setSensorOpen(false)}>\n Cancel\n </Button>\n <Button onClick={handleAddSensor} disabled={createSensor.isPending}>\n {createSensor.isPending ? 'Adding…' : 'Add sensor'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Add threshold dialog */}\n <ResponsiveDialog open={thresholdOpen} onOpenChange={setThresholdOpen}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Add alert threshold</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Flag readings that drift out of an acceptable range.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"grid gap-4 py-2\">\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"thParam\">Parameter *</Label>\n <Input\n id=\"thParam\"\n value={thresholdForm.parameter}\n onChange={(e) =>\n setThresholdForm({ ...thresholdForm, parameter: e.target.value })\n }\n placeholder=\"PM2.5\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"thUnit\">Unit *</Label>\n <Input\n id=\"thUnit\"\n value={thresholdForm.unit}\n onChange={(e) => setThresholdForm({ ...thresholdForm, unit: e.target.value })}\n placeholder=\"µg/m³\"\n />\n </div>\n </div>\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"thDir\">Direction</Label>\n <Select\n value={thresholdForm.direction}\n onValueChange={(v) =>\n setThresholdForm({ ...thresholdForm, direction: v as EcoThresholdDirection })\n }\n >\n <SelectTrigger id=\"thDir\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {THRESHOLD_DIRECTIONS.map((d) => (\n <SelectItem key={d} value={d}>\n {formatEnumLabel(d)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"thSev\">Severity</Label>\n <Select\n value={thresholdForm.severity}\n onValueChange={(v) =>\n setThresholdForm({ ...thresholdForm, severity: v as EcoThresholdSeverity })\n }\n >\n <SelectTrigger id=\"thSev\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {THRESHOLD_SEVERITIES.map((s) => (\n <SelectItem key={s} value={s}>\n {formatEnumLabel(s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n {thresholdForm.direction === 'OUTSIDE_RANGE' ? (\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"thMin\">Min value</Label>\n <Input\n id=\"thMin\"\n type=\"number\"\n step=\"any\"\n value={thresholdForm.minValue}\n onChange={(e) =>\n setThresholdForm({ ...thresholdForm, minValue: e.target.value })\n }\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"thMax\">Max value</Label>\n <Input\n id=\"thMax\"\n type=\"number\"\n step=\"any\"\n value={thresholdForm.maxValue}\n onChange={(e) =>\n setThresholdForm({ ...thresholdForm, maxValue: e.target.value })\n }\n />\n </div>\n </div>\n ) : (\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"thLimit\">Limit value</Label>\n <Input\n id=\"thLimit\"\n type=\"number\"\n step=\"any\"\n value={thresholdForm.limitValue}\n onChange={(e) =>\n setThresholdForm({ ...thresholdForm, limitValue: e.target.value })\n }\n placeholder=\"35\"\n />\n </div>\n )}\n </div>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setThresholdOpen(false)}>\n Cancel\n </Button>\n <Button onClick={handleAddThreshold} disabled={createThreshold.isPending}>\n {createThreshold.isPending ? 'Adding…' : 'Add threshold'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Record measurement dialog */}\n <ResponsiveDialog open={measureOpen} onOpenChange={setMeasureOpen}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Record measurement</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Log a reading. It is checked against active thresholds and flagged if it breaches.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"grid gap-4 py-2\">\n <div className=\"grid gap-2 sm:grid-cols-3\">\n <div className=\"grid gap-1.5 sm:col-span-1\">\n <Label htmlFor=\"mParam\">Parameter *</Label>\n <Input\n id=\"mParam\"\n value={measureForm.parameter}\n onChange={(e) => setMeasureForm({ ...measureForm, parameter: e.target.value })}\n placeholder=\"PM2.5\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"mValue\">Value *</Label>\n <Input\n id=\"mValue\"\n type=\"number\"\n step=\"any\"\n value={measureForm.value}\n onChange={(e) => setMeasureForm({ ...measureForm, value: e.target.value })}\n placeholder=\"42\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"mUnit\">Unit *</Label>\n <Input\n id=\"mUnit\"\n value={measureForm.unit}\n onChange={(e) => setMeasureForm({ ...measureForm, unit: e.target.value })}\n placeholder=\"µg/m³\"\n />\n </div>\n </div>\n {sensors.length > 0 && (\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"mSensor\">Sensor (optional)</Label>\n <Select\n value={measureForm.sensorId}\n onValueChange={(v) => setMeasureForm({ ...measureForm, sensorId: v })}\n >\n <SelectTrigger id=\"mSensor\">\n <SelectValue placeholder=\"Manual entry\" />\n </SelectTrigger>\n <SelectContent>\n {sensors.map((s) => (\n <SelectItem key={s.id} value={s.id}>\n {s.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n )}\n </div>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setMeasureOpen(false)}>\n Cancel\n </Button>\n <Button onClick={handleRecordMeasurement} disabled={recordMeasurement.isPending}>\n {recordMeasurement.isPending ? 'Recording…' : 'Record'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SiteDetailPage.js","names":[],"sources":["../../../../src/ecoimpacthub/pages/sites/SiteDetailPage.tsx"],"sourcesContent":["/**\n * Site detail — sensors, alert thresholds, recent measurements & site actions.\n * @module ecoimpacthub/pages/sites\n *\n * Reads `ecoSite` (+ nested sensors/thresholds) and `ecoListMeasurements`;\n * writes `ecoCreateSensor`, `ecoSetSensorStatus`, `ecoCreateThreshold`,\n * `ecoSetThresholdActive`, `ecoRecordMeasurement`, `ecoArchiveSite`.\n */\n\nimport { useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { ArrowLeft, Plus, Radio, Gauge, Activity, Archive, Zap, ShieldCheck } from 'lucide-react';\nimport {\n Button,\n Input,\n Label,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n GlassCard,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { DataTable, type Column } from '../../components/DataTable';\nimport { LoadingView, ErrorView, Section } from '../../components/StateViews';\nimport { EmptyState } from '../../components/EmptyState';\nimport { EcoTags } from '../../components/EcoTags';\nimport { MiniBar } from '../../components/MiniBar';\nimport { SiteTypeBadge } from './siteTypeVisuals';\nimport { useEcoImpactHub } from '../../context';\nimport {\n useEcoSite,\n useEcoMeasurements,\n useEcoCreateSensor,\n useEcoSetSensorStatus,\n useEcoCreateThreshold,\n useEcoSetThresholdActive,\n useEcoRecordMeasurement,\n useEcoArchiveSite,\n} from '../../hooks';\nimport {\n formatDateTime,\n formatEnumLabel,\n formatNumber,\n formatCoords,\n formatRelativeTime,\n} from '../../utils/formatters';\nimport type {\n EcoSensor,\n EcoSensorKind,\n EcoSensorStatus,\n EcoAlertThreshold,\n EcoMeasurement,\n EcoThresholdDirection,\n EcoThresholdSeverity,\n} from '../../types';\n\nconst SENSOR_KINDS: EcoSensorKind[] = [\n 'AIR_QUALITY',\n 'WATER_QUALITY',\n 'WEATHER',\n 'SOIL',\n 'ENERGY_METER',\n 'FLOW',\n 'ACOUSTIC',\n 'CAMERA_TRAP',\n 'MANUAL',\n];\nconst SENSOR_STATUSES: EcoSensorStatus[] = [\n 'ONLINE',\n 'OFFLINE',\n 'DEGRADED',\n 'MAINTENANCE',\n 'RETIRED',\n];\nconst THRESHOLD_DIRECTIONS: EcoThresholdDirection[] = ['ABOVE', 'BELOW', 'OUTSIDE_RANGE'];\nconst THRESHOLD_SEVERITIES: EcoThresholdSeverity[] = ['INFO', 'WARNING', 'CRITICAL'];\n\nexport function SiteDetailPage() {\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useEcoImpactHub();\n const { data: site, isLoading, isError, refetch } = useEcoSite(id);\n const measurements = useEcoMeasurements({ siteId: id, pagination: { limit: 10, offset: 0 } });\n const latestBreach = useEcoMeasurements({\n siteId: id,\n breachesOnly: true,\n pagination: { limit: 1, offset: 0 },\n });\n\n const createSensor = useEcoCreateSensor();\n const setSensorStatus = useEcoSetSensorStatus();\n const createThreshold = useEcoCreateThreshold();\n const setThresholdActive = useEcoSetThresholdActive();\n const recordMeasurement = useEcoRecordMeasurement();\n const archiveSite = useEcoArchiveSite();\n\n const [sensorOpen, setSensorOpen] = useState(false);\n const [sensorForm, setSensorForm] = useState({\n name: '',\n kind: 'AIR_QUALITY' as EcoSensorKind,\n unit: '',\n hardwareId: '',\n });\n const [thresholdOpen, setThresholdOpen] = useState(false);\n const [thresholdForm, setThresholdForm] = useState({\n parameter: '',\n unit: '',\n direction: 'ABOVE' as EcoThresholdDirection,\n severity: 'WARNING' as EcoThresholdSeverity,\n limitValue: '',\n minValue: '',\n maxValue: '',\n });\n const [measureOpen, setMeasureOpen] = useState(false);\n const [measureForm, setMeasureForm] = useState({\n parameter: '',\n value: '',\n unit: '',\n sensorId: '',\n });\n const [error, setError] = useState<string | null>(null);\n\n if (isLoading) {\n return (\n <PageLayout title=\"Site\">\n <LoadingView />\n </PageLayout>\n );\n }\n if (isError || !site) {\n return (\n <PageLayout title=\"Site\">\n <ErrorView message=\"This site could not be loaded.\" onRetry={() => refetch()} />\n </PageLayout>\n );\n }\n\n const sensors = site.sensors ?? [];\n const thresholds = site.thresholds ?? [];\n const measurementRows = measurements.data?.items ?? [];\n\n // ── Monitoring summary (derived from already-loaded site data) ─────────────\n const onlineSensors = sensors.filter((s) => s.status === 'ONLINE').length;\n const attentionSensors = sensors.filter((s) =>\n ['OFFLINE', 'DEGRADED', 'MAINTENANCE'].includes(s.status)\n ).length;\n const activeThresholds = thresholds.filter((t) => t.isActive).length;\n const latestBreachRow = latestBreach.data?.items?.[0] ?? null;\n\n const handleAddSensor = async () => {\n if (!id || !sensorForm.name.trim()) {\n setError('Sensor name is required.');\n return;\n }\n setError(null);\n try {\n await createSensor.mutateAsync({\n name: sensorForm.name.trim(),\n siteId: id,\n kind: sensorForm.kind,\n unit: sensorForm.unit.trim() || null,\n hardwareId: sensorForm.hardwareId.trim() || null,\n });\n setSensorOpen(false);\n setSensorForm({ name: '', kind: 'AIR_QUALITY', unit: '', hardwareId: '' });\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to add sensor.');\n }\n };\n\n const handleAddThreshold = async () => {\n if (!id || !thresholdForm.parameter.trim() || !thresholdForm.unit.trim()) {\n setError('Parameter and unit are required.');\n return;\n }\n setError(null);\n try {\n await createThreshold.mutateAsync({\n siteId: id,\n parameter: thresholdForm.parameter.trim(),\n unit: thresholdForm.unit.trim(),\n direction: thresholdForm.direction,\n severity: thresholdForm.severity,\n limitValue: thresholdForm.limitValue ? Number(thresholdForm.limitValue) : null,\n minValue: thresholdForm.minValue ? Number(thresholdForm.minValue) : null,\n maxValue: thresholdForm.maxValue ? Number(thresholdForm.maxValue) : null,\n });\n setThresholdOpen(false);\n setThresholdForm({\n parameter: '',\n unit: '',\n direction: 'ABOVE',\n severity: 'WARNING',\n limitValue: '',\n minValue: '',\n maxValue: '',\n });\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to add threshold.');\n }\n };\n\n const handleRecordMeasurement = async () => {\n if (!id || !measureForm.parameter.trim() || !measureForm.unit.trim() || !measureForm.value) {\n setError('Parameter, value and unit are required.');\n return;\n }\n setError(null);\n try {\n await recordMeasurement.mutateAsync({\n siteId: id,\n parameter: measureForm.parameter.trim(),\n value: Number(measureForm.value),\n unit: measureForm.unit.trim(),\n recordedAt: new Date().toISOString(),\n sensorId: measureForm.sensorId || null,\n source: 'MANUAL',\n });\n setMeasureOpen(false);\n setMeasureForm({ parameter: '', value: '', unit: '', sensorId: '' });\n measurements.refetch();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to record measurement.');\n }\n };\n\n const handleArchive = async () => {\n if (!id) return;\n setError(null);\n try {\n await archiveSite.mutateAsync(id);\n navigateTo('sites');\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to archive site.');\n }\n };\n\n const sensorColumns: Column<EcoSensor>[] = [\n {\n key: 'name',\n header: 'Sensor',\n render: (s) => (\n <div>\n <p className=\"font-medium text-text-primary\">{s.name}</p>\n <p className=\"text-xs text-text-tertiary\">{formatEnumLabel(s.kind)}</p>\n </div>\n ),\n },\n {\n key: 'unit',\n header: 'Unit',\n hideOnMobile: true,\n render: (s) => <span className=\"text-text-secondary\">{s.unit ?? '—'}</span>,\n },\n { key: 'status', header: 'Status', render: (s) => <StatusBadge status={s.status} /> },\n {\n key: 'action',\n header: '',\n align: 'right',\n render: (s) => (\n <Select\n value={s.status}\n onValueChange={(v) => setSensorStatus.mutate({ id: s.id, status: v as EcoSensorStatus })}\n >\n <SelectTrigger className=\"h-8 w-36\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {SENSOR_STATUSES.map((st) => (\n <SelectItem key={st} value={st}>\n {formatEnumLabel(st)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n ),\n },\n ];\n\n const thresholdColumns: Column<EcoAlertThreshold>[] = [\n {\n key: 'parameter',\n header: 'Parameter',\n render: (t) => <span className=\"font-medium text-text-primary\">{t.parameter}</span>,\n },\n {\n key: 'rule',\n header: 'Rule',\n render: (t) => (\n <span className=\"text-text-secondary\">\n {t.direction === 'OUTSIDE_RANGE'\n ? `${formatNumber(t.minValue)}–${formatNumber(t.maxValue)} ${t.unit}`\n : `${formatEnumLabel(t.direction)} ${formatNumber(t.limitValue)} ${t.unit}`}\n </span>\n ),\n },\n { key: 'severity', header: 'Severity', render: (t) => <StatusBadge status={t.severity} /> },\n {\n key: 'active',\n header: 'Active',\n align: 'right',\n render: (t) => (\n <Button\n size=\"sm\"\n variant={t.isActive ? 'outline' : 'ghost'}\n onClick={() => setThresholdActive.mutate({ id: t.id, isActive: !t.isActive })}\n >\n {t.isActive ? 'Active' : 'Inactive'}\n </Button>\n ),\n },\n ];\n\n const measurementColumns: Column<EcoMeasurement>[] = [\n {\n key: 'parameter',\n header: 'Parameter',\n render: (m) => <span className=\"font-medium text-text-primary\">{m.parameter}</span>,\n },\n {\n key: 'value',\n header: 'Value',\n align: 'right',\n render: (m) => (\n <span className=\"tabular-nums text-text-primary\">\n {formatNumber(m.value)} {m.unit}\n </span>\n ),\n },\n {\n key: 'source',\n header: 'Source',\n hideOnMobile: true,\n render: (m) => <span className=\"text-text-secondary\">{formatEnumLabel(m.source)}</span>,\n },\n {\n key: 'breach',\n header: 'Status',\n render: (m) =>\n m.isBreach ? (\n <StatusBadge status=\"BREACH\" className=\"uppercase\" />\n ) : (\n <StatusBadge status=\"OK\" className=\"uppercase\" />\n ),\n },\n {\n key: 'recorded',\n header: 'Recorded',\n align: 'right',\n hideOnMobile: true,\n render: (m) => <span className=\"text-text-tertiary\">{formatDateTime(m.recordedAt)}</span>,\n },\n ];\n\n return (\n <PageLayout\n title={site.name}\n description={[site.code, formatEnumLabel(site.type)].filter(Boolean).join(' · ')}\n actions={\n <>\n <StatusBadge status={site.status} />\n <Button variant=\"ghost\" onClick={() => navigateTo('sites')}>\n <ArrowLeft className=\"mr-1.5 h-4 w-4\" /> Back\n </Button>\n </>\n }\n >\n {/* Hero: overview · quick actions + monitoring summary */}\n <div className=\"grid grid-cols-1 gap-4 lg:grid-cols-3\">\n {/* Overview */}\n <GlassCard treatment=\"glass\" className=\"p-6 lg:col-span-2\">\n <dl className=\"grid grid-cols-2 gap-x-6 gap-y-4 sm:grid-cols-3\">\n <div>\n <dt className=\"text-xs text-text-tertiary\">Type</dt>\n <dd className=\"mt-1\">\n <SiteTypeBadge type={site.type} />\n </dd>\n </div>\n <div>\n <dt className=\"text-xs text-text-tertiary\">Region</dt>\n <dd className=\"mt-0.5 text-sm text-text-primary\">\n {[site.region, site.country].filter(Boolean).join(', ') || '—'}\n </dd>\n </div>\n <div>\n <dt className=\"text-xs text-text-tertiary\">Area</dt>\n <dd className=\"mt-0.5 text-sm text-text-primary tabular-nums\">\n {site.areaHectares != null ? `${formatNumber(site.areaHectares)} ha` : '—'}\n </dd>\n </div>\n <div>\n <dt className=\"text-xs text-text-tertiary\">Coordinates</dt>\n <dd className=\"mt-0.5 text-sm text-text-primary tabular-nums\">\n {formatCoords(site.latitude, site.longitude)}\n </dd>\n </div>\n <div>\n <dt className=\"text-xs text-text-tertiary\">Sensors</dt>\n <dd className=\"mt-0.5 text-sm text-text-primary tabular-nums\">{sensors.length}</dd>\n </div>\n <div>\n <dt className=\"text-xs text-text-tertiary\">Thresholds</dt>\n <dd className=\"mt-0.5 text-sm text-text-primary tabular-nums\">{thresholds.length}</dd>\n </div>\n </dl>\n {site.description && (\n <p className=\"mt-5 rounded-lg bg-bg-sunken/60 p-3 text-sm text-text-secondary\">\n {site.description}\n </p>\n )}\n <div className=\"mt-5 border-t border-border-seam pt-4\">\n <EcoTags entityType=\"ecoimpacthub-site\" entityId={site.id} />\n </div>\n </GlassCard>\n\n {/* Right rail: quick actions + monitoring summary */}\n <div className=\"flex flex-col gap-4\">\n <GlassCard treatment=\"glass\" className=\"p-6\">\n <div className=\"mb-3 flex items-center gap-2\">\n <Zap className=\"h-4 w-4 text-accent\" />\n <h2 className=\"text-base font-semibold text-text-primary\">Quick actions</h2>\n </div>\n <div className=\"flex flex-col gap-2.5\">\n <Button onClick={() => setMeasureOpen(true)}>\n <Activity className=\"mr-1.5 h-4 w-4\" /> Record measurement\n </Button>\n <Button variant=\"outline\" onClick={() => setSensorOpen(true)}>\n <Radio className=\"mr-1.5 h-4 w-4\" /> Add sensor\n </Button>\n <Button variant=\"outline\" onClick={() => setThresholdOpen(true)}>\n <Gauge className=\"mr-1.5 h-4 w-4\" /> Add threshold\n </Button>\n {site.status !== 'ARCHIVED' && (\n <Button\n variant=\"ghost\"\n onClick={handleArchive}\n disabled={archiveSite.isPending}\n className=\"text-status-error-text hover:text-status-error-text\"\n >\n <Archive className=\"mr-1.5 h-4 w-4\" /> Archive site\n </Button>\n )}\n </div>\n </GlassCard>\n\n <GlassCard treatment=\"glass\" className=\"p-6\">\n <div className=\"mb-4 flex items-center gap-2\">\n <Radio className=\"h-4 w-4 text-accent\" />\n <h2 className=\"text-base font-semibold text-text-primary\">Monitoring summary</h2>\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"rounded-xl bg-bg-sunken/60 p-3\">\n <p className=\"text-2xl font-semibold text-text-primary tabular-nums\">\n {onlineSensors}\n <span className=\"text-base text-text-tertiary\">/{sensors.length}</span>\n </p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">Sensors online</p>\n <MiniBar\n className=\"mt-2\"\n value={sensors.length ? (onlineSensors / sensors.length) * 100 : 0}\n barClassName=\"bg-[var(--color-status-success-text)]\"\n />\n </div>\n <div className=\"rounded-xl bg-bg-sunken/60 p-3\">\n <p className=\"text-2xl font-semibold text-text-primary tabular-nums\">\n {activeThresholds}\n <span className=\"text-base text-text-tertiary\">/{thresholds.length}</span>\n </p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">Active thresholds</p>\n <p\n className={\n attentionSensors > 0\n ? 'mt-2 text-xs text-status-warning-text'\n : 'mt-2 text-xs text-text-tertiary'\n }\n >\n {attentionSensors > 0\n ? `${attentionSensors} sensor${attentionSensors > 1 ? 's' : ''} need attention`\n : 'All sensors healthy'}\n </p>\n </div>\n </div>\n <div className=\"mt-4 border-t border-border-seam pt-4\">\n <p className=\"mb-2 text-xs text-text-tertiary\">Latest breach</p>\n {latestBreachRow ? (\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {latestBreachRow.parameter}\n </p>\n <p className=\"text-xs text-text-tertiary tabular-nums\">\n {formatNumber(latestBreachRow.value)} {latestBreachRow.unit} ·{' '}\n {formatRelativeTime(latestBreachRow.recordedAt)}\n </p>\n </div>\n <StatusBadge status=\"BREACH\" className=\"shrink-0 uppercase\" />\n </div>\n ) : (\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <ShieldCheck className=\"h-4 w-4 shrink-0 text-status-success-text\" />\n No breaches recorded\n </div>\n )}\n </div>\n </GlassCard>\n </div>\n </div>\n\n {error && <p className=\"mt-4 text-sm text-status-error-text\">{error}</p>}\n\n {/* Sensors */}\n <Section\n title=\"Sensors\"\n description=\"Instruments streaming or recording readings for this site.\"\n action={\n <Button size=\"sm\" onClick={() => setSensorOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Add sensor\n </Button>\n }\n >\n {sensors.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No sensors\"\n description=\"Add a sensor to begin monitoring.\"\n />\n ) : (\n <DataTable columns={sensorColumns} rows={sensors} rowKey={(s) => s.id} />\n )}\n </Section>\n\n {/* Thresholds */}\n <Section\n title=\"Alert thresholds\"\n description=\"Rules that flag measurements as breaches when values drift out of range.\"\n action={\n <Button size=\"sm\" onClick={() => setThresholdOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Add threshold\n </Button>\n }\n >\n {thresholds.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No thresholds\"\n description=\"Define a threshold to flag breaches.\"\n />\n ) : (\n <DataTable columns={thresholdColumns} rows={thresholds} rowKey={(t) => t.id} />\n )}\n </Section>\n\n {/* Recent measurements */}\n <Section title=\"Recent measurements\" description=\"The latest readings recorded at this site.\">\n {measurementRows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No measurements yet\"\n description=\"Record a measurement to see it here.\"\n />\n ) : (\n <DataTable columns={measurementColumns} rows={measurementRows} rowKey={(m) => m.id} />\n )}\n </Section>\n\n {/* Add sensor dialog */}\n <ResponsiveDialog open={sensorOpen} onOpenChange={setSensorOpen}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Add sensor</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Register a sensor at {site.name}.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"grid gap-4 py-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"sensorName\">Name *</Label>\n <Input\n id=\"sensorName\"\n value={sensorForm.name}\n onChange={(e) => setSensorForm({ ...sensorForm, name: e.target.value })}\n placeholder=\"North gate PM2.5\"\n />\n </div>\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"sensorKind\">Kind</Label>\n <Select\n value={sensorForm.kind}\n onValueChange={(v) => setSensorForm({ ...sensorForm, kind: v as EcoSensorKind })}\n >\n <SelectTrigger id=\"sensorKind\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {SENSOR_KINDS.map((k) => (\n <SelectItem key={k} value={k}>\n {formatEnumLabel(k)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"sensorUnit\">Unit</Label>\n <Input\n id=\"sensorUnit\"\n value={sensorForm.unit}\n onChange={(e) => setSensorForm({ ...sensorForm, unit: e.target.value })}\n placeholder=\"µg/m³\"\n />\n </div>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"sensorHw\">Hardware ID</Label>\n <Input\n id=\"sensorHw\"\n value={sensorForm.hardwareId}\n onChange={(e) => setSensorForm({ ...sensorForm, hardwareId: e.target.value })}\n placeholder=\"Optional device identifier\"\n />\n </div>\n </div>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setSensorOpen(false)}>\n Cancel\n </Button>\n <Button onClick={handleAddSensor} disabled={createSensor.isPending}>\n {createSensor.isPending ? 'Adding…' : 'Add sensor'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Add threshold dialog */}\n <ResponsiveDialog open={thresholdOpen} onOpenChange={setThresholdOpen}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Add alert threshold</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Flag readings that drift out of an acceptable range.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"grid gap-4 py-2\">\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"thParam\">Parameter *</Label>\n <Input\n id=\"thParam\"\n value={thresholdForm.parameter}\n onChange={(e) =>\n setThresholdForm({ ...thresholdForm, parameter: e.target.value })\n }\n placeholder=\"PM2.5\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"thUnit\">Unit *</Label>\n <Input\n id=\"thUnit\"\n value={thresholdForm.unit}\n onChange={(e) => setThresholdForm({ ...thresholdForm, unit: e.target.value })}\n placeholder=\"µg/m³\"\n />\n </div>\n </div>\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"thDir\">Direction</Label>\n <Select\n value={thresholdForm.direction}\n onValueChange={(v) =>\n setThresholdForm({ ...thresholdForm, direction: v as EcoThresholdDirection })\n }\n >\n <SelectTrigger id=\"thDir\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {THRESHOLD_DIRECTIONS.map((d) => (\n <SelectItem key={d} value={d}>\n {formatEnumLabel(d)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"thSev\">Severity</Label>\n <Select\n value={thresholdForm.severity}\n onValueChange={(v) =>\n setThresholdForm({ ...thresholdForm, severity: v as EcoThresholdSeverity })\n }\n >\n <SelectTrigger id=\"thSev\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {THRESHOLD_SEVERITIES.map((s) => (\n <SelectItem key={s} value={s}>\n {formatEnumLabel(s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n {thresholdForm.direction === 'OUTSIDE_RANGE' ? (\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"thMin\">Min value</Label>\n <Input\n id=\"thMin\"\n type=\"number\"\n step=\"any\"\n value={thresholdForm.minValue}\n onChange={(e) =>\n setThresholdForm({ ...thresholdForm, minValue: e.target.value })\n }\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"thMax\">Max value</Label>\n <Input\n id=\"thMax\"\n type=\"number\"\n step=\"any\"\n value={thresholdForm.maxValue}\n onChange={(e) =>\n setThresholdForm({ ...thresholdForm, maxValue: e.target.value })\n }\n />\n </div>\n </div>\n ) : (\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"thLimit\">Limit value</Label>\n <Input\n id=\"thLimit\"\n type=\"number\"\n step=\"any\"\n value={thresholdForm.limitValue}\n onChange={(e) =>\n setThresholdForm({ ...thresholdForm, limitValue: e.target.value })\n }\n placeholder=\"35\"\n />\n </div>\n )}\n </div>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setThresholdOpen(false)}>\n Cancel\n </Button>\n <Button onClick={handleAddThreshold} disabled={createThreshold.isPending}>\n {createThreshold.isPending ? 'Adding…' : 'Add threshold'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Record measurement dialog */}\n <ResponsiveDialog open={measureOpen} onOpenChange={setMeasureOpen}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Record measurement</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Log a reading. It is checked against active thresholds and flagged if it breaches.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"grid gap-4 py-2\">\n <div className=\"grid gap-2 sm:grid-cols-3\">\n <div className=\"grid gap-1.5 sm:col-span-1\">\n <Label htmlFor=\"mParam\">Parameter *</Label>\n <Input\n id=\"mParam\"\n value={measureForm.parameter}\n onChange={(e) => setMeasureForm({ ...measureForm, parameter: e.target.value })}\n placeholder=\"PM2.5\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"mValue\">Value *</Label>\n <Input\n id=\"mValue\"\n type=\"number\"\n step=\"any\"\n value={measureForm.value}\n onChange={(e) => setMeasureForm({ ...measureForm, value: e.target.value })}\n placeholder=\"42\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"mUnit\">Unit *</Label>\n <Input\n id=\"mUnit\"\n value={measureForm.unit}\n onChange={(e) => setMeasureForm({ ...measureForm, unit: e.target.value })}\n placeholder=\"µg/m³\"\n />\n </div>\n </div>\n {sensors.length > 0 && (\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"mSensor\">Sensor (optional)</Label>\n <Select\n value={measureForm.sensorId}\n onValueChange={(v) => setMeasureForm({ ...measureForm, sensorId: v })}\n >\n <SelectTrigger id=\"mSensor\">\n <SelectValue placeholder=\"Manual entry\" />\n </SelectTrigger>\n <SelectContent>\n {sensors.map((s) => (\n <SelectItem key={s.id} value={s.id}>\n {s.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <p className=\"text-xs text-text-tertiary\">\n Select a specific sensor for context, or leave blank for a site-level reading.\n </p>\n </div>\n )}\n {error && <p className=\"text-sm text-status-error-text\">{error}</p>}\n </div>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setMeasureOpen(false)}>\n Cancel\n </Button>\n <Button onClick={handleRecordMeasurement} disabled={recordMeasurement.isPending}>\n {recordMeasurement.isPending ? 'Recording…' : 'Record'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|