@pablo2410/shared-ui 0.28.2 → 0.29.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.
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  OpiTourHost,
3
3
  TakeTheTourButton
4
- } from "./chunk-BG4DVZAY.js";
4
+ } from "./chunk-L54JNSVS.js";
5
5
  import {
6
6
  platformUrl,
7
7
  portalUrl
8
- } from "./chunk-7LI4Y6WT.js";
8
+ } from "./chunk-IK3B4YWQ.js";
9
9
  import {
10
10
  readActiveEnterpriseCookie
11
11
  } from "./chunk-5YS2PQN3.js";
@@ -1257,4 +1257,4 @@ export {
1257
1257
  policyDeploymentConfig,
1258
1258
  SERVICE_CONFIGS
1259
1259
  };
1260
- //# sourceMappingURL=chunk-ZVAS2BMF.js.map
1260
+ //# sourceMappingURL=chunk-32NHRLWI.js.map
@@ -9,6 +9,13 @@ var SANDBOX_LOCALHOST_PORTS = {
9
9
  businesshub: 3004,
10
10
  connect: 3005,
11
11
  "policy-deployment": 3006,
12
+ // Policy Deployment's actual subdomainLabel (catalog.ts) and the label
13
+ // SharedFooter.tsx calls platformUrl() with are both "policy", not
14
+ // "policy-deployment" - that mismatch left those two callers falling
15
+ // through to production on localhost (#128). Same port as the
16
+ // "policy-deployment" key above; kept both since nothing calls
17
+ // platformUrl() with "policy-deployment" to make that key worth removing.
18
+ policy: 3006,
12
19
  "marketing-site": 3007
13
20
  };
14
21
  function getPlatformDomain() {
@@ -44,4 +51,4 @@ export {
44
51
  platformUrl,
45
52
  portalUrl
46
53
  };
47
- //# sourceMappingURL=chunk-7LI4Y6WT.js.map
54
+ //# sourceMappingURL=chunk-IK3B4YWQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/services/platformDomain.ts"],"sourcesContent":["/**\n * Platform domain resolution — single source of truth for building\n * cross-service URLs (Service Hub cards, sidebar cross-nav, footers,\n * \"Back to Portal\" links).\n *\n * Every service runs on a sibling subdomain of one parent domain\n * (portal.oplyticsdigital.net, sqdcp.oplyticsdigital.net, …), so the parent\n * domain is derived at runtime from the current hostname: strip the service\n * label, keep the rest. Links therefore stay on whichever domain the app is\n * served from — no per-app build args, and a future domain move needs no\n * code change here.\n *\n * Outside a browser (SSR, tests) or on hosts with no parent domain\n * (raw IPs) the canonical production domain is used.\n *\n * The local MacBook sandbox (docker-compose.sandbox.yml) is a special case:\n * every app is served from bare `localhost` on a fixed port per service,\n * not from sibling subdomains — so there is no domain to derive from the\n * hostname the way there is for sandbox-*.oplyticsdigital.net (the tunnel\n * variant) or real subdomains. Without this map, any cross-service link\n * built from `localhost` silently fell back to production (see incident:\n * Business Hub's sidebar link sent an authenticated sandbox user to\n * businesshub.oplyticsdigital.net instead of localhost:3004). This list\n * must be kept in sync with the port assignments in each sandbox compose\n * file — there is currently no single generated source for both.\n */\n\nexport const DEFAULT_PLATFORM_DOMAIN = \"oplyticsdigital.net\";\n\nconst IPV4 = /^\\d{1,3}(\\.\\d{1,3}){3}$/;\n\n/**\n * Fixed local-sandbox port per service subdomain label. Keys match the\n * actual subdomain label / sandbox compose service name each app uses to\n * call platformUrl(...) (e.g. \"oeemanager\", not the hyphenated\n * \"/oee-manager\" Infisical secrets folder — those two naming schemes are\n * already inconsistent elsewhere in this codebase; don't propagate that\n * inconsistency here).\n */\nexport const SANDBOX_LOCALHOST_PORTS: Record<string, number> = {\n portal: 3000,\n sqdcp: 3001,\n oeemanager: 3002,\n actionmanager: 3003,\n businesshub: 3004,\n connect: 3005,\n \"policy-deployment\": 3006,\n // Policy Deployment's actual subdomainLabel (catalog.ts) and the label\n // SharedFooter.tsx calls platformUrl() with are both \"policy\", not\n // \"policy-deployment\" - that mismatch left those two callers falling\n // through to production on localhost (#128). Same port as the\n // \"policy-deployment\" key above; kept both since nothing calls\n // platformUrl() with \"policy-deployment\" to make that key worth removing.\n policy: 3006,\n \"marketing-site\": 3007,\n};\n\nexport function getPlatformDomain(): string {\n if (typeof window === \"undefined\") return DEFAULT_PLATFORM_DOMAIN;\n const host = window.location.hostname;\n if (!host || IPV4.test(host)) {\n return DEFAULT_PLATFORM_DOMAIN;\n }\n if (host === \"localhost\") {\n // Handled by platformUrl's port-map branch below — a bare domain has no\n // meaning on localhost, since every sandbox service shares the same\n // host and differs only by port. Falling back to the production domain\n // here would be wrong for any localhost caller, so this path should\n // only be reached by code that doesn't also need a port (rare); return\n // the production domain as the least-surprising default for that case.\n return DEFAULT_PLATFORM_DOMAIN;\n }\n const parts = host.split(\".\");\n // sqdcp.oplyticsdigital.net → oplyticsdigital.net\n if (parts.length >= 3) return parts.slice(1).join(\".\");\n // already on the apex (oplyticsdigital.net)\n if (parts.length === 2) return host;\n return DEFAULT_PLATFORM_DOMAIN;\n}\n\n/**\n * Absolute URL for a service subdomain label on the current platform\n * domain, e.g. `platformUrl(\"sqdcp\")` → `https://sqdcp.oplyticsdigital.net`.\n *\n * On the local sandbox (hostname === \"localhost\"), resolves instead to\n * `http://localhost:<port>` using SANDBOX_LOCALHOST_PORTS, so cross-service\n * links (e.g. Portal's sidebar → Business Hub) stay local instead of\n * silently jumping to production.\n */\nexport function platformUrl(subdomainLabel: string, path = \"\"): string {\n if (\n typeof window !== \"undefined\" &&\n window.location.hostname === \"localhost\"\n ) {\n const port = SANDBOX_LOCALHOST_PORTS[subdomainLabel];\n if (port !== undefined) {\n return `http://localhost:${port}${path}`;\n }\n // Unknown label on localhost — no safe local target exists, so fall\n // through to the production URL rather than guessing a port. Better to\n // land on a working page and be visibly wrong (prod) than a broken one.\n }\n return `https://${subdomainLabel}.${getPlatformDomain()}${path}`;\n}\n\n/** Portal URL builder — the portal always lives at the `portal` label. */\nexport function portalUrl(path = \"\"): string {\n return platformUrl(\"portal\", path);\n}\n"],"mappings":";AA2BO,IAAM,0BAA0B;AAEvC,IAAM,OAAO;AAUN,IAAM,0BAAkD;AAAA,EAC7D,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,aAAa;AAAA,EACb,SAAS;AAAA,EACT,qBAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOrB,QAAQ;AAAA,EACR,kBAAkB;AACpB;AAEO,SAAS,oBAA4B;AAC1C,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,QAAM,OAAO,OAAO,SAAS;AAC7B,MAAI,CAAC,QAAQ,KAAK,KAAK,IAAI,GAAG;AAC5B,WAAO;AAAA,EACT;AACA,MAAI,SAAS,aAAa;AAOxB,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,KAAK,MAAM,GAAG;AAE5B,MAAI,MAAM,UAAU,EAAG,QAAO,MAAM,MAAM,CAAC,EAAE,KAAK,GAAG;AAErD,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,SAAO;AACT;AAWO,SAAS,YAAY,gBAAwB,OAAO,IAAY;AACrE,MACE,OAAO,WAAW,eAClB,OAAO,SAAS,aAAa,aAC7B;AACA,UAAM,OAAO,wBAAwB,cAAc;AACnD,QAAI,SAAS,QAAW;AACtB,aAAO,oBAAoB,IAAI,GAAG,IAAI;AAAA,IACxC;AAAA,EAIF;AACA,SAAO,WAAW,cAAc,IAAI,kBAAkB,CAAC,GAAG,IAAI;AAChE;AAGO,SAAS,UAAU,OAAO,IAAY;AAC3C,SAAO,YAAY,UAAU,IAAI;AACnC;","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  portalUrl
3
- } from "./chunk-7LI4Y6WT.js";
3
+ } from "./chunk-IK3B4YWQ.js";
4
4
  import {
5
5
  cn
6
6
  } from "./chunk-JT3XLKKD.js";
@@ -1308,4 +1308,4 @@ export {
1308
1308
  useTour,
1309
1309
  OpiTourHost
1310
1310
  };
1311
- //# sourceMappingURL=chunk-BG4DVZAY.js.map
1311
+ //# sourceMappingURL=chunk-L54JNSVS.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  platformUrl
3
- } from "./chunk-7LI4Y6WT.js";
3
+ } from "./chunk-IK3B4YWQ.js";
4
4
  import {
5
5
  appendSelectionParam
6
6
  } from "./chunk-DMUQ5WNO.js";
@@ -174,4 +174,4 @@ export {
174
174
  normalizeServiceSlug,
175
175
  buildCrossServiceLinks
176
176
  };
177
- //# sourceMappingURL=chunk-45ZQDLPA.js.map
177
+ //# sourceMappingURL=chunk-NRA42OCM.js.map
@@ -19,9 +19,9 @@ import {
19
19
  policyDeploymentConfig,
20
20
  sqdcpConfig,
21
21
  useSidebarResize
22
- } from "../chunk-ZVAS2BMF.js";
23
- import "../chunk-BG4DVZAY.js";
24
- import "../chunk-7LI4Y6WT.js";
22
+ } from "../chunk-32NHRLWI.js";
23
+ import "../chunk-L54JNSVS.js";
24
+ import "../chunk-IK3B4YWQ.js";
25
25
  import "../chunk-5YS2PQN3.js";
26
26
  import "../chunk-DMUQ5WNO.js";
27
27
  import "../chunk-A6UTXAPS.js";
@@ -137,6 +137,19 @@ interface StopWorkIncidentReport extends IncidentReportBase {
137
137
  * etc.). Each variant maps 1:1 onto its own bespoke form; see
138
138
  * ReportingToolbar.tsx's SafetyForm/EnvironmentalForm/StopWorkForm. */
139
139
  type IncidentReport = SafetyIncidentReport | EnvironmentalIncidentReport | StopWorkIncidentReport;
