@soulbatical/tetra-ui 0.1.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.
Files changed (123) hide show
  1. package/dist/components/AutoCard.d.ts +41 -0
  2. package/dist/components/AutoCard.d.ts.map +1 -0
  3. package/dist/components/AutoCard.js +73 -0
  4. package/dist/components/AutoCard.js.map +1 -0
  5. package/dist/components/CookieConsent.d.ts +15 -0
  6. package/dist/components/CookieConsent.d.ts.map +1 -0
  7. package/dist/components/CookieConsent.js +27 -0
  8. package/dist/components/CookieConsent.js.map +1 -0
  9. package/dist/components/GenericListLayout.d.ts +48 -0
  10. package/dist/components/GenericListLayout.d.ts.map +1 -0
  11. package/dist/components/GenericListLayout.js +18 -0
  12. package/dist/components/GenericListLayout.js.map +1 -0
  13. package/dist/components/ListGenerator.d.ts +41 -0
  14. package/dist/components/ListGenerator.d.ts.map +1 -0
  15. package/dist/components/ListGenerator.js +40 -0
  16. package/dist/components/ListGenerator.js.map +1 -0
  17. package/dist/components/LoadingStates.d.ts +38 -0
  18. package/dist/components/LoadingStates.d.ts.map +1 -0
  19. package/dist/components/LoadingStates.js +51 -0
  20. package/dist/components/LoadingStates.js.map +1 -0
  21. package/dist/components/OrgSwitcher.d.ts +15 -0
  22. package/dist/components/OrgSwitcher.d.ts.map +1 -0
  23. package/dist/components/OrgSwitcher.js +36 -0
  24. package/dist/components/OrgSwitcher.js.map +1 -0
  25. package/dist/components/PageHeader.d.ts +16 -0
  26. package/dist/components/PageHeader.d.ts.map +1 -0
  27. package/dist/components/PageHeader.js +10 -0
  28. package/dist/components/PageHeader.js.map +1 -0
  29. package/dist/components/ProtectedRoute.d.ts +9 -0
  30. package/dist/components/ProtectedRoute.d.ts.map +1 -0
  31. package/dist/components/ProtectedRoute.js +30 -0
  32. package/dist/components/ProtectedRoute.js.map +1 -0
  33. package/dist/components/StatusBadge.d.ts +15 -0
  34. package/dist/components/StatusBadge.d.ts.map +1 -0
  35. package/dist/components/StatusBadge.js +79 -0
  36. package/dist/components/StatusBadge.js.map +1 -0
  37. package/dist/components/ThemeToggle.d.ts +14 -0
  38. package/dist/components/ThemeToggle.d.ts.map +1 -0
  39. package/dist/components/ThemeToggle.js +20 -0
  40. package/dist/components/ThemeToggle.js.map +1 -0
  41. package/dist/hooks/use-mobile.d.ts +2 -0
  42. package/dist/hooks/use-mobile.d.ts.map +1 -0
  43. package/dist/hooks/use-mobile.js +17 -0
  44. package/dist/hooks/use-mobile.js.map +1 -0
  45. package/dist/hooks/useAuth.d.ts +34 -0
  46. package/dist/hooks/useAuth.d.ts.map +1 -0
  47. package/dist/hooks/useAuth.js +156 -0
  48. package/dist/hooks/useAuth.js.map +1 -0
  49. package/dist/hooks/useCacheInvalidation.d.ts +25 -0
  50. package/dist/hooks/useCacheInvalidation.d.ts.map +1 -0
  51. package/dist/hooks/useCacheInvalidation.js +57 -0
  52. package/dist/hooks/useCacheInvalidation.js.map +1 -0
  53. package/dist/hooks/useDebounce.d.ts +2 -0
  54. package/dist/hooks/useDebounce.d.ts.map +1 -0
  55. package/dist/hooks/useDebounce.js +15 -0
  56. package/dist/hooks/useDebounce.js.map +1 -0
  57. package/dist/hooks/useDialog.d.ts +26 -0
  58. package/dist/hooks/useDialog.d.ts.map +1 -0
  59. package/dist/hooks/useDialog.js +37 -0
  60. package/dist/hooks/useDialog.js.map +1 -0
  61. package/dist/hooks/useFilterConfigs.d.ts +81 -0
  62. package/dist/hooks/useFilterConfigs.d.ts.map +1 -0
  63. package/dist/hooks/useFilterConfigs.js +68 -0
  64. package/dist/hooks/useFilterConfigs.js.map +1 -0
  65. package/dist/hooks/useFormSubmit.d.ts +79 -0
  66. package/dist/hooks/useFormSubmit.d.ts.map +1 -0
  67. package/dist/hooks/useFormSubmit.js +57 -0
  68. package/dist/hooks/useFormSubmit.js.map +1 -0
  69. package/dist/hooks/useGenericList.d.ts +62 -0
  70. package/dist/hooks/useGenericList.d.ts.map +1 -0
  71. package/dist/hooks/useGenericList.js +75 -0
  72. package/dist/hooks/useGenericList.js.map +1 -0
  73. package/dist/hooks/useGenericListWithConfig.d.ts +65 -0
  74. package/dist/hooks/useGenericListWithConfig.d.ts.map +1 -0
  75. package/dist/hooks/useGenericListWithConfig.js +98 -0
  76. package/dist/hooks/useGenericListWithConfig.js.map +1 -0
  77. package/dist/hooks/useInfiniteScroll.d.ts +11 -0
  78. package/dist/hooks/useInfiniteScroll.d.ts.map +1 -0
  79. package/dist/hooks/useInfiniteScroll.js +63 -0
  80. package/dist/hooks/useInfiniteScroll.js.map +1 -0
  81. package/dist/hooks/useOrganizations.d.ts +27 -0
  82. package/dist/hooks/useOrganizations.d.ts.map +1 -0
  83. package/dist/hooks/useOrganizations.js +56 -0
  84. package/dist/hooks/useOrganizations.js.map +1 -0
  85. package/dist/hooks/usePageContext.d.ts +29 -0
  86. package/dist/hooks/usePageContext.d.ts.map +1 -0
  87. package/dist/hooks/usePageContext.js +32 -0
  88. package/dist/hooks/usePageContext.js.map +1 -0
  89. package/dist/hooks/usePersistedQueryState.d.ts +35 -0
  90. package/dist/hooks/usePersistedQueryState.d.ts.map +1 -0
  91. package/dist/hooks/usePersistedQueryState.js +187 -0
  92. package/dist/hooks/usePersistedQueryState.js.map +1 -0
  93. package/dist/hooks/useQueryState.d.ts +40 -0
  94. package/dist/hooks/useQueryState.d.ts.map +1 -0
  95. package/dist/hooks/useQueryState.js +246 -0
  96. package/dist/hooks/useQueryState.js.map +1 -0
  97. package/dist/index.d.ts +30 -0
  98. package/dist/index.d.ts.map +1 -0
  99. package/dist/index.js +43 -0
  100. package/dist/index.js.map +1 -0
  101. package/dist/lib/utils.d.ts +3 -0
  102. package/dist/lib/utils.d.ts.map +1 -0
  103. package/dist/lib/utils.js +6 -0
  104. package/dist/lib/utils.js.map +1 -0
  105. package/dist/providers/QueryProvider.d.ts +9 -0
  106. package/dist/providers/QueryProvider.d.ts.map +1 -0
  107. package/dist/providers/QueryProvider.js +20 -0
  108. package/dist/providers/QueryProvider.js.map +1 -0
  109. package/dist/providers/ThemeProvider.d.ts +15 -0
  110. package/dist/providers/ThemeProvider.d.ts.map +1 -0
  111. package/dist/providers/ThemeProvider.js +13 -0
  112. package/dist/providers/ThemeProvider.js.map +1 -0
  113. package/dist/services/apiClient.d.ts +20 -0
  114. package/dist/services/apiClient.d.ts.map +1 -0
  115. package/dist/services/apiClient.js +85 -0
  116. package/dist/services/apiClient.js.map +1 -0
  117. package/dist/types/index.d.ts +119 -0
  118. package/dist/types/index.d.ts.map +1 -0
  119. package/dist/types/index.js +5 -0
  120. package/dist/types/index.js.map +1 -0
  121. package/package.json +68 -0
  122. package/src/styles/dark-mode.css +135 -0
  123. package/src/styles/theme.css +105 -0
