@pablo2410/shared-ui 0.25.1 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{HealthFooter-BoOozbWG.d.ts → HealthFooter-Cepg3mRC.d.ts} +10 -1
- package/dist/{chunk-MGTOQY6O.js → chunk-45ZQDLPA.js} +3 -65
- package/dist/chunk-45ZQDLPA.js.map +1 -0
- package/dist/{chunk-UQUMDBCZ.js → chunk-YCTIC6XW.js} +2 -12
- package/dist/chunk-YCTIC6XW.js.map +1 -0
- package/dist/{crossNav-Bz0RXU_F.d.ts → crossNav-DBX4Eyx7.d.ts} +2 -2
- package/dist/layout/index.d.ts +3 -3
- package/dist/layout/index.js +1 -1
- package/dist/reporting/index.js +0 -1
- package/dist/reporting/index.js.map +1 -1
- package/dist/services/index.d.ts +3 -7
- package/dist/services/index.js +1 -5
- package/dist/shell/index.d.ts +3 -3
- package/dist/shell/index.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-MGTOQY6O.js.map +0 -1
- package/dist/chunk-UQUMDBCZ.js.map +0 -1
|
@@ -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: \"connect\", label: \"Connect\" },\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} 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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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;AA4XV,SAYE,UAPA,KALF;AA1VZ,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;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,eAAe,MAAM;AAC3B,YAAM,QAAQ,uCAAuC;AACrD,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;;;ACj5BO,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;AAwCO,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,UAIwB;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":[]}
|
package/dist/services/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as ADMIN_SEE_ALL_ROLES, B as BuildCrossServiceLinksOptions, C as CrossServiceLink, S as SLUG_ALIASES, b as buildCrossServiceLinks, n as normalizeServiceSlug } from '../crossNav-
|
|
1
|
+
export { A as ADMIN_SEE_ALL_ROLES, B as BuildCrossServiceLinksOptions, C as CrossServiceLink, S as SLUG_ALIASES, b as buildCrossServiceLinks, n as normalizeServiceSlug } from '../crossNav-DBX4Eyx7.js';
|
|
2
2
|
import '../orgHierarchy-Buky69YP.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -53,7 +53,6 @@ declare function portalUrl(path?: string): string;
|
|
|
53
53
|
* - Platform adds: route, external URL, React icon component
|
|
54
54
|
* - Marketing adds: heroImage, demoImage, problem, howItWorks, results, aiFeatures
|
|
55
55
|
*/
|
|
56
|
-
type ServiceStatus = "live" | "in-development" | "coming-soon";
|
|
57
56
|
type ServiceCategory = "core" | "hub";
|
|
58
57
|
interface ServiceDefinition {
|
|
59
58
|
/** Unique identifier used for DB lookups, URL slugs, and cross-referencing */
|
|
@@ -68,8 +67,6 @@ interface ServiceDefinition {
|
|
|
68
67
|
icon: string;
|
|
69
68
|
/** Hex accent color for cards, badges, and theming */
|
|
70
69
|
accentColor: string;
|
|
71
|
-
/** Current availability status */
|
|
72
|
-
status: ServiceStatus;
|
|
73
70
|
/** Core = standalone product, Hub = extension/add-on module */
|
|
74
71
|
category: ServiceCategory;
|
|
75
72
|
/**
|
|
@@ -121,12 +118,11 @@ interface ServiceOnboardingFeature {
|
|
|
121
118
|
declare const SERVICE_CATALOG: ServiceDefinition[];
|
|
122
119
|
declare const coreServices: ServiceDefinition[];
|
|
123
120
|
declare const hubServices: ServiceDefinition[];
|
|
121
|
+
/** Every service in the catalog is live: an entry is only added once it ships. */
|
|
124
122
|
declare const liveServices: ServiceDefinition[];
|
|
125
|
-
declare const inDevServices: ServiceDefinition[];
|
|
126
|
-
declare const comingSoonServices: ServiceDefinition[];
|
|
127
123
|
/** Services with a scripted onboarding tour step, in catalog order. */
|
|
128
124
|
declare const onboardingTourServices: ServiceDefinition[];
|
|
129
125
|
declare function getServiceBySlug(slug: string): ServiceDefinition | undefined;
|
|
130
126
|
declare function getServiceUrl(service: ServiceDefinition): string | null;
|
|
131
127
|
|
|
132
|
-
export { DEFAULT_PLATFORM_DOMAIN, SERVICE_CATALOG, type ServiceCategory, type ServiceDefinition, type ServiceOnboardingContent, type ServiceOnboardingFeature,
|
|
128
|
+
export { DEFAULT_PLATFORM_DOMAIN, SERVICE_CATALOG, type ServiceCategory, type ServiceDefinition, type ServiceOnboardingContent, type ServiceOnboardingFeature, coreServices, getPlatformDomain, getServiceBySlug, getServiceUrl, hubServices, liveServices, onboardingTourServices, platformUrl, portalUrl };
|
package/dist/services/index.js
CHANGED
|
@@ -3,16 +3,14 @@ import {
|
|
|
3
3
|
SERVICE_CATALOG,
|
|
4
4
|
SLUG_ALIASES,
|
|
5
5
|
buildCrossServiceLinks,
|
|
6
|
-
comingSoonServices,
|
|
7
6
|
coreServices,
|
|
8
7
|
getServiceBySlug,
|
|
9
8
|
getServiceUrl,
|
|
10
9
|
hubServices,
|
|
11
|
-
inDevServices,
|
|
12
10
|
liveServices,
|
|
13
11
|
normalizeServiceSlug,
|
|
14
12
|
onboardingTourServices
|
|
15
|
-
} from "../chunk-
|
|
13
|
+
} from "../chunk-45ZQDLPA.js";
|
|
16
14
|
import {
|
|
17
15
|
DEFAULT_PLATFORM_DOMAIN,
|
|
18
16
|
getPlatformDomain,
|
|
@@ -27,13 +25,11 @@ export {
|
|
|
27
25
|
SERVICE_CATALOG,
|
|
28
26
|
SLUG_ALIASES,
|
|
29
27
|
buildCrossServiceLinks,
|
|
30
|
-
comingSoonServices,
|
|
31
28
|
coreServices,
|
|
32
29
|
getPlatformDomain,
|
|
33
30
|
getServiceBySlug,
|
|
34
31
|
getServiceUrl,
|
|
35
32
|
hubServices,
|
|
36
|
-
inDevServices,
|
|
37
33
|
liveServices,
|
|
38
34
|
normalizeServiceSlug,
|
|
39
35
|
onboardingTourServices,
|
package/dist/shell/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { D as DashboardLayout, a as DashboardLayoutEnterprise, b as DashboardLayoutProps, c as DashboardLayoutUser } from '../HealthFooter-
|
|
2
|
-
export { C as CrossServiceNav, d as CrossServiceNavProps, H as HealthFooter, e as HealthFooterEnterprise, f as HealthFooterProps, g as HealthFooterUser, M as MenuItem, h as MenuSection } from '../HealthFooter-
|
|
3
|
-
export { C as CrossServiceLink, b as buildCrossServiceLinks } from '../crossNav-
|
|
1
|
+
import { D as DashboardLayout, a as DashboardLayoutEnterprise, b as DashboardLayoutProps, c as DashboardLayoutUser } from '../HealthFooter-Cepg3mRC.js';
|
|
2
|
+
export { C as CrossServiceNav, d as CrossServiceNavProps, H as HealthFooter, e as HealthFooterEnterprise, f as HealthFooterProps, g as HealthFooterUser, M as MenuItem, h as MenuSection } from '../HealthFooter-Cepg3mRC.js';
|
|
3
|
+
export { C as CrossServiceLink, b as buildCrossServiceLinks } from '../crossNav-DBX4Eyx7.js';
|
|
4
4
|
import 'react';
|
|
5
5
|
import '../orgHierarchy-Buky69YP.js';
|
|
6
6
|
|
package/dist/shell/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildCrossServiceLinks
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-45ZQDLPA.js";
|
|
4
4
|
import {
|
|
5
5
|
CrossServiceNav,
|
|
6
6
|
DashboardLayout,
|
|
7
7
|
HealthFooter
|
|
8
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-YCTIC6XW.js";
|
|
9
9
|
import "../chunk-LUQAR6NB.js";
|
|
10
10
|
import "../chunk-7LI4Y6WT.js";
|
|
11
11
|
import "../chunk-RSFGV7CG.js";
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/services/catalog.ts","../src/services/crossNav.ts"],"sourcesContent":["/**\n * Shared Service Catalog — Single source of truth for all Oplytics.digital services.\n *\n * Consumed by:\n * - oplytics-subdomains (ServiceHub.tsx) — service cards, access gating\n * - oplytics-marketing-site (services.ts) — marketing pages, hero sections\n *\n * Each consumer extends with context-specific fields locally:\n * - Platform adds: route, external URL, React icon component\n * - Marketing adds: heroImage, demoImage, problem, howItWorks, results, aiFeatures\n */\n\nimport { platformUrl } from \"./platformDomain\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type ServiceStatus = \"live\" | \"in-development\" | \"coming-soon\";\nexport type ServiceCategory = \"core\" | \"hub\";\n\nexport interface ServiceDefinition {\n /** Unique identifier used for DB lookups, URL slugs, and cross-referencing */\n slug: string;\n /** Display name shown in cards, headers, and navigation */\n name: string;\n /** Short one-line value proposition */\n tagline: string;\n /** Longer description (1-2 sentences) for cards and summaries */\n description: string;\n /** Lucide icon name (string) — consumers resolve to component locally */\n icon: string;\n /** Hex accent color for cards, badges, and theming */\n accentColor: string;\n /** Current availability status */\n status: ServiceStatus;\n /** Core = standalone product, Hub = extension/add-on module */\n category: ServiceCategory;\n /**\n * Subdomain label on the platform domain (e.g. \"sqdcp\") — null if hosted\n * on portal. Full URLs are built via getServiceUrl(), which resolves the\n * platform domain at runtime (see ./platformDomain).\n */\n subdomainLabel: string | null;\n /**\n * Scripted content for the Opi onboarding tour (see @pablo2410/shared-ui/tour).\n * Optional and deliberately sparse — only services with a real, verified\n * first-run path should have this filled in. A service with no `onboarding`\n * block is skipped by the tour entirely rather than shown with placeholder\n * copy. Marketing/other consumers ignore this field.\n */\n onboarding?: ServiceOnboardingContent;\n}\n\nexport interface ServiceOnboardingContent {\n /** Opi's scripted opening line for this step — the always-available fallback if the live LLM narration call fails or AI is gated off. */\n hook: string;\n /** 2-4 short bullets: what's actually here, in plain language. Superseded\n * by `features` for display when both are present — kept for back-compat\n * and as the fallback shape for any service not yet upgraded. */\n highlights: string[];\n /**\n * Named feature call-outs, richer than a flat bullet — one sentence each\n * on what it does AND why the user should care, not just what it's\n * called. Convention (not enforced): aim for 3 per service. Each `name`\n * doubles as the natural spotlight-target label a later in-app tour\n * (inside the service's own subdomain, once the tour follows the user\n * in) can reuse — this is deliberately the same data written once.\n */\n features?: ServiceOnboardingFeature[];\n /** The one thing the step ends on. */\n firstAction: {\n label: string;\n /** Path relative to the service's own root (e.g. \"/xmatrix\"). */\n path: string;\n };\n /** What Opi should say instead when the service has no data yet for this enterprise (empty-state framing), rather than pointing at the normal firstAction. */\n emptyStateHint?: string;\n}\n\nexport interface ServiceOnboardingFeature {\n /** Short feature name, e.g. \"X-Matrix\". */\n name: string;\n /** One deeper sentence — what it does and why the user should care. */\n detail: string;\n}\n\n// ---------------------------------------------------------------------------\n// Catalog\n// ---------------------------------------------------------------------------\n\nexport const SERVICE_CATALOG: ServiceDefinition[] = [\n // ─── CORE SERVICES ───\n {\n slug: \"policy-deployment\",\n name: \"Policy Deployment\",\n tagline: \"Align strategy to execution\",\n description:\n \"Hoshin Kanri and X-matrix planning. Cascade strategic objectives through every level of your organisation with live tracking and bowling charts.\",\n icon: \"Target\",\n accentColor: \"#F59E0B\",\n status: \"live\",\n category: \"core\",\n subdomainLabel: \"policy\",\n onboarding: {\n hook: \"This is where your strategy becomes a plan everyone can see. Long-term goals break into this year's objectives, objectives break into projects, and projects carry the metrics that prove it's working.\",\n highlights: [\n \"X-Matrix — the rollup view: goals, objectives, and projects, with dots showing which project moves which goal.\",\n \"Catchball — where the plan actually gets built: negotiate objectives and projects up and down the org, add a metric to a project.\",\n \"Actions — every task that comes out of the plan, tracked to closure.\",\n ],\n features: [\n {\n name: \"X-Matrix\",\n detail:\n \"The whole plan at a glance — goals, objectives, and projects on one grid, with dots showing exactly which project moves which goal, so nobody's guessing why a project exists.\",\n },\n {\n name: \"Catchball\",\n detail:\n \"Where the plan actually gets negotiated — objectives and projects get pitched up and down the org until everyone agrees, and that's also where a project picks up the metric it'll be measured on.\",\n },\n {\n name: \"Actions\",\n detail:\n \"Every task the plan generates, tracked to closure by pillar — so a strategic project never quietly stalls because nobody was assigned the next step.\",\n },\n ],\n firstAction: { label: \"Open X-Matrix\", path: \"/xmatrix\" },\n emptyStateHint:\n \"No plan yet for this enterprise — say so plainly and point at Catchball's \\\"Create your first plan\\\" instead of X-Matrix's empty state.\",\n },\n },\n {\n slug: \"sqdcp\",\n name: \"SQDCP Dashboard\",\n tagline: \"Daily management boards, digitised\",\n description:\n \"Digital SQDCP boards that drive daily accountability. Safety, Quality, Delivery, Cost, and People metrics at a glance for every team.\",\n icon: \"LayoutGrid\",\n accentColor: \"#1DB8CE\",\n status: \"live\",\n category: \"core\",\n subdomainLabel: \"sqdcp\",\n onboarding: {\n hook: \"This is your daily board — Safety, Quality, Delivery, Cost, People, live. Every team has a room; the huddle happens here, and the numbers you log in Data Entry are what turn the board green or red.\",\n highlights: [\n \"Rooms — one per team, tiered (Team Huddle → Area/Plant Review → Monthly Review), each feeding the one above.\",\n \"Data Entry — where actuals get logged against each pillar's metrics.\",\n \"Safety Manager / Quality Manager tabs — the deeper pillar-specific views inside SQDCP.\",\n \"If AI Facilitator is on, Opi runs the huddle itself — drafts the line for each red pillar, opens the action review by name.\",\n ],\n features: [\n {\n name: \"Rooms\",\n detail:\n \"One room per team, tiered from Team Huddle up to Area/Plant Review to Monthly Review — each level feeds the one above, so a problem raised on the floor is visible all the way up without anyone re-typing it into a slide deck.\",\n },\n {\n name: \"Data Entry\",\n detail:\n \"Where actuals get logged against each pillar's metrics — this is the one habit that makes everything else here true; skip it and the board just goes stale, not red.\",\n },\n {\n name: \"Safety & Quality Manager\",\n detail:\n \"The deeper pillar-specific views inside SQDCP — ISO-aligned gap analysis for Safety and Quality, one level below the daily board for when a red needs more than a glance.\",\n },\n ],\n firstAction: { label: \"Open Rooms\", path: \"/\" },\n emptyStateHint:\n \"No rooms configured yet — this needs an admin to set up rooms first; point at Admin rather than Rooms.\",\n },\n },\n {\n slug: \"oee-manager\",\n name: \"OEE Manager\",\n tagline: \"Real-time equipment effectiveness tracking\",\n description:\n \"Monitor Overall Equipment Effectiveness in real-time. Track availability, performance, and quality metrics across your entire production line.\",\n icon: \"Gauge\",\n accentColor: \"#8C34E9\",\n status: \"live\",\n category: \"core\",\n subdomainLabel: \"oeemanager\",\n // No `onboarding` block yet — held back for a later cohort (2026-09-11 decision).\n },\n {\n slug: \"action-manager\",\n name: \"Action Manager\",\n tagline: \"Track every action to closure\",\n description:\n \"Capture, assign, and track corrective and preventive actions from any source. Ensure nothing falls through the cracks.\",\n icon: \"ClipboardCheck\",\n accentColor: \"#22C55E\",\n status: \"live\",\n category: \"core\",\n subdomainLabel: \"actionmanager\",\n onboarding: {\n hook: \"Every action from every service lands here — SQDCP huddles, Policy Deployment projects, safety reports, all in one place, tracked until someone closes it out.\",\n highlights: [\n \"Board — kanban view, the default way most people work actions day to day.\",\n \"List and Calendar — the same actions, sorted or scheduled instead.\",\n \"Improvement (A3) — structured 7-box root-cause work for the actions that need more than a quick fix.\",\n ],\n features: [\n {\n name: \"Board\",\n detail:\n \"The default working view — a kanban of every open action, so a team can see what's moving and what's stuck without opening a single report.\",\n },\n {\n name: \"List & Calendar\",\n detail:\n \"The same actions, just sorted or scheduled instead of columned — List for triaging a long backlog, Calendar for seeing what's due this week before it becomes overdue.\",\n },\n {\n name: \"Improvement (A3)\",\n detail:\n \"Structured 7-box root-cause work for the actions that keep coming back — Background through Follow-up, one page, so a real fix gets the same rigor a quick fix doesn't need.\",\n },\n ],\n firstAction: { label: \"Open Board\", path: \"/board\" },\n emptyStateHint:\n \"Zero actions yet — say this is normal on day one, actions will start showing up as SQDCP and Policy Deployment generate them, rather than describing an empty board as broken.\",\n },\n },\n {\n slug: \"connect\",\n name: \"OplyticsConnect\",\n tagline: \"Integrate machines, sensors, and systems\",\n description:\n \"Industrial IoT connectivity layer. Connect PLCs, sensors, and legacy systems to the Oplytics platform with zero-code configuration.\",\n icon: \"Plug\",\n accentColor: \"#1DB8CE\",\n status: \"in-development\",\n category: \"core\",\n subdomainLabel: \"connect\",\n },\n\n // ─── HUB SERVICES ───\n {\n slug: \"safety-manager\",\n name: \"Safety Manager\",\n tagline: \"Proactive safety culture, powered by data\",\n description:\n \"Incident reporting, hazard tracking, safety observations, and compliance management in one unified platform.\",\n icon: \"Shield\",\n accentColor: \"#EF4444\",\n status: \"coming-soon\",\n category: \"hub\",\n subdomainLabel: \"safetymanager\",\n },\n {\n slug: \"quality-manager\",\n name: \"Quality Manager\",\n tagline: \"End-to-end quality assurance\",\n description:\n \"Non-conformance tracking, CAPA management, audit scheduling, and quality metrics dashboards.\",\n icon: \"CheckCircle\",\n accentColor: \"#22C55E\",\n status: \"coming-soon\",\n category: \"hub\",\n subdomainLabel: null,\n },\n {\n slug: \"certification-manager\",\n name: \"Certification Manager\",\n tagline: \"Stay audit-ready, always\",\n description:\n \"Manage ISO, IATF, and other certification requirements. Document control, audit trails, and compliance tracking.\",\n icon: \"Award\",\n accentColor: \"#F97316\",\n status: \"coming-soon\",\n category: \"hub\",\n subdomainLabel: null,\n },\n];\n\n// ---------------------------------------------------------------------------\n// Derived helpers\n// ---------------------------------------------------------------------------\n\nexport const coreServices = SERVICE_CATALOG.filter(s => s.category === \"core\");\nexport const hubServices = SERVICE_CATALOG.filter(s => s.category === \"hub\");\nexport const liveServices = SERVICE_CATALOG.filter(s => s.status === \"live\");\nexport const inDevServices = SERVICE_CATALOG.filter(\n s => s.status === \"in-development\"\n);\nexport const comingSoonServices = SERVICE_CATALOG.filter(\n s => s.status === \"coming-soon\"\n);\n/** Services with a scripted onboarding tour step, in catalog order. */\nexport const onboardingTourServices = SERVICE_CATALOG.filter(\n s => s.onboarding !== undefined\n);\n\nexport function getServiceBySlug(slug: string): ServiceDefinition | undefined {\n return SERVICE_CATALOG.find(s => s.slug === slug);\n}\n\nexport function getServiceUrl(service: ServiceDefinition): string | null {\n if (service.subdomainLabel) {\n return platformUrl(service.subdomainLabel);\n }\n return null;\n}\n","/**\n * crossNav — build the sidebar cross-service links (service → service jump\n * without going through the Portal Service Hub).\n *\n * Visibility mirrors the Portal ServiceHub rule: platform_admin and\n * enterprise_admin see every live service; everyone else only sees the\n * services granted in the portal `user_services` table. The current service\n * never links to itself. Each link URL carries the caller's current\n * org-hierarchy selection (?oh=…) so the breadcrumb arrives pre-aligned.\n */\nimport type { OrgHierarchySelection } from \"../hierarchy/orgHierarchy\";\nimport { appendSelectionParam } from \"../hierarchy/selectionParams\";\nimport { SERVICE_CATALOG, getServiceUrl } from \"./catalog\";\n\n/** Roles that see every live service regardless of individual grants. */\nexport const ADMIN_SEE_ALL_ROLES = [\n \"platform_admin\",\n \"enterprise_admin\",\n] as const;\n\n/**\n * Legacy grant slugs → canonical catalog slugs. Auto-provisioned accounts\n * were granted \"sqdcp-logic\" while the catalog slug is \"sqdcp\".\n */\nexport const SLUG_ALIASES: Record<string, string> = {\n \"sqdcp-logic\": \"sqdcp\",\n};\n\nexport function normalizeServiceSlug(slug: string): string {\n return SLUG_ALIASES[slug] ?? slug;\n}\n\n/** Display-ready cross-service link entry. */\nexport interface CrossServiceLink {\n slug: string;\n name: string;\n /** Lucide icon name from the service catalog (consumers resolve locally). */\n icon: string;\n accentColor: string;\n /** Absolute URL including the ?oh= hierarchy selection param. */\n url: string;\n}\n\nexport interface BuildCrossServiceLinksOptions {\n /** Catalog slug of the service doing the rendering (excluded from links). */\n currentSlug: string;\n /** Grant slugs from the portal (raw — aliases are normalized here). */\n grantedSlugs: string[];\n /** Portal role of the current user (drives the admin see-all rule). */\n role?: string | null;\n /** Current org-hierarchy selection to carry across (may be empty/null). */\n selection?: OrgHierarchySelection | null;\n}\n\nexport function buildCrossServiceLinks(\n options: BuildCrossServiceLinksOptions\n): CrossServiceLink[] {\n const { currentSlug, grantedSlugs, role, selection } = options;\n\n const isAdmin =\n !!role && (ADMIN_SEE_ALL_ROLES as readonly string[]).includes(role);\n const granted = new Set(grantedSlugs.map(normalizeServiceSlug));\n const current = normalizeServiceSlug(currentSlug);\n\n return SERVICE_CATALOG.filter(\n svc =>\n svc.status === \"live\" &&\n svc.subdomainLabel !== null &&\n svc.slug !== current &&\n (isAdmin || granted.has(svc.slug))\n ).map(svc => ({\n slug: svc.slug,\n name: svc.name,\n icon: svc.icon,\n accentColor: svc.accentColor,\n url: appendSelectionParam(getServiceUrl(svc)!, selection),\n }));\n}\n"],"mappings":"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","names":[]}
|