@burdenoff/microfe-ecoimpacthub 2026.819.3 → 2026.827.1

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.
Files changed (45) hide show
  1. package/dist/dev/main.d.ts.map +1 -1
  2. package/dist/ecoimpacthub/hooks/index.d.ts +1 -0
  3. package/dist/ecoimpacthub/hooks/index.d.ts.map +1 -1
  4. package/dist/ecoimpacthub/hooks/index.js +1 -0
  5. package/dist/ecoimpacthub/hooks/useActiveEcoEntity.d.ts +11 -0
  6. package/dist/ecoimpacthub/hooks/useActiveEcoEntity.d.ts.map +1 -0
  7. package/dist/ecoimpacthub/hooks/useActiveEcoEntity.js +21 -0
  8. package/dist/ecoimpacthub/hooks/useActiveEcoEntity.js.map +1 -0
  9. package/dist/ecoimpacthub/pages/_shared/MethodPicker.d.ts +15 -0
  10. package/dist/ecoimpacthub/pages/_shared/MethodPicker.d.ts.map +1 -0
  11. package/dist/ecoimpacthub/pages/_shared/MethodPicker.js +36 -0
  12. package/dist/ecoimpacthub/pages/_shared/MethodPicker.js.map +1 -0
  13. package/dist/ecoimpacthub/pages/_shared/UseMyLocationButton.d.ts +15 -0
  14. package/dist/ecoimpacthub/pages/_shared/UseMyLocationButton.d.ts.map +1 -0
  15. package/dist/ecoimpacthub/pages/_shared/UseMyLocationButton.js +66 -0
  16. package/dist/ecoimpacthub/pages/_shared/UseMyLocationButton.js.map +1 -0
  17. package/dist/ecoimpacthub/pages/habitat/HabitatProjectDetailPage.d.ts.map +1 -1
  18. package/dist/ecoimpacthub/pages/habitat/HabitatProjectDetailPage.js +127 -124
  19. package/dist/ecoimpacthub/pages/habitat/HabitatProjectDetailPage.js.map +1 -1
  20. package/dist/ecoimpacthub/pages/observations/ObservationsPage.d.ts.map +1 -1
  21. package/dist/ecoimpacthub/pages/observations/ObservationsPage.js +226 -224
  22. package/dist/ecoimpacthub/pages/observations/ObservationsPage.js.map +1 -1
  23. package/dist/ecoimpacthub/pages/reports/ReportDetailPage.d.ts.map +1 -1
  24. package/dist/ecoimpacthub/pages/reports/ReportDetailPage.js +159 -156
  25. package/dist/ecoimpacthub/pages/reports/ReportDetailPage.js.map +1 -1
  26. package/dist/ecoimpacthub/pages/sites/SiteDetailPage.d.ts.map +1 -1
  27. package/dist/ecoimpacthub/pages/sites/SiteDetailPage.js +40 -37
  28. package/dist/ecoimpacthub/pages/sites/SiteDetailPage.js.map +1 -1
  29. package/dist/ecoimpacthub/pages/sites/SitesPage.d.ts.map +1 -1
  30. package/dist/ecoimpacthub/pages/sites/SitesPage.js +208 -171
  31. package/dist/ecoimpacthub/pages/sites/SitesPage.js.map +1 -1
  32. package/dist/ecoimpacthub/pages/species/SpeciesDetailPage.d.ts.map +1 -1
  33. package/dist/ecoimpacthub/pages/species/SpeciesDetailPage.js +211 -214
  34. package/dist/ecoimpacthub/pages/species/SpeciesDetailPage.js.map +1 -1
  35. package/dist/ecoimpacthub/pages/species/SpeciesPage.d.ts.map +1 -1
  36. package/dist/ecoimpacthub/pages/species/SpeciesPage.js +174 -157
  37. package/dist/ecoimpacthub/pages/species/SpeciesPage.js.map +1 -1
  38. package/dist/ecoimpacthub/utils/activeEntity.d.ts +30 -0
  39. package/dist/ecoimpacthub/utils/activeEntity.d.ts.map +1 -0
  40. package/dist/ecoimpacthub/utils/activeEntity.js +12 -0
  41. package/dist/ecoimpacthub/utils/activeEntity.js.map +1 -0
  42. package/dist/ecoimpacthub/utils/pageContext.d.ts.map +1 -1
  43. package/dist/ecoimpacthub/utils/pageContext.js +30 -25
  44. package/dist/ecoimpacthub/utils/pageContext.js.map +1 -1
  45. package/package.json +4 -1
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/dev/main.tsx"],"names":[],"mappings":"AAIA,OAAO,cAAc,CAAC"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/dev/main.tsx"],"names":[],"mappings":"AAMA,OAAO,cAAc,CAAC"}
@@ -8,4 +8,5 @@ export * from './useEcoFiles';
8
8
  export * from './useEcoComments';
