@pablo2410/shared-ui 0.3.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.
@@ -0,0 +1,788 @@
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
+ ] })
606
+ ] })
607
+ }
608
+ );
609
+ }
610
+
611
+ // src/layout/createServiceLayout.tsx
612
+ function defineServiceLayout(config) {
613
+ return {
614
+ defaultWidth: 260,
615
+ minWidth: 200,
616
+ maxWidth: 480,
617
+ showPageHeader: true,
618
+ showReportingToolbar: false,
619
+ wrapHierarchyProvider: false,
620
+ showAuthGate: true,
621
+ ...config,
622
+ storageKeyPrefix: config.storageKeyPrefix || config.serviceName.toLowerCase().replace(/\s+/g, "-")
623
+ };
624
+ }
625
+
626
+ // src/layout/serviceConfigs.ts
627
+ var sqdcpConfig = {
628
+ serviceName: "SQDCP",
629
+ serviceAbbreviation: "S",
630
+ serviceIconName: "BarChart3",
631
+ menuSections: [
632
+ {
633
+ items: [
634
+ { iconName: "LayoutDashboard", label: "Dashboard", path: "/" },
635
+ { iconName: "PenLine", label: "Data Entry", path: "/entry" },
636
+ { iconName: "ClipboardList", label: "Action Tracker", path: "/actions" },
637
+ { iconName: "FileText", label: "Reports", path: "/reports" },
638
+ { iconName: "Settings", label: "Admin", path: "/admin" }
639
+ ]
640
+ }
641
+ ],
642
+ showPageHeader: true,
643
+ showReportingToolbar: true,
644
+ reportingModuleName: "SQDCP",
645
+ wrapHierarchyProvider: false,
646
+ showAuthGate: false,
647
+ storageKeyPrefix: "sqdcp-sidebar",
648
+ defaultWidth: 260,
649
+ minWidth: 200,
650
+ maxWidth: 480
651
+ };
652
+ var oeeManagerConfig = {
653
+ serviceName: "OEE Manager",
654
+ serviceAbbreviation: "o",
655
+ serviceIconName: "BarChart3",
656
+ menuSections: [
657
+ {
658
+ items: [
659
+ { iconName: "BarChart3", label: "Dashboard", path: "/" },
660
+ { iconName: "Database", label: "Data Input", path: "/data-input" },
661
+ { iconName: "Clock", label: "Downtime", path: "/downtime" },
662
+ { iconName: "Settings", label: "Settings", path: "/settings" }
663
+ ]
664
+ }
665
+ ],
666
+ backLink: {
667
+ label: "Back to Portal",
668
+ path: "https://portal.oplytics.digital"
669
+ },
670
+ showPageHeader: true,
671
+ showReportingToolbar: true,
672
+ reportingModuleName: "OEE Enterprise Manager",
673
+ wrapHierarchyProvider: false,
674
+ showAuthGate: false,
675
+ storageKeyPrefix: "oee-sidebar",
676
+ defaultWidth: 240,
677
+ minWidth: 200,
678
+ maxWidth: 400
679
+ };
680
+ var actionManagerConfig = {
681
+ serviceName: "Action Manager",
682
+ serviceAbbreviation: "A",
683
+ serviceIconName: "LayoutDashboard",
684
+ menuSections: [
685
+ {
686
+ items: [
687
+ { iconName: "LayoutDashboard", label: "Dashboard", path: "/" },
688
+ { iconName: "Users", label: "Teams", path: "/teams" }
689
+ ]
690
+ }
691
+ ],
692
+ showPageHeader: true,
693
+ showReportingToolbar: false,
694
+ reportingModuleName: "Action Manager",
695
+ wrapHierarchyProvider: false,
696
+ showAuthGate: true,
697
+ storageKeyPrefix: "actionmanager-sidebar",
698
+ defaultWidth: 280,
699
+ minWidth: 200,
700
+ maxWidth: 480
701
+ };
702
+ var businessHubConfig = {
703
+ serviceName: "Business Hub",
704
+ serviceAbbreviation: "B",
705
+ serviceIconName: "LayoutDashboard",
706
+ menuSections: [
707
+ {
708
+ items: [
709
+ { iconName: "LayoutDashboard", label: "Dashboard", path: "/" },
710
+ { iconName: "Users", label: "Analytics", path: "/analytics" }
711
+ ]
712
+ }
713
+ ],
714
+ showPageHeader: true,
715
+ showReportingToolbar: false,
716
+ reportingModuleName: "Business Hub",
717
+ wrapHierarchyProvider: false,
718
+ showAuthGate: true,
719
+ storageKeyPrefix: "businesshub-sidebar",
720
+ defaultWidth: 280,
721
+ minWidth: 200,
722
+ maxWidth: 480
723
+ };
724
+ var policyDeploymentConfig = {
725
+ serviceName: "Policy Deployment",
726
+ serviceAbbreviation: "PD",
727
+ serviceIconName: "FileStack",
728
+ menuSections: [
729
+ {
730
+ label: "Analysis",
731
+ items: [
732
+ { iconName: "LayoutGrid", label: "Dashboard", path: "/app/policy-deployment" },
733
+ { iconName: "Grid3X3", label: "X-Matrix", path: "/app/policy-deployment/xmatrix" },
734
+ { iconName: "BarChart3", label: "Bowling Chart", path: "/app/policy-deployment/bowling" },
735
+ { iconName: "FolderKanban", label: "Action Plans", path: "/app/policy-deployment/actions" },
736
+ { iconName: "GitBranchPlus", label: "Catchball", path: "/app/policy-deployment/catchball" },
737
+ { iconName: "Target", label: "Deployments", path: "/app/policy-deployment/deployments" }
738
+ ]
739
+ },
740
+ {
741
+ label: "Administration",
742
+ adminOnly: true,
743
+ items: [
744
+ { iconName: "Settings", label: "Manage Policy", path: "/app/policy-deployment/manage" },
745
+ { iconName: "Plug", label: "Integrations", path: "/app/policy-deployment/integrations" }
746
+ ]
747
+ }
748
+ ],
749
+ backLink: {
750
+ label: "Service Hub",
751
+ path: "/app"
752
+ },
753
+ showPageHeader: true,
754
+ showReportingToolbar: true,
755
+ reportingModuleName: "Policy Deployment",
756
+ wrapHierarchyProvider: true,
757
+ showAuthGate: true,
758
+ storageKeyPrefix: "policy-sidebar",
759
+ defaultWidth: 260,
760
+ minWidth: 200,
761
+ maxWidth: 480
762
+ };
763
+ var SERVICE_CONFIGS = {
764
+ sqdcp: sqdcpConfig,
765
+ oeeManager: oeeManagerConfig,
766
+ actionManager: actionManagerConfig,
767
+ businessHub: businessHubConfig,
768
+ policyDeployment: policyDeploymentConfig
769
+ };
770
+ export {
771
+ DashboardLayout,
772
+ DashboardLayoutSkeleton,
773
+ FOOTER_SERVICES,
774
+ SERVICE_CONFIGS,
775
+ SharedFooter,
776
+ SharedPageHeader,
777
+ actionManagerConfig,
778
+ businessHubConfig,
779
+ defineServiceLayout,
780
+ getInitials,
781
+ isAdminRole,
782
+ isMenuItemActive,
783
+ oeeManagerConfig,
784
+ policyDeploymentConfig,
785
+ sqdcpConfig,
786
+ useSidebarResize
787
+ };
788
+ //# sourceMappingURL=index.js.map