@pol-studios/ui 1.0.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.
@@ -0,0 +1,1556 @@
1
+ "use client";
2
+
3
+ // src/nav/BottomNav.tsx
4
+ import { Tooltip } from "flowbite-react";
5
+ import { HiCog } from "react-icons/hi";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ function BottomNav({ settingsRoute = "/settings" }) {
8
+ return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center gap-x-5", children: /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Tooltip, { content: "Settings page", children: /* @__PURE__ */ jsxs(
9
+ "a",
10
+ {
11
+ href: settingsRoute,
12
+ className: "inline-flex cursor-pointer justify-center rounded p-2 text-text-500 hover:bg-muted-100 hover:text-text-900 ",
13
+ children: [
14
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Settings page" }),
15
+ /* @__PURE__ */ jsx(HiCog, { className: "text-2xl text-text-400 text-text-500 hover:text-text-900 " })
16
+ ]
17
+ }
18
+ ) }) }) });
19
+ }
20
+
21
+ // src/nav/MenuToggle.tsx
22
+ import { motion } from "framer-motion";
23
+
24
+ // src/primitives/button.tsx
25
+ import * as React from "react";
26
+ import { Slot } from "@radix-ui/react-slot";
27
+ import { cva } from "class-variance-authority";
28
+ import { cn } from "@pol-studios/utils";
29
+ import { jsx as jsx2 } from "react/jsx-runtime";
30
+ var buttonVariants = cva(
31
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
32
+ {
33
+ variants: {
34
+ variant: {
35
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
36
+ destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
37
+ outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
38
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
39
+ ghost: "hover:bg-accent hover:text-accent-foreground",
40
+ link: "text-primary underline-offset-4 hover:underline"
41
+ },
42
+ size: {
43
+ default: "h-10 px-4 py-2",
44
+ sm: "h-9 rounded-md px-3",
45
+ lg: "h-11 rounded-md px-8",
46
+ icon: "h-10 w-10"
47
+ }
48
+ },
49
+ defaultVariants: {
50
+ variant: "default",
51
+ size: "default"
52
+ }
53
+ }
54
+ );
55
+ var Button = React.forwardRef(
56
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
57
+ const Comp = asChild ? Slot : "button";
58
+ return /* @__PURE__ */ jsx2(
59
+ Comp,
60
+ {
61
+ className: cn(buttonVariants({ variant, size, className })),
62
+ ref,
63
+ ...props
64
+ }
65
+ );
66
+ }
67
+ );
68
+ Button.displayName = "Button";
69
+
70
+ // src/nav/MenuToggle.tsx
71
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
72
+ var Path = (props) => /* @__PURE__ */ jsx3(
73
+ motion.path,
74
+ {
75
+ animate: props.isOpen ? "open" : "closed",
76
+ fill: "transparent",
77
+ strokeWidth: "3",
78
+ strokeLinecap: "round",
79
+ ...props
80
+ }
81
+ );
82
+ var MenuToggle = ({ toggle, isOpen }) => {
83
+ return /* @__PURE__ */ jsx3(Button, { onClick: toggle, className: "z-[100000] p-2", variant: "ghost", "data-testid": "nav-button", children: /* @__PURE__ */ jsxs2("svg", { width: "23", height: "23", className: "stroke-text-50 ", viewBox: "0 0 22 18", children: [
84
+ /* @__PURE__ */ jsx3(
85
+ Path,
86
+ {
87
+ isOpen,
88
+ variants: {
89
+ closed: { d: "M 2 2.5 L 20 2.5" },
90
+ open: { d: "M 3 16.5 L 17 2.5" }
91
+ }
92
+ }
93
+ ),
94
+ /* @__PURE__ */ jsx3(
95
+ Path,
96
+ {
97
+ isOpen,
98
+ d: "M 2 9.423 L 20 9.423",
99
+ variants: {
100
+ closed: { opacity: 1 },
101
+ open: { opacity: 0 }
102
+ },
103
+ transition: { duration: 0.1 }
104
+ }
105
+ ),
106
+ /* @__PURE__ */ jsx3(
107
+ Path,
108
+ {
109
+ isOpen,
110
+ variants: {
111
+ closed: { d: "M 2 16.346 L 20 16.346" },
112
+ open: { d: "M 3 2.5 L 17 16.346" }
113
+ }
114
+ }
115
+ )
116
+ ] }) });
117
+ };
118
+ var MenuToggle_default = MenuToggle;
119
+
120
+ // src/nav/NavBarGroupItem.tsx
121
+ import { Collapse } from "@mui/material";
122
+ import { useState } from "react";
123
+ import { ChevronUp, ChevronDown } from "lucide-react";
124
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
125
+ function NavBarGroupItem({ title, icon, children, className }) {
126
+ const [isExpanded, setIsExpanded] = useState(false);
127
+ return /* @__PURE__ */ jsxs3("div", { className, children: [
128
+ /* @__PURE__ */ jsxs3(
129
+ "div",
130
+ {
131
+ onClick: () => setIsExpanded(!isExpanded),
132
+ className: "grid cursor-pointer grid-flow-col grid-cols-[auto_1fr]\n rounded-md bg-background-950 px-2 py-0.5 transition-all hover:bg-slate-100\n ",
133
+ children: [
134
+ icon && /* @__PURE__ */ jsx4("span", { className: "my-auto mr-5", children: icon }),
135
+ /* @__PURE__ */ jsx4("span", { className: "text-left align-bottom leading-[2.5rem] font-semibold", children: title }),
136
+ /* @__PURE__ */ jsx4("span", { className: "mb-auto ml-5 mr-0.5 mt-auto", children: isExpanded ? /* @__PURE__ */ jsx4(ChevronUp, {}) : /* @__PURE__ */ jsx4(ChevronDown, {}) })
137
+ ]
138
+ }
139
+ ),
140
+ /* @__PURE__ */ jsx4(Collapse, { in: isExpanded, timeout: "auto", unmountOnExit: true, style: {}, children })
141
+ ] });
142
+ }
143
+
144
+ // src/nav/UserProfileName.tsx
145
+ import { cn as cn2 } from "@pol-studios/utils";
146
+ import { jsx as jsx5 } from "react/jsx-runtime";
147
+ function UserProfileName({
148
+ userId,
149
+ className,
150
+ size = "1rem",
151
+ firstName,
152
+ lastName,
153
+ isLoading = false
154
+ }) {
155
+ if (isLoading) {
156
+ return /* @__PURE__ */ jsx5("span", { className: cn2("animate-pulse bg-muted-200 rounded h-4 w-24 inline-block", className) });
157
+ }
158
+ return /* @__PURE__ */ jsx5("span", { className, style: { fontSize: size }, children: userId ? (firstName ?? "") + " " + (lastName ?? "") : "Application" });
159
+ }
160
+
161
+ // src/nav/UserProfilePicture.tsx
162
+ import { cn as cn4, isUsable } from "@pol-studios/utils";
163
+
164
+ // src/primitives/spinner.tsx
165
+ import { Loader2Icon } from "lucide-react";
166
+ import { cn as cn3 } from "@pol-studios/utils";
167
+ import { jsx as jsx6 } from "react/jsx-runtime";
168
+ function Spinner({ className, ...props }) {
169
+ return /* @__PURE__ */ jsx6(
170
+ Loader2Icon,
171
+ {
172
+ role: "status",
173
+ "aria-label": "Loading",
174
+ className: cn3("size-4 animate-spin", className),
175
+ ...props
176
+ }
177
+ );
178
+ }
179
+
180
+ // src/nav/UserProfilePicture.tsx
181
+ import { jsx as jsx7 } from "react/jsx-runtime";
182
+ function UserProfilePicture({
183
+ userId,
184
+ className = "w-10 h-10",
185
+ size = "1rem",
186
+ onMouseEnter,
187
+ onMouseLeave,
188
+ firstName,
189
+ lastName,
190
+ profileImageUrl,
191
+ isLoading = false
192
+ }) {
193
+ const initials = (firstName?.[0] ?? "") + (lastName?.[0] ?? "");
194
+ const fallBack = /* @__PURE__ */ jsx7("span", { className: cn4("m-auto p-0 leading-[unset] text-text-50 text-sm"), children: firstName || lastName ? initials : "" });
195
+ const hasProfilePath = isUsable(profileImageUrl);
196
+ return /* @__PURE__ */ jsx7(
197
+ "div",
198
+ {
199
+ onMouseEnter: () => {
200
+ onMouseEnter && onMouseEnter();
201
+ },
202
+ onMouseLeave: () => {
203
+ onMouseLeave && onMouseLeave();
204
+ },
205
+ className: cn4(
206
+ "m-auto cursor-pointer rounded-full bg-primary-700 transition hover:bg-primary-800 flex items-center justify-center",
207
+ className
208
+ ),
209
+ children: isLoading ? /* @__PURE__ */ jsx7("div", { className: "center-Items grid h-full", children: /* @__PURE__ */ jsx7(
210
+ Spinner,
211
+ {
212
+ className: cn4("m-auto h-full", className)
213
+ }
214
+ ) }) : hasProfilePath ? /* @__PURE__ */ jsx7(
215
+ "img",
216
+ {
217
+ src: profileImageUrl,
218
+ alt: `${firstName} ${lastName}`,
219
+ className: cn4("transition rounded-full object-cover", className)
220
+ }
221
+ ) : fallBack
222
+ }
223
+ );
224
+ }
225
+
226
+ // src/nav/aceternity/breadcrumb.config.ts
227
+ var globalBreadcrumbConfigs = [];
228
+ var globalHierarchicalConfigs = [];
229
+ var setBreadcrumbConfigs = (configs) => {
230
+ globalBreadcrumbConfigs = configs;
231
+ };
232
+ var setHierarchicalBreadcrumbConfigs = (configs) => {
233
+ globalHierarchicalConfigs = configs;
234
+ globalBreadcrumbConfigs = flattenHierarchicalConfigs(configs);
235
+ };
236
+ var flattenHierarchicalConfigs = (configs) => {
237
+ const flattened = [];
238
+ const flatten = (config, parentPattern = "", parentHref = "", inheritedSegments = []) => {
239
+ let fullPattern;
240
+ if (parentPattern === "" && config.pattern.startsWith("/")) {
241
+ fullPattern = config.pattern;
242
+ } else {
243
+ const childPattern = config.pattern.startsWith("/") ? config.pattern.substring(1) : config.pattern;
244
+ fullPattern = parentPattern + "/" + childPattern;
245
+ }
246
+ const segments = config.segments.map((segment) => {
247
+ let href = segment.href;
248
+ if (segment.path !== void 0) {
249
+ if (segment.isAbsolute || segment.path.startsWith("/")) {
250
+ href = segment.path;
251
+ } else {
252
+ const baseHref = parentHref || fullPattern;
253
+ const relativePath = segment.path.startsWith("/") ? segment.path : "/" + segment.path;
254
+ if (baseHref.includes(":")) {
255
+ href = (pathname) => {
256
+ const dynamicValues = extractDynamicValues(pathname, fullPattern);
257
+ let resolvedBaseHref = baseHref;
258
+ Object.entries(dynamicValues).forEach(([key, value]) => {
259
+ resolvedBaseHref = resolvedBaseHref.replace(`:${key}`, value);
260
+ });
261
+ return resolvedBaseHref + relativePath;
262
+ };
263
+ } else {
264
+ href = baseHref + relativePath;
265
+ }
266
+ }
267
+ } else if (segment.href === void 0) {
268
+ href = fullPattern;
269
+ }
270
+ if (typeof href === "string" && href.includes(":")) {
271
+ const originalHref = href;
272
+ href = (pathname) => {
273
+ const dynamicValues = extractDynamicValues(pathname, fullPattern);
274
+ let resolvedHref = originalHref;
275
+ Object.entries(dynamicValues).forEach(([key, value]) => {
276
+ resolvedHref = resolvedHref.replace(`:${key}`, value);
277
+ });
278
+ return resolvedHref;
279
+ };
280
+ }
281
+ return {
282
+ label: segment.label,
283
+ href,
284
+ icon: segment.icon,
285
+ dynamic: segment.dynamic,
286
+ dynamicLabel: segment.dynamicLabel,
287
+ path: segment.path,
288
+ isAbsolute: segment.isAbsolute
289
+ };
290
+ });
291
+ const allSegments = [...inheritedSegments, ...segments];
292
+ flattened.push({
293
+ pattern: fullPattern,
294
+ segments: allSegments,
295
+ priority: config.priority
296
+ });
297
+ if (config.children) {
298
+ config.children.forEach(
299
+ (child) => flatten(child, fullPattern, fullPattern, allSegments)
300
+ );
301
+ }
302
+ };
303
+ configs.forEach((config) => flatten(config));
304
+ return flattened;
305
+ };
306
+ var addBreadcrumbConfigs = (configs) => {
307
+ globalBreadcrumbConfigs = [...globalBreadcrumbConfigs, ...configs];
308
+ };
309
+ var addHierarchicalBreadcrumbConfigs = (configs) => {
310
+ globalHierarchicalConfigs = [...globalHierarchicalConfigs, ...configs];
311
+ globalBreadcrumbConfigs = [
312
+ ...globalBreadcrumbConfigs,
313
+ ...flattenHierarchicalConfigs(configs)
314
+ ];
315
+ };
316
+ var getBreadcrumbConfigs = () => {
317
+ return globalBreadcrumbConfigs;
318
+ };
319
+ var getHierarchicalBreadcrumbConfigs = () => {
320
+ return globalHierarchicalConfigs;
321
+ };
322
+ var clearBreadcrumbConfigs = () => {
323
+ globalBreadcrumbConfigs = [];
324
+ globalHierarchicalConfigs = [];
325
+ };
326
+ var findParentConfig = (pattern, allConfigs) => {
327
+ const patternSegments = pattern.split("/").filter(Boolean);
328
+ if (patternSegments.length === 0) {
329
+ return null;
330
+ }
331
+ for (let i = patternSegments.length - 1; i > 0; i--) {
332
+ const parentPattern = "/" + patternSegments.slice(0, i).join("/");
333
+ const parentConfig = allConfigs.find((c) => c.pattern === parentPattern);
334
+ if (parentConfig) {
335
+ return parentConfig;
336
+ }
337
+ }
338
+ const rootConfig = allConfigs.find((c) => c.pattern === "/");
339
+ return rootConfig || null;
340
+ };
341
+ var buildInheritedBreadcrumbConfig = (config, allConfigs, visitedPatterns = /* @__PURE__ */ new Set()) => {
342
+ if (visitedPatterns.has(config.pattern)) {
343
+ console.warn(
344
+ `Circular reference detected in breadcrumb pattern: ${config.pattern}`
345
+ );
346
+ return config;
347
+ }
348
+ visitedPatterns.add(config.pattern);
349
+ const parentConfig = findParentConfig(config.pattern, allConfigs);
350
+ if (!parentConfig) {
351
+ return config;
352
+ }
353
+ const builtParent = buildInheritedBreadcrumbConfig(
354
+ parentConfig,
355
+ allConfigs,
356
+ visitedPatterns
357
+ );
358
+ return {
359
+ ...config,
360
+ segments: [...builtParent.segments, ...config.segments]
361
+ };
362
+ };
363
+ var findBreadcrumbConfig = (pathname) => {
364
+ const sortedConfigs = [...globalBreadcrumbConfigs].sort(
365
+ (a, b) => (b.priority || 0) - (a.priority || 0)
366
+ );
367
+ for (const config of sortedConfigs) {
368
+ const matches = matchesPattern(pathname, config.pattern);
369
+ if (matches) {
370
+ return config;
371
+ }
372
+ }
373
+ const pathSegments = pathname.split("/").filter(Boolean);
374
+ const prefixCandidates = sortedConfigs.filter((c) => matchesPrefix(pathname, c.pattern)).sort((a, b) => {
375
+ const aLen = a.pattern.split("/").filter(Boolean).length;
376
+ const bLen = b.pattern.split("/").filter(Boolean).length;
377
+ if (bLen !== aLen) return bLen - aLen;
378
+ return (b.priority || 0) - (a.priority || 0);
379
+ });
380
+ if (prefixCandidates.length > 0) {
381
+ const best = prefixCandidates[0];
382
+ const inherited = buildInheritedBreadcrumbConfig(best, sortedConfigs);
383
+ const patternSegments = best.pattern.split("/").filter(Boolean);
384
+ const remaining = pathSegments.slice(patternSegments.length);
385
+ let currentPath2 = best.pattern === "/" ? "" : best.pattern;
386
+ const remainingSegments = [];
387
+ remaining.forEach((segment) => {
388
+ currentPath2 += `/${segment}`;
389
+ const isId = /^\d+$/.test(segment);
390
+ remainingSegments.push({
391
+ label: isId ? `Item ${segment}` : segment.charAt(0).toUpperCase() + segment.slice(1).replace(/-/g, " "),
392
+ href: currentPath2,
393
+ icon: void 0
394
+ });
395
+ });
396
+ return {
397
+ pattern: pathname,
398
+ segments: normalizeSegments([
399
+ ...inherited.segments,
400
+ ...remainingSegments
401
+ ]),
402
+ priority: inherited.priority ?? 0
403
+ };
404
+ }
405
+ const fallbackSegments = [
406
+ {
407
+ label: "Home",
408
+ href: "/",
409
+ icon: void 0
410
+ }
411
+ ];
412
+ let currentPath = "";
413
+ pathSegments.forEach((segment) => {
414
+ currentPath += `/${segment}`;
415
+ const isId = /^\d+$/.test(segment);
416
+ fallbackSegments.push({
417
+ label: isId ? `Item ${segment}` : segment.charAt(0).toUpperCase() + segment.slice(1).replace(/-/g, " "),
418
+ href: currentPath,
419
+ icon: void 0
420
+ });
421
+ });
422
+ return {
423
+ pattern: pathname,
424
+ segments: fallbackSegments,
425
+ priority: 0
426
+ };
427
+ };
428
+ var matchesPattern = (pathname, pattern) => {
429
+ if (pattern === "/" && pathname === "/") {
430
+ return true;
431
+ }
432
+ const pathSegments = pathname.split("/").filter(Boolean);
433
+ const patternSegments = pattern.split("/").filter(Boolean);
434
+ if (pattern !== "/") {
435
+ if (pathSegments.length !== patternSegments.length) {
436
+ return false;
437
+ }
438
+ for (let i = 0; i < patternSegments.length; i++) {
439
+ if (patternSegments[i].startsWith(":")) {
440
+ continue;
441
+ }
442
+ if (patternSegments[i] !== pathSegments[i]) {
443
+ return false;
444
+ }
445
+ }
446
+ return true;
447
+ }
448
+ return false;
449
+ };
450
+ var matchesPrefix = (pathname, pattern) => {
451
+ if (pattern === "/") return true;
452
+ const pathSegs = pathname.split("/").filter(Boolean);
453
+ const patSegs = pattern.split("/").filter(Boolean);
454
+ if (patSegs.length > pathSegs.length) return false;
455
+ for (let i = 0; i < patSegs.length; i++) {
456
+ const pat = patSegs[i];
457
+ const seg = pathSegs[i];
458
+ if (pat.startsWith(":")) continue;
459
+ if (pat !== seg) return false;
460
+ }
461
+ return true;
462
+ };
463
+ var normalizeSegments = (segments) => {
464
+ if (segments.length === 0) return segments;
465
+ const result = [];
466
+ for (const seg of segments) {
467
+ if (result.length === 0) {
468
+ result.push(seg);
469
+ continue;
470
+ }
471
+ const prev = result[result.length - 1];
472
+ const prevHref = typeof prev.href === "function" ? void 0 : prev.href;
473
+ const currHref = typeof seg.href === "function" ? void 0 : seg.href;
474
+ const isPrevHome = typeof prev.label === "string" && prev.label.trim().toLowerCase() === "home" && prevHref === "/";
475
+ const isCurrHome = typeof seg.label === "string" && seg.label.trim().toLowerCase() === "home" && currHref === "/";
476
+ if (isPrevHome && isCurrHome) {
477
+ continue;
478
+ }
479
+ if (prevHref && currHref && prevHref === currHref) {
480
+ continue;
481
+ }
482
+ result.push(seg);
483
+ }
484
+ return result;
485
+ };
486
+ var extractDynamicValues = (pathname, pattern) => {
487
+ const pathSegments = pathname.split("/").filter(Boolean);
488
+ const patternSegments = pattern.split("/").filter(Boolean);
489
+ const dynamicValues = {};
490
+ for (let i = 0; i < patternSegments.length; i++) {
491
+ if (patternSegments[i].startsWith(":")) {
492
+ const key = patternSegments[i].slice(1);
493
+ if (i < pathSegments.length) {
494
+ dynamicValues[key] = pathSegments[i];
495
+ }
496
+ }
497
+ }
498
+ return dynamicValues;
499
+ };
500
+ var resolveBreadcrumbLabel = async (label, supabase, pathname) => {
501
+ if (typeof label === "function") {
502
+ try {
503
+ if (supabase && pathname && label.length >= 2) {
504
+ return await label(supabase, pathname);
505
+ } else if (supabase && label.length >= 1) {
506
+ return await label(
507
+ supabase
508
+ );
509
+ } else {
510
+ return await label();
511
+ }
512
+ } catch (error) {
513
+ if (supabase) {
514
+ try {
515
+ return await label();
516
+ } catch (fallbackError) {
517
+ console.warn("Failed to resolve breadcrumb label:", fallbackError);
518
+ return "Error";
519
+ }
520
+ }
521
+ throw error;
522
+ }
523
+ }
524
+ return label;
525
+ };
526
+ var resolveBreadcrumbConfig = async (config, supabase, pathname) => {
527
+ const resolvedSegments = await Promise.all(
528
+ config.segments.map(async (segment) => {
529
+ const resolvedLabel = await resolveBreadcrumbLabel(
530
+ segment.label,
531
+ supabase,
532
+ pathname
533
+ );
534
+ let resolvedHref = segment.href;
535
+ if (typeof segment.href === "function" && pathname) {
536
+ try {
537
+ resolvedHref = segment.href(pathname);
538
+ } catch (error) {
539
+ console.warn("Error resolving href:", error);
540
+ resolvedHref = typeof segment.href === "string" ? segment.href : "#";
541
+ }
542
+ }
543
+ return {
544
+ ...segment,
545
+ label: resolvedLabel,
546
+ href: resolvedHref
547
+ };
548
+ })
549
+ );
550
+ return {
551
+ ...config,
552
+ segments: resolvedSegments
553
+ };
554
+ };
555
+
556
+ // src/nav/aceternity/useBreadcrumbs.ts
557
+ import { useCallback, useEffect, useState as useState2 } from "react";
558
+ var useBreadcrumbs = ({
559
+ pathname,
560
+ supabase,
561
+ autoResolve = true
562
+ }) => {
563
+ const [breadcrumbs, setBreadcrumbs] = useState2([]);
564
+ const [isLoading, setIsLoading] = useState2(false);
565
+ const [error, setError] = useState2(null);
566
+ const [rawConfig, setRawConfig] = useState2(null);
567
+ const generateBreadcrumbs = useCallback(async () => {
568
+ setIsLoading(true);
569
+ setError(null);
570
+ try {
571
+ const breadcrumbConfig = findBreadcrumbConfig(pathname);
572
+ if (!breadcrumbConfig) {
573
+ setBreadcrumbs([]);
574
+ setRawConfig(null);
575
+ return;
576
+ }
577
+ setRawConfig(breadcrumbConfig);
578
+ if (!autoResolve) {
579
+ const rawBreadcrumbs = breadcrumbConfig.segments.map((segment) => ({
580
+ label: typeof segment.label === "string" ? segment.label : "Loading...",
581
+ href: typeof segment.href === "function" && pathname ? segment.href(pathname) : segment.href,
582
+ icon: segment.icon
583
+ }));
584
+ setBreadcrumbs(rawBreadcrumbs);
585
+ return;
586
+ }
587
+ const resolvedConfig = await resolveBreadcrumbConfig(
588
+ breadcrumbConfig,
589
+ supabase,
590
+ pathname
591
+ );
592
+ const resolvedBreadcrumbs = resolvedConfig.segments.map((segment) => ({
593
+ label: typeof segment.label === "string" ? segment.label : "Loading...",
594
+ href: segment.href,
595
+ icon: segment.icon
596
+ }));
597
+ setBreadcrumbs(resolvedBreadcrumbs);
598
+ } catch (err) {
599
+ const errorMessage = err instanceof Error ? err.message : "Unknown error";
600
+ setError(errorMessage);
601
+ console.error("Error generating breadcrumbs:", err);
602
+ setBreadcrumbs([]);
603
+ } finally {
604
+ setIsLoading(false);
605
+ }
606
+ }, [pathname, supabase, autoResolve]);
607
+ const refresh = useCallback(async () => {
608
+ await generateBreadcrumbs();
609
+ }, [generateBreadcrumbs]);
610
+ useEffect(() => {
611
+ generateBreadcrumbs();
612
+ }, [generateBreadcrumbs]);
613
+ return {
614
+ breadcrumbs,
615
+ isLoading,
616
+ error,
617
+ refresh,
618
+ rawConfig
619
+ };
620
+ };
621
+ var usePageHeaderBreadcrumbs = (pathname, supabase) => {
622
+ const { breadcrumbs, isLoading, error } = useBreadcrumbs({
623
+ pathname,
624
+ supabase,
625
+ autoResolve: true
626
+ });
627
+ const customBreadcrumbs = breadcrumbs.map((item) => ({
628
+ label: item.label,
629
+ href: item.href,
630
+ icon: item.icon
631
+ }));
632
+ return {
633
+ customBreadcrumbs,
634
+ showBreadcrumbs: true,
635
+ isLoading,
636
+ error
637
+ };
638
+ };
639
+
640
+ // src/nav/aceternity/usePageHeader.ts
641
+ import { useEffect as useEffect2 } from "react";
642
+
643
+ // src/nav/aceternity/BreadcrumbContext.tsx
644
+ import { createContext, useContext } from "react";
645
+ import { jsx as jsx8 } from "react/jsx-runtime";
646
+ var BreadcrumbContext = createContext(
647
+ void 0
648
+ );
649
+ var useBreadcrumbContext = () => {
650
+ const context = useContext(BreadcrumbContext);
651
+ if (!context) {
652
+ throw new Error(
653
+ "useBreadcrumbContext must be used within a BreadcrumbProvider"
654
+ );
655
+ }
656
+ return context;
657
+ };
658
+ var BreadcrumbProvider = ({
659
+ children,
660
+ value
661
+ }) => {
662
+ return /* @__PURE__ */ jsx8(BreadcrumbContext.Provider, { value, children });
663
+ };
664
+
665
+ // src/nav/aceternity/usePageHeader.ts
666
+ var usePageHeader = (options) => {
667
+ const {
668
+ setPageTitle,
669
+ setPageSubtitle,
670
+ setCustomBreadcrumbs,
671
+ setBreadcrumbsVisibility,
672
+ clearCustomBreadcrumbs,
673
+ resetBreadcrumbs
674
+ } = useBreadcrumbContext();
675
+ useEffect2(() => {
676
+ if (options.title !== void 0) {
677
+ setPageTitle(options.title);
678
+ }
679
+ if (options.subtitle !== void 0) {
680
+ setPageSubtitle(options.subtitle);
681
+ }
682
+ if (options.customBreadcrumbs !== void 0) {
683
+ setCustomBreadcrumbs(options.customBreadcrumbs);
684
+ }
685
+ if (options.showBreadcrumbs !== void 0) {
686
+ setBreadcrumbsVisibility(options.showBreadcrumbs);
687
+ }
688
+ return () => {
689
+ if (options.customBreadcrumbs !== void 0) {
690
+ clearCustomBreadcrumbs();
691
+ }
692
+ if (options.title !== void 0 || options.subtitle !== void 0) {
693
+ resetBreadcrumbs();
694
+ }
695
+ };
696
+ }, [
697
+ options.title,
698
+ options.subtitle,
699
+ options.customBreadcrumbs,
700
+ options.showBreadcrumbs,
701
+ setPageTitle,
702
+ setPageSubtitle,
703
+ setCustomBreadcrumbs,
704
+ setBreadcrumbsVisibility,
705
+ clearCustomBreadcrumbs,
706
+ resetBreadcrumbs
707
+ ]);
708
+ return {
709
+ setPageTitle,
710
+ setPageSubtitle,
711
+ setCustomBreadcrumbs,
712
+ setBreadcrumbsVisibility,
713
+ clearCustomBreadcrumbs,
714
+ resetBreadcrumbs
715
+ };
716
+ };
717
+
718
+ // src/nav/aceternity/PageHeader.tsx
719
+ import { cn as cn6 } from "@pol-studios/utils";
720
+
721
+ // src/nav/aceternity/Breadcrumbs.tsx
722
+ import React3, { useEffect as useEffect3, useState as useState3 } from "react";
723
+ import { Link } from "@tanstack/react-router";
724
+ import { cn as cn5 } from "@pol-studios/utils";
725
+ import { jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
726
+ var Breadcrumbs = ({
727
+ config,
728
+ customSegments,
729
+ pathname,
730
+ className,
731
+ showHome = true
732
+ }) => {
733
+ const [resolvedSegments, setResolvedSegments] = useState3([]);
734
+ const [isLoading, setIsLoading] = useState3(false);
735
+ useEffect3(() => {
736
+ const processSegments = async () => {
737
+ setIsLoading(true);
738
+ const segments = customSegments || config?.segments || [];
739
+ const processedSegments = await Promise.all(
740
+ segments.map(async (segment) => {
741
+ const resolvedLabel = await resolveBreadcrumbLabel(segment.label);
742
+ return {
743
+ ...segment,
744
+ label: resolvedLabel,
745
+ resolvedLabel
746
+ };
747
+ })
748
+ );
749
+ setResolvedSegments(processedSegments);
750
+ setIsLoading(false);
751
+ };
752
+ processSegments();
753
+ }, [config, customSegments, pathname]);
754
+ if (isLoading) {
755
+ return /* @__PURE__ */ jsx9(
756
+ "nav",
757
+ {
758
+ className: cn5(
759
+ "flex items-center space-x-1 text-sm text-muted-600",
760
+ className
761
+ ),
762
+ children: /* @__PURE__ */ jsx9("div", { className: "animate-pulse bg-muted-200 h-4 w-24 rounded" })
763
+ }
764
+ );
765
+ }
766
+ if (!config && !customSegments) {
767
+ return null;
768
+ }
769
+ const displaySegments = showHome ? resolvedSegments : resolvedSegments.filter((segment) => segment.href !== "/");
770
+ if (displaySegments.length === 0) {
771
+ return null;
772
+ }
773
+ return /* @__PURE__ */ jsx9(
774
+ "nav",
775
+ {
776
+ className: cn5(
777
+ "flex items-center gap-2 text-sm",
778
+ className
779
+ ),
780
+ "aria-label": "Breadcrumb navigation",
781
+ children: displaySegments.map((segment, index) => {
782
+ const isLast = index === displaySegments.length - 1;
783
+ const isClickable = segment.href && !isLast;
784
+ return /* @__PURE__ */ jsxs4(React3.Fragment, { children: [
785
+ index > 0 && /* @__PURE__ */ jsx9("span", { className: "text-muted-400", children: "/" }),
786
+ isClickable ? /* @__PURE__ */ jsx9(
787
+ Link,
788
+ {
789
+ to: segment.href,
790
+ className: cn5(
791
+ "transition-colors duration-200",
792
+ "hover:text-muted-900",
793
+ "text-muted-600"
794
+ ),
795
+ "aria-label": `Navigate to ${segment.label}`,
796
+ children: segment.resolvedLabel
797
+ }
798
+ ) : /* @__PURE__ */ jsx9(
799
+ "span",
800
+ {
801
+ className: cn5(
802
+ isLast ? "text-muted-900 font-medium" : "text-muted-600"
803
+ ),
804
+ "aria-current": isLast ? "page" : void 0,
805
+ children: segment.resolvedLabel
806
+ }
807
+ )
808
+ ] }, `${segment.label}-${index}`);
809
+ })
810
+ }
811
+ );
812
+ };
813
+ var Breadcrumbs_default = Breadcrumbs;
814
+
815
+ // src/nav/aceternity/PageHeader.tsx
816
+ import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
817
+ var PageHeader = ({
818
+ title,
819
+ subtitle,
820
+ breadcrumbs,
821
+ showBreadcrumbs = true,
822
+ className,
823
+ children,
824
+ actions
825
+ }) => {
826
+ return /* @__PURE__ */ jsx10(
827
+ "div",
828
+ {
829
+ className: cn6(
830
+ "sticky top-0 h-[40px] z-40 bg-muted-100 backdrop-blur-xl box-border border-b border-muted-200",
831
+ className
832
+ ),
833
+ children: /* @__PURE__ */ jsx10("div", { className: "px-4 py-1 sm:px-6 lg:px-8", children: /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between", children: [
834
+ /* @__PURE__ */ jsx10("div", { className: "flex-1", children: showBreadcrumbs && /* @__PURE__ */ jsx10(Breadcrumbs, { customSegments: breadcrumbs }) }),
835
+ actions && /* @__PURE__ */ jsx10("div", { className: "ml-4 flex items-center gap-2", children: actions })
836
+ ] }) })
837
+ }
838
+ );
839
+ };
840
+ var PageHeader_default = PageHeader;
841
+
842
+ // src/nav/aceternity/side-bar-2.tsx
843
+ import { useState as useState5, createContext as createContext3, useContext as useContext3 } from "react";
844
+ import { AnimatePresence as AnimatePresence2, motion as motion3 } from "framer-motion";
845
+ import { cn as cn8 } from "@pol-studios/utils";
846
+ import { Link as Link2, useLocation } from "@tanstack/react-router";
847
+ import { ChevronLeft, ChevronRight, PanelRight } from "lucide-react";
848
+
849
+ // src/animate-primitives/primitives/radix/tooltip.tsx
850
+ import { Tooltip as TooltipPrimitive } from "radix-ui";
851
+ import {
852
+ AnimatePresence,
853
+ motion as motion2,
854
+ useMotionValue,
855
+ useSpring
856
+ } from "motion/react";
857
+
858
+ // src/lib/get-strict-context.tsx
859
+ import * as React4 from "react";
860
+ import { jsx as jsx11 } from "react/jsx-runtime";
861
+ function getStrictContext(name) {
862
+ const Context = React4.createContext(null);
863
+ Context.displayName = name;
864
+ function Provider({ value, children }) {
865
+ return /* @__PURE__ */ jsx11(Context.Provider, { value, children });
866
+ }
867
+ function useContext4() {
868
+ const context = React4.useContext(Context);
869
+ if (context === null) {
870
+ throw new Error(`${name} must be used within its provider`);
871
+ }
872
+ return context;
873
+ }
874
+ return [Provider, useContext4];
875
+ }
876
+
877
+ // src/hooks/use-controlled-state.tsx
878
+ import * as React5 from "react";
879
+ function useControlledState(props) {
880
+ const { value, defaultValue, onChange } = props;
881
+ const [state, setInternalState] = React5.useState(
882
+ value !== void 0 ? value : defaultValue
883
+ );
884
+ React5.useEffect(() => {
885
+ if (value !== void 0) setInternalState(value);
886
+ }, [value]);
887
+ const setState = React5.useCallback(
888
+ (next, ...args) => {
889
+ setInternalState(next);
890
+ onChange?.(next, ...args);
891
+ },
892
+ [onChange]
893
+ );
894
+ return [state, setState];
895
+ }
896
+
897
+ // src/animate-primitives/primitives/radix/tooltip.tsx
898
+ import { jsx as jsx12 } from "react/jsx-runtime";
899
+ var [LocalTooltipProvider, useTooltip] = getStrictContext("TooltipContext");
900
+ function TooltipProvider(props) {
901
+ return /* @__PURE__ */ jsx12(TooltipPrimitive.Provider, { "data-slot": "tooltip-provider", ...props });
902
+ }
903
+ function Tooltip2({
904
+ followCursor = false,
905
+ followCursorSpringOptions = { stiffness: 200, damping: 17 },
906
+ ...props
907
+ }) {
908
+ const [isOpen, setIsOpen] = useControlledState({
909
+ value: props?.open,
910
+ defaultValue: props?.defaultOpen,
911
+ onChange: props?.onOpenChange
912
+ });
913
+ const x = useMotionValue(0);
914
+ const y = useMotionValue(0);
915
+ return /* @__PURE__ */ jsx12(
916
+ LocalTooltipProvider,
917
+ {
918
+ value: {
919
+ isOpen,
920
+ setIsOpen,
921
+ x,
922
+ y,
923
+ followCursor,
924
+ followCursorSpringOptions
925
+ },
926
+ children: /* @__PURE__ */ jsx12(
927
+ TooltipPrimitive.Root,
928
+ {
929
+ "data-slot": "tooltip",
930
+ ...props,
931
+ onOpenChange: setIsOpen
932
+ }
933
+ )
934
+ }
935
+ );
936
+ }
937
+ function TooltipTrigger({ onMouseMove, ...props }) {
938
+ const { x, y, followCursor } = useTooltip();
939
+ const handleMouseMove = (event) => {
940
+ onMouseMove?.(event);
941
+ const target = event.currentTarget.getBoundingClientRect();
942
+ if (followCursor === "x" || followCursor === true) {
943
+ const eventOffsetX = event.clientX - target.left;
944
+ const offsetXFromCenter = (eventOffsetX - target.width / 2) / 2;
945
+ x.set(offsetXFromCenter);
946
+ }
947
+ if (followCursor === "y" || followCursor === true) {
948
+ const eventOffsetY = event.clientY - target.top;
949
+ const offsetYFromCenter = (eventOffsetY - target.height / 2) / 2;
950
+ y.set(offsetYFromCenter);
951
+ }
952
+ };
953
+ return /* @__PURE__ */ jsx12(
954
+ TooltipPrimitive.Trigger,
955
+ {
956
+ "data-slot": "tooltip-trigger",
957
+ onMouseMove: handleMouseMove,
958
+ ...props
959
+ }
960
+ );
961
+ }
962
+ function TooltipPortal(props) {
963
+ const { isOpen } = useTooltip();
964
+ return /* @__PURE__ */ jsx12(AnimatePresence, { children: isOpen && /* @__PURE__ */ jsx12(
965
+ TooltipPrimitive.Portal,
966
+ {
967
+ forceMount: true,
968
+ "data-slot": "tooltip-portal",
969
+ ...props
970
+ }
971
+ ) });
972
+ }
973
+ function TooltipContent({
974
+ onEscapeKeyDown,
975
+ onPointerDownOutside,
976
+ side,
977
+ sideOffset,
978
+ align,
979
+ alignOffset,
980
+ avoidCollisions,
981
+ collisionBoundary,
982
+ collisionPadding,
983
+ arrowPadding,
984
+ sticky,
985
+ hideWhenDetached,
986
+ style,
987
+ transition = { type: "spring", stiffness: 300, damping: 25 },
988
+ ...props
989
+ }) {
990
+ const { x, y, followCursor, followCursorSpringOptions } = useTooltip();
991
+ const translateX = useSpring(x, followCursorSpringOptions);
992
+ const translateY = useSpring(y, followCursorSpringOptions);
993
+ return /* @__PURE__ */ jsx12(
994
+ TooltipPrimitive.Content,
995
+ {
996
+ asChild: true,
997
+ forceMount: true,
998
+ align,
999
+ alignOffset,
1000
+ side,
1001
+ sideOffset,
1002
+ avoidCollisions,
1003
+ collisionBoundary,
1004
+ collisionPadding,
1005
+ arrowPadding,
1006
+ sticky,
1007
+ hideWhenDetached,
1008
+ onEscapeKeyDown,
1009
+ onPointerDownOutside,
1010
+ children: /* @__PURE__ */ jsx12(
1011
+ motion2.div,
1012
+ {
1013
+ "data-slot": "popover-content",
1014
+ initial: { opacity: 0, scale: 0.5 },
1015
+ animate: { opacity: 1, scale: 1 },
1016
+ exit: { opacity: 0, scale: 0.5 },
1017
+ transition,
1018
+ style: {
1019
+ x: followCursor === "x" || followCursor === true ? translateX : void 0,
1020
+ y: followCursor === "y" || followCursor === true ? translateY : void 0,
1021
+ ...style
1022
+ },
1023
+ ...props
1024
+ },
1025
+ "popover-content"
1026
+ )
1027
+ }
1028
+ );
1029
+ }
1030
+ function TooltipArrow(props) {
1031
+ return /* @__PURE__ */ jsx12(TooltipPrimitive.Arrow, { "data-slot": "tooltip-arrow", ...props });
1032
+ }
1033
+
1034
+ // src/animate-primitives/components/radix/tooltip.tsx
1035
+ import { cn as cn7 } from "@pol-studios/utils/tailwind";
1036
+ import { jsx as jsx13, jsxs as jsxs6 } from "react/jsx-runtime";
1037
+ function TooltipProvider2({
1038
+ delayDuration = 0,
1039
+ ...props
1040
+ }) {
1041
+ return /* @__PURE__ */ jsx13(TooltipProvider, { delayDuration, ...props });
1042
+ }
1043
+ function Tooltip3({ delayDuration = 0, ...props }) {
1044
+ return /* @__PURE__ */ jsx13(TooltipProvider2, { delayDuration, children: /* @__PURE__ */ jsx13(Tooltip2, { ...props }) });
1045
+ }
1046
+ function TooltipTrigger2({ ...props }) {
1047
+ return /* @__PURE__ */ jsx13(TooltipTrigger, { ...props });
1048
+ }
1049
+ function TooltipContent2({
1050
+ className,
1051
+ sideOffset = 4,
1052
+ children,
1053
+ ...props
1054
+ }) {
1055
+ return /* @__PURE__ */ jsx13(TooltipPortal, { children: /* @__PURE__ */ jsxs6(
1056
+ TooltipContent,
1057
+ {
1058
+ sideOffset,
1059
+ className: cn7(
1060
+ "bg-foreground text-background z-50 w-fit origin-[var(--radix-tooltip-content-transform-origin)] rounded-md px-3 py-1.5 text-xs text-balance animate-in fade-in-0 zoom-in-[0.98] data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-[0.98]",
1061
+ className
1062
+ ),
1063
+ ...props,
1064
+ children: [
1065
+ children,
1066
+ /* @__PURE__ */ jsx13(TooltipArrow, { className: "bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" })
1067
+ ]
1068
+ }
1069
+ ) });
1070
+ }
1071
+
1072
+ // src/nav/aceternity/sidebar.config.tsx
1073
+ import { Home, Contact, FolderGit2, BookImage, Database, Calendar, Receipt, WalletCards, History, MessageSquarePlus, Settings2, ArrowLeft } from "lucide-react";
1074
+ import { jsx as jsx14 } from "react/jsx-runtime";
1075
+ var defaultSidebarLinks = [
1076
+ {
1077
+ label: "Home",
1078
+ icon: /* @__PURE__ */ jsx14(Home, { className: "w-5 h-5" }),
1079
+ href: "/",
1080
+ section: "main"
1081
+ },
1082
+ {
1083
+ label: "Clients",
1084
+ icon: /* @__PURE__ */ jsx14(Contact, { className: "w-5 h-5" }),
1085
+ href: "/clients",
1086
+ userAccess: "admin_project",
1087
+ section: "main"
1088
+ },
1089
+ {
1090
+ label: "Projects",
1091
+ icon: /* @__PURE__ */ jsx14(FolderGit2, { className: "w-5 h-5" }),
1092
+ href: "/projects",
1093
+ userAccess: "admin_project",
1094
+ section: "main"
1095
+ },
1096
+ {
1097
+ label: "Fixture Catalog",
1098
+ icon: /* @__PURE__ */ jsx14(BookImage, { className: "w-5 h-5" }),
1099
+ href: "/fixtures",
1100
+ userAccess: "fixture_catalog",
1101
+ section: "main"
1102
+ },
1103
+ {
1104
+ label: "Project Database",
1105
+ icon: /* @__PURE__ */ jsx14(Database, { className: "w-5 h-5" }),
1106
+ href: "/project-databases",
1107
+ userAccess: "project_database",
1108
+ section: "main"
1109
+ },
1110
+ {
1111
+ label: "Timesheet",
1112
+ icon: /* @__PURE__ */ jsx14(Calendar, { className: "w-5 h-5" }),
1113
+ href: "/timesheet",
1114
+ userAccess: "timesheet",
1115
+ section: "main"
1116
+ },
1117
+ {
1118
+ label: "Expense Reports",
1119
+ icon: /* @__PURE__ */ jsx14(Receipt, { className: "w-5 h-5" }),
1120
+ href: "/expense",
1121
+ userAccess: "expense_report",
1122
+ section: "main"
1123
+ },
1124
+ {
1125
+ label: "Invoice",
1126
+ icon: /* @__PURE__ */ jsx14(WalletCards, { className: "w-5 h-5" }),
1127
+ href: "/invoices",
1128
+ userAccess: "invoicing",
1129
+ section: "main"
1130
+ },
1131
+ {
1132
+ label: "Change Log",
1133
+ icon: /* @__PURE__ */ jsx14(History, { className: "w-5 h-5" }),
1134
+ href: "/changelog",
1135
+ section: "secondary"
1136
+ },
1137
+ {
1138
+ label: "Support",
1139
+ icon: /* @__PURE__ */ jsx14(MessageSquarePlus, { className: "w-5 h-5" }),
1140
+ href: "/support/tickets",
1141
+ section: "secondary"
1142
+ },
1143
+ {
1144
+ label: "Admin",
1145
+ icon: /* @__PURE__ */ jsx14(Settings2, { className: "w-5 h-5" }),
1146
+ href: "/admin",
1147
+ userAccess: "admin",
1148
+ section: "secondary"
1149
+ },
1150
+ {
1151
+ label: "Log Out",
1152
+ icon: /* @__PURE__ */ jsx14(ArrowLeft, { className: "w-5 h-5" }),
1153
+ onClick: void 0,
1154
+ section: "auth"
1155
+ }
1156
+ ];
1157
+ var sidebarLinks = [...defaultSidebarLinks];
1158
+ var setSidebarLinks = (links) => {
1159
+ sidebarLinks = links;
1160
+ };
1161
+ var addSidebarLinks = (links) => {
1162
+ sidebarLinks = [...sidebarLinks, ...links];
1163
+ };
1164
+ var resetSidebarLinks = () => {
1165
+ sidebarLinks = [...defaultSidebarLinks];
1166
+ };
1167
+
1168
+ // src/nav/aceternity/side-bar-2.tsx
1169
+ import { Fragment, jsx as jsx15, jsxs as jsxs7 } from "react/jsx-runtime";
1170
+ var SidebarContext = createContext3(
1171
+ void 0
1172
+ );
1173
+ var useSidebar = () => {
1174
+ const context = useContext3(SidebarContext);
1175
+ if (!context) {
1176
+ throw new Error("useSidebar must be used within a SidebarProvider");
1177
+ }
1178
+ return context;
1179
+ };
1180
+ var SidebarProvider = ({
1181
+ children,
1182
+ open: openProp,
1183
+ setOpen: setOpenProp,
1184
+ animate = true
1185
+ }) => {
1186
+ const [openState, setOpenState] = useState5(false);
1187
+ const open = openProp !== void 0 ? openProp : openState;
1188
+ const setOpen = setOpenProp !== void 0 ? setOpenProp : setOpenState;
1189
+ return /* @__PURE__ */ jsx15(SidebarContext.Provider, { value: { open, setOpen, animate }, children });
1190
+ };
1191
+ var Sidebar = ({
1192
+ children,
1193
+ open,
1194
+ setOpen,
1195
+ animate
1196
+ }) => {
1197
+ return /* @__PURE__ */ jsx15(SidebarProvider, { open, setOpen, animate, children });
1198
+ };
1199
+ function SidebarLayout({
1200
+ className,
1201
+ children,
1202
+ links = sidebarLinks,
1203
+ onSignOut,
1204
+ loginRoute = "/login",
1205
+ profile,
1206
+ hasAccess = () => true,
1207
+ headerActions,
1208
+ supabase,
1209
+ userProfilePicture,
1210
+ notificationIcon
1211
+ }) {
1212
+ const location = useLocation();
1213
+ const mainLinks = links.filter((l) => l.section === "main");
1214
+ const secondaryLinks = links.filter((l) => l.section === "secondary");
1215
+ const authLinks = links.filter((l) => l.section === "auth");
1216
+ if (authLinks.length > 0 && onSignOut) {
1217
+ authLinks[0].onClick = () => onSignOut();
1218
+ }
1219
+ const [open, setOpen] = useState5(false);
1220
+ const { customBreadcrumbs, showBreadcrumbs, isLoading } = usePageHeaderBreadcrumbs(location.pathname, supabase);
1221
+ return /* @__PURE__ */ jsx15(
1222
+ "div",
1223
+ {
1224
+ className: cn8(
1225
+ "mx-auto flex w-full flex-1 flex-col overflow-hidden md:flex-row min-h-screen z-[100]",
1226
+ className
1227
+ ),
1228
+ children: /* @__PURE__ */ jsxs7(Fragment, { children: [
1229
+ /* @__PURE__ */ jsx15(Sidebar, { open, setOpen, children: /* @__PURE__ */ jsx15(SidebarBody, { className: "justify-between z-50 gap-5", children: /* @__PURE__ */ jsxs7(
1230
+ "aside",
1231
+ {
1232
+ "data-testId": "side-bar",
1233
+ className: "flex flex-1 gap-4 flex-col p-2 px-3 overflow-y-auto overflow-x-hidden",
1234
+ role: "navigation",
1235
+ "aria-label": "Main sidebar navigation",
1236
+ children: [
1237
+ /* @__PURE__ */ jsxs7(
1238
+ "section",
1239
+ {
1240
+ className: cn8(
1241
+ "flex items-center gap-2 py-4 px-2 transition-all",
1242
+ open ? "justify-between" : "justify-center flex-col gap-3"
1243
+ ),
1244
+ "aria-label": "User profile and notifications",
1245
+ children: [
1246
+ /* @__PURE__ */ jsxs7(
1247
+ "div",
1248
+ {
1249
+ className: cn8(
1250
+ "flex items-center gap-2",
1251
+ open ? "" : "flex-col"
1252
+ ),
1253
+ children: [
1254
+ userProfilePicture,
1255
+ open && profile && /* @__PURE__ */ jsxs7("div", { className: "flex flex-col ml-2", children: [
1256
+ /* @__PURE__ */ jsxs7("span", { className: "font-semibold text-base text-muted-900 ", children: [
1257
+ profile.firstName,
1258
+ " ",
1259
+ profile.lastName
1260
+ ] }),
1261
+ /* @__PURE__ */ jsx15("span", { className: "text-xs text-muted-500 ", children: profile.email })
1262
+ ] })
1263
+ ]
1264
+ }
1265
+ ),
1266
+ /* @__PURE__ */ jsx15(
1267
+ "div",
1268
+ {
1269
+ className: cn8(
1270
+ "flex items-center gap-2",
1271
+ open ? "" : "flex-col"
1272
+ ),
1273
+ children: notificationIcon
1274
+ }
1275
+ )
1276
+ ]
1277
+ }
1278
+ ),
1279
+ /* @__PURE__ */ jsx15(
1280
+ "nav",
1281
+ {
1282
+ className: "flex flex-col gap-0.5 mt-2",
1283
+ "aria-label": "Sidebar controls",
1284
+ children: /* @__PURE__ */ jsx15(SidebarCollapseExpandButton, { open, setOpen })
1285
+ }
1286
+ ),
1287
+ /* @__PURE__ */ jsx15(
1288
+ "nav",
1289
+ {
1290
+ className: "flex flex-col gap-0.5 mt-2",
1291
+ "aria-label": "Main navigation",
1292
+ children: mainLinks.map((link, idx) => /* @__PURE__ */ jsx15(
1293
+ SidebarLink,
1294
+ {
1295
+ link,
1296
+ activePath: location.pathname,
1297
+ sidebarOpen: open,
1298
+ hasAccess,
1299
+ tabIndex: 0
1300
+ },
1301
+ idx
1302
+ ))
1303
+ }
1304
+ ),
1305
+ /* @__PURE__ */ jsx15("div", { className: "my-4 h-px w-full bg-muted-200" }),
1306
+ /* @__PURE__ */ jsx15("nav", { className: "flex flex-col", "aria-label": "Secondary navigation", children: secondaryLinks.map((link, idx) => /* @__PURE__ */ jsx15(
1307
+ SidebarLink,
1308
+ {
1309
+ link,
1310
+ activePath: location.pathname,
1311
+ sidebarOpen: open,
1312
+ hasAccess,
1313
+ tabIndex: 0
1314
+ },
1315
+ idx
1316
+ )) }),
1317
+ /* @__PURE__ */ jsx15("div", { className: "my-4 h-px w-full bg-muted-200" }),
1318
+ /* @__PURE__ */ jsx15("nav", { className: "flex flex-col", "aria-label": "Auth navigation", children: authLinks.map((link, idx) => /* @__PURE__ */ jsx15(
1319
+ SidebarLink,
1320
+ {
1321
+ link,
1322
+ activePath: location.pathname,
1323
+ sidebarOpen: open,
1324
+ hasAccess,
1325
+ tabIndex: 0
1326
+ },
1327
+ idx
1328
+ )) })
1329
+ ]
1330
+ }
1331
+ ) }) }),
1332
+ /* @__PURE__ */ jsxs7("div", { className: "flex-1 flex flex-col min-w-0", children: [
1333
+ /* @__PURE__ */ jsx15(
1334
+ PageHeader,
1335
+ {
1336
+ title: void 0,
1337
+ subtitle: void 0,
1338
+ breadcrumbs: (() => {
1339
+ const breadcrumbs = customBreadcrumbs || [];
1340
+ if (isLoading && breadcrumbs.length > 0) {
1341
+ return breadcrumbs.map((b) => ({
1342
+ ...b,
1343
+ label: "Loading...",
1344
+ href: typeof b.href === "function" ? "#" : b.href
1345
+ }));
1346
+ }
1347
+ return breadcrumbs.map((b) => ({
1348
+ ...b,
1349
+ href: typeof b.href === "function" ? "#" : b.href
1350
+ }));
1351
+ })(),
1352
+ showBreadcrumbs,
1353
+ actions: headerActions
1354
+ }
1355
+ ),
1356
+ /* @__PURE__ */ jsx15("div", { className: "flex-1 overflow-auto", children })
1357
+ ] })
1358
+ ] })
1359
+ }
1360
+ );
1361
+ }
1362
+ var SidebarBody = (props) => {
1363
+ return /* @__PURE__ */ jsxs7(Fragment, { children: [
1364
+ /* @__PURE__ */ jsx15(DesktopSidebar, { ...props }),
1365
+ /* @__PURE__ */ jsx15(MobileSidebar, { ...props })
1366
+ ] });
1367
+ };
1368
+ var DesktopSidebar = ({
1369
+ className,
1370
+ children,
1371
+ ...props
1372
+ }) => {
1373
+ const { open, setOpen, animate } = useSidebar();
1374
+ return /* @__PURE__ */ jsx15(Fragment, { children: /* @__PURE__ */ jsx15(
1375
+ motion3.div,
1376
+ {
1377
+ className: cn8(
1378
+ "h-[100dvh] hidden md:flex md:flex-col w-[300px] shrink-0 bg-muted-100/85 drop-shadow-2xl md:drop-shadow-md sticky top-0 left-0 backdrop-blur-xl backdrop-saturate-150",
1379
+ className,
1380
+ open && "px-4 py-4 "
1381
+ ),
1382
+ animate: {
1383
+ width: animate ? open ? "300px" : "75px" : "300px"
1384
+ },
1385
+ ...props,
1386
+ children
1387
+ }
1388
+ ) });
1389
+ };
1390
+ var MobileSidebar = ({
1391
+ className,
1392
+ children,
1393
+ ...props
1394
+ }) => {
1395
+ const { open, setOpen } = useSidebar();
1396
+ return /* @__PURE__ */ jsx15(Fragment, { children: /* @__PURE__ */ jsxs7(
1397
+ "div",
1398
+ {
1399
+ className: cn8(
1400
+ "h-10 px-4 py-4 flex flex-row md:hidden items-center justify-between w-full z-[98] bg-muted-100/85"
1401
+ ),
1402
+ ...props,
1403
+ children: [
1404
+ /* @__PURE__ */ jsxs7("div", { className: "flex z-20 w-full gap-2 justify-between", children: [
1405
+ /* @__PURE__ */ jsx15("div", { className: "font-semibold", children: "Menu" }),
1406
+ /* @__PURE__ */ jsx15(
1407
+ Button,
1408
+ {
1409
+ variant: "ghost",
1410
+ size: "icon",
1411
+ className: "ml-auto p-1 min-h-[unset] h-min my-auto",
1412
+ onClick: () => setOpen((p) => !p),
1413
+ children: /* @__PURE__ */ jsx15(PanelRight, { className: "w-5 h-5" })
1414
+ }
1415
+ )
1416
+ ] }),
1417
+ /* @__PURE__ */ jsx15(AnimatePresence2, { children: open && /* @__PURE__ */ jsx15(
1418
+ motion3.div,
1419
+ {
1420
+ initial: { x: "100%", opacity: 1 },
1421
+ animate: { x: 0, opacity: 1 },
1422
+ exit: { x: "100%", opacity: 1 },
1423
+ transition: { duration: 0.3, ease: "easeInOut" },
1424
+ className: cn8(
1425
+ "fixed inset-y-0 right-0 w-80 z-[100] flex flex-col justify-between bg-muted-100/85 drop-shadow-2xl rounded-2xl border border-muted-200 backdrop-blur-xl backdrop-saturate-150",
1426
+ className
1427
+ ),
1428
+ children
1429
+ }
1430
+ ) })
1431
+ ]
1432
+ }
1433
+ ) });
1434
+ };
1435
+ var SidebarLink = ({
1436
+ link,
1437
+ className,
1438
+ activePath,
1439
+ sidebarOpen,
1440
+ tabIndex,
1441
+ hasAccess = () => true
1442
+ }) => {
1443
+ const { open, setOpen, animate } = useSidebar();
1444
+ const isActive = link.href === "/" ? activePath === "/" : activePath && link.href && activePath.startsWith(link.href);
1445
+ const canAccess = link.userAccess ? hasAccess(link.userAccess) : true;
1446
+ if (!canAccess) return null;
1447
+ const linkContent = /* @__PURE__ */ jsxs7(
1448
+ Link2,
1449
+ {
1450
+ to: link.href || "/",
1451
+ className: cn8(
1452
+ "flex items-center gap-3 w-full rounded-xl px-3 py-2 transition cursor-pointer group/sidebar relative focus:outline-none",
1453
+ isActive && (sidebarOpen ? "bg-muted-100 text-muted-900" : "bg-muted-100/80 text-muted-900"),
1454
+ className
1455
+ ),
1456
+ onClick: (e) => {
1457
+ setOpen(false);
1458
+ link.onClick && link.onClick(e);
1459
+ },
1460
+ "aria-current": isActive ? "page" : void 0,
1461
+ "aria-label": link.label,
1462
+ tabIndex,
1463
+ children: [
1464
+ isActive && /* @__PURE__ */ jsx15(
1465
+ "span",
1466
+ {
1467
+ className: cn8(
1468
+ "absolute left-0 top-1/2 -translate-y-1/2 h-8 w-1 rounded bg-primary-400 shadow transition-all",
1469
+ sidebarOpen ? "opacity-100 left-1" : "opacity-80 left-1"
1470
+ )
1471
+ }
1472
+ ),
1473
+ /* @__PURE__ */ jsx15("span", { className: "flex items-center justify-center", children: link.icon }),
1474
+ /* @__PURE__ */ jsx15(
1475
+ motion3.span,
1476
+ {
1477
+ animate: {
1478
+ display: animate ? open ? "inline-block" : "none" : "inline-block",
1479
+ opacity: animate ? open ? 1 : 0 : 1,
1480
+ width: animate ? open ? "auto" : 0 : "auto"
1481
+ },
1482
+ className: cn8(
1483
+ isActive ? "text-muted-900 font-semibold w-max text-sm group-hover/sidebar:translate-x-1 transition duration-150 whitespace-pre inline-block !p-0 !m-0" : "text-muted-700 w-max text-sm group-hover/sidebar:translate-x-1 transition duration-150 whitespace-pre inline-block !p-0 !m-0",
1484
+ sidebarOpen === false && "hidden"
1485
+ ),
1486
+ children: link.label
1487
+ }
1488
+ )
1489
+ ]
1490
+ }
1491
+ );
1492
+ if (!open) {
1493
+ return /* @__PURE__ */ jsx15(TooltipProvider2, { children: /* @__PURE__ */ jsxs7(Tooltip3, { children: [
1494
+ /* @__PURE__ */ jsx15(TooltipTrigger2, { asChild: true, children: linkContent }),
1495
+ /* @__PURE__ */ jsx15(TooltipContent2, { side: "right", children: link.label })
1496
+ ] }) });
1497
+ }
1498
+ return linkContent;
1499
+ };
1500
+ var SidebarCollapseExpandButton = ({
1501
+ open,
1502
+ setOpen
1503
+ }) => /* @__PURE__ */ jsx15(
1504
+ Button,
1505
+ {
1506
+ variant: "ghost",
1507
+ size: "icon",
1508
+ className: cn8(
1509
+ "rounded-xl w-full flex items-center justify-center px-3 py-2 mb-1 border border-transparent bg-transparent hover:bg-muted-200 transition"
1510
+ ),
1511
+ "aria-label": open ? "Collapse sidebar" : "Expand sidebar",
1512
+ onClick: () => setOpen((p) => !p),
1513
+ children: open ? /* @__PURE__ */ jsx15(ChevronLeft, { className: "w-5 h-5" }) : /* @__PURE__ */ jsx15(ChevronRight, { className: "w-5 h-5" })
1514
+ }
1515
+ );
1516
+ export {
1517
+ BottomNav,
1518
+ BreadcrumbProvider,
1519
+ Breadcrumbs_default as Breadcrumbs,
1520
+ DesktopSidebar,
1521
+ MenuToggle,
1522
+ MenuToggle_default as MenuToggleDefault,
1523
+ MobileSidebar,
1524
+ NavBarGroupItem,
1525
+ PageHeader_default as PageHeader,
1526
+ Sidebar,
1527
+ SidebarBody,
1528
+ SidebarCollapseExpandButton,
1529
+ SidebarLayout,
1530
+ SidebarLink,
1531
+ SidebarProvider,
1532
+ UserProfileName,
1533
+ UserProfilePicture,
1534
+ addBreadcrumbConfigs,
1535
+ addHierarchicalBreadcrumbConfigs,
1536
+ addSidebarLinks,
1537
+ buildInheritedBreadcrumbConfig,
1538
+ clearBreadcrumbConfigs,
1539
+ defaultSidebarLinks,
1540
+ extractDynamicValues,
1541
+ findBreadcrumbConfig,
1542
+ getBreadcrumbConfigs,
1543
+ getHierarchicalBreadcrumbConfigs,
1544
+ resetSidebarLinks,
1545
+ resolveBreadcrumbConfig,
1546
+ resolveBreadcrumbLabel,
1547
+ setBreadcrumbConfigs,
1548
+ setHierarchicalBreadcrumbConfigs,
1549
+ setSidebarLinks,
1550
+ sidebarLinks,
1551
+ useBreadcrumbContext,
1552
+ useBreadcrumbs,
1553
+ usePageHeader,
1554
+ usePageHeaderBreadcrumbs,
1555
+ useSidebar
1556
+ };