@burdenoff/microfe-ecoimpacthub 2026.720.4 → 2026.720.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/components/DashboardCustomizerDialog.d.ts +11 -0
- package/dist/ecoimpacthub/components/DashboardCustomizerDialog.d.ts.map +1 -0
- package/dist/ecoimpacthub/components/DashboardCustomizerDialog.js +182 -0
- package/dist/ecoimpacthub/components/DashboardCustomizerDialog.js.map +1 -0
- package/dist/ecoimpacthub/hooks/index.d.ts +1 -0
- package/dist/ecoimpacthub/hooks/index.d.ts.map +1 -1
- package/dist/ecoimpacthub/hooks/index.js +1 -0
- package/dist/ecoimpacthub/hooks/useDashboardLayout.d.ts +29 -0
- package/dist/ecoimpacthub/hooks/useDashboardLayout.d.ts.map +1 -0
- package/dist/ecoimpacthub/hooks/useDashboardLayout.js +95 -0
- package/dist/ecoimpacthub/hooks/useDashboardLayout.js.map +1 -0
- package/dist/ecoimpacthub/pages/DashboardPage.d.ts +7 -0
- package/dist/ecoimpacthub/pages/DashboardPage.d.ts.map +1 -1
- package/dist/ecoimpacthub/pages/DashboardPage.js +505 -447
- package/dist/ecoimpacthub/pages/DashboardPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DashboardPage.js","names":[],"sources":["../../../src/ecoimpacthub/pages/DashboardPage.tsx"],"sourcesContent":["/**\n * Dashboard — EcoImpactHub environment, conservation & sustainability overview.\n * @module ecoimpacthub/pages\n *\n * KPIs are aggregated client-side from the real `eco*` list endpoints plus the\n * `ecoCarbonTotalKg` aggregate (by scope). Widgets show recent field\n * observations and open threshold breaches. No mock data.\n */\n\nimport { useMemo } from 'react';\nimport {\n MapPin,\n PawPrint,\n Trees,\n Factory,\n TriangleAlert,\n Binoculars,\n ShieldCheck,\n ArrowRight,\n Radio,\n} from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport {\n BarChart,\n Bar,\n XAxis,\n YAxis,\n CartesianGrid,\n ResponsiveContainer,\n Tooltip,\n Cell,\n LabelList,\n} from 'recharts';\nimport { PageLayout } from '../components/PageLayout';\nimport { MetricsCard } from '../components/MetricsCard';\nimport { StatusBadge } from '../components/StatusBadge';\nimport { MiniBar } from '../components/MiniBar';\nimport { LoadingView, Section } from '../components/StateViews';\nimport { EmptyState } from '../components/EmptyState';\nimport { ScopeSplitBar, ScopeLegend, Meter, SCOPE_HUE } from './carbonVisuals';\nimport { useEcoImpactHub } from '../context';\nimport {\n useEcoSites,\n useEcoSpecies,\n useEcoHabitatProjects,\n useEcoCarbonTotals,\n useEcoMeasurements,\n useEcoObservations,\n useEcoSensors,\n} from '../hooks';\nimport {\n formatNumber,\n formatRelativeTime,\n formatCo2e,\n formatEnumLabel,\n formatPercent,\n} from '../utils/formatters';\nimport type { EcoConservationStatus, EcoEmissionScope } from '../types';\n\nconst ACTIVE_PROJECT_STATUSES = new Set(['PLANNED', 'IN_PROGRESS', 'MONITORING']);\n\n/** Conservation-status → meter tint (semantic, IUCN-severity ordered). */\nconst CONSERVATION_TONE: Record<EcoConservationStatus, string> = {\n LEAST_CONCERN: 'var(--color-status-success-text)',\n NEAR_THREATENED: 'var(--color-accent-teal)',\n VULNERABLE: 'var(--color-status-warning-text)',\n ENDANGERED: 'var(--color-status-error-text)',\n CRITICALLY_ENDANGERED: 'var(--color-status-error-text)',\n EXTINCT_IN_WILD: 'var(--color-text-tertiary)',\n DATA_DEFICIENT: 'var(--color-text-tertiary)',\n NOT_EVALUATED: 'var(--color-text-tertiary)',\n};\n\nexport function DashboardPage() {\n const { navigateTo, currentUser } = useEcoImpactHub();\n\n const sites = useEcoSites({ pagination: { limit: 100, offset: 0 } });\n const species = useEcoSpecies({ pagination: { limit: 100, offset: 0 } });\n const projects = useEcoHabitatProjects();\n const carbon = useEcoCarbonTotals();\n const breaches = useEcoMeasurements({ breachesOnly: true, pagination: { limit: 6, offset: 0 } });\n const observations = useEcoObservations({ pagination: { limit: 6, offset: 0 } });\n const sensors = useEcoSensors();\n\n const siteRows = sites.data?.items ?? [];\n const speciesRows = species.data?.items ?? [];\n const projectRows = projects.data?.items ?? [];\n const sensorRows = sensors.data?.items ?? [];\n\n const activeSites = siteRows.filter((s) => s.status === 'ACTIVE').length;\n const priorityCount = speciesRows.filter((s) => s.isPriority).length;\n const threatened = speciesRows.filter((s) =>\n ['VULNERABLE', 'ENDANGERED', 'CRITICALLY_ENDANGERED'].includes(s.conservationStatus)\n ).length;\n const activeProjects = projectRows.filter((p) => ACTIVE_PROJECT_STATUSES.has(p.status)).length;\n const onlineSensors = sensorRows.filter((s) => s.status === 'ONLINE').length;\n const offlineSensors = sensorRows.filter((s) =>\n ['OFFLINE', 'DEGRADED', 'MAINTENANCE'].includes(s.status)\n ).length;\n\n const carbonData = useMemo<{ scope: EcoEmissionScope; label: string; kg: number }[]>(() => {\n const c = carbon.data;\n if (!c) return [];\n return [\n { scope: 'SCOPE_1', label: 'Scope 1', kg: c.scope1 },\n { scope: 'SCOPE_2', label: 'Scope 2', kg: c.scope2 },\n { scope: 'SCOPE_3', label: 'Scope 3', kg: c.scope3 },\n ];\n }, [carbon.data]);\n\n const isInitialLoading =\n sites.isLoading && species.isLoading && projects.isLoading && carbon.isLoading;\n\n if (isInitialLoading) {\n return (\n <PageLayout title=\"EcoImpactHub\" titleGradient aurora>\n <LoadingView label=\"Loading your impact overview…\" />\n </PageLayout>\n );\n }\n\n const greetingName = currentUser?.firstName || currentUser?.name || 'there';\n const breachRows = breaches.data?.items ?? [];\n const observationRows = observations.data?.items ?? [];\n\n return (\n <PageLayout\n title=\"EcoImpactHub\"\n titleGradient\n aurora\n description={`Welcome back, ${greetingName} — your environment, conservation & sustainability control room.`}\n >\n {/* KPI grid */}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6\">\n <MetricsCard\n icon={<MapPin className=\"h-5 w-5\" />}\n value={\n sites.data?.pageInfo?.hasMore ? `${siteRows.length}+` : formatNumber(siteRows.length)\n }\n label=\"Monitored sites\"\n footer={\n <div className=\"space-y-2\">\n <MiniBar\n value={siteRows.length ? (activeSites / siteRows.length) * 100 : 0}\n height={4}\n barClassName=\"bg-action-primary-bg\"\n />\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums text-text-secondary\">{activeSites}</span> active\n </p>\n </div>\n }\n />\n <MetricsCard\n icon={<PawPrint className=\"h-5 w-5\" />}\n value={\n species.data?.pageInfo?.hasMore\n ? `${speciesRows.length}+`\n : formatNumber(speciesRows.length)\n }\n label=\"Species tracked\"\n accent={threatened > 0 ? 'warning' : undefined}\n footer={\n <div className=\"space-y-2\">\n <MiniBar\n value={speciesRows.length ? (threatened / speciesRows.length) * 100 : 0}\n height={4}\n barClassName=\"bg-status-warning-text\"\n />\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums text-text-secondary\">{priorityCount}</span> priority ·{' '}\n <span className=\"tabular-nums text-text-secondary\">{threatened}</span> threatened\n </p>\n </div>\n }\n />\n <MetricsCard\n icon={<Trees className=\"h-5 w-5\" />}\n value={formatNumber(activeProjects)}\n label=\"Active habitat projects\"\n footer={\n <div className=\"space-y-2\">\n <MiniBar\n value={projectRows.length ? (activeProjects / projectRows.length) * 100 : 0}\n height={4}\n barClassName=\"bg-action-primary-bg\"\n />\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums text-text-secondary\">\n {formatNumber(projectRows.length)}\n </span>{' '}\n total\n </p>\n </div>\n }\n />\n <MetricsCard\n icon={<Factory className=\"h-5 w-5\" />}\n value={carbon.data ? formatCo2e(carbon.data.total) : '—'}\n label=\"Total footprint\"\n featured\n footer={\n carbon.data && carbon.data.total > 0 ? (\n <div className=\"space-y-2\">\n <ScopeSplitBar\n scope1={carbon.data.scope1}\n scope2={carbon.data.scope2}\n scope3={carbon.data.scope3}\n height={6}\n />\n <p className=\"text-xs text-text-tertiary\">Scope 1 · 2 · 3 split</p>\n </div>\n ) : undefined\n }\n />\n <MetricsCard\n icon={<Radio className=\"h-5 w-5\" />}\n value={formatNumber(onlineSensors)}\n label=\"Sensors online\"\n accent={offlineSensors > 0 ? 'warning' : 'success'}\n footer={\n <div className=\"space-y-2\">\n <MiniBar\n value={sensorRows.length ? (onlineSensors / sensorRows.length) * 100 : 0}\n height={4}\n barClassName=\"bg-status-success-text\"\n />\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums text-text-secondary\">{offlineSensors}</span> need\n attention\n </p>\n </div>\n }\n />\n <MetricsCard\n icon={<TriangleAlert className=\"h-5 w-5\" />}\n value={\n breaches.data?.pageInfo?.hasMore\n ? `${breachRows.length}+`\n : formatNumber(breachRows.length)\n }\n label=\"Open breaches\"\n accent={breachRows.length > 0 ? 'error' : 'success'}\n footer={\n <p\n className={\n breachRows.length > 0\n ? 'text-xs font-medium text-status-error-text'\n : 'text-xs text-text-tertiary'\n }\n >\n {breachRows.length > 0 ? 'Needs attention' : 'All parameters in range'}\n </p>\n }\n />\n </div>\n\n <div className=\"mt-6 grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* Carbon by scope */}\n <GlassCard treatment=\"glass\" className=\"p-6 lg:col-span-2\">\n <div className=\"mb-4 flex flex-wrap items-start justify-between gap-3\">\n <div>\n <h2 className=\"text-base font-semibold text-text-primary\">\n Carbon footprint by scope\n </h2>\n <p className=\"mt-0.5 text-sm text-text-secondary\">\n GHG Protocol Scope 1 / 2 / 3 emissions (CO₂e).\n </p>\n </div>\n <div className=\"flex items-center gap-4\">\n {carbon.data && carbon.data.total > 0 && (\n <div className=\"text-right\">\n <p className=\"text-lg font-semibold tabular-nums text-text-primary\">\n {formatCo2e(carbon.data.total)}\n </p>\n <p className=\"text-xs text-text-tertiary\">total</p>\n </div>\n )}\n <button\n type=\"button\"\n onClick={() => navigateTo('carbon')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n Ledger <ArrowRight className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n {carbonData.every((d) => d.kg === 0) ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Factory className=\"h-10 w-10\" />}\n title=\"No carbon entries yet\"\n description=\"Record activity in the carbon ledger to see your Scope 1–3 footprint here.\"\n />\n ) : (\n <>\n <div className=\"h-56\">\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <BarChart data={carbonData} margin={{ top: 20, right: 8, left: 8, bottom: 0 }}>\n <defs>\n {carbonData.map((d) => (\n <linearGradient\n key={d.scope}\n id={`ecoScopeGrad-${d.scope}`}\n x1=\"0\"\n y1=\"0\"\n x2=\"0\"\n y2=\"1\"\n >\n <stop\n offset=\"0%\"\n style={{ stopColor: SCOPE_HUE[d.scope], stopOpacity: 1 }}\n />\n <stop\n offset=\"100%\"\n style={{ stopColor: SCOPE_HUE[d.scope], stopOpacity: 0.55 }}\n />\n </linearGradient>\n ))}\n </defs>\n <CartesianGrid\n vertical={false}\n stroke=\"var(--color-border-seam)\"\n strokeOpacity={0.6}\n strokeDasharray=\"3 4\"\n />\n <XAxis\n dataKey=\"label\"\n tick={{ fill: 'var(--color-text-secondary)', fontSize: 12 }}\n axisLine={{ stroke: 'var(--color-border-seam)' }}\n tickLine={false}\n dy={4}\n />\n <YAxis\n tick={{ fill: 'var(--color-text-tertiary)', fontSize: 11 }}\n axisLine={false}\n tickLine={false}\n width={48}\n tickFormatter={(v: number) =>\n v >= 1000 ? `${Math.round(v / 1000)}t` : `${v}`\n }\n />\n <Tooltip\n cursor={{ fill: 'var(--color-bg-sunken)', opacity: 0.4 }}\n contentStyle={{\n background: 'var(--color-bg-surface)',\n border: '1px solid var(--color-border-seam)',\n borderRadius: 12,\n color: 'var(--color-text-primary)',\n }}\n formatter={(value: number) => [formatCo2e(value), 'Emissions']}\n />\n <Bar dataKey=\"kg\" radius={[8, 8, 2, 2]} maxBarSize={72}>\n {carbonData.map((d) => (\n <Cell key={d.scope} fill={`url(#ecoScopeGrad-${d.scope})`} />\n ))}\n <LabelList\n dataKey=\"kg\"\n position=\"top\"\n formatter={(v: number) => (v > 0 ? formatCo2e(v) : '')}\n style={{\n fill: 'var(--color-text-secondary)',\n fontSize: 11,\n fontWeight: 600,\n }}\n />\n </Bar>\n </BarChart>\n </ResponsiveContainer>\n </div>\n <ScopeLegend data={carbonData} className=\"mt-4 border-t border-border-seam pt-4\" />\n </>\n )}\n </GlassCard>\n\n {/* Recent breaches */}\n <GlassCard treatment=\"glass\" className=\"p-6\">\n <div className=\"mb-4 flex items-center justify-between gap-3\">\n <div className=\"flex items-center gap-2\">\n <h2 className=\"text-base font-semibold text-text-primary\">Recent breaches</h2>\n {breachRows.length > 0 && (\n <span className=\"rounded-full bg-status-error-bg-subtle px-2 py-0.5 text-xs font-medium tabular-nums text-status-error-text\">\n {breaches.data?.pageInfo?.hasMore ? `${breachRows.length}+` : breachRows.length}\n </span>\n )}\n </div>\n <button\n type=\"button\"\n onClick={() => navigateTo('monitoring')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n Monitoring <ArrowRight className=\"h-4 w-4\" />\n </button>\n </div>\n {breachRows.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center rounded-xl border border-dashed border-border-seam py-10 text-center\">\n <span className=\"flex size-12 items-center justify-center rounded-full bg-status-success-bg-subtle\">\n <ShieldCheck className=\"h-6 w-6 text-status-success-text\" />\n </span>\n <p className=\"mt-3 text-sm font-medium text-text-primary\">All parameters in range</p>\n <p className=\"mt-1 text-xs text-text-tertiary\">No open threshold breaches.</p>\n </div>\n ) : (\n <ul className=\"space-y-2.5\">\n {breachRows.map((m) => (\n <li\n key={m.id}\n className=\"flex items-start justify-between gap-3 rounded-xl bg-bg-sunken/50 p-3\"\n >\n <div className=\"flex min-w-0 items-start gap-2.5\">\n <span\n aria-hidden=\"true\"\n className=\"mt-1.5 size-2 shrink-0 rounded-full bg-status-error-text motion-safe:animate-vc-glow-pulse\"\n />\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {m.parameter}\n </p>\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums\">\n {formatNumber(m.value)} {m.unit}\n </span>{' '}\n · {formatRelativeTime(m.recordedAt)}\n </p>\n </div>\n </div>\n <StatusBadge status=\"BREACH\" />\n </li>\n ))}\n </ul>\n )}\n </GlassCard>\n </div>\n\n {/* Recent observations */}\n <Section\n title=\"Recent field observations\"\n description=\"Latest wildlife & biodiversity sightings recorded across your sites.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('observations')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n All observations <ArrowRight className=\"h-4 w-4\" />\n </button>\n }\n >\n {observationRows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Binoculars className=\"h-10 w-10\" />}\n title=\"No observations yet\"\n description=\"Record your first field observation to build your biodiversity dataset.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {observationRows.map((o) => (\n <GlassCard\n key={o.id}\n treatment=\"plain\"\n className=\"p-4 transition-shadow hover:shadow-float\"\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex min-w-0 items-start gap-2.5\">\n <span className=\"mt-0.5 flex size-8 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-accent\">\n <Binoculars className=\"h-4 w-4\" />\n </span>\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold text-text-primary\">\n {o.species?.commonName ?? 'Unknown species'}\n </p>\n {o.species?.scientificName && (\n <p className=\"truncate text-xs italic text-text-tertiary\">\n {o.species.scientificName}\n </p>\n )}\n </div>\n </div>\n <span className=\"shrink-0 rounded-full bg-[var(--color-accent-soft)] px-2 py-0.5 text-xs font-medium tabular-nums text-text-primary\">\n ×{formatNumber(o.count)}\n </span>\n </div>\n <div className=\"mt-3 flex items-center justify-between border-t border-border-seam/70 pt-2.5\">\n <span className=\"rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary\">\n {formatEnumLabel(o.method)}\n </span>\n <span className=\"text-xs text-text-tertiary\">\n {formatRelativeTime(o.observedAt)}\n </span>\n </div>\n </GlassCard>\n ))}\n </div>\n )}\n </Section>\n\n {/* Conservation status snapshot */}\n {speciesRows.length > 0 && (\n <Section\n title=\"Conservation status\"\n description=\"Distribution of tracked species by IUCN-style status.\"\n >\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <div className=\"space-y-3.5\">\n {(() => {\n const counts = new Map<string, number>();\n for (const s of speciesRows) {\n counts.set(s.conservationStatus, (counts.get(s.conservationStatus) ?? 0) + 1);\n }\n const total = speciesRows.length;\n return [...counts.entries()]\n .sort((a, b) => b[1] - a[1])\n .map(([status, count]) => {\n const pct = (count / total) * 100;\n const tone =\n CONSERVATION_TONE[status as EcoConservationStatus] ??\n 'var(--color-text-tertiary)';\n return (\n <div key={status} className=\"flex items-center gap-3 sm:gap-4\">\n <div className=\"w-36 shrink-0 sm:w-48\">\n <StatusBadge status={status} />\n </div>\n <Meter value={pct} color={tone} className=\"flex-1\" />\n <span className=\"w-9 shrink-0 text-right text-sm font-medium tabular-nums text-text-primary\">\n {count}\n </span>\n <span className=\"hidden w-12 shrink-0 text-right text-xs tabular-nums text-text-tertiary sm:inline\">\n {formatPercent(pct)}\n </span>\n </div>\n );\n });\n })()}\n </div>\n </GlassCard>\n </Section>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DashboardPage.js","names":[],"sources":["../../../src/ecoimpacthub/pages/DashboardPage.tsx"],"sourcesContent":["/**\n * Dashboard — EcoImpactHub environment, conservation & sustainability overview.\n * @module ecoimpacthub/pages\n *\n * KPIs are aggregated client-side from the real `eco*` list endpoints plus the\n * `ecoCarbonTotalKg` aggregate (by scope). Widgets show recent field\n * observations and open threshold breaches. No mock data.\n *\n * Customizable (BOFF-3943): the \"Customize dashboard\" CTA opens a widget\n * palette (show/hide/reorder + freeform notes), persisted per user/workspace\n * via {@link useDashboardLayout}. Uncustomized users see the exact same\n * layout as before this change — the widget order defaults to this file's\n * original hardcoded order, and the carbon-by-scope / recent-breaches pair\n * keeps its original side-by-side layout whenever they remain adjacent.\n */\n\nimport { useMemo, useState, type ReactNode } from 'react';\nimport {\n MapPin,\n PawPrint,\n Trees,\n Factory,\n TriangleAlert,\n Binoculars,\n ShieldCheck,\n ArrowRight,\n Radio,\n LayoutGrid,\n StickyNote,\n} from 'lucide-react';\nimport { Button, GlassCard } from '@burdenoff/fe-libs/ui';\nimport {\n BarChart,\n Bar,\n XAxis,\n YAxis,\n CartesianGrid,\n ResponsiveContainer,\n Tooltip,\n Cell,\n LabelList,\n} from 'recharts';\nimport { PageLayout } from '../components/PageLayout';\nimport { MetricsCard } from '../components/MetricsCard';\nimport { StatusBadge } from '../components/StatusBadge';\nimport { MiniBar } from '../components/MiniBar';\nimport { LoadingView, Section } from '../components/StateViews';\nimport { EmptyState } from '../components/EmptyState';\nimport { DashboardCustomizerDialog } from '../components/DashboardCustomizerDialog';\nimport { ScopeSplitBar, ScopeLegend, Meter, SCOPE_HUE } from './carbonVisuals';\nimport { useEcoImpactHub } from '../context';\nimport {\n useEcoSites,\n useEcoSpecies,\n useEcoHabitatProjects,\n useEcoCarbonTotals,\n useEcoMeasurements,\n useEcoObservations,\n useEcoSensors,\n useDashboardLayout,\n type DashboardWidgetId,\n} from '../hooks';\nimport {\n formatNumber,\n formatRelativeTime,\n formatCo2e,\n formatEnumLabel,\n formatPercent,\n} from '../utils/formatters';\nimport type { EcoConservationStatus, EcoEmissionScope } from '../types';\n\nconst ACTIVE_PROJECT_STATUSES = new Set(['PLANNED', 'IN_PROGRESS', 'MONITORING']);\n\n/** Conservation-status → meter tint (semantic, IUCN-severity ordered). */\nconst CONSERVATION_TONE: Record<EcoConservationStatus, string> = {\n LEAST_CONCERN: 'var(--color-status-success-text)',\n NEAR_THREATENED: 'var(--color-accent-teal)',\n VULNERABLE: 'var(--color-status-warning-text)',\n ENDANGERED: 'var(--color-status-error-text)',\n CRITICALLY_ENDANGERED: 'var(--color-status-error-text)',\n EXTINCT_IN_WILD: 'var(--color-text-tertiary)',\n DATA_DEFICIENT: 'var(--color-text-tertiary)',\n NOT_EVALUATED: 'var(--color-text-tertiary)',\n};\n\nexport function DashboardPage() {\n const { navigateTo, currentUser, workspaceId } = useEcoImpactHub();\n const [customizerOpen, setCustomizerOpen] = useState(false);\n const dashboardLayout = useDashboardLayout(workspaceId, currentUser?.id);\n\n const sites = useEcoSites({ pagination: { limit: 100, offset: 0 } });\n const species = useEcoSpecies({ pagination: { limit: 100, offset: 0 } });\n const projects = useEcoHabitatProjects();\n const carbon = useEcoCarbonTotals();\n const breaches = useEcoMeasurements({ breachesOnly: true, pagination: { limit: 6, offset: 0 } });\n const observations = useEcoObservations({ pagination: { limit: 6, offset: 0 } });\n const sensors = useEcoSensors();\n\n const siteRows = sites.data?.items ?? [];\n const speciesRows = species.data?.items ?? [];\n const projectRows = projects.data?.items ?? [];\n const sensorRows = sensors.data?.items ?? [];\n\n const activeSites = siteRows.filter((s) => s.status === 'ACTIVE').length;\n const priorityCount = speciesRows.filter((s) => s.isPriority).length;\n const threatened = speciesRows.filter((s) =>\n ['VULNERABLE', 'ENDANGERED', 'CRITICALLY_ENDANGERED'].includes(s.conservationStatus)\n ).length;\n const activeProjects = projectRows.filter((p) => ACTIVE_PROJECT_STATUSES.has(p.status)).length;\n const onlineSensors = sensorRows.filter((s) => s.status === 'ONLINE').length;\n const offlineSensors = sensorRows.filter((s) =>\n ['OFFLINE', 'DEGRADED', 'MAINTENANCE'].includes(s.status)\n ).length;\n\n const carbonData = useMemo<{ scope: EcoEmissionScope; label: string; kg: number }[]>(() => {\n const c = carbon.data;\n if (!c) return [];\n return [\n { scope: 'SCOPE_1', label: 'Scope 1', kg: c.scope1 },\n { scope: 'SCOPE_2', label: 'Scope 2', kg: c.scope2 },\n { scope: 'SCOPE_3', label: 'Scope 3', kg: c.scope3 },\n ];\n }, [carbon.data]);\n\n const isInitialLoading =\n sites.isLoading && species.isLoading && projects.isLoading && carbon.isLoading;\n\n const headerActions = (\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => setCustomizerOpen(true)}>\n <LayoutGrid className=\"mr-1.5 h-3.5 w-3.5\" />\n Customize dashboard\n </Button>\n );\n\n if (isInitialLoading) {\n return (\n <PageLayout title=\"EcoImpactHub\" titleGradient aurora actions={headerActions}>\n <LoadingView label=\"Loading your impact overview…\" />\n </PageLayout>\n );\n }\n\n const greetingName = currentUser?.firstName || currentUser?.name || 'there';\n const breachRows = breaches.data?.items ?? [];\n const observationRows = observations.data?.items ?? [];\n\n const kpisWidget = (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6\">\n <MetricsCard\n icon={<MapPin className=\"h-5 w-5\" />}\n value={\n sites.data?.pageInfo?.hasMore ? `${siteRows.length}+` : formatNumber(siteRows.length)\n }\n label=\"Monitored sites\"\n footer={\n <div className=\"space-y-2\">\n <MiniBar\n value={siteRows.length ? (activeSites / siteRows.length) * 100 : 0}\n height={4}\n barClassName=\"bg-action-primary-bg\"\n />\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums text-text-secondary\">{activeSites}</span> active\n </p>\n </div>\n }\n />\n <MetricsCard\n icon={<PawPrint className=\"h-5 w-5\" />}\n value={\n species.data?.pageInfo?.hasMore\n ? `${speciesRows.length}+`\n : formatNumber(speciesRows.length)\n }\n label=\"Species tracked\"\n accent={threatened > 0 ? 'warning' : undefined}\n footer={\n <div className=\"space-y-2\">\n <MiniBar\n value={speciesRows.length ? (threatened / speciesRows.length) * 100 : 0}\n height={4}\n barClassName=\"bg-status-warning-text\"\n />\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums text-text-secondary\">{priorityCount}</span> priority ·{' '}\n <span className=\"tabular-nums text-text-secondary\">{threatened}</span> threatened\n </p>\n </div>\n }\n />\n <MetricsCard\n icon={<Trees className=\"h-5 w-5\" />}\n value={formatNumber(activeProjects)}\n label=\"Active habitat projects\"\n footer={\n <div className=\"space-y-2\">\n <MiniBar\n value={projectRows.length ? (activeProjects / projectRows.length) * 100 : 0}\n height={4}\n barClassName=\"bg-action-primary-bg\"\n />\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums text-text-secondary\">\n {formatNumber(projectRows.length)}\n </span>{' '}\n total\n </p>\n </div>\n }\n />\n <MetricsCard\n icon={<Factory className=\"h-5 w-5\" />}\n value={carbon.data ? formatCo2e(carbon.data.total) : '—'}\n label=\"Total footprint\"\n featured\n footer={\n carbon.data && carbon.data.total > 0 ? (\n <div className=\"space-y-2\">\n <ScopeSplitBar\n scope1={carbon.data.scope1}\n scope2={carbon.data.scope2}\n scope3={carbon.data.scope3}\n height={6}\n />\n <p className=\"text-xs text-text-tertiary\">Scope 1 · 2 · 3 split</p>\n </div>\n ) : undefined\n }\n />\n <MetricsCard\n icon={<Radio className=\"h-5 w-5\" />}\n value={formatNumber(onlineSensors)}\n label=\"Sensors online\"\n accent={offlineSensors > 0 ? 'warning' : 'success'}\n footer={\n <div className=\"space-y-2\">\n <MiniBar\n value={sensorRows.length ? (onlineSensors / sensorRows.length) * 100 : 0}\n height={4}\n barClassName=\"bg-status-success-text\"\n />\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums text-text-secondary\">{offlineSensors}</span> need\n attention\n </p>\n </div>\n }\n />\n <MetricsCard\n icon={<TriangleAlert className=\"h-5 w-5\" />}\n value={\n breaches.data?.pageInfo?.hasMore\n ? `${breachRows.length}+`\n : formatNumber(breachRows.length)\n }\n label=\"Open breaches\"\n accent={breachRows.length > 0 ? 'error' : 'success'}\n footer={\n <p\n className={\n breachRows.length > 0\n ? 'text-xs font-medium text-status-error-text'\n : 'text-xs text-text-tertiary'\n }\n >\n {breachRows.length > 0 ? 'Needs attention' : 'All parameters in range'}\n </p>\n }\n />\n </div>\n );\n\n const carbonByScopeWidget = (\n <GlassCard treatment=\"glass\" className=\"p-6 h-full\">\n <div className=\"mb-4 flex flex-wrap items-start justify-between gap-3\">\n <div>\n <h2 className=\"text-base font-semibold text-text-primary\">Carbon footprint by scope</h2>\n <p className=\"mt-0.5 text-sm text-text-secondary\">\n GHG Protocol Scope 1 / 2 / 3 emissions (CO₂e).\n </p>\n </div>\n <div className=\"flex items-center gap-4\">\n {carbon.data && carbon.data.total > 0 && (\n <div className=\"text-right\">\n <p className=\"text-lg font-semibold tabular-nums text-text-primary\">\n {formatCo2e(carbon.data.total)}\n </p>\n <p className=\"text-xs text-text-tertiary\">total</p>\n </div>\n )}\n <button\n type=\"button\"\n onClick={() => navigateTo('carbon')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n Ledger <ArrowRight className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n {carbonData.every((d) => d.kg === 0) ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Factory className=\"h-10 w-10\" />}\n title=\"No carbon entries yet\"\n description=\"Record activity in the carbon ledger to see your Scope 1–3 footprint here.\"\n />\n ) : (\n <>\n <div className=\"h-56\">\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <BarChart data={carbonData} margin={{ top: 20, right: 8, left: 8, bottom: 0 }}>\n <defs>\n {carbonData.map((d) => (\n <linearGradient\n key={d.scope}\n id={`ecoScopeGrad-${d.scope}`}\n x1=\"0\"\n y1=\"0\"\n x2=\"0\"\n y2=\"1\"\n >\n <stop offset=\"0%\" style={{ stopColor: SCOPE_HUE[d.scope], stopOpacity: 1 }} />\n <stop\n offset=\"100%\"\n style={{ stopColor: SCOPE_HUE[d.scope], stopOpacity: 0.55 }}\n />\n </linearGradient>\n ))}\n </defs>\n <CartesianGrid\n vertical={false}\n stroke=\"var(--color-border-seam)\"\n strokeOpacity={0.6}\n strokeDasharray=\"3 4\"\n />\n <XAxis\n dataKey=\"label\"\n tick={{ fill: 'var(--color-text-secondary)', fontSize: 12 }}\n axisLine={{ stroke: 'var(--color-border-seam)' }}\n tickLine={false}\n dy={4}\n />\n <YAxis\n tick={{ fill: 'var(--color-text-tertiary)', fontSize: 11 }}\n axisLine={false}\n tickLine={false}\n width={48}\n tickFormatter={(v: number) => (v >= 1000 ? `${Math.round(v / 1000)}t` : `${v}`)}\n />\n <Tooltip\n cursor={{ fill: 'var(--color-bg-sunken)', opacity: 0.4 }}\n contentStyle={{\n background: 'var(--color-bg-surface)',\n border: '1px solid var(--color-border-seam)',\n borderRadius: 12,\n color: 'var(--color-text-primary)',\n }}\n formatter={(value: number) => [formatCo2e(value), 'Emissions']}\n />\n <Bar dataKey=\"kg\" radius={[8, 8, 2, 2]} maxBarSize={72}>\n {carbonData.map((d) => (\n <Cell key={d.scope} fill={`url(#ecoScopeGrad-${d.scope})`} />\n ))}\n <LabelList\n dataKey=\"kg\"\n position=\"top\"\n formatter={(v: number) => (v > 0 ? formatCo2e(v) : '')}\n style={{ fill: 'var(--color-text-secondary)', fontSize: 11, fontWeight: 600 }}\n />\n </Bar>\n </BarChart>\n </ResponsiveContainer>\n </div>\n <ScopeLegend data={carbonData} className=\"mt-4 border-t border-border-seam pt-4\" />\n </>\n )}\n </GlassCard>\n );\n\n const recentBreachesWidget = (\n <GlassCard treatment=\"glass\" className=\"p-6 h-full\">\n <div className=\"mb-4 flex items-center justify-between gap-3\">\n <div className=\"flex items-center gap-2\">\n <h2 className=\"text-base font-semibold text-text-primary\">Recent breaches</h2>\n {breachRows.length > 0 && (\n <span className=\"rounded-full bg-status-error-bg-subtle px-2 py-0.5 text-xs font-medium tabular-nums text-status-error-text\">\n {breaches.data?.pageInfo?.hasMore ? `${breachRows.length}+` : breachRows.length}\n </span>\n )}\n </div>\n <button\n type=\"button\"\n onClick={() => navigateTo('monitoring')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n Monitoring <ArrowRight className=\"h-4 w-4\" />\n </button>\n </div>\n {breachRows.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center rounded-xl border border-dashed border-border-seam py-10 text-center\">\n <span className=\"flex size-12 items-center justify-center rounded-full bg-status-success-bg-subtle\">\n <ShieldCheck className=\"h-6 w-6 text-status-success-text\" />\n </span>\n <p className=\"mt-3 text-sm font-medium text-text-primary\">All parameters in range</p>\n <p className=\"mt-1 text-xs text-text-tertiary\">No open threshold breaches.</p>\n </div>\n ) : (\n <ul className=\"space-y-2.5\">\n {breachRows.map((m) => (\n <li\n key={m.id}\n className=\"flex items-start justify-between gap-3 rounded-xl bg-bg-sunken/50 p-3\"\n >\n <div className=\"flex min-w-0 items-start gap-2.5\">\n <span\n aria-hidden=\"true\"\n className=\"mt-1.5 size-2 shrink-0 rounded-full bg-status-error-text motion-safe:animate-vc-glow-pulse\"\n />\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{m.parameter}</p>\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums\">\n {formatNumber(m.value)} {m.unit}\n </span>{' '}\n · {formatRelativeTime(m.recordedAt)}\n </p>\n </div>\n </div>\n <StatusBadge status=\"BREACH\" />\n </li>\n ))}\n </ul>\n )}\n </GlassCard>\n );\n\n const recentObservationsWidget = (\n <Section\n title=\"Recent field observations\"\n description=\"Latest wildlife & biodiversity sightings recorded across your sites.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('observations')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n All observations <ArrowRight className=\"h-4 w-4\" />\n </button>\n }\n >\n {observationRows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Binoculars className=\"h-10 w-10\" />}\n title=\"No observations yet\"\n description=\"Record your first field observation to build your biodiversity dataset.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {observationRows.map((o) => (\n <GlassCard\n key={o.id}\n treatment=\"plain\"\n className=\"p-4 transition-shadow hover:shadow-float\"\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex min-w-0 items-start gap-2.5\">\n <span className=\"mt-0.5 flex size-8 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-accent\">\n <Binoculars className=\"h-4 w-4\" />\n </span>\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold text-text-primary\">\n {o.species?.commonName ?? 'Unknown species'}\n </p>\n {o.species?.scientificName && (\n <p className=\"truncate text-xs italic text-text-tertiary\">\n {o.species.scientificName}\n </p>\n )}\n </div>\n </div>\n <span className=\"shrink-0 rounded-full bg-[var(--color-accent-soft)] px-2 py-0.5 text-xs font-medium tabular-nums text-text-primary\">\n ×{formatNumber(o.count)}\n </span>\n </div>\n <div className=\"mt-3 flex items-center justify-between border-t border-border-seam/70 pt-2.5\">\n <span className=\"rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary\">\n {formatEnumLabel(o.method)}\n </span>\n <span className=\"text-xs text-text-tertiary\">\n {formatRelativeTime(o.observedAt)}\n </span>\n </div>\n </GlassCard>\n ))}\n </div>\n )}\n </Section>\n );\n\n const conservationStatusWidget =\n speciesRows.length > 0 ? (\n <Section\n title=\"Conservation status\"\n description=\"Distribution of tracked species by IUCN-style status.\"\n >\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <div className=\"space-y-3.5\">\n {(() => {\n const counts = new Map<string, number>();\n for (const s of speciesRows) {\n counts.set(s.conservationStatus, (counts.get(s.conservationStatus) ?? 0) + 1);\n }\n const total = speciesRows.length;\n return [...counts.entries()]\n .sort((a, b) => b[1] - a[1])\n .map(([status, count]) => {\n const pct = (count / total) * 100;\n const tone =\n CONSERVATION_TONE[status as EcoConservationStatus] ??\n 'var(--color-text-tertiary)';\n return (\n <div key={status} className=\"flex items-center gap-3 sm:gap-4\">\n <div className=\"w-36 shrink-0 sm:w-48\">\n <StatusBadge status={status} />\n </div>\n <Meter value={pct} color={tone} className=\"flex-1\" />\n <span className=\"w-9 shrink-0 text-right text-sm font-medium tabular-nums text-text-primary\">\n {count}\n </span>\n <span className=\"hidden w-12 shrink-0 text-right text-xs tabular-nums text-text-tertiary sm:inline\">\n {formatPercent(pct)}\n </span>\n </div>\n );\n });\n })()}\n </div>\n </GlassCard>\n </Section>\n ) : null;\n\n const WIDGET_RENDER: Record<DashboardWidgetId, ReactNode> = {\n kpis: kpisWidget,\n carbonByScope: carbonByScopeWidget,\n recentBreaches: recentBreachesWidget,\n recentObservations: recentObservationsWidget,\n conservationStatus: conservationStatusWidget,\n };\n\n const order = dashboardLayout.layout.widgetOrder;\n const blocks: ReactNode[] = [];\n for (let i = 0; i < order.length; i++) {\n const id = order[i];\n if (id === 'kpis') {\n blocks.push(<div key={id}>{WIDGET_RENDER[id]}</div>);\n continue;\n }\n // Preserve the original side-by-side carbon-by-scope / recent-breaches pairing\n // whenever they remain adjacent in that order (the default, uncustomized case).\n if (id === 'carbonByScope' && order[i + 1] === 'recentBreaches') {\n blocks.push(\n <div key={`${id}-pair`} className=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n <div className=\"lg:col-span-2\">{carbonByScopeWidget}</div>\n <div>{recentBreachesWidget}</div>\n </div>\n );\n i += 1;\n continue;\n }\n if (id === 'recentBreaches' && order[i - 1] === 'carbonByScope') {\n // Already rendered as part of the pair above.\n continue;\n }\n const content = WIDGET_RENDER[id];\n if (!content) continue;\n blocks.push(<div key={id}>{content}</div>);\n }\n\n return (\n <PageLayout\n title=\"EcoImpactHub\"\n titleGradient\n aurora\n description={`Welcome back, ${greetingName} — your environment, conservation & sustainability control room.`}\n actions={headerActions}\n >\n <div className=\"space-y-6\">\n {blocks}\n\n {dashboardLayout.layout.notes.length > 0 && (\n <Section title=\"Custom notes\" description=\"Your personal notes, visible only to you.\">\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {dashboardLayout.layout.notes.map((note) => (\n <GlassCard key={note.id} treatment=\"plain\" className=\"p-4\">\n <div className=\"flex items-start gap-2.5\">\n <StickyNote className=\"mt-0.5 h-4 w-4 shrink-0 text-accent\" />\n <div className=\"min-w-0\">\n {note.title && (\n <p className=\"truncate text-sm font-semibold text-text-primary\">\n {note.title}\n </p>\n )}\n {note.body && (\n <p className=\"mt-0.5 text-sm text-text-secondary\">{note.body}</p>\n )}\n </div>\n </div>\n </GlassCard>\n ))}\n </div>\n </Section>\n )}\n </div>\n\n <DashboardCustomizerDialog\n open={customizerOpen}\n onOpenChange={setCustomizerOpen}\n layout={dashboardLayout}\n />\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|