@@ -0,0 +1,15 @@
1
+ import React from "react";
2
+ interface StatusBadgeProps {
3
+ status: string;
4
+ type?: "default" | "order" | "user" | "voucher" | "production" | "workflow";
5
+ icon?: boolean;
6
+ className?: string;
7
+ }
8
+ /**
9
+ * StatusBadge — Colored badge with optional icon for entity status.
10
+ * Uses plain HTML + Tailwind (no shadcn/ui Badge dependency).
11
+ * Ported from SparkBuddy shared components.
12
+ */
13
+ export declare const StatusBadge: React.FC<StatusBadgeProps>;
14
+ export default StatusBadge;
15
+ //# sourceMappingURL=StatusBadge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StatusBadge.d.ts","sourceRoot":"","sources":["../../src/components/StatusBadge.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,UAAU,gBAAgB;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,YAAY,GAAG,UAAU,CAAC;IAC5E,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAkJD;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAiBlD,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -0,0 +1,79 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /** Map status to Tailwind color classes */
4
+ const getStatusClasses = (status) => {
5
+ const lowerStatus = status.toLowerCase();
6
+ // Success states
7
+ if (["completed", "paid", "active", "finished", "success", "published"].includes(lowerStatus)) {
8
+ return "bg-green-100 text-green-800 border-green-200 dark:bg-green-900/30 dark:text-green-400 dark:border-green-800";
9
+ }
10
+ // Warning/pending states
11
+ if (["pending", "processing", "in_progress", "draft", "unpaid"].includes(lowerStatus)) {
12
+ return "bg-yellow-100 text-yellow-800 border-yellow-200 dark:bg-yellow-900/30 dark:text-yellow-400 dark:border-yellow-800";
13
+ }
14
+ // Error states
15
+ if (["cancelled", "failed", "expired", "inactive", "error", "rejected"].includes(lowerStatus)) {
16
+ return "bg-red-100 text-red-800 border-red-200 dark:bg-red-900/30 dark:text-red-400 dark:border-red-800";
17
+ }
18
+ // Default/neutral states
19
+ return "bg-gray-100 text-gray-800 border-gray-200 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-700";
20
+ };
21
+ /**
22
+ * Returns an inline SVG icon based on status.
23
+ * Replaces lucide-react dependency with inline SVGs.
24
+ */
25
+ function StatusIcon({ status, type, }) {
26
+ const lowerStatus = status.toLowerCase();
27
+ const cls = "h-3 w-3 shrink-0";
28
+ // Type-specific icons
29
+ if (type === "user") {
30
+ return (_jsx("svg", { className: cls, fill: "none", viewBox: "0 0 24 24", strokeWidth: 2, stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M15.75 6a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0ZM4.501 20.118a7.5 7.5 0 0 1 14.998 0A17.933 17.933 0 0 1 12 21.75c-2.676 0-5.216-.584-7.499-1.632Z" }) }));
31
+ }
32
+ if (type === "voucher") {
33
+ return (_jsxs("svg", { className: cls, fill: "none", viewBox: "0 0 24 24", strokeWidth: 2, stroke: "currentColor", children: [_jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9.568 3H5.25A2.25 2.25 0 0 0 3 5.25v4.318c0 .597.237 1.17.659 1.591l9.581 9.581c.699.699 1.78.872 2.607.33a18.095 18.095 0 0 0 5.223-5.223c.542-.827.369-1.908-.33-2.607L11.16 3.66A2.25 2.25 0 0 0 9.568 3Z" }), _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 6h.008v.008H6V6Z" })] }));
34
+ }
35
+ if (type === "production") {
36
+ return (_jsx("svg", { className: cls, fill: "none", viewBox: "0 0 24 24", strokeWidth: 2, stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "m20.25 7.5-.625 10.632a2.25 2.25 0 0 1-2.247 2.118H6.622a2.25 2.25 0 0 1-2.247-2.118L3.75 7.5M10 11.25h4M3.375 7.5h17.25c.621 0 1.125-.504 1.125-1.125v-1.5c0-.621-.504-1.125-1.125-1.125H3.375c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125Z" }) }));
37
+ }
38
+ if (type === "workflow") {
39
+ return (_jsxs("svg", { className: cls, fill: "none", viewBox: "0 0 24 24", strokeWidth: 2, stroke: "currentColor", children: [_jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z" }), _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" })] }));
40
+ }
41
+ // Status-specific icons
42
+ if (["completed", "paid", "active", "finished", "success"].includes(lowerStatus)) {
43
+ // CheckCircle
44
+ return (_jsx("svg", { className: cls, fill: "none", viewBox: "0 0 24 24", strokeWidth: 2, stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" }) }));
45
+ }
46
+ if (["pending", "processing", "in_progress"].includes(lowerStatus)) {
47
+ // Clock
48
+ return (_jsx("svg", { className: cls, fill: "none", viewBox: "0 0 24 24", strokeWidth: 2, stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" }) }));
49
+ }
50
+ if (["cancelled", "failed", "expired", "inactive", "error"].includes(lowerStatus)) {
51
+ // XCircle
52
+ return (_jsx("svg", { className: cls, fill: "none", viewBox: "0 0 24 24", strokeWidth: 2, stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "m9.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" }) }));
53
+ }
54
+ if (["draft", "unpaid"].includes(lowerStatus)) {
55
+ // AlertCircle
56
+ return (_jsx("svg", { className: cls, fill: "none", viewBox: "0 0 24 24", strokeWidth: 2, stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z" }) }));
57
+ }
58
+ // Default: CircleDot
59
+ return (_jsxs("svg", { className: cls, fill: "none", viewBox: "0 0 24 24", strokeWidth: 2, stroke: "currentColor", children: [_jsx("circle", { cx: "12", cy: "12", r: "9" }), _jsx("circle", { cx: "12", cy: "12", r: "3", fill: "currentColor" })] }));
60
+ }
61
+ /** Format status text for display: "in_progress" -> "In Progress" */
62
+ const formatStatusText = (status) => {
63
+ return status
64
+ .split("_")
65
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
66
+ .join(" ");
67
+ };
68
+ /**
69
+ * StatusBadge — Colored badge with optional icon for entity status.
70
+ * Uses plain HTML + Tailwind (no shadcn/ui Badge dependency).
71
+ * Ported from SparkBuddy shared components.
72
+ */
73
+ export const StatusBadge = ({ status, type = "default", icon = true, className = "", }) => {
74
+ const colorClasses = getStatusClasses(status);
75
+ const displayText = formatStatusText(status);
76
+ return (_jsxs("span", { className: `inline-flex items-center gap-1 rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors ${colorClasses} ${className}`, children: [icon && _jsx(StatusIcon, { status: status, type: type }), displayText] }));
77
+ };
78
+ export default StatusBadge;
79
+ //# sourceMappingURL=StatusBadge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StatusBadge.js","sourceRoot":"","sources":["../../src/components/StatusBadge.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ interface ThemeToggleProps {
2
+ /** Hide text label (icon only) */
3
+ collapsed?: boolean;
4
+ /** Custom class name */
5
+ className?: string;
6
+ }
7
+ /**
8
+ * Sun/Moon toggle for light/dark mode.
9
+ * Uses next-themes via @tetra/ui ThemeProvider.
10
+ * No external icon deps — inline SVG.
11
+ */
12
+ export declare function ThemeToggle({ collapsed, className }: ThemeToggleProps): import("react/jsx-runtime").JSX.Element | null;
13
+ export {};
14
+ //# sourceMappingURL=ThemeToggle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ThemeToggle.d.ts","sourceRoot":"","sources":["../../src/components/ThemeToggle.tsx"],"names":[],"mappings":"AAKA,UAAU,gBAAgB;IACxB,kCAAkC;IAClC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,wBAAwB;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,gBAAgB,kDAkCrE"}
@@ -0,0 +1,20 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useState } from "react";
4
+ import { useTheme } from "../providers/ThemeProvider.js";
5
+ /**
6
+ * Sun/Moon toggle for light/dark mode.
7
+ * Uses next-themes via @tetra/ui ThemeProvider.
8
+ * No external icon deps — inline SVG.
9
+ */
10
+ export function ThemeToggle({ collapsed, className }) {
11
+ const { resolvedTheme, setTheme } = useTheme();
12
+ const [mounted, setMounted] = useState(false);
13
+ useEffect(() => setMounted(true), []);
14
+ if (!mounted)
15
+ return null;
16
+ const isDark = resolvedTheme === "dark";
17
+ return (_jsxs("button", { onClick: () => setTheme(isDark ? "light" : "dark"), title: isDark ? "Switch to light mode" : "Switch to dark mode", "aria-label": isDark ? "Switch to light mode" : "Switch to dark mode", className: className ??
18
+ `flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm text-gray-600 transition-colors hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-white ${collapsed ? "justify-center" : ""}`, children: [isDark ? (_jsx("svg", { className: "h-5 w-5 shrink-0", fill: "none", viewBox: "0 0 24 24", strokeWidth: 1.5, stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 3v2.25m6.364.386-1.591 1.591M21 12h-2.25m-.386 6.364-1.591-1.591M12 18.75V21m-4.773-4.227-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0Z" }) })) : (_jsx("svg", { className: "h-5 w-5 shrink-0", fill: "none", viewBox: "0 0 24 24", strokeWidth: 1.5, stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M21.752 15.002A9.72 9.72 0 0 1 18 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 0 0 3 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 0 0 9.002-5.998Z" }) })), !collapsed && _jsx("span", { children: isDark ? "Light mode" : "Dark mode" })] }));
19
+ }
20
+ //# sourceMappingURL=ThemeToggle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ThemeToggle.js","sourceRoot":"","sources":["../../src/components/ThemeToggle.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AASzD;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,EAAE,SAAS,EAAE,SAAS,EAAoB;IACpE,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,GAAG,QAAQ,EAAE,CAAC;IAC/C,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAE9C,SAAS,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;IAEtC,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAE1B,MAAM,MAAM,GAAG,aAAa,KAAK,MAAM,CAAC;IAExC,OAAO,CACL,kBACE,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,EAClD,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,qBAAqB,gBAClD,MAAM,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,qBAAqB,EACnE,SAAS,EACP,SAAS;YACT,iLACE,SAAS,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,EACjC,EAAE,aAGH,MAAM,CAAC,CAAC,CAAC,CACR,cAAK,SAAS,EAAC,kBAAkB,EAAC,IAAI,EAAC,MAAM,EAAC,OAAO,EAAC,WAAW,EAAC,WAAW,EAAE,GAAG,EAAE,MAAM,EAAC,cAAc,YACvG,eAAM,aAAa,EAAC,OAAO,EAAC,cAAc,EAAC,OAAO,EAAC,CAAC,EAAC,mMAAmM,GAAG,GACvP,CACP,CAAC,CAAC,CAAC,CACF,cAAK,SAAS,EAAC,kBAAkB,EAAC,IAAI,EAAC,MAAM,EAAC,OAAO,EAAC,WAAW,EAAC,WAAW,EAAE,GAAG,EAAE,MAAM,EAAC,cAAc,YACvG,eAAM,aAAa,EAAC,OAAO,EAAC,cAAc,EAAC,OAAO,EAAC,CAAC,EAAC,sLAAsL,GAAG,GAC1O,CACP,EACA,CAAC,SAAS,IAAI,yBAAO,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,GAAQ,IAC1D,CACV,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function useIsMobile(): boolean;
2
+ //# sourceMappingURL=use-mobile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-mobile.d.ts","sourceRoot":"","sources":["../../src/hooks/use-mobile.tsx"],"names":[],"mappings":"AAMA,wBAAgB,WAAW,YAc1B"}
@@ -0,0 +1,17 @@
1
+ "use client";
2
+ import * as React from "react";
3
+ const MOBILE_BREAKPOINT = 768;
4
+ export function useIsMobile() {
5
+ const [isMobile, setIsMobile] = React.useState(undefined);
6
+ React.useEffect(() => {
7
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
8
+ const onChange = () => {
9
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
10
+ };
11
+ mql.addEventListener("change", onChange);
12
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
13
+ return () => mql.removeEventListener("change", onChange);
14
+ }, []);
15
+ return !!isMobile;
16
+ }
17
+ //# sourceMappingURL=use-mobile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-mobile.js","sourceRoot":"","sources":["../../src/hooks/use-mobile.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,MAAM,iBAAiB,GAAG,GAAG,CAAA;AAE7B,MAAM,UAAU,WAAW;IACzB,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAsB,SAAS,CAAC,CAAA;IAE9E,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,eAAe,iBAAiB,GAAG,CAAC,KAAK,CAAC,CAAA;QACxE,MAAM,QAAQ,GAAG,GAAG,EAAE;YACpB,WAAW,CAAC,MAAM,CAAC,UAAU,GAAG,iBAAiB,CAAC,CAAA;QACpD,CAAC,CAAA;QACD,GAAG,CAAC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;QACxC,WAAW,CAAC,MAAM,CAAC,UAAU,GAAG,iBAAiB,CAAC,CAAA;QAClD,OAAO,GAAG,EAAE,CAAC,GAAG,CAAC,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;IAC1D,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,OAAO,CAAC,CAAC,QAAQ,CAAA;AACnB,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Standard auth configuration. Call configureAuth() at app startup.
3
+ */
4
+ interface AuthConfig {
5
+ /** API base URL */
6
+ apiBaseUrl: string;
7
+ /** sessionStorage key for refresh token (default: "tetra_refresh") */
8
+ storageKey?: string;
9
+ /** Login page path (default: "/auth/login") */
10
+ loginPath?: string;
11
+ }
12
+ export declare function configureAuth(config: AuthConfig): void;
13
+ interface AuthUser {
14
+ id: string;
15
+ email: string;
16
+ }
17
+ /**
18
+ * Wait for auth to finish restoring tokens.
19
+ * Other hooks should await this before making authenticated API calls.
20
+ */
21
+ export declare function waitForAuthReady(): Promise<void>;
22
+ /** Get current access token. Used by apiClient for Authorization header. */
23
+ export declare function getAccessToken(): string | null;
24
+ export declare function useAuth(): {
25
+ user: AuthUser | null;
26
+ loading: boolean;
27
+ isAuthenticated: boolean;
28
+ signIn: (email: string, password: string) => Promise<{
29
+ error?: string;
30
+ }>;
31
+ signOut: () => Promise<void>;
32
+ };
33
+ export {};
34
+ //# sourceMappingURL=useAuth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAuth.d.ts","sourceRoot":"","sources":["../../src/hooks/useAuth.ts"],"names":[],"mappings":"AAKA;;GAEG;AACH,UAAU,UAAU;IAClB,mBAAmB;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAQD,wBAAgB,aAAa,CAAC,MAAM,EAAE,UAAU,QAE/C;AAED,UAAU,QAAQ;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf;AAgBD;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAEhD;AA4BD,4EAA4E;AAC5E,wBAAgB,cAAc,IAAI,MAAM,GAAG,IAAI,CAE9C;AAED,wBAAgB,OAAO;;;;oBAiFV,MAAM,YACH,MAAM,KACf,OAAO,CAAC;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;;EAqCjC"}
@@ -0,0 +1,156 @@
1
+ "use client";
2
+ import { useEffect, useState, useCallback } from "react";
3
+ import { useRouter } from "next/navigation.js";
4
+ let _config = {
5
+ apiBaseUrl: "",
6
+ storageKey: "tetra_refresh",
7
+ loginPath: "/auth/login",
8
+ };
9
+ export function configureAuth(config) {
10
+ _config = { ..._config, ...config };
11
+ }
12
+ // In-memory token storage (more secure against XSS than localStorage)
13
+ let tokens = null;
14
+ let authReadyResolve = null;
15
+ const authReadyPromise = new Promise((resolve) => {
16
+ authReadyResolve = resolve;
17
+ });
18
+ /**
19
+ * Wait for auth to finish restoring tokens.
20
+ * Other hooks should await this before making authenticated API calls.
21
+ */
22
+ export function waitForAuthReady() {
23
+ return authReadyPromise;
24
+ }
25
+ function saveTokens(t) {
26
+ tokens = t;
27
+ try {
28
+ sessionStorage.setItem(_config.storageKey, t.refresh_token);
29
+ }
30
+ catch {
31
+ // SSR or unavailable
32
+ }
33
+ }
34
+ function clearTokens() {
35
+ tokens = null;
36
+ try {
37
+ sessionStorage.removeItem(_config.storageKey);
38
+ }
39
+ catch {
40
+ // SSR or unavailable
41
+ }
42
+ }
43
+ function getSavedRefreshToken() {
44
+ try {
45
+ return sessionStorage.getItem(_config.storageKey);
46
+ }
47
+ catch {
48
+ return null;
49
+ }
50
+ }
51
+ /** Get current access token. Used by apiClient for Authorization header. */
52
+ export function getAccessToken() {
53
+ return tokens?.access_token ?? null;
54
+ }
55
+ export function useAuth() {
56
+ const [user, setUser] = useState(null);
57
+ const [loading, setLoading] = useState(true);
58
+ const router = useRouter();
59
+ const signOut = useCallback(async () => {
60
+ try {
61
+ const token = tokens?.access_token;
62
+ if (token) {
63
+ await fetch(`${_config.apiBaseUrl}/api/public/auth/logout`, {
64
+ method: "POST",
65
+ headers: { Authorization: `Bearer ${token}` },
66
+ });
67
+ }
68
+ }
69
+ catch {
70
+ // Best-effort
71
+ }
72
+ clearTokens();
73
+ setUser(null);
74
+ router.push(_config.loginPath);
75
+ }, [router]);
76
+ useEffect(() => {
77
+ async function restoreSession() {
78
+ if (tokens?.access_token) {
79
+ try {
80
+ const res = await fetch(`${_config.apiBaseUrl}/api/public/auth/session`, { headers: { Authorization: `Bearer ${tokens.access_token}` } });
81
+ const json = await res.json();
82
+ if (json.success && json.data?.user) {
83
+ setUser(json.data.user);
84
+ setLoading(false);
85
+ authReadyResolve?.();
86
+ return;
87
+ }
88
+ }
89
+ catch {
90
+ // Token invalid
91
+ }
92
+ }
93
+ const refreshToken = getSavedRefreshToken();
94
+ if (refreshToken) {
95
+ try {
96
+ const res = await fetch(`${_config.apiBaseUrl}/api/public/auth/refresh`, {
97
+ method: "POST",
98
+ headers: { "Content-Type": "application/json" },
99
+ body: JSON.stringify({ refresh_token: refreshToken }),
100
+ });
101
+ const json = await res.json();
102
+ if (json.success && json.data) {
103
+ saveTokens({
104
+ access_token: json.data.access_token,
105
+ refresh_token: json.data.refresh_token,
106
+ expires_at: json.data.expires_at,
107
+ });
108
+ setUser(json.data.user);
109
+ setLoading(false);
110
+ authReadyResolve?.();
111
+ return;
112
+ }
113
+ }
114
+ catch {
115
+ // Refresh failed
116
+ }
117
+ }
118
+ clearTokens();
119
+ setUser(null);
120
+ setLoading(false);
121
+ authReadyResolve?.();
122
+ }
123
+ restoreSession();
124
+ }, []);
125
+ const signIn = useCallback(async (email, password) => {
126
+ try {
127
+ const res = await fetch(`${_config.apiBaseUrl}/api/public/auth/login`, {
128
+ method: "POST",
129
+ headers: { "Content-Type": "application/json" },
130
+ body: JSON.stringify({ email, password }),
131
+ });
132
+ const json = await res.json();
133
+ if (!json.success) {
134
+ return { error: json.error || "Login failed" };
135
+ }
136
+ saveTokens({
137
+ access_token: json.data.access_token,
138
+ refresh_token: json.data.refresh_token,
139
+ expires_at: json.data.expires_at,
140
+ });
141
+ setUser(json.data.user);
142
+ return {};
143
+ }
144
+ catch {
145
+ return { error: "Network error. Is the backend running?" };
146
+ }
147
+ }, []);
148
+ return {
149
+ user,
150
+ loading,
151
+ isAuthenticated: !!user,
152
+ signIn,
153
+ signOut,
154
+ };
155
+ }
156
+ //# sourceMappingURL=useAuth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAuth.js","sourceRoot":"","sources":["../../src/hooks/useAuth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Hook for invalidating React Query cache entries
3
+ *
4
+ * Simplifies cache invalidation patterns.
5
+ *
6
+ * @example
7
+ * ```tsx
8
+ * const { invalidate, invalidateMultiple } = useCacheInvalidation();
9
+ *
10
+ * // Invalidate single cache
11
+ * invalidate('users');
12
+ *
13
+ * // Invalidate multiple caches
14
+ * invalidate('users', 'vouchers', 'orders');
15
+ *
16
+ * // Or using array
17
+ * invalidateMultiple(['users', 'vouchers', 'orders']);
18
+ * ```
19
+ */
20
+ export declare const useCacheInvalidation: () => {
21
+ invalidate: (...keys: string[]) => void;
22
+ invalidateMultiple: (keys: string[]) => void;
23
+ invalidatePattern: (pattern: string) => void;
24
+ };
25
+ //# sourceMappingURL=useCacheInvalidation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCacheInvalidation.d.ts","sourceRoot":"","sources":["../../src/hooks/useCacheInvalidation.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,oBAAoB;0BAMF,MAAM,EAAE;+BASH,MAAM,EAAE;iCASN,MAAM;CAc3C,CAAC"}
@@ -0,0 +1,57 @@
1
+ "use client";
2
+ import { useQueryClient } from '@tanstack/react-query';
3
+ /**
4
+ * Hook for invalidating React Query cache entries
5
+ *
6
+ * Simplifies cache invalidation patterns.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * const { invalidate, invalidateMultiple } = useCacheInvalidation();
11
+ *
12
+ * // Invalidate single cache
13
+ * invalidate('users');
14
+ *
15
+ * // Invalidate multiple caches
16
+ * invalidate('users', 'vouchers', 'orders');
17
+ *
18
+ * // Or using array
19
+ * invalidateMultiple(['users', 'vouchers', 'orders']);
20
+ * ```
21
+ */
22
+ export const useCacheInvalidation = () => {
23
+ const queryClient = useQueryClient();
24
+ /**
25
+ * Invalidate one or more cache keys
26
+ */
27
+ const invalidate = (...keys) => {
28
+ keys.forEach(key => {
29
+ queryClient.invalidateQueries({ queryKey: [key] });
30
+ });
31
+ };
32
+ /**
33
+ * Invalidate multiple cache keys from an array
34
+ */
35
+ const invalidateMultiple = (keys) => {
36
+ keys.forEach(key => {
37
+ queryClient.invalidateQueries({ queryKey: [key] });
38
+ });
39
+ };
40
+ /**
41
+ * Invalidate all queries matching a pattern
42
+ */
43
+ const invalidatePattern = (pattern) => {
44
+ queryClient.invalidateQueries({
45
+ predicate: (query) => {
46
+ const queryKey = query.queryKey[0];
47
+ return typeof queryKey === 'string' && queryKey.includes(pattern);
48
+ }
49
+ });
50
+ };
51
+ return {
52
+ invalidate,
53
+ invalidateMultiple,
54
+ invalidatePattern
55
+ };
56
+ };
57
+ //# sourceMappingURL=useCacheInvalidation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCacheInvalidation.js","sourceRoot":"","sources":["../../src/hooks/useCacheInvalidation.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,EAAE;IACvC,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IAErC;;OAEG;IACH,MAAM,UAAU,GAAG,CAAC,GAAG,IAAc,EAAE,EAAE;QACvC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;YACjB,WAAW,CAAC,iBAAiB,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACrD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IAEF;;OAEG;IACH,MAAM,kBAAkB,GAAG,CAAC,IAAc,EAAE,EAAE;QAC5C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;YACjB,WAAW,CAAC,iBAAiB,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACrD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IAEF;;OAEG;IACH,MAAM,iBAAiB,GAAG,CAAC,OAAe,EAAE,EAAE;QAC5C,WAAW,CAAC,iBAAiB,CAAC;YAC5B,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE;gBACnB,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;gBACnC,OAAO,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YACpE,CAAC;SACF,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,OAAO;QACL,UAAU;QACV,kBAAkB;QAClB,iBAAiB;KAClB,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function useDebounce<T>(value: T, delay: number): T;
2
+ //# sourceMappingURL=useDebounce.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDebounce.d.ts","sourceRoot":"","sources":["../../src/hooks/useDebounce.ts"],"names":[],"mappings":"AAIA,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,CAczD"}
@@ -0,0 +1,15 @@
1
+ "use client";
2
+ import { useEffect, useState } from 'react';
3
+ export function useDebounce(value, delay) {
4
+ const [debouncedValue, setDebouncedValue] = useState(value);
5
+ useEffect(() => {
6
+ const handler = setTimeout(() => {
7
+ setDebouncedValue(value);
8
+ }, delay);
9
+ return () => {
10
+ clearTimeout(handler);
11
+ };
12
+ }, [value, delay]);
13
+ return debouncedValue;
14
+ }
15
+ //# sourceMappingURL=useDebounce.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDebounce.js","sourceRoot":"","sources":["../../src/hooks/useDebounce.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE5C,MAAM,UAAU,WAAW,CAAI,KAAQ,EAAE,KAAa;IACpD,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,GAAG,QAAQ,CAAI,KAAK,CAAC,CAAC;IAE/D,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;YAC9B,iBAAiB,CAAC,KAAK,CAAC,CAAC;QAC3B,CAAC,EAAE,KAAK,CAAC,CAAC;QAEV,OAAO,GAAG,EAAE;YACV,YAAY,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;IAEnB,OAAO,cAAc,CAAC;AACxB,CAAC"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Hook for managing dialog state (open/close + loading)
3
+ *
4
+ * Eliminates duplicate state management across dialog components.
5
+ *
6
+ * @example
7
+ * ```tsx
8
+ * const { open, setOpen, loading, setLoading, openDialog, closeDialog } = useDialog();
9
+ *
10
+ * return (
11
+ * <Dialog open={open} onOpenChange={setOpen}>
12
+ * {loading ? <Spinner /> : <Content />}
13
+ * </Dialog>
14
+ * );
15
+ * ```
16
+ */
17
+ export declare const useDialog: (defaultOpen?: boolean) => {
18
+ open: boolean;
19
+ setOpen: import("react").Dispatch<import("react").SetStateAction<boolean>>;
20
+ loading: boolean;
21
+ setLoading: import("react").Dispatch<import("react").SetStateAction<boolean>>;
22
+ openDialog: () => void;
23
+ closeDialog: () => void;
24
+ toggleDialog: () => void;
25
+ };
26
+ //# sourceMappingURL=useDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDialog.d.ts","sourceRoot":"","sources":["../../src/hooks/useDialog.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,SAAS,GAAI,qBAAmB;;;;;;;;CAoB5C,CAAC"}
@@ -0,0 +1,37 @@
1
+ "use client";
2
+ import { useState, useCallback } from 'react';
3
+ /**
4
+ * Hook for managing dialog state (open/close + loading)
5
+ *
6
+ * Eliminates duplicate state management across dialog components.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * const { open, setOpen, loading, setLoading, openDialog, closeDialog } = useDialog();
11
+ *
12
+ * return (
13
+ * <Dialog open={open} onOpenChange={setOpen}>
14
+ * {loading ? <Spinner /> : <Content />}
15
+ * </Dialog>
16
+ * );
17
+ * ```
18
+ */
19
+ export const useDialog = (defaultOpen = false) => {
20
+ const [open, setOpen] = useState(defaultOpen);
21
+ const [loading, setLoading] = useState(false);
22
+ const openDialog = useCallback(() => setOpen(true), []);
23
+ const closeDialog = useCallback(() => setOpen(false), []);
24
+ const toggleDialog = useCallback(() => setOpen(prev => !prev), []);
25
+ return {
26
+ // State
27
+ open,
28
+ setOpen,
29
+ loading,
30
+ setLoading,
31
+ // Helper methods
32
+ openDialog,
33
+ closeDialog,
34
+ toggleDialog
35
+ };
36
+ };
37
+ //# sourceMappingURL=useDialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDialog.js","sourceRoot":"","sources":["../../src/hooks/useDialog.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAE9C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,WAAW,GAAG,KAAK,EAAE,EAAE;IAC/C,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC9C,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAE9C,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;IACxD,MAAM,WAAW,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;IAC1D,MAAM,YAAY,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;IAEnE,OAAO;QACL,QAAQ;QACR,IAAI;QACJ,OAAO;QACP,OAAO;QACP,UAAU;QAEV,iBAAiB;QACjB,UAAU;QACV,WAAW;QACX,YAAY;KACb,CAAC;AACJ,CAAC,CAAC"}