@pablo2410/shared-ui 0.9.2 → 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,63 @@
1
- import { OrgHierarchyData, OrgHierarchyContextValue } from './core.js';
2
- export { ACTIVE_ENTERPRISE_COOKIE, ORG_HIERARCHY_DATA_EMPTY, ORG_HIERARCHY_EMPTY, ORG_HIERARCHY_LABELS, ORG_HIERARCHY_LEVELS, OrgHierarchyArea, OrgHierarchyAsset, OrgHierarchyBusinessUnit, OrgHierarchyEnterprise, OrgHierarchyLevel, OrgHierarchyNode, OrgHierarchySelection, OrgHierarchySite, applyEnterpriseOverride, scopeHierarchyToEnterprise } from './core.js';
1
+ import { O as OrgHierarchySelection, a as OrgHierarchyData, b as OrgHierarchyContextValue } from '../orgHierarchy-BLW4dt8Q.js';
2
+ export { c as ORG_HIERARCHY_DATA_EMPTY, d as ORG_HIERARCHY_EMPTY, e as ORG_HIERARCHY_LABELS, f as ORG_HIERARCHY_LEVELS, g as OrgHierarchyArea, h as OrgHierarchyAsset, i as OrgHierarchyBusinessUnit, j as OrgHierarchyEnterprise, k as OrgHierarchyLevel, l as OrgHierarchyNode, m as OrgHierarchySite } from '../orgHierarchy-BLW4dt8Q.js';
3
+ export { ACTIVE_ENTERPRISE_COOKIE, applyEnterpriseOverride, scopeHierarchyToEnterprise } from './core.js';
3
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
5
  import { ReactNode } from 'react';
5
6
 
