@pablo2410/shared-ui 0.18.1 → 0.19.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.
Files changed (44) hide show
  1. package/README.md +18 -15
  2. package/dist/{HealthFooter-C5K9s38i.d.ts → HealthFooter-BoOozbWG.d.ts} +4 -4
  3. package/dist/{chunk-G7XJLTNY.js → chunk-5RY3AJKI.js} +8 -9
  4. package/dist/{chunk-G7XJLTNY.js.map → chunk-5RY3AJKI.js.map} +1 -1
  5. package/dist/{chunk-3G43UG63.js → chunk-A6UTXAPS.js} +11 -3
  6. package/dist/chunk-A6UTXAPS.js.map +1 -0
  7. package/dist/{chunk-R4NOLCSI.js → chunk-BKGUIIN2.js} +1 -1
  8. package/dist/{chunk-R4NOLCSI.js.map → chunk-BKGUIIN2.js.map} +1 -1
  9. package/dist/{chunk-IEBZGU6X.js → chunk-DMUQ5WNO.js} +2 -4
  10. package/dist/chunk-DMUQ5WNO.js.map +1 -0
  11. package/dist/{chunk-PLCWXXUE.js → chunk-K6FHU4HN.js} +9 -15
  12. package/dist/chunk-K6FHU4HN.js.map +1 -0
  13. package/dist/{chunk-KXUNOSMZ.js → chunk-NLQ45OYS.js} +193 -91
  14. package/dist/chunk-NLQ45OYS.js.map +1 -0
  15. package/dist/{chunk-WTR6SNOE.js → chunk-VJEXXX2P.js} +158 -40
  16. package/dist/chunk-VJEXXX2P.js.map +1 -0
  17. package/dist/components/index.d.ts +7 -8
  18. package/dist/components/index.js +85 -63
  19. package/dist/components/index.js.map +1 -1
  20. package/dist/hierarchy/core.js +1 -1
  21. package/dist/hierarchy/index.d.ts +3 -3
  22. package/dist/hierarchy/index.js +5 -5
  23. package/dist/insights/index.d.ts +8 -8
  24. package/dist/insights/index.js +150 -61
  25. package/dist/insights/index.js.map +1 -1
  26. package/dist/layout/index.d.ts +8 -8
  27. package/dist/layout/index.js +6 -6
  28. package/dist/primitives/index.d.ts +288 -289
  29. package/dist/primitives/index.js +2 -2
  30. package/dist/rbac/index.js.map +1 -1
  31. package/dist/reporting/index.d.ts +101 -43
  32. package/dist/reporting/index.js +208 -187
  33. package/dist/reporting/index.js.map +1 -1
  34. package/dist/services/index.js +2 -2
  35. package/dist/shell/index.d.ts +2 -3
  36. package/dist/shell/index.js +7 -7
  37. package/dist/shell/oplytics-shell.css +2 -2
  38. package/dist/theme/oplytics-theme.css +4 -4
  39. package/package.json +48 -46
  40. package/dist/chunk-3G43UG63.js.map +0 -1
  41. package/dist/chunk-IEBZGU6X.js.map +0 -1
  42. package/dist/chunk-KXUNOSMZ.js.map +0 -1
  43. package/dist/chunk-PLCWXXUE.js.map +0 -1
  44. package/dist/chunk-WTR6SNOE.js.map +0 -1