9
9
  export * from './useEcoTags';
10
10
  export * from './useDashboardLayout';
11
+ export * from './useActiveEcoEntity';
11
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ecoimpacthub/hooks/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ecoimpacthub/hooks/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC"}
@@ -4,3 +4,4 @@ import "./useCursorPager.js";
4
4
  import "./useEcoFiles.js";
5
5
  import "./useEcoComments.js";
6
6
  import "./useEcoTags.js";
7
+ import "./useActiveEcoEntity.js";
@@ -0,0 +1,11 @@
1
+ /**
2
+ * useActiveEcoEntity — publish the record a detail page is showing so the
3
+ * assistant can refer to it by name.
4
+ * @module ecoimpacthub/hooks
5
+ *
6
+ * Call it on every detail page with the loaded record (or `null` while it is
7
+ * still loading / errored). The registration is cleared on unmount so a name
8
+ * never outlives the page that owned it.
9
+ */
10
+ export declare function useActiveEcoEntity(id: string | null | undefined, name: string | null | undefined): void;
11
+ //# sourceMappingURL=useActiveEcoEntity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useActiveEcoEntity.d.ts","sourceRoot":"","sources":["../../../src/ecoimpacthub/hooks/useActiveEcoEntity.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC7B,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC9B,IAAI,CAWN"}
@@ -0,0 +1,21 @@
1
+ import { setActiveEcoEntity as e } from "../utils/activeEntity.js";
2
+ import { useEffect as t } from "react";
3
+ //#region src/ecoimpacthub/hooks/useActiveEcoEntity.ts
4
+ function n(n, r) {
5
+ t(() => {
6
+ if (!n || !r) {
7
+ e(null);
8
+ return;
9
+ }
10
+ return e({
11
+ id: n,
12
+ name: r
13
+ }), () => {
14
+ e(null);
15
+ };
16
+ }, [n, r]);
17
+ }
18
+ //#endregion
19
+ export { n as useActiveEcoEntity };
20
+
21
+ //# sourceMappingURL=useActiveEcoEntity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useActiveEcoEntity.js","names":[],"sources":["../../../src/ecoimpacthub/hooks/useActiveEcoEntity.ts"],"sourcesContent":["/**\n * useActiveEcoEntity — publish the record a detail page is showing so the\n * assistant can refer to it by name.\n * @module ecoimpacthub/hooks\n *\n * Call it on every detail page with the loaded record (or `null` while it is\n * still loading / errored). The registration is cleared on unmount so a name\n * never outlives the page that owned it.\n */\n\nimport { useEffect } from 'react';\nimport { setActiveEcoEntity } from '../utils/activeEntity';\n\nexport function useActiveEcoEntity(\n id: string | null | undefined,\n name: string | null | undefined\n): void {\n useEffect(() => {\n if (!id || !name) {\n setActiveEcoEntity(null);\n return;\n }\n setActiveEcoEntity({ id, name });\n return () => {\n setActiveEcoEntity(null);\n };\n }, [id, name]);\n}\n"],"mappings":";;;AAaA,SAAgB,EACd,GACA,GACM;CACN,QAAgB;EACd,IAAI,CAAC,KAAM,CAAC,GAAM;GAChB,EAAmB,IAAI;GACvB;EACF;EAEA,OADA,EAAmB;GAAE;GAAI;EAAK,CAAC,SAClB;GACX,EAAmB,IAAI;EACzB;CACF,GAAG,CAAC,GAAI,CAAI,CAAC;AACf"}
@@ -0,0 +1,15 @@
1
+ import { FC } from 'react';
2
+ import { EcoObservationMethod } from '../../types';
3
+
4
+ interface MethodPickerProps {
5
+ /** Full option set, in display order. */
6
+ options: readonly EcoObservationMethod[];
7
+ value: EcoObservationMethod;
8
+ onChange: (method: EcoObservationMethod) => void;
9
+ /** Accessible name for the group (rendered by the caller's own label). */
10
+ label: string;
11
+ className?: string;
12
+ }
13
+ export declare const MethodPicker: FC<MethodPickerProps>;
14
+ export {};
15
+ //# sourceMappingURL=MethodPicker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MethodPicker.d.ts","sourceRoot":"","sources":["../../../../src/ecoimpacthub/pages/_shared/MethodPicker.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAchC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAYxD,UAAU,iBAAiB;IACzB,yCAAyC;IACzC,OAAO,EAAE,SAAS,oBAAoB,EAAE,CAAC;IACzC,KAAK,EAAE,oBAAoB,CAAC;IAC5B,QAAQ,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACjD,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,YAAY,EAAE,EAAE,CAAC,iBAAiB,CA+B9C,CAAC"}
@@ -0,0 +1,36 @@
1
+ import { cn as e } from "../../utils/cn.js";
2
+ import { formatEnumLabel as t } from "../../utils/formatters.js";
3
+ import { AudioLines as n, Camera as r, Check as i, Dna as a, Eye as o, Footprints as s, SatelliteDish as c, Users as l } from "lucide-react";
4
+ import { jsx as u, jsxs as d } from "react/jsx-runtime";
5
+ //#region src/ecoimpacthub/pages/_shared/MethodPicker.tsx
6
+ var f = {
7
+ DIRECT_SIGHTING: o,
8
+ CAMERA_TRAP: r,
9
+ ACOUSTIC: n,
10
+ TRACKS_SIGNS: s,
11
+ EDNA: a,
12
+ TELEMETRY: c,
13
+ COMMUNITY_REPORT: l
14
+ }, p = ({ options: n, value: r, onChange: a, label: s, className: c }) => /* @__PURE__ */ u("div", {
15
+ role: "radiogroup",
16
+ "aria-label": s,
17
+ className: e("flex flex-wrap gap-1.5", c),
18
+ children: n.map((n) => {
19
+ let s = n === r, c = s ? i : f[n.toUpperCase()] ?? o;
20
+ return /* @__PURE__ */ d("button", {
21
+ type: "button",
22
+ role: "radio",
23
+ "aria-checked": s,
24
+ onClick: () => a(n),
25
+ className: e("inline-flex items-center gap-1.5 rounded-full border px-3 py-1.5 text-sm font-medium transition-colors", s ? "border-transparent bg-action-primary-bg text-action-primary-text" : "border-border-seam bg-bg-surface text-text-secondary hover:bg-bg-sunken hover:text-text-primary"),
26
+ children: [/* @__PURE__ */ u(c, {
27
+ className: "h-3.5 w-3.5 shrink-0",
28
+ "aria-hidden": "true"
29
+ }), t(n)]
30
+ }, n);
31
+ })
32
+ });
33
+ //#endregion
34
+ export { p as MethodPicker };
35
+
36
+ //# sourceMappingURL=MethodPicker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MethodPicker.js","names":[],"sources":["../../../../src/ecoimpacthub/pages/_shared/MethodPicker.tsx"],"sourcesContent":["/**\n * MethodPicker — one-tap chip group for choosing an observation method.\n * @module ecoimpacthub/pages/_shared\n *\n * Field recording happens on a phone, one-handed, so the survey method is a\n * chip group rather than a dropdown: every option is visible and reachable in a\n * single tap. Presentational-only; semantic tokens exclusively. Shared by the\n * Observations feed and the Species detail record dialogs so the two\n * record-observation surfaces stay identical.\n */\n\nimport type { FC } from 'react';\nimport {\n AudioLines,\n Camera,\n Check,\n Dna,\n Eye,\n Footprints,\n SatelliteDish,\n Users,\n type LucideIcon,\n} from 'lucide-react';\nimport { cn } from '../../utils/cn';\nimport { formatEnumLabel } from '../../utils/formatters';\nimport type { EcoObservationMethod } from '../../types';\n\nconst METHOD_ICON: Record<string, LucideIcon> = {\n DIRECT_SIGHTING: Eye,\n CAMERA_TRAP: Camera,\n ACOUSTIC: AudioLines,\n TRACKS_SIGNS: Footprints,\n EDNA: Dna,\n TELEMETRY: SatelliteDish,\n COMMUNITY_REPORT: Users,\n};\n\ninterface MethodPickerProps {\n /** Full option set, in display order. */\n options: readonly EcoObservationMethod[];\n value: EcoObservationMethod;\n onChange: (method: EcoObservationMethod) => void;\n /** Accessible name for the group (rendered by the caller's own label). */\n label: string;\n className?: string;\n}\n\nexport const MethodPicker: FC<MethodPickerProps> = ({\n options,\n value,\n onChange,\n label,\n className,\n}) => (\n <div role=\"radiogroup\" aria-label={label} className={cn('flex flex-wrap gap-1.5', className)}>\n {options.map((method) => {\n const selected = method === value;\n const Icon = selected ? Check : (METHOD_ICON[method.toUpperCase()] ?? Eye);\n return (\n <button\n key={method}\n type=\"button\"\n role=\"radio\"\n aria-checked={selected}\n onClick={() => onChange(method)}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-full border px-3 py-1.5 text-sm font-medium transition-colors',\n selected\n ? 'border-transparent bg-action-primary-bg text-action-primary-text'\n : 'border-border-seam bg-bg-surface text-text-secondary hover:bg-bg-sunken hover:text-text-primary'\n )}\n >\n <Icon className=\"h-3.5 w-3.5 shrink-0\" aria-hidden=\"true\" />\n {formatEnumLabel(method)}\n </button>\n );\n })}\n </div>\n);\n"],"mappings":";;;;;AA2BA,IAAM,IAA0C;CAC9C,iBAAiB;CACjB,aAAa;CACb,UAAU;CACV,cAAc;CACd,MAAM;CACN,WAAW;CACX,kBAAkB;AACpB,GAYa,KAAuC,EAClD,YACA,UACA,aACA,UACA,mBAEA,kBAAC,OAAD;CAAK,MAAK;CAAa,cAAY;CAAO,WAAW,EAAG,0BAA0B,CAAS;WACxF,EAAQ,KAAK,MAAW;EACvB,IAAM,IAAW,MAAW,GACtB,IAAO,IAAW,IAAS,EAAY,EAAO,YAAY,MAAM;EACtE,OACE,kBAAC,UAAD;GAEE,MAAK;GACL,MAAK;GACL,gBAAc;GACd,eAAe,EAAS,CAAM;GAC9B,WAAW,EACT,0GACA,IACI,qEACA,iGACN;aAXF,CAaE,kBAAC,GAAD;IAAM,WAAU;IAAuB,eAAY;GAAQ,CAAA,GAC1D,EAAgB,CAAM,CACjB;KAdD,CAcC;CAEZ,CAAC;AACE,CAAA"}
@@ -0,0 +1,15 @@
1
+ import { FC } from 'react';
2
+
3
+ export interface CapturedCoordinates {
4
+ latitude: number;
5
+ longitude: number;
6
+ /** Reported horizontal accuracy in metres, when the device supplies one. */
7
+ accuracyMeters: number | null;
8
+ }
9
+ interface UseMyLocationButtonProps {
10
+ onCapture: (coords: CapturedCoordinates) => void;
11
+ className?: string;
12
+ }
13
+ export declare const UseMyLocationButton: FC<UseMyLocationButtonProps>;
14
+ export {};
15
+ //# sourceMappingURL=UseMyLocationButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UseMyLocationButton.d.ts","sourceRoot":"","sources":["../../../../src/ecoimpacthub/pages/_shared/UseMyLocationButton.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAY,KAAK,EAAE,EAAE,MAAM,OAAO,CAAC;AAK1C,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,UAAU,wBAAwB;IAChC,SAAS,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAiBD,eAAO,MAAM,mBAAmB,EAAE,EAAE,CAAC,wBAAwB,CAsD5D,CAAC"}
@@ -0,0 +1,66 @@
1
+ import { cn as e } from "../../utils/cn.js";
2
+ import { useState as t } from "react";
3
+ import { Crosshair as n, LoaderCircle as r } from "lucide-react";
4
+ import { jsx as i, jsxs as a } from "react/jsx-runtime";
5
+ import { Button as o } from "@burdenoff/fe-libs/ui";
6
+ //#region src/ecoimpacthub/pages/_shared/UseMyLocationButton.tsx
7
+ var s = 15e3;
8
+ function c(e) {
9
+ switch (e.code) {
10
+ case e.PERMISSION_DENIED: return "Location permission denied — enter the coordinates manually.";
11
+ case e.POSITION_UNAVAILABLE: return "No position fix available right now — try again outdoors.";
12
+ case e.TIMEOUT: return "Timed out waiting for a GPS fix — try again.";
13
+ default: return "Could not read this device’s location.";
14
+ }
15
+ }
16
+ var l = ({ onCapture: l, className: u }) => {
17
+ let d = typeof navigator < "u" && "geolocation" in navigator, [f, p] = t(!1), [m, h] = t(null), [g, _] = t(null);
18
+ return d ? /* @__PURE__ */ a("div", {
19
+ className: e("grid gap-1.5", u),
20
+ children: [/* @__PURE__ */ a(o, {
21
+ type: "button",
22
+ variant: "outline",
23
+ size: "sm",
24
+ onClick: () => {
25
+ p(!0), h(null), navigator.geolocation.getCurrentPosition((e) => {
26
+ let t = Number.isFinite(e.coords.accuracy) ? e.coords.accuracy : null;
27
+ _(t), p(!1), l({
28
+ latitude: e.coords.latitude,
29
+ longitude: e.coords.longitude,
30
+ accuracyMeters: t
31
+ });
32
+ }, (e) => {
33
+ _(null), p(!1), h(c(e));
34
+ }, {
35
+ enableHighAccuracy: !0,
36
+ timeout: s,
37
+ maximumAge: 0
38
+ });
39
+ },
40
+ disabled: f,
41
+ children: [f ? /* @__PURE__ */ i(r, {
42
+ className: "mr-1.5 h-4 w-4 animate-spin",
43
+ "aria-hidden": "true"
44
+ }) : /* @__PURE__ */ i(n, {
45
+ className: "mr-1.5 h-4 w-4",
46
+ "aria-hidden": "true"
47
+ }), f ? "Locating…" : "Use my location"]
48
+ }), m ? /* @__PURE__ */ i("p", {
49
+ className: "text-xs text-status-error-text",
50
+ role: "status",
51
+ children: m
52
+ }) : g == null ? null : /* @__PURE__ */ a("p", {
53
+ className: "text-xs text-text-tertiary",
54
+ role: "status",
55
+ children: [
56
+ "GPS fix accurate to ±",
57
+ Math.round(g),
58
+ " m."
59
+ ]
60
+ })]
61
+ }) : null;
62
+ };
63
+ //#endregion
64
+ export { l as UseMyLocationButton };
65
+
66
+ //# sourceMappingURL=UseMyLocationButton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UseMyLocationButton.js","names":[],"sources":["../../../../src/ecoimpacthub/pages/_shared/UseMyLocationButton.tsx"],"sourcesContent":["/**\n * UseMyLocationButton — capture the device's GPS fix into a coordinate pair.\n * @module ecoimpacthub/pages/_shared\n *\n * Field observations are recorded where the animal is, on a phone; typing a\n * decimal latitude by hand is not a field workflow. This reads the browser\n * Geolocation API — real device data, never a synthesised coordinate — and\n * hands the caller the fix.\n *\n * Renders nothing when the browser exposes no Geolocation API, so the control\n * is never present unless it can actually do something. Permission denial and\n * timeouts surface as inline text rather than silently leaving the fields blank.\n */\n\nimport { useState, type FC } from 'react';\nimport { Crosshair, LoaderCircle } from 'lucide-react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../../utils/cn';\n\nexport interface CapturedCoordinates {\n latitude: number;\n longitude: number;\n /** Reported horizontal accuracy in metres, when the device supplies one. */\n accuracyMeters: number | null;\n}\n\ninterface UseMyLocationButtonProps {\n onCapture: (coords: CapturedCoordinates) => void;\n className?: string;\n}\n\nconst GEOLOCATION_TIMEOUT_MS = 15000;\n\nfunction describeGeolocationError(error: GeolocationPositionError): string {\n switch (error.code) {\n case error.PERMISSION_DENIED:\n return 'Location permission denied — enter the coordinates manually.';\n case error.POSITION_UNAVAILABLE:\n return 'No position fix available right now — try again outdoors.';\n case error.TIMEOUT:\n return 'Timed out waiting for a GPS fix — try again.';\n default:\n return 'Could not read this device’s location.';\n }\n}\n\nexport const UseMyLocationButton: FC<UseMyLocationButtonProps> = ({ onCapture, className }) => {\n const supported = typeof navigator !== 'undefined' && 'geolocation' in navigator;\n const [pending, setPending] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [accuracy, setAccuracy] = useState<number | null>(null);\n\n if (!supported) return null;\n\n const capture = () => {\n setPending(true);\n setError(null);\n navigator.geolocation.getCurrentPosition(\n (position) => {\n const accuracyMeters = Number.isFinite(position.coords.accuracy)\n ? position.coords.accuracy\n : null;\n setAccuracy(accuracyMeters);\n setPending(false);\n onCapture({\n latitude: position.coords.latitude,\n longitude: position.coords.longitude,\n accuracyMeters,\n });\n },\n (positionError) => {\n setAccuracy(null);\n setPending(false);\n setError(describeGeolocationError(positionError));\n },\n { enableHighAccuracy: true, timeout: GEOLOCATION_TIMEOUT_MS, maximumAge: 0 }\n );\n };\n\n return (\n <div className={cn('grid gap-1.5', className)}>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={capture} disabled={pending}>\n {pending ? (\n <LoaderCircle className=\"mr-1.5 h-4 w-4 animate-spin\" aria-hidden=\"true\" />\n ) : (\n <Crosshair className=\"mr-1.5 h-4 w-4\" aria-hidden=\"true\" />\n )}\n {pending ? 'Locating…' : 'Use my location'}\n </Button>\n {error ? (\n <p className=\"text-xs text-status-error-text\" role=\"status\">\n {error}\n </p>\n ) : accuracy != null ? (\n <p className=\"text-xs text-text-tertiary\" role=\"status\">\n GPS fix accurate to ±{Math.round(accuracy)} m.\n </p>\n ) : null}\n </div>\n );\n};\n"],"mappings":";;;;;;AA+BA,IAAM,IAAyB;AAE/B,SAAS,EAAyB,GAAyC;CACzE,QAAQ,EAAM,MAAd;EACE,KAAK,EAAM,mBACT,OAAO;EACT,KAAK,EAAM,sBACT,OAAO;EACT,KAAK,EAAM,SACT,OAAO;EACT,SACE,OAAO;CACX;AACF;AAEA,IAAa,KAAqD,EAAE,cAAW,mBAAgB;CAC7F,IAAM,IAAY,OAAO,YAAc,OAAe,iBAAiB,WACjE,CAAC,GAAS,KAAc,EAAS,EAAK,GACtC,CAAC,GAAO,KAAY,EAAwB,IAAI,GAChD,CAAC,GAAU,KAAe,EAAwB,IAAI;CA6B5D,OA3BK,IA4BH,kBAAC,OAAD;EAAK,WAAW,EAAG,gBAAgB,CAAS;YAA5C,CACE,kBAAC,GAAD;GAAQ,MAAK;GAAS,SAAQ;GAAU,MAAK;GAAK,eA3BhC;IAGpB,AAFA,EAAW,EAAI,GACf,EAAS,IAAI,GACb,UAAU,YAAY,oBACnB,MAAa;KACZ,IAAM,IAAiB,OAAO,SAAS,EAAS,OAAO,QAAQ,IAC3D,EAAS,OAAO,WAChB;KAGJ,AAFA,EAAY,CAAc,GAC1B,EAAW,EAAK,GAChB,EAAU;MACR,UAAU,EAAS,OAAO;MAC1B,WAAW,EAAS,OAAO;MAC3B;KACF,CAAC;IACH,IACC,MAAkB;KAGjB,AAFA,EAAY,IAAI,GAChB,EAAW,EAAK,GAChB,EAAS,EAAyB,CAAa,CAAC;IAClD,GACA;KAAE,oBAAoB;KAAM,SAAS;KAAwB,YAAY;IAAE,CAC7E;GACF;GAIwE,UAAU;aAA9E,CACG,IACC,kBAAC,GAAD;IAAc,WAAU;IAA8B,eAAY;GAAQ,CAAA,IAE1E,kBAAC,GAAD;IAAW,WAAU;IAAiB,eAAY;GAAQ,CAAA,GAE3D,IAAU,cAAc,iBACnB;MACP,IACC,kBAAC,KAAD;GAAG,WAAU;GAAiC,MAAK;aAChD;EACA,CAAA,IACD,KAAY,OAIZ,OAHF,kBAAC,KAAD;GAAG,WAAU;GAA6B,MAAK;aAA/C;IAAwD;IAChC,KAAK,MAAM,CAAQ;IAAE;GAC1C;IAEF;MA9CgB;AAgDzB"}
@@ -1 +1 @@
1
- {"version":3,"file":"HabitatProjectDetailPage.d.ts","sourceRoot":"","sources":["../../../../src/ecoimpacthub/pages/habitat/HabitatProjectDetailPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAmGH,wBAAgB,wBAAwB,4CA6VvC"}
1
+ {"version":3,"file":"HabitatProjectDetailPage.d.ts","sourceRoot":"","sources":["../../../../src/ecoimpacthub/pages/habitat/HabitatProjectDetailPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAoGH,wBAAgB,wBAAwB,4CAgWvC"}