7
+ /**
8
+ * selectionParams — serialize/deserialize the org-hierarchy selection through
9
+ * a URL query param so it can travel across subdomains (cross-service links).
10
+ *
11
+ * Format: ?oh=<entId>.<buId>.<siteId>.<areaId>.<assetId>, dot-joined ids
12
+ * top-down, truncated at the first unselected level (the cascade setters
13
+ * guarantee the selection is always a contiguous chain, so no gaps occur).
14
+ * e.g. ?oh=3.7.12 = enterprise 3 > business unit 7 > site 12.
15
+ *
16
+ * Node ids come from the Portal DB and are identical across every service,
17
+ * which is what makes this transport valid.
18
+ */
19
+
20
+ /** Query-param key carrying the org-hierarchy selection between services. */
21
+ declare const ORG_HIERARCHY_PARAM = "oh";
22
+ /** Selection expressed as raw ids (what the URL param decodes to). */
23
+ interface OrgSelectionIds {
24
+ enterpriseId: number | null;
25
+ businessUnitId: number | null;
26
+ siteId: number | null;
27
+ areaId: number | null;
28
+ assetId: number | null;
29
+ }
30
+ /**
31
+ * Serialize a selection to the compact param value.
32
+ * Returns null when there is nothing meaningful to carry (no enterprise).
33
+ */
34
+ declare function serializeSelectionParam(selection: OrgHierarchySelection): string | null;
35
+ /**
36
+ * Parse a param value back to ids. Returns null for anything malformed
37
+ * (empty, non-numeric, negative, or too many segments).
38
+ */
39
+ declare function parseSelectionParam(value: string): OrgSelectionIds | null;
40
+ /**
41
+ * Append the selection param to an absolute URL (used when building
42
+ * cross-service links). Returns the URL unchanged when there is no selection
43
+ * or the URL is unparseable.
44
+ */
45
+ declare function appendSelectionParam(url: string, selection: OrgHierarchySelection | null | undefined): string;
46
+ /** Read (without modifying) the selection param from the current location. */
47
+ declare function readSelectionParamFromLocation(): OrgSelectionIds | null;
48
+ /**
49
+ * Remove ONLY the selection param from the address bar (history.replaceState),
50
+ * preserving every other query param and the hash.
51
+ */
52
+ declare function stripSelectionParamFromLocation(): void;
53
+ /**
54
+ * Resolve raw ids against the (already enterprise-scoped) hierarchy data,
55
+ * validating existence AND parent-child integrity level by level. Returns the
56
+ * deepest valid prefix as full nodes; an invalid enterprise yields the empty
57
+ * selection so callers fall back to their normal default behaviour.
58
+ */
59
+ declare function resolveSelectionFromIds(ids: OrgSelectionIds, hierarchy: OrgHierarchyData): OrgHierarchySelection;
60
+
6
61
  /**
7
62
  * Read the Portal-set active-enterprise cookie on the client. This is the only
8
63
  * signal a subdomain has that the platform admin switched enterprise; the value
@@ -69,4 +124,4 @@ interface HierarchyNavigatorProps {
69
124
  }
70
125
  declare function HierarchyNavigator({ hierarchy: ctx, compact, maxDepth, className, renderDropdown, }: HierarchyNavigatorProps): react_jsx_runtime.JSX.Element;
71
126
 
72
- export { type CreateOrgHierarchyContextOptions, type DropdownRenderProps, HierarchyNavigator, type HierarchyNavigatorProps, OrgHierarchyContextValue, OrgHierarchyData, createOrgHierarchyContext, readActiveEnterpriseCookie };
127
+ export { type CreateOrgHierarchyContextOptions, type DropdownRenderProps, HierarchyNavigator, type HierarchyNavigatorProps, ORG_HIERARCHY_PARAM, OrgHierarchyContextValue, OrgHierarchyData, OrgHierarchySelection, type OrgSelectionIds, appendSelectionParam, createOrgHierarchyContext, parseSelectionParam, readActiveEnterpriseCookie, readSelectionParamFromLocation, resolveSelectionFromIds, serializeSelectionParam, stripSelectionParamFromLocation };
@@ -2,16 +2,27 @@ import {
2
2
  HierarchyNavigator,
3
3
  createOrgHierarchyContext,
4
4
  readActiveEnterpriseCookie
5
- } from "../chunk-YVMCOFHL.js";
5
+ } from "../chunk-ZLOUWBQN.js";
6
+ import {
7
+ ORG_HIERARCHY_PARAM,
8
+ appendSelectionParam,
9
+ parseSelectionParam,
10
+ readSelectionParamFromLocation,
11
+ resolveSelectionFromIds,
12
+ serializeSelectionParam,
13
+ stripSelectionParamFromLocation
14
+ } from "../chunk-3E3GLIQV.js";
6
15
  import {
7
16
  ACTIVE_ENTERPRISE_COOKIE,
17
+ applyEnterpriseOverride,
18
+ scopeHierarchyToEnterprise
19
+ } from "../chunk-3G43UG63.js";
20
+ import {
8
21
  ORG_HIERARCHY_DATA_EMPTY,
9
22
  ORG_HIERARCHY_EMPTY,
10
23
  ORG_HIERARCHY_LABELS,
11
- ORG_HIERARCHY_LEVELS,
12
- applyEnterpriseOverride,
13
- scopeHierarchyToEnterprise
14
- } from "../chunk-NXSFRNI7.js";
24
+ ORG_HIERARCHY_LEVELS
25
+ } from "../chunk-PS4K7FKB.js";
15
26
  import "../chunk-JT3XLKKD.js";
16
27
  export {
17
28
  ACTIVE_ENTERPRISE_COOKIE,
@@ -20,9 +31,16 @@ export {
20
31
  ORG_HIERARCHY_EMPTY,
21
32
  ORG_HIERARCHY_LABELS,
22
33
  ORG_HIERARCHY_LEVELS,
34
+ ORG_HIERARCHY_PARAM,
35
+ appendSelectionParam,
23
36
  applyEnterpriseOverride,
24
37
  createOrgHierarchyContext,
38
+ parseSelectionParam,
25
39
  readActiveEnterpriseCookie,
26
- scopeHierarchyToEnterprise
40
+ readSelectionParamFromLocation,
41
+ resolveSelectionFromIds,
42
+ scopeHierarchyToEnterprise,
43
+ serializeSelectionParam,
44
+ stripSelectionParamFromLocation
27
45
  };
28
46
  //# sourceMappingURL=index.js.map
@@ -1,7 +1,9 @@
1
- import { S as SharedSidebarConfig } from '../HealthFooter-BusozVzz.js';
2
- export { D as DashboardLayout, a as DashboardLayoutEnterprise, b as DashboardLayoutProps, c as DashboardLayoutUser, H as HealthFooter, d as HealthFooterEnterprise, e as HealthFooterProps, f as HealthFooterUser, M as MenuItem, g as MenuSection, h as SharedSidebarProps, i as getInitials, j as isAdminRole, k as isMenuItemActive, u as useSidebarResize } from '../HealthFooter-BusozVzz.js';
1
+ import { S as SharedSidebarConfig } from '../HealthFooter-wAa7a7WR.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-wAa7a7WR.js';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
  import { ReactNode } from 'react';
5
+ import '../crossNav-Bd8-0qoi.js';
6
+ import '../orgHierarchy-BLW4dt8Q.js';
5
7
 
6
8
  interface DashboardLayoutSkeletonProps {
7
9
  className?: string;
@@ -95,12 +97,21 @@ interface ServiceFooterProps {
95
97
  environment?: "production" | "staging" | "development";
96
98
  /** Build version / commit hash */
