@pablo2410/shared-ui 0.4.1 → 0.6.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.
@@ -1,874 +1,24 @@
1
1
  import {
2
- cn
3
- } from "../chunk-JT3XLKKD.js";
4
-
5
- // src/layout/SharedSidebar.tsx
6
- import { useEffect, useRef, useState } from "react";
7
- var ADMIN_ROLES = ["enterprise_admin", "platform_admin", "admin", "superuser"];
8
- function isAdminRole(role) {
9
- return !!role && ADMIN_ROLES.includes(role);
10
- }
11
- function getInitials(name) {
12
- if (!name) return "U";
13
- return name.split(" ").map((n) => n[0]).join("").toUpperCase().slice(0, 2);
14
- }
15
- function isMenuItemActive(itemPath, location, basePath) {
16
- if (basePath) {
17
- if (itemPath === basePath) return location === basePath;
18
- return location.startsWith(itemPath);
19
- }
20
- if (itemPath === "/") return location === "/";
21
- return location.startsWith(itemPath);
22
- }
23
- function useSidebarResize(config) {
24
- const [width, setWidth] = useState(() => {
25
- if (typeof window === "undefined") return config.defaultWidth;
26
- const saved = localStorage.getItem(config.storageKey);
27
- return saved ? parseInt(saved, 10) : config.defaultWidth;
28
- });
29
- const [isResizing, setIsResizing] = useState(false);
30
- const startX = useRef(0);
31
- const startWidth = useRef(config.defaultWidth);
32
- useEffect(() => {
33
- localStorage.setItem(config.storageKey, String(width));
34
- }, [width, config.storageKey]);
35
- useEffect(() => {
36
- if (!isResizing) return;
37
- const onMove = (e) => {
38
- const newWidth = Math.min(
39
- config.maxWidth,
40
- Math.max(config.minWidth, startWidth.current + (e.clientX - startX.current))
41
- );
42
- setWidth(newWidth);
43
- };
44
- const onUp = () => {
45
- setIsResizing(false);
46
- };
47
- document.addEventListener("mousemove", onMove);
48
- document.addEventListener("mouseup", onUp);
49
- document.body.style.cursor = "col-resize";
50
- document.body.style.userSelect = "none";
51
- return () => {
52
- document.removeEventListener("mousemove", onMove);
53
- document.removeEventListener("mouseup", onUp);
54
- document.body.style.cursor = "";
55
- document.body.style.userSelect = "";
56
- };
57
- }, [isResizing, config.minWidth, config.maxWidth]);
58
- const startResize = (e) => {
59
- setIsResizing(true);
60
- startX.current = e.clientX;
61
- startWidth.current = width;
62
- };
63
- return { width, isResizing, startResize };
64
- }
65
-
66
- // src/layout/DashboardLayout.tsx
67
- import {
68
- ChevronLeft,
69
- ChevronRight,
70
- LogOut,
71
- Menu,
72
- X,
73
- ExternalLink
74
- } from "lucide-react";
75
- import {
76
- useState as useState2,
77
- useCallback
78
- } from "react";
79
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
80
- function SidebarItem({
81
- item,
82
- isActive,
83
- collapsed,
84
- onNavigate
85
- }) {
86
- const Icon = item.icon;
87
- return /* @__PURE__ */ jsxs(
88
- "button",
89
- {
90
- onClick: () => onNavigate(item.path),
91
- className: cn(
92
- "w-full flex items-center gap-3 rounded-lg transition-all duration-200",
93
- "text-sm font-medium outline-none",
94
- "focus-visible:ring-1 focus-visible:ring-[#8C34E9]",
95
- collapsed ? "px-3 py-2.5 justify-center" : "px-3 py-2",
96
- isActive ? "bg-[#8C34E9]/15 text-[#A855F7] border-l-2 border-[#8C34E9]" : "text-[#8890A0] hover:bg-[#1E2738] hover:text-[#E2E8F0]"
97
- ),
98
- title: collapsed ? item.label : void 0,
99
- children: [
100
- Icon && /* @__PURE__ */ jsx(Icon, { className: cn("shrink-0", collapsed ? "h-5 w-5" : "h-4 w-4") }),
101
- !collapsed && /* @__PURE__ */ jsx("span", { className: "truncate", children: item.label }),
102
- !collapsed && item.badge && /* @__PURE__ */ jsx("span", { className: "ml-auto shrink-0 text-[10px] font-semibold px-1.5 py-0.5 rounded-full bg-[#8C34E9]/20 text-[#A855F7]", children: item.badge })
103
- ]
104
- }
105
- );
106
- }
107
- function SidebarSection({
108
- section,
109
- activePath,
110
- collapsed,
111
- onNavigate
112
- }) {
113
- return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
114
- !collapsed && section.title && /* @__PURE__ */ jsx("p", { className: "px-3 py-1 text-[10px] font-semibold uppercase tracking-wider text-[#596475]", children: section.title }),
115
- collapsed && section.title && /* @__PURE__ */ jsx("div", { className: "mx-auto w-6 border-t border-[#1E2738] my-2" }),
116
- section.items.map((item) => /* @__PURE__ */ jsx(
117
- SidebarItem,
118
- {
119
- item,
120
- isActive: isMenuItemActive(item.path, activePath),
121
- collapsed,
122
- onNavigate
123
- },
124
- item.path
125
- ))
126
- ] });
127
- }
128
- function UserProfile({
129
- user,
130
- collapsed,
131
- onLogout
132
- }) {
133
- const initials = getInitials(user.name);
134
- return /* @__PURE__ */ jsxs(
135
- "div",
136
- {
137
- className: cn(
138
- "border-t border-[#1E2738] p-3",
139
- collapsed ? "flex flex-col items-center gap-2" : "flex items-center gap-3"
140
- ),
141
- children: [
142
- user.avatarUrl ? /* @__PURE__ */ jsx(
143
- "img",
144
- {
145
- src: user.avatarUrl,
146
- alt: user.name,
147
- className: "h-8 w-8 rounded-full shrink-0 object-cover"
148
- }
149
- ) : /* @__PURE__ */ jsx("div", { className: "h-8 w-8 rounded-full shrink-0 bg-[#8C34E9]/20 flex items-center justify-center", children: /* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-[#A855F7]", children: initials }) }),
150
- !collapsed && /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
151
- /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-[#E2E8F0] truncate", children: user.name }),
152
- user.email && /* @__PURE__ */ jsx("p", { className: "text-[10px] text-[#596475] truncate", children: user.email }),
153
- user.role && /* @__PURE__ */ jsx("p", { className: "text-[10px] text-[#8C34E9] capitalize", children: user.role })
154
- ] }),
155
- onLogout && /* @__PURE__ */ jsx(
156
- "button",
157
- {
158
- onClick: onLogout,
159
- className: cn(
160
- "shrink-0 p-1.5 rounded-md",
161
- "text-[#596475] hover:text-[#E2E8F0] hover:bg-[#1E2738]",
162
- "transition-colors cursor-pointer",
163
- "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[#8C34E9]"
164
- ),
165
- title: "Sign out",
166
- children: /* @__PURE__ */ jsx(LogOut, { className: "h-4 w-4" })
167
- }
168
- )
169
- ]
170
- }
171
- );
172
- }
173
- function DashboardLayout({
174
- serviceName,
175
- serviceIcon,
176
- menuSections,
177
- activePath,
178
- onNavigate,
179
- user,
180
- isAuthenticated = true,
181
- isLoading = false,
182
- onLogout,
183
- onLogin,
184
- showBackToPortal = false,
185
- portalUrl = "https://portal.oplytics.digital",
186
- isAdmin = false,
187
- adminSections,
188
- hierarchyNavigator,
189
- reportingToolbar,
190
- loadingSkeleton,
191
- children,
192
- className
193
- }) {
194
- const [collapsed, setCollapsed] = useState2(false);
195
- const [mobileOpen, setMobileOpen] = useState2(false);
196
- const { width: sidebarWidth } = useSidebarResize({
197
- storageKey: `oplytics-sidebar-${serviceName}`,
198
- defaultWidth: collapsed ? 72 : 260,
199
- minWidth: 72,
200
- maxWidth: 480
201
- });
202
- const toggleCollapse = useCallback(() => setCollapsed((c) => !c), []);
203
- const toggleMobile = useCallback(() => setMobileOpen((o) => !o), []);
204
- const closeMobile = useCallback(() => setMobileOpen(false), []);
205
- const handleNavigate = useCallback(
206
- (path) => {
207
- onNavigate(path);
208
- closeMobile();
209
- },
210
- [onNavigate, closeMobile]
211
- );
212
- if (isLoading && loadingSkeleton) {
213
- return /* @__PURE__ */ jsx(Fragment, { children: loadingSkeleton });
214
- }
215
- if (!isLoading && !isAuthenticated && onLogin) {
216
- onLogin();
217
- return loadingSkeleton ? /* @__PURE__ */ jsx(Fragment, { children: loadingSkeleton }) : null;
218
- }
219
- return /* @__PURE__ */ jsxs("div", { className: cn("min-h-screen flex", "bg-[#0A0E1A]", className), children: [
220
- mobileOpen && /* @__PURE__ */ jsx(
221
- "div",
222
- {
223
- className: "fixed inset-0 z-40 bg-black/60 lg:hidden",
224
- onClick: closeMobile
225
- }
226
- ),
227
- /* @__PURE__ */ jsxs(
228
- "aside",
229
- {
230
- className: cn(
231
- "fixed top-0 left-0 h-full z-50 flex flex-col",
232
- "bg-[#0D1220] border-r border-[#1E2738]",
233
- "transition-all duration-300",
234
- // Mobile: slide in/out
235
- "lg:relative lg:translate-x-0",
236
- mobileOpen ? "translate-x-0" : "-translate-x-full"
237
- ),
238
- style: { width: sidebarWidth },
239
- children: [
240
- /* @__PURE__ */ jsxs("div", { className: cn(
241
- "flex items-center gap-2 p-4 border-b border-[#1E2738]",
242
- collapsed && "justify-center px-2"
243
- ), children: [
244
- serviceIcon && /* @__PURE__ */ jsx("div", { className: "shrink-0 text-[#8C34E9]", children: serviceIcon }),
245
- !collapsed && /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-[#E2E8F0] truncate font-[Montserrat,sans-serif]", children: serviceName }),
246
- /* @__PURE__ */ jsx(
247
- "button",
248
- {
249
- onClick: toggleCollapse,
250
- className: cn(
251
- "hidden lg:flex shrink-0 p-1 rounded-md ml-auto",
252
- "text-[#596475] hover:text-[#E2E8F0] hover:bg-[#1E2738]",
253
- "transition-colors cursor-pointer",
254
- collapsed && "ml-0"
255
- ),
256
- children: collapsed ? /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
257
- }
258
- ),
259
- /* @__PURE__ */ jsx(
260
- "button",
261
- {
262
- onClick: closeMobile,
263
- className: "lg:hidden shrink-0 p-1 rounded-md ml-auto text-[#596475] hover:text-[#E2E8F0]",
264
- children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
265
- }
266
- )
267
- ] }),
268
- hierarchyNavigator && !collapsed && /* @__PURE__ */ jsx("div", { className: "px-3 py-2 border-b border-[#1E2738]", children: hierarchyNavigator }),
269
- showBackToPortal && /* @__PURE__ */ jsx("div", { className: cn("px-3 py-2", collapsed && "px-2"), children: /* @__PURE__ */ jsxs(
270
- "a",
271
- {
272
- href: portalUrl,
273
- className: cn(
274
- "flex items-center gap-2 px-3 py-1.5 rounded-md text-xs",
275
- "text-[#596475] hover:text-[#8890A0] hover:bg-[#1E2738]",
276
- "transition-colors",
277
- collapsed && "justify-center px-2"
278
- ),
279
- children: [
280
- /* @__PURE__ */ jsx(ExternalLink, { className: "h-3.5 w-3.5 shrink-0" }),
281
- !collapsed && /* @__PURE__ */ jsx("span", { children: "Back to Portal" })
282
- ]
283
- }
284
- ) }),
285
- /* @__PURE__ */ jsxs("nav", { className: "flex-1 overflow-y-auto px-3 py-3 space-y-4", children: [
286
- menuSections.map((section, idx) => /* @__PURE__ */ jsx(
287
- SidebarSection,
288
- {
289
- section,
290
- activePath,
291
- collapsed,
292
- onNavigate: handleNavigate
293
- },
294
- section.title || `section-${idx}`
295
- )),
296
- isAdmin && adminSections && adminSections.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
297
- !collapsed && /* @__PURE__ */ jsx("div", { className: "mx-3 border-t border-[#1E2738] my-2" }),
298
- adminSections.map((section, idx) => /* @__PURE__ */ jsx(
299
- SidebarSection,
300
- {
301
- section,
302
- activePath,
303
- collapsed,
304
- onNavigate: handleNavigate
305
- },
306
- section.title || `admin-${idx}`
307
- ))
308
- ] })
309
- ] }),
310
- user && /* @__PURE__ */ jsx(
311
- UserProfile,
312
- {
313
- user,
314
- collapsed,
315
- onLogout
316
- }
317
- )
318
- ]
319
- }
320
- ),
321
- /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col min-w-0", children: [
322
- /* @__PURE__ */ jsxs("header", { className: "lg:hidden flex items-center gap-3 px-4 py-2.5 border-b border-[#1E2738] bg-[#0D1220]", children: [
323
- /* @__PURE__ */ jsx(
324
- "button",
325
- {
326
- onClick: toggleMobile,
327
- className: "p-1.5 rounded-md text-[#8890A0] hover:text-[#E2E8F0] hover:bg-[#1E2738]",
328
- children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
329
- }
330
- ),
331
- /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-[#E2E8F0] font-[Montserrat,sans-serif]", children: serviceName }),
332
- reportingToolbar && /* @__PURE__ */ jsx("div", { className: "ml-auto", children: reportingToolbar })
333
- ] }),
334
- reportingToolbar && /* @__PURE__ */ jsx("div", { className: "hidden lg:flex items-center justify-end px-4 py-1.5 border-b border-[#1E2738] bg-[#0D1220]/50", children: reportingToolbar }),
335
- /* @__PURE__ */ jsx("main", { className: "flex-1 overflow-y-auto", children })
336
- ] })
337
- ] });
338
- }
339
-
340
- // src/layout/DashboardLayoutSkeleton.tsx
341
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
342
- function Skeleton({ className }) {
343
- return /* @__PURE__ */ jsx2(
344
- "div",
345
- {
346
- className: cn(
347
- "animate-pulse rounded-md bg-[#1E2738]",
348
- className
349
- )
350
- }
351
- );
352
- }
353
- function DashboardLayoutSkeleton({ className }) {
354
- return /* @__PURE__ */ jsxs2("div", { className: cn("flex min-h-screen bg-[#0A0E1A]", className), children: [
355
- /* @__PURE__ */ jsxs2("div", { className: "w-[260px] border-r border-[#1E2738] bg-[#0D1220] p-4 space-y-6", children: [
356
- /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-3 px-2", children: [
357
- /* @__PURE__ */ jsx2(Skeleton, { className: "h-8 w-8 rounded-md" }),
358
- /* @__PURE__ */ jsx2(Skeleton, { className: "h-4 w-24" })
359
- ] }),
360
- /* @__PURE__ */ jsxs2("div", { className: "space-y-2 px-2", children: [
361
- /* @__PURE__ */ jsx2(Skeleton, { className: "h-9 w-full rounded-lg" }),
362
- /* @__PURE__ */ jsx2(Skeleton, { className: "h-9 w-full rounded-lg" }),
363
- /* @__PURE__ */ jsx2(Skeleton, { className: "h-9 w-full rounded-lg" }),
364
- /* @__PURE__ */ jsx2(Skeleton, { className: "h-9 w-full rounded-lg" })
365
- ] }),
366
- /* @__PURE__ */ jsxs2("div", { className: "px-2", children: [
367
- /* @__PURE__ */ jsx2(Skeleton, { className: "h-3 w-16 mb-3" }),
368
- /* @__PURE__ */ jsx2(Skeleton, { className: "h-9 w-full rounded-lg" }),
369
- /* @__PURE__ */ jsx2(Skeleton, { className: "h-9 w-full rounded-lg mt-2" })
370
- ] }),
371
- /* @__PURE__ */ jsx2("div", { className: "absolute bottom-4 left-4 right-4", children: /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-3 px-1", children: [
372
- /* @__PURE__ */ jsx2(Skeleton, { className: "h-8 w-8 rounded-full" }),
373
- /* @__PURE__ */ jsxs2("div", { className: "flex-1 space-y-2", children: [
374
- /* @__PURE__ */ jsx2(Skeleton, { className: "h-3 w-20" }),
375
- /* @__PURE__ */ jsx2(Skeleton, { className: "h-2 w-32" })
376
- ] })
377
- ] }) })
378
- ] }),
379
- /* @__PURE__ */ jsxs2("div", { className: "flex-1 p-6 space-y-4", children: [
380
- /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between", children: [
381
- /* @__PURE__ */ jsx2(Skeleton, { className: "h-8 w-48 rounded-lg" }),
382
- /* @__PURE__ */ jsx2(Skeleton, { className: "h-9 w-24 rounded-lg" })
383
- ] }),
384
- /* @__PURE__ */ jsxs2("div", { className: "grid gap-4 md:grid-cols-2 lg:grid-cols-4", children: [
385
- /* @__PURE__ */ jsx2(Skeleton, { className: "h-24 rounded-xl" }),
386
- /* @__PURE__ */ jsx2(Skeleton, { className: "h-24 rounded-xl" }),
387
- /* @__PURE__ */ jsx2(Skeleton, { className: "h-24 rounded-xl" }),
388
- /* @__PURE__ */ jsx2(Skeleton, { className: "h-24 rounded-xl" })
389
- ] }),
390
- /* @__PURE__ */ jsx2(Skeleton, { className: "h-64 rounded-xl" }),
391
- /* @__PURE__ */ jsxs2("div", { className: "space-y-2", children: [
392
- /* @__PURE__ */ jsx2(Skeleton, { className: "h-10 w-full rounded-lg" }),
393
- /* @__PURE__ */ jsx2(Skeleton, { className: "h-10 w-full rounded-lg" }),
394
- /* @__PURE__ */ jsx2(Skeleton, { className: "h-10 w-full rounded-lg" })
395
- ] })
396
- ] })
397
- ] });
398
- }
399
-
400
- // src/layout/SharedPageHeader.tsx
401
- import { ChevronLeft as ChevronLeft2 } from "lucide-react";
402
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
403
- function SharedPageHeader({
404
- title,
405
- subtitle,
406
- icon,
407
- onBack,
408
- actions,
409
- breadcrumbs,
410
- showBorder = true,
411
- compact = false,
412
- className,
413
- children
414
- }) {
415
- return /* @__PURE__ */ jsxs3(
416
- "div",
417
- {
418
- className: cn(
419
- "w-full",
420
- showBorder && "border-b border-[#1E2738]",
421
- compact ? "px-4 py-3" : "px-6 py-4",
422
- className
423
- ),
424
- children: [
425
- breadcrumbs && /* @__PURE__ */ jsx3("div", { className: cn("mb-2", compact && "mb-1"), children: breadcrumbs }),
426
- /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between gap-4", children: [
427
- /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-3 min-w-0", children: [
428
- onBack && /* @__PURE__ */ jsx3(
429
- "button",
430
- {
431
- onClick: onBack,
432
- className: cn(
433
- "shrink-0 p-1.5 rounded-md",
434
- "text-[#8890A0] hover:text-[#E2E8F0] hover:bg-[#1E2738]",
435
- "transition-colors cursor-pointer",
436
- "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[#8C34E9]"
437
- ),
438
- "aria-label": "Go back",
439
- children: /* @__PURE__ */ jsx3(ChevronLeft2, { className: compact ? "h-4 w-4" : "h-5 w-5" })
440
- }
441
- ),
442
- icon && /* @__PURE__ */ jsx3("div", { className: "shrink-0 text-[#8C34E9]", children: icon }),
443
- /* @__PURE__ */ jsxs3("div", { className: "min-w-0", children: [
444
- /* @__PURE__ */ jsx3(
445
- "h1",
446
- {
447
- className: cn(
448
- "font-semibold text-[#E2E8F0] truncate",
449
- "font-[Montserrat,sans-serif]",
450
- compact ? "text-lg" : "text-xl"
451
- ),
452
- children: title
453
- }
454
- ),
455
- subtitle && /* @__PURE__ */ jsx3(
456
- "p",
457
- {
458
- className: cn(
459
- "text-[#8890A0] truncate mt-0.5",
460
- "font-[Space_Grotesk,sans-serif]",
461
- compact ? "text-xs" : "text-sm"
462
- ),
463
- children: subtitle
464
- }
465
- )
466
- ] })
467
- ] }),
468
- actions && /* @__PURE__ */ jsx3("div", { className: "shrink-0 flex items-center gap-2", children: actions })
469
- ] }),
470
- children && /* @__PURE__ */ jsx3("div", { className: cn("mt-3", compact && "mt-2"), children })
471
- ]
472
- }
473
- );
474
- }
475
-
476
- // src/layout/SharedFooter.tsx
477
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
478
- var FOOTER_SERVICES = [
479
- { name: "SQDCP", slug: "sqdcp", localRoute: "/services/sqdcp", externalUrl: "https://sqdcp.oplytics.digital" },
480
- { name: "OEE Manager", slug: "oee-manager", localRoute: "/services/oee-manager", externalUrl: "https://oee.oplytics.digital" },
481
- { name: "Policy Deployment", slug: "policy-deployment", localRoute: "/services/policy-deployment", externalUrl: "https://policy.oplytics.digital" },
482
- { name: "Action Manager", slug: "action-manager", localRoute: "/services/action-manager", externalUrl: "https://actions.oplytics.digital" },
483
- { name: "Safety Manager", slug: "safety-manager", localRoute: "/services/safety-manager" },
484
- { name: "Connect", slug: "connect", localRoute: "/services/connect", externalUrl: "https://connect.oplytics.digital" }
485
- ];
486
- function SharedFooter({
487
- services = FOOTER_SERVICES,
488
- showPricing = true,
489
- showSignIn = true,
490
- onServiceClick,
491
- onPricingClick,
492
- onSignInClick,
493
- children,
494
- className,
495
- useExternalUrls = false,
496
- year
497
- }) {
498
- const currentYear = year ?? (/* @__PURE__ */ new Date()).getFullYear();
499
- return /* @__PURE__ */ jsx4(
500
- "footer",
501
- {
502
- className: cn(
503
- "w-full border-t border-[#1E2738]",
504
- "bg-[#0D1220] text-[#8890A0]",
505
- "font-[Space_Grotesk,sans-serif]",
506
- className
507
- ),
508
- children: /* @__PURE__ */ jsxs4("div", { className: "max-w-7xl mx-auto px-6 py-10", children: [
509
- /* @__PURE__ */ jsxs4("div", { className: "grid grid-cols-1 md:grid-cols-4 gap-8 mb-8", children: [
510
- /* @__PURE__ */ jsxs4("div", { className: "md:col-span-1", children: [
511
- /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2 mb-3", children: [
512
- /* @__PURE__ */ jsx4(
513
- "div",
514
- {
515
- className: "w-7 h-7 rounded-full flex items-center justify-center",
516
- style: { background: "#8C34E9" },
517
- children: /* @__PURE__ */ jsx4("span", { className: "text-white font-bold text-xs", children: "O" })
518
- }
519
- ),
520
- /* @__PURE__ */ jsx4("span", { className: "text-[#E2E8F0] font-semibold text-sm font-[Montserrat,sans-serif]", children: "Oplytics.digital" })
521
- ] }),
522
- /* @__PURE__ */ jsx4("p", { className: "text-xs text-[#596475] leading-relaxed", children: "Operational intelligence platform for manufacturing excellence." })
523
- ] }),
524
- /* @__PURE__ */ jsxs4("div", { children: [
525
- /* @__PURE__ */ jsx4("h4", { className: "text-[#E2E8F0] font-medium text-xs uppercase tracking-wider mb-3 font-[Montserrat,sans-serif]", children: "Services" }),
526
- /* @__PURE__ */ jsx4("ul", { className: "space-y-2", children: services.map((service) => /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsx4(
527
- "a",
528
- {
529
- href: useExternalUrls && service.externalUrl ? service.externalUrl : service.localRoute,
530
- onClick: onServiceClick ? (e) => onServiceClick(service, e) : void 0,
531
- className: "text-xs text-[#8890A0] hover:text-[#E2E8F0] transition-colors",
532
- children: service.name
533
- }
534
- ) }, service.slug)) })
535
- ] }),
536
- /* @__PURE__ */ jsxs4("div", { children: [
537
- /* @__PURE__ */ jsx4("h4", { className: "text-[#E2E8F0] font-medium text-xs uppercase tracking-wider mb-3 font-[Montserrat,sans-serif]", children: "Company" }),
538
- /* @__PURE__ */ jsxs4("ul", { className: "space-y-2", children: [
539
- showPricing && /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsx4(
540
- "a",
541
- {
542
- href: "/pricing",
543
- onClick: onPricingClick,
544
- className: "text-xs text-[#8890A0] hover:text-[#E2E8F0] transition-colors",
545
- children: "Pricing"
546
- }
547
- ) }),
548
- /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsx4(
549
- "a",
550
- {
551
- href: "/about",
552
- className: "text-xs text-[#8890A0] hover:text-[#E2E8F0] transition-colors",
553
- children: "About"
554
- }
555
- ) }),
556
- /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsx4(
557
- "a",
558
- {
559
- href: "/contact",
560
- className: "text-xs text-[#8890A0] hover:text-[#E2E8F0] transition-colors",
561
- children: "Contact"
562
- }
563
- ) })
564
- ] })
565
- ] }),
566
- /* @__PURE__ */ jsxs4("div", { children: [
567
- /* @__PURE__ */ jsx4("h4", { className: "text-[#E2E8F0] font-medium text-xs uppercase tracking-wider mb-3 font-[Montserrat,sans-serif]", children: "Legal" }),
568
- /* @__PURE__ */ jsxs4("ul", { className: "space-y-2", children: [
569
- /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsx4(
570
- "a",
571
- {
572
- href: "/privacy",
573
- className: "text-xs text-[#8890A0] hover:text-[#E2E8F0] transition-colors",
574
- children: "Privacy Policy"
575
- }
576
- ) }),
577
- /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsx4(
578
- "a",
579
- {
580
- href: "/terms",
581
- className: "text-xs text-[#8890A0] hover:text-[#E2E8F0] transition-colors",
582
- children: "Terms of Service"
583
- }
584
- ) })
585
- ] }),
586
- showSignIn && /* @__PURE__ */ jsx4("div", { className: "mt-4", children: /* @__PURE__ */ jsx4(
587
- "a",
588
- {
589
- href: "/login",
590
- onClick: onSignInClick,
591
- className: "text-xs text-[#8C34E9] hover:text-[#A855F7] transition-colors font-medium",
592
- children: "Sign In"
593
- }
594
- ) })
595
- ] })
596
- ] }),
597
- children,
598
- /* @__PURE__ */ jsxs4("div", { className: "pt-6 border-t border-[#1E2738] flex flex-col sm:flex-row items-center justify-between gap-3", children: [
599
- /* @__PURE__ */ jsxs4("p", { className: "text-xs text-[#596475]", children: [
600
- "\xA9 ",
601
- currentYear,
602
- " Oplytics.digital. All rights reserved."
603
- ] }),
604
- /* @__PURE__ */ jsx4("p", { className: "text-xs text-[#3E4A5C]", children: "Built for manufacturing excellence" }),
605
- typeof __APP_VERSION__ !== "undefined" && /* @__PURE__ */ jsxs4("p", { className: "text-xs text-[#3E4A5C]", children: [
606
- "Platform v",
607
- __APP_VERSION__
608
- ] })
609
- ] })
610
- ] })
611
- }
612
- );
613
- }
614
-
615
- // src/layout/ServiceFooter.tsx
616
- import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
617
- var ENV_LABELS = {
618
- production: { label: "prod", color: "#10b981" },
619
- staging: { label: "stg", color: "#f59e0b" },
620
- development: { label: "dev", color: "#8C34E9" }
621
- };
622
- function ServiceFooter({
623
- enterpriseId,
624
- enterpriseName,
625
- userRole,
626
- userEmail,
627
- environment,
628
- buildVersion,
629
- showHealthMonitor = false,
630
- className
631
- }) {
632
- const currentYear = (/* @__PURE__ */ new Date()).getFullYear();
633
- const env = environment ? ENV_LABELS[environment] : null;
634
- const version = buildVersion ?? (typeof __APP_VERSION__ !== "undefined" ? __APP_VERSION__ : void 0);
635
- return /* @__PURE__ */ jsxs5(
636
- "footer",
637
- {
638
- className: cn(
639
- "w-full border-t border-[#1E2738] bg-[#0D1220]",
640
- "flex items-center justify-between px-4",
641
- "h-9 min-h-[36px] shrink-0",
642
- "font-[Montserrat,sans-serif]",
643
- className
644
- ),
645
- children: [
646
- /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2 min-w-0", children: [
647
- /* @__PURE__ */ jsx5(
648
- "div",
649
- {
650
- className: "w-5 h-5 rounded-full flex items-center justify-center shrink-0",
651
- style: { background: "#8C34E9" },
652
- children: /* @__PURE__ */ jsx5("span", { className: "text-white font-bold", style: { fontSize: 9 }, children: "O" })
653
- }
654
- ),
655
- /* @__PURE__ */ jsx5("span", { className: "text-[10px] text-[#596475] whitespace-nowrap hidden sm:inline", children: "Operational Excellence. One Digital Platform." })
656
- ] }),
657
- showHealthMonitor && /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-1.5 text-[10px] text-[#3E4A5C] min-w-0 overflow-hidden", children: [
658
- enterpriseName && /* @__PURE__ */ jsxs5(Fragment2, { children: [
659
- /* @__PURE__ */ jsxs5("span", { className: "whitespace-nowrap", children: [
660
- /* @__PURE__ */ jsx5("span", { className: "text-[#596475]", children: enterpriseName }),
661
- enterpriseId != null && /* @__PURE__ */ jsxs5("span", { className: "text-[#3E4A5C]", children: [
662
- " (",
663
- enterpriseId,
664
- ")"
665
- ] })
666
- ] }),
667
- /* @__PURE__ */ jsx5(Separator, {})
668
- ] }),
669
- userEmail && /* @__PURE__ */ jsxs5(Fragment2, { children: [
670
- /* @__PURE__ */ jsx5("span", { className: "whitespace-nowrap text-[#596475]", children: userEmail }),
671
- userRole && /* @__PURE__ */ jsxs5("span", { className: "text-[#3E4A5C]", children: [
672
- " (",
673
- userRole,
674
- ")"
675
- ] }),
676
- /* @__PURE__ */ jsx5(Separator, {})
677
- ] }),
678
- env && /* @__PURE__ */ jsxs5(Fragment2, { children: [
679
- /* @__PURE__ */ jsxs5("span", { className: "whitespace-nowrap", children: [
680
- "ENV:",
681
- " ",
682
- /* @__PURE__ */ jsx5("span", { style: { color: env.color }, className: "font-semibold", children: env.label })
683
- ] }),
684
- /* @__PURE__ */ jsx5(Separator, {})
685
- ] }),
686
- version && /* @__PURE__ */ jsxs5("span", { className: "whitespace-nowrap text-[#3E4A5C] font-mono", children: [
687
- "v.",
688
- version
689
- ] })
690
- ] }),
691
- /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2 text-[10px] text-[#3E4A5C] whitespace-nowrap shrink-0", children: [
692
- /* @__PURE__ */ jsxs5("span", { children: [
693
- "\xA9 ",
694
- currentYear,
695
- " Oplytics.digital"
696
- ] }),
697
- !showHealthMonitor && version && /* @__PURE__ */ jsxs5(Fragment2, { children: [
698
- /* @__PURE__ */ jsx5(Separator, {}),
699
- /* @__PURE__ */ jsxs5("span", { className: "font-mono", children: [
700
- "v.",
701
- version
702
- ] })
703
- ] })
704
- ] })
705
- ]
706
- }
707
- );
708
- }
709
- function Separator() {
710
- return /* @__PURE__ */ jsx5("span", { className: "text-[#1E2738] select-none", "aria-hidden": true, children: "|" });
711
- }
712
-
713
- // src/layout/createServiceLayout.tsx
714
- function defineServiceLayout(config) {
715
- return {
716
- defaultWidth: 260,
717
- minWidth: 200,
718
- maxWidth: 480,
719
- showPageHeader: true,
720
- showReportingToolbar: false,
721
- wrapHierarchyProvider: false,
722
- showAuthGate: true,
723
- ...config,
724
- storageKeyPrefix: config.storageKeyPrefix || config.serviceName.toLowerCase().replace(/\s+/g, "-")
725
- };
726
- }
727
-
728
- // src/layout/serviceConfigs.ts
729
- var sqdcpConfig = {
730
- serviceName: "SQDCP",
731
- serviceAbbreviation: "S",
732
- serviceIconName: "BarChart3",
733
- menuSections: [
734
- {
735
- items: [
736
- { iconName: "LayoutDashboard", label: "Dashboard", path: "/" },
737
- { iconName: "PenLine", label: "Data Entry", path: "/entry" },
738
- { iconName: "ClipboardList", label: "Action Tracker", path: "/actions" },
739
- { iconName: "FileText", label: "Reports", path: "/reports" },
740
- { iconName: "Settings", label: "Admin", path: "/admin" }
741
- ]
742
- }
743
- ],
744
- showPageHeader: true,
745
- showReportingToolbar: true,
746
- reportingModuleName: "SQDCP",
747
- wrapHierarchyProvider: false,
748
- showAuthGate: false,
749
- storageKeyPrefix: "sqdcp-sidebar",
750
- defaultWidth: 260,
751
- minWidth: 200,
752
- maxWidth: 480
753
- };
754
- var oeeManagerConfig = {
755
- serviceName: "OEE Manager",
756
- serviceAbbreviation: "o",
757
- serviceIconName: "BarChart3",
758
- menuSections: [
759
- {
760
- items: [
761
- { iconName: "BarChart3", label: "Dashboard", path: "/" },
762
- { iconName: "Database", label: "Data Input", path: "/data-input" },
763
- { iconName: "Clock", label: "Downtime", path: "/downtime" },
764
- { iconName: "Settings", label: "Settings", path: "/settings" }
765
- ]
766
- }
767
- ],
768
- backLink: {
769
- label: "Back to Portal",
770
- path: "https://portal.oplytics.digital"
771
- },
772
- showPageHeader: true,
773
- showReportingToolbar: true,
774
- reportingModuleName: "OEE Enterprise Manager",
775
- wrapHierarchyProvider: false,
776
- showAuthGate: false,
777
- storageKeyPrefix: "oee-sidebar",
778
- defaultWidth: 240,
779
- minWidth: 200,
780
- maxWidth: 400
781
- };
782
- var actionManagerConfig = {
783
- serviceName: "Action Manager",
784
- serviceAbbreviation: "A",
785
- serviceIconName: "LayoutDashboard",
786
- menuSections: [
787
- {
788
- items: [
789
- { iconName: "LayoutDashboard", label: "Dashboard", path: "/" },
790
- { iconName: "Users", label: "Teams", path: "/teams" }
791
- ]
792
- }
793
- ],
794
- showPageHeader: true,
795
- showReportingToolbar: false,
796
- reportingModuleName: "Action Manager",
797
- wrapHierarchyProvider: false,
798
- showAuthGate: true,
799
- storageKeyPrefix: "actionmanager-sidebar",
800
- defaultWidth: 280,
801
- minWidth: 200,
802
- maxWidth: 480
803
- };
804
- var businessHubConfig = {
805
- serviceName: "Business Hub",
806
- serviceAbbreviation: "B",
807
- serviceIconName: "LayoutDashboard",
808
- menuSections: [
809
- {
810
- items: [
811
- { iconName: "LayoutDashboard", label: "Dashboard", path: "/" },
812
- { iconName: "Users", label: "Analytics", path: "/analytics" }
813
- ]
814
- }
815
- ],
816
- showPageHeader: true,
817
- showReportingToolbar: false,
818
- reportingModuleName: "Business Hub",
819
- wrapHierarchyProvider: false,
820
- showAuthGate: true,
821
- storageKeyPrefix: "businesshub-sidebar",
822
- defaultWidth: 280,
823
- minWidth: 200,
824
- maxWidth: 480
825
- };
826
- var policyDeploymentConfig = {
827
- serviceName: "Policy Deployment",
828
- serviceAbbreviation: "PD",
829
- serviceIconName: "FileStack",
830
- menuSections: [
831
- {
832
- label: "Analysis",
833
- items: [
834
- { iconName: "LayoutGrid", label: "Dashboard", path: "/app/policy-deployment" },
835
- { iconName: "Grid3X3", label: "X-Matrix", path: "/app/policy-deployment/xmatrix" },
836
- { iconName: "BarChart3", label: "Bowling Chart", path: "/app/policy-deployment/bowling" },
837
- { iconName: "FolderKanban", label: "Action Plans", path: "/app/policy-deployment/actions" },
838
- { iconName: "GitBranchPlus", label: "Catchball", path: "/app/policy-deployment/catchball" },
839
- { iconName: "Target", label: "Deployments", path: "/app/policy-deployment/deployments" }
840
- ]
841
- },
842
- {
843
- label: "Administration",
844
- adminOnly: true,
845
- items: [
846
- { iconName: "Settings", label: "Manage Policy", path: "/app/policy-deployment/manage" },
847
- { iconName: "Plug", label: "Integrations", path: "/app/policy-deployment/integrations" }
848
- ]
849
- }
850
- ],
851
- backLink: {
852
- label: "Service Hub",
853
- path: "/app"
854
- },
855
- showPageHeader: true,
856
- showReportingToolbar: true,
857
- reportingModuleName: "Policy Deployment",
858
- wrapHierarchyProvider: true,
859
- showAuthGate: true,
860
- storageKeyPrefix: "policy-sidebar",
861
- defaultWidth: 260,
862
- minWidth: 200,
863
- maxWidth: 480
864
- };
865
- var SERVICE_CONFIGS = {
866
- sqdcp: sqdcpConfig,
867
- oeeManager: oeeManagerConfig,
868
- actionManager: actionManagerConfig,
869
- businessHub: businessHubConfig,
870
- policyDeployment: policyDeploymentConfig
871
- };
2
+ DashboardLayout,
3
+ DashboardLayoutSkeleton,
4
+ FOOTER_SERVICES,
5
+ SERVICE_CONFIGS,
6
+ ServiceFooter,
7
+ SharedFooter,
8
+ SharedPageHeader,
9
+ actionManagerConfig,
10
+ businessHubConfig,
11
+ defineServiceLayout,
12
+ getInitials,
13
+ isAdminRole,
14
+ isMenuItemActive,
15
+ oeeManagerConfig,
16
+ policyDeploymentConfig,
17
+ sqdcpConfig,
18
+ useSidebarResize
19
+ } from "../chunk-MAKRKBBI.js";
20
+ import "../chunk-JT3XLKKD.js";
21
+ import "../chunk-LTUYIBMA.js";
872
22
  export {
873
23
  DashboardLayout,
874
24
  DashboardLayoutSkeleton,