@@ -17,25 +17,45 @@ var defaultSkin = {
17
17
  function severityColor(severity) {
18
18
  switch (severity) {
19
19
  case "good":
20
- return { text: "#10b981", bg: "rgba(16,185,129,0.1)", border: "rgba(16,185,129,0.25)" };
20
+ return {
21
+ text: "#10b981",
22
+ bg: "rgba(16,185,129,0.1)",
23
+ border: "rgba(16,185,129,0.25)"
24
+ };
21
25
  case "watch":
22
- return { text: "#f59e0b", bg: "rgba(245,158,11,0.1)", border: "rgba(245,158,11,0.25)" };
26
+ return {
27
+ text: "#f59e0b",
28
+ bg: "rgba(245,158,11,0.1)",
29
+ border: "rgba(245,158,11,0.25)"
30
+ };
23
31
  case "gap":
24
- return { text: "#ef4444", bg: "rgba(239,68,68,0.1)", border: "rgba(239,68,68,0.25)" };
32
+ return {
33
+ text: "#ef4444",
34
+ bg: "rgba(239,68,68,0.1)",
35
+ border: "rgba(239,68,68,0.25)"
36
+ };
25
37
  }
26
38
  }
27
- function OpiBadgeChip({ skin = defaultSkin }) {
39
+ function OpiBadgeChip({
40
+ skin = defaultSkin
41
+ }) {
28
42
  return /* @__PURE__ */ jsxs(
29
43
  "span",
30
44
  {
31
45
  className: "inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[9px] font-medium uppercase tracking-wide",
32
- style: { background: "#0b0f1a", color: skin.accent, border: `1px solid ${skin.accent}33` },
46
+ style: {
47
+ background: "#0b0f1a",
48
+ color: skin.accent,
49
+ border: `1px solid ${skin.accent}33`
50
+ },
33
51
  children: [
34
52
  /* @__PURE__ */ jsx(
35
53
  "span",
36
54
  {
37
55
  className: "w-2.5 h-2.5 rounded-full",
38
- style: { background: `radial-gradient(circle at 30% 30%, ${skin.accent}, #0d5a63 70%)` }
56
+ style: {
57
+ background: `radial-gradient(circle at 30% 30%, ${skin.accent}, #0d5a63 70%)`
58
+ }
39
59
  }
40
60
  ),
41
61
  "Powered by AI"
@@ -43,14 +63,27 @@ function OpiBadgeChip({ skin = defaultSkin }) {
43
63
  }
44
64
  );
45
65
  }
46
- function OpiScoreDial({ score, size = 56 }) {
66
+ function OpiScoreDial({
67
+ score,
68
+ size = 56
69
+ }) {
47
70
  const radius = (size - 6) / 2;
48
71
  const circumference = 2 * Math.PI * radius;
49
72
  const offset = circumference - score / 100 * circumference;
50
73
  const color = score >= 75 ? "#10b981" : score >= 45 ? "#f59e0b" : "#ef4444";
51
74
  return /* @__PURE__ */ jsxs("div", { className: "relative shrink-0", style: { width: size, height: size }, children: [
52
75
  /* @__PURE__ */ jsxs("svg", { width: size, height: size, className: "-rotate-90", children: [
53
- /* @__PURE__ */ jsx("circle", { cx: size / 2, cy: size / 2, r: radius, fill: "none", stroke: "rgba(255,255,255,0.06)", strokeWidth: 4 }),
76
+ /* @__PURE__ */ jsx(
77
+ "circle",
78
+ {
79
+ cx: size / 2,
80
+ cy: size / 2,
81
+ r: radius,
82
+ fill: "none",
83
+ stroke: "rgba(255,255,255,0.06)",
84
+ strokeWidth: 4
85
+ }
86
+ ),
54
87
  /* @__PURE__ */ jsx(
55
88
  "circle",
56
89
  {
@@ -77,7 +110,14 @@ function OpiSectionHeading({
77
110
  }) {
78
111
  return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-2.5", children: [
79
112
  /* @__PURE__ */ jsx("span", { style: { color: skin.accent }, children: icon }),
80
- /* @__PURE__ */ jsx("h4", { className: "text-[11px] font-bold uppercase tracking-wider", style: { color: skin.softText }, children: title })
113
+ /* @__PURE__ */ jsx(
114
+ "h4",
115
+ {
116
+ className: "text-[11px] font-bold uppercase tracking-wider",
117
+ style: { color: skin.softText },
118
+ children: title
119
+ }
120
+ )
81
121
  ] });
82
122
  }
83
123
  function OpiInsightsShell({
@@ -101,7 +141,11 @@ function OpiInsightsShell({
101
141
  {
102
142
  onClick: () => setOpen(true),
103
143
  className: "flex flex-col items-center gap-2 py-4 px-1.5 rounded-l-lg shrink-0 self-start sticky top-4 transition-colors hover:brightness-110",
104
- style: { background: skin.panelBg, border: skin.panelBorder, borderRight: "none" },
144
+ style: {
145
+ background: skin.panelBg,
146
+ border: skin.panelBorder,
147
+ borderRight: "none"
148
+ },
105
149
  title: `Open ${title}`,
106
150
  children: [
107
151
  /* @__PURE__ */ jsx(Sparkles, { size: 14, style: { color: skin.accent } }),
@@ -128,7 +172,9 @@ function OpiInsightsShell({
128
172
  "div",
129
173
  {
130
174
  className: "flex items-center justify-between gap-2 px-4 py-3 shrink-0",
131
- style: { background: "linear-gradient(135deg, #0d5a63 0%, #0b3a40 100%)" },
175
+ style: {
176
+ background: "linear-gradient(135deg, #0d5a63 0%, #0b3a40 100%)"
177
+ },
132
178
  children: [
133
179
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
134
180
  /* @__PURE__ */ jsx(
@@ -155,7 +201,14 @@ function OpiInsightsShell({
155
201
  ),
156
202
  /* @__PURE__ */ jsxs("div", { className: "overflow-y-auto px-4 py-3.5 space-y-5", children: [
157
203
  headline && /* @__PURE__ */ jsxs("div", { children: [
158
- /* @__PURE__ */ jsx("p", { className: "text-[12px] leading-relaxed mb-2", style: { color: skin.bodyText }, children: headline }),
204
+ /* @__PURE__ */ jsx(
205
+ "p",
206
+ {
207
+ className: "text-[12px] leading-relaxed mb-2",
208
+ style: { color: skin.bodyText },
209
+ children: headline
210
+ }
211
+ ),
159
212
  /* @__PURE__ */ jsx(OpiBadgeChip, { skin })
160
213
  ] }),
161
214
  children
@@ -185,7 +238,11 @@ var defaultSkin2 = {
185
238
  bodyText: "#c0c6d0",
186
239
  softText: "#8890a0"
187
240
  };
188
- function OpiAskBar({ onAsk, placeholder = "Ask Opi about this\u2026", skin = defaultSkin2 }) {
241
+ function OpiAskBar({
242
+ onAsk,
243
+ placeholder = "Ask Opi about this\u2026",
244
+ skin = defaultSkin2
245
+ }) {
189
246
  const [thread, setThread] = useState2([]);
190
247
  const [input, setInput] = useState2("");
191
248
  const [pending, setPending] = useState2(false);
@@ -207,54 +264,86 @@ function OpiAskBar({ onAsk, placeholder = "Ask Opi about this\u2026", skin = def
207
264
  setPending(false);
208
265
  }
209
266
  };
210
- return /* @__PURE__ */ jsxs2("div", { className: "pt-1 -mx-4 -mb-3.5 px-4 pb-3.5", style: { borderTop: skin.panelBorder }, children: [
211
- thread.length > 0 && /* @__PURE__ */ jsxs2("div", { className: "space-y-2 py-3 max-h-64 overflow-y-auto", children: [
212
- thread.map((m, i) => /* @__PURE__ */ jsxs2(
213
- "div",
214
- {
215
- className: "text-[11px] leading-relaxed rounded-lg px-2.5 py-2",
216
- style: m.role === "user" ? { background: "rgba(255,255,255,0.04)", color: skin.bodyText } : { background: `${skin.accent}14`, color: skin.bodyText, border: `1px solid ${skin.accent}26` },
217
- children: [
218
- m.role === "assistant" && /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-1 mb-1", style: { color: skin.accent }, children: [
219
- /* @__PURE__ */ jsx2(Sparkles2, { size: 10 }),
220
- /* @__PURE__ */ jsx2("span", { className: "text-[9px] font-bold uppercase tracking-wide", children: "Opi" })
221
- ] }),
222
- m.content
223
- ]
224
- },
225
- i
226
- )),
227
- pending && /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-1.5 text-[10px]", style: { color: skin.mutedText }, children: [
228
- /* @__PURE__ */ jsx2(Loader2, { size: 11, className: "animate-spin" }),
229
- "Thinking\u2026"
230
- ] })
231
- ] }),
232
- error && /* @__PURE__ */ jsx2("p", { className: "text-[10px] pb-2", style: { color: "#ef4444" }, children: error }),
233
- /* @__PURE__ */ jsxs2("form", { onSubmit: handleSubmit, className: "flex items-center gap-1.5 pt-2", children: [
234
- /* @__PURE__ */ jsx2(
235
- "input",
236
- {
237
- value: input,
238
- onChange: (e) => setInput(e.target.value),
239
- placeholder,
240
- disabled: pending,
241
- className: "flex-1 min-w-0 rounded-md px-2.5 py-1.5 text-[11px] outline-none",
242
- style: { background: "#0b0f1a", border: skin.panelBorder, color: skin.bodyText }
243
- }
244
- ),
245
- /* @__PURE__ */ jsx2(
246
- "button",
247
- {
248
- type: "submit",
249
- disabled: !input.trim() || pending,
250
- className: "shrink-0 w-7 h-7 rounded-md flex items-center justify-center transition-opacity disabled:opacity-40",
251
- style: { background: `${skin.accent}26`, color: skin.accent },
252
- title: "Ask Opi",
253
- children: /* @__PURE__ */ jsx2(Send, { size: 12 })
254
- }
255
- )
256
- ] })
257
- ] });
267
+ return /* @__PURE__ */ jsxs2(
268
+ "div",
269
+ {
270
+ className: "pt-1 -mx-4 -mb-3.5 px-4 pb-3.5",
271
+ style: { borderTop: skin.panelBorder },
272
+ children: [
273
+ thread.length > 0 && /* @__PURE__ */ jsxs2("div", { className: "space-y-2 py-3 max-h-64 overflow-y-auto", children: [
274
+ thread.map((m, i) => /* @__PURE__ */ jsxs2(
275
+ "div",
276
+ {
277
+ className: "text-[11px] leading-relaxed rounded-lg px-2.5 py-2",
278
+ style: m.role === "user" ? {
279
+ background: "rgba(255,255,255,0.04)",
280
+ color: skin.bodyText
281
+ } : {
282
+ background: `${skin.accent}14`,
283
+ color: skin.bodyText,
284
+ border: `1px solid ${skin.accent}26`
285
+ },
286
+ children: [
287
+ m.role === "assistant" && /* @__PURE__ */ jsxs2(
288
+ "div",
289
+ {
290
+ className: "flex items-center gap-1 mb-1",
291
+ style: { color: skin.accent },
292
+ children: [
293
+ /* @__PURE__ */ jsx2(Sparkles2, { size: 10 }),
294
+ /* @__PURE__ */ jsx2("span", { className: "text-[9px] font-bold uppercase tracking-wide", children: "Opi" })
295
+ ]
296
+ }
297
+ ),
298
+ m.content
299
+ ]
300
+ },
301
+ i
302
+ )),
303
+ pending && /* @__PURE__ */ jsxs2(
304
+ "div",
305
+ {
306
+ className: "flex items-center gap-1.5 text-[10px]",
307
+ style: { color: skin.mutedText },
308
+ children: [
309
+ /* @__PURE__ */ jsx2(Loader2, { size: 11, className: "animate-spin" }),
310
+ "Thinking\u2026"
311
+ ]
312
+ }
313
+ )
314
+ ] }),
315
+ error && /* @__PURE__ */ jsx2("p", { className: "text-[10px] pb-2", style: { color: "#ef4444" }, children: error }),
316
+ /* @__PURE__ */ jsxs2("form", { onSubmit: handleSubmit, className: "flex items-center gap-1.5 pt-2", children: [
317
+ /* @__PURE__ */ jsx2(
318
+ "input",
319
+ {
320
+ value: input,
321
+ onChange: (e) => setInput(e.target.value),
322
+ placeholder,
323
+ disabled: pending,
324
+ className: "flex-1 min-w-0 rounded-md px-2.5 py-1.5 text-[11px] outline-none",
325
+ style: {
326
+ background: "#0b0f1a",
327
+ border: skin.panelBorder,
328
+ color: skin.bodyText
329
+ }
330
+ }
331
+ ),
332
+ /* @__PURE__ */ jsx2(
333
+ "button",
334
+ {
335
+ type: "submit",
336
+ disabled: !input.trim() || pending,
337
+ className: "shrink-0 w-7 h-7 rounded-md flex items-center justify-center transition-opacity disabled:opacity-40",
338
+ style: { background: `${skin.accent}26`, color: skin.accent },
339
+ title: "Ask Opi",
340
+ children: /* @__PURE__ */ jsx2(Send, { size: 12 })
341
+ }
342
+ )
343
+ ] })
344
+ ]
345
+ }
346
+ );
258
347
  }
259
348
  export {
260
349
  OpiAskBar,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/insights/OpiInsightsShell.tsx","../../src/insights/OpiAskBar.tsx"],"sourcesContent":["/**\n * OpiInsightsShell — the standard collapsible \"Opi Insights\" AI coaching\n * sidebar chrome, shared by every Oplytics subdomain service.\n *\n * This is deliberately shell-only: it owns the collapsible rail, header,\n * \"Powered by AI\" badge, and the small set of visual primitives every\n * service's AI panel tends to need (score dial, severity styling, section\n * heading, footer note). Each service supplies its OWN domain analysis and\n * renders its own content as children — same pattern as reportingToolbar\n * and hierarchyNavigator. Policy Deployment is the design source (see\n * OpiInsightsPanel there for the first real consumer) and the reference\n * for what a service-specific content tree looks like inside this shell.\n *\n * Services with their own dark-theme skin tokens (panel background, border,\n * text colors) should pass them via `skin` so the panel matches their other\n * views; omit it to fall back to the shared oplytics-theme teal/dark tokens.\n */\nimport { useState, type ReactNode } from \"react\";\nimport { Sparkles, ChevronLeft, ChevronRight } from \"lucide-react\";\nimport { OPLYTICS_COLORS } from \"../theme\";\n\nexport type InsightSeverity = \"good\" | \"watch\" | \"gap\";\n\nexport interface OpiInsightsSkin {\n accent: string;\n panelBg: string;\n panelBorder: string;\n mutedText: string;\n bodyText: string;\n softText: string;\n}\n\nconst defaultSkin: OpiInsightsSkin = {\n accent: OPLYTICS_COLORS.accentTeal,\n panelBg: \"#0e1624\",\n panelBorder: \"1px solid #1e2738\",\n mutedText: \"#596475\",\n bodyText: \"#c0c6d0\",\n softText: \"#8890a0\",\n};\n\nexport function severityColor(severity: InsightSeverity): { text: string; bg: string; border: string } {\n switch (severity) {\n case \"good\":\n return { text: \"#10b981\", bg: \"rgba(16,185,129,0.1)\", border: \"rgba(16,185,129,0.25)\" };\n case \"watch\":\n return { text: \"#f59e0b\", bg: \"rgba(245,158,11,0.1)\", border: \"rgba(245,158,11,0.25)\" };\n case \"gap\":\n return { text: \"#ef4444\", bg: \"rgba(239,68,68,0.1)\", border: \"rgba(239,68,68,0.25)\" };\n }\n}\n\n/** Small \"Powered by AI\" chip — the standard cross-service Opi AI marker. */\nexport function OpiBadgeChip({ skin = defaultSkin }: { skin?: OpiInsightsSkin }) {\n return (\n <span\n className=\"inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[9px] font-medium uppercase tracking-wide\"\n style={{ background: \"#0b0f1a\", color: skin.accent, border: `1px solid ${skin.accent}33` }}\n >\n <span\n className=\"w-2.5 h-2.5 rounded-full\"\n style={{ background: `radial-gradient(circle at 30% 30%, ${skin.accent}, #0d5a63 70%)` }}\n />\n Powered by AI\n </span>\n );\n}\n\n/** Circular 0-100 score dial — green/amber/red by threshold, used for any composite score. */\nexport function OpiScoreDial({ score, size = 56 }: { score: number; size?: number }) {\n const radius = (size - 6) / 2;\n const circumference = 2 * Math.PI * radius;\n const offset = circumference - (score / 100) * circumference;\n const color = score >= 75 ? \"#10b981\" : score >= 45 ? \"#f59e0b\" : \"#ef4444\";\n return (\n <div className=\"relative shrink-0\" style={{ width: size, height: size }}>\n <svg width={size} height={size} className=\"-rotate-90\">\n <circle cx={size / 2} cy={size / 2} r={radius} fill=\"none\" stroke=\"rgba(255,255,255,0.06)\" strokeWidth={4} />\n <circle\n cx={size / 2} cy={size / 2} r={radius} fill=\"none\" stroke={color} strokeWidth={4}\n strokeLinecap=\"round\" strokeDasharray={circumference} strokeDashoffset={offset}\n style={{ transition: \"stroke-dashoffset 0.6s ease-out\" }}\n />\n </svg>\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <span className=\"text-sm font-bold text-white\">{score}</span>\n </div>\n </div>\n );\n}\n\n/** Section label with an icon — used to group the panel's content into named sections. */\nexport function OpiSectionHeading({\n icon,\n title,\n skin = defaultSkin,\n}: {\n icon: ReactNode;\n title: string;\n skin?: OpiInsightsSkin;\n}) {\n return (\n <div className=\"flex items-center gap-2 mb-2.5\">\n <span style={{ color: skin.accent }}>{icon}</span>\n <h4 className=\"text-[11px] font-bold uppercase tracking-wider\" style={{ color: skin.softText }}>\n {title}\n </h4>\n </div>\n );\n}\n\nexport interface OpiInsightsShellProps {\n /** Panel content — build with OpiSectionHeading/OpiScoreDial/severityColor etc. */\n children: ReactNode;\n /** Optional headline shown above the badge (e.g. a one-line AI summary). */\n headline?: ReactNode;\n /** Footer note — defaults to the standard \"mocked pending real engine\" preview notice. */\n footerNote?: ReactNode;\n /** Service-specific dark-theme tokens; falls back to the shared theme's teal/dark defaults. */\n skin?: OpiInsightsSkin;\n /** Panel title in the header. Defaults to \"Opi Insights\". */\n title?: string;\n /** Controlled open state. Omit to let the shell manage its own collapse state. */\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n}\n\n/**\n * The collapsible teal Opi Insights rail. Renders nothing if collapsed\n * except a slim vertical tab; expanded, it's a fixed-width sticky sidebar\n * with a header, scrollable content area, and footer note.\n */\nexport function OpiInsightsShell({\n children,\n headline,\n footerNote = \"Preview build — coaching logic is mocked pending the real Opi engine.\",\n skin = defaultSkin,\n title = \"Opi Insights\",\n open: openProp,\n onOpenChange,\n}: OpiInsightsShellProps) {\n const [openState, setOpenState] = useState(true);\n const open = openProp ?? openState;\n const setOpen = (value: boolean) => {\n setOpenState(value);\n onOpenChange?.(value);\n };\n\n if (!open) {\n return (\n <button\n onClick={() => setOpen(true)}\n className=\"flex flex-col items-center gap-2 py-4 px-1.5 rounded-l-lg shrink-0 self-start sticky top-4 transition-colors hover:brightness-110\"\n style={{ background: skin.panelBg, border: skin.panelBorder, borderRight: \"none\" }}\n title={`Open ${title}`}\n >\n <Sparkles size={14} style={{ color: skin.accent }} />\n <span\n className=\"text-[10px] font-semibold uppercase tracking-wider\"\n style={{ color: skin.accent, writingMode: \"vertical-rl\" }}\n >\n {title}\n </span>\n <ChevronLeft size={12} style={{ color: skin.mutedText }} />\n </button>\n );\n }\n\n return (\n <aside\n className=\"w-[300px] shrink-0 rounded-lg overflow-hidden self-start sticky top-4 max-h-[calc(100vh-6rem)] flex flex-col\"\n style={{ background: skin.panelBg, border: skin.panelBorder }}\n >\n <div\n className=\"flex items-center justify-between gap-2 px-4 py-3 shrink-0\"\n style={{ background: \"linear-gradient(135deg, #0d5a63 0%, #0b3a40 100%)\" }}\n >\n <div className=\"flex items-center gap-2 min-w-0\">\n <div\n className=\"w-6 h-6 rounded-md flex items-center justify-center shrink-0\"\n style={{ background: `${skin.accent}26` }}\n >\n <Sparkles size={13} style={{ color: skin.accent }} />\n </div>\n <span className=\"text-xs font-bold text-white truncate\">{title}</span>\n </div>\n <button\n onClick={() => setOpen(false)}\n className=\"p-1 rounded hover:bg-white/10 transition-colors shrink-0\"\n title=\"Collapse\"\n >\n <ChevronRight size={14} className=\"text-white/70\" />\n </button>\n </div>\n\n <div className=\"overflow-y-auto px-4 py-3.5 space-y-5\">\n {headline && (\n <div>\n <p className=\"text-[12px] leading-relaxed mb-2\" style={{ color: skin.bodyText }}>\n {headline}\n </p>\n <OpiBadgeChip skin={skin} />\n </div>\n )}\n {children}\n </div>\n\n {footerNote && (\n <div\n className=\"px-4 py-2 text-[9px] text-center shrink-0\"\n style={{ color: skin.mutedText, borderTop: skin.panelBorder }}\n >\n {footerNote}\n </div>\n )}\n </aside>\n );\n}\n","/**\n * OpiAskBar — the follow-up chat input for the Opi Insights panel.\n *\n * The shell and every service's analysis content are read-only displays;\n * this is the one interactive surface where a user can ask Opi a question\n * about what's on screen. Transport-agnostic by design: each consuming\n * app has its own way of reaching the portal's `ai.askOpi` endpoint (same-\n * origin tRPC in the portal itself, a cross-origin client elsewhere), so\n * this component takes a plain `onAsk(question)` callback rather than\n * knowing about tRPC. Renders its own short message thread above the\n * input; the shell just places it as the last child.\n */\nimport { useState, type FormEvent } from \"react\";\nimport { Sparkles, Send, Loader2 } from \"lucide-react\";\nimport type { OpiInsightsSkin } from \"./OpiInsightsShell\";\n\ninterface AskMessage {\n role: \"user\" | \"assistant\";\n content: string;\n}\n\nexport interface OpiAskBarProps {\n /** Sends the question (with prior thread already appended by the caller if desired) and resolves with Opi's reply. */\n onAsk: (question: string) => Promise<string>;\n /** Placeholder for the input. Defaults to a generic prompt. */\n placeholder?: string;\n skin?: OpiInsightsSkin;\n}\n\nconst defaultSkin: OpiInsightsSkin = {\n accent: \"#14b8a6\",\n panelBg: \"#0e1624\",\n panelBorder: \"1px solid #1e2738\",\n mutedText: \"#596475\",\n bodyText: \"#c0c6d0\",\n softText: \"#8890a0\",\n};\n\nexport function OpiAskBar({ onAsk, placeholder = \"Ask Opi about this…\", skin = defaultSkin }: OpiAskBarProps) {\n const [thread, setThread] = useState<AskMessage[]>([]);\n const [input, setInput] = useState(\"\");\n const [pending, setPending] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n const question = input.trim();\n if (!question || pending) return;\n\n setThread((prev) => [...prev, { role: \"user\", content: question }]);\n setInput(\"\");\n setPending(true);\n setError(null);\n\n try {\n const reply = await onAsk(question);\n setThread((prev) => [...prev, { role: \"assistant\", content: reply }]);\n } catch {\n setError(\"Couldn't reach Opi just then — give it another go.\");\n } finally {\n setPending(false);\n }\n };\n\n return (\n <div className=\"pt-1 -mx-4 -mb-3.5 px-4 pb-3.5\" style={{ borderTop: skin.panelBorder }}>\n {thread.length > 0 && (\n <div className=\"space-y-2 py-3 max-h-64 overflow-y-auto\">\n {thread.map((m, i) => (\n <div\n key={i}\n className=\"text-[11px] leading-relaxed rounded-lg px-2.5 py-2\"\n style={\n m.role === \"user\"\n ? { background: \"rgba(255,255,255,0.04)\", color: skin.bodyText }\n : { background: `${skin.accent}14`, color: skin.bodyText, border: `1px solid ${skin.accent}26` }\n }\n >\n {m.role === \"assistant\" && (\n <div className=\"flex items-center gap-1 mb-1\" style={{ color: skin.accent }}>\n <Sparkles size={10} />\n <span className=\"text-[9px] font-bold uppercase tracking-wide\">Opi</span>\n </div>\n )}\n {m.content}\n </div>\n ))}\n {pending && (\n <div className=\"flex items-center gap-1.5 text-[10px]\" style={{ color: skin.mutedText }}>\n <Loader2 size={11} className=\"animate-spin\" />\n Thinking…\n </div>\n )}\n </div>\n )}\n\n {error && (\n <p className=\"text-[10px] pb-2\" style={{ color: \"#ef4444\" }}>\n {error}\n </p>\n )}\n\n <form onSubmit={handleSubmit} className=\"flex items-center gap-1.5 pt-2\">\n <input\n value={input}\n onChange={(e) => setInput(e.target.value)}\n placeholder={placeholder}\n disabled={pending}\n className=\"flex-1 min-w-0 rounded-md px-2.5 py-1.5 text-[11px] outline-none\"\n style={{ background: \"#0b0f1a\", border: skin.panelBorder, color: skin.bodyText }}\n />\n <button\n type=\"submit\"\n disabled={!input.trim() || pending}\n className=\"shrink-0 w-7 h-7 rounded-md flex items-center justify-center transition-opacity disabled:opacity-40\"\n style={{ background: `${skin.accent}26`, color: skin.accent }}\n title=\"Ask Opi\"\n >\n <Send size={12} />\n </button>\n </form>\n </div>\n );\n}\n"],"mappings":"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","names":["useState","Sparkles","jsx","jsxs","defaultSkin"]}
1
+ {"version":3,"sources":["../../src/insights/OpiInsightsShell.tsx","../../src/insights/OpiAskBar.tsx"],"sourcesContent":["/**\n * OpiInsightsShell — the standard collapsible \"Opi Insights\" AI coaching\n * sidebar chrome, shared by every Oplytics subdomain service.\n *\n * This is deliberately shell-only: it owns the collapsible rail, header,\n * \"Powered by AI\" badge, and the small set of visual primitives every\n * service's AI panel tends to need (score dial, severity styling, section\n * heading, footer note). Each service supplies its OWN domain analysis and\n * renders its own content as children — same pattern as reportingToolbar\n * and hierarchyNavigator. Policy Deployment is the design source (see\n * OpiInsightsPanel there for the first real consumer) and the reference\n * for what a service-specific content tree looks like inside this shell.\n *\n * Services with their own dark-theme skin tokens (panel background, border,\n * text colors) should pass them via `skin` so the panel matches their other\n * views; omit it to fall back to the shared oplytics-theme teal/dark tokens.\n */\nimport { useState, type ReactNode } from \"react\";\nimport { Sparkles, ChevronLeft, ChevronRight } from \"lucide-react\";\nimport { OPLYTICS_COLORS } from \"../theme\";\n\nexport type InsightSeverity = \"good\" | \"watch\" | \"gap\";\n\nexport interface OpiInsightsSkin {\n accent: string;\n panelBg: string;\n panelBorder: string;\n mutedText: string;\n bodyText: string;\n softText: string;\n}\n\nconst defaultSkin: OpiInsightsSkin = {\n accent: OPLYTICS_COLORS.accentTeal,\n panelBg: \"#0e1624\",\n panelBorder: \"1px solid #1e2738\",\n mutedText: \"#596475\",\n bodyText: \"#c0c6d0\",\n softText: \"#8890a0\",\n};\n\nexport function severityColor(severity: InsightSeverity): {\n text: string;\n bg: string;\n border: string;\n} {\n switch (severity) {\n case \"good\":\n return {\n text: \"#10b981\",\n bg: \"rgba(16,185,129,0.1)\",\n border: \"rgba(16,185,129,0.25)\",\n };\n case \"watch\":\n return {\n text: \"#f59e0b\",\n bg: \"rgba(245,158,11,0.1)\",\n border: \"rgba(245,158,11,0.25)\",\n };\n case \"gap\":\n return {\n text: \"#ef4444\",\n bg: \"rgba(239,68,68,0.1)\",\n border: \"rgba(239,68,68,0.25)\",\n };\n }\n}\n\n/** Small \"Powered by AI\" chip — the standard cross-service Opi AI marker. */\nexport function OpiBadgeChip({\n skin = defaultSkin,\n}: {\n skin?: OpiInsightsSkin;\n}) {\n return (\n <span\n className=\"inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[9px] font-medium uppercase tracking-wide\"\n style={{\n background: \"#0b0f1a\",\n color: skin.accent,\n border: `1px solid ${skin.accent}33`,\n }}\n >\n <span\n className=\"w-2.5 h-2.5 rounded-full\"\n style={{\n background: `radial-gradient(circle at 30% 30%, ${skin.accent}, #0d5a63 70%)`,\n }}\n />\n Powered by AI\n </span>\n );\n}\n\n/** Circular 0-100 score dial — green/amber/red by threshold, used for any composite score. */\nexport function OpiScoreDial({\n score,\n size = 56,\n}: {\n score: number;\n size?: number;\n}) {\n const radius = (size - 6) / 2;\n const circumference = 2 * Math.PI * radius;\n const offset = circumference - (score / 100) * circumference;\n const color = score >= 75 ? \"#10b981\" : score >= 45 ? \"#f59e0b\" : \"#ef4444\";\n return (\n <div className=\"relative shrink-0\" style={{ width: size, height: size }}>\n <svg width={size} height={size} className=\"-rotate-90\">\n <circle\n cx={size / 2}\n cy={size / 2}\n r={radius}\n fill=\"none\"\n stroke=\"rgba(255,255,255,0.06)\"\n strokeWidth={4}\n />\n <circle\n cx={size / 2}\n cy={size / 2}\n r={radius}\n fill=\"none\"\n stroke={color}\n strokeWidth={4}\n strokeLinecap=\"round\"\n strokeDasharray={circumference}\n strokeDashoffset={offset}\n style={{ transition: \"stroke-dashoffset 0.6s ease-out\" }}\n />\n </svg>\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <span className=\"text-sm font-bold text-white\">{score}</span>\n </div>\n </div>\n );\n}\n\n/** Section label with an icon — used to group the panel's content into named sections. */\nexport function OpiSectionHeading({\n icon,\n title,\n skin = defaultSkin,\n}: {\n icon: ReactNode;\n title: string;\n skin?: OpiInsightsSkin;\n}) {\n return (\n <div className=\"flex items-center gap-2 mb-2.5\">\n <span style={{ color: skin.accent }}>{icon}</span>\n <h4\n className=\"text-[11px] font-bold uppercase tracking-wider\"\n style={{ color: skin.softText }}\n >\n {title}\n </h4>\n </div>\n );\n}\n\nexport interface OpiInsightsShellProps {\n /** Panel content — build with OpiSectionHeading/OpiScoreDial/severityColor etc. */\n children: ReactNode;\n /** Optional headline shown above the badge (e.g. a one-line AI summary). */\n headline?: ReactNode;\n /** Footer note — defaults to the standard \"mocked pending real engine\" preview notice. */\n footerNote?: ReactNode;\n /** Service-specific dark-theme tokens; falls back to the shared theme's teal/dark defaults. */\n skin?: OpiInsightsSkin;\n /** Panel title in the header. Defaults to \"Opi Insights\". */\n title?: string;\n /** Controlled open state. Omit to let the shell manage its own collapse state. */\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n}\n\n/**\n * The collapsible teal Opi Insights rail. Renders nothing if collapsed\n * except a slim vertical tab; expanded, it's a fixed-width sticky sidebar\n * with a header, scrollable content area, and footer note.\n */\nexport function OpiInsightsShell({\n children,\n headline,\n footerNote = \"Preview build — coaching logic is mocked pending the real Opi engine.\",\n skin = defaultSkin,\n title = \"Opi Insights\",\n open: openProp,\n onOpenChange,\n}: OpiInsightsShellProps) {\n const [openState, setOpenState] = useState(true);\n const open = openProp ?? openState;\n const setOpen = (value: boolean) => {\n setOpenState(value);\n onOpenChange?.(value);\n };\n\n if (!open) {\n return (\n <button\n onClick={() => setOpen(true)}\n className=\"flex flex-col items-center gap-2 py-4 px-1.5 rounded-l-lg shrink-0 self-start sticky top-4 transition-colors hover:brightness-110\"\n style={{\n background: skin.panelBg,\n border: skin.panelBorder,\n borderRight: \"none\",\n }}\n title={`Open ${title}`}\n >\n <Sparkles size={14} style={{ color: skin.accent }} />\n <span\n className=\"text-[10px] font-semibold uppercase tracking-wider\"\n style={{ color: skin.accent, writingMode: \"vertical-rl\" }}\n >\n {title}\n </span>\n <ChevronLeft size={12} style={{ color: skin.mutedText }} />\n </button>\n );\n }\n\n return (\n <aside\n className=\"w-[300px] shrink-0 rounded-lg overflow-hidden self-start sticky top-4 max-h-[calc(100vh-6rem)] flex flex-col\"\n style={{ background: skin.panelBg, border: skin.panelBorder }}\n >\n <div\n className=\"flex items-center justify-between gap-2 px-4 py-3 shrink-0\"\n style={{\n background: \"linear-gradient(135deg, #0d5a63 0%, #0b3a40 100%)\",\n }}\n >\n <div className=\"flex items-center gap-2 min-w-0\">\n <div\n className=\"w-6 h-6 rounded-md flex items-center justify-center shrink-0\"\n style={{ background: `${skin.accent}26` }}\n >\n <Sparkles size={13} style={{ color: skin.accent }} />\n </div>\n <span className=\"text-xs font-bold text-white truncate\">{title}</span>\n </div>\n <button\n onClick={() => setOpen(false)}\n className=\"p-1 rounded hover:bg-white/10 transition-colors shrink-0\"\n title=\"Collapse\"\n >\n <ChevronRight size={14} className=\"text-white/70\" />\n </button>\n </div>\n\n <div className=\"overflow-y-auto px-4 py-3.5 space-y-5\">\n {headline && (\n <div>\n <p\n className=\"text-[12px] leading-relaxed mb-2\"\n style={{ color: skin.bodyText }}\n >\n {headline}\n </p>\n <OpiBadgeChip skin={skin} />\n </div>\n )}\n {children}\n </div>\n\n {footerNote && (\n <div\n className=\"px-4 py-2 text-[9px] text-center shrink-0\"\n style={{ color: skin.mutedText, borderTop: skin.panelBorder }}\n >\n {footerNote}\n </div>\n )}\n </aside>\n );\n}\n","/**\n * OpiAskBar — the follow-up chat input for the Opi Insights panel.\n *\n * The shell and every service's analysis content are read-only displays;\n * this is the one interactive surface where a user can ask Opi a question\n * about what's on screen. Transport-agnostic by design: each consuming\n * app has its own way of reaching the portal's `ai.askOpi` endpoint (same-\n * origin tRPC in the portal itself, a cross-origin client elsewhere), so\n * this component takes a plain `onAsk(question)` callback rather than\n * knowing about tRPC. Renders its own short message thread above the\n * input; the shell just places it as the last child.\n */\nimport { useState, type FormEvent } from \"react\";\nimport { Sparkles, Send, Loader2 } from \"lucide-react\";\nimport type { OpiInsightsSkin } from \"./OpiInsightsShell\";\n\ninterface AskMessage {\n role: \"user\" | \"assistant\";\n content: string;\n}\n\nexport interface OpiAskBarProps {\n /** Sends the question (with prior thread already appended by the caller if desired) and resolves with Opi's reply. */\n onAsk: (question: string) => Promise<string>;\n /** Placeholder for the input. Defaults to a generic prompt. */\n placeholder?: string;\n skin?: OpiInsightsSkin;\n}\n\nconst defaultSkin: OpiInsightsSkin = {\n accent: \"#14b8a6\",\n panelBg: \"#0e1624\",\n panelBorder: \"1px solid #1e2738\",\n mutedText: \"#596475\",\n bodyText: \"#c0c6d0\",\n softText: \"#8890a0\",\n};\n\nexport function OpiAskBar({\n onAsk,\n placeholder = \"Ask Opi about this…\",\n skin = defaultSkin,\n}: OpiAskBarProps) {\n const [thread, setThread] = useState<AskMessage[]>([]);\n const [input, setInput] = useState(\"\");\n const [pending, setPending] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n const question = input.trim();\n if (!question || pending) return;\n\n setThread(prev => [...prev, { role: \"user\", content: question }]);\n setInput(\"\");\n setPending(true);\n setError(null);\n\n try {\n const reply = await onAsk(question);\n setThread(prev => [...prev, { role: \"assistant\", content: reply }]);\n } catch {\n setError(\"Couldn't reach Opi just then — give it another go.\");\n } finally {\n setPending(false);\n }\n };\n\n return (\n <div\n className=\"pt-1 -mx-4 -mb-3.5 px-4 pb-3.5\"\n style={{ borderTop: skin.panelBorder }}\n >\n {thread.length > 0 && (\n <div className=\"space-y-2 py-3 max-h-64 overflow-y-auto\">\n {thread.map((m, i) => (\n <div\n key={i}\n className=\"text-[11px] leading-relaxed rounded-lg px-2.5 py-2\"\n style={\n m.role === \"user\"\n ? {\n background: \"rgba(255,255,255,0.04)\",\n color: skin.bodyText,\n }\n : {\n background: `${skin.accent}14`,\n color: skin.bodyText,\n border: `1px solid ${skin.accent}26`,\n }\n }\n >\n {m.role === \"assistant\" && (\n <div\n className=\"flex items-center gap-1 mb-1\"\n style={{ color: skin.accent }}\n >\n <Sparkles size={10} />\n <span className=\"text-[9px] font-bold uppercase tracking-wide\">\n Opi\n </span>\n </div>\n )}\n {m.content}\n </div>\n ))}\n {pending && (\n <div\n className=\"flex items-center gap-1.5 text-[10px]\"\n style={{ color: skin.mutedText }}\n >\n <Loader2 size={11} className=\"animate-spin\" />\n Thinking…\n </div>\n )}\n </div>\n )}\n\n {error && (\n <p className=\"text-[10px] pb-2\" style={{ color: \"#ef4444\" }}>\n {error}\n </p>\n )}\n\n <form onSubmit={handleSubmit} className=\"flex items-center gap-1.5 pt-2\">\n <input\n value={input}\n onChange={e => setInput(e.target.value)}\n placeholder={placeholder}\n disabled={pending}\n className=\"flex-1 min-w-0 rounded-md px-2.5 py-1.5 text-[11px] outline-none\"\n style={{\n background: \"#0b0f1a\",\n border: skin.panelBorder,\n color: skin.bodyText,\n }}\n />\n <button\n type=\"submit\"\n disabled={!input.trim() || pending}\n className=\"shrink-0 w-7 h-7 rounded-md flex items-center justify-center transition-opacity disabled:opacity-40\"\n style={{ background: `${skin.accent}26`, color: skin.accent }}\n title=\"Ask Opi\"\n >\n <Send size={12} />\n </button>\n </form>\n </div>\n );\n}\n"],"mappings":"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","names":["useState","Sparkles","jsx","jsxs","defaultSkin"]}
@@ -1,6 +1,6 @@
1
- import { S as SharedSidebarConfig } from '../HealthFooter-C5K9s38i.js';
2
- export { C as CrossServiceNav, d as CrossServiceNavProps, D as DashboardLayout, a as DashboardLayoutEnterprise, b as DashboardLayoutProps, c as DashboardLayoutUser, H as HealthFooter, e as HealthFooterEnterprise, f as HealthFooterProps, g as HealthFooterUser, M as MenuItem, h as MenuSection, i as SharedSidebarProps, j as getInitials, k as isAdminRole, l as isMenuItemActive, u as useSidebarResize } from '../HealthFooter-C5K9s38i.js';
3
- import * as react_jsx_runtime from 'react/jsx-runtime';
1
+ import { S as SharedSidebarConfig } from '../HealthFooter-BoOozbWG.js';
2
+ export { C as CrossServiceNav, d as CrossServiceNavProps, D as DashboardLayout, a as DashboardLayoutEnterprise, b as DashboardLayoutProps, c as DashboardLayoutUser, H as HealthFooter, e as HealthFooterEnterprise, f as HealthFooterProps, g as HealthFooterUser, M as MenuItem, h as MenuSection, i as SharedSidebarProps, j as getInitials, k as isAdminRole, l as isMenuItemActive, u as useSidebarResize } from '../HealthFooter-BoOozbWG.js';
3
+ import * as React$1 from 'react';
4
4
  import { ReactNode } from 'react';
5
5
  import '../crossNav-Bz0RXU_F.js';
6
6
  import '../orgHierarchy-Buky69YP.js';
@@ -8,7 +8,7 @@ import '../orgHierarchy-Buky69YP.js';
8
8
  interface DashboardLayoutSkeletonProps {
9
9
  className?: string;
10
10
  }
11
- declare function DashboardLayoutSkeleton({ className }: DashboardLayoutSkeletonProps): react_jsx_runtime.JSX.Element;
11
+ declare function DashboardLayoutSkeleton({ className, }: DashboardLayoutSkeletonProps): React$1.JSX.Element;
12
12
 
13
13
  interface SharedPageHeaderProps {
14
14
  /** Page title */
@@ -32,7 +32,7 @@ interface SharedPageHeaderProps {
32
32
  /** Children rendered below the header row */
33
33
  children?: ReactNode;
34
34
  }
35
- declare function SharedPageHeader({ title, subtitle, icon, onBack, actions, breadcrumbs, showBorder, compact, className, children, }: SharedPageHeaderProps): react_jsx_runtime.JSX.Element;
35
+ declare function SharedPageHeader({ title, subtitle, icon, onBack, actions, breadcrumbs, showBorder, compact, className, children, }: SharedPageHeaderProps): React$1.JSX.Element;
36
36
 
37
37
  interface EnterpriseDebugLabelProps {
38
38
  /**
@@ -48,7 +48,7 @@ interface EnterpriseDebugLabelProps {
48
48
  cookieEnterpriseName?: string | null;
49
49
  className?: string;
50
50
  }
51
- declare function EnterpriseDebugLabel({ selectionEnterpriseId, cookieEnterpriseName, className, }: EnterpriseDebugLabelProps): react_jsx_runtime.JSX.Element;
51
+ declare function EnterpriseDebugLabel({ selectionEnterpriseId, cookieEnterpriseName, className, }: EnterpriseDebugLabelProps): React$1.JSX.Element;
52
52
 
53
53
  interface FooterService {
54
54
  name: string;
@@ -82,7 +82,7 @@ interface SharedFooterProps {
82
82
  /** Current year override (default: auto) */
83
83
  year?: number;
84
84
  }
85
- declare function SharedFooter({ services, showPricing, showSignIn, onServiceClick, onPricingClick, onSignInClick, children, className, useExternalUrls, year, }: SharedFooterProps): react_jsx_runtime.JSX.Element;
85
+ declare function SharedFooter({ services, showPricing, showSignIn, onServiceClick, onPricingClick, onSignInClick, children, className, useExternalUrls, year, }: SharedFooterProps): React$1.JSX.Element;
86
86
 
87
87
  interface ServiceFooterProps {
88
88
  /** Enterprise ID for health monitor */
@@ -111,7 +111,7 @@ interface ServiceFooterProps {
111
111
  /** CSS class for the footer container */
112
112
  className?: string;
113
113
  }
114
- declare function ServiceFooter({ enterpriseId, enterpriseName, userRole, userEmail, environment, buildVersion, buildDate, commitHash, showHealthMonitor, className, }: ServiceFooterProps): react_jsx_runtime.JSX.Element;
114
+ declare function ServiceFooter({ enterpriseId, enterpriseName, userRole, userEmail, environment, buildVersion, buildDate, commitHash, showHealthMonitor, className, }: ServiceFooterProps): React$1.JSX.Element;
115
115
 
116
116
  /**
117
117
  * createServiceLayout — Factory function that generates a complete DashboardLayout
@@ -19,15 +19,15 @@ import {
19
19
  policyDeploymentConfig,
20
20
  sqdcpConfig,
21
21
  useSidebarResize
22
- } from "../chunk-KXUNOSMZ.js";
22
+ } from "../chunk-NLQ45OYS.js";
23
23
  import "../chunk-4QJMLQSH.js";
24
- import "../chunk-WTR6SNOE.js";
25
- import "../chunk-IEBZGU6X.js";
26
- import "../chunk-3G43UG63.js";
24
+ import "../chunk-VJEXXX2P.js";
25
+ import "../chunk-DMUQ5WNO.js";
26
+ import "../chunk-A6UTXAPS.js";
27
27
  import "../chunk-HLMIR5UV.js";
28
- import "../chunk-PLCWXXUE.js";
28
+ import "../chunk-K6FHU4HN.js";
29
29
  import "../chunk-JT3XLKKD.js";
30
- import "../chunk-R4NOLCSI.js";
30
+ import "../chunk-BKGUIIN2.js";
31
31
  export {
32
32
  CrossServiceNav,
33
33
  DashboardLayout,