97
99
  buildVersion?: string;
100
+ /**
101
+ * Build/deploy timestamp — when the currently-served bundle was built.
102
+ * ISO string (e.g. "2026-07-09T07:37:31Z"). Falls back to the build-time
103
+ * __BUILD_DATE__ constant. Rendered as a short date+time so you can tell at
104
+ * a glance whether the deployed code is the latest (root-cause aid).
105
+ */
106
+ buildDate?: string;
107
+ /** Short commit hash of the deployed build (e.g. "c3af23f"). Falls back to __COMMIT_HASH__. */
108
+ commitHash?: string;
98
109
  /** Show health monitor strip (default: false) */
99
110
  showHealthMonitor?: boolean;
100
111
  /** CSS class for the footer container */
101
112
  className?: string;
102
113
  }
103
- declare function ServiceFooter({ enterpriseId, enterpriseName, userRole, userEmail, environment, buildVersion, showHealthMonitor, className, }: ServiceFooterProps): react_jsx_runtime.JSX.Element;
114
+ declare function ServiceFooter({ enterpriseId, enterpriseName, userRole, userEmail, environment, buildVersion, buildDate, commitHash, showHealthMonitor, className, }: ServiceFooterProps): react_jsx_runtime.JSX.Element;
104
115
 
105
116
  /**
106
117
  * createServiceLayout — Factory function that generates a complete DashboardLayout
@@ -1,4 +1,5 @@
1
1
  import {
2
+ CrossServiceNav,
2
3
  DashboardLayout,
3
4
  DashboardLayoutSkeleton,
4
5
  EnterpriseDebugLabel,
@@ -18,12 +19,15 @@ import {
18
19
  policyDeploymentConfig,
19
20
  sqdcpConfig,
20
21
  useSidebarResize
21
- } from "../chunk-O4Y6TELY.js";
22
- import "../chunk-YVMCOFHL.js";
23
- import "../chunk-NXSFRNI7.js";
22
+ } from "../chunk-POOTKX4M.js";
23
+ import "../chunk-ZLOUWBQN.js";
24
+ import "../chunk-3E3GLIQV.js";
25
+ import "../chunk-3G43UG63.js";
26
+ import "../chunk-PS4K7FKB.js";
24
27
  import "../chunk-LTUYIBMA.js";
25
28
  import "../chunk-JT3XLKKD.js";
26
29
  export {
30
+ CrossServiceNav,
27
31
  DashboardLayout,
28
32
  DashboardLayoutSkeleton,
29
33
  EnterpriseDebugLabel,
@@ -0,0 +1,90 @@
1
+ /**
2
+ * @oplytics/shared-ui/hierarchy — Org Hierarchy Types & Utilities
3
+ *
4
+ * Hierarchy: Enterprise > Business Unit > Site > Area > Asset
5
+ *
6
+ * Single source of truth for org-hierarchy types used by:
7
+ * - Portal (direct DB via tRPC)
8
+ * - All subdomains (via Portal Service API)
9
+ *
10
+ * The canonical data shape matches the Portal Service API response:
11
+ * GET /api/service/hierarchy
12
+ */
13
+ interface OrgHierarchyNode {
14
+ id: number;
15
+ name: string;
16
+ code?: string;
17
+ }
18
+ interface OrgHierarchyEnterprise extends OrgHierarchyNode {
19
+ }
20
+ interface OrgHierarchyBusinessUnit extends OrgHierarchyNode {
21
+ enterpriseId: number;
22
+ }
23
+ interface OrgHierarchySite extends OrgHierarchyNode {
24
+ enterpriseId: number;
25
+ businessUnitId: number;
26
+ location?: string | null;
27
+ }
28
+ interface OrgHierarchyArea extends OrgHierarchyNode {
29
+ siteId: number;
30
+ }
31
+ interface OrgHierarchyAsset extends OrgHierarchyNode {
32
+ siteId: number;
33
+ areaId: number;
34
+ assetType?: string;
35
+ }
36
+ /**
37
+ * Canonical flat hierarchy payload.
38
+ * Every consumer (Portal tRPC, Service API REST) must return this shape.
39
+ */
40
+ interface OrgHierarchyData {
41
+ enterprises: OrgHierarchyEnterprise[];
42
+ businessUnits: OrgHierarchyBusinessUnit[];
43
+ sites: OrgHierarchySite[];
44
+ areas: OrgHierarchyArea[];
45
+ assets: OrgHierarchyAsset[];
46
+ }
47
+ interface OrgHierarchySelection {
48
+ enterprise: OrgHierarchyNode | null;
49
+ businessUnit: OrgHierarchyNode | null;
50
+ site: OrgHierarchyNode | null;
51
+ area: OrgHierarchyNode | null;
52
+ asset: OrgHierarchyNode | null;
53
+ }
54
+ declare const ORG_HIERARCHY_EMPTY: OrgHierarchySelection;
55
+ /**
56
+ * An empty hierarchy payload. Use as the canonical "no data" value so every
57
+ * consumer returns the same shape (never null/undefined for a missing tree).
58
+ */
59
+ declare const ORG_HIERARCHY_DATA_EMPTY: OrgHierarchyData;
60
+ interface OrgHierarchyContextValue {
61
+ /** Current selection at each level */
62
+ selection: OrgHierarchySelection;
63
+ /** Flat lists filtered by current selection */
64
+ enterprises: OrgHierarchyNode[];
65
+ businessUnits: OrgHierarchyNode[];
66
+ sites: OrgHierarchyNode[];
67
+ areas: OrgHierarchyNode[];
68
+ assets: OrgHierarchyNode[];
69
+ /** Setters — selecting a level clears all levels below it */
70
+ selectEnterprise: (node: OrgHierarchyNode | null) => void;
71
+ selectBusinessUnit: (node: OrgHierarchyNode | null) => void;
72
+ selectSite: (node: OrgHierarchyNode | null) => void;
73
+ selectArea: (node: OrgHierarchyNode | null) => void;
74
+ selectAsset: (node: OrgHierarchyNode | null) => void;
75
+ /** Whether the user can switch enterprises (platform_admin only) */
76
+ canSwitchEnterprise: boolean;
77
+ /** Loading state */
78
+ loading: boolean;
79
+ /** Breadcrumb trail as an array of { level, node } */
80
+ breadcrumbs: {
81
+ level: string;
82
+ node: OrgHierarchyNode;
83
+ }[];
84
+ }
85
+ declare const ORG_HIERARCHY_LEVELS: readonly ["enterprise", "businessUnit", "site", "area", "asset"];
86
+ type OrgHierarchyLevel = (typeof ORG_HIERARCHY_LEVELS)[number];
87
+ /** Human-readable labels for each level */
88
+ declare const ORG_HIERARCHY_LABELS: Record<OrgHierarchyLevel, string>;
89
+
90
+ export { type OrgHierarchySelection as O, type OrgHierarchyData as a, type OrgHierarchyContextValue as b, ORG_HIERARCHY_DATA_EMPTY as c, ORG_HIERARCHY_EMPTY as d, ORG_HIERARCHY_LABELS as e, ORG_HIERARCHY_LEVELS as f, type OrgHierarchyArea as g, type OrgHierarchyAsset as h, type OrgHierarchyBusinessUnit as i, type OrgHierarchyEnterprise as j, type OrgHierarchyLevel as k, type OrgHierarchyNode as l, type OrgHierarchySite as m };
@@ -1,3 +1,6 @@
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-Bd8-0qoi.js';
2
+ import '../orgHierarchy-BLW4dt8Q.js';
3
+
1
4
  /**
2
5
  * Shared Service Catalog — Single source of truth for all Oplytics.digital services.
3
6
  *
@@ -1,126 +1,31 @@
1
- // src/services/catalog.ts
2
- var SERVICE_CATALOG = [
3
- // ─── CORE SERVICES ───
4
- {
5
- slug: "policy-deployment",
6
- name: "Policy Deployment",
7
- tagline: "Align strategy to execution",
8
- description: "Hoshin Kanri and X-matrix planning. Cascade strategic objectives through every level of your organisation with live tracking and bowling charts.",
9
- icon: "Target",
10
- accentColor: "#F59E0B",
11
- status: "live",
12
- category: "core",
13
- subdomain: "policydeployment.oplytics.digital"
14
- },
15
- {
16
- slug: "sqdcp",
17
- name: "SQDCP Dashboard",
18
- tagline: "Daily management boards, digitised",
19
- description: "Digital SQDCP boards that drive daily accountability. Safety, Quality, Delivery, Cost, and People metrics at a glance for every team.",
20
- icon: "LayoutGrid",
21
- accentColor: "#1DB8CE",
22
- status: "live",
23
- category: "core",
24
- subdomain: "sqdcp.oplytics.digital"
25
- },
26
- {
27
- slug: "oee-manager",
28
- name: "OEE Manager",
29
- tagline: "Real-time equipment effectiveness tracking",
30
- description: "Monitor Overall Equipment Effectiveness in real-time. Track availability, performance, and quality metrics across your entire production line.",
31
- icon: "Gauge",
32
- accentColor: "#8C34E9",
33
- status: "live",
34
- category: "core",
35
- subdomain: "oeemanager.oplytics.digital"
36
- },
37
- {
38
- slug: "action-manager",
39
- name: "Action Manager",
40
- tagline: "Track every action to closure",
41
- description: "Capture, assign, and track corrective and preventive actions from any source. Ensure nothing falls through the cracks.",
42
- icon: "ClipboardCheck",
43
- accentColor: "#22C55E",
44
- status: "live",
45
- category: "core",
46
- subdomain: "actionmanager.oplytics.digital"
47
- },
48
- {
49
- slug: "connect",
50
- name: "OplyticsConnect",
51
- tagline: "Integrate machines, sensors, and systems",
52
- description: "Industrial IoT connectivity layer. Connect PLCs, sensors, and legacy systems to the Oplytics platform with zero-code configuration.",
53
- icon: "Plug",
54
- accentColor: "#1DB8CE",
55
- status: "in-development",
56
- category: "core",
57
- subdomain: "connect.oplytics.digital"
58
- },
59
- // ─── HUB SERVICES ───
60
- {
61
- slug: "safety-manager",
62
- name: "Safety Manager",
63
- tagline: "Proactive safety culture, powered by data",
64
- description: "Incident reporting, hazard tracking, safety observations, and compliance management in one unified platform.",
65
- icon: "Shield",
66
- accentColor: "#EF4444",
67
- status: "coming-soon",
68
- category: "hub",
69
- subdomain: "safetymanager.oplytics.digital"
70
- },
71
- {
72
- slug: "quality-manager",
73
- name: "Quality Manager",
74
- tagline: "End-to-end quality assurance",
75
- description: "Non-conformance tracking, CAPA management, audit scheduling, and quality metrics dashboards.",
76
- icon: "CheckCircle",
77
- accentColor: "#22C55E",
78
- status: "coming-soon",
79
- category: "hub",
80
- subdomain: null
81
- },
82
- {
83
- slug: "certification-manager",
84
- name: "Certification Manager",
85
- tagline: "Stay audit-ready, always",
86
- description: "Manage ISO, IATF, and other certification requirements. Document control, audit trails, and compliance tracking.",
87
- icon: "Award",
88
- accentColor: "#F97316",
89
- status: "coming-soon",
90
- category: "hub",
91
- subdomain: null
92
- }
93
- ];
94
- var coreServices = SERVICE_CATALOG.filter(
95
- (s) => s.category === "core"
96
- );
97
- var hubServices = SERVICE_CATALOG.filter((s) => s.category === "hub");
98
- var liveServices = SERVICE_CATALOG.filter(
99
- (s) => s.status === "live"
100
- );
101
- var inDevServices = SERVICE_CATALOG.filter(
102
- (s) => s.status === "in-development"
103
- );
104
- var comingSoonServices = SERVICE_CATALOG.filter(
105
- (s) => s.status === "coming-soon"
106
- );
107
- function getServiceBySlug(slug) {
108
- return SERVICE_CATALOG.find((s) => s.slug === slug);
109
- }
110
- function getServiceUrl(service) {
111
- if (service.subdomain) {
112
- return `https://${service.subdomain}`;
113
- }
114
- return null;
115
- }
1
+ import {
2
+ ADMIN_SEE_ALL_ROLES,
3
+ SERVICE_CATALOG,
4
+ SLUG_ALIASES,
5
+ buildCrossServiceLinks,
6
+ comingSoonServices,
7
+ coreServices,
8
+ getServiceBySlug,
9
+ getServiceUrl,
10
+ hubServices,
11
+ inDevServices,
12
+ liveServices,
13
+ normalizeServiceSlug
14
+ } from "../chunk-HNOROBPK.js";
15
+ import "../chunk-3E3GLIQV.js";
16
+ import "../chunk-PS4K7FKB.js";
116
17
  export {
18
+ ADMIN_SEE_ALL_ROLES,
117
19
  SERVICE_CATALOG,
20
+ SLUG_ALIASES,
21
+ buildCrossServiceLinks,
118
22
  comingSoonServices,
119
23
  coreServices,
120
24
  getServiceBySlug,
121
25
  getServiceUrl,
122
26
  hubServices,
123
27
  inDevServices,
124
- liveServices
28
+ liveServices,
29
+ normalizeServiceSlug
125
30
  };
126
31
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/services/catalog.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\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 /** Subdomain hostname (without protocol) — null if hosted on portal */\n subdomain: string | null;\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 subdomain: \"policydeployment.oplytics.digital\",\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 subdomain: \"sqdcp.oplytics.digital\",\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 subdomain: \"oeemanager.oplytics.digital\",\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 subdomain: \"actionmanager.oplytics.digital\",\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 subdomain: \"connect.oplytics.digital\",\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 subdomain: \"safetymanager.oplytics.digital\",\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 subdomain: 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 subdomain: null,\n },\n];\n\n// ---------------------------------------------------------------------------\n// Derived helpers\n// ---------------------------------------------------------------------------\n\nexport const coreServices = SERVICE_CATALOG.filter(\n (s) => s.category === \"core\",\n);\nexport const hubServices = SERVICE_CATALOG.filter((s) => s.category === \"hub\");\nexport const liveServices = SERVICE_CATALOG.filter(\n (s) => s.status === \"live\",\n);\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\nexport function getServiceBySlug(\n slug: string,\n): ServiceDefinition | undefined {\n return SERVICE_CATALOG.find((s) => s.slug === slug);\n}\n\nexport function getServiceUrl(service: ServiceDefinition): string | null {\n if (service.subdomain) {\n return `https://${service.subdomain}`;\n }\n return null;\n}\n"],"mappings":";AA4CO,IAAM,kBAAuC;AAAA;AAAA,EAElD;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,SAAS;AAAA,IACT,aACE;AAAA,IACF,MAAM;AAAA,IACN,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,SAAS;AAAA,IACT,aACE;AAAA,IACF,MAAM;AAAA,IACN,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,SAAS;AAAA,IACT,aACE;AAAA,IACF,MAAM;AAAA,IACN,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,SAAS;AAAA,IACT,aACE;AAAA,IACF,MAAM;AAAA,IACN,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,SAAS;AAAA,IACT,aACE;AAAA,IACF,MAAM;AAAA,IACN,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,WAAW;AAAA,EACb;AAAA;AAAA,EAGA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,SAAS;AAAA,IACT,aACE;AAAA,IACF,MAAM;AAAA,IACN,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,SAAS;AAAA,IACT,aACE;AAAA,IACF,MAAM;AAAA,IACN,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,SAAS;AAAA,IACT,aACE;AAAA,IACF,MAAM;AAAA,IACN,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,WAAW;AAAA,EACb;AACF;AAMO,IAAM,eAAe,gBAAgB;AAAA,EAC1C,CAAC,MAAM,EAAE,aAAa;AACxB;AACO,IAAM,cAAc,gBAAgB,OAAO,CAAC,MAAM,EAAE,aAAa,KAAK;AACtE,IAAM,eAAe,gBAAgB;AAAA,EAC1C,CAAC,MAAM,EAAE,WAAW;AACtB;AACO,IAAM,gBAAgB,gBAAgB;AAAA,EAC3C,CAAC,MAAM,EAAE,WAAW;AACtB;AACO,IAAM,qBAAqB,gBAAgB;AAAA,EAChD,CAAC,MAAM,EAAE,WAAW;AACtB;AAEO,SAAS,iBACd,MAC+B;AAC/B,SAAO,gBAAgB,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI;AACpD;AAEO,SAAS,cAAc,SAA2C;AACvE,MAAI,QAAQ,WAAW;AACrB,WAAO,WAAW,QAAQ,SAAS;AAAA,EACrC;AACA,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,7 +1,9 @@
1
- import { D as DashboardLayout, a as DashboardLayoutEnterprise, b as DashboardLayoutProps, c as DashboardLayoutUser } from '../HealthFooter-BusozVzz.js';
2
- export { H as HealthFooter, d as HealthFooterEnterprise, e as HealthFooterProps, f as HealthFooterUser, M as MenuItem, g as MenuSection } from '../HealthFooter-BusozVzz.js';
1
+ import { D as DashboardLayout, a as DashboardLayoutEnterprise, b as DashboardLayoutProps, c as DashboardLayoutUser } from '../HealthFooter-wAa7a7WR.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-wAa7a7WR.js';
3
+ export { C as CrossServiceLink, b as buildCrossServiceLinks } from '../crossNav-Bd8-0qoi.js';
3
4
  import 'react/jsx-runtime';