140
+ /**
141
+ * What happened to the Action Manager action Portal's `POST
142
+ * /api/service/reports` tries to create alongside the report itself. A host
143
+ * that doesn't return this from `onReportSubmit` gets the pre-#135 toast
144
+ * unchanged (shared-ui#135, from Oplytics-Digital/oplytics-subdomains#1078).
145
+ */
146
+ interface ReportOutcome {
147
+ actionStatus?: "created" | "not_created";
148
+ /** Plain sentence for the reporter when `actionStatus` is "not_created". */
149
+ actionProblem?: string | null;
150
+ /** e.g. "ACT-026" — shown when `actionStatus` is "created". */
151
+ actionCode?: string | null;
152
+ }
140
153
  interface FeedbackReport {
141
154
  category: FeedbackCategory;
142
155
  /** Canonical service slug the feedback is about (from the picker). */
@@ -178,8 +191,11 @@ interface ReportingToolbarConfig {
178
191
  /** Feedback categories to show (default: both). */
179
192
  feedbackCategories?: FeedbackCategory[];
180
193
  /** Called when an incident report is submitted. The host is responsible
181
- * for actually persisting it — see `reportsComingSoon` above. */
182
- onReportSubmit: (report: IncidentReport) => Promise<void> | void;
194
+ * for actually persisting it — see `reportsComingSoon` above. May
195
+ * optionally return a `ReportOutcome` (shared-ui#135) so the toolbar can
196
+ * tell the reporter whether the linked Action Manager action was
197
+ * created; a host that returns nothing gets the pre-#135 toast. */
198
+ onReportSubmit: (report: IncidentReport) => Promise<void | ReportOutcome> | void | ReportOutcome;
183
199
  /** Called when feedback is submitted — the host POSTs it to Portal. */
184
200
  onFeedbackSubmit: (feedback: FeedbackReport) => Promise<void> | void;
185
201
  }
@@ -209,7 +225,7 @@ interface ReportStandardReference {
209
225
  declare const REPORT_STANDARD_REFERENCES: Record<ReportCategory, ReportStandardReference>;
210
226
  /** Create a default ReportingToolbarConfig with sensible defaults. */
211
227
  declare function createReportingConfig(moduleName: string, handlers: {
212
- onReportSubmit: (report: IncidentReport) => Promise<void> | void;
228
+ onReportSubmit: (report: IncidentReport) => Promise<void | ReportOutcome> | void | ReportOutcome;
213
229
  onFeedbackSubmit: (feedback: FeedbackReport) => Promise<void> | void;
214
230
  }): ReportingToolbarConfig;
215
231
  /**
@@ -229,4 +245,4 @@ declare function isAcceptableScreenshot(file: {
229
245
  };
230
246
  declare function readFileAsScreenshot(file: File): Promise<ReportScreenshot>;
231
247
 
232
- export { type EnvironmentalAspect, type EnvironmentalIncidentReport, FEEDBACK_BUTTONS, FEEDBACK_SCREENSHOT_MAX_BYTES, type FeedbackButtonDef, type FeedbackCategory, type FeedbackReport, type HazardCategory, type IncidentReport, type IncidentSeverity, OPLYTICS_SERVICES, type OplyticsService, type OshaInjuryClassification, REPORT_BUTTONS, REPORT_STANDARD_REFERENCES, type ReportButtonDef, type ReportCategory, type ReportHierarchyContext, type ReportScreenshot, type ReportStandardReference, type ReportSubmitter, ReportingToolbar, type ReportingToolbarConfig, type ReportingToolbarProps, type SafetyIncidentReport, type StopWorkIncidentReport, createReportingConfig, isAcceptableScreenshot, readFileAsScreenshot };
248
+ export { type EnvironmentalAspect, type EnvironmentalIncidentReport, FEEDBACK_BUTTONS, FEEDBACK_SCREENSHOT_MAX_BYTES, type FeedbackButtonDef, type FeedbackCategory, type FeedbackReport, type HazardCategory, type IncidentReport, type IncidentSeverity, OPLYTICS_SERVICES, type OplyticsService, type OshaInjuryClassification, REPORT_BUTTONS, REPORT_STANDARD_REFERENCES, type ReportButtonDef, type ReportCategory, type ReportHierarchyContext, type ReportOutcome, type ReportScreenshot, type ReportStandardReference, type ReportSubmitter, ReportingToolbar, type ReportingToolbarConfig, type ReportingToolbarProps, type SafetyIncidentReport, type StopWorkIncidentReport, createReportingConfig, isAcceptableScreenshot, readFileAsScreenshot };
@@ -95,6 +95,22 @@ var EMPTY_DRAFT = {
95
95
  severity: "medium",
96
96
  immediateAction: ""
97
97
  };
98
+ function toastReportOutcome(outcome) {
99
+ if (!outcome || !outcome.actionStatus) {
100
+ toast.success("Report logged. Thanks for raising it.");
101
+ return;
102
+ }
103
+ if (outcome.actionStatus === "created") {
104
+ toast.success(
105
+ outcome.actionCode ? `Report logged. Action ${outcome.actionCode} was created for it.` : "Report logged. An action was created for it."
106
+ );
107
+ return;
108
+ }
109
+ toast.success("Report logged.");
110
+ toast.warning(
111
+ outcome.actionProblem || "The report is saved, but its Action Manager action could not be created."
112
+ );
113
+ }
98
114
  function ReportingToolbar({
99
115
  moduleName = "Oplytics",
100
116
  showIncidentReporting = true,
@@ -241,8 +257,8 @@ function ReportingToolbar({
241
257
  };
242
258
  report = stopWork;
243
259
  }
244
- await onReportSubmit(report);
245
- toast.success("Report logged. Thanks for raising it.");
260
+ const outcome = await onReportSubmit(report);
261
+ toastReportOutcome(outcome);
246
262
  setReportOpen(false);
247
263
  } catch (err) {
248
264
  console.error("[ReportingToolbar] report submit failed:", err);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/reporting/ReportingToolbar.tsx","../../src/reporting/index.ts"],"sourcesContent":["/**\n * ReportingToolbar — the header \"User Actions\" dropdown for all Oplytics\n * subdomains.\n *\n * ⚙ User Actions ▾ → Report\n * Safety Incident (OSHA 1904 · ISO 45001)\n * Environmental (ISO 14001)\n * Stop Work (ISO 45001)\n * ── (separator) ──\n * Feedback\n * Report a Problem\n * Suggestion\n *\n * Feedback opens a self-contained dialog: pick the service, say whether it's\n * blocking you, a short summary, the detail, and optionally attach a\n * screenshot. On submit `onFeedbackSubmit(feedback: FeedbackReport)` is called\n * — the host POSTs it to Portal's /api/service/feedback.\n *\n * Report opens one of three bespoke dialogs (Safety / Environmental / Stop\n * Work), each built against the external standard named in its header and\n * `REPORT_STANDARD_REFERENCES` (see reporting/index.ts) — shown to the user\n * via an `Alert` at the top of the form, not just documented in code. On\n * submit `onReportSubmit(report: IncidentReport)` is called with the\n * discriminated union — the host POSTs it to Portal's /api/service/reports.\n *\n * Oplytics dark theme: #0A0E1A bg, #8C34E9 accent.\n */\nimport { useRef, useState } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport {\n Button,\n Badge,\n Textarea,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Input,\n Label,\n Switch,\n Checkbox,\n Alert,\n AlertTitle,\n AlertDescription,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n DialogFooter,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from \"../primitives\";\nimport {\n ShieldAlert,\n Leaf,\n OctagonX,\n Bug,\n Lightbulb,\n ChevronDown,\n Send,\n ImagePlus,\n X,\n UserCog,\n BookMarked,\n} from \"lucide-react\";\nimport { toast } from \"sonner\";\nimport {\n OPLYTICS_SERVICES,\n REPORT_STANDARD_REFERENCES,\n isAcceptableScreenshot,\n readFileAsScreenshot,\n type ReportCategory,\n type FeedbackCategory,\n type IncidentReport,\n type SafetyIncidentReport,\n type EnvironmentalIncidentReport,\n type StopWorkIncidentReport,\n type IncidentSeverity,\n type OshaInjuryClassification,\n type EnvironmentalAspect,\n type HazardCategory,\n type FeedbackReport,\n type ReportScreenshot,\n type ReportSubmitter,\n type ReportHierarchyContext,\n type OplyticsService,\n type ReportingToolbarConfig,\n} from \"./index\";\n\n/* ── Option catalogues ── */\n\ninterface ReportOption {\n category: ReportCategory;\n label: string;\n description: string;\n icon: React.ElementType;\n color: string;\n}\n\nconst ALL_REPORT_OPTIONS: ReportOption[] = [\n {\n category: \"safety\",\n label: \"Safety Incident\",\n description: \"Report a safety hazard, near miss, or injury\",\n icon: ShieldAlert,\n color: \"#EF4444\",\n },\n {\n category: \"environmental\",\n label: \"Environmental\",\n description: \"Report an environmental concern or spill\",\n icon: Leaf,\n color: \"#22C55E\",\n },\n {\n category: \"stop-work\",\n label: \"Stop Work\",\n description: \"Raise a stop work authority — immediate halt\",\n icon: OctagonX,\n color: \"#F59E0B\",\n },\n];\n\ninterface FeedbackOption {\n category: FeedbackCategory;\n label: string;\n description: string;\n icon: React.ElementType;\n color: string;\n}\n\nconst ALL_FEEDBACK_OPTIONS: FeedbackOption[] = [\n {\n category: \"problem\",\n label: \"Report a Problem\",\n description: \"Something isn't working\",\n icon: Bug,\n color: \"#EF4444\",\n },\n {\n category: \"suggestion\",\n label: \"Suggestion\",\n description: \"An idea for improvement\",\n icon: Lightbulb,\n color: \"#8C34E9\",\n },\n];\n\n/* ── Props ── */\n\nexport interface ReportingToolbarProps extends Omit<\n ReportingToolbarConfig,\n \"moduleName\"\n> {\n /** Service/module name (e.g. \"Connect\"). Defaults to \"Oplytics\". */\n moduleName?: string;\n /** The current org-hierarchy selection, forwarded on every submission. */\n hierarchyContext?: ReportHierarchyContext;\n /** The authenticated user — openId is authoritative, name/email are hints. */\n currentUser?: ReportSubmitter;\n /** Service list for the feedback picker. Defaults to OPLYTICS_SERVICES. */\n services?: OplyticsService[];\n /** Pre-selected service slug in the feedback picker. */\n defaultServiceSlug?: string;\n /** Show the \"attach screenshot\" control (default: true). */\n enableScreenshot?: boolean;\n /**\n * While true, the Report items are disabled with a \"Coming soon\" badge and\n * `onReportSubmit` is never called. Defaults to `false` — pass `true` only\n * if this host's `onReportSubmit` doesn't actually persist the report yet.\n */\n reportsComingSoon?: boolean;\n /**\n * @deprecated the toolbar handles responsive collapse itself.\n */\n compact?: boolean;\n /** CSS class for the container. */\n className?: string;\n}\n\n/* ── Component ── */\n\n/** Fields shared by all three Report forms' local state. */\ninterface ReportDraft {\n description: string;\n location: string;\n severity: IncidentSeverity;\n immediateAction: string;\n}\n\nconst EMPTY_DRAFT: ReportDraft = {\n description: \"\",\n location: \"\",\n severity: \"medium\",\n immediateAction: \"\",\n};\n\nexport function ReportingToolbar({\n moduleName = \"Oplytics\",\n showIncidentReporting = true,\n showFeedback = true,\n reportCategories = [\"safety\", \"environmental\", \"stop-work\"],\n feedbackCategories = [\"problem\", \"suggestion\"],\n onReportSubmit,\n onFeedbackSubmit,\n hierarchyContext,\n currentUser,\n services = OPLYTICS_SERVICES,\n defaultServiceSlug,\n enableScreenshot = true,\n reportsComingSoon = false,\n className,\n}: ReportingToolbarProps) {\n const reportOptions = ALL_REPORT_OPTIONS.filter(opt =>\n reportCategories.includes(opt.category)\n );\n const feedbackOptions = ALL_FEEDBACK_OPTIONS.filter(opt =>\n feedbackCategories.includes(opt.category)\n );\n\n const firstServiceSlug = defaultServiceSlug ?? services[0]?.slug ?? \"portal\";\n\n /* ── Feedback dialog state ── */\n const [feedbackOpen, setFeedbackOpen] = useState(false);\n const [category, setCategory] = useState<FeedbackCategory>(\"problem\");\n const [serviceSlug, setServiceSlug] = useState(firstServiceSlug);\n const [isBlocking, setIsBlocking] = useState(false);\n const [summary, setSummary] = useState(\"\");\n const [details, setDetails] = useState(\"\");\n const [screenshot, setScreenshot] = useState<ReportScreenshot | null>(null);\n const [screenshotName, setScreenshotName] = useState<string | null>(null);\n const [submitting, setSubmitting] = useState(false);\n const fileInputRef = useRef<HTMLInputElement | null>(null);\n\n const openFeedback = (c: FeedbackCategory) => {\n setCategory(c);\n setServiceSlug(firstServiceSlug);\n setIsBlocking(false);\n setSummary(\"\");\n setDetails(\"\");\n setScreenshot(null);\n setScreenshotName(null);\n setFeedbackOpen(true);\n };\n\n /* ── Report dialog state (Safety / Environmental / Stop Work) ── */\n const [reportOpen, setReportOpen] = useState(false);\n const [reportCategory, setReportCategory] =\n useState<ReportCategory>(\"safety\");\n const [reportDraft, setReportDraft] = useState<ReportDraft>(EMPTY_DRAFT);\n const [reportSubmitting, setReportSubmitting] = useState(false);\n\n // Safety-specific\n const [injuryOccurred, setInjuryOccurred] = useState(false);\n const [injuryClassification, setInjuryClassification] = useState<\n OshaInjuryClassification | undefined\n >(undefined);\n const [daysAwayFromWork, setDaysAwayFromWork] = useState(\"\");\n const [daysRestrictedOrTransferred, setDaysRestrictedOrTransferred] =\n useState(\"\");\n const [firstAiderAttended, setFirstAiderAttended] = useState(false);\n const [witnessName, setWitnessName] = useState(\"\");\n const [bodyPart, setBodyPart] = useState(\"\");\n const [treatmentType, setTreatmentType] = useState(\"\");\n\n // Environmental-specific\n const [environmentalAspect, setEnvironmentalAspect] = useState<\n EnvironmentalAspect | undefined\n >(undefined);\n const [regulatoryReportable, setRegulatoryReportable] = useState(false);\n const [substance, setSubstance] = useState(\"\");\n const [estimatedQuantity, setEstimatedQuantity] = useState(\"\");\n const [contained, setContained] = useState(false);\n const [reachedWatercourse, setReachedWatercourse] = useState(false);\n\n // Stop Work-specific\n const [hazardCategory, setHazardCategory] = useState<\n HazardCategory | undefined\n >(undefined);\n const [hazardDescription, setHazardDescription] = useState(\"\");\n const [areaMadeSafe, setAreaMadeSafe] = useState(false);\n const [workResumed, setWorkResumed] = useState(false);\n\n const openReport = (c: ReportCategory) => {\n setReportCategory(c);\n setReportDraft({\n ...EMPTY_DRAFT,\n severity: c === \"stop-work\" ? \"critical\" : \"medium\",\n });\n setInjuryOccurred(false);\n setInjuryClassification(undefined);\n setDaysAwayFromWork(\"\");\n setDaysRestrictedOrTransferred(\"\");\n setFirstAiderAttended(false);\n setWitnessName(\"\");\n setBodyPart(\"\");\n setTreatmentType(\"\");\n setEnvironmentalAspect(undefined);\n setRegulatoryReportable(false);\n setSubstance(\"\");\n setEstimatedQuantity(\"\");\n setContained(false);\n setReachedWatercourse(false);\n setHazardCategory(undefined);\n setHazardDescription(\"\");\n setAreaMadeSafe(false);\n setWorkResumed(false);\n setReportOpen(true);\n };\n\n const submitReport = async () => {\n if (!reportDraft.description.trim()) {\n toast.error(\"Please describe what happened.\");\n return;\n }\n setReportSubmitting(true);\n try {\n const base = {\n description: reportDraft.description.trim(),\n location: reportDraft.location.trim() || undefined,\n severity: reportDraft.severity,\n immediateAction: reportDraft.immediateAction.trim() || undefined,\n moduleName,\n timestamp: new Date().toISOString(),\n submittedBy: currentUser,\n hierarchyContext,\n };\n\n let report: IncidentReport;\n if (reportCategory === \"safety\") {\n const safety: SafetyIncidentReport = {\n ...base,\n category: \"safety\",\n injuryOccurred,\n injuryClassification,\n daysAwayFromWork: daysAwayFromWork\n ? Number(daysAwayFromWork)\n : undefined,\n daysRestrictedOrTransferred: daysRestrictedOrTransferred\n ? Number(daysRestrictedOrTransferred)\n : undefined,\n firstAiderAttended,\n witnessName: witnessName.trim() || undefined,\n bodyPart: bodyPart.trim() || undefined,\n treatmentType: treatmentType.trim() || undefined,\n };\n report = safety;\n } else if (reportCategory === \"environmental\") {\n const environmental: EnvironmentalIncidentReport = {\n ...base,\n category: \"environmental\",\n environmentalAspect,\n regulatoryReportable,\n substance: substance.trim() || undefined,\n estimatedQuantity: estimatedQuantity.trim() || undefined,\n contained,\n reachedWatercourse,\n };\n report = environmental;\n } else {\n const stopWork: StopWorkIncidentReport = {\n ...base,\n category: \"stop-work\",\n hazardCategory,\n hazardDescription: hazardDescription.trim() || undefined,\n areaMadeSafe,\n workResumed,\n };\n report = stopWork;\n }\n\n await onReportSubmit(report);\n toast.success(\"Report logged. Thanks for raising it.\");\n setReportOpen(false);\n } catch (err) {\n console.error(\"[ReportingToolbar] report submit failed:\", err);\n toast.error(\"Couldn't submit that. Please try again.\");\n } finally {\n setReportSubmitting(false);\n }\n };\n\n const handlePickFile = async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n e.target.value = \"\"; // allow re-picking the same file\n if (!file) return;\n const check = isAcceptableScreenshot(file);\n if (!check.ok) {\n toast.error(check.reason);\n return;\n }\n try {\n const shot = await readFileAsScreenshot(file);\n setScreenshot(shot);\n setScreenshotName(file.name);\n } catch (err) {\n console.error(\"[ReportingToolbar] screenshot read failed:\", err);\n toast.error(\"Could not read that image.\");\n }\n };\n\n const submitFeedback = async () => {\n if (!details.trim()) {\n toast.error(\"Please describe the problem or idea.\");\n return;\n }\n setSubmitting(true);\n try {\n const feedback: FeedbackReport = {\n category,\n serviceSlug,\n summary: summary.trim() || undefined,\n details: details.trim(),\n isBlocking,\n moduleName,\n timestamp: new Date().toISOString(),\n pageContext:\n typeof window !== \"undefined\" ? window.location.pathname : undefined,\n submittedBy: currentUser,\n screenshot: screenshot ?? undefined,\n };\n await onFeedbackSubmit(feedback);\n toast.success(\n category === \"problem\"\n ? \"Thanks — we've logged it. You'll get a confirmation email.\"\n : \"Thanks for the suggestion — you'll get a confirmation email.\"\n );\n setFeedbackOpen(false);\n } catch (err) {\n console.error(\"[ReportingToolbar] feedback submit failed:\", err);\n toast.error(\"Couldn't submit that. Please try again.\");\n } finally {\n setSubmitting(false);\n }\n };\n\n const showReportGroup = showIncidentReporting && reportOptions.length > 0;\n const showFeedbackGroup = showFeedback && feedbackOptions.length > 0;\n if (!showReportGroup && !showFeedbackGroup) return null;\n\n return (\n <>\n <div className={cn(\"flex items-center gap-2\", className)}>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-9 gap-1.5 text-xs font-medium text-[#8890A0] hover:bg-[#1E2738] hover:text-[#E2E8F0] border border-[#1E2738]\"\n >\n <UserCog className=\"h-3.5 w-3.5\" />\n <span className=\"shell-hidden shell-sm:inline\">User Actions</span>\n <ChevronDown className=\"h-3 w-3\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-60\">\n {showReportGroup && (\n <>\n <DropdownMenuLabel className=\"flex items-center gap-2 text-[10px] uppercase tracking-wide text-muted-foreground\">\n Report\n {reportsComingSoon && (\n <Badge\n variant=\"outline\"\n className=\"border-[#8C34E9]/40 text-[9px] font-normal text-[#C084FC]\"\n >\n Coming soon\n </Badge>\n )}\n </DropdownMenuLabel>\n {reportOptions.map(opt => (\n <DropdownMenuItem\n key={opt.category}\n disabled={reportsComingSoon}\n onClick={() => {\n if (!reportsComingSoon) openReport(opt.category);\n }}\n className=\"gap-3 py-2.5\"\n >\n <opt.icon\n className=\"h-4 w-4\"\n style={{ color: opt.color }}\n />\n <div>\n <p className=\"text-sm font-medium\">{opt.label}</p>\n <p className=\"text-[10px] text-muted-foreground\">\n {REPORT_STANDARD_REFERENCES[opt.category].label}\n </p>\n </div>\n </DropdownMenuItem>\n ))}\n </>\n )}\n\n {showReportGroup && showFeedbackGroup && <DropdownMenuSeparator />}\n\n {showFeedbackGroup && (\n <>\n <DropdownMenuLabel className=\"text-[10px] uppercase tracking-wide text-muted-foreground\">\n Feedback\n </DropdownMenuLabel>\n {feedbackOptions.map(opt => (\n <DropdownMenuItem\n key={opt.category}\n onClick={() => openFeedback(opt.category)}\n className=\"gap-3 py-2.5\"\n >\n <opt.icon\n className=\"h-4 w-4\"\n style={{ color: opt.color }}\n />\n <div>\n <p className=\"text-sm font-medium\">{opt.label}</p>\n <p className=\"text-[10px] text-muted-foreground\">\n {opt.description}\n </p>\n </div>\n </DropdownMenuItem>\n ))}\n </>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n\n {/* Feedback dialog */}\n <Dialog open={feedbackOpen} onOpenChange={setFeedbackOpen}>\n <DialogContent className=\"sm:max-w-[520px]\">\n <DialogHeader>\n <DialogTitle className=\"flex items-center gap-2\">\n {category === \"problem\" ? (\n <Bug className=\"h-5 w-5 text-[#EF4444]\" />\n ) : (\n <Lightbulb className=\"h-5 w-5 text-[#8C34E9]\" />\n )}\n {category === \"problem\"\n ? \"Report a Problem\"\n : \"Share a Suggestion\"}\n </DialogTitle>\n <DialogDescription>\n {category === \"problem\"\n ? \"Tell us what went wrong. We triage within 24 hours.\"\n : \"We review every suggestion and schedule improvements against the roadmap.\"}\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4 py-2\">\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">Which service?</Label>\n <Select value={serviceSlug} onValueChange={setServiceSlug}>\n <SelectTrigger className=\"h-9\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {services.map(s => (\n <SelectItem key={s.slug} value={s.slug}>\n {s.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {category === \"problem\" && (\n <div className=\"flex items-center justify-between rounded-md border border-[#1E2738] px-3 py-2\">\n <div>\n <p className=\"text-sm font-medium\">Is this blocking you?</p>\n <p className=\"text-[10px] text-muted-foreground\">\n Blocking issues are treated as high priority.\n </p>\n </div>\n <Switch checked={isBlocking} onCheckedChange={setIsBlocking} />\n </div>\n )}\n\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">Summary</Label>\n <Input\n value={summary}\n onChange={e => setSummary(e.target.value)}\n placeholder={\n category === \"problem\"\n ? \"Brief summary of the issue\"\n : \"Brief summary of your idea\"\n }\n className=\"h-9\"\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">Details *</Label>\n <Textarea\n value={details}\n onChange={e => setDetails(e.target.value)}\n placeholder={\n category === \"problem\"\n ? \"What were you trying to do? What happened instead?\"\n : \"Describe your suggestion and how it would help…\"\n }\n rows={5}\n />\n </div>\n\n {enableScreenshot && (\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">\n Screenshot (optional)\n </Label>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n className=\"hidden\"\n onChange={handlePickFile}\n />\n {screenshot ? (\n <div className=\"flex items-center gap-2 rounded-md border border-[#1E2738] px-3 py-2\">\n <ImagePlus className=\"h-4 w-4 text-[#8C34E9]\" />\n <span className=\"flex-1 truncate text-xs text-[#E2E8F0]\">\n {screenshotName ?? \"screenshot\"}\n </span>\n <button\n type=\"button\"\n onClick={() => {\n setScreenshot(null);\n setScreenshotName(null);\n }}\n className=\"text-muted-foreground hover:text-[#E2E8F0]\"\n aria-label=\"Remove screenshot\"\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n ) : (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"h-9 gap-1.5\"\n onClick={() => fileInputRef.current?.click()}\n >\n <ImagePlus className=\"h-3.5 w-3.5\" />\n Attach an image\n </Button>\n )}\n </div>\n )}\n\n <p className=\"text-[10px] text-muted-foreground\">\n {moduleName} · you'll get a confirmation email once this is\n logged.\n </p>\n </div>\n\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => setFeedbackOpen(false)}>\n Cancel\n </Button>\n <Button\n onClick={submitFeedback}\n disabled={submitting || !details.trim()}\n className=\"gap-2\"\n style={{\n backgroundColor: category === \"problem\" ? \"#EF4444\" : \"#8C34E9\",\n color: \"white\",\n }}\n >\n <Send className=\"h-3.5 w-3.5\" />\n {submitting ? \"Submitting…\" : \"Submit\"}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n\n {/* Report dialog — Safety / Environmental / Stop Work */}\n <Dialog open={reportOpen} onOpenChange={setReportOpen}>\n <DialogContent className=\"sm:max-w-[560px] max-h-[90vh] overflow-y-auto\">\n <DialogHeader>\n <DialogTitle className=\"flex items-center gap-2\">\n {reportCategory === \"safety\" && (\n <ShieldAlert className=\"h-5 w-5 text-[#EF4444]\" />\n )}\n {reportCategory === \"environmental\" && (\n <Leaf className=\"h-5 w-5 text-[#22C55E]\" />\n )}\n {reportCategory === \"stop-work\" && (\n <OctagonX className=\"h-5 w-5 text-[#F59E0B]\" />\n )}\n {ALL_REPORT_OPTIONS.find(o => o.category === reportCategory)\n ?.label ?? \"Report\"}\n </DialogTitle>\n <DialogDescription>\n {\n ALL_REPORT_OPTIONS.find(o => o.category === reportCategory)\n ?.description\n }\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4 py-2\">\n <Alert className=\"border-[#8C34E9]/30 bg-[#8C34E9]/5\">\n <BookMarked className=\"h-4 w-4 text-[#8C34E9]\" />\n <AlertTitle className=\"text-xs font-semibold text-[#C084FC]\">\n {REPORT_STANDARD_REFERENCES[reportCategory].label}\n </AlertTitle>\n <AlertDescription className=\"text-[11px]\">\n {REPORT_STANDARD_REFERENCES[reportCategory].description}\n </AlertDescription>\n </Alert>\n\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">Severity</Label>\n <Select\n value={reportDraft.severity}\n onValueChange={(v: IncidentSeverity) =>\n setReportDraft(d => ({ ...d, severity: v }))\n }\n >\n <SelectTrigger className=\"h-9\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"low\">Low</SelectItem>\n <SelectItem value=\"medium\">Medium</SelectItem>\n <SelectItem value=\"high\">High</SelectItem>\n <SelectItem value=\"critical\">Critical</SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">Location (optional)</Label>\n <Input\n value={reportDraft.location}\n onChange={e =>\n setReportDraft(d => ({ ...d, location: e.target.value }))\n }\n placeholder=\"Where did this happen?\"\n className=\"h-9\"\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">What happened? *</Label>\n <Textarea\n value={reportDraft.description}\n onChange={e =>\n setReportDraft(d => ({\n ...d,\n description: e.target.value,\n }))\n }\n placeholder=\"Describe what happened…\"\n rows={4}\n />\n </div>\n\n {reportCategory === \"safety\" && (\n <>\n <div className=\"flex items-center justify-between rounded-md border border-[#1E2738] px-3 py-2\">\n <p className=\"text-sm font-medium\">Was anyone injured?</p>\n <Switch\n checked={injuryOccurred}\n onCheckedChange={setInjuryOccurred}\n />\n </div>\n\n {injuryOccurred && (\n <>\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">\n Injury classification (OSHA 1904)\n </Label>\n <Select\n value={injuryClassification}\n onValueChange={(v: OshaInjuryClassification) =>\n setInjuryClassification(v)\n }\n >\n <SelectTrigger className=\"h-9\">\n <SelectValue placeholder=\"Select a classification\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"first_aid_only\">\n First aid only\n </SelectItem>\n <SelectItem value=\"recordable\">Recordable</SelectItem>\n <SelectItem value=\"days_away_restricted_transfer\">\n Days away, restricted, or transferred (DART)\n </SelectItem>\n <SelectItem value=\"fatality\">Fatality</SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n {injuryClassification ===\n \"days_away_restricted_transfer\" && (\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">\n Days away from work\n </Label>\n <Input\n type=\"number\"\n min={0}\n value={daysAwayFromWork}\n onChange={e => setDaysAwayFromWork(e.target.value)}\n className=\"h-9\"\n />\n </div>\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">\n Days restricted/transferred\n </Label>\n <Input\n type=\"number\"\n min={0}\n value={daysRestrictedOrTransferred}\n onChange={e =>\n setDaysRestrictedOrTransferred(e.target.value)\n }\n className=\"h-9\"\n />\n </div>\n </div>\n )}\n\n <div className=\"flex items-center gap-2\">\n <Checkbox\n id=\"firstAiderAttended\"\n checked={firstAiderAttended}\n onCheckedChange={c => setFirstAiderAttended(!!c)}\n />\n <Label\n htmlFor=\"firstAiderAttended\"\n className=\"text-xs font-normal\"\n >\n A first aider attended\n </Label>\n </div>\n\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">\n Body part affected\n </Label>\n <Input\n value={bodyPart}\n onChange={e => setBodyPart(e.target.value)}\n className=\"h-9\"\n />\n </div>\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">\n Treatment given\n </Label>\n <Input\n value={treatmentType}\n onChange={e => setTreatmentType(e.target.value)}\n className=\"h-9\"\n />\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">\n Witness name (optional)\n </Label>\n <Input\n value={witnessName}\n onChange={e => setWitnessName(e.target.value)}\n className=\"h-9\"\n />\n </div>\n </>\n )}\n </>\n )}\n\n {reportCategory === \"environmental\" && (\n <>\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">\n Environmental aspect (ISO 14001)\n </Label>\n <Select\n value={environmentalAspect}\n onValueChange={(v: EnvironmentalAspect) =>\n setEnvironmentalAspect(v)\n }\n >\n <SelectTrigger className=\"h-9\">\n <SelectValue placeholder=\"What element of the environment is affected?\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"air_emission\">Air emission</SelectItem>\n <SelectItem value=\"water_discharge\">\n Water discharge\n </SelectItem>\n <SelectItem value=\"soil_contamination\">\n Soil contamination\n </SelectItem>\n <SelectItem value=\"waste\">Waste</SelectItem>\n <SelectItem value=\"spill\">Spill</SelectItem>\n <SelectItem value=\"noise\">Noise</SelectItem>\n <SelectItem value=\"other\">Other</SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">\n Substance (optional)\n </Label>\n <Input\n value={substance}\n onChange={e => setSubstance(e.target.value)}\n className=\"h-9\"\n />\n </div>\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">\n Estimated quantity\n </Label>\n <Input\n value={estimatedQuantity}\n onChange={e => setEstimatedQuantity(e.target.value)}\n className=\"h-9\"\n />\n </div>\n </div>\n\n <div className=\"flex items-center gap-2\">\n <Checkbox\n id=\"contained\"\n checked={contained}\n onCheckedChange={c => setContained(!!c)}\n />\n <Label htmlFor=\"contained\" className=\"text-xs font-normal\">\n Contained on site\n </Label>\n </div>\n\n <div className=\"flex items-center gap-2\">\n <Checkbox\n id=\"reachedWatercourse\"\n checked={reachedWatercourse}\n onCheckedChange={c => setReachedWatercourse(!!c)}\n />\n <Label\n htmlFor=\"reachedWatercourse\"\n className=\"text-xs font-normal\"\n >\n Reached a watercourse\n </Label>\n </div>\n\n <div className=\"flex items-center gap-2\">\n <Checkbox\n id=\"regulatoryReportable\"\n checked={regulatoryReportable}\n onCheckedChange={c => setRegulatoryReportable(!!c)}\n />\n <Label\n htmlFor=\"regulatoryReportable\"\n className=\"text-xs font-normal\"\n >\n This may need reporting to a regulator\n </Label>\n </div>\n </>\n )}\n\n {reportCategory === \"stop-work\" && (\n <>\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">\n Hazard category (ISO 45001)\n </Label>\n <Select\n value={hazardCategory}\n onValueChange={(v: HazardCategory) => setHazardCategory(v)}\n >\n <SelectTrigger className=\"h-9\">\n <SelectValue placeholder=\"What kind of hazard?\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"mechanical\">Mechanical</SelectItem>\n <SelectItem value=\"electrical\">Electrical</SelectItem>\n <SelectItem value=\"chemical\">Chemical</SelectItem>\n <SelectItem value=\"working_at_height\">\n Working at height\n </SelectItem>\n <SelectItem value=\"confined_space\">\n Confined space\n </SelectItem>\n <SelectItem value=\"vehicle_traffic\">\n Vehicle / traffic\n </SelectItem>\n <SelectItem value=\"other\">Other</SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">\n Hazard description\n </Label>\n <Textarea\n value={hazardDescription}\n onChange={e => setHazardDescription(e.target.value)}\n rows={3}\n />\n </div>\n\n <div className=\"flex items-center gap-2\">\n <Checkbox\n id=\"areaMadeSafe\"\n checked={areaMadeSafe}\n onCheckedChange={c => setAreaMadeSafe(!!c)}\n />\n <Label htmlFor=\"areaMadeSafe\" className=\"text-xs font-normal\">\n Area has been made safe\n </Label>\n </div>\n\n <div className=\"flex items-center gap-2\">\n <Checkbox\n id=\"workResumed\"\n checked={workResumed}\n onCheckedChange={c => setWorkResumed(!!c)}\n />\n <Label htmlFor=\"workResumed\" className=\"text-xs font-normal\">\n Work has resumed\n </Label>\n </div>\n </>\n )}\n\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">\n Immediate action taken (optional)\n </Label>\n <Textarea\n value={reportDraft.immediateAction}\n onChange={e =>\n setReportDraft(d => ({\n ...d,\n immediateAction: e.target.value,\n }))\n }\n rows={2}\n />\n </div>\n\n <p className=\"text-[10px] text-muted-foreground\">\n {moduleName} · this is logged centrally and escalated\n automatically.\n </p>\n </div>\n\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => setReportOpen(false)}>\n Cancel\n </Button>\n <Button\n onClick={submitReport}\n disabled={reportSubmitting || !reportDraft.description.trim()}\n className=\"gap-2\"\n style={{\n backgroundColor: ALL_REPORT_OPTIONS.find(\n o => o.category === reportCategory\n )?.color,\n color: \"white\",\n }}\n >\n <Send className=\"h-3.5 w-3.5\" />\n {reportSubmitting ? \"Submitting…\" : \"Submit\"}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </>\n );\n}\n","/**\n * @oplytics/shared-ui/reporting\n *\n * Reporting and feedback toolbar types + configuration for all Oplytics\n * subdomains — the header \"User Actions\" dropdown (ReportingToolbar).\n *\n * Both groups are fully wired. Feedback collects a service, an \"is this\n * blocking you?\" signal, a short summary, the detail, and an optional\n * screenshot, then calls `onFeedbackSubmit` with the canonical\n * `FeedbackReport` shape. Report (Safety / Environmental / Stop Work) is\n * three bespoke forms, one per category, each collecting the fields needed\n * to conform to the external standard it's built against (see each\n * category's comment below), then calls `onReportSubmit` with the\n * discriminated `IncidentReport` union. Either way the host owns the\n * transport (Feedback POSTs to Portal's `/api/service/feedback`; Report\n * POSTs to Portal's `/api/service/reports`).\n *\n * `reportsComingSoon` defaults to `false` as of this version — pass `true`\n * per-host only if that host's `onReportSubmit` doesn't actually persist yet.\n */\n\n// ─── Report types ───────────────────────────────────────────────────────────\n\nexport type ReportCategory = \"safety\" | \"environmental\" | \"stop-work\";\nexport type FeedbackCategory = \"problem\" | \"suggestion\";\n\n/** Where in the org hierarchy the submission was made. */\nexport interface ReportHierarchyContext {\n enterpriseId?: number;\n businessUnitId?: number;\n siteId?: number;\n areaId?: number;\n assetId?: number;\n}\n\n/** The authenticated user, as much as the host can supply. */\nexport interface ReportSubmitter {\n /** JWT `openId` claim — the host must NOT trust name/email over this. */\n openId?: string;\n name?: string;\n email?: string;\n}\n\n/** An attached screenshot: base64 WITHOUT the `data:...;base64,` prefix. */\nexport interface ReportScreenshot {\n dataBase64: string;\n mime: string;\n}\n\nexport type IncidentSeverity = \"low\" | \"medium\" | \"high\" | \"critical\";\n\n/**\n * OSHA 29 CFR 1904's own injury-and-illness recordkeeping classification —\n * the standard the Safety form's injury fields conform to, alongside\n * ISO 45001 (the international OH&S management-system standard it sits\n * under). Shown to the user on the form, not just used internally.\n */\nexport type OshaInjuryClassification =\n | \"none\"\n | \"first_aid_only\"\n | \"recordable\"\n | \"days_away_restricted_transfer\"\n | \"fatality\";\n\n/** ISO 14001's \"environmental aspect\" concept — which element of the\n * environment an incident affects. The standard the Environmental form's\n * classification field conforms to. */\nexport type EnvironmentalAspect =\n | \"air_emission\"\n | \"water_discharge\"\n | \"soil_contamination\"\n | \"waste\"\n | \"spill\"\n | \"noise\"\n | \"other\";\n\n/** ISO 45001's hierarchy-of-controls hazard classification. Stop Work\n * authority isn't itself a numbered standard — it's a control mechanism\n * exercised under an ISO 45001 OH&S management system, which this\n * classification maps to. */\nexport type HazardCategory =\n | \"mechanical\"\n | \"electrical\"\n | \"chemical\"\n | \"working_at_height\"\n | \"confined_space\"\n | \"vehicle_traffic\"\n | \"other\";\n\ninterface IncidentReportBase {\n description: string;\n location?: string;\n severity?: IncidentSeverity;\n /** Optional — what was done at the scene to make the area safe. */\n immediateAction?: string;\n moduleName: string;\n /** ISO timestamp */\n timestamp: string;\n submittedBy?: ReportSubmitter;\n hierarchyContext?: ReportHierarchyContext;\n}\n\nexport interface SafetyIncidentReport extends IncidentReportBase {\n category: \"safety\";\n injuryOccurred: boolean;\n /** OSHA 1904 classification — see `OshaInjuryClassification`. */\n injuryClassification?: OshaInjuryClassification;\n /** OSHA DART-rate inputs — only meaningful when injuryClassification is\n * \"days_away_restricted_transfer\". */\n daysAwayFromWork?: number;\n daysRestrictedOrTransferred?: number;\n firstAiderAttended?: boolean;\n witnessName?: string;\n bodyPart?: string;\n treatmentType?: string;\n}\n\nexport interface EnvironmentalIncidentReport extends IncidentReportBase {\n category: \"environmental\";\n /** ISO 14001 environmental aspect — see `EnvironmentalAspect`. */\n environmentalAspect?: EnvironmentalAspect;\n /** Does this trigger a duty to notify an external regulator? */\n regulatoryReportable?: boolean;\n substance?: string;\n estimatedQuantity?: string;\n contained?: boolean;\n reachedWatercourse?: boolean;\n}\n\nexport interface StopWorkIncidentReport extends IncidentReportBase {\n category: \"stop-work\";\n /** ISO 45001 hazard classification — see `HazardCategory`. */\n hazardCategory?: HazardCategory;\n hazardDescription?: string;\n areaMadeSafe?: boolean;\n workResumed?: boolean;\n}\n\n/** Discriminated on `category` — narrow on it to get the right per-type\n * fields (`report.category === \"safety\"` narrows to `SafetyIncidentReport`,\n * etc.). Each variant maps 1:1 onto its own bespoke form; see\n * ReportingToolbar.tsx's SafetyForm/EnvironmentalForm/StopWorkForm. */\nexport type IncidentReport =\n SafetyIncidentReport | EnvironmentalIncidentReport | StopWorkIncidentReport;\n\nexport interface FeedbackReport {\n category: FeedbackCategory;\n /** Canonical service slug the feedback is about (from the picker). */\n serviceSlug: string;\n /** Optional short one-liner shown above the detail. */\n summary?: string;\n /** Required detail body. */\n details: string;\n /** \"Is this blocking you?\" — drives priority server-side. */\n isBlocking: boolean;\n /** Human-readable originating module (\"OEE Manager\", \"SQDCP\", …). */\n moduleName: string;\n /** ISO timestamp */\n timestamp: string;\n /** Route the submitter was on. */\n pageContext?: string;\n submittedBy?: ReportSubmitter;\n screenshot?: ReportScreenshot;\n}\n\n// ─── Service catalogue ──────────────────────────────────────────────────────\n\nexport interface OplyticsService {\n slug: string;\n label: string;\n}\n\n/**\n * The services a user can attribute feedback to. Kept here so every subdomain's\n * picker shows the same list; a host can narrow or override it via the\n * `services` prop. Slugs match Portal's `platform_feedback.serviceSlug`.\n */\nexport const OPLYTICS_SERVICES: OplyticsService[] = [\n { slug: \"sqdcp\", label: \"SQDCP\" },\n { slug: \"oee-manager\", label: \"OEE Manager\" },\n { slug: \"action-manager\", label: \"Action Manager\" },\n { slug: \"policy-deployment\", label: \"Policy Deployment\" },\n { slug: \"portal\", label: \"Portal\" },\n];\n\n// ─── Toolbar configuration ──────────────────────────────────────────────────\n\nexport interface ReportingToolbarConfig {\n /** Service/module name (e.g. \"SQDCP\", \"OEE Manager\"). */\n moduleName: string;\n /** Whether to show the Report group at all (default: true). */\n showIncidentReporting?: boolean;\n /** Whether to show the Feedback group at all (default: true). */\n showFeedback?: boolean;\n /** Report categories to show (default: all three). */\n reportCategories?: ReportCategory[];\n /** Feedback categories to show (default: both). */\n feedbackCategories?: FeedbackCategory[];\n /** Called when an incident report is submitted. The host is responsible\n * for actually persisting it — see `reportsComingSoon` above. */\n onReportSubmit: (report: IncidentReport) => Promise<void> | void;\n /** Called when feedback is submitted — the host POSTs it to Portal. */\n onFeedbackSubmit: (feedback: FeedbackReport) => Promise<void> | void;\n}\n\n// ─── Button definitions (kept for backwards compatibility / config helpers) ──\n\nexport interface ReportButtonDef {\n category: ReportCategory;\n label: string;\n iconName: string;\n color: string;\n description: string;\n}\n\nexport interface FeedbackButtonDef {\n category: FeedbackCategory;\n label: string;\n iconName: string;\n color: string;\n description: string;\n}\n\nexport const REPORT_BUTTONS: ReportButtonDef[] = [\n {\n category: \"safety\",\n label: \"Safety\",\n iconName: \"ShieldAlert\",\n color: \"#ef4444\",\n description: \"Report a safety incident, near-miss, or hazard\",\n },\n {\n category: \"environmental\",\n label: \"Environmental\",\n iconName: \"Leaf\",\n color: \"#22c55e\",\n description: \"Report an environmental incident or concern\",\n },\n {\n category: \"stop-work\",\n label: \"Stop Work\",\n iconName: \"OctagonX\",\n color: \"#f59e0b\",\n description: \"Raise a stop work authority — immediate safety concern\",\n },\n];\n\nexport const FEEDBACK_BUTTONS: FeedbackButtonDef[] = [\n {\n category: \"problem\",\n label: \"Problem\",\n iconName: \"AlertTriangle\",\n color: \"#ef4444\",\n description: \"Report a problem with the platform\",\n },\n {\n category: \"suggestion\",\n label: \"Suggestion\",\n iconName: \"Lightbulb\",\n color: \"#8C34E9\",\n description: \"Suggest an improvement or new feature\",\n },\n];\n\n// ─── Standards references ──────────────────────────────────────────────────\n//\n// Each Report form is built against a named external standard, shown to the\n// user on the form itself (not just documented in code) so it's clear the\n// classification fields aren't arbitrary. Locked with Paul, 2026-09-19:\n// Safety → OSHA 1904 + ISO 45001; Environmental → ISO 14001; Stop Work →\n// ISO 45001 (as the parent OH&S management-system framework stop-work\n// authority sits under — it isn't itself a numbered standard).\n\nexport interface ReportStandardReference {\n category: ReportCategory;\n /** Short label shown on the form, e.g. \"OSHA 1904 · ISO 45001\". */\n label: string;\n /** One sentence explaining what the standard governs here. */\n description: string;\n}\n\nexport const REPORT_STANDARD_REFERENCES: Record<\n ReportCategory,\n ReportStandardReference\n> = {\n safety: {\n category: \"safety\",\n label: \"OSHA 29 CFR 1904 · ISO 45001\",\n description:\n \"Injury classification follows OSHA's recordkeeping taxonomy (first aid only / recordable / days away, restricted, or transferred / fatality), within an ISO 45001 occupational health & safety management system.\",\n },\n environmental: {\n category: \"environmental\",\n label: \"ISO 14001\",\n description:\n \"Environmental aspect and regulatory-reportable classification follow ISO 14001's environmental management system requirements.\",\n },\n \"stop-work\": {\n category: \"stop-work\",\n label: \"ISO 45001\",\n description:\n \"Stop Work authority is a hazard-control mechanism exercised under an ISO 45001 occupational health & safety management system.\",\n },\n};\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\n/** Create a default ReportingToolbarConfig with sensible defaults. */\nexport function createReportingConfig(\n moduleName: string,\n handlers: {\n onReportSubmit: (report: IncidentReport) => Promise<void> | void;\n onFeedbackSubmit: (feedback: FeedbackReport) => Promise<void> | void;\n }\n): ReportingToolbarConfig {\n return {\n moduleName,\n showIncidentReporting: true,\n showFeedback: true,\n reportCategories: [\"safety\", \"environmental\", \"stop-work\"],\n feedbackCategories: [\"problem\", \"suggestion\"],\n ...handlers,\n };\n}\n\n/**\n * Read a picked File into `{ dataBase64, mime }` (no data: prefix), rejecting\n * non-images and anything over `maxBytes` decoded. Pure-ish (needs FileReader);\n * exported so the size rule can be unit-tested and reused.\n */\nexport const FEEDBACK_SCREENSHOT_MAX_BYTES = 300 * 1024;\n\nexport function isAcceptableScreenshot(\n file: { type: string; size: number },\n maxBytes: number = FEEDBACK_SCREENSHOT_MAX_BYTES\n): { ok: true } | { ok: false; reason: string } {\n if (!file.type.startsWith(\"image/\")) {\n return { ok: false, reason: \"Please choose an image file.\" };\n }\n if (file.size > maxBytes) {\n return {\n ok: false,\n reason: `Image is too large (max ${Math.round(maxBytes / 1024)} KB).`,\n };\n }\n return { ok: true };\n}\n\nexport function readFileAsScreenshot(file: File): Promise<ReportScreenshot> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onerror = () => reject(new Error(\"Could not read the file.\"));\n reader.onload = () => {\n const result = String(reader.result || \"\");\n const comma = result.indexOf(\",\");\n const dataBase64 = comma >= 0 ? result.slice(comma + 1) : result;\n resolve({ dataBase64, mime: file.type || \"image/png\" });\n };\n reader.readAsDataURL(file);\n });\n}\n\n// ─── ReportingToolbar component ─────────────────────────────────────────────\n\nexport {\n ReportingToolbar,\n type ReportingToolbarProps,\n} from \"./ReportingToolbar\";\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/reporting/ReportingToolbar.tsx","../../src/reporting/index.ts"],"sourcesContent":["/**\n * ReportingToolbar — the header \"User Actions\" dropdown for all Oplytics\n * subdomains.\n *\n * ⚙ User Actions ▾ → Report\n * Safety Incident (OSHA 1904 · ISO 45001)\n * Environmental (ISO 14001)\n * Stop Work (ISO 45001)\n * ── (separator) ──\n * Feedback\n * Report a Problem\n * Suggestion\n *\n * Feedback opens a self-contained dialog: pick the service, say whether it's\n * blocking you, a short summary, the detail, and optionally attach a\n * screenshot. On submit `onFeedbackSubmit(feedback: FeedbackReport)` is called\n * — the host POSTs it to Portal's /api/service/feedback.\n *\n * Report opens one of three bespoke dialogs (Safety / Environmental / Stop\n * Work), each built against the external standard named in its header and\n * `REPORT_STANDARD_REFERENCES` (see reporting/index.ts) — shown to the user\n * via an `Alert` at the top of the form, not just documented in code. On\n * submit `onReportSubmit(report: IncidentReport)` is called with the\n * discriminated union — the host POSTs it to Portal's /api/service/reports.\n *\n * Oplytics dark theme: #0A0E1A bg, #8C34E9 accent.\n */\nimport { useRef, useState } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport {\n Button,\n Badge,\n Textarea,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Input,\n Label,\n Switch,\n Checkbox,\n Alert,\n AlertTitle,\n AlertDescription,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n DialogFooter,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from \"../primitives\";\nimport {\n ShieldAlert,\n Leaf,\n OctagonX,\n Bug,\n Lightbulb,\n ChevronDown,\n Send,\n ImagePlus,\n X,\n UserCog,\n BookMarked,\n} from \"lucide-react\";\nimport { toast } from \"sonner\";\nimport {\n OPLYTICS_SERVICES,\n REPORT_STANDARD_REFERENCES,\n isAcceptableScreenshot,\n readFileAsScreenshot,\n type ReportCategory,\n type FeedbackCategory,\n type IncidentReport,\n type SafetyIncidentReport,\n type EnvironmentalIncidentReport,\n type StopWorkIncidentReport,\n type IncidentSeverity,\n type OshaInjuryClassification,\n type EnvironmentalAspect,\n type HazardCategory,\n type FeedbackReport,\n type ReportScreenshot,\n type ReportSubmitter,\n type ReportHierarchyContext,\n type OplyticsService,\n type ReportingToolbarConfig,\n type ReportOutcome,\n} from \"./index\";\n\n/* ── Option catalogues ── */\n\ninterface ReportOption {\n category: ReportCategory;\n label: string;\n description: string;\n icon: React.ElementType;\n color: string;\n}\n\nconst ALL_REPORT_OPTIONS: ReportOption[] = [\n {\n category: \"safety\",\n label: \"Safety Incident\",\n description: \"Report a safety hazard, near miss, or injury\",\n icon: ShieldAlert,\n color: \"#EF4444\",\n },\n {\n category: \"environmental\",\n label: \"Environmental\",\n description: \"Report an environmental concern or spill\",\n icon: Leaf,\n color: \"#22C55E\",\n },\n {\n category: \"stop-work\",\n label: \"Stop Work\",\n description: \"Raise a stop work authority — immediate halt\",\n icon: OctagonX,\n color: \"#F59E0B\",\n },\n];\n\ninterface FeedbackOption {\n category: FeedbackCategory;\n label: string;\n description: string;\n icon: React.ElementType;\n color: string;\n}\n\nconst ALL_FEEDBACK_OPTIONS: FeedbackOption[] = [\n {\n category: \"problem\",\n label: \"Report a Problem\",\n description: \"Something isn't working\",\n icon: Bug,\n color: \"#EF4444\",\n },\n {\n category: \"suggestion\",\n label: \"Suggestion\",\n description: \"An idea for improvement\",\n icon: Lightbulb,\n color: \"#8C34E9\",\n },\n];\n\n/* ── Props ── */\n\nexport interface ReportingToolbarProps extends Omit<\n ReportingToolbarConfig,\n \"moduleName\"\n> {\n /** Service/module name (e.g. \"Connect\"). Defaults to \"Oplytics\". */\n moduleName?: string;\n /** The current org-hierarchy selection, forwarded on every submission. */\n hierarchyContext?: ReportHierarchyContext;\n /** The authenticated user — openId is authoritative, name/email are hints. */\n currentUser?: ReportSubmitter;\n /** Service list for the feedback picker. Defaults to OPLYTICS_SERVICES. */\n services?: OplyticsService[];\n /** Pre-selected service slug in the feedback picker. */\n defaultServiceSlug?: string;\n /** Show the \"attach screenshot\" control (default: true). */\n enableScreenshot?: boolean;\n /**\n * While true, the Report items are disabled with a \"Coming soon\" badge and\n * `onReportSubmit` is never called. Defaults to `false` — pass `true` only\n * if this host's `onReportSubmit` doesn't actually persist the report yet.\n */\n reportsComingSoon?: boolean;\n /**\n * @deprecated the toolbar handles responsive collapse itself.\n */\n compact?: boolean;\n /** CSS class for the container. */\n className?: string;\n}\n\n/* ── Component ── */\n\n/** Fields shared by all three Report forms' local state. */\ninterface ReportDraft {\n description: string;\n location: string;\n severity: IncidentSeverity;\n immediateAction: string;\n}\n\nconst EMPTY_DRAFT: ReportDraft = {\n description: \"\",\n location: \"\",\n severity: \"medium\",\n immediateAction: \"\",\n};\n\n/**\n * shared-ui#135: a host may return a `ReportOutcome` from `onReportSubmit`\n * to say whether the linked Action Manager action was created. A host that\n * returns nothing (today's hosts) gets the pre-#135 toast, unchanged.\n * Exported for direct unit testing (see ReportingToolbar.reportOutcome.test.ts).\n */\nexport function toastReportOutcome(outcome: void | ReportOutcome) {\n if (!outcome || !outcome.actionStatus) {\n toast.success(\"Report logged. Thanks for raising it.\");\n return;\n }\n if (outcome.actionStatus === \"created\") {\n toast.success(\n outcome.actionCode\n ? `Report logged. Action ${outcome.actionCode} was created for it.`\n : \"Report logged. An action was created for it.\"\n );\n return;\n }\n toast.success(\"Report logged.\");\n toast.warning(\n outcome.actionProblem ||\n \"The report is saved, but its Action Manager action could not be created.\"\n );\n}\n\nexport function ReportingToolbar({\n moduleName = \"Oplytics\",\n showIncidentReporting = true,\n showFeedback = true,\n reportCategories = [\"safety\", \"environmental\", \"stop-work\"],\n feedbackCategories = [\"problem\", \"suggestion\"],\n onReportSubmit,\n onFeedbackSubmit,\n hierarchyContext,\n currentUser,\n services = OPLYTICS_SERVICES,\n defaultServiceSlug,\n enableScreenshot = true,\n reportsComingSoon = false,\n className,\n}: ReportingToolbarProps) {\n const reportOptions = ALL_REPORT_OPTIONS.filter(opt =>\n reportCategories.includes(opt.category)\n );\n const feedbackOptions = ALL_FEEDBACK_OPTIONS.filter(opt =>\n feedbackCategories.includes(opt.category)\n );\n\n const firstServiceSlug = defaultServiceSlug ?? services[0]?.slug ?? \"portal\";\n\n /* ── Feedback dialog state ── */\n const [feedbackOpen, setFeedbackOpen] = useState(false);\n const [category, setCategory] = useState<FeedbackCategory>(\"problem\");\n const [serviceSlug, setServiceSlug] = useState(firstServiceSlug);\n const [isBlocking, setIsBlocking] = useState(false);\n const [summary, setSummary] = useState(\"\");\n const [details, setDetails] = useState(\"\");\n const [screenshot, setScreenshot] = useState<ReportScreenshot | null>(null);\n const [screenshotName, setScreenshotName] = useState<string | null>(null);\n const [submitting, setSubmitting] = useState(false);\n const fileInputRef = useRef<HTMLInputElement | null>(null);\n\n const openFeedback = (c: FeedbackCategory) => {\n setCategory(c);\n setServiceSlug(firstServiceSlug);\n setIsBlocking(false);\n setSummary(\"\");\n setDetails(\"\");\n setScreenshot(null);\n setScreenshotName(null);\n setFeedbackOpen(true);\n };\n\n /* ── Report dialog state (Safety / Environmental / Stop Work) ── */\n const [reportOpen, setReportOpen] = useState(false);\n const [reportCategory, setReportCategory] =\n useState<ReportCategory>(\"safety\");\n const [reportDraft, setReportDraft] = useState<ReportDraft>(EMPTY_DRAFT);\n const [reportSubmitting, setReportSubmitting] = useState(false);\n\n // Safety-specific\n const [injuryOccurred, setInjuryOccurred] = useState(false);\n const [injuryClassification, setInjuryClassification] = useState<\n OshaInjuryClassification | undefined\n >(undefined);\n const [daysAwayFromWork, setDaysAwayFromWork] = useState(\"\");\n const [daysRestrictedOrTransferred, setDaysRestrictedOrTransferred] =\n useState(\"\");\n const [firstAiderAttended, setFirstAiderAttended] = useState(false);\n const [witnessName, setWitnessName] = useState(\"\");\n const [bodyPart, setBodyPart] = useState(\"\");\n const [treatmentType, setTreatmentType] = useState(\"\");\n\n // Environmental-specific\n const [environmentalAspect, setEnvironmentalAspect] = useState<\n EnvironmentalAspect | undefined\n >(undefined);\n const [regulatoryReportable, setRegulatoryReportable] = useState(false);\n const [substance, setSubstance] = useState(\"\");\n const [estimatedQuantity, setEstimatedQuantity] = useState(\"\");\n const [contained, setContained] = useState(false);\n const [reachedWatercourse, setReachedWatercourse] = useState(false);\n\n // Stop Work-specific\n const [hazardCategory, setHazardCategory] = useState<\n HazardCategory | undefined\n >(undefined);\n const [hazardDescription, setHazardDescription] = useState(\"\");\n const [areaMadeSafe, setAreaMadeSafe] = useState(false);\n const [workResumed, setWorkResumed] = useState(false);\n\n const openReport = (c: ReportCategory) => {\n setReportCategory(c);\n setReportDraft({\n ...EMPTY_DRAFT,\n severity: c === \"stop-work\" ? \"critical\" : \"medium\",\n });\n setInjuryOccurred(false);\n setInjuryClassification(undefined);\n setDaysAwayFromWork(\"\");\n setDaysRestrictedOrTransferred(\"\");\n setFirstAiderAttended(false);\n setWitnessName(\"\");\n setBodyPart(\"\");\n setTreatmentType(\"\");\n setEnvironmentalAspect(undefined);\n setRegulatoryReportable(false);\n setSubstance(\"\");\n setEstimatedQuantity(\"\");\n setContained(false);\n setReachedWatercourse(false);\n setHazardCategory(undefined);\n setHazardDescription(\"\");\n setAreaMadeSafe(false);\n setWorkResumed(false);\n setReportOpen(true);\n };\n\n const submitReport = async () => {\n if (!reportDraft.description.trim()) {\n toast.error(\"Please describe what happened.\");\n return;\n }\n setReportSubmitting(true);\n try {\n const base = {\n description: reportDraft.description.trim(),\n location: reportDraft.location.trim() || undefined,\n severity: reportDraft.severity,\n immediateAction: reportDraft.immediateAction.trim() || undefined,\n moduleName,\n timestamp: new Date().toISOString(),\n submittedBy: currentUser,\n hierarchyContext,\n };\n\n let report: IncidentReport;\n if (reportCategory === \"safety\") {\n const safety: SafetyIncidentReport = {\n ...base,\n category: \"safety\",\n injuryOccurred,\n injuryClassification,\n daysAwayFromWork: daysAwayFromWork\n ? Number(daysAwayFromWork)\n : undefined,\n daysRestrictedOrTransferred: daysRestrictedOrTransferred\n ? Number(daysRestrictedOrTransferred)\n : undefined,\n firstAiderAttended,\n witnessName: witnessName.trim() || undefined,\n bodyPart: bodyPart.trim() || undefined,\n treatmentType: treatmentType.trim() || undefined,\n };\n report = safety;\n } else if (reportCategory === \"environmental\") {\n const environmental: EnvironmentalIncidentReport = {\n ...base,\n category: \"environmental\",\n environmentalAspect,\n regulatoryReportable,\n substance: substance.trim() || undefined,\n estimatedQuantity: estimatedQuantity.trim() || undefined,\n contained,\n reachedWatercourse,\n };\n report = environmental;\n } else {\n const stopWork: StopWorkIncidentReport = {\n ...base,\n category: \"stop-work\",\n hazardCategory,\n hazardDescription: hazardDescription.trim() || undefined,\n areaMadeSafe,\n workResumed,\n };\n report = stopWork;\n }\n\n const outcome = await onReportSubmit(report);\n toastReportOutcome(outcome);\n setReportOpen(false);\n } catch (err) {\n console.error(\"[ReportingToolbar] report submit failed:\", err);\n toast.error(\"Couldn't submit that. Please try again.\");\n } finally {\n setReportSubmitting(false);\n }\n };\n\n const handlePickFile = async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n e.target.value = \"\"; // allow re-picking the same file\n if (!file) return;\n const check = isAcceptableScreenshot(file);\n if (!check.ok) {\n toast.error(check.reason);\n return;\n }\n try {\n const shot = await readFileAsScreenshot(file);\n setScreenshot(shot);\n setScreenshotName(file.name);\n } catch (err) {\n console.error(\"[ReportingToolbar] screenshot read failed:\", err);\n toast.error(\"Could not read that image.\");\n }\n };\n\n const submitFeedback = async () => {\n if (!details.trim()) {\n toast.error(\"Please describe the problem or idea.\");\n return;\n }\n setSubmitting(true);\n try {\n const feedback: FeedbackReport = {\n category,\n serviceSlug,\n summary: summary.trim() || undefined,\n details: details.trim(),\n isBlocking,\n moduleName,\n timestamp: new Date().toISOString(),\n pageContext:\n typeof window !== \"undefined\" ? window.location.pathname : undefined,\n submittedBy: currentUser,\n screenshot: screenshot ?? undefined,\n };\n await onFeedbackSubmit(feedback);\n toast.success(\n category === \"problem\"\n ? \"Thanks — we've logged it. You'll get a confirmation email.\"\n : \"Thanks for the suggestion — you'll get a confirmation email.\"\n );\n setFeedbackOpen(false);\n } catch (err) {\n console.error(\"[ReportingToolbar] feedback submit failed:\", err);\n toast.error(\"Couldn't submit that. Please try again.\");\n } finally {\n setSubmitting(false);\n }\n };\n\n const showReportGroup = showIncidentReporting && reportOptions.length > 0;\n const showFeedbackGroup = showFeedback && feedbackOptions.length > 0;\n if (!showReportGroup && !showFeedbackGroup) return null;\n\n return (\n <>\n <div className={cn(\"flex items-center gap-2\", className)}>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-9 gap-1.5 text-xs font-medium text-[#8890A0] hover:bg-[#1E2738] hover:text-[#E2E8F0] border border-[#1E2738]\"\n >\n <UserCog className=\"h-3.5 w-3.5\" />\n <span className=\"shell-hidden shell-sm:inline\">User Actions</span>\n <ChevronDown className=\"h-3 w-3\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-60\">\n {showReportGroup && (\n <>\n <DropdownMenuLabel className=\"flex items-center gap-2 text-[10px] uppercase tracking-wide text-muted-foreground\">\n Report\n {reportsComingSoon && (\n <Badge\n variant=\"outline\"\n className=\"border-[#8C34E9]/40 text-[9px] font-normal text-[#C084FC]\"\n >\n Coming soon\n </Badge>\n )}\n </DropdownMenuLabel>\n {reportOptions.map(opt => (\n <DropdownMenuItem\n key={opt.category}\n disabled={reportsComingSoon}\n onClick={() => {\n if (!reportsComingSoon) openReport(opt.category);\n }}\n className=\"gap-3 py-2.5\"\n >\n <opt.icon\n className=\"h-4 w-4\"\n style={{ color: opt.color }}\n />\n <div>\n <p className=\"text-sm font-medium\">{opt.label}</p>\n <p className=\"text-[10px] text-muted-foreground\">\n {REPORT_STANDARD_REFERENCES[opt.category].label}\n </p>\n </div>\n </DropdownMenuItem>\n ))}\n </>\n )}\n\n {showReportGroup && showFeedbackGroup && <DropdownMenuSeparator />}\n\n {showFeedbackGroup && (\n <>\n <DropdownMenuLabel className=\"text-[10px] uppercase tracking-wide text-muted-foreground\">\n Feedback\n </DropdownMenuLabel>\n {feedbackOptions.map(opt => (\n <DropdownMenuItem\n key={opt.category}\n onClick={() => openFeedback(opt.category)}\n className=\"gap-3 py-2.5\"\n >\n <opt.icon\n className=\"h-4 w-4\"\n style={{ color: opt.color }}\n />\n <div>\n <p className=\"text-sm font-medium\">{opt.label}</p>\n <p className=\"text-[10px] text-muted-foreground\">\n {opt.description}\n </p>\n </div>\n </DropdownMenuItem>\n ))}\n </>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n\n {/* Feedback dialog */}\n <Dialog open={feedbackOpen} onOpenChange={setFeedbackOpen}>\n <DialogContent className=\"sm:max-w-[520px]\">\n <DialogHeader>\n <DialogTitle className=\"flex items-center gap-2\">\n {category === \"problem\" ? (\n <Bug className=\"h-5 w-5 text-[#EF4444]\" />\n ) : (\n <Lightbulb className=\"h-5 w-5 text-[#8C34E9]\" />\n )}\n {category === \"problem\"\n ? \"Report a Problem\"\n : \"Share a Suggestion\"}\n </DialogTitle>\n <DialogDescription>\n {category === \"problem\"\n ? \"Tell us what went wrong. We triage within 24 hours.\"\n : \"We review every suggestion and schedule improvements against the roadmap.\"}\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4 py-2\">\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">Which service?</Label>\n <Select value={serviceSlug} onValueChange={setServiceSlug}>\n <SelectTrigger className=\"h-9\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {services.map(s => (\n <SelectItem key={s.slug} value={s.slug}>\n {s.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {category === \"problem\" && (\n <div className=\"flex items-center justify-between rounded-md border border-[#1E2738] px-3 py-2\">\n <div>\n <p className=\"text-sm font-medium\">Is this blocking you?</p>\n <p className=\"text-[10px] text-muted-foreground\">\n Blocking issues are treated as high priority.\n </p>\n </div>\n <Switch checked={isBlocking} onCheckedChange={setIsBlocking} />\n </div>\n )}\n\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">Summary</Label>\n <Input\n value={summary}\n onChange={e => setSummary(e.target.value)}\n placeholder={\n category === \"problem\"\n ? \"Brief summary of the issue\"\n : \"Brief summary of your idea\"\n }\n className=\"h-9\"\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">Details *</Label>\n <Textarea\n value={details}\n onChange={e => setDetails(e.target.value)}\n placeholder={\n category === \"problem\"\n ? \"What were you trying to do? What happened instead?\"\n : \"Describe your suggestion and how it would help…\"\n }\n rows={5}\n />\n </div>\n\n {enableScreenshot && (\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">\n Screenshot (optional)\n </Label>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n className=\"hidden\"\n onChange={handlePickFile}\n />\n {screenshot ? (\n <div className=\"flex items-center gap-2 rounded-md border border-[#1E2738] px-3 py-2\">\n <ImagePlus className=\"h-4 w-4 text-[#8C34E9]\" />\n <span className=\"flex-1 truncate text-xs text-[#E2E8F0]\">\n {screenshotName ?? \"screenshot\"}\n </span>\n <button\n type=\"button\"\n onClick={() => {\n setScreenshot(null);\n setScreenshotName(null);\n }}\n className=\"text-muted-foreground hover:text-[#E2E8F0]\"\n aria-label=\"Remove screenshot\"\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n ) : (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"h-9 gap-1.5\"\n onClick={() => fileInputRef.current?.click()}\n >\n <ImagePlus className=\"h-3.5 w-3.5\" />\n Attach an image\n </Button>\n )}\n </div>\n )}\n\n <p className=\"text-[10px] text-muted-foreground\">\n {moduleName} · you'll get a confirmation email once this is\n logged.\n </p>\n </div>\n\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => setFeedbackOpen(false)}>\n Cancel\n </Button>\n <Button\n onClick={submitFeedback}\n disabled={submitting || !details.trim()}\n className=\"gap-2\"\n style={{\n backgroundColor: category === \"problem\" ? \"#EF4444\" : \"#8C34E9\",\n color: \"white\",\n }}\n >\n <Send className=\"h-3.5 w-3.5\" />\n {submitting ? \"Submitting…\" : \"Submit\"}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n\n {/* Report dialog — Safety / Environmental / Stop Work */}\n <Dialog open={reportOpen} onOpenChange={setReportOpen}>\n <DialogContent className=\"sm:max-w-[560px] max-h-[90vh] overflow-y-auto\">\n <DialogHeader>\n <DialogTitle className=\"flex items-center gap-2\">\n {reportCategory === \"safety\" && (\n <ShieldAlert className=\"h-5 w-5 text-[#EF4444]\" />\n )}\n {reportCategory === \"environmental\" && (\n <Leaf className=\"h-5 w-5 text-[#22C55E]\" />\n )}\n {reportCategory === \"stop-work\" && (\n <OctagonX className=\"h-5 w-5 text-[#F59E0B]\" />\n )}\n {ALL_REPORT_OPTIONS.find(o => o.category === reportCategory)\n ?.label ?? \"Report\"}\n </DialogTitle>\n <DialogDescription>\n {\n ALL_REPORT_OPTIONS.find(o => o.category === reportCategory)\n ?.description\n }\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4 py-2\">\n <Alert className=\"border-[#8C34E9]/30 bg-[#8C34E9]/5\">\n <BookMarked className=\"h-4 w-4 text-[#8C34E9]\" />\n <AlertTitle className=\"text-xs font-semibold text-[#C084FC]\">\n {REPORT_STANDARD_REFERENCES[reportCategory].label}\n </AlertTitle>\n <AlertDescription className=\"text-[11px]\">\n {REPORT_STANDARD_REFERENCES[reportCategory].description}\n </AlertDescription>\n </Alert>\n\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">Severity</Label>\n <Select\n value={reportDraft.severity}\n onValueChange={(v: IncidentSeverity) =>\n setReportDraft(d => ({ ...d, severity: v }))\n }\n >\n <SelectTrigger className=\"h-9\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"low\">Low</SelectItem>\n <SelectItem value=\"medium\">Medium</SelectItem>\n <SelectItem value=\"high\">High</SelectItem>\n <SelectItem value=\"critical\">Critical</SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">Location (optional)</Label>\n <Input\n value={reportDraft.location}\n onChange={e =>\n setReportDraft(d => ({ ...d, location: e.target.value }))\n }\n placeholder=\"Where did this happen?\"\n className=\"h-9\"\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">What happened? *</Label>\n <Textarea\n value={reportDraft.description}\n onChange={e =>\n setReportDraft(d => ({\n ...d,\n description: e.target.value,\n }))\n }\n placeholder=\"Describe what happened…\"\n rows={4}\n />\n </div>\n\n {reportCategory === \"safety\" && (\n <>\n <div className=\"flex items-center justify-between rounded-md border border-[#1E2738] px-3 py-2\">\n <p className=\"text-sm font-medium\">Was anyone injured?</p>\n <Switch\n checked={injuryOccurred}\n onCheckedChange={setInjuryOccurred}\n />\n </div>\n\n {injuryOccurred && (\n <>\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">\n Injury classification (OSHA 1904)\n </Label>\n <Select\n value={injuryClassification}\n onValueChange={(v: OshaInjuryClassification) =>\n setInjuryClassification(v)\n }\n >\n <SelectTrigger className=\"h-9\">\n <SelectValue placeholder=\"Select a classification\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"first_aid_only\">\n First aid only\n </SelectItem>\n <SelectItem value=\"recordable\">Recordable</SelectItem>\n <SelectItem value=\"days_away_restricted_transfer\">\n Days away, restricted, or transferred (DART)\n </SelectItem>\n <SelectItem value=\"fatality\">Fatality</SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n {injuryClassification ===\n \"days_away_restricted_transfer\" && (\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">\n Days away from work\n </Label>\n <Input\n type=\"number\"\n min={0}\n value={daysAwayFromWork}\n onChange={e => setDaysAwayFromWork(e.target.value)}\n className=\"h-9\"\n />\n </div>\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">\n Days restricted/transferred\n </Label>\n <Input\n type=\"number\"\n min={0}\n value={daysRestrictedOrTransferred}\n onChange={e =>\n setDaysRestrictedOrTransferred(e.target.value)\n }\n className=\"h-9\"\n />\n </div>\n </div>\n )}\n\n <div className=\"flex items-center gap-2\">\n <Checkbox\n id=\"firstAiderAttended\"\n checked={firstAiderAttended}\n onCheckedChange={c => setFirstAiderAttended(!!c)}\n />\n <Label\n htmlFor=\"firstAiderAttended\"\n className=\"text-xs font-normal\"\n >\n A first aider attended\n </Label>\n </div>\n\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">\n Body part affected\n </Label>\n <Input\n value={bodyPart}\n onChange={e => setBodyPart(e.target.value)}\n className=\"h-9\"\n />\n </div>\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">\n Treatment given\n </Label>\n <Input\n value={treatmentType}\n onChange={e => setTreatmentType(e.target.value)}\n className=\"h-9\"\n />\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">\n Witness name (optional)\n </Label>\n <Input\n value={witnessName}\n onChange={e => setWitnessName(e.target.value)}\n className=\"h-9\"\n />\n </div>\n </>\n )}\n </>\n )}\n\n {reportCategory === \"environmental\" && (\n <>\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">\n Environmental aspect (ISO 14001)\n </Label>\n <Select\n value={environmentalAspect}\n onValueChange={(v: EnvironmentalAspect) =>\n setEnvironmentalAspect(v)\n }\n >\n <SelectTrigger className=\"h-9\">\n <SelectValue placeholder=\"What element of the environment is affected?\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"air_emission\">Air emission</SelectItem>\n <SelectItem value=\"water_discharge\">\n Water discharge\n </SelectItem>\n <SelectItem value=\"soil_contamination\">\n Soil contamination\n </SelectItem>\n <SelectItem value=\"waste\">Waste</SelectItem>\n <SelectItem value=\"spill\">Spill</SelectItem>\n <SelectItem value=\"noise\">Noise</SelectItem>\n <SelectItem value=\"other\">Other</SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">\n Substance (optional)\n </Label>\n <Input\n value={substance}\n onChange={e => setSubstance(e.target.value)}\n className=\"h-9\"\n />\n </div>\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">\n Estimated quantity\n </Label>\n <Input\n value={estimatedQuantity}\n onChange={e => setEstimatedQuantity(e.target.value)}\n className=\"h-9\"\n />\n </div>\n </div>\n\n <div className=\"flex items-center gap-2\">\n <Checkbox\n id=\"contained\"\n checked={contained}\n onCheckedChange={c => setContained(!!c)}\n />\n <Label htmlFor=\"contained\" className=\"text-xs font-normal\">\n Contained on site\n </Label>\n </div>\n\n <div className=\"flex items-center gap-2\">\n <Checkbox\n id=\"reachedWatercourse\"\n checked={reachedWatercourse}\n onCheckedChange={c => setReachedWatercourse(!!c)}\n />\n <Label\n htmlFor=\"reachedWatercourse\"\n className=\"text-xs font-normal\"\n >\n Reached a watercourse\n </Label>\n </div>\n\n <div className=\"flex items-center gap-2\">\n <Checkbox\n id=\"regulatoryReportable\"\n checked={regulatoryReportable}\n onCheckedChange={c => setRegulatoryReportable(!!c)}\n />\n <Label\n htmlFor=\"regulatoryReportable\"\n className=\"text-xs font-normal\"\n >\n This may need reporting to a regulator\n </Label>\n </div>\n </>\n )}\n\n {reportCategory === \"stop-work\" && (\n <>\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">\n Hazard category (ISO 45001)\n </Label>\n <Select\n value={hazardCategory}\n onValueChange={(v: HazardCategory) => setHazardCategory(v)}\n >\n <SelectTrigger className=\"h-9\">\n <SelectValue placeholder=\"What kind of hazard?\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"mechanical\">Mechanical</SelectItem>\n <SelectItem value=\"electrical\">Electrical</SelectItem>\n <SelectItem value=\"chemical\">Chemical</SelectItem>\n <SelectItem value=\"working_at_height\">\n Working at height\n </SelectItem>\n <SelectItem value=\"confined_space\">\n Confined space\n </SelectItem>\n <SelectItem value=\"vehicle_traffic\">\n Vehicle / traffic\n </SelectItem>\n <SelectItem value=\"other\">Other</SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">\n Hazard description\n </Label>\n <Textarea\n value={hazardDescription}\n onChange={e => setHazardDescription(e.target.value)}\n rows={3}\n />\n </div>\n\n <div className=\"flex items-center gap-2\">\n <Checkbox\n id=\"areaMadeSafe\"\n checked={areaMadeSafe}\n onCheckedChange={c => setAreaMadeSafe(!!c)}\n />\n <Label htmlFor=\"areaMadeSafe\" className=\"text-xs font-normal\">\n Area has been made safe\n </Label>\n </div>\n\n <div className=\"flex items-center gap-2\">\n <Checkbox\n id=\"workResumed\"\n checked={workResumed}\n onCheckedChange={c => setWorkResumed(!!c)}\n />\n <Label htmlFor=\"workResumed\" className=\"text-xs font-normal\">\n Work has resumed\n </Label>\n </div>\n </>\n )}\n\n <div className=\"space-y-2\">\n <Label className=\"text-xs font-medium\">\n Immediate action taken (optional)\n </Label>\n <Textarea\n value={reportDraft.immediateAction}\n onChange={e =>\n setReportDraft(d => ({\n ...d,\n immediateAction: e.target.value,\n }))\n }\n rows={2}\n />\n </div>\n\n <p className=\"text-[10px] text-muted-foreground\">\n {moduleName} · this is logged centrally and escalated\n automatically.\n </p>\n </div>\n\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => setReportOpen(false)}>\n Cancel\n </Button>\n <Button\n onClick={submitReport}\n disabled={reportSubmitting || !reportDraft.description.trim()}\n className=\"gap-2\"\n style={{\n backgroundColor: ALL_REPORT_OPTIONS.find(\n o => o.category === reportCategory\n )?.color,\n color: \"white\",\n }}\n >\n <Send className=\"h-3.5 w-3.5\" />\n {reportSubmitting ? \"Submitting…\" : \"Submit\"}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </>\n );\n}\n","/**\n * @oplytics/shared-ui/reporting\n *\n * Reporting and feedback toolbar types + configuration for all Oplytics\n * subdomains — the header \"User Actions\" dropdown (ReportingToolbar).\n *\n * Both groups are fully wired. Feedback collects a service, an \"is this\n * blocking you?\" signal, a short summary, the detail, and an optional\n * screenshot, then calls `onFeedbackSubmit` with the canonical\n * `FeedbackReport` shape. Report (Safety / Environmental / Stop Work) is\n * three bespoke forms, one per category, each collecting the fields needed\n * to conform to the external standard it's built against (see each\n * category's comment below), then calls `onReportSubmit` with the\n * discriminated `IncidentReport` union. Either way the host owns the\n * transport (Feedback POSTs to Portal's `/api/service/feedback`; Report\n * POSTs to Portal's `/api/service/reports`).\n *\n * `reportsComingSoon` defaults to `false` as of this version — pass `true`\n * per-host only if that host's `onReportSubmit` doesn't actually persist yet.\n */\n\n// ─── Report types ───────────────────────────────────────────────────────────\n\nexport type ReportCategory = \"safety\" | \"environmental\" | \"stop-work\";\nexport type FeedbackCategory = \"problem\" | \"suggestion\";\n\n/** Where in the org hierarchy the submission was made. */\nexport interface ReportHierarchyContext {\n enterpriseId?: number;\n businessUnitId?: number;\n siteId?: number;\n areaId?: number;\n assetId?: number;\n}\n\n/** The authenticated user, as much as the host can supply. */\nexport interface ReportSubmitter {\n /** JWT `openId` claim — the host must NOT trust name/email over this. */\n openId?: string;\n name?: string;\n email?: string;\n}\n\n/** An attached screenshot: base64 WITHOUT the `data:...;base64,` prefix. */\nexport interface ReportScreenshot {\n dataBase64: string;\n mime: string;\n}\n\nexport type IncidentSeverity = \"low\" | \"medium\" | \"high\" | \"critical\";\n\n/**\n * OSHA 29 CFR 1904's own injury-and-illness recordkeeping classification —\n * the standard the Safety form's injury fields conform to, alongside\n * ISO 45001 (the international OH&S management-system standard it sits\n * under). Shown to the user on the form, not just used internally.\n */\nexport type OshaInjuryClassification =\n | \"none\"\n | \"first_aid_only\"\n | \"recordable\"\n | \"days_away_restricted_transfer\"\n | \"fatality\";\n\n/** ISO 14001's \"environmental aspect\" concept — which element of the\n * environment an incident affects. The standard the Environmental form's\n * classification field conforms to. */\nexport type EnvironmentalAspect =\n | \"air_emission\"\n | \"water_discharge\"\n | \"soil_contamination\"\n | \"waste\"\n | \"spill\"\n | \"noise\"\n | \"other\";\n\n/** ISO 45001's hierarchy-of-controls hazard classification. Stop Work\n * authority isn't itself a numbered standard — it's a control mechanism\n * exercised under an ISO 45001 OH&S management system, which this\n * classification maps to. */\nexport type HazardCategory =\n | \"mechanical\"\n | \"electrical\"\n | \"chemical\"\n | \"working_at_height\"\n | \"confined_space\"\n | \"vehicle_traffic\"\n | \"other\";\n\ninterface IncidentReportBase {\n description: string;\n location?: string;\n severity?: IncidentSeverity;\n /** Optional — what was done at the scene to make the area safe. */\n immediateAction?: string;\n moduleName: string;\n /** ISO timestamp */\n timestamp: string;\n submittedBy?: ReportSubmitter;\n hierarchyContext?: ReportHierarchyContext;\n}\n\nexport interface SafetyIncidentReport extends IncidentReportBase {\n category: \"safety\";\n injuryOccurred: boolean;\n /** OSHA 1904 classification — see `OshaInjuryClassification`. */\n injuryClassification?: OshaInjuryClassification;\n /** OSHA DART-rate inputs — only meaningful when injuryClassification is\n * \"days_away_restricted_transfer\". */\n daysAwayFromWork?: number;\n daysRestrictedOrTransferred?: number;\n firstAiderAttended?: boolean;\n witnessName?: string;\n bodyPart?: string;\n treatmentType?: string;\n}\n\nexport interface EnvironmentalIncidentReport extends IncidentReportBase {\n category: \"environmental\";\n /** ISO 14001 environmental aspect — see `EnvironmentalAspect`. */\n environmentalAspect?: EnvironmentalAspect;\n /** Does this trigger a duty to notify an external regulator? */\n regulatoryReportable?: boolean;\n substance?: string;\n estimatedQuantity?: string;\n contained?: boolean;\n reachedWatercourse?: boolean;\n}\n\nexport interface StopWorkIncidentReport extends IncidentReportBase {\n category: \"stop-work\";\n /** ISO 45001 hazard classification — see `HazardCategory`. */\n hazardCategory?: HazardCategory;\n hazardDescription?: string;\n areaMadeSafe?: boolean;\n workResumed?: boolean;\n}\n\n/** Discriminated on `category` — narrow on it to get the right per-type\n * fields (`report.category === \"safety\"` narrows to `SafetyIncidentReport`,\n * etc.). Each variant maps 1:1 onto its own bespoke form; see\n * ReportingToolbar.tsx's SafetyForm/EnvironmentalForm/StopWorkForm. */\nexport type IncidentReport =\n SafetyIncidentReport | EnvironmentalIncidentReport | StopWorkIncidentReport;\n\n/**\n * What happened to the Action Manager action Portal's `POST\n * /api/service/reports` tries to create alongside the report itself. A host\n * that doesn't return this from `onReportSubmit` gets the pre-#135 toast\n * unchanged (shared-ui#135, from Oplytics-Digital/oplytics-subdomains#1078).\n */\nexport interface ReportOutcome {\n actionStatus?: \"created\" | \"not_created\";\n /** Plain sentence for the reporter when `actionStatus` is \"not_created\". */\n actionProblem?: string | null;\n /** e.g. \"ACT-026\" — shown when `actionStatus` is \"created\". */\n actionCode?: string | null;\n}\n\nexport interface FeedbackReport {\n category: FeedbackCategory;\n /** Canonical service slug the feedback is about (from the picker). */\n serviceSlug: string;\n /** Optional short one-liner shown above the detail. */\n summary?: string;\n /** Required detail body. */\n details: string;\n /** \"Is this blocking you?\" — drives priority server-side. */\n isBlocking: boolean;\n /** Human-readable originating module (\"OEE Manager\", \"SQDCP\", …). */\n moduleName: string;\n /** ISO timestamp */\n timestamp: string;\n /** Route the submitter was on. */\n pageContext?: string;\n submittedBy?: ReportSubmitter;\n screenshot?: ReportScreenshot;\n}\n\n// ─── Service catalogue ──────────────────────────────────────────────────────\n\nexport interface OplyticsService {\n slug: string;\n label: string;\n}\n\n/**\n * The services a user can attribute feedback to. Kept here so every subdomain's\n * picker shows the same list; a host can narrow or override it via the\n * `services` prop. Slugs match Portal's `platform_feedback.serviceSlug`.\n */\nexport const OPLYTICS_SERVICES: OplyticsService[] = [\n { slug: \"sqdcp\", label: \"SQDCP\" },\n { slug: \"oee-manager\", label: \"OEE Manager\" },\n { slug: \"action-manager\", label: \"Action Manager\" },\n { slug: \"policy-deployment\", label: \"Policy Deployment\" },\n { slug: \"portal\", label: \"Portal\" },\n];\n\n// ─── Toolbar configuration ──────────────────────────────────────────────────\n\nexport interface ReportingToolbarConfig {\n /** Service/module name (e.g. \"SQDCP\", \"OEE Manager\"). */\n moduleName: string;\n /** Whether to show the Report group at all (default: true). */\n showIncidentReporting?: boolean;\n /** Whether to show the Feedback group at all (default: true). */\n showFeedback?: boolean;\n /** Report categories to show (default: all three). */\n reportCategories?: ReportCategory[];\n /** Feedback categories to show (default: both). */\n feedbackCategories?: FeedbackCategory[];\n /** Called when an incident report is submitted. The host is responsible\n * for actually persisting it — see `reportsComingSoon` above. May\n * optionally return a `ReportOutcome` (shared-ui#135) so the toolbar can\n * tell the reporter whether the linked Action Manager action was\n * created; a host that returns nothing gets the pre-#135 toast. */\n onReportSubmit: (\n report: IncidentReport\n ) => Promise<void | ReportOutcome> | void | ReportOutcome;\n /** Called when feedback is submitted — the host POSTs it to Portal. */\n onFeedbackSubmit: (feedback: FeedbackReport) => Promise<void> | void;\n}\n\n// ─── Button definitions (kept for backwards compatibility / config helpers) ──\n\nexport interface ReportButtonDef {\n category: ReportCategory;\n label: string;\n iconName: string;\n color: string;\n description: string;\n}\n\nexport interface FeedbackButtonDef {\n category: FeedbackCategory;\n label: string;\n iconName: string;\n color: string;\n description: string;\n}\n\nexport const REPORT_BUTTONS: ReportButtonDef[] = [\n {\n category: \"safety\",\n label: \"Safety\",\n iconName: \"ShieldAlert\",\n color: \"#ef4444\",\n description: \"Report a safety incident, near-miss, or hazard\",\n },\n {\n category: \"environmental\",\n label: \"Environmental\",\n iconName: \"Leaf\",\n color: \"#22c55e\",\n description: \"Report an environmental incident or concern\",\n },\n {\n category: \"stop-work\",\n label: \"Stop Work\",\n iconName: \"OctagonX\",\n color: \"#f59e0b\",\n description: \"Raise a stop work authority — immediate safety concern\",\n },\n];\n\nexport const FEEDBACK_BUTTONS: FeedbackButtonDef[] = [\n {\n category: \"problem\",\n label: \"Problem\",\n iconName: \"AlertTriangle\",\n color: \"#ef4444\",\n description: \"Report a problem with the platform\",\n },\n {\n category: \"suggestion\",\n label: \"Suggestion\",\n iconName: \"Lightbulb\",\n color: \"#8C34E9\",\n description: \"Suggest an improvement or new feature\",\n },\n];\n\n// ─── Standards references ──────────────────────────────────────────────────\n//\n// Each Report form is built against a named external standard, shown to the\n// user on the form itself (not just documented in code) so it's clear the\n// classification fields aren't arbitrary. Locked with Paul, 2026-09-19:\n// Safety → OSHA 1904 + ISO 45001; Environmental → ISO 14001; Stop Work →\n// ISO 45001 (as the parent OH&S management-system framework stop-work\n// authority sits under — it isn't itself a numbered standard).\n\nexport interface ReportStandardReference {\n category: ReportCategory;\n /** Short label shown on the form, e.g. \"OSHA 1904 · ISO 45001\". */\n label: string;\n /** One sentence explaining what the standard governs here. */\n description: string;\n}\n\nexport const REPORT_STANDARD_REFERENCES: Record<\n ReportCategory,\n ReportStandardReference\n> = {\n safety: {\n category: \"safety\",\n label: \"OSHA 29 CFR 1904 · ISO 45001\",\n description:\n \"Injury classification follows OSHA's recordkeeping taxonomy (first aid only / recordable / days away, restricted, or transferred / fatality), within an ISO 45001 occupational health & safety management system.\",\n },\n environmental: {\n category: \"environmental\",\n label: \"ISO 14001\",\n description:\n \"Environmental aspect and regulatory-reportable classification follow ISO 14001's environmental management system requirements.\",\n },\n \"stop-work\": {\n category: \"stop-work\",\n label: \"ISO 45001\",\n description:\n \"Stop Work authority is a hazard-control mechanism exercised under an ISO 45001 occupational health & safety management system.\",\n },\n};\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\n/** Create a default ReportingToolbarConfig with sensible defaults. */\nexport function createReportingConfig(\n moduleName: string,\n handlers: {\n onReportSubmit: (\n report: IncidentReport\n ) => Promise<void | ReportOutcome> | void | ReportOutcome;\n onFeedbackSubmit: (feedback: FeedbackReport) => Promise<void> | void;\n }\n): ReportingToolbarConfig {\n return {\n moduleName,\n showIncidentReporting: true,\n showFeedback: true,\n reportCategories: [\"safety\", \"environmental\", \"stop-work\"],\n feedbackCategories: [\"problem\", \"suggestion\"],\n ...handlers,\n };\n}\n\n/**\n * Read a picked File into `{ dataBase64, mime }` (no data: prefix), rejecting\n * non-images and anything over `maxBytes` decoded. Pure-ish (needs FileReader);\n * exported so the size rule can be unit-tested and reused.\n */\nexport const FEEDBACK_SCREENSHOT_MAX_BYTES = 300 * 1024;\n\nexport function isAcceptableScreenshot(\n file: { type: string; size: number },\n maxBytes: number = FEEDBACK_SCREENSHOT_MAX_BYTES\n): { ok: true } | { ok: false; reason: string } {\n if (!file.type.startsWith(\"image/\")) {\n return { ok: false, reason: \"Please choose an image file.\" };\n }\n if (file.size > maxBytes) {\n return {\n ok: false,\n reason: `Image is too large (max ${Math.round(maxBytes / 1024)} KB).`,\n };\n }\n return { ok: true };\n}\n\nexport function readFileAsScreenshot(file: File): Promise<ReportScreenshot> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onerror = () => reject(new Error(\"Could not read the file.\"));\n reader.onload = () => {\n const result = String(reader.result || \"\");\n const comma = result.indexOf(\",\");\n const dataBase64 = comma >= 0 ? result.slice(comma + 1) : result;\n resolve({ dataBase64, mime: file.type || \"image/png\" });\n };\n reader.readAsDataURL(file);\n });\n}\n\n// ─── ReportingToolbar component ─────────────────────────────────────────────\n\nexport {\n ReportingToolbar,\n type ReportingToolbarProps,\n} from \"./ReportingToolbar\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,SAAS,QAAQ,gBAAgB;AA+BjC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,aAAa;AAuZV,SAYE,UAPA,KALF;AApXZ,IAAM,qBAAqC;AAAA,EACzC;AAAA,IACE,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,IACb,MAAM;AAAA,IACN,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,IACb,MAAM;AAAA,IACN,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,IACb,MAAM;AAAA,IACN,OAAO;AAAA,EACT;AACF;AAUA,IAAM,uBAAyC;AAAA,EAC7C;AAAA,IACE,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,IACb,MAAM;AAAA,IACN,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,IACb,MAAM;AAAA,IACN,OAAO;AAAA,EACT;AACF;AA4CA,IAAM,cAA2B;AAAA,EAC/B,aAAa;AAAA,EACb,UAAU;AAAA,EACV,UAAU;AAAA,EACV,iBAAiB;AACnB;AAQO,SAAS,mBAAmB,SAA+B;AAChE,MAAI,CAAC,WAAW,CAAC,QAAQ,cAAc;AACrC,UAAM,QAAQ,uCAAuC;AACrD;AAAA,EACF;AACA,MAAI,QAAQ,iBAAiB,WAAW;AACtC,UAAM;AAAA,MACJ,QAAQ,aACJ,yBAAyB,QAAQ,UAAU,yBAC3C;AAAA,IACN;AACA;AAAA,EACF;AACA,QAAM,QAAQ,gBAAgB;AAC9B,QAAM;AAAA,IACJ,QAAQ,iBACN;AAAA,EACJ;AACF;AAEO,SAAS,iBAAiB;AAAA,EAC/B,aAAa;AAAA,EACb,wBAAwB;AAAA,EACxB,eAAe;AAAA,EACf,mBAAmB,CAAC,UAAU,iBAAiB,WAAW;AAAA,EAC1D,qBAAqB,CAAC,WAAW,YAAY;AAAA,EAC7C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA,mBAAmB;AAAA,EACnB,oBAAoB;AAAA,EACpB;AACF,GAA0B;AACxB,QAAM,gBAAgB,mBAAmB;AAAA,IAAO,SAC9C,iBAAiB,SAAS,IAAI,QAAQ;AAAA,EACxC;AACA,QAAM,kBAAkB,qBAAqB;AAAA,IAAO,SAClD,mBAAmB,SAAS,IAAI,QAAQ;AAAA,EAC1C;AAEA,QAAM,mBAAmB,sBAAsB,SAAS,CAAC,GAAG,QAAQ;AAGpE,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,KAAK;AACtD,QAAM,CAAC,UAAU,WAAW,IAAI,SAA2B,SAAS;AACpE,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,gBAAgB;AAC/D,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAClD,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,EAAE;AACzC,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,EAAE;AACzC,QAAM,CAAC,YAAY,aAAa,IAAI,SAAkC,IAAI;AAC1E,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,SAAwB,IAAI;AACxE,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAClD,QAAM,eAAe,OAAgC,IAAI;AAEzD,QAAM,eAAe,CAAC,MAAwB;AAC5C,gBAAY,CAAC;AACb,mBAAe,gBAAgB;AAC/B,kBAAc,KAAK;AACnB,eAAW,EAAE;AACb,eAAW,EAAE;AACb,kBAAc,IAAI;AAClB,sBAAkB,IAAI;AACtB,oBAAgB,IAAI;AAAA,EACtB;AAGA,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAClD,QAAM,CAAC,gBAAgB,iBAAiB,IACtC,SAAyB,QAAQ;AACnC,QAAM,CAAC,aAAa,cAAc,IAAI,SAAsB,WAAW;AACvE,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,SAAS,KAAK;AAG9D,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,SAAS,KAAK;AAC1D,QAAM,CAAC,sBAAsB,uBAAuB,IAAI,SAEtD,MAAS;AACX,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,SAAS,EAAE;AAC3D,QAAM,CAAC,6BAA6B,8BAA8B,IAChE,SAAS,EAAE;AACb,QAAM,CAAC,oBAAoB,qBAAqB,IAAI,SAAS,KAAK;AAClE,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,EAAE;AACjD,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,EAAE;AAC3C,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAS,EAAE;AAGrD,QAAM,CAAC,qBAAqB,sBAAsB,IAAI,SAEpD,MAAS;AACX,QAAM,CAAC,sBAAsB,uBAAuB,IAAI,SAAS,KAAK;AACtE,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,EAAE;AAC7C,QAAM,CAAC,mBAAmB,oBAAoB,IAAI,SAAS,EAAE;AAC7D,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAChD,QAAM,CAAC,oBAAoB,qBAAqB,IAAI,SAAS,KAAK;AAGlE,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,SAE1C,MAAS;AACX,QAAM,CAAC,mBAAmB,oBAAoB,IAAI,SAAS,EAAE;AAC7D,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,KAAK;AACtD,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,KAAK;AAEpD,QAAM,aAAa,CAAC,MAAsB;AACxC,sBAAkB,CAAC;AACnB,mBAAe;AAAA,MACb,GAAG;AAAA,MACH,UAAU,MAAM,cAAc,aAAa;AAAA,IAC7C,CAAC;AACD,sBAAkB,KAAK;AACvB,4BAAwB,MAAS;AACjC,wBAAoB,EAAE;AACtB,mCAA+B,EAAE;AACjC,0BAAsB,KAAK;AAC3B,mBAAe,EAAE;AACjB,gBAAY,EAAE;AACd,qBAAiB,EAAE;AACnB,2BAAuB,MAAS;AAChC,4BAAwB,KAAK;AAC7B,iBAAa,EAAE;AACf,yBAAqB,EAAE;AACvB,iBAAa,KAAK;AAClB,0BAAsB,KAAK;AAC3B,sBAAkB,MAAS;AAC3B,yBAAqB,EAAE;AACvB,oBAAgB,KAAK;AACrB,mBAAe,KAAK;AACpB,kBAAc,IAAI;AAAA,EACpB;AAEA,QAAM,eAAe,YAAY;AAC/B,QAAI,CAAC,YAAY,YAAY,KAAK,GAAG;AACnC,YAAM,MAAM,gCAAgC;AAC5C;AAAA,IACF;AACA,wBAAoB,IAAI;AACxB,QAAI;AACF,YAAM,OAAO;AAAA,QACX,aAAa,YAAY,YAAY,KAAK;AAAA,QAC1C,UAAU,YAAY,SAAS,KAAK,KAAK;AAAA,QACzC,UAAU,YAAY;AAAA,QACtB,iBAAiB,YAAY,gBAAgB,KAAK,KAAK;AAAA,QACvD;AAAA,QACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,QAClC,aAAa;AAAA,QACb;AAAA,MACF;AAEA,UAAI;AACJ,UAAI,mBAAmB,UAAU;AAC/B,cAAM,SAA+B;AAAA,UACnC,GAAG;AAAA,UACH,UAAU;AAAA,UACV;AAAA,UACA;AAAA,UACA,kBAAkB,mBACd,OAAO,gBAAgB,IACvB;AAAA,UACJ,6BAA6B,8BACzB,OAAO,2BAA2B,IAClC;AAAA,UACJ;AAAA,UACA,aAAa,YAAY,KAAK,KAAK;AAAA,UACnC,UAAU,SAAS,KAAK,KAAK;AAAA,UAC7B,eAAe,cAAc,KAAK,KAAK;AAAA,QACzC;AACA,iBAAS;AAAA,MACX,WAAW,mBAAmB,iBAAiB;AAC7C,cAAM,gBAA6C;AAAA,UACjD,GAAG;AAAA,UACH,UAAU;AAAA,UACV;AAAA,UACA;AAAA,UACA,WAAW,UAAU,KAAK,KAAK;AAAA,UAC/B,mBAAmB,kBAAkB,KAAK,KAAK;AAAA,UAC/C;AAAA,UACA;AAAA,QACF;AACA,iBAAS;AAAA,MACX,OAAO;AACL,cAAM,WAAmC;AAAA,UACvC,GAAG;AAAA,UACH,UAAU;AAAA,UACV;AAAA,UACA,mBAAmB,kBAAkB,KAAK,KAAK;AAAA,UAC/C;AAAA,UACA;AAAA,QACF;AACA,iBAAS;AAAA,MACX;AAEA,YAAM,UAAU,MAAM,eAAe,MAAM;AAC3C,yBAAmB,OAAO;AAC1B,oBAAc,KAAK;AAAA,IACrB,SAAS,KAAK;AACZ,cAAQ,MAAM,4CAA4C,GAAG;AAC7D,YAAM,MAAM,yCAAyC;AAAA,IACvD,UAAE;AACA,0BAAoB,KAAK;AAAA,IAC3B;AAAA,EACF;AAEA,QAAM,iBAAiB,OAAO,MAA2C;AACvE,UAAM,OAAO,EAAE,OAAO,QAAQ,CAAC;AAC/B,MAAE,OAAO,QAAQ;AACjB,QAAI,CAAC,KAAM;AACX,UAAM,QAAQ,uBAAuB,IAAI;AACzC,QAAI,CAAC,MAAM,IAAI;AACb,YAAM,MAAM,MAAM,MAAM;AACxB;AAAA,IACF;AACA,QAAI;AACF,YAAM,OAAO,MAAM,qBAAqB,IAAI;AAC5C,oBAAc,IAAI;AAClB,wBAAkB,KAAK,IAAI;AAAA,IAC7B,SAAS,KAAK;AACZ,cAAQ,MAAM,8CAA8C,GAAG;AAC/D,YAAM,MAAM,4BAA4B;AAAA,IAC1C;AAAA,EACF;AAEA,QAAM,iBAAiB,YAAY;AACjC,QAAI,CAAC,QAAQ,KAAK,GAAG;AACnB,YAAM,MAAM,sCAAsC;AAClD;AAAA,IACF;AACA,kBAAc,IAAI;AAClB,QAAI;AACF,YAAM,WAA2B;AAAA,QAC/B;AAAA,QACA;AAAA,QACA,SAAS,QAAQ,KAAK,KAAK;AAAA,QAC3B,SAAS,QAAQ,KAAK;AAAA,QACtB;AAAA,QACA;AAAA,QACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,QAClC,aACE,OAAO,WAAW,cAAc,OAAO,SAAS,WAAW;AAAA,QAC7D,aAAa;AAAA,QACb,YAAY,cAAc;AAAA,MAC5B;AACA,YAAM,iBAAiB,QAAQ;AAC/B,YAAM;AAAA,QACJ,aAAa,YACT,oEACA;AAAA,MACN;AACA,sBAAgB,KAAK;AAAA,IACvB,SAAS,KAAK;AACZ,cAAQ,MAAM,8CAA8C,GAAG;AAC/D,YAAM,MAAM,yCAAyC;AAAA,IACvD,UAAE;AACA,oBAAc,KAAK;AAAA,IACrB;AAAA,EACF;AAEA,QAAM,kBAAkB,yBAAyB,cAAc,SAAS;AACxE,QAAM,oBAAoB,gBAAgB,gBAAgB,SAAS;AACnE,MAAI,CAAC,mBAAmB,CAAC,kBAAmB,QAAO;AAEnD,SACE,iCACE;AAAA,wBAAC,SAAI,WAAW,GAAG,2BAA2B,SAAS,GACrD,+BAAC,gBACC;AAAA,0BAAC,uBAAoB,SAAO,MAC1B;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR,MAAK;AAAA,UACL,WAAU;AAAA,UAEV;AAAA,gCAAC,WAAQ,WAAU,eAAc;AAAA,YACjC,oBAAC,UAAK,WAAU,gCAA+B,0BAAY;AAAA,YAC3D,oBAAC,eAAY,WAAU,WAAU;AAAA;AAAA;AAAA,MACnC,GACF;AAAA,MACA,qBAAC,uBAAoB,OAAM,OAAM,WAAU,QACxC;AAAA,2BACC,iCACE;AAAA,+BAAC,qBAAkB,WAAU,qFAAoF;AAAA;AAAA,YAE9G,qBACC;AAAA,cAAC;AAAA;AAAA,gBACC,SAAQ;AAAA,gBACR,WAAU;AAAA,gBACX;AAAA;AAAA,YAED;AAAA,aAEJ;AAAA,UACC,cAAc,IAAI,SACjB;AAAA,YAAC;AAAA;AAAA,cAEC,UAAU;AAAA,cACV,SAAS,MAAM;AACb,oBAAI,CAAC,kBAAmB,YAAW,IAAI,QAAQ;AAAA,cACjD;AAAA,cACA,WAAU;AAAA,cAEV;AAAA;AAAA,kBAAC,IAAI;AAAA,kBAAJ;AAAA,oBACC,WAAU;AAAA,oBACV,OAAO,EAAE,OAAO,IAAI,MAAM;AAAA;AAAA,gBAC5B;AAAA,gBACA,qBAAC,SACC;AAAA,sCAAC,OAAE,WAAU,uBAAuB,cAAI,OAAM;AAAA,kBAC9C,oBAAC,OAAE,WAAU,qCACV,qCAA2B,IAAI,QAAQ,EAAE,OAC5C;AAAA,mBACF;AAAA;AAAA;AAAA,YAhBK,IAAI;AAAA,UAiBX,CACD;AAAA,WACH;AAAA,QAGD,mBAAmB,qBAAqB,oBAAC,yBAAsB;AAAA,QAE/D,qBACC,iCACE;AAAA,8BAAC,qBAAkB,WAAU,6DAA4D,sBAEzF;AAAA,UACC,gBAAgB,IAAI,SACnB;AAAA,YAAC;AAAA;AAAA,cAEC,SAAS,MAAM,aAAa,IAAI,QAAQ;AAAA,cACxC,WAAU;AAAA,cAEV;AAAA;AAAA,kBAAC,IAAI;AAAA,kBAAJ;AAAA,oBACC,WAAU;AAAA,oBACV,OAAO,EAAE,OAAO,IAAI,MAAM;AAAA;AAAA,gBAC5B;AAAA,gBACA,qBAAC,SACC;AAAA,sCAAC,OAAE,WAAU,uBAAuB,cAAI,OAAM;AAAA,kBAC9C,oBAAC,OAAE,WAAU,qCACV,cAAI,aACP;AAAA,mBACF;AAAA;AAAA;AAAA,YAbK,IAAI;AAAA,UAcX,CACD;AAAA,WACH;AAAA,SAEJ;AAAA,OACF,GACF;AAAA,IAGA,oBAAC,UAAO,MAAM,cAAc,cAAc,iBACxC,+BAAC,iBAAc,WAAU,oBACvB;AAAA,2BAAC,gBACC;AAAA,6BAAC,eAAY,WAAU,2BACpB;AAAA,uBAAa,YACZ,oBAAC,OAAI,WAAU,0BAAyB,IAExC,oBAAC,aAAU,WAAU,0BAAyB;AAAA,UAE/C,aAAa,YACV,qBACA;AAAA,WACN;AAAA,QACA,oBAAC,qBACE,uBAAa,YACV,wDACA,6EACN;AAAA,SACF;AAAA,MAEA,qBAAC,SAAI,WAAU,kBACb;AAAA,6BAAC,SAAI,WAAU,aACb;AAAA,8BAAC,SAAM,WAAU,uBAAsB,4BAAc;AAAA,UACrD,qBAAC,UAAO,OAAO,aAAa,eAAe,gBACzC;AAAA,gCAAC,iBAAc,WAAU,OACvB,8BAAC,eAAY,GACf;AAAA,YACA,oBAAC,iBACE,mBAAS,IAAI,OACZ,oBAAC,cAAwB,OAAO,EAAE,MAC/B,YAAE,SADY,EAAE,IAEnB,CACD,GACH;AAAA,aACF;AAAA,WACF;AAAA,QAEC,aAAa,aACZ,qBAAC,SAAI,WAAU,kFACb;AAAA,+BAAC,SACC;AAAA,gCAAC,OAAE,WAAU,uBAAsB,mCAAqB;AAAA,YACxD,oBAAC,OAAE,WAAU,qCAAoC,2DAEjD;AAAA,aACF;AAAA,UACA,oBAAC,UAAO,SAAS,YAAY,iBAAiB,eAAe;AAAA,WAC/D;AAAA,QAGF,qBAAC,SAAI,WAAU,aACb;AAAA,8BAAC,SAAM,WAAU,uBAAsB,qBAAO;AAAA,UAC9C;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,UAAU,OAAK,WAAW,EAAE,OAAO,KAAK;AAAA,cACxC,aACE,aAAa,YACT,+BACA;AAAA,cAEN,WAAU;AAAA;AAAA,UACZ;AAAA,WACF;AAAA,QAEA,qBAAC,SAAI,WAAU,aACb;AAAA,8BAAC,SAAM,WAAU,uBAAsB,uBAAS;AAAA,UAChD;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,UAAU,OAAK,WAAW,EAAE,OAAO,KAAK;AAAA,cACxC,aACE,aAAa,YACT,uDACA;AAAA,cAEN,MAAM;AAAA;AAAA,UACR;AAAA,WACF;AAAA,QAEC,oBACC,qBAAC,SAAI,WAAU,aACb;AAAA,8BAAC,SAAM,WAAU,uBAAsB,mCAEvC;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,KAAK;AAAA,cACL,MAAK;AAAA,cACL,QAAO;AAAA,cACP,WAAU;AAAA,cACV,UAAU;AAAA;AAAA,UACZ;AAAA,UACC,aACC,qBAAC,SAAI,WAAU,wEACb;AAAA,gCAAC,aAAU,WAAU,0BAAyB;AAAA,YAC9C,oBAAC,UAAK,WAAU,0CACb,4BAAkB,cACrB;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAS,MAAM;AACb,gCAAc,IAAI;AAClB,oCAAkB,IAAI;AAAA,gBACxB;AAAA,gBACA,WAAU;AAAA,gBACV,cAAW;AAAA,gBAEX,8BAAC,KAAE,WAAU,eAAc;AAAA;AAAA,YAC7B;AAAA,aACF,IAEA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAQ;AAAA,cACR,MAAK;AAAA,cACL,WAAU;AAAA,cACV,SAAS,MAAM,aAAa,SAAS,MAAM;AAAA,cAE3C;AAAA,oCAAC,aAAU,WAAU,eAAc;AAAA,gBAAE;AAAA;AAAA;AAAA,UAEvC;AAAA,WAEJ;AAAA,QAGF,qBAAC,OAAE,WAAU,qCACV;AAAA;AAAA,UAAW;AAAA,WAEd;AAAA,SACF;AAAA,MAEA,qBAAC,gBACC;AAAA,4BAAC,UAAO,SAAQ,WAAU,SAAS,MAAM,gBAAgB,KAAK,GAAG,oBAEjE;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS;AAAA,YACT,UAAU,cAAc,CAAC,QAAQ,KAAK;AAAA,YACtC,WAAU;AAAA,YACV,OAAO;AAAA,cACL,iBAAiB,aAAa,YAAY,YAAY;AAAA,cACtD,OAAO;AAAA,YACT;AAAA,YAEA;AAAA,kCAAC,QAAK,WAAU,eAAc;AAAA,cAC7B,aAAa,qBAAgB;AAAA;AAAA;AAAA,QAChC;AAAA,SACF;AAAA,OACF,GACF;AAAA,IAGA,oBAAC,UAAO,MAAM,YAAY,cAAc,eACtC,+BAAC,iBAAc,WAAU,iDACvB;AAAA,2BAAC,gBACC;AAAA,6BAAC,eAAY,WAAU,2BACpB;AAAA,6BAAmB,YAClB,oBAAC,eAAY,WAAU,0BAAyB;AAAA,UAEjD,mBAAmB,mBAClB,oBAAC,QAAK,WAAU,0BAAyB;AAAA,UAE1C,mBAAmB,eAClB,oBAAC,YAAS,WAAU,0BAAyB;AAAA,UAE9C,mBAAmB,KAAK,OAAK,EAAE,aAAa,cAAc,GACvD,SAAS;AAAA,WACf;AAAA,QACA,oBAAC,qBAEG,6BAAmB,KAAK,OAAK,EAAE,aAAa,cAAc,GACtD,aAER;AAAA,SACF;AAAA,MAEA,qBAAC,SAAI,WAAU,kBACb;AAAA,6BAAC,SAAM,WAAU,sCACf;AAAA,8BAAC,cAAW,WAAU,0BAAyB;AAAA,UAC/C,oBAAC,cAAW,WAAU,wCACnB,qCAA2B,cAAc,EAAE,OAC9C;AAAA,UACA,oBAAC,oBAAiB,WAAU,eACzB,qCAA2B,cAAc,EAAE,aAC9C;AAAA,WACF;AAAA,QAEA,qBAAC,SAAI,WAAU,aACb;AAAA,8BAAC,SAAM,WAAU,uBAAsB,sBAAQ;AAAA,UAC/C;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,YAAY;AAAA,cACnB,eAAe,CAAC,MACd,eAAe,QAAM,EAAE,GAAG,GAAG,UAAU,EAAE,EAAE;AAAA,cAG7C;AAAA,oCAAC,iBAAc,WAAU,OACvB,8BAAC,eAAY,GACf;AAAA,gBACA,qBAAC,iBACC;AAAA,sCAAC,cAAW,OAAM,OAAM,iBAAG;AAAA,kBAC3B,oBAAC,cAAW,OAAM,UAAS,oBAAM;AAAA,kBACjC,oBAAC,cAAW,OAAM,QAAO,kBAAI;AAAA,kBAC7B,oBAAC,cAAW,OAAM,YAAW,sBAAQ;AAAA,mBACvC;AAAA;AAAA;AAAA,UACF;AAAA,WACF;AAAA,QAEA,qBAAC,SAAI,WAAU,aACb;AAAA,8BAAC,SAAM,WAAU,uBAAsB,iCAAmB;AAAA,UAC1D;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,YAAY;AAAA,cACnB,UAAU,OACR,eAAe,QAAM,EAAE,GAAG,GAAG,UAAU,EAAE,OAAO,MAAM,EAAE;AAAA,cAE1D,aAAY;AAAA,cACZ,WAAU;AAAA;AAAA,UACZ;AAAA,WACF;AAAA,QAEA,qBAAC,SAAI,WAAU,aACb;AAAA,8BAAC,SAAM,WAAU,uBAAsB,8BAAgB;AAAA,UACvD;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,YAAY;AAAA,cACnB,UAAU,OACR,eAAe,QAAM;AAAA,gBACnB,GAAG;AAAA,gBACH,aAAa,EAAE,OAAO;AAAA,cACxB,EAAE;AAAA,cAEJ,aAAY;AAAA,cACZ,MAAM;AAAA;AAAA,UACR;AAAA,WACF;AAAA,QAEC,mBAAmB,YAClB,iCACE;AAAA,+BAAC,SAAI,WAAU,kFACb;AAAA,gCAAC,OAAE,WAAU,uBAAsB,iCAAmB;AAAA,YACtD;AAAA,cAAC;AAAA;AAAA,gBACC,SAAS;AAAA,gBACT,iBAAiB;AAAA;AAAA,YACnB;AAAA,aACF;AAAA,UAEC,kBACC,iCACE;AAAA,iCAAC,SAAI,WAAU,aACb;AAAA,kCAAC,SAAM,WAAU,uBAAsB,+CAEvC;AAAA,cACA;AAAA,gBAAC;AAAA;AAAA,kBACC,OAAO;AAAA,kBACP,eAAe,CAAC,MACd,wBAAwB,CAAC;AAAA,kBAG3B;AAAA,wCAAC,iBAAc,WAAU,OACvB,8BAAC,eAAY,aAAY,2BAA0B,GACrD;AAAA,oBACA,qBAAC,iBACC;AAAA,0CAAC,cAAW,OAAM,kBAAiB,4BAEnC;AAAA,sBACA,oBAAC,cAAW,OAAM,cAAa,wBAAU;AAAA,sBACzC,oBAAC,cAAW,OAAM,iCAAgC,0DAElD;AAAA,sBACA,oBAAC,cAAW,OAAM,YAAW,sBAAQ;AAAA,uBACvC;AAAA;AAAA;AAAA,cACF;AAAA,eACF;AAAA,YAEC,yBACC,mCACA,qBAAC,SAAI,WAAU,0BACb;AAAA,mCAAC,SAAI,WAAU,aACb;AAAA,oCAAC,SAAM,WAAU,uBAAsB,iCAEvC;AAAA,gBACA;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,KAAK;AAAA,oBACL,OAAO;AAAA,oBACP,UAAU,OAAK,oBAAoB,EAAE,OAAO,KAAK;AAAA,oBACjD,WAAU;AAAA;AAAA,gBACZ;AAAA,iBACF;AAAA,cACA,qBAAC,SAAI,WAAU,aACb;AAAA,oCAAC,SAAM,WAAU,uBAAsB,yCAEvC;AAAA,gBACA;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,KAAK;AAAA,oBACL,OAAO;AAAA,oBACP,UAAU,OACR,+BAA+B,EAAE,OAAO,KAAK;AAAA,oBAE/C,WAAU;AAAA;AAAA,gBACZ;AAAA,iBACF;AAAA,eACF;AAAA,YAGF,qBAAC,SAAI,WAAU,2BACb;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,IAAG;AAAA,kBACH,SAAS;AAAA,kBACT,iBAAiB,OAAK,sBAAsB,CAAC,CAAC,CAAC;AAAA;AAAA,cACjD;AAAA,cACA;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAQ;AAAA,kBACR,WAAU;AAAA,kBACX;AAAA;AAAA,cAED;AAAA,eACF;AAAA,YAEA,qBAAC,SAAI,WAAU,0BACb;AAAA,mCAAC,SAAI,WAAU,aACb;AAAA,oCAAC,SAAM,WAAU,uBAAsB,gCAEvC;AAAA,gBACA;AAAA,kBAAC;AAAA;AAAA,oBACC,OAAO;AAAA,oBACP,UAAU,OAAK,YAAY,EAAE,OAAO,KAAK;AAAA,oBACzC,WAAU;AAAA;AAAA,gBACZ;AAAA,iBACF;AAAA,cACA,qBAAC,SAAI,WAAU,aACb;AAAA,oCAAC,SAAM,WAAU,uBAAsB,6BAEvC;AAAA,gBACA;AAAA,kBAAC;AAAA;AAAA,oBACC,OAAO;AAAA,oBACP,UAAU,OAAK,iBAAiB,EAAE,OAAO,KAAK;AAAA,oBAC9C,WAAU;AAAA;AAAA,gBACZ;AAAA,iBACF;AAAA,eACF;AAAA,YAEA,qBAAC,SAAI,WAAU,aACb;AAAA,kCAAC,SAAM,WAAU,uBAAsB,qCAEvC;AAAA,cACA;AAAA,gBAAC;AAAA;AAAA,kBACC,OAAO;AAAA,kBACP,UAAU,OAAK,eAAe,EAAE,OAAO,KAAK;AAAA,kBAC5C,WAAU;AAAA;AAAA,cACZ;AAAA,eACF;AAAA,aACF;AAAA,WAEJ;AAAA,QAGD,mBAAmB,mBAClB,iCACE;AAAA,+BAAC,SAAI,WAAU,aACb;AAAA,gCAAC,SAAM,WAAU,uBAAsB,8CAEvC;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,OAAO;AAAA,gBACP,eAAe,CAAC,MACd,uBAAuB,CAAC;AAAA,gBAG1B;AAAA,sCAAC,iBAAc,WAAU,OACvB,8BAAC,eAAY,aAAY,gDAA+C,GAC1E;AAAA,kBACA,qBAAC,iBACC;AAAA,wCAAC,cAAW,OAAM,gBAAe,0BAAY;AAAA,oBAC7C,oBAAC,cAAW,OAAM,mBAAkB,6BAEpC;AAAA,oBACA,oBAAC,cAAW,OAAM,sBAAqB,gCAEvC;AAAA,oBACA,oBAAC,cAAW,OAAM,SAAQ,mBAAK;AAAA,oBAC/B,oBAAC,cAAW,OAAM,SAAQ,mBAAK;AAAA,oBAC/B,oBAAC,cAAW,OAAM,SAAQ,mBAAK;AAAA,oBAC/B,oBAAC,cAAW,OAAM,SAAQ,mBAAK;AAAA,qBACjC;AAAA;AAAA;AAAA,YACF;AAAA,aACF;AAAA,UAEA,qBAAC,SAAI,WAAU,0BACb;AAAA,iCAAC,SAAI,WAAU,aACb;AAAA,kCAAC,SAAM,WAAU,uBAAsB,kCAEvC;AAAA,cACA;AAAA,gBAAC;AAAA;AAAA,kBACC,OAAO;AAAA,kBACP,UAAU,OAAK,aAAa,EAAE,OAAO,KAAK;AAAA,kBAC1C,WAAU;AAAA;AAAA,cACZ;AAAA,eACF;AAAA,YACA,qBAAC,SAAI,WAAU,aACb;AAAA,kCAAC,SAAM,WAAU,uBAAsB,gCAEvC;AAAA,cACA;AAAA,gBAAC;AAAA;AAAA,kBACC,OAAO;AAAA,kBACP,UAAU,OAAK,qBAAqB,EAAE,OAAO,KAAK;AAAA,kBAClD,WAAU;AAAA;AAAA,cACZ;AAAA,eACF;AAAA,aACF;AAAA,UAEA,qBAAC,SAAI,WAAU,2BACb;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,IAAG;AAAA,gBACH,SAAS;AAAA,gBACT,iBAAiB,OAAK,aAAa,CAAC,CAAC,CAAC;AAAA;AAAA,YACxC;AAAA,YACA,oBAAC,SAAM,SAAQ,aAAY,WAAU,uBAAsB,+BAE3D;AAAA,aACF;AAAA,UAEA,qBAAC,SAAI,WAAU,2BACb;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,IAAG;AAAA,gBACH,SAAS;AAAA,gBACT,iBAAiB,OAAK,sBAAsB,CAAC,CAAC,CAAC;AAAA;AAAA,YACjD;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,SAAQ;AAAA,gBACR,WAAU;AAAA,gBACX;AAAA;AAAA,YAED;AAAA,aACF;AAAA,UAEA,qBAAC,SAAI,WAAU,2BACb;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,IAAG;AAAA,gBACH,SAAS;AAAA,gBACT,iBAAiB,OAAK,wBAAwB,CAAC,CAAC,CAAC;AAAA;AAAA,YACnD;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,SAAQ;AAAA,gBACR,WAAU;AAAA,gBACX;AAAA;AAAA,YAED;AAAA,aACF;AAAA,WACF;AAAA,QAGD,mBAAmB,eAClB,iCACE;AAAA,+BAAC,SAAI,WAAU,aACb;AAAA,gCAAC,SAAM,WAAU,uBAAsB,yCAEvC;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,OAAO;AAAA,gBACP,eAAe,CAAC,MAAsB,kBAAkB,CAAC;AAAA,gBAEzD;AAAA,sCAAC,iBAAc,WAAU,OACvB,8BAAC,eAAY,aAAY,wBAAuB,GAClD;AAAA,kBACA,qBAAC,iBACC;AAAA,wCAAC,cAAW,OAAM,cAAa,wBAAU;AAAA,oBACzC,oBAAC,cAAW,OAAM,cAAa,wBAAU;AAAA,oBACzC,oBAAC,cAAW,OAAM,YAAW,sBAAQ;AAAA,oBACrC,oBAAC,cAAW,OAAM,qBAAoB,+BAEtC;AAAA,oBACA,oBAAC,cAAW,OAAM,kBAAiB,4BAEnC;AAAA,oBACA,oBAAC,cAAW,OAAM,mBAAkB,+BAEpC;AAAA,oBACA,oBAAC,cAAW,OAAM,SAAQ,mBAAK;AAAA,qBACjC;AAAA;AAAA;AAAA,YACF;AAAA,aACF;AAAA,UAEA,qBAAC,SAAI,WAAU,aACb;AAAA,gCAAC,SAAM,WAAU,uBAAsB,gCAEvC;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,OAAO;AAAA,gBACP,UAAU,OAAK,qBAAqB,EAAE,OAAO,KAAK;AAAA,gBAClD,MAAM;AAAA;AAAA,YACR;AAAA,aACF;AAAA,UAEA,qBAAC,SAAI,WAAU,2BACb;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,IAAG;AAAA,gBACH,SAAS;AAAA,gBACT,iBAAiB,OAAK,gBAAgB,CAAC,CAAC,CAAC;AAAA;AAAA,YAC3C;AAAA,YACA,oBAAC,SAAM,SAAQ,gBAAe,WAAU,uBAAsB,qCAE9D;AAAA,aACF;AAAA,UAEA,qBAAC,SAAI,WAAU,2BACb;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,IAAG;AAAA,gBACH,SAAS;AAAA,gBACT,iBAAiB,OAAK,eAAe,CAAC,CAAC,CAAC;AAAA;AAAA,YAC1C;AAAA,YACA,oBAAC,SAAM,SAAQ,eAAc,WAAU,uBAAsB,8BAE7D;AAAA,aACF;AAAA,WACF;AAAA,QAGF,qBAAC,SAAI,WAAU,aACb;AAAA,8BAAC,SAAM,WAAU,uBAAsB,+CAEvC;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,YAAY;AAAA,cACnB,UAAU,OACR,eAAe,QAAM;AAAA,gBACnB,GAAG;AAAA,gBACH,iBAAiB,EAAE,OAAO;AAAA,cAC5B,EAAE;AAAA,cAEJ,MAAM;AAAA;AAAA,UACR;AAAA,WACF;AAAA,QAEA,qBAAC,OAAE,WAAU,qCACV;AAAA;AAAA,UAAW;AAAA,WAEd;AAAA,SACF;AAAA,MAEA,qBAAC,gBACC;AAAA,4BAAC,UAAO,SAAQ,WAAU,SAAS,MAAM,cAAc,KAAK,GAAG,oBAE/D;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS;AAAA,YACT,UAAU,oBAAoB,CAAC,YAAY,YAAY,KAAK;AAAA,YAC5D,WAAU;AAAA,YACV,OAAO;AAAA,cACL,iBAAiB,mBAAmB;AAAA,gBAClC,OAAK,EAAE,aAAa;AAAA,cACtB,GAAG;AAAA,cACH,OAAO;AAAA,YACT;AAAA,YAEA;AAAA,kCAAC,QAAK,WAAU,eAAc;AAAA,cAC7B,mBAAmB,qBAAgB;AAAA;AAAA;AAAA,QACtC;AAAA,SACF;AAAA,OACF,GACF;AAAA,KACF;AAEJ;;;AC95BO,IAAM,oBAAuC;AAAA,EAClD,EAAE,MAAM,SAAS,OAAO,QAAQ;AAAA,EAChC,EAAE,MAAM,eAAe,OAAO,cAAc;AAAA,EAC5C,EAAE,MAAM,kBAAkB,OAAO,iBAAiB;AAAA,EAClD,EAAE,MAAM,qBAAqB,OAAO,oBAAoB;AAAA,EACxD,EAAE,MAAM,UAAU,OAAO,SAAS;AACpC;AA6CO,IAAM,iBAAoC;AAAA,EAC/C;AAAA,IACE,UAAU;AAAA,IACV,OAAO;AAAA,IACP,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,OAAO;AAAA,IACP,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,OAAO;AAAA,IACP,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AACF;AAEO,IAAM,mBAAwC;AAAA,EACnD;AAAA,IACE,UAAU;AAAA,IACV,OAAO;AAAA,IACP,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,OAAO;AAAA,IACP,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AACF;AAmBO,IAAM,6BAGT;AAAA,EACF,QAAQ;AAAA,IACN,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aACE;AAAA,EACJ;AAAA,EACA,eAAe;AAAA,IACb,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aACE;AAAA,EACJ;AAAA,EACA,aAAa;AAAA,IACX,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aACE;AAAA,EACJ;AACF;AAKO,SAAS,sBACd,YACA,UAMwB;AACxB,SAAO;AAAA,IACL;AAAA,IACA,uBAAuB;AAAA,IACvB,cAAc;AAAA,IACd,kBAAkB,CAAC,UAAU,iBAAiB,WAAW;AAAA,IACzD,oBAAoB,CAAC,WAAW,YAAY;AAAA,IAC5C,GAAG;AAAA,EACL;AACF;AAOO,IAAM,gCAAgC,MAAM;AAE5C,SAAS,uBACd,MACA,WAAmB,+BAC2B;AAC9C,MAAI,CAAC,KAAK,KAAK,WAAW,QAAQ,GAAG;AACnC,WAAO,EAAE,IAAI,OAAO,QAAQ,+BAA+B;AAAA,EAC7D;AACA,MAAI,KAAK,OAAO,UAAU;AACxB,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,QAAQ,2BAA2B,KAAK,MAAM,WAAW,IAAI,CAAC;AAAA,IAChE;AAAA,EACF;AACA,SAAO,EAAE,IAAI,KAAK;AACpB;AAEO,SAAS,qBAAqB,MAAuC;AAC1E,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,SAAS,IAAI,WAAW;AAC9B,WAAO,UAAU,MAAM,OAAO,IAAI,MAAM,0BAA0B,CAAC;AACnE,WAAO,SAAS,MAAM;AACpB,YAAM,SAAS,OAAO,OAAO,UAAU,EAAE;AACzC,YAAM,QAAQ,OAAO,QAAQ,GAAG;AAChC,YAAM,aAAa,SAAS,IAAI,OAAO,MAAM,QAAQ,CAAC,IAAI;AAC1D,cAAQ,EAAE,YAAY,MAAM,KAAK,QAAQ,YAAY,CAAC;AAAA,IACxD;AACA,WAAO,cAAc,IAAI;AAAA,EAC3B,CAAC;AACH;","names":[]}
@@ -10,13 +10,13 @@ import {
10
10
  liveServices,
11
11
  normalizeServiceSlug,
12
12
  onboardingTourServices
13
- } from "../chunk-45ZQDLPA.js";
13
+ } from "../chunk-NRA42OCM.js";
14
14
  import {
15
15
  DEFAULT_PLATFORM_DOMAIN,
16
16
  getPlatformDomain,
17
17
  platformUrl,
18
18
  portalUrl
19
- } from "../chunk-7LI4Y6WT.js";
19
+ } from "../chunk-IK3B4YWQ.js";
20
20
  import "../chunk-DMUQ5WNO.js";
21
21
  import "../chunk-HLMIR5UV.js";
22
22
  export {
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  buildCrossServiceLinks
3
- } from "../chunk-45ZQDLPA.js";
3
+ } from "../chunk-NRA42OCM.js";
4
4
  import {
5
5
  CrossServiceNav,
6
6
  DashboardLayout,
7
7
  HealthFooter
8
- } from "../chunk-ZVAS2BMF.js";
9
- import "../chunk-BG4DVZAY.js";
10
- import "../chunk-7LI4Y6WT.js";
8
+ } from "../chunk-32NHRLWI.js";
9
+ import "../chunk-L54JNSVS.js";
10
+ import "../chunk-IK3B4YWQ.js";
11
11
  import "../chunk-5YS2PQN3.js";
12
12
  import "../chunk-DMUQ5WNO.js";
13
13
  import "../chunk-A6UTXAPS.js";
@@ -27,8 +27,8 @@ import {
27
27
  useTourProgress,
28
28
  useTourState,
29
29
  validateTourDefinition
30
- } from "../chunk-BG4DVZAY.js";
31
- import "../chunk-7LI4Y6WT.js";
30
+ } from "../chunk-L54JNSVS.js";
31
+ import "../chunk-IK3B4YWQ.js";
32
32
  import "../chunk-JT3XLKKD.js";
33
33
  export {
34
34
  InvalidTourDefinitionError,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pablo2410/shared-ui",
3
- "version": "0.28.2",
3
+ "version": "0.29.1",
4
4
  "description": "Shared UI component library for all Oplytics.digital subdomains",
5
5
  "type": "module",
6
6
  "exports": {
@@ -85,10 +85,10 @@
85
85
  },
86
86
  "devDependencies": {
87
87
  "@tailwindcss/cli": "^4.3.3",
88
- "@types/google.maps": "^3.66.5",
88
+ "@types/google.maps": "^3.66.6",
89
89
  "@types/react": "^19.3.0",
90
90
  "@types/react-dom": "^19.3.0",
91
- "prettier": "^3.9.9",
91
+ "prettier": "^3.9.10",
92
92
  "react": "^19.3.0",
93
93
  "react-dom": "^19.3.0",
94
94
  "tailwindcss": "^4.3.3",
@@ -129,11 +129,11 @@
129
129
  "cmdk": "^1.1.1",
130
130
  "embla-carousel-react": "^8.6.0",
131
131
  "input-otp": "^1.5.0",
132
- "lucide-react": "^1.54.0",
132
+ "lucide-react": "^1.55.0",
133
133
  "next-themes": "^0.4.6",
134
134
  "react-day-picker": "^10.0.2",
135
135
  "react-hook-form": "^7.89.0",
136
- "react-resizable-panels": "^4.14.3",
136
+ "react-resizable-panels": "^4.14.4",
137
137
  "recharts": "^3.10.1",
138
138
  "sonner": "^2.0.8",
139
139
  "tailwind-merge": "^3.7.0",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/services/platformDomain.ts"],"sourcesContent":["/**\n * Platform domain resolution — single source of truth for building\n * cross-service URLs (Service Hub cards, sidebar cross-nav, footers,\n * \"Back to Portal\" links).\n *\n * Every service runs on a sibling subdomain of one parent domain\n * (portal.oplyticsdigital.net, sqdcp.oplyticsdigital.net, …), so the parent\n * domain is derived at runtime from the current hostname: strip the service\n * label, keep the rest. Links therefore stay on whichever domain the app is\n * served from — no per-app build args, and a future domain move needs no\n * code change here.\n *\n * Outside a browser (SSR, tests) or on hosts with no parent domain\n * (raw IPs) the canonical production domain is used.\n *\n * The local MacBook sandbox (docker-compose.sandbox.yml) is a special case:\n * every app is served from bare `localhost` on a fixed port per service,\n * not from sibling subdomains — so there is no domain to derive from the\n * hostname the way there is for sandbox-*.oplyticsdigital.net (the tunnel\n * variant) or real subdomains. Without this map, any cross-service link\n * built from `localhost` silently fell back to production (see incident:\n * Business Hub's sidebar link sent an authenticated sandbox user to\n * businesshub.oplyticsdigital.net instead of localhost:3004). This list\n * must be kept in sync with the port assignments in each sandbox compose\n * file — there is currently no single generated source for both.\n */\n\nexport const DEFAULT_PLATFORM_DOMAIN = \"oplyticsdigital.net\";\n\nconst IPV4 = /^\\d{1,3}(\\.\\d{1,3}){3}$/;\n\n/**\n * Fixed local-sandbox port per service subdomain label. Keys match the\n * actual subdomain label / sandbox compose service name each app uses to\n * call platformUrl(...) (e.g. \"oeemanager\", not the hyphenated\n * \"/oee-manager\" Infisical secrets folder — those two naming schemes are\n * already inconsistent elsewhere in this codebase; don't propagate that\n * inconsistency here).\n */\nexport const SANDBOX_LOCALHOST_PORTS: Record<string, number> = {\n portal: 3000,\n sqdcp: 3001,\n oeemanager: 3002,\n actionmanager: 3003,\n businesshub: 3004,\n connect: 3005,\n \"policy-deployment\": 3006,\n \"marketing-site\": 3007,\n};\n\nexport function getPlatformDomain(): string {\n if (typeof window === \"undefined\") return DEFAULT_PLATFORM_DOMAIN;\n const host = window.location.hostname;\n if (!host || IPV4.test(host)) {\n return DEFAULT_PLATFORM_DOMAIN;\n }\n if (host === \"localhost\") {\n // Handled by platformUrl's port-map branch below — a bare domain has no\n // meaning on localhost, since every sandbox service shares the same\n // host and differs only by port. Falling back to the production domain\n // here would be wrong for any localhost caller, so this path should\n // only be reached by code that doesn't also need a port (rare); return\n // the production domain as the least-surprising default for that case.\n return DEFAULT_PLATFORM_DOMAIN;\n }\n const parts = host.split(\".\");\n // sqdcp.oplyticsdigital.net → oplyticsdigital.net\n if (parts.length >= 3) return parts.slice(1).join(\".\");\n // already on the apex (oplyticsdigital.net)\n if (parts.length === 2) return host;\n return DEFAULT_PLATFORM_DOMAIN;\n}\n\n/**\n * Absolute URL for a service subdomain label on the current platform\n * domain, e.g. `platformUrl(\"sqdcp\")` → `https://sqdcp.oplyticsdigital.net`.\n *\n * On the local sandbox (hostname === \"localhost\"), resolves instead to\n * `http://localhost:<port>` using SANDBOX_LOCALHOST_PORTS, so cross-service\n * links (e.g. Portal's sidebar → Business Hub) stay local instead of\n * silently jumping to production.\n */\nexport function platformUrl(subdomainLabel: string, path = \"\"): string {\n if (\n typeof window !== \"undefined\" &&\n window.location.hostname === \"localhost\"\n ) {\n const port = SANDBOX_LOCALHOST_PORTS[subdomainLabel];\n if (port !== undefined) {\n return `http://localhost:${port}${path}`;\n }\n // Unknown label on localhost — no safe local target exists, so fall\n // through to the production URL rather than guessing a port. Better to\n // land on a working page and be visibly wrong (prod) than a broken one.\n }\n return `https://${subdomainLabel}.${getPlatformDomain()}${path}`;\n}\n\n/** Portal URL builder — the portal always lives at the `portal` label. */\nexport function portalUrl(path = \"\"): string {\n return platformUrl(\"portal\", path);\n}\n"],"mappings":";AA2BO,IAAM,0BAA0B;AAEvC,IAAM,OAAO;AAUN,IAAM,0BAAkD;AAAA,EAC7D,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,aAAa;AAAA,EACb,SAAS;AAAA,EACT,qBAAqB;AAAA,EACrB,kBAAkB;AACpB;AAEO,SAAS,oBAA4B;AAC1C,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,QAAM,OAAO,OAAO,SAAS;AAC7B,MAAI,CAAC,QAAQ,KAAK,KAAK,IAAI,GAAG;AAC5B,WAAO;AAAA,EACT;AACA,MAAI,SAAS,aAAa;AAOxB,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,KAAK,MAAM,GAAG;AAE5B,MAAI,MAAM,UAAU,EAAG,QAAO,MAAM,MAAM,CAAC,EAAE,KAAK,GAAG;AAErD,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,SAAO;AACT;AAWO,SAAS,YAAY,gBAAwB,OAAO,IAAY;AACrE,MACE,OAAO,WAAW,eAClB,OAAO,SAAS,aAAa,aAC7B;AACA,UAAM,OAAO,wBAAwB,cAAc;AACnD,QAAI,SAAS,QAAW;AACtB,aAAO,oBAAoB,IAAI,GAAG,IAAI;AAAA,IACxC;AAAA,EAIF;AACA,SAAO,WAAW,cAAc,IAAI,kBAAkB,CAAC,GAAG,IAAI;AAChE;AAGO,SAAS,UAAU,OAAO,IAAY;AAC3C,SAAO,YAAY,UAAU,IAAI;AACnC;","names":[]}