@zoiq.io/dev-kit 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/client.d.mts +120 -0
- package/dist/client.d.ts +120 -0
- package/dist/client.js +453 -0
- package/dist/client.js.map +1 -0
- package/dist/client.mjs +436 -0
- package/dist/client.mjs.map +1 -0
- package/dist/index.d.mts +6 -234
- package/dist/index.d.ts +6 -234
- package/dist/index.js +114 -203
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +112 -191
- package/dist/index.mjs.map +1 -1
- package/dist/server.d.mts +24 -0
- package/dist/server.d.ts +24 -0
- package/dist/server.js +22 -0
- package/dist/server.js.map +1 -0
- package/dist/server.mjs +20 -0
- package/dist/server.mjs.map +1 -0
- package/dist/types-DaSMeWe9.d.mts +151 -0
- package/dist/types-DaSMeWe9.d.ts +151 -0
- package/dist/useZOIQEntity-CkkGzG6D.d.ts +42 -0
- package/dist/useZOIQEntity-oTb7n2Cg.d.mts +42 -0
- package/package.json +14 -3
package/dist/client.js
ADDED
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var react = require('react');
|
|
5
|
+
var clsx = require('clsx');
|
|
6
|
+
var tailwindMerge = require('tailwind-merge');
|
|
7
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
8
|
+
|
|
9
|
+
function cn(...inputs) {
|
|
10
|
+
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
11
|
+
}
|
|
12
|
+
var ScrollButton = ({ className, threshold = 200 }) => {
|
|
13
|
+
const [isVisible, setIsVisible] = react.useState(false);
|
|
14
|
+
react.useEffect(() => {
|
|
15
|
+
const onScroll = () => setIsVisible(typeof window !== "undefined" ? window.scrollY > threshold : false);
|
|
16
|
+
window.addEventListener("scroll", onScroll, { passive: true });
|
|
17
|
+
onScroll();
|
|
18
|
+
return () => window.removeEventListener("scroll", onScroll);
|
|
19
|
+
}, [threshold]);
|
|
20
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("fixed bottom-10 right-10 z-50 hidden md:block", className), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
21
|
+
"button",
|
|
22
|
+
{
|
|
23
|
+
type: "button",
|
|
24
|
+
className: cn(
|
|
25
|
+
"rounded-full p-2 cursor-pointer shadow-lg transition-all duration-300",
|
|
26
|
+
"bg-[var(--primary,hsl(220,70%,50%))] text-[var(--primary-foreground,#fff)]",
|
|
27
|
+
isVisible ? "opacity-100" : "opacity-0 pointer-events-none"
|
|
28
|
+
),
|
|
29
|
+
onClick: () => window.scrollTo({ top: 0, behavior: "smooth" }),
|
|
30
|
+
"aria-label": "Scroll to top",
|
|
31
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block w-6 h-6", "aria-hidden": true, children: "\u2191" })
|
|
32
|
+
}
|
|
33
|
+
) });
|
|
34
|
+
};
|
|
35
|
+
var Text = ({
|
|
36
|
+
children,
|
|
37
|
+
className,
|
|
38
|
+
as: As = "span",
|
|
39
|
+
...props
|
|
40
|
+
}) => {
|
|
41
|
+
return /* @__PURE__ */ jsxRuntime.jsx(As, { className: cn("text-[var(--text-primary,#111)]", className), ...props, children });
|
|
42
|
+
};
|
|
43
|
+
var Subtitle = ({ children, className }) => {
|
|
44
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Text, { as: "div", className: cn("text-sm md:text-base text-[var(--text-secondary,#666)]", className), children });
|
|
45
|
+
};
|
|
46
|
+
var Title = ({ children, className }) => {
|
|
47
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
48
|
+
"h1",
|
|
49
|
+
{
|
|
50
|
+
className: cn(
|
|
51
|
+
"text-2xl md:text-3xl mb-0 font-semibold text-pretty text-[var(--text-primary,#111)]",
|
|
52
|
+
className
|
|
53
|
+
),
|
|
54
|
+
children
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
var ToggleTheme = ({ className, onToggle, theme, children }) => {
|
|
59
|
+
return /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: onToggle, className: cn(className), "aria-label": `Toggle theme (current: ${theme ?? "unknown"})`, children: children ?? "Theme" });
|
|
60
|
+
};
|
|
61
|
+
var Footer = ({ children, className }) => {
|
|
62
|
+
return /* @__PURE__ */ jsxRuntime.jsx("footer", { className: cn("w-full border-t border-[var(--border,transparent)] py-6 px-4 md:px-8", className), children });
|
|
63
|
+
};
|
|
64
|
+
var Header = ({ children, className, left, right }) => {
|
|
65
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
66
|
+
"header",
|
|
67
|
+
{
|
|
68
|
+
className: cn(
|
|
69
|
+
"w-full h-14 sticky top-0 z-50 border-b border-[var(--border,transparent)]",
|
|
70
|
+
"bg-[var(--sidebar,var(--background))]",
|
|
71
|
+
className
|
|
72
|
+
),
|
|
73
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("nav", { className: "flex h-full w-full items-center justify-between gap-4 px-4 md:px-6", "aria-label": "Main", children: [
|
|
74
|
+
left != null ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-3", children: left }) : null,
|
|
75
|
+
children,
|
|
76
|
+
right != null ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-3", children: right }) : null
|
|
77
|
+
] })
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
};
|
|
81
|
+
var Page = ({ children, className, fullWidth }) => {
|
|
82
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("w-full flex-1 flex flex-col min-h-screen", className), children: /* @__PURE__ */ jsxRuntime.jsx("main", { className: cn("w-full flex-1 flex flex-col", fullWidth ? "px-0" : "px-4 md:px-8"), children }) });
|
|
83
|
+
};
|
|
84
|
+
var SecondarySidebar = ({ items, className, isActive, renderLink }) => {
|
|
85
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("px-4", className), children: /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex p-1 rounded-lg bg-[var(--accent,#f4f4f5)] border border-[var(--border,#e4e4e7)] w-full justify-between gap-4", children: items.map((item) => {
|
|
86
|
+
const active = isActive?.(item) ?? false;
|
|
87
|
+
const content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
88
|
+
item.icon,
|
|
89
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: item.label })
|
|
90
|
+
] });
|
|
91
|
+
const child = renderLink ? renderLink(item, content) : /* @__PURE__ */ jsxRuntime.jsx("a", { href: item.href, className: cn("flex items-center justify-center gap-2 text-center h-8 w-full rounded-md text-sm font-medium transition-colors", active ? "bg-[var(--sidebar-accent,var(--accent))] text-[var(--foreground)]" : "text-[var(--muted-foreground,#71717a)]"), children: content });
|
|
92
|
+
return /* @__PURE__ */ jsxRuntime.jsx("li", { className: "w-full", children: child }, item.href);
|
|
93
|
+
}) }) });
|
|
94
|
+
};
|
|
95
|
+
var Section = ({ children, className }) => {
|
|
96
|
+
return /* @__PURE__ */ jsxRuntime.jsx("section", { className: cn("max-w-7xl mx-auto w-full px-8 md:px-0 pt-12 md:pt-24 pb-12 md:pb-24", className), children });
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// src/components/molecules/ToastContainer.tsx
|
|
100
|
+
var ToastContainer = () => {
|
|
101
|
+
return null;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// src/api/client.ts
|
|
105
|
+
async function fetchProjectConfig(projectKey, apiBaseUrl2) {
|
|
106
|
+
const base = apiBaseUrl2 ?? (typeof window !== "undefined" ? window.location.origin : "");
|
|
107
|
+
const url = `${base.replace(/\/$/, "")}/api/zoiq/project/${encodeURIComponent(projectKey)}`;
|
|
108
|
+
const res = await fetch(url, { credentials: "include" });
|
|
109
|
+
if (!res.ok) {
|
|
110
|
+
throw new Error(`Failed to fetch project config: ${res.status} ${res.statusText}`);
|
|
111
|
+
}
|
|
112
|
+
const data = await res.json();
|
|
113
|
+
return data;
|
|
114
|
+
}
|
|
115
|
+
async function fetchSectionFlags(projectKey, apiBaseUrl2) {
|
|
116
|
+
const base = apiBaseUrl2 ?? (typeof window !== "undefined" ? window.location.origin : "");
|
|
117
|
+
const url = `${base.replace(/\/$/, "")}/api/zoiq/project/${encodeURIComponent(projectKey)}/sections`;
|
|
118
|
+
const res = await fetch(url, { credentials: "include" });
|
|
119
|
+
if (!res.ok) {
|
|
120
|
+
throw new Error(`Failed to fetch section flags: ${res.status} ${res.statusText}`);
|
|
121
|
+
}
|
|
122
|
+
return res.json();
|
|
123
|
+
}
|
|
124
|
+
async function fetchEntityData(projectKey, entity, options, apiBaseUrl2) {
|
|
125
|
+
const base = apiBaseUrl2 ?? (typeof window !== "undefined" ? window.location.origin : "");
|
|
126
|
+
const params = new URLSearchParams();
|
|
127
|
+
if (options.id) params.set("id", options.id);
|
|
128
|
+
if (options.query) {
|
|
129
|
+
Object.entries(options.query).forEach(([k, v]) => params.set(k, v));
|
|
130
|
+
}
|
|
131
|
+
const qs = params.toString();
|
|
132
|
+
const url = `${base.replace(/\/$/, "")}/api/zoiq/project/${encodeURIComponent(projectKey)}/data/${encodeURIComponent(entity)}${qs ? `?${qs}` : ""}`;
|
|
133
|
+
const headers = {};
|
|
134
|
+
if (options.apiKey) headers["x-api-key"] = options.apiKey;
|
|
135
|
+
const res = await fetch(url, { credentials: "include", headers: Object.keys(headers).length ? headers : void 0 });
|
|
136
|
+
if (!res.ok) {
|
|
137
|
+
throw new Error(`Failed to fetch entity ${entity}: ${res.status} ${res.statusText}`);
|
|
138
|
+
}
|
|
139
|
+
const data = await res.json();
|
|
140
|
+
return data;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/theme/applyTheme.ts
|
|
144
|
+
var STYLE_GUIDE_TO_CSS_VAR = {
|
|
145
|
+
background: "--background",
|
|
146
|
+
foreground: "--foreground",
|
|
147
|
+
primary: "--primary",
|
|
148
|
+
secondary: "--secondary",
|
|
149
|
+
card: "--card",
|
|
150
|
+
cardForeground: "--card-foreground",
|
|
151
|
+
popover: "--popover",
|
|
152
|
+
popoverForeground: "--popover-foreground",
|
|
153
|
+
muted: "--muted",
|
|
154
|
+
mutedForeground: "--muted-foreground",
|
|
155
|
+
accent: "--accent",
|
|
156
|
+
accentForeground: "--accent-foreground",
|
|
157
|
+
destructive: "--destructive",
|
|
158
|
+
destructiveForeground: "--destructive-foreground",
|
|
159
|
+
border: "--border",
|
|
160
|
+
input: "--input",
|
|
161
|
+
ring: "--ring",
|
|
162
|
+
chart1: "--chart-1",
|
|
163
|
+
chart2: "--chart-2",
|
|
164
|
+
chart3: "--chart-3",
|
|
165
|
+
chart4: "--chart-4",
|
|
166
|
+
chart5: "--chart-5",
|
|
167
|
+
sidebar: "--sidebar",
|
|
168
|
+
sidebarForeground: "--sidebar-foreground",
|
|
169
|
+
sidebarPrimary: "--sidebar-primary",
|
|
170
|
+
sidebarPrimaryForeground: "--sidebar-primary-foreground",
|
|
171
|
+
sidebarAccent: "--sidebar-accent",
|
|
172
|
+
sidebarAccentForeground: "--sidebar-accent-foreground",
|
|
173
|
+
sidebarBorder: "--sidebar-border",
|
|
174
|
+
sidebarRing: "--sidebar-ring",
|
|
175
|
+
radius: "--radius"
|
|
176
|
+
};
|
|
177
|
+
var ZOIQ_THEME_STYLE_ID = "zoiq-theme-override";
|
|
178
|
+
function toTailwindHsl(value) {
|
|
179
|
+
if (!value || typeof value !== "string") return value;
|
|
180
|
+
const trimmed = value.trim();
|
|
181
|
+
return trimmed.replace(/,/g, " ");
|
|
182
|
+
}
|
|
183
|
+
function applyTheme(settings) {
|
|
184
|
+
if (typeof document === "undefined") return;
|
|
185
|
+
const root = document.documentElement;
|
|
186
|
+
const declarations = [];
|
|
187
|
+
if (settings.styleGuide && typeof settings.styleGuide === "object") {
|
|
188
|
+
for (const [key, value] of Object.entries(settings.styleGuide)) {
|
|
189
|
+
if (value == null || value === "") continue;
|
|
190
|
+
const cssVar = STYLE_GUIDE_TO_CSS_VAR[key] ?? `--${key}`;
|
|
191
|
+
const normalized = typeof value === "string" && /^\s*[\d.-]+\s*,/.test(value) ? toTailwindHsl(value) : value;
|
|
192
|
+
declarations.push(`${cssVar}: ${normalized};`);
|
|
193
|
+
root.style.setProperty(cssVar, normalized);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (settings.definedColors?.length) {
|
|
197
|
+
for (const color of settings.definedColors) {
|
|
198
|
+
if (!color?.name || !color?.value) continue;
|
|
199
|
+
const varName = `--color-${color.name.replace(/\s+/g, "-").toLowerCase()}`;
|
|
200
|
+
const normalized = /^\s*[\d.-]+\s*,/.test(color.value) ? toTailwindHsl(color.value) : color.value;
|
|
201
|
+
declarations.push(`${varName}: ${normalized};`);
|
|
202
|
+
root.style.setProperty(varName, normalized);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (settings.clientDefinedColors?.length) {
|
|
206
|
+
for (const item of settings.clientDefinedColors) {
|
|
207
|
+
const value = item?.definedColor?.value;
|
|
208
|
+
const name = item?.name;
|
|
209
|
+
if (!name || !value) continue;
|
|
210
|
+
const varName = `--${name.replace(/\s+/g, "-").toLowerCase()}`;
|
|
211
|
+
const normalized = /^\s*[\d.-]+\s*,/.test(value) ? toTailwindHsl(value) : value;
|
|
212
|
+
declarations.push(`${varName}: ${normalized};`);
|
|
213
|
+
root.style.setProperty(varName, normalized);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (declarations.length > 0) {
|
|
217
|
+
const css = `:root {
|
|
218
|
+
${declarations.join("\n ")}
|
|
219
|
+
}
|
|
220
|
+
.dark {
|
|
221
|
+
${declarations.join("\n ")}
|
|
222
|
+
}
|
|
223
|
+
`;
|
|
224
|
+
let styleEl = document.getElementById(ZOIQ_THEME_STYLE_ID);
|
|
225
|
+
if (!styleEl) {
|
|
226
|
+
styleEl = document.createElement("style");
|
|
227
|
+
styleEl.id = ZOIQ_THEME_STYLE_ID;
|
|
228
|
+
document.head.appendChild(styleEl);
|
|
229
|
+
}
|
|
230
|
+
styleEl.textContent = css;
|
|
231
|
+
}
|
|
232
|
+
const branding = settings.branding;
|
|
233
|
+
if (branding?.primaryFont?.link || branding?.secondaryFont?.link || branding?.tertiaryFont?.link) {
|
|
234
|
+
const fontLinks = [];
|
|
235
|
+
if (branding.primaryFont?.link) fontLinks.push(`<link rel="stylesheet" href="${branding.primaryFont.link}" />`);
|
|
236
|
+
if (branding.secondaryFont?.link) fontLinks.push(`<link rel="stylesheet" href="${branding.secondaryFont.link}" />`);
|
|
237
|
+
if (branding.tertiaryFont?.link) fontLinks.push(`<link rel="stylesheet" href="${branding.tertiaryFont.link}" />`);
|
|
238
|
+
if (fontLinks.length > 0) {
|
|
239
|
+
const id = "zoiq-fonts";
|
|
240
|
+
let el = document.getElementById(id);
|
|
241
|
+
if (!el) {
|
|
242
|
+
el = document.createElement("div");
|
|
243
|
+
el.id = id;
|
|
244
|
+
el.innerHTML = fontLinks.join("");
|
|
245
|
+
document.head.append(...el.children);
|
|
246
|
+
el.remove();
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
var ZOIQContext = react.createContext(null);
|
|
252
|
+
var apiBaseUrl = process.env.NEXT_PUBLIC_ZOIQ_API_BASE_URL || "https://portal.zoiq.io";
|
|
253
|
+
function ZOIQProvider({ projectKey, children }) {
|
|
254
|
+
const [settings, setSettings] = react.useState(null);
|
|
255
|
+
const [loading, setLoading] = react.useState(true);
|
|
256
|
+
const [error, setError] = react.useState(null);
|
|
257
|
+
const fetchAndApply = react.useCallback(async () => {
|
|
258
|
+
if (!projectKey) {
|
|
259
|
+
setLoading(false);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
setLoading(true);
|
|
263
|
+
setError(null);
|
|
264
|
+
try {
|
|
265
|
+
const data = await fetchProjectConfig(projectKey, apiBaseUrl);
|
|
266
|
+
setSettings(data ?? null);
|
|
267
|
+
} catch (err) {
|
|
268
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
269
|
+
setSettings(null);
|
|
270
|
+
} finally {
|
|
271
|
+
setLoading(false);
|
|
272
|
+
}
|
|
273
|
+
}, [projectKey]);
|
|
274
|
+
react.useEffect(() => {
|
|
275
|
+
if (!projectKey || typeof window === "undefined") return;
|
|
276
|
+
const params = new URLSearchParams(window.location.search);
|
|
277
|
+
const isPreview = params.get("preview") === "true";
|
|
278
|
+
const previewUrl = params.get("previewUrl");
|
|
279
|
+
if (!isPreview) {
|
|
280
|
+
fetchAndApply();
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
const applyPayload = (payload) => {
|
|
284
|
+
setSettings(payload);
|
|
285
|
+
setError(null);
|
|
286
|
+
setLoading(false);
|
|
287
|
+
};
|
|
288
|
+
if (!previewUrl || !previewUrl.trim()) {
|
|
289
|
+
fetchAndApply();
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
const url = previewUrl.trim();
|
|
293
|
+
let cancelled = false;
|
|
294
|
+
fetch(url, { credentials: "omit" }).then((res) => {
|
|
295
|
+
if (cancelled) return;
|
|
296
|
+
if (!res.ok) throw new Error(res.statusText || "Failed to load preview");
|
|
297
|
+
return res.json();
|
|
298
|
+
}).then((data) => {
|
|
299
|
+
if (cancelled) return;
|
|
300
|
+
applyPayload(data);
|
|
301
|
+
}).catch((err) => {
|
|
302
|
+
if (cancelled) return;
|
|
303
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
304
|
+
setSettings(null);
|
|
305
|
+
setLoading(false);
|
|
306
|
+
});
|
|
307
|
+
return () => {
|
|
308
|
+
cancelled = true;
|
|
309
|
+
};
|
|
310
|
+
}, [projectKey, fetchAndApply]);
|
|
311
|
+
react.useEffect(() => {
|
|
312
|
+
if (settings) {
|
|
313
|
+
applyTheme(settings);
|
|
314
|
+
}
|
|
315
|
+
}, [settings]);
|
|
316
|
+
const value = {
|
|
317
|
+
projectKey,
|
|
318
|
+
settings,
|
|
319
|
+
loading,
|
|
320
|
+
error,
|
|
321
|
+
branding: settings?.branding,
|
|
322
|
+
apiBaseUrl,
|
|
323
|
+
refetch: fetchAndApply
|
|
324
|
+
};
|
|
325
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ZOIQContext.Provider, { value, children: loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { children: "Loading..." }) : children });
|
|
326
|
+
}
|
|
327
|
+
function useZOIQContext() {
|
|
328
|
+
return react.useContext(ZOIQContext);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// src/hooks/useProjectSettings.ts
|
|
332
|
+
function useProjectSettings() {
|
|
333
|
+
const ctx = useZOIQContext();
|
|
334
|
+
if (!ctx) {
|
|
335
|
+
return {
|
|
336
|
+
data: null,
|
|
337
|
+
loading: false,
|
|
338
|
+
error: new Error("useProjectSettings must be used within ZOIQProvider"),
|
|
339
|
+
refetch: async () => {
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
return {
|
|
344
|
+
data: ctx.settings,
|
|
345
|
+
loading: ctx.loading,
|
|
346
|
+
error: ctx.error,
|
|
347
|
+
refetch: ctx.refetch
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
function useSectionFlags() {
|
|
351
|
+
const ctx = useZOIQContext();
|
|
352
|
+
const [enabledIds, setEnabledIds] = react.useState(/* @__PURE__ */ new Set());
|
|
353
|
+
const [disabledIds, setDisabledIds] = react.useState(/* @__PURE__ */ new Set());
|
|
354
|
+
const [loading, setLoading] = react.useState(true);
|
|
355
|
+
const [error, setError] = react.useState(null);
|
|
356
|
+
const fetch2 = react.useCallback(async () => {
|
|
357
|
+
if (!ctx?.projectKey) {
|
|
358
|
+
setLoading(false);
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
setLoading(true);
|
|
362
|
+
setError(null);
|
|
363
|
+
try {
|
|
364
|
+
const data = await fetchSectionFlags(ctx.projectKey, ctx.apiBaseUrl);
|
|
365
|
+
setEnabledIds(new Set(data.enabledIds ?? []));
|
|
366
|
+
setDisabledIds(new Set(data.disabledIds ?? []));
|
|
367
|
+
} catch (err) {
|
|
368
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
369
|
+
setEnabledIds(/* @__PURE__ */ new Set());
|
|
370
|
+
setDisabledIds(/* @__PURE__ */ new Set());
|
|
371
|
+
} finally {
|
|
372
|
+
setLoading(false);
|
|
373
|
+
}
|
|
374
|
+
}, [ctx?.projectKey, ctx?.apiBaseUrl]);
|
|
375
|
+
react.useEffect(() => {
|
|
376
|
+
fetch2();
|
|
377
|
+
}, [fetch2]);
|
|
378
|
+
if (!ctx) {
|
|
379
|
+
return {
|
|
380
|
+
enabledIds: /* @__PURE__ */ new Set(),
|
|
381
|
+
disabledIds: /* @__PURE__ */ new Set(),
|
|
382
|
+
loading: false,
|
|
383
|
+
error: new Error("useSectionFlags must be used within ZOIQProvider"),
|
|
384
|
+
refetch: async () => {
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
return { enabledIds, disabledIds, loading, error, refetch: fetch2 };
|
|
389
|
+
}
|
|
390
|
+
function useZOIQEntity(entity, options = {}) {
|
|
391
|
+
const ctx = useZOIQContext();
|
|
392
|
+
const [data, setData] = react.useState(null);
|
|
393
|
+
const [loading, setLoading] = react.useState(true);
|
|
394
|
+
const [error, setError] = react.useState(null);
|
|
395
|
+
const fetch2 = react.useCallback(async () => {
|
|
396
|
+
if (!ctx?.projectKey) {
|
|
397
|
+
setLoading(false);
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
setLoading(true);
|
|
401
|
+
setError(null);
|
|
402
|
+
try {
|
|
403
|
+
const result = await fetchEntityData(
|
|
404
|
+
ctx.projectKey,
|
|
405
|
+
entity,
|
|
406
|
+
{
|
|
407
|
+
id: options.id,
|
|
408
|
+
query: options.query,
|
|
409
|
+
apiKey: ctx.settings?.apiKey ?? void 0
|
|
410
|
+
},
|
|
411
|
+
ctx.apiBaseUrl
|
|
412
|
+
);
|
|
413
|
+
setData(result);
|
|
414
|
+
} catch (err) {
|
|
415
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
416
|
+
setData(null);
|
|
417
|
+
} finally {
|
|
418
|
+
setLoading(false);
|
|
419
|
+
}
|
|
420
|
+
}, [ctx?.projectKey, entity, options.id, JSON.stringify(options.query)]);
|
|
421
|
+
react.useEffect(() => {
|
|
422
|
+
fetch2();
|
|
423
|
+
}, [fetch2]);
|
|
424
|
+
if (!ctx) {
|
|
425
|
+
return {
|
|
426
|
+
data: null,
|
|
427
|
+
loading: false,
|
|
428
|
+
error: new Error("useZOIQEntity must be used within ZOIQProvider"),
|
|
429
|
+
refetch: async () => {
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
return { data, loading, error, refetch: fetch2 };
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
exports.Footer = Footer;
|
|
437
|
+
exports.Header = Header;
|
|
438
|
+
exports.Page = Page;
|
|
439
|
+
exports.ScrollButton = ScrollButton;
|
|
440
|
+
exports.SecondarySidebar = SecondarySidebar;
|
|
441
|
+
exports.Section = Section;
|
|
442
|
+
exports.Subtitle = Subtitle;
|
|
443
|
+
exports.Text = Text;
|
|
444
|
+
exports.Title = Title;
|
|
445
|
+
exports.ToastContainer = ToastContainer;
|
|
446
|
+
exports.ToggleTheme = ToggleTheme;
|
|
447
|
+
exports.ZOIQProvider = ZOIQProvider;
|
|
448
|
+
exports.useProjectSettings = useProjectSettings;
|
|
449
|
+
exports.useSectionFlags = useSectionFlags;
|
|
450
|
+
exports.useZOIQContext = useZOIQContext;
|
|
451
|
+
exports.useZOIQEntity = useZOIQEntity;
|
|
452
|
+
//# sourceMappingURL=client.js.map
|
|
453
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/cn.ts","../src/components/atoms/ScrollButton.tsx","../src/components/atoms/Text.tsx","../src/components/atoms/Subtitle.tsx","../src/components/atoms/Title.tsx","../src/components/atoms/ToggleTheme.tsx","../src/components/layout/Footer.tsx","../src/components/layout/Header.tsx","../src/components/layout/Page.tsx","../src/components/layout/SecondarySidebar.tsx","../src/components/molecules/Section.tsx","../src/components/molecules/ToastContainer.tsx","../src/api/client.ts","../src/theme/applyTheme.ts","../src/context/ZOIQProvider.tsx","../src/hooks/useProjectSettings.ts","../src/hooks/useSectionFlags.ts","../src/hooks/useZOIQEntity.ts"],"names":["twMerge","clsx","useState","useEffect","jsx","jsxs","Fragment","apiBaseUrl","createContext","useCallback","useContext","fetch"],"mappings":";;;;;;;AAGO,SAAS,MAAM,MAAA,EAAsB;AAC1C,EAAA,OAAOA,qBAAA,CAAQC,SAAA,CAAK,MAAM,CAAC,CAAA;AAC7B;ACMO,IAAM,eAAe,CAAC,EAAE,SAAA,EAAW,SAAA,GAAY,KAAI,KAAyB;AACjF,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAIC,eAAS,KAAK,CAAA;AAEhD,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,MAAM,QAAA,GAAW,MAAM,YAAA,CAAa,OAAO,WAAW,WAAA,GAAc,MAAA,CAAO,OAAA,GAAU,SAAA,GAAY,KAAK,CAAA;AACtG,IAAA,MAAA,CAAO,iBAAiB,QAAA,EAAU,QAAA,EAAU,EAAE,OAAA,EAAS,MAAM,CAAA;AAC7D,IAAA,QAAA,EAAS;AACT,IAAA,OAAO,MAAM,MAAA,CAAO,mBAAA,CAAoB,QAAA,EAAU,QAAQ,CAAA;AAAA,EAC5D,CAAA,EAAG,CAAC,SAAS,CAAC,CAAA;AAEd,EAAA,sCACG,KAAA,EAAA,EAAI,SAAA,EAAW,EAAA,CAAG,+CAAA,EAAiD,SAAS,CAAA,EAC3E,QAAA,kBAAAC,cAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,IAAA,EAAK,QAAA;AAAA,MACL,SAAA,EAAW,EAAA;AAAA,QACT,uEAAA;AAAA,QACA,4EAAA;AAAA,QACA,YAAY,aAAA,GAAgB;AAAA,OAC9B;AAAA,MACA,OAAA,EAAS,MAAM,MAAA,CAAO,QAAA,CAAS,EAAE,GAAA,EAAK,CAAA,EAAG,QAAA,EAAU,QAAA,EAAU,CAAA;AAAA,MAC7D,YAAA,EAAW,eAAA;AAAA,MAEX,yCAAC,MAAA,EAAA,EAAK,SAAA,EAAU,sBAAA,EAAuB,aAAA,EAAW,MAAC,QAAA,EAAA,QAAA,EAEnD;AAAA;AAAA,GACF,EACF,CAAA;AAEJ;AC5BO,IAAM,OAAO,CAAC;AAAA,EACnB,QAAA;AAAA,EACA,SAAA;AAAA,EACA,IAAI,EAAA,GAAK,MAAA;AAAA,EACT,GAAG;AACL,CAAA,KAAiB;AACf,EAAA,uBACEA,cAAAA,CAAC,EAAA,EAAA,EAAG,SAAA,EAAW,EAAA,CAAG,mCAAmC,SAAS,CAAA,EAAI,GAAG,KAAA,EAClE,QAAA,EACH,CAAA;AAEJ;ACXO,IAAM,QAAA,GAAW,CAAC,EAAE,QAAA,EAAU,WAAU,KAAqB;AAClE,EAAA,uBACEA,cAAAA,CAAC,IAAA,EAAA,EAAK,EAAA,EAAG,KAAA,EAAM,WAAW,EAAA,CAAG,wDAAA,EAA0D,SAAS,CAAA,EAC7F,QAAA,EACH,CAAA;AAEJ;ACPO,IAAM,KAAA,GAAQ,CAAC,EAAE,QAAA,EAAU,WAAU,KAAkB;AAC5D,EAAA,uBACEA,cAAAA;AAAA,IAAC,IAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,EAAA;AAAA,QACT,qFAAA;AAAA,QACA;AAAA,OACF;AAAA,MAEC;AAAA;AAAA,GACH;AAEJ;ACNO,IAAM,cAAc,CAAC,EAAE,WAAW,QAAA,EAAU,KAAA,EAAO,UAAS,KAAwB;AACzF,EAAA,uBACEA,cAAAA,CAAC,QAAA,EAAA,EAAO,IAAA,EAAK,QAAA,EAAS,SAAS,QAAA,EAAU,SAAA,EAAW,EAAA,CAAG,SAAS,GAAG,YAAA,EAAY,CAAA,uBAAA,EAA0B,SAAS,SAAS,CAAA,CAAA,CAAA,EACxH,sBAAY,OAAA,EACf,CAAA;AAEJ;ACXO,IAAM,MAAA,GAAS,CAAC,EAAE,QAAA,EAAU,WAAU,KAAmB;AAC9D,EAAA,uBACEA,eAAC,QAAA,EAAA,EAAO,SAAA,EAAW,GAAG,sEAAA,EAAwE,SAAS,GACpG,QAAA,EACH,CAAA;AAEJ;ACFO,IAAM,SAAS,CAAC,EAAE,UAAU,SAAA,EAAW,IAAA,EAAM,OAAM,KAAmB;AAC3E,EAAA,uBACEA,cAAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,EAAA;AAAA,QACT,2EAAA;AAAA,QACA,uCAAA;AAAA,QACA;AAAA,OACF;AAAA,MAEA,QAAA,kBAAAC,eAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,oEAAA,EAAqE,cAAW,MAAA,EAC5F,QAAA,EAAA;AAAA,QAAA,IAAA,IAAQ,uBAAOD,cAAAA,CAAC,SAAI,SAAA,EAAU,yBAAA,EAA2B,gBAAK,CAAA,GAAS,IAAA;AAAA,QACvE,QAAA;AAAA,QACA,KAAA,IAAS,uBAAOA,cAAAA,CAAC,SAAI,SAAA,EAAU,yBAAA,EAA2B,iBAAM,CAAA,GAAS;AAAA,OAAA,EAC5E;AAAA;AAAA,GACF;AAEJ;AClBO,IAAM,OAAO,CAAC,EAAE,QAAA,EAAU,SAAA,EAAW,WAAU,KAAiB;AACrE,EAAA,uBACEA,cAAAA,CAAC,KAAA,EAAA,EAAI,WAAW,EAAA,CAAG,0CAAA,EAA4C,SAAS,CAAA,EACtE,QAAA,kBAAAA,eAAC,MAAA,EAAA,EAAK,SAAA,EAAW,GAAG,6BAAA,EAA+B,SAAA,GAAY,SAAS,cAAc,CAAA,EACnF,UACH,CAAA,EACF,CAAA;AAEJ;ACAO,IAAM,mBAAmB,CAAC,EAAE,OAAO,SAAA,EAAW,QAAA,EAAU,YAAW,KAA6B;AACrG,EAAA,uBACEA,cAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,EAAA,CAAG,QAAQ,SAAS,CAAA,EAClC,QAAA,kBAAAA,cAAAA,CAAC,QAAG,SAAA,EAAU,mHAAA,EACX,QAAA,EAAA,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,KAAS;AACnB,IAAA,MAAM,MAAA,GAAS,QAAA,GAAW,IAAI,CAAA,IAAK,KAAA;AACnC,IAAA,MAAM,OAAA,mBACJC,eAAAA,CAAAC,mBAAA,EAAA,EACG,QAAA,EAAA;AAAA,MAAA,IAAA,CAAK,IAAA;AAAA,sBACNF,cAAAA,CAAC,MAAA,EAAA,EAAM,QAAA,EAAA,IAAA,CAAK,KAAA,EAAM;AAAA,KAAA,EACpB,CAAA;AAEF,IAAA,MAAM,QAAQ,UAAA,GACZ,UAAA,CAAW,MAAM,OAAO,CAAA,mBAExBA,cAAAA,CAAC,GAAA,EAAA,EAAE,MAAM,IAAA,CAAK,IAAA,EAAM,WAAW,EAAA,CAAG,gHAAA,EAAkH,SAAS,mEAAA,GAAsE,wCAAwC,GACxQ,QAAA,EAAA,OAAA,EACH,CAAA;AAEF,IAAA,uBACEA,cAAAA,CAAC,IAAA,EAAA,EAAmB,WAAU,QAAA,EAC3B,QAAA,EAAA,KAAA,EAAA,EADM,KAAK,IAEd,CAAA;AAAA,EAEJ,CAAC,GACH,CAAA,EACF,CAAA;AAEJ;ACtCO,IAAM,OAAA,GAAU,CAAC,EAAE,QAAA,EAAU,WAAU,KAAoB;AAChE,EAAA,uBACEA,eAAC,SAAA,EAAA,EAAQ,SAAA,EAAW,GAAG,qEAAA,EAAuE,SAAS,GACpG,QAAA,EACH,CAAA;AAEJ;;;ACRO,IAAM,iBAAiB,MAAM;AAClC,EAAA,OAAO,IAAA;AACT;;;ACJA,eAAsB,kBAAA,CAAmB,YAAoBG,WAAAA,EAAsD;AACjH,EAAA,MAAM,OAAOA,WAAAA,KAAe,OAAO,WAAW,WAAA,GAAc,MAAA,CAAO,SAAS,MAAA,GAAS,EAAA,CAAA;AACrF,EAAA,MAAM,GAAA,GAAM,CAAA,EAAG,IAAA,CAAK,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAC,CAAA,kBAAA,EAAqB,kBAAA,CAAmB,UAAU,CAAC,CAAA,CAAA;AACzF,EAAA,MAAM,MAAM,MAAM,KAAA,CAAM,KAAK,EAAE,WAAA,EAAa,WAAW,CAAA;AACvD,EAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,IAAA,MAAM,IAAI,MAAM,CAAA,gCAAA,EAAmC,GAAA,CAAI,MAAM,CAAA,CAAA,EAAI,GAAA,CAAI,UAAU,CAAA,CAAE,CAAA;AAAA,EACnF;AACA,EAAA,MAAM,IAAA,GAAQ,MAAM,GAAA,CAAI,IAAA,EAAK;AAC7B,EAAA,OAAO,IAAA;AACT;AAKA,eAAsB,iBAAA,CAAkB,YAAoBA,WAAAA,EAA+E;AACzI,EAAA,MAAM,OAAOA,WAAAA,KAAe,OAAO,WAAW,WAAA,GAAc,MAAA,CAAO,SAAS,MAAA,GAAS,EAAA,CAAA;AACrF,EAAA,MAAM,GAAA,GAAM,CAAA,EAAG,IAAA,CAAK,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAC,CAAA,kBAAA,EAAqB,kBAAA,CAAmB,UAAU,CAAC,CAAA,SAAA,CAAA;AACzF,EAAA,MAAM,MAAM,MAAM,KAAA,CAAM,KAAK,EAAE,WAAA,EAAa,WAAW,CAAA;AACvD,EAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,IAAA,MAAM,IAAI,MAAM,CAAA,+BAAA,EAAkC,GAAA,CAAI,MAAM,CAAA,CAAA,EAAI,GAAA,CAAI,UAAU,CAAA,CAAE,CAAA;AAAA,EAClF;AACA,EAAA,OAAO,IAAI,IAAA,EAAK;AAClB;AAMA,eAAsB,eAAA,CAAmB,UAAA,EAAoB,MAAA,EAAgB,OAAA,EAAkFA,WAAAA,EAAwC;AACrM,EAAA,MAAM,OAAOA,WAAAA,KAAe,OAAO,WAAW,WAAA,GAAc,MAAA,CAAO,SAAS,MAAA,GAAS,EAAA,CAAA;AACrF,EAAA,MAAM,MAAA,GAAS,IAAI,eAAA,EAAgB;AACnC,EAAA,IAAI,QAAQ,EAAA,EAAI,MAAA,CAAO,GAAA,CAAI,IAAA,EAAM,QAAQ,EAAE,CAAA;AAC3C,EAAA,IAAI,QAAQ,KAAA,EAAO;AACjB,IAAA,MAAA,CAAO,OAAA,CAAQ,OAAA,CAAQ,KAAK,CAAA,CAAE,QAAQ,CAAC,CAAC,CAAA,EAAG,CAAC,CAAA,KAAM,MAAA,CAAO,GAAA,CAAI,CAAA,EAAG,CAAC,CAAC,CAAA;AAAA,EACpE;AACA,EAAA,MAAM,EAAA,GAAK,OAAO,QAAA,EAAS;AAC3B,EAAA,MAAM,GAAA,GAAM,GAAG,IAAA,CAAK,OAAA,CAAQ,OAAO,EAAE,CAAC,qBAAqB,kBAAA,CAAmB,UAAU,CAAC,CAAA,MAAA,EAAS,kBAAA,CAAmB,MAAM,CAAC,CAAA,EAAG,KAAK,CAAA,CAAA,EAAI,EAAE,KAAK,EAAE,CAAA,CAAA;AACjJ,EAAA,MAAM,UAAkC,EAAC;AACzC,EAAA,IAAI,OAAA,CAAQ,MAAA,EAAQ,OAAA,CAAQ,WAAW,IAAI,OAAA,CAAQ,MAAA;AACnD,EAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,GAAA,EAAK,EAAE,WAAA,EAAa,SAAA,EAAW,OAAA,EAAS,MAAA,CAAO,KAAK,OAAO,CAAA,CAAE,MAAA,GAAS,OAAA,GAAU,QAAW,CAAA;AACnH,EAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,uBAAA,EAA0B,MAAM,CAAA,EAAA,EAAK,IAAI,MAAM,CAAA,CAAA,EAAI,GAAA,CAAI,UAAU,CAAA,CAAE,CAAA;AAAA,EACrF;AACA,EAAA,MAAM,IAAA,GAAQ,MAAM,GAAA,CAAI,IAAA,EAAK;AAC7B,EAAA,OAAO,IAAA;AACT;;;AC7CA,IAAM,sBAAA,GAAiD;AAAA,EACrD,UAAA,EAAY,cAAA;AAAA,EACZ,UAAA,EAAY,cAAA;AAAA,EACZ,OAAA,EAAS,WAAA;AAAA,EACT,SAAA,EAAW,aAAA;AAAA,EACX,IAAA,EAAM,QAAA;AAAA,EACN,cAAA,EAAgB,mBAAA;AAAA,EAChB,OAAA,EAAS,WAAA;AAAA,EACT,iBAAA,EAAmB,sBAAA;AAAA,EACnB,KAAA,EAAO,SAAA;AAAA,EACP,eAAA,EAAiB,oBAAA;AAAA,EACjB,MAAA,EAAQ,UAAA;AAAA,EACR,gBAAA,EAAkB,qBAAA;AAAA,EAClB,WAAA,EAAa,eAAA;AAAA,EACb,qBAAA,EAAuB,0BAAA;AAAA,EACvB,MAAA,EAAQ,UAAA;AAAA,EACR,KAAA,EAAO,SAAA;AAAA,EACP,IAAA,EAAM,QAAA;AAAA,EACN,MAAA,EAAQ,WAAA;AAAA,EACR,MAAA,EAAQ,WAAA;AAAA,EACR,MAAA,EAAQ,WAAA;AAAA,EACR,MAAA,EAAQ,WAAA;AAAA,EACR,MAAA,EAAQ,WAAA;AAAA,EACR,OAAA,EAAS,WAAA;AAAA,EACT,iBAAA,EAAmB,sBAAA;AAAA,EACnB,cAAA,EAAgB,mBAAA;AAAA,EAChB,wBAAA,EAA0B,8BAAA;AAAA,EAC1B,aAAA,EAAe,kBAAA;AAAA,EACf,uBAAA,EAAyB,6BAAA;AAAA,EACzB,aAAA,EAAe,kBAAA;AAAA,EACf,WAAA,EAAa,gBAAA;AAAA,EACb,MAAA,EAAQ;AACV,CAAA;AAEA,IAAM,mBAAA,GAAsB,qBAAA;AAG5B,SAAS,cAAc,KAAA,EAAuB;AAC5C,EAAA,IAAI,CAAC,KAAA,IAAS,OAAO,KAAA,KAAU,UAAU,OAAO,KAAA;AAChD,EAAA,MAAM,OAAA,GAAU,MAAM,IAAA,EAAK;AAC3B,EAAA,OAAO,OAAA,CAAQ,OAAA,CAAQ,IAAA,EAAM,GAAG,CAAA;AAClC;AAOO,SAAS,WAAW,QAAA,EAAiC;AAC1D,EAAA,IAAI,OAAO,aAAa,WAAA,EAAa;AAErC,EAAA,MAAM,OAAO,QAAA,CAAS,eAAA;AACtB,EAAA,MAAM,eAAyB,EAAC;AAEhC,EAAA,IAAI,QAAA,CAAS,UAAA,IAAc,OAAO,QAAA,CAAS,eAAe,QAAA,EAAU;AAClE,IAAA,KAAA,MAAW,CAAC,KAAK,KAAK,CAAA,IAAK,OAAO,OAAA,CAAQ,QAAA,CAAS,UAAU,CAAA,EAAG;AAC9D,MAAA,IAAI,KAAA,IAAS,IAAA,IAAQ,KAAA,KAAU,EAAA,EAAI;AACnC,MAAA,MAAM,MAAA,GAAS,sBAAA,CAAuB,GAAG,CAAA,IAAK,KAAK,GAAG,CAAA,CAAA;AACtD,MAAA,MAAM,UAAA,GAAa,OAAO,KAAA,KAAU,QAAA,IAAY,iBAAA,CAAkB,KAAK,KAAK,CAAA,GAAI,aAAA,CAAc,KAAK,CAAA,GAAK,KAAA;AACxG,MAAA,YAAA,CAAa,IAAA,CAAK,CAAA,EAAG,MAAM,CAAA,EAAA,EAAK,UAAU,CAAA,CAAA,CAAG,CAAA;AAC7C,MAAA,IAAA,CAAK,KAAA,CAAM,WAAA,CAAY,MAAA,EAAQ,UAAU,CAAA;AAAA,IAC3C;AAAA,EACF;AAEA,EAAA,IAAI,QAAA,CAAS,eAAe,MAAA,EAAQ;AAClC,IAAA,KAAA,MAAW,KAAA,IAAS,SAAS,aAAA,EAAe;AAC1C,MAAA,IAAI,CAAC,KAAA,EAAO,IAAA,IAAQ,CAAC,OAAO,KAAA,EAAO;AACnC,MAAA,MAAM,OAAA,GAAU,WAAW,KAAA,CAAM,IAAA,CAAK,QAAQ,MAAA,EAAQ,GAAG,CAAA,CAAE,WAAA,EAAa,CAAA,CAAA;AACxE,MAAA,MAAM,UAAA,GAAa,iBAAA,CAAkB,IAAA,CAAK,KAAA,CAAM,KAAK,IAAI,aAAA,CAAc,KAAA,CAAM,KAAK,CAAA,GAAI,KAAA,CAAM,KAAA;AAC5F,MAAA,YAAA,CAAa,IAAA,CAAK,CAAA,EAAG,OAAO,CAAA,EAAA,EAAK,UAAU,CAAA,CAAA,CAAG,CAAA;AAC9C,MAAA,IAAA,CAAK,KAAA,CAAM,WAAA,CAAY,OAAA,EAAS,UAAU,CAAA;AAAA,IAC5C;AAAA,EACF;AAEA,EAAA,IAAI,QAAA,CAAS,qBAAqB,MAAA,EAAQ;AACxC,IAAA,KAAA,MAAW,IAAA,IAAQ,SAAS,mBAAA,EAAqB;AAC/C,MAAA,MAAM,KAAA,GAAQ,MAAM,YAAA,EAAc,KAAA;AAClC,MAAA,MAAM,OAAO,IAAA,EAAM,IAAA;AACnB,MAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,KAAA,EAAO;AACrB,MAAA,MAAM,OAAA,GAAU,KAAK,IAAA,CAAK,OAAA,CAAQ,QAAQ,GAAG,CAAA,CAAE,aAAa,CAAA,CAAA;AAC5D,MAAA,MAAM,aAAa,iBAAA,CAAkB,IAAA,CAAK,KAAK,CAAA,GAAI,aAAA,CAAc,KAAK,CAAA,GAAI,KAAA;AAC1E,MAAA,YAAA,CAAa,IAAA,CAAK,CAAA,EAAG,OAAO,CAAA,EAAA,EAAK,UAAU,CAAA,CAAA,CAAG,CAAA;AAC9C,MAAA,IAAA,CAAK,KAAA,CAAM,WAAA,CAAY,OAAA,EAAS,UAAU,CAAA;AAAA,IAC5C;AAAA,EACF;AAEA,EAAA,IAAI,YAAA,CAAa,SAAS,CAAA,EAAG;AAC3B,IAAA,MAAM,GAAA,GAAM,CAAA;AAAA,EAAA,EAAc,YAAA,CAAa,IAAA,CAAK,MAAM,CAAC;AAAA;AAAA;AAAA,EAAA,EAAmB,YAAA,CAAa,IAAA,CAAK,MAAM,CAAC;AAAA;AAAA,CAAA;AAC/F,IAAA,IAAI,OAAA,GAAU,QAAA,CAAS,cAAA,CAAe,mBAAmB,CAAA;AACzD,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,OAAA,GAAU,QAAA,CAAS,cAAc,OAAO,CAAA;AACxC,MAAA,OAAA,CAAQ,EAAA,GAAK,mBAAA;AACb,MAAA,QAAA,CAAS,IAAA,CAAK,YAAY,OAAO,CAAA;AAAA,IACnC;AACA,IAAA,OAAA,CAAQ,WAAA,GAAc,GAAA;AAAA,EACxB;AAEA,EAAA,MAAM,WAAW,QAAA,CAAS,QAAA;AAC1B,EAAA,IAAI,QAAA,EAAU,aAAa,IAAA,IAAQ,QAAA,EAAU,eAAe,IAAA,IAAQ,QAAA,EAAU,cAAc,IAAA,EAAM;AAChG,IAAA,MAAM,YAAsB,EAAC;AAC7B,IAAA,IAAI,QAAA,CAAS,aAAa,IAAA,EAAM,SAAA,CAAU,KAAK,CAAA,6BAAA,EAAgC,QAAA,CAAS,WAAA,CAAY,IAAI,CAAA,IAAA,CAAM,CAAA;AAC9G,IAAA,IAAI,QAAA,CAAS,eAAe,IAAA,EAAM,SAAA,CAAU,KAAK,CAAA,6BAAA,EAAgC,QAAA,CAAS,aAAA,CAAc,IAAI,CAAA,IAAA,CAAM,CAAA;AAClH,IAAA,IAAI,QAAA,CAAS,cAAc,IAAA,EAAM,SAAA,CAAU,KAAK,CAAA,6BAAA,EAAgC,QAAA,CAAS,YAAA,CAAa,IAAI,CAAA,IAAA,CAAM,CAAA;AAChH,IAAA,IAAI,SAAA,CAAU,SAAS,CAAA,EAAG;AACxB,MAAA,MAAM,EAAA,GAAK,YAAA;AACX,MAAA,IAAI,EAAA,GAAK,QAAA,CAAS,cAAA,CAAe,EAAE,CAAA;AACnC,MAAA,IAAI,CAAC,EAAA,EAAI;AACP,QAAA,EAAA,GAAK,QAAA,CAAS,cAAc,KAAK,CAAA;AACjC,QAAA,EAAA,CAAG,EAAA,GAAK,EAAA;AACR,QAAA,EAAA,CAAG,SAAA,GAAY,SAAA,CAAU,IAAA,CAAK,EAAE,CAAA;AAChC,QAAA,QAAA,CAAS,IAAA,CAAK,MAAA,CAAO,GAAG,EAAA,CAAG,QAAQ,CAAA;AACnC,QAAA,EAAA,CAAG,MAAA,EAAO;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AACF;AChGA,IAAM,WAAA,GAAcC,oBAAuC,IAAI,CAAA;AAE/D,IAAM,UAAA,GAAa,OAAA,CAAQ,GAAA,CAAI,6BAAA,IAAiC,wBAAA;AAEzD,SAAS,YAAA,CAAa,EAAE,UAAA,EAAY,QAAA,EAAS,EAAsB;AACxE,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAIN,eAAiC,IAAI,CAAA;AACrE,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAIA,eAAS,IAAI,CAAA;AAC3C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIA,eAAuB,IAAI,CAAA;AAErD,EAAA,MAAM,aAAA,GAAgBO,kBAAY,YAAY;AAC5C,IAAA,IAAI,CAAC,UAAA,EAAY;AACf,MAAA,UAAA,CAAW,KAAK,CAAA;AAChB,MAAA;AAAA,IACF;AACA,IAAA,UAAA,CAAW,IAAI,CAAA;AACf,IAAA,QAAA,CAAS,IAAI,CAAA;AACb,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,GAAO,MAAM,kBAAA,CAAmB,UAAA,EAAY,UAAU,CAAA;AAC5D,MAAA,WAAA,CAAY,QAAQ,IAAI,CAAA;AAAA,IAC1B,SAAS,GAAA,EAAK;AACZ,MAAA,QAAA,CAAS,GAAA,YAAe,QAAQ,GAAA,GAAM,IAAI,MAAM,MAAA,CAAO,GAAG,CAAC,CAAC,CAAA;AAC5D,MAAA,WAAA,CAAY,IAAI,CAAA;AAAA,IAClB,CAAA,SAAE;AACA,MAAA,UAAA,CAAW,KAAK,CAAA;AAAA,IAClB;AAAA,EACF,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAGf,EAAAN,gBAAU,MAAM;AACd,IAAA,IAAI,CAAC,UAAA,IAAc,OAAO,MAAA,KAAW,WAAA,EAAa;AAElD,IAAA,MAAM,MAAA,GAAS,IAAI,eAAA,CAAgB,MAAA,CAAO,SAAS,MAAM,CAAA;AACzD,IAAA,MAAM,SAAA,GAAY,MAAA,CAAO,GAAA,CAAI,SAAS,CAAA,KAAM,MAAA;AAC5C,IAAA,MAAM,UAAA,GAAa,MAAA,CAAO,GAAA,CAAI,YAAY,CAAA;AAE1C,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAA,aAAA,EAAc;AACd,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,YAAA,GAAe,CAAC,OAAA,KAA6B;AACjD,MAAA,WAAA,CAAY,OAAO,CAAA;AACnB,MAAA,QAAA,CAAS,IAAI,CAAA;AACb,MAAA,UAAA,CAAW,KAAK,CAAA;AAAA,IAClB,CAAA;AAEA,IAAA,IAAI,CAAC,UAAA,IAAc,CAAC,UAAA,CAAW,MAAK,EAAG;AACrC,MAAA,aAAA,EAAc;AACd,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,GAAA,GAAM,WAAW,IAAA,EAAK;AAC5B,IAAA,IAAI,SAAA,GAAY,KAAA;AAChB,IAAA,KAAA,CAAM,KAAK,EAAE,WAAA,EAAa,QAAQ,CAAA,CAC/B,KAAK,CAAA,GAAA,KAAO;AACX,MAAA,IAAI,SAAA,EAAW;AACf,MAAA,IAAI,CAAC,IAAI,EAAA,EAAI,MAAM,IAAI,KAAA,CAAM,GAAA,CAAI,cAAc,wBAAwB,CAAA;AACvE,MAAA,OAAO,IAAI,IAAA,EAAK;AAAA,IAClB,CAAC,CAAA,CACA,IAAA,CAAK,CAAC,IAAA,KAA0B;AAC/B,MAAA,IAAI,SAAA,EAAW;AACf,MAAA,YAAA,CAAa,IAAI,CAAA;AAAA,IACnB,CAAC,CAAA,CACA,KAAA,CAAM,CAAA,GAAA,KAAO;AACZ,MAAA,IAAI,SAAA,EAAW;AACf,MAAA,QAAA,CAAS,GAAA,YAAe,QAAQ,GAAA,GAAM,IAAI,MAAM,MAAA,CAAO,GAAG,CAAC,CAAC,CAAA;AAC5D,MAAA,WAAA,CAAY,IAAI,CAAA;AAChB,MAAA,UAAA,CAAW,KAAK,CAAA;AAAA,IAClB,CAAC,CAAA;AAEH,IAAA,OAAO,MAAM;AACX,MAAA,SAAA,GAAY,IAAA;AAAA,IACd,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,UAAA,EAAY,aAAa,CAAC,CAAA;AAI9B,EAAAA,gBAAU,MAAM;AACd,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,UAAA,CAAW,QAAQ,CAAA;AAAA,IACrB;AAAA,EACF,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAEb,EAAA,MAAM,KAAA,GAA0B;AAAA,IAC9B,UAAA;AAAA,IACA,QAAA;AAAA,IACA,OAAA;AAAA,IACA,KAAA;AAAA,IACA,UAAU,QAAA,EAAU,QAAA;AAAA,IACpB,UAAA;AAAA,IACA,OAAA,EAAS;AAAA,GACX;AAEA,EAAA,uBAAOC,cAAAA,CAAC,WAAA,CAAY,QAAA,EAAZ,EAAqB,KAAA,EAAe,QAAA,EAAA,OAAA,mBAAUA,cAAAA,CAAC,KAAA,EAAA,EAAI,QAAA,EAAA,YAAA,EAAU,CAAA,GAAS,QAAA,EAAS,CAAA;AACzF;AAEO,SAAS,cAAA,GAA0C;AACxD,EAAA,OAAOM,iBAAW,WAAW,CAAA;AAC/B;;;AC5GO,SAAS,kBAAA,GAA+C;AAC7D,EAAA,MAAM,MAAM,cAAA,EAAe;AAC3B,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,OAAO;AAAA,MACL,IAAA,EAAM,IAAA;AAAA,MACN,OAAA,EAAS,KAAA;AAAA,MACT,KAAA,EAAO,IAAI,KAAA,CAAM,qDAAqD,CAAA;AAAA,MACtE,SAAS,YAAY;AAAA,MAAC;AAAA,KACxB;AAAA,EACF;AACA,EAAA,OAAO;AAAA,IACL,MAAM,GAAA,CAAI,QAAA;AAAA,IACV,SAAS,GAAA,CAAI,OAAA;AAAA,IACb,OAAO,GAAA,CAAI,KAAA;AAAA,IACX,SAAS,GAAA,CAAI;AAAA,GACf;AACF;ACbO,SAAS,eAAA,GAAyC;AACvD,EAAA,MAAM,MAAM,cAAA,EAAe;AAC3B,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,IAAIR,cAAAA,iBAAsB,IAAI,KAAK,CAAA;AACnE,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,IAAIA,cAAAA,iBAAsB,IAAI,KAAK,CAAA;AACrE,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAIA,eAAS,IAAI,CAAA;AAC3C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIA,eAAuB,IAAI,CAAA;AAErD,EAAA,MAAMS,MAAAA,GAAQF,kBAAY,YAAY;AACpC,IAAA,IAAI,CAAC,KAAK,UAAA,EAAY;AACpB,MAAA,UAAA,CAAW,KAAK,CAAA;AAChB,MAAA;AAAA,IACF;AACA,IAAA,UAAA,CAAW,IAAI,CAAA;AACf,IAAA,QAAA,CAAS,IAAI,CAAA;AACb,IAAA,IAAI;AACF,MAAA,MAAM,OAAO,MAAM,iBAAA,CAAkB,GAAA,CAAI,UAAA,EAAY,IAAI,UAAU,CAAA;AACnE,MAAA,aAAA,CAAc,IAAI,GAAA,CAAI,IAAA,CAAK,UAAA,IAAc,EAAE,CAAC,CAAA;AAC5C,MAAA,cAAA,CAAe,IAAI,GAAA,CAAI,IAAA,CAAK,WAAA,IAAe,EAAE,CAAC,CAAA;AAAA,IAChD,SAAS,GAAA,EAAK;AACZ,MAAA,QAAA,CAAS,GAAA,YAAe,QAAQ,GAAA,GAAM,IAAI,MAAM,MAAA,CAAO,GAAG,CAAC,CAAC,CAAA;AAC5D,MAAA,aAAA,iBAAc,IAAI,KAAK,CAAA;AACvB,MAAA,cAAA,iBAAe,IAAI,KAAK,CAAA;AAAA,IAC1B,CAAA,SAAE;AACA,MAAA,UAAA,CAAW,KAAK,CAAA;AAAA,IAClB;AAAA,EACF,GAAG,CAAC,GAAA,EAAK,UAAA,EAAY,GAAA,EAAK,UAAU,CAAC,CAAA;AAErC,EAAAN,gBAAU,MAAM;AACd,IAAAQ,MAAAA,EAAM;AAAA,EACR,CAAA,EAAG,CAACA,MAAK,CAAC,CAAA;AAEV,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,OAAO;AAAA,MACL,UAAA,sBAAgB,GAAA,EAAI;AAAA,MACpB,WAAA,sBAAiB,GAAA,EAAI;AAAA,MACrB,OAAA,EAAS,KAAA;AAAA,MACT,KAAA,EAAO,IAAI,KAAA,CAAM,kDAAkD,CAAA;AAAA,MACnE,SAAS,YAAY;AAAA,MAAC;AAAA,KACxB;AAAA,EACF;AAEA,EAAA,OAAO,EAAE,UAAA,EAAY,WAAA,EAAa,OAAA,EAAS,KAAA,EAAO,SAASA,MAAAA,EAAM;AACnE;ACtCO,SAAS,aAAA,CAA2B,MAAA,EAAgB,OAAA,GAAgC,EAAC,EAA2B;AACrH,EAAA,MAAM,MAAM,cAAA,EAAe;AAC3B,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAIT,eAAmB,IAAI,CAAA;AAC/C,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAIA,eAAS,IAAI,CAAA;AAC3C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIA,eAAuB,IAAI,CAAA;AAErD,EAAA,MAAMS,MAAAA,GAAQF,kBAAY,YAAY;AACpC,IAAA,IAAI,CAAC,KAAK,UAAA,EAAY;AACpB,MAAA,UAAA,CAAW,KAAK,CAAA;AAChB,MAAA;AAAA,IACF;AACA,IAAA,UAAA,CAAW,IAAI,CAAA;AACf,IAAA,QAAA,CAAS,IAAI,CAAA;AACb,IAAA,IAAI;AACF,MAAA,MAAM,SAAS,MAAM,eAAA;AAAA,QACnB,GAAA,CAAI,UAAA;AAAA,QACJ,MAAA;AAAA,QACA;AAAA,UACE,IAAI,OAAA,CAAQ,EAAA;AAAA,UACZ,OAAO,OAAA,CAAQ,KAAA;AAAA,UACf,MAAA,EAAQ,GAAA,CAAI,QAAA,EAAU,MAAA,IAAU,KAAA;AAAA,SAClC;AAAA,QACA,GAAA,CAAI;AAAA,OACN;AACA,MAAA,OAAA,CAAQ,MAAM,CAAA;AAAA,IAChB,SAAS,GAAA,EAAK;AACZ,MAAA,QAAA,CAAS,GAAA,YAAe,QAAQ,GAAA,GAAM,IAAI,MAAM,MAAA,CAAO,GAAG,CAAC,CAAC,CAAA;AAC5D,MAAA,OAAA,CAAQ,IAAI,CAAA;AAAA,IACd,CAAA,SAAE;AACA,MAAA,UAAA,CAAW,KAAK,CAAA;AAAA,IAClB;AAAA,EACF,CAAA,EAAG,CAAC,GAAA,EAAK,UAAA,EAAY,MAAA,EAAQ,OAAA,CAAQ,EAAA,EAAI,IAAA,CAAK,SAAA,CAAU,OAAA,CAAQ,KAAK,CAAC,CAAC,CAAA;AAEvE,EAAAN,gBAAU,MAAM;AACd,IAAAQ,MAAAA,EAAM;AAAA,EACR,CAAA,EAAG,CAACA,MAAK,CAAC,CAAA;AAEV,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,OAAO;AAAA,MACL,IAAA,EAAM,IAAA;AAAA,MACN,OAAA,EAAS,KAAA;AAAA,MACT,KAAA,EAAO,IAAI,KAAA,CAAM,gDAAgD,CAAA;AAAA,MACjE,SAAS,YAAY;AAAA,MAAC;AAAA,KACxB;AAAA,EACF;AAEA,EAAA,OAAO,EAAE,IAAA,EAAM,OAAA,EAAS,KAAA,EAAO,SAASA,MAAAA,EAAM;AAChD","file":"client.js","sourcesContent":["import { type ClassValue, clsx } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n","'use client'\n\nimport React, { useState, useEffect } from 'react'\nimport { cn } from '../../utils/cn'\n\nexport interface ScrollButtonProps {\n className?: string\n /** Scroll Y threshold above which the button is shown (default 200) */\n threshold?: number\n}\n\nexport const ScrollButton = ({ className, threshold = 200 }: ScrollButtonProps) => {\n const [isVisible, setIsVisible] = useState(false)\n\n useEffect(() => {\n const onScroll = () => setIsVisible(typeof window !== 'undefined' ? window.scrollY > threshold : false)\n window.addEventListener('scroll', onScroll, { passive: true })\n onScroll()\n return () => window.removeEventListener('scroll', onScroll)\n }, [threshold])\n\n return (\n <div className={cn('fixed bottom-10 right-10 z-50 hidden md:block', className)}>\n <button\n type=\"button\"\n className={cn(\n 'rounded-full p-2 cursor-pointer shadow-lg transition-all duration-300',\n 'bg-[var(--primary,hsl(220,70%,50%))] text-[var(--primary-foreground,#fff)]',\n isVisible ? 'opacity-100' : 'opacity-0 pointer-events-none'\n )}\n onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })}\n aria-label=\"Scroll to top\"\n >\n <span className=\"inline-block w-6 h-6\" aria-hidden>\n ↑\n </span>\n </button>\n </div>\n )\n}\n\nexport default ScrollButton\n","'use client'\n\nimport React from 'react'\nimport { cn } from '../../utils/cn'\n\nexport interface TextProps extends React.HTMLAttributes<HTMLElement> {\n children?: React.ReactNode\n className?: string\n as?: 'span' | 'p' | 'div'\n}\n\nexport const Text = ({\n children,\n className,\n as: As = 'span',\n ...props\n}: TextProps) => {\n return (\n <As className={cn('text-[var(--text-primary,#111)]', className)} {...props}>\n {children}\n </As>\n )\n}\n\nexport default Text\n","'use client'\n\nimport React from 'react'\nimport { Text } from './Text'\nimport { cn } from '../../utils/cn'\n\nexport interface SubtitleProps {\n children?: React.ReactNode\n className?: string\n}\n\nexport const Subtitle = ({ children, className }: SubtitleProps) => {\n return (\n <Text as=\"div\" className={cn('text-sm md:text-base text-[var(--text-secondary,#666)]', className)}>\n {children}\n </Text>\n )\n}\n\nexport default Subtitle\n","'use client'\n\nimport React from 'react'\nimport { cn } from '../../utils/cn'\n\nexport interface TitleProps {\n children?: React.ReactNode\n className?: string\n}\n\nexport const Title = ({ children, className }: TitleProps) => {\n return (\n <h1\n className={cn(\n 'text-2xl md:text-3xl mb-0 font-semibold text-pretty text-[var(--text-primary,#111)]',\n className\n )}\n >\n {children}\n </h1>\n )\n}\n\nexport default Title\n","'use client'\n\nimport React from 'react'\nimport { cn } from '../../utils/cn'\n\nexport interface ToggleThemeProps {\n className?: string\n /** Callback when user requests theme toggle; host app controls actual theme state */\n onToggle?: () => void\n /** Optional current theme name for rendering (e.g. \"light\" | \"dark\") */\n theme?: string\n /** Content to show (e.g. icon). If not provided, shows \"Theme\" */\n children?: React.ReactNode\n}\n\nexport const ToggleTheme = ({ className, onToggle, theme, children }: ToggleThemeProps) => {\n return (\n <button type=\"button\" onClick={onToggle} className={cn(className)} aria-label={`Toggle theme (current: ${theme ?? 'unknown'})`}>\n {children ?? 'Theme'}\n </button>\n )\n}\n\nexport default ToggleTheme\n","'use client'\n\nimport React from 'react'\nimport { cn } from '../../utils/cn'\n\nexport interface FooterProps {\n children?: React.ReactNode\n className?: string\n}\n\nexport const Footer = ({ children, className }: FooterProps) => {\n return (\n <footer className={cn('w-full border-t border-[var(--border,transparent)] py-6 px-4 md:px-8', className)}>\n {children}\n </footer>\n )\n}\n\nexport default Footer\n","'use client'\n\nimport React from 'react'\nimport { cn } from '../../utils/cn'\n\nexport interface HeaderProps {\n children?: React.ReactNode\n className?: string\n /** Optional left slot (e.g. logo, nav) */\n left?: React.ReactNode\n /** Optional right slot (e.g. user menu, theme toggle) */\n right?: React.ReactNode\n}\n\nexport const Header = ({ children, className, left, right }: HeaderProps) => {\n return (\n <header\n className={cn(\n 'w-full h-14 sticky top-0 z-50 border-b border-[var(--border,transparent)]',\n 'bg-[var(--sidebar,var(--background))]',\n className\n )}\n >\n <nav className=\"flex h-full w-full items-center justify-between gap-4 px-4 md:px-6\" aria-label=\"Main\">\n {left != null ? <div className=\"flex items-center gap-3\">{left}</div> : null}\n {children}\n {right != null ? <div className=\"flex items-center gap-3\">{right}</div> : null}\n </nav>\n </header>\n )\n}\n\nexport default Header\n","'use client'\n\nimport React from 'react'\nimport { cn } from '../../utils/cn'\n\nexport interface PageProps {\n children?: React.ReactNode\n className?: string\n /** Optional full-width layout (no horizontal padding) */\n fullWidth?: boolean\n}\n\nexport const Page = ({ children, className, fullWidth }: PageProps) => {\n return (\n <div className={cn('w-full flex-1 flex flex-col min-h-screen', className)}>\n <main className={cn('w-full flex-1 flex flex-col', fullWidth ? 'px-0' : 'px-4 md:px-8')}>\n {children}\n </main>\n </div>\n )\n}\n\nexport default Page\n","'use client'\n\nimport React from 'react'\nimport { cn } from '../../utils/cn'\n\nexport interface SecondarySidebarItem {\n label: string\n href: string\n icon?: React.ReactNode\n}\n\nexport interface SecondarySidebarProps {\n items: SecondarySidebarItem[]\n className?: string\n /** Optional: return true if item is active (e.g. from pathname) */\n isActive?: (item: SecondarySidebarItem) => boolean\n /** Optional: custom link renderer (e.g. Next.js Link). Default: <a> */\n renderLink?: (item: SecondarySidebarItem, children: React.ReactNode) => React.ReactNode\n}\n\nexport const SecondarySidebar = ({ items, className, isActive, renderLink }: SecondarySidebarProps) => {\n return (\n <div className={cn('px-4', className)}>\n <ul className=\"flex p-1 rounded-lg bg-[var(--accent,#f4f4f5)] border border-[var(--border,#e4e4e7)] w-full justify-between gap-4\">\n {items.map((item) => {\n const active = isActive?.(item) ?? false\n const content = (\n <>\n {item.icon}\n <span>{item.label}</span>\n </>\n )\n const child = renderLink ? (\n renderLink(item, content)\n ) : (\n <a href={item.href} className={cn('flex items-center justify-center gap-2 text-center h-8 w-full rounded-md text-sm font-medium transition-colors', active ? 'bg-[var(--sidebar-accent,var(--accent))] text-[var(--foreground)]' : 'text-[var(--muted-foreground,#71717a)]')}>\n {content}\n </a>\n )\n return (\n <li key={item.href} className=\"w-full\">\n {child}\n </li>\n )\n })}\n </ul>\n </div>\n )\n}\n\nexport default SecondarySidebar\n","'use client'\n\nimport React from 'react'\nimport { cn } from '../../utils/cn'\n\nexport interface SectionProps {\n children?: React.ReactNode\n className?: string\n}\n\nexport const Section = ({ children, className }: SectionProps) => {\n return (\n <section className={cn('max-w-7xl mx-auto w-full px-8 md:px-0 pt-12 md:pt-24 pb-12 md:pb-24', className)}>\n {children}\n </section>\n )\n}\n\nexport default Section\n","'use client'\n\nimport React from 'react'\n\n/**\n * Placeholder for toast container. Host app should render Toaster from \"sonner\" (or similar)\n * inside the app; ZOIQ does not bundle a toast library.\n */\nexport const ToastContainer = () => {\n return null\n}\n\nexport default ToastContainer\n","import type { ProjectSettings } from '../context/types'\n\n/**\n * Default fetch for project config. Calls GET {apiBaseUrl}/project/{projectKey}\n * and expects JSON matching ProjectSettings.\n */\nexport async function fetchProjectConfig(projectKey: string, apiBaseUrl?: string): Promise<ProjectSettings | null> {\n const base = apiBaseUrl ?? (typeof window !== 'undefined' ? window.location.origin : '')\n const url = `${base.replace(/\\/$/, '')}/api/zoiq/project/${encodeURIComponent(projectKey)}`\n const res = await fetch(url, { credentials: 'include' })\n if (!res.ok) {\n throw new Error(`Failed to fetch project config: ${res.status} ${res.statusText}`)\n }\n const data = (await res.json()) as ProjectSettings\n return data\n}\n\n/**\n * Fetch section flags for the project. Calls GET {apiBaseUrl}/project/{projectKey}/sections\n */\nexport async function fetchSectionFlags(projectKey: string, apiBaseUrl?: string): Promise<{ enabledIds: string[]; disabledIds: string[] }> {\n const base = apiBaseUrl ?? (typeof window !== 'undefined' ? window.location.origin : '')\n const url = `${base.replace(/\\/$/, '')}/api/zoiq/project/${encodeURIComponent(projectKey)}/sections`\n const res = await fetch(url, { credentials: 'include' })\n if (!res.ok) {\n throw new Error(`Failed to fetch section flags: ${res.status} ${res.statusText}`)\n }\n return res.json() as Promise<{ enabledIds: string[]; disabledIds: string[] }>\n}\n\n/**\n * Fetch entity data for the project. Calls GET {apiBaseUrl}/project/{projectKey}/data/{entity}?id=...\n * If apiKey is provided (e.g. from project settings), it is sent as x-api-key header for auth.\n */\nexport async function fetchEntityData<T>(projectKey: string, entity: string, options: { id?: string; query?: Record<string, string>; apiKey?: string | null }, apiBaseUrl?: string): Promise<T | null> {\n const base = apiBaseUrl ?? (typeof window !== 'undefined' ? window.location.origin : '')\n const params = new URLSearchParams()\n if (options.id) params.set('id', options.id)\n if (options.query) {\n Object.entries(options.query).forEach(([k, v]) => params.set(k, v))\n }\n const qs = params.toString()\n const url = `${base.replace(/\\/$/, '')}/api/zoiq/project/${encodeURIComponent(projectKey)}/data/${encodeURIComponent(entity)}${qs ? `?${qs}` : ''}`\n const headers: Record<string, string> = {}\n if (options.apiKey) headers['x-api-key'] = options.apiKey\n const res = await fetch(url, { credentials: 'include', headers: Object.keys(headers).length ? headers : undefined })\n if (!res.ok) {\n throw new Error(`Failed to fetch entity ${entity}: ${res.status} ${res.statusText}`)\n }\n const data = (await res.json()) as T\n return data\n}\n","import type { ProjectSettings } from '../context/types'\n\n/**\n * Maps styleGuide keys to CSS variable names (e.g. for :root or a wrapper).\n * Aligns with portal globals.css / Tailwind tokens.\n */\nconst STYLE_GUIDE_TO_CSS_VAR: Record<string, string> = {\n background: '--background',\n foreground: '--foreground',\n primary: '--primary',\n secondary: '--secondary',\n card: '--card',\n cardForeground: '--card-foreground',\n popover: '--popover',\n popoverForeground: '--popover-foreground',\n muted: '--muted',\n mutedForeground: '--muted-foreground',\n accent: '--accent',\n accentForeground: '--accent-foreground',\n destructive: '--destructive',\n destructiveForeground: '--destructive-foreground',\n border: '--border',\n input: '--input',\n ring: '--ring',\n chart1: '--chart-1',\n chart2: '--chart-2',\n chart3: '--chart-3',\n chart4: '--chart-4',\n chart5: '--chart-5',\n sidebar: '--sidebar',\n sidebarForeground: '--sidebar-foreground',\n sidebarPrimary: '--sidebar-primary',\n sidebarPrimaryForeground: '--sidebar-primary-foreground',\n sidebarAccent: '--sidebar-accent',\n sidebarAccentForeground: '--sidebar-accent-foreground',\n sidebarBorder: '--sidebar-border',\n sidebarRing: '--sidebar-ring',\n radius: '--radius',\n}\n\nconst ZOIQ_THEME_STYLE_ID = 'zoiq-theme-override'\n\n/** Normalize \"H, S%, L%\" (DB) to \"H S% L%\" for Tailwind hsl(var(--x)) */\nfunction toTailwindHsl(value: string): string {\n if (!value || typeof value !== 'string') return value\n const trimmed = value.trim()\n return trimmed.replace(/,/g, ' ')\n}\n\n/**\n * Injects theme from ProjectSettings into document so it overrides layout.css / globals.css.\n * Injects a <style id=\"zoiq-theme-override\"> with :root { ... } at the end of <head>,\n * so it wins in the cascade. Values from DB are \"H, S%, L%\"; we output \"H S% L%\" for Tailwind.\n */\nexport function applyTheme(settings: ProjectSettings): void {\n if (typeof document === 'undefined') return\n\n const root = document.documentElement\n const declarations: string[] = []\n\n if (settings.styleGuide && typeof settings.styleGuide === 'object') {\n for (const [key, value] of Object.entries(settings.styleGuide)) {\n if (value == null || value === '') continue\n const cssVar = STYLE_GUIDE_TO_CSS_VAR[key] ?? `--${key}`\n const normalized = typeof value === 'string' && /^\\s*[\\d.-]+\\s*,/.test(value) ? toTailwindHsl(value) : (value as string)\n declarations.push(`${cssVar}: ${normalized};`)\n root.style.setProperty(cssVar, normalized)\n }\n }\n\n if (settings.definedColors?.length) {\n for (const color of settings.definedColors) {\n if (!color?.name || !color?.value) continue\n const varName = `--color-${color.name.replace(/\\s+/g, '-').toLowerCase()}`\n const normalized = /^\\s*[\\d.-]+\\s*,/.test(color.value) ? toTailwindHsl(color.value) : color.value\n declarations.push(`${varName}: ${normalized};`)\n root.style.setProperty(varName, normalized)\n }\n }\n\n if (settings.clientDefinedColors?.length) {\n for (const item of settings.clientDefinedColors) {\n const value = item?.definedColor?.value\n const name = item?.name\n if (!name || !value) continue\n const varName = `--${name.replace(/\\s+/g, '-').toLowerCase()}`\n const normalized = /^\\s*[\\d.-]+\\s*,/.test(value) ? toTailwindHsl(value) : value\n declarations.push(`${varName}: ${normalized};`)\n root.style.setProperty(varName, normalized)\n }\n }\n\n if (declarations.length > 0) {\n const css = `:root {\\n ${declarations.join('\\n ')}\\n}\\n.dark {\\n ${declarations.join('\\n ')}\\n}\\n`\n let styleEl = document.getElementById(ZOIQ_THEME_STYLE_ID) as HTMLStyleElement | null\n if (!styleEl) {\n styleEl = document.createElement('style')\n styleEl.id = ZOIQ_THEME_STYLE_ID\n document.head.appendChild(styleEl)\n }\n styleEl.textContent = css\n }\n\n const branding = settings.branding\n if (branding?.primaryFont?.link || branding?.secondaryFont?.link || branding?.tertiaryFont?.link) {\n const fontLinks: string[] = []\n if (branding.primaryFont?.link) fontLinks.push(`<link rel=\"stylesheet\" href=\"${branding.primaryFont.link}\" />`)\n if (branding.secondaryFont?.link) fontLinks.push(`<link rel=\"stylesheet\" href=\"${branding.secondaryFont.link}\" />`)\n if (branding.tertiaryFont?.link) fontLinks.push(`<link rel=\"stylesheet\" href=\"${branding.tertiaryFont.link}\" />`)\n if (fontLinks.length > 0) {\n const id = 'zoiq-fonts'\n let el = document.getElementById(id)\n if (!el) {\n el = document.createElement('div')\n el.id = id\n el.innerHTML = fontLinks.join('')\n document.head.append(...el.children)\n el.remove()\n }\n }\n }\n}\n","'use client'\n\nimport React, { createContext, useCallback, useContext, useEffect, useState } from 'react'\nimport { fetchProjectConfig } from '../api/client'\nimport { applyTheme } from '../theme/applyTheme'\nimport type { ProjectSettings } from './types'\n\nexport interface ZOIQProviderProps {\n /** Project key (e.g. project ID or public API key) to fetch theme and config */\n projectKey: string\n /** Base URL for the project config API (e.g. https://your-portal.com/api/zoiq) */\n /** Optional custom fetcher; if provided, apiBaseUrl is ignored for config fetch */\n children: React.ReactNode\n}\n\ninterface ZOIQContextValue {\n projectKey: string\n apiBaseUrl?: string\n settings: ProjectSettings | null\n loading: boolean\n error: Error | null\n refetch: () => Promise<void>\n branding: ProjectSettings['branding'] | null\n}\n\nconst ZOIQContext = createContext<ZOIQContextValue | null>(null)\n\nconst apiBaseUrl = process.env.NEXT_PUBLIC_ZOIQ_API_BASE_URL || 'https://portal.zoiq.io'\n\nexport function ZOIQProvider({ projectKey, children }: ZOIQProviderProps) {\n const [settings, setSettings] = useState<ProjectSettings | null>(null)\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<Error | null>(null)\n\n const fetchAndApply = useCallback(async () => {\n if (!projectKey) {\n setLoading(false)\n return\n }\n setLoading(true)\n setError(null)\n try {\n const data = await fetchProjectConfig(projectKey, apiBaseUrl)\n setSettings(data ?? null)\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)))\n setSettings(null)\n } finally {\n setLoading(false)\n }\n }, [projectKey])\n\n // Preview mode: ?preview=true&previewUrl=... — load project settings by fetching the preview JSON URL directly (S3 public URL)\n useEffect(() => {\n if (!projectKey || typeof window === 'undefined') return\n\n const params = new URLSearchParams(window.location.search)\n const isPreview = params.get('preview') === 'true'\n const previewUrl = params.get('previewUrl')\n\n if (!isPreview) {\n fetchAndApply()\n return\n }\n\n const applyPayload = (payload: ProjectSettings) => {\n setSettings(payload)\n setError(null)\n setLoading(false)\n }\n\n if (!previewUrl || !previewUrl.trim()) {\n fetchAndApply()\n return\n }\n\n const url = previewUrl.trim()\n let cancelled = false\n fetch(url, { credentials: 'omit' })\n .then(res => {\n if (cancelled) return\n if (!res.ok) throw new Error(res.statusText || 'Failed to load preview')\n return res.json()\n })\n .then((data: ProjectSettings) => {\n if (cancelled) return\n applyPayload(data)\n })\n .catch(err => {\n if (cancelled) return\n setError(err instanceof Error ? err : new Error(String(err)))\n setSettings(null)\n setLoading(false)\n })\n\n return () => {\n cancelled = true\n }\n }, [projectKey, fetchAndApply])\n\n // Apply theme to document root whenever settings change so Tailwind in the client app\n // picks up CSS variables (e.g. --primary, --background) from :root\n useEffect(() => {\n if (settings) {\n applyTheme(settings)\n }\n }, [settings])\n\n const value: ZOIQContextValue = {\n projectKey,\n settings,\n loading,\n error,\n branding: settings?.branding,\n apiBaseUrl,\n refetch: fetchAndApply,\n }\n\n return <ZOIQContext.Provider value={value}>{loading ? <div>Loading...</div> : children}</ZOIQContext.Provider>\n}\n\nexport function useZOIQContext(): ZOIQContextValue | null {\n return useContext(ZOIQContext)\n}\n\nexport function useZOIQBranding(): ZOIQContextValue['branding'] | null {\n const ctx = useZOIQContext()\n return ctx?.branding ?? null\n}\n\nexport function useStyleGuide(): ProjectSettings['styleGuide'] | null {\n const ctx = useZOIQContext()\n return ctx?.settings?.styleGuide ?? (null as ProjectSettings['styleGuide'] | null)\n}\n","'use client'\n\nimport { useZOIQContext } from '../context/ZOIQProvider'\n\nexport interface UseProjectSettingsResult {\n data: import('../context/types').ProjectSettings | null\n loading: boolean\n error: Error | null\n refetch: () => Promise<void>\n}\n\n/**\n * Returns the full project theme/settings for the current project key.\n * Must be used within ZOIQProvider. Data is the same that the provider used to apply theme.\n */\nexport function useProjectSettings(): UseProjectSettingsResult {\n const ctx = useZOIQContext()\n if (!ctx) {\n return {\n data: null,\n loading: false,\n error: new Error('useProjectSettings must be used within ZOIQProvider'),\n refetch: async () => {},\n }\n }\n return {\n data: ctx.settings,\n loading: ctx.loading,\n error: ctx.error,\n refetch: ctx.refetch,\n }\n}\n","'use client'\n\nimport { useCallback, useEffect, useState } from 'react'\nimport { fetchSectionFlags } from '../api/client'\nimport { useZOIQContext } from '../context/ZOIQProvider'\n\nexport interface UseSectionFlagsResult {\n enabledIds: Set<string>\n disabledIds: Set<string>\n loading: boolean\n error: Error | null\n refetch: () => Promise<void>\n}\n\n/**\n * Returns which section IDs are enabled or disabled for the current project.\n * Must be used within ZOIQProvider. Uses the same apiBaseUrl as the provider.\n */\nexport function useSectionFlags(): UseSectionFlagsResult {\n const ctx = useZOIQContext()\n const [enabledIds, setEnabledIds] = useState<Set<string>>(new Set())\n const [disabledIds, setDisabledIds] = useState<Set<string>>(new Set())\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<Error | null>(null)\n\n const fetch = useCallback(async () => {\n if (!ctx?.projectKey) {\n setLoading(false)\n return\n }\n setLoading(true)\n setError(null)\n try {\n const data = await fetchSectionFlags(ctx.projectKey, ctx.apiBaseUrl)\n setEnabledIds(new Set(data.enabledIds ?? []))\n setDisabledIds(new Set(data.disabledIds ?? []))\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)))\n setEnabledIds(new Set())\n setDisabledIds(new Set())\n } finally {\n setLoading(false)\n }\n }, [ctx?.projectKey, ctx?.apiBaseUrl])\n\n useEffect(() => {\n fetch()\n }, [fetch])\n\n if (!ctx) {\n return {\n enabledIds: new Set(),\n disabledIds: new Set(),\n loading: false,\n error: new Error('useSectionFlags must be used within ZOIQProvider'),\n refetch: async () => {},\n }\n }\n\n return { enabledIds, disabledIds, loading, error, refetch: fetch }\n}\n","'use client'\n\nimport { useCallback, useEffect, useState } from 'react'\nimport { fetchEntityData } from '../api/client'\nimport { useZOIQContext } from '../context/ZOIQProvider'\n\nexport interface UseZOIQEntityOptions {\n id?: string\n query?: Record<string, string>\n}\n\nexport interface UseZOIQEntityResult<T> {\n data: T | null\n loading: boolean\n error: Error | null\n refetch: () => Promise<void>\n}\n\n/**\n * Fetches custom entity data for the current project.\n * Must be used within ZOIQProvider.\n */\nexport function useZOIQEntity<T = unknown>(entity: string, options: UseZOIQEntityOptions = {}): UseZOIQEntityResult<T> {\n const ctx = useZOIQContext()\n const [data, setData] = useState<T | null>(null)\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<Error | null>(null)\n\n const fetch = useCallback(async () => {\n if (!ctx?.projectKey) {\n setLoading(false)\n return\n }\n setLoading(true)\n setError(null)\n try {\n const result = await fetchEntityData<T>(\n ctx.projectKey,\n entity,\n {\n id: options.id,\n query: options.query,\n apiKey: ctx.settings?.apiKey ?? undefined,\n },\n ctx.apiBaseUrl,\n )\n setData(result)\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)))\n setData(null)\n } finally {\n setLoading(false)\n }\n }, [ctx?.projectKey, entity, options.id, JSON.stringify(options.query)])\n\n useEffect(() => {\n fetch()\n }, [fetch])\n\n if (!ctx) {\n return {\n data: null,\n loading: false,\n error: new Error('useZOIQEntity must be used within ZOIQProvider'),\n refetch: async () => {},\n }\n }\n\n return { data, loading, error, refetch: fetch }\n}\n"]}
|