4
5
  import 'react';
6
+ import '../orgHierarchy-BLW4dt8Q.js';
5
7
 
6
8
  /**
7
9
  * @pablo2410/shared-ui/shell — Oplytics Service Shell
@@ -1,16 +1,24 @@
1
1
  import {
2
+ buildCrossServiceLinks
3
+ } from "../chunk-HNOROBPK.js";
4
+ import {
5
+ CrossServiceNav,
2
6
  DashboardLayout,
3
7
  HealthFooter
4
- } from "../chunk-O4Y6TELY.js";
5
- import "../chunk-YVMCOFHL.js";
6
- import "../chunk-NXSFRNI7.js";
8
+ } from "../chunk-POOTKX4M.js";
9
+ import "../chunk-ZLOUWBQN.js";
10
+ import "../chunk-3E3GLIQV.js";
11
+ import "../chunk-3G43UG63.js";
12
+ import "../chunk-PS4K7FKB.js";
7
13
  import "../chunk-LTUYIBMA.js";
8
14
  import "../chunk-JT3XLKKD.js";
9
15
 
10
16
  // src/shell/index.ts
11
17
  var OplyticsServiceShell = DashboardLayout;
12
18
  export {
19
+ CrossServiceNav,
13
20
  HealthFooter,
14
- OplyticsServiceShell
21
+ OplyticsServiceShell,
22
+ buildCrossServiceLinks
15
23
  };
16
24
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/shell/index.ts"],"sourcesContent":["/**\n * @pablo2410/shared-ui/shell — Oplytics Service Shell\n *\n * The single composed chrome (sidebar + header + breadcrumb + reporting +\n * footer + user menu) used by every Oplytics subdomain. Subdomains become\n * a ~50-line adapter that supplies *data and identity*, nothing visual.\n * One PR to shared-ui + a coordinated version bump propagates a chrome\n * change to every service.\n *\n * Composition: re-exports the existing `DashboardLayout` from\n * `@pablo2410/shared-ui/layout` under the clearer canonical name\n * `OplyticsServiceShell`. The two are the same component; consumers\n * should prefer `/shell` going forward — `/layout` will be marked\n * `@deprecated` once every subdomain has migrated and removed in\n * shared-ui v1.0 (see oplytics-subdomains tracking epic).\n *\n * CSS: ship `dist/shell/oplytics-shell.css` containing every Tailwind v4\n * utility this shell uses (built by `pnpm build:shell-css`). Consumers\n * `@import` it once in their own `index.css` and never need an\n * `@source` directive.\n *\n * ```css\n * @import \"tailwindcss\";\n * @import \"@pablo2410/shared-ui/theme/oplytics-theme.css\";\n * @import \"@pablo2410/shared-ui/shell/oplytics-shell.css\";\n * ```\n *\n * Usage:\n *\n * ```tsx\n * import { OplyticsServiceShell } from \"@pablo2410/shared-ui/shell\";\n *\n * <OplyticsServiceShell\n * serviceName=\"Connect\"\n * serviceIcon={<Zap />}\n * menuSections={menuSections}\n * adminSections={adminSections}\n * isAdmin={user?.role === \"platform_admin\"}\n * activePath={location}\n * onNavigate={setLocation}\n * user={user}\n * onLogout={logout}\n * enterprise={selection.enterprise}\n * hierarchyNavigator={<HierarchyNavigator hierarchy={hierarchy} />}\n * reportingToolbar={<ReportingToolbar … />}\n * >\n * <Routes />\n * </OplyticsServiceShell>\n * ```\n */\n\nimport {\n DashboardLayout,\n type DashboardLayoutProps,\n type DashboardLayoutUser,\n type DashboardLayoutEnterprise,\n type MenuItem,\n type MenuSection,\n HealthFooter,\n type HealthFooterProps,\n type HealthFooterUser,\n type HealthFooterEnterprise,\n} from \"../layout\";\n\n/**\n * The canonical composed Oplytics chrome.\n *\n * This is the same React component as `DashboardLayout` in\n * `@pablo2410/shared-ui/layout`, re-exported under a clearer name.\n * Consumers should prefer `OplyticsServiceShell` going forward.\n */\nconst OplyticsServiceShell = DashboardLayout;\n\n/**\n * Props accepted by `OplyticsServiceShell`.\n * (Alias of `DashboardLayoutProps`.)\n */\ntype ServiceShellProps = DashboardLayoutProps;\n\n/**\n * Authenticated user passed to the shell. Small enough to fit both\n * stateless-JWT subdomains (Connect, Policy Deployment) and DB-backed\n * ones (OEE Manager).\n */\ntype ServiceShellUser = DashboardLayoutUser;\n\n/**\n * Currently-scoped enterprise — drives the header enterprise badge.\n */\ntype ServiceShellEnterprise = DashboardLayoutEnterprise;\n\nexport {\n OplyticsServiceShell,\n type ServiceShellProps,\n type ServiceShellUser,\n type ServiceShellEnterprise,\n type MenuItem,\n type MenuSection,\n HealthFooter,\n type HealthFooterProps,\n type HealthFooterUser,\n type HealthFooterEnterprise,\n};\n"],"mappings":";;;;;;;;;;AAuEA,IAAM,uBAAuB;","names":[]}
1
+ {"version":3,"sources":["../../src/shell/index.ts"],"sourcesContent":["/**\n * @pablo2410/shared-ui/shell — Oplytics Service Shell\n *\n * The single composed chrome (sidebar + header + breadcrumb + reporting +\n * footer + user menu) used by every Oplytics subdomain. Subdomains become\n * a ~50-line adapter that supplies *data and identity*, nothing visual.\n * One PR to shared-ui + a coordinated version bump propagates a chrome\n * change to every service.\n *\n * Composition: re-exports the existing `DashboardLayout` from\n * `@pablo2410/shared-ui/layout` under the clearer canonical name\n * `OplyticsServiceShell`. The two are the same component; consumers\n * should prefer `/shell` going forward — `/layout` will be marked\n * `@deprecated` once every subdomain has migrated and removed in\n * shared-ui v1.0 (see oplytics-subdomains tracking epic).\n *\n * CSS: ship `dist/shell/oplytics-shell.css` containing every Tailwind v4\n * utility this shell uses (built by `pnpm build:shell-css`). Consumers\n * `@import` it once in their own `index.css` and never need an\n * `@source` directive.\n *\n * ```css\n * @import \"tailwindcss\";\n * @import \"@pablo2410/shared-ui/theme/oplytics-theme.css\";\n * @import \"@pablo2410/shared-ui/shell/oplytics-shell.css\";\n * ```\n *\n * Usage:\n *\n * ```tsx\n * import { OplyticsServiceShell } from \"@pablo2410/shared-ui/shell\";\n *\n * <OplyticsServiceShell\n * serviceName=\"Connect\"\n * serviceIcon={<Zap />}\n * menuSections={menuSections}\n * adminSections={adminSections}\n * isAdmin={user?.role === \"platform_admin\"}\n * activePath={location}\n * onNavigate={setLocation}\n * user={user}\n * onLogout={logout}\n * enterprise={selection.enterprise}\n * hierarchyNavigator={<HierarchyNavigator hierarchy={hierarchy} />}\n * reportingToolbar={<ReportingToolbar … />}\n * >\n * <Routes />\n * </OplyticsServiceShell>\n * ```\n */\n\nimport {\n DashboardLayout,\n type DashboardLayoutProps,\n type DashboardLayoutUser,\n type DashboardLayoutEnterprise,\n type MenuItem,\n type MenuSection,\n HealthFooter,\n type HealthFooterProps,\n type HealthFooterUser,\n type HealthFooterEnterprise,\n CrossServiceNav,\n type CrossServiceNavProps,\n} from \"../layout\";\nimport {\n buildCrossServiceLinks,\n type CrossServiceLink,\n} from \"../services/crossNav\";\n\n/**\n * The canonical composed Oplytics chrome.\n *\n * This is the same React component as `DashboardLayout` in\n * `@pablo2410/shared-ui/layout`, re-exported under a clearer name.\n * Consumers should prefer `OplyticsServiceShell` going forward.\n */\nconst OplyticsServiceShell = DashboardLayout;\n\n/**\n * Props accepted by `OplyticsServiceShell`.\n * (Alias of `DashboardLayoutProps`.)\n */\ntype ServiceShellProps = DashboardLayoutProps;\n\n/**\n * Authenticated user passed to the shell. Small enough to fit both\n * stateless-JWT subdomains (Connect, Policy Deployment) and DB-backed\n * ones (OEE Manager).\n */\ntype ServiceShellUser = DashboardLayoutUser;\n\n/**\n * Currently-scoped enterprise — drives the header enterprise badge.\n */\ntype ServiceShellEnterprise = DashboardLayoutEnterprise;\n\nexport {\n OplyticsServiceShell,\n type ServiceShellProps,\n type ServiceShellUser,\n type ServiceShellEnterprise,\n type MenuItem,\n type MenuSection,\n HealthFooter,\n type HealthFooterProps,\n type HealthFooterUser,\n type HealthFooterEnterprise,\n CrossServiceNav,\n type CrossServiceNavProps,\n buildCrossServiceLinks,\n type CrossServiceLink,\n};\n"],"mappings":";;;;;;;;;;;;;;;;AA6EA,IAAM,uBAAuB;","names":[]}