@pablo2410/shared-ui 0.18.1 → 0.18.2
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/README.md +18 -15
- package/dist/{HealthFooter-C5K9s38i.d.ts → HealthFooter-BoOozbWG.d.ts} +4 -4
- package/dist/{chunk-G7XJLTNY.js → chunk-5RY3AJKI.js} +8 -9
- package/dist/{chunk-G7XJLTNY.js.map → chunk-5RY3AJKI.js.map} +1 -1
- package/dist/{chunk-3G43UG63.js → chunk-A6UTXAPS.js} +11 -3
- package/dist/chunk-A6UTXAPS.js.map +1 -0
- package/dist/{chunk-R4NOLCSI.js → chunk-BKGUIIN2.js} +1 -1
- package/dist/{chunk-R4NOLCSI.js.map → chunk-BKGUIIN2.js.map} +1 -1
- package/dist/{chunk-WTR6SNOE.js → chunk-CPRJSW7F.js} +158 -40
- package/dist/chunk-CPRJSW7F.js.map +1 -0
- package/dist/{chunk-IEBZGU6X.js → chunk-DMUQ5WNO.js} +2 -4
- package/dist/chunk-DMUQ5WNO.js.map +1 -0
- package/dist/{chunk-PLCWXXUE.js → chunk-NXE653CX.js} +4 -10
- package/dist/chunk-NXE653CX.js.map +1 -0
- package/dist/{chunk-KXUNOSMZ.js → chunk-TTDKZK6L.js} +193 -91
- package/dist/chunk-TTDKZK6L.js.map +1 -0
- package/dist/components/index.d.ts +7 -8
- package/dist/components/index.js +85 -63
- package/dist/components/index.js.map +1 -1
- package/dist/hierarchy/core.js +1 -1
- package/dist/hierarchy/index.d.ts +3 -3
- package/dist/hierarchy/index.js +5 -5
- package/dist/insights/index.d.ts +8 -8
- package/dist/insights/index.js +150 -61
- package/dist/insights/index.js.map +1 -1
- package/dist/layout/index.d.ts +8 -8
- package/dist/layout/index.js +6 -6
- package/dist/primitives/index.d.ts +286 -287
- package/dist/primitives/index.js +2 -2
- package/dist/rbac/index.js.map +1 -1
- package/dist/reporting/index.d.ts +2 -2
- package/dist/reporting/index.js +42 -8
- package/dist/reporting/index.js.map +1 -1
- package/dist/services/index.js +2 -2
- package/dist/shell/index.d.ts +2 -3
- package/dist/shell/index.js +7 -7
- package/dist/shell/oplytics-shell.css +2 -2
- package/dist/theme/oplytics-theme.css +4 -4
- package/package.json +46 -44
- package/dist/chunk-3G43UG63.js.map +0 -1
- package/dist/chunk-IEBZGU6X.js.map +0 -1
- package/dist/chunk-KXUNOSMZ.js.map +0 -1
- package/dist/chunk-PLCWXXUE.js.map +0 -1
- package/dist/chunk-WTR6SNOE.js.map +0 -1
package/README.md
CHANGED
|
@@ -12,41 +12,44 @@ pnpm add @oplytics/shared-ui
|
|
|
12
12
|
|
|
13
13
|
## Modules
|
|
14
14
|
|
|
15
|
-
| Module
|
|
16
|
-
|
|
17
|
-
| **Layout**
|
|
18
|
-
| **Hierarchy**
|
|
19
|
-
| **RBAC**
|
|
20
|
-
| **Reporting**
|
|
15
|
+
| Module | Import | Description |
|
|
16
|
+
| -------------- | -------------------------------- | ---------------------------------------------------------- |
|
|
17
|
+
| **Layout** | `@oplytics/shared-ui/layout` | Config-driven sidebar, page header, footer, layout factory |
|
|
18
|
+
| **Hierarchy** | `@oplytics/shared-ui/hierarchy` | Org hierarchy types, selection state, enterprise override |
|
|
19
|
+
| **RBAC** | `@oplytics/shared-ui/rbac` | 7-role model, permission checks, error constants |
|
|
20
|
+
| **Reporting** | `@oplytics/shared-ui/reporting` | Incident reporting and feedback toolbar types/config |
|
|
21
21
|
| **Components** | `@oplytics/shared-ui/components` | SharedFooter, NotFound, ErrorBoundary, ViewOnlyBadge types |
|
|
22
|
-
| **Hooks**
|
|
23
|
-
| **Theme**
|
|
22
|
+
| **Hooks** | `@oplytics/shared-ui/hooks` | useMobile, useComposition, usePersistFn |
|
|
23
|
+
| **Theme** | `@oplytics/shared-ui/theme` | Design tokens, color palette, typography, CSS variables |
|
|
24
24
|
|
|
25
25
|
## Quick Start
|
|
26
26
|
|
|
27
27
|
```tsx
|
|
28
28
|
// RBAC — check permissions
|
|
29
|
-
import { hasMinimumRole, ALL_ROLES } from
|
|
29
|
+
import { hasMinimumRole, ALL_ROLES } from "@oplytics/shared-ui/rbac";
|
|
30
30
|
|
|
31
|
-
if (hasMinimumRole(user.role,
|
|
31
|
+
if (hasMinimumRole(user.role, "enterprise_admin")) {
|
|
32
32
|
// show admin features
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
// Hierarchy — enterprise override
|
|
36
|
-
import { applyEnterpriseOverride } from
|
|
36
|
+
import { applyEnterpriseOverride } from "@oplytics/shared-ui/hierarchy";
|
|
37
37
|
|
|
38
38
|
const resolvedUser = applyEnterpriseOverride(user, req.headers.cookie);
|
|
39
39
|
|
|
40
40
|
// Layout — service config
|
|
41
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
SERVICE_CONFIGS,
|
|
43
|
+
defineServiceLayout,
|
|
44
|
+
} from "@oplytics/shared-ui/layout";
|
|
42
45
|
|
|
43
46
|
const myConfig = SERVICE_CONFIGS.sqdcp;
|
|
44
47
|
|
|
45
48
|
// Theme — design tokens
|
|
46
|
-
import { OPLYTICS_COLORS, OPLYTICS_FONTS } from
|
|
49
|
+
import { OPLYTICS_COLORS, OPLYTICS_FONTS } from "@oplytics/shared-ui/theme";
|
|
47
50
|
|
|
48
51
|
// Hooks
|
|
49
|
-
import { useMobile, usePersistFn } from
|
|
52
|
+
import { useMobile, usePersistFn } from "@oplytics/shared-ui/hooks";
|
|
50
53
|
```
|
|
51
54
|
|
|
52
55
|
## Theme CSS
|
|
@@ -54,7 +57,7 @@ import { useMobile, usePersistFn } from '@oplytics/shared-ui/hooks';
|
|
|
54
57
|
Import the canonical Oplytics theme variables:
|
|
55
58
|
|
|
56
59
|
```css
|
|
57
|
-
@import
|
|
60
|
+
@import "@oplytics/shared-ui/theme/oplytics-theme.css";
|
|
58
61
|
```
|
|
59
62
|
|
|
60
63
|
This provides all `--oplytics-*` CSS custom properties for backgrounds, text, accents, borders, spacing, radius, and shadows.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as React from 'react';
|
|
2
2
|
import React__default, { ReactNode, CSSProperties } from 'react';
|
|
3
3
|
import { C as CrossServiceLink } from './crossNav-Bz0RXU_F.js';
|
|
4
4
|
|
|
@@ -216,7 +216,7 @@ interface DashboardLayoutProps {
|
|
|
216
216
|
/** Main content. */
|
|
217
217
|
children: ReactNode;
|
|
218
218
|
}
|
|
219
|
-
declare function DashboardLayout(props: DashboardLayoutProps):
|
|
219
|
+
declare function DashboardLayout(props: DashboardLayoutProps): React.JSX.Element;
|
|
220
220
|
|
|
221
221
|
interface CrossServiceNavProps {
|
|
222
222
|
/** Display-ready links from buildCrossServiceLinks. Renders nothing when empty. */
|
|
@@ -224,7 +224,7 @@ interface CrossServiceNavProps {
|
|
|
224
224
|
/** Section label above the links (hidden when the sidebar is collapsed). */
|
|
225
225
|
label?: string;
|
|
226
226
|
}
|
|
227
|
-
declare function CrossServiceNav({ links, label, }: CrossServiceNavProps):
|
|
227
|
+
declare function CrossServiceNav({ links, label, }: CrossServiceNavProps): React.JSX.Element | null;
|
|
228
228
|
|
|
229
229
|
interface HealthFooterUser {
|
|
230
230
|
/** JWT role string (e.g. "platform_admin"). */
|
|
@@ -255,6 +255,6 @@ interface HealthFooterProps {
|
|
|
255
255
|
/** Override the deployed commit hash (default: build-time __COMMIT_HASH__). */
|
|
256
256
|
commitHash?: string;
|
|
257
257
|
}
|
|
258
|
-
declare function HealthFooter({ user, enterprise, className, style, buildVersion, buildDate, commitHash, }: HealthFooterProps):
|
|
258
|
+
declare function HealthFooter({ user, enterprise, className, style, buildVersion, buildDate, commitHash, }: HealthFooterProps): React.JSX.Element;
|
|
259
259
|
|
|
260
260
|
export { CrossServiceNav as C, DashboardLayout as D, HealthFooter as H, type MenuItem as M, type SharedSidebarConfig as S, type DashboardLayoutEnterprise as a, type DashboardLayoutProps as b, type DashboardLayoutUser as c, type CrossServiceNavProps as d, type HealthFooterEnterprise as e, type HealthFooterProps as f, type HealthFooterUser as g, type MenuSection as h, type SharedSidebarProps as i, getInitials as j, isAdminRole as k, isMenuItemActive as l, useSidebarResize as u };
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-4QJMLQSH.js";
|
|
4
4
|
import {
|
|
5
5
|
appendSelectionParam
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-DMUQ5WNO.js";
|
|
7
7
|
|
|
8
8
|
// src/services/catalog.ts
|
|
9
9
|
var SERVICE_CATALOG = [
|
|
@@ -98,13 +98,9 @@ var SERVICE_CATALOG = [
|
|
|
98
98
|
subdomainLabel: null
|
|
99
99
|
}
|
|
100
100
|
];
|
|
101
|
-
var coreServices = SERVICE_CATALOG.filter(
|
|
102
|
-
(s) => s.category === "core"
|
|
103
|
-
);
|
|
101
|
+
var coreServices = SERVICE_CATALOG.filter((s) => s.category === "core");
|
|
104
102
|
var hubServices = SERVICE_CATALOG.filter((s) => s.category === "hub");
|
|
105
|
-
var liveServices = SERVICE_CATALOG.filter(
|
|
106
|
-
(s) => s.status === "live"
|
|
107
|
-
);
|
|
103
|
+
var liveServices = SERVICE_CATALOG.filter((s) => s.status === "live");
|
|
108
104
|
var inDevServices = SERVICE_CATALOG.filter(
|
|
109
105
|
(s) => s.status === "in-development"
|
|
110
106
|
);
|
|
@@ -122,7 +118,10 @@ function getServiceUrl(service) {
|
|
|
122
118
|
}
|
|
123
119
|
|
|
124
120
|
// src/services/crossNav.ts
|
|
125
|
-
var ADMIN_SEE_ALL_ROLES = [
|
|
121
|
+
var ADMIN_SEE_ALL_ROLES = [
|
|
122
|
+
"platform_admin",
|
|
123
|
+
"enterprise_admin"
|
|
124
|
+
];
|
|
126
125
|
var SLUG_ALIASES = {
|
|
127
126
|
"sqdcp-logic": "sqdcp"
|
|
128
127
|
};
|
|
@@ -159,4 +158,4 @@ export {
|
|
|
159
158
|
normalizeServiceSlug,
|
|
160
159
|
buildCrossServiceLinks
|
|
161
160
|
};
|
|
162
|
-
//# sourceMappingURL=chunk-
|
|
161
|
+
//# sourceMappingURL=chunk-5RY3AJKI.js.map
|
|
@@ -1 +1 @@
|
|
|
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\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 },\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 },\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 },\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 },\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(\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.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 = [\"platform_admin\", \"enterprise_admin\"] 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":[]}
|
|
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\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 },\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 },\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 },\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 },\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\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":[]}
|
|
@@ -20,13 +20,21 @@ function applyEnterpriseOverride(user, cookieHeader) {
|
|
|
20
20
|
// src/hierarchy/scopeHierarchy.ts
|
|
21
21
|
function scopeHierarchyToEnterprise(data, enterpriseId) {
|
|
22
22
|
if (!data || enterpriseId === null) {
|
|
23
|
-
return {
|
|
23
|
+
return {
|
|
24
|
+
enterprises: [],
|
|
25
|
+
businessUnits: [],
|
|
26
|
+
sites: [],
|
|
27
|
+
areas: [],
|
|
28
|
+
assets: []
|
|
29
|
+
};
|
|
24
30
|
}
|
|
25
31
|
const sites = data.sites.filter((s) => s.enterpriseId === enterpriseId);
|
|
26
32
|
const siteIds = new Set(sites.map((s) => s.id));
|
|
27
33
|
return {
|
|
28
34
|
enterprises: data.enterprises.filter((e) => e.id === enterpriseId),
|
|
29
|
-
businessUnits: data.businessUnits.filter(
|
|
35
|
+
businessUnits: data.businessUnits.filter(
|
|
36
|
+
(bu) => bu.enterpriseId === enterpriseId
|
|
37
|
+
),
|
|
30
38
|
sites,
|
|
31
39
|
areas: data.areas.filter((a) => siteIds.has(a.siteId)),
|
|
32
40
|
assets: data.assets.filter((a) => siteIds.has(a.siteId))
|
|
@@ -38,4 +46,4 @@ export {
|
|
|
38
46
|
applyEnterpriseOverride,
|
|
39
47
|
scopeHierarchyToEnterprise
|
|
40
48
|
};
|
|
41
|
-
//# sourceMappingURL=chunk-
|
|
49
|
+
//# sourceMappingURL=chunk-A6UTXAPS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hierarchy/enterpriseOverride.ts","../src/hierarchy/scopeHierarchy.ts"],"sourcesContent":["/**\n * Enterprise Override — shared helper for platform_admin enterprise switching.\n *\n * When a platform_admin switches enterprise in the Portal UI, a cookie\n * `oplytics-active-enterprise` is set with the selected enterprise ID.\n * This cookie is shared across all *.oplytics.digital subdomains.\n *\n * Each subdomain's createContext should call applyEnterpriseOverride()\n * to override the user's DB-stored enterpriseId with the cookie value.\n *\n * Used by: Portal, SQDCP, OEE Manager, Action Manager, Business Hub, Connect\n */\n\n/**\n * Cookie name for the platform_admin active enterprise override.\n * Shared across all *.oplytics.digital subdomains via the .oplytics.digital domain.\n */\nexport const ACTIVE_ENTERPRISE_COOKIE = \"oplytics-active-enterprise\";\n\n/**\n * Parse a single cookie value from a cookie header string.\n */\nfunction parseCookieValue(cookieHeader: string, name: string): string | null {\n const match = cookieHeader\n .split(\";\")\n .map(c => c.trim())\n .find(c => c.startsWith(`${name}=`));\n if (!match) return null;\n return decodeURIComponent(match.slice(name.length + 1));\n}\n\n/**\n * Apply the enterprise override for platform_admin users.\n *\n * If the user is a platform_admin and has the active enterprise cookie set,\n * returns a shallow copy of the user with the overridden enterpriseId.\n * Otherwise returns the user unchanged.\n *\n * @param user - The authenticated user (or null)\n * @param cookieHeader - The raw Cookie header string from the request\n * @returns The user with potentially overridden enterpriseId\n */\nexport function applyEnterpriseOverride<\n T extends { role: string; enterpriseId?: number | null },\n>(user: T | null, cookieHeader: string): T | null {\n if (!user || user.role !== \"platform_admin\") return user;\n\n const activeEnterprise = parseCookieValue(\n cookieHeader,\n ACTIVE_ENTERPRISE_COOKIE\n );\n if (!activeEnterprise) return user;\n\n const parsedId = parseInt(activeEnterprise, 10);\n if (isNaN(parsedId) || parsedId <= 0) return user;\n\n // Return a shallow copy with the overridden enterpriseId\n return { ...user, enterpriseId: parsedId };\n}\n","/**\n * scopeHierarchyToEnterprise — the single source of truth for enterprise isolation.\n *\n * The Portal Service API (`GET /api/service/hierarchy`) is called with a\n * platform-level service key, so it returns every enterprise's hierarchy. This\n * function filters that payload down to ONE enterprise before it reaches a\n * subdomain client. It is pure and isomorphic (no I/O), so the same rule runs in\n * the fetch service, in tests, and anywhere else scoping is needed.\n *\n * Policy (deliberate — do not \"helpfully\" relax it):\n * - `null` data → empty hierarchy.\n * - `null` enterpriseId → empty hierarchy. Enterprise selection is owned solely\n * by the Portal switcher; a caller with no resolved enterprise (e.g. a\n * platform admin who hasn't picked one in Portal yet) sees nothing until they\n * select. Every non-admin always has exactly one enterprise, so they are\n * unaffected. Admin/Portal tooling that genuinely needs the full tree must use\n * the unscoped fetch, never this function.\n */\nimport type { OrgHierarchyData } from \"./orgHierarchy\";\n\nexport function scopeHierarchyToEnterprise(\n data: OrgHierarchyData | null,\n enterpriseId: number | null\n): OrgHierarchyData {\n if (!data || enterpriseId === null) {\n return {\n enterprises: [],\n businessUnits: [],\n sites: [],\n areas: [],\n assets: [],\n };\n }\n\n const sites = data.sites.filter(s => s.enterpriseId === enterpriseId);\n const siteIds = new Set(sites.map(s => s.id));\n\n return {\n enterprises: data.enterprises.filter(e => e.id === enterpriseId),\n businessUnits: data.businessUnits.filter(\n bu => bu.enterpriseId === enterpriseId\n ),\n sites,\n areas: data.areas.filter(a => siteIds.has(a.siteId)),\n assets: data.assets.filter(a => siteIds.has(a.siteId)),\n };\n}\n"],"mappings":";AAiBO,IAAM,2BAA2B;AAKxC,SAAS,iBAAiB,cAAsB,MAA6B;AAC3E,QAAM,QAAQ,aACX,MAAM,GAAG,EACT,IAAI,OAAK,EAAE,KAAK,CAAC,EACjB,KAAK,OAAK,EAAE,WAAW,GAAG,IAAI,GAAG,CAAC;AACrC,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,mBAAmB,MAAM,MAAM,KAAK,SAAS,CAAC,CAAC;AACxD;AAaO,SAAS,wBAEd,MAAgB,cAAgC;AAChD,MAAI,CAAC,QAAQ,KAAK,SAAS,iBAAkB,QAAO;AAEpD,QAAM,mBAAmB;AAAA,IACvB;AAAA,IACA;AAAA,EACF;AACA,MAAI,CAAC,iBAAkB,QAAO;AAE9B,QAAM,WAAW,SAAS,kBAAkB,EAAE;AAC9C,MAAI,MAAM,QAAQ,KAAK,YAAY,EAAG,QAAO;AAG7C,SAAO,EAAE,GAAG,MAAM,cAAc,SAAS;AAC3C;;;ACtCO,SAAS,2BACd,MACA,cACkB;AAClB,MAAI,CAAC,QAAQ,iBAAiB,MAAM;AAClC,WAAO;AAAA,MACL,aAAa,CAAC;AAAA,MACd,eAAe,CAAC;AAAA,MAChB,OAAO,CAAC;AAAA,MACR,OAAO,CAAC;AAAA,MACR,QAAQ,CAAC;AAAA,IACX;AAAA,EACF;AAEA,QAAM,QAAQ,KAAK,MAAM,OAAO,OAAK,EAAE,iBAAiB,YAAY;AACpE,QAAM,UAAU,IAAI,IAAI,MAAM,IAAI,OAAK,EAAE,EAAE,CAAC;AAE5C,SAAO;AAAA,IACL,aAAa,KAAK,YAAY,OAAO,OAAK,EAAE,OAAO,YAAY;AAAA,IAC/D,eAAe,KAAK,cAAc;AAAA,MAChC,QAAM,GAAG,iBAAiB;AAAA,IAC5B;AAAA,IACA;AAAA,IACA,OAAO,KAAK,MAAM,OAAO,OAAK,QAAQ,IAAI,EAAE,MAAM,CAAC;AAAA,IACnD,QAAQ,KAAK,OAAO,OAAO,OAAK,QAAQ,IAAI,EAAE,MAAM,CAAC;AAAA,EACvD;AACF;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/primitives/cn.ts","../src/primitives/button.tsx","../src/primitives/dialog.tsx","../src/primitives/label.tsx","../src/primitives/radio-group.tsx"],"sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import * as React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"./cn\";\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\",\n {\n variants: {\n variant: {\n default:\n \"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60\",\n outline:\n \"border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground\",\n secondary:\n \"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80\",\n ghost:\n \"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-9 px-4 py-2 has-[>svg]:px-3\",\n sm: \"h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5\",\n lg: \"h-10 rounded-md px-6 has-[>svg]:px-4\",\n icon: \"size-9\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n);\nfunction Button({\n className,\n variant,\n size,\n asChild = false,\n ...props\n}: React.ComponentProps<\"button\"> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean;\n }) {\n const Comp = asChild ? Slot : \"button\";\n return (\n <Comp\n data-slot=\"button\"\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n );\n}\nexport { Button, buttonVariants };\n","import { cn } from \"./cn\";\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport { XIcon } from \"lucide-react\";\nimport * as React from \"react\";\n\n// Context to track composition state across dialog children\nconst DialogCompositionContext = React.createContext<{\n isComposing: () => boolean;\n setComposing: (composing: boolean) => void;\n justEndedComposing: () => boolean;\n markCompositionEnd: () => void;\n}>({\n isComposing: () => false,\n setComposing: () => {},\n justEndedComposing: () => false,\n markCompositionEnd: () => {},\n});\n\nexport const useDialogComposition = () =>\n React.useContext(DialogCompositionContext);\n\nfunction Dialog({\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Root>) {\n const composingRef = React.useRef(false);\n const justEndedRef = React.useRef(false);\n const endTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const contextValue = React.useMemo(\n () => ({\n isComposing: () => composingRef.current,\n setComposing: (composing: boolean) => {\n composingRef.current = composing;\n },\n justEndedComposing: () => justEndedRef.current,\n markCompositionEnd: () => {\n justEndedRef.current = true;\n if (endTimerRef.current) {\n clearTimeout(endTimerRef.current);\n }\n endTimerRef.current = setTimeout(() => {\n justEndedRef.current = false;\n }, 150);\n },\n }),\n []\n );\n\n return (\n <DialogCompositionContext.Provider value={contextValue}>\n <DialogPrimitive.Root data-slot=\"dialog\" {...props} />\n </DialogCompositionContext.Provider>\n );\n}\n\nfunction DialogTrigger({\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {\n return <DialogPrimitive.Trigger data-slot=\"dialog-trigger\" {...props} />;\n}\n\nfunction DialogPortal({\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Portal>) {\n return <DialogPrimitive.Portal data-slot=\"dialog-portal\" {...props} />;\n}\n\nfunction DialogClose({\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Close>) {\n return <DialogPrimitive.Close data-slot=\"dialog-close\" {...props} />;\n}\n\nfunction DialogOverlay({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {\n return (\n <DialogPrimitive.Overlay\n data-slot=\"dialog-overlay\"\n className={cn(\n \"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50\",\n className\n )}\n {...props}\n />\n );\n}\n\nDialogOverlay.displayName = \"DialogOverlay\";\n\nfunction DialogContent({\n className,\n children,\n showCloseButton = true,\n onEscapeKeyDown,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Content> & {\n showCloseButton?: boolean;\n}) {\n const { isComposing } = useDialogComposition();\n\n const handleEscapeKeyDown = React.useCallback(\n (e: KeyboardEvent) => {\n // Check both the native isComposing property and our context state\n // This handles Safari's timing issues with composition events\n const isCurrentlyComposing = (e as any).isComposing || isComposing();\n\n // If IME is composing, prevent dialog from closing\n if (isCurrentlyComposing) {\n e.preventDefault();\n return;\n }\n\n // Call user's onEscapeKeyDown if provided\n onEscapeKeyDown?.(e);\n },\n [isComposing, onEscapeKeyDown]\n );\n\n return (\n <DialogPortal data-slot=\"dialog-portal\">\n <DialogOverlay />\n <DialogPrimitive.Content\n data-slot=\"dialog-content\"\n className={cn(\n \"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg\",\n className\n )}\n onEscapeKeyDown={handleEscapeKeyDown}\n {...props}\n >\n {children}\n {showCloseButton && (\n <DialogPrimitive.Close\n data-slot=\"dialog-close\"\n className=\"ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\"\n >\n <XIcon />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n )}\n </DialogPrimitive.Content>\n </DialogPortal>\n );\n}\n\nfunction DialogHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"dialog-header\"\n className={cn(\"flex flex-col gap-2 text-center sm:text-left\", className)}\n {...props}\n />\n );\n}\n\nfunction DialogFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"dialog-footer\"\n className={cn(\n \"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\",\n className\n )}\n {...props}\n />\n );\n}\n\nfunction DialogTitle({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Title>) {\n return (\n <DialogPrimitive.Title\n data-slot=\"dialog-title\"\n className={cn(\"text-lg leading-none font-semibold\", className)}\n {...props}\n />\n );\n}\n\nfunction DialogDescription({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Description>) {\n return (\n <DialogPrimitive.Description\n data-slot=\"dialog-description\"\n className={cn(\"text-muted-foreground text-sm\", className)}\n {...props}\n />\n );\n}\n\nexport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogOverlay,\n DialogPortal,\n DialogTitle,\n DialogTrigger\n};\n\n","import * as React from \"react\";\nimport * as LabelPrimitive from \"@radix-ui/react-label\";\nimport { cn } from \"./cn\";\n\nfunction Label({\n className,\n ...props\n}: React.ComponentProps<typeof LabelPrimitive.Root>) {\n return (\n <LabelPrimitive.Root\n data-slot=\"label\"\n className={cn(\n \"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport { Label };\n","import * as React from \"react\";\nimport * as RadioGroupPrimitive from \"@radix-ui/react-radio-group\";\nimport { CircleIcon } from \"lucide-react\";\n\nimport { cn } from \"./cn\";\n\nfunction RadioGroup({\n className,\n ...props\n}: React.ComponentProps<typeof RadioGroupPrimitive.Root>) {\n return (\n <RadioGroupPrimitive.Root\n data-slot=\"radio-group\"\n className={cn(\"grid gap-3\", className)}\n {...props}\n />\n );\n}\n\nfunction RadioGroupItem({\n className,\n ...props\n}: React.ComponentProps<typeof RadioGroupPrimitive.Item>) {\n return (\n <RadioGroupPrimitive.Item\n data-slot=\"radio-group-item\"\n className={cn(\n \"border-input text-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50\",\n className\n )}\n {...props}\n >\n <RadioGroupPrimitive.Indicator\n data-slot=\"radio-group-indicator\"\n className=\"relative flex items-center justify-center\"\n >\n <CircleIcon className=\"fill-primary absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2\" />\n </RadioGroupPrimitive.Indicator>\n </RadioGroupPrimitive.Item>\n );\n}\n\nexport { RadioGroup, RadioGroupItem };\n"],"mappings":"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","names":["jsx","jsx","jsx"]}
|
|
1
|
+
{"version":3,"sources":["../src/primitives/cn.ts","../src/primitives/button.tsx","../src/primitives/dialog.tsx","../src/primitives/label.tsx","../src/primitives/radio-group.tsx"],"sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import * as React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"./cn\";\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\",\n {\n variants: {\n variant: {\n default:\n \"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60\",\n outline:\n \"border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground\",\n secondary:\n \"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80\",\n ghost:\n \"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-9 px-4 py-2 has-[>svg]:px-3\",\n sm: \"h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5\",\n lg: \"h-10 rounded-md px-6 has-[>svg]:px-4\",\n icon: \"size-9\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n);\nfunction Button({\n className,\n variant,\n size,\n asChild = false,\n ...props\n}: React.ComponentProps<\"button\"> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean;\n }) {\n const Comp = asChild ? Slot : \"button\";\n return (\n <Comp\n data-slot=\"button\"\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n );\n}\nexport { Button, buttonVariants };\n","import { cn } from \"./cn\";\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport { XIcon } from \"lucide-react\";\nimport * as React from \"react\";\n\n// Context to track composition state across dialog children\nconst DialogCompositionContext = React.createContext<{\n isComposing: () => boolean;\n setComposing: (composing: boolean) => void;\n justEndedComposing: () => boolean;\n markCompositionEnd: () => void;\n}>({\n isComposing: () => false,\n setComposing: () => {},\n justEndedComposing: () => false,\n markCompositionEnd: () => {},\n});\n\nexport const useDialogComposition = () =>\n React.useContext(DialogCompositionContext);\n\nfunction Dialog({\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Root>) {\n const composingRef = React.useRef(false);\n const justEndedRef = React.useRef(false);\n const endTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const contextValue = React.useMemo(\n () => ({\n isComposing: () => composingRef.current,\n setComposing: (composing: boolean) => {\n composingRef.current = composing;\n },\n justEndedComposing: () => justEndedRef.current,\n markCompositionEnd: () => {\n justEndedRef.current = true;\n if (endTimerRef.current) {\n clearTimeout(endTimerRef.current);\n }\n endTimerRef.current = setTimeout(() => {\n justEndedRef.current = false;\n }, 150);\n },\n }),\n []\n );\n\n return (\n <DialogCompositionContext.Provider value={contextValue}>\n <DialogPrimitive.Root data-slot=\"dialog\" {...props} />\n </DialogCompositionContext.Provider>\n );\n}\n\nfunction DialogTrigger({\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {\n return <DialogPrimitive.Trigger data-slot=\"dialog-trigger\" {...props} />;\n}\n\nfunction DialogPortal({\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Portal>) {\n return <DialogPrimitive.Portal data-slot=\"dialog-portal\" {...props} />;\n}\n\nfunction DialogClose({\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Close>) {\n return <DialogPrimitive.Close data-slot=\"dialog-close\" {...props} />;\n}\n\nfunction DialogOverlay({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {\n return (\n <DialogPrimitive.Overlay\n data-slot=\"dialog-overlay\"\n className={cn(\n \"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50\",\n className\n )}\n {...props}\n />\n );\n}\n\nDialogOverlay.displayName = \"DialogOverlay\";\n\nfunction DialogContent({\n className,\n children,\n showCloseButton = true,\n onEscapeKeyDown,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Content> & {\n showCloseButton?: boolean;\n}) {\n const { isComposing } = useDialogComposition();\n\n const handleEscapeKeyDown = React.useCallback(\n (e: KeyboardEvent) => {\n // Check both the native isComposing property and our context state\n // This handles Safari's timing issues with composition events\n const isCurrentlyComposing = (e as any).isComposing || isComposing();\n\n // If IME is composing, prevent dialog from closing\n if (isCurrentlyComposing) {\n e.preventDefault();\n return;\n }\n\n // Call user's onEscapeKeyDown if provided\n onEscapeKeyDown?.(e);\n },\n [isComposing, onEscapeKeyDown]\n );\n\n return (\n <DialogPortal data-slot=\"dialog-portal\">\n <DialogOverlay />\n <DialogPrimitive.Content\n data-slot=\"dialog-content\"\n className={cn(\n \"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg\",\n className\n )}\n onEscapeKeyDown={handleEscapeKeyDown}\n {...props}\n >\n {children}\n {showCloseButton && (\n <DialogPrimitive.Close\n data-slot=\"dialog-close\"\n className=\"ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\"\n >\n <XIcon />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n )}\n </DialogPrimitive.Content>\n </DialogPortal>\n );\n}\n\nfunction DialogHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"dialog-header\"\n className={cn(\"flex flex-col gap-2 text-center sm:text-left\", className)}\n {...props}\n />\n );\n}\n\nfunction DialogFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"dialog-footer\"\n className={cn(\n \"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\",\n className\n )}\n {...props}\n />\n );\n}\n\nfunction DialogTitle({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Title>) {\n return (\n <DialogPrimitive.Title\n data-slot=\"dialog-title\"\n className={cn(\"text-lg leading-none font-semibold\", className)}\n {...props}\n />\n );\n}\n\nfunction DialogDescription({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Description>) {\n return (\n <DialogPrimitive.Description\n data-slot=\"dialog-description\"\n className={cn(\"text-muted-foreground text-sm\", className)}\n {...props}\n />\n );\n}\n\nexport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogOverlay,\n DialogPortal,\n DialogTitle,\n DialogTrigger,\n};\n","import * as React from \"react\";\nimport * as LabelPrimitive from \"@radix-ui/react-label\";\nimport { cn } from \"./cn\";\n\nfunction Label({\n className,\n ...props\n}: React.ComponentProps<typeof LabelPrimitive.Root>) {\n return (\n <LabelPrimitive.Root\n data-slot=\"label\"\n className={cn(\n \"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport { Label };\n","import * as React from \"react\";\nimport * as RadioGroupPrimitive from \"@radix-ui/react-radio-group\";\nimport { CircleIcon } from \"lucide-react\";\n\nimport { cn } from \"./cn\";\n\nfunction RadioGroup({\n className,\n ...props\n}: React.ComponentProps<typeof RadioGroupPrimitive.Root>) {\n return (\n <RadioGroupPrimitive.Root\n data-slot=\"radio-group\"\n className={cn(\"grid gap-3\", className)}\n {...props}\n />\n );\n}\n\nfunction RadioGroupItem({\n className,\n ...props\n}: React.ComponentProps<typeof RadioGroupPrimitive.Item>) {\n return (\n <RadioGroupPrimitive.Item\n data-slot=\"radio-group-item\"\n className={cn(\n \"border-input text-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50\",\n className\n )}\n {...props}\n >\n <RadioGroupPrimitive.Indicator\n data-slot=\"radio-group-indicator\"\n className=\"relative flex items-center justify-center\"\n >\n <CircleIcon className=\"fill-primary absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2\" />\n </RadioGroupPrimitive.Indicator>\n </RadioGroupPrimitive.Item>\n );\n}\n\nexport { RadioGroup, RadioGroupItem };\n"],"mappings":"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","names":["jsx","jsx","jsx"]}
|