@chaaskit/client 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/dist/lib/index.js +970 -80
  3. package/dist/lib/index.js.map +1 -1
  4. package/dist/lib/routes/AdminPromoCodesRoute.js +19 -0
  5. package/dist/lib/routes/AdminPromoCodesRoute.js.map +1 -0
  6. package/dist/lib/routes/AdminWaitlistRoute.js +19 -0
  7. package/dist/lib/routes/AdminWaitlistRoute.js.map +1 -0
  8. package/dist/lib/routes.js +47 -37
  9. package/dist/lib/routes.js.map +1 -1
  10. package/dist/lib/ssr-utils.js +36 -16
  11. package/dist/lib/ssr-utils.js.map +1 -1
  12. package/dist/lib/styles.css +37 -0
  13. package/dist/lib/useExtensions-B5nX_8XD.js.map +1 -1
  14. package/package.json +20 -12
  15. package/src/components/MessageItem.tsx +35 -4
  16. package/src/components/MessageList.tsx +51 -5
  17. package/src/components/OAuthAppsSection.tsx +1 -1
  18. package/src/components/Sidebar.tsx +1 -3
  19. package/src/components/ToolCallDisplay.tsx +102 -11
  20. package/src/components/tool-renderers/DocumentListRenderer.tsx +44 -0
  21. package/src/components/tool-renderers/DocumentReadRenderer.tsx +33 -0
  22. package/src/components/tool-renderers/DocumentSaveRenderer.tsx +32 -0
  23. package/src/components/tool-renderers/DocumentSearchRenderer.tsx +33 -0
  24. package/src/components/tool-renderers/index.ts +36 -0
  25. package/src/components/tool-renderers/utils.ts +7 -0
  26. package/src/contexts/AuthContext.tsx +16 -6
  27. package/src/contexts/ConfigContext.tsx +22 -28
  28. package/src/extensions/registry.ts +2 -1
  29. package/src/hooks/__tests__/basePath.test.ts +42 -0
  30. package/src/index.tsx +5 -2
  31. package/src/pages/AdminDashboardPage.tsx +15 -1
  32. package/src/pages/AdminPromoCodesPage.tsx +378 -0
  33. package/src/pages/AdminTeamPage.tsx +29 -1
  34. package/src/pages/AdminTeamsPage.tsx +15 -1
  35. package/src/pages/AdminUsersPage.tsx +15 -1
  36. package/src/pages/AdminWaitlistPage.tsx +156 -0
  37. package/src/pages/RegisterPage.tsx +91 -9
  38. package/src/routes/AdminPromoCodesRoute.tsx +24 -0
  39. package/src/routes/AdminWaitlistRoute.tsx +24 -0
  40. package/src/routes/index.ts +2 -0
  41. package/src/ssr-utils.tsx +32 -12
  42. package/src/stores/chatStore.ts +5 -0
  43. package/dist/favicon.svg +0 -11
  44. package/dist/index.html +0 -17
  45. package/dist/logo.svg +0 -12
package/dist/lib/index.js CHANGED
@@ -1,11 +1,129 @@
1
- import { jsx, jsxs, Fragment } from "react/jsx-runtime";
1
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
+ import { c as clientRegistry, a as useSidebarPages, b as useExtensionTools } from "./useExtensions-B5nX_8XD.js";
2
3
  import { C, a, S } from "./LoadingSkeletons-IcIC2JPq.js";
3
4
  import React, { createContext, useState, useEffect, useContext, useCallback, useRef, forwardRef, createElement as createElement$1, useMemo, useImperativeHandle } from "react";
4
5
  import { useSearchParams, useNavigate, Link as Link$1, useMatch, useParams, Navigate } from "react-router";
5
6
  import { S as S2, b, u, a as a2, c } from "./ServerThemeProvider-DNF0LAyk.js";
6
- import { a as useSidebarPages } from "./useExtensions-B5nX_8XD.js";
7
- import { c as c2 } from "./useExtensions-B5nX_8XD.js";
8
7
  import { createPortal } from "react-dom";
8
+ function getTextContent(content2) {
9
+ if (!content2) return null;
10
+ const firstText = content2.find((item) => item.type === "text" && typeof item.text === "string");
11
+ return (firstText == null ? void 0 : firstText.text) ?? null;
12
+ }
13
+ function DocumentListRenderer({ toolCall, toolResult }) {
14
+ const structured = toolResult.structuredContent;
15
+ const documents = (structured == null ? void 0 : structured.documents) ?? [];
16
+ const fallback = getTextContent(toolResult.content);
17
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
18
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-text-primary", children: "Documents" }),
19
+ documents.length > 0 ? /* @__PURE__ */ jsx("ul", { className: "space-y-2", children: documents.map((doc, index2) => /* @__PURE__ */ jsxs("li", { className: "rounded-md border border-border bg-background px-3 py-2", children: [
20
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-text-primary", children: doc.path ?? doc.name ?? "Untitled document" }),
21
+ /* @__PURE__ */ jsxs("div", { className: "text-xs text-text-muted", children: [
22
+ doc.mimeType ?? "text/plain",
23
+ typeof doc.charCount === "number" ? ` • ${doc.charCount} chars` : "",
24
+ doc.teamId ? " • team" : "",
25
+ doc.projectId ? " • project" : ""
26
+ ] })
27
+ ] }, doc.id ?? doc.path ?? index2)) }) : /* @__PURE__ */ jsx("div", { className: "text-sm text-text-secondary", children: fallback ?? "No documents found." }),
28
+ /* @__PURE__ */ jsxs("div", { className: "text-xs text-text-muted", children: [
29
+ "Tool: ",
30
+ toolCall.toolName
31
+ ] })
32
+ ] });
33
+ }
34
+ function DocumentReadRenderer({ toolCall, toolResult }) {
35
+ const structured = toolResult.structuredContent;
36
+ const fallback = getTextContent(toolResult.content);
37
+ const rangeText = structured ? `Lines ${Number(structured.offset ?? 0) + 1}-${Number(structured.offset ?? 0) + Number(structured.linesReturned ?? 0)} of ${structured.totalLines ?? "unknown"}` : null;
38
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
39
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-text-primary", children: "Document Preview" }),
40
+ structured ? /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-border bg-background px-3 py-2 text-sm text-text-secondary", children: [
41
+ /* @__PURE__ */ jsx("div", { className: "font-medium text-text-primary", children: structured.path ?? "Document" }),
42
+ rangeText && /* @__PURE__ */ jsxs("div", { className: "text-xs text-text-muted", children: [
43
+ rangeText,
44
+ structured.truncated ? " (truncated)" : ""
45
+ ] })
46
+ ] }) : /* @__PURE__ */ jsx("div", { className: "text-sm text-text-secondary", children: fallback ?? "No structured document details." }),
47
+ /* @__PURE__ */ jsxs("div", { className: "text-xs text-text-muted", children: [
48
+ "Tool: ",
49
+ toolCall.toolName
50
+ ] })
51
+ ] });
52
+ }
53
+ function DocumentSearchRenderer({ toolCall, toolResult }) {
54
+ const structured = toolResult.structuredContent;
55
+ const fallback = getTextContent(toolResult.content);
56
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
57
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-text-primary", children: "Document Search" }),
58
+ structured ? /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-border bg-background px-3 py-2 text-sm text-text-secondary", children: [
59
+ /* @__PURE__ */ jsx("div", { className: "font-medium text-text-primary", children: structured.path ?? "Document" }),
60
+ /* @__PURE__ */ jsxs("div", { className: "text-xs text-text-muted", children: [
61
+ "Query: ",
62
+ structured.query ?? "unknown"
63
+ ] }),
64
+ /* @__PURE__ */ jsxs("div", { className: "text-xs text-text-muted", children: [
65
+ "Matches: ",
66
+ structured.matchCount ?? 0
67
+ ] }),
68
+ structured.matchLines && structured.matchLines.length > 0 && /* @__PURE__ */ jsxs("div", { className: "text-xs text-text-muted", children: [
69
+ "Lines: ",
70
+ structured.matchLines.join(", ")
71
+ ] })
72
+ ] }) : /* @__PURE__ */ jsx("div", { className: "text-sm text-text-secondary", children: fallback ?? "No structured search data." }),
73
+ /* @__PURE__ */ jsxs("div", { className: "text-xs text-text-muted", children: [
74
+ "Tool: ",
75
+ toolCall.toolName
76
+ ] })
77
+ ] });
78
+ }
79
+ function DocumentSaveRenderer({ toolCall, toolResult }) {
80
+ const structured = toolResult.structuredContent;
81
+ const fallback = getTextContent(toolResult.content);
82
+ const success = (structured == null ? void 0 : structured.success) !== false && !toolResult.isError;
83
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
84
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-text-primary", children: "Save Document" }),
85
+ structured ? /* @__PURE__ */ jsxs("div", { className: `rounded-md border px-3 py-2 text-sm ${success ? "border-success/30 bg-success/10 text-success" : "border-error/30 bg-error/10 text-error"}`, children: [
86
+ /* @__PURE__ */ jsx("div", { className: "font-medium", children: success ? "Saved" : "Failed" }),
87
+ structured.path && /* @__PURE__ */ jsxs("div", { className: "text-xs", children: [
88
+ "Path: ",
89
+ structured.path
90
+ ] }),
91
+ structured.id && /* @__PURE__ */ jsxs("div", { className: "text-xs", children: [
92
+ "ID: ",
93
+ structured.id
94
+ ] }),
95
+ typeof structured.charCount === "number" && /* @__PURE__ */ jsxs("div", { className: "text-xs", children: [
96
+ "Size: ",
97
+ structured.charCount,
98
+ " chars"
99
+ ] })
100
+ ] }) : /* @__PURE__ */ jsx("div", { className: "text-sm text-text-secondary", children: fallback ?? "No structured save data." }),
101
+ /* @__PURE__ */ jsxs("div", { className: "text-xs text-text-muted", children: [
102
+ "Tool: ",
103
+ toolCall.toolName
104
+ ] })
105
+ ] });
106
+ }
107
+ clientRegistry.registerTool({
108
+ name: "list_documents",
109
+ description: "Render document list results",
110
+ resultRenderer: DocumentListRenderer
111
+ });
112
+ clientRegistry.registerTool({
113
+ name: "read_document",
114
+ description: "Render document read results",
115
+ resultRenderer: DocumentReadRenderer
116
+ });
117
+ clientRegistry.registerTool({
118
+ name: "search_in_document",
119
+ description: "Render document search results",
120
+ resultRenderer: DocumentSearchRenderer
121
+ });
122
+ clientRegistry.registerTool({
123
+ name: "save_document",
124
+ description: "Render document save results",
125
+ resultRenderer: DocumentSaveRenderer
126
+ });
9
127
  class ApiError extends Error {
10
128
  constructor(status, message, code2) {
11
129
  super(message);
@@ -85,8 +203,8 @@ function AuthProvider({ children }) {
85
203
  setUser(response.user);
86
204
  return { requiresVerification: response.requiresVerification ?? false };
87
205
  }
88
- async function register2(email, password, name2) {
89
- const response = await api.post("/api/auth/register", { email, password, name: name2 });
206
+ async function register2(email, password, name2, options) {
207
+ const response = await api.post("/api/auth/register", { email, password, name: name2, ...options });
90
208
  setUser(response.user);
91
209
  return { requiresVerification: response.requiresVerification ?? false };
92
210
  }
@@ -94,8 +212,8 @@ function AuthProvider({ children }) {
94
212
  await api.post("/api/auth/logout", {});
95
213
  setUser(null);
96
214
  }
97
- async function sendMagicLink(email) {
98
- await api.post("/api/auth/magic-link", { email });
215
+ async function sendMagicLink(email, inviteToken) {
216
+ await api.post("/api/auth/magic-link", { email, inviteToken });
99
217
  }
100
218
  async function verifyEmail(code2) {
101
219
  await api.post(
@@ -178,7 +296,8 @@ const defaultConfig = {
178
296
  app: {
179
297
  name: "AI Chat",
180
298
  description: "Your AI assistant",
181
- url: "http://localhost:5173"
299
+ url: "http://localhost:5173",
300
+ basePath: "/chat"
182
301
  },
183
302
  ui: {
184
303
  welcomeTitle: "Welcome to AI Chat",
@@ -261,28 +380,16 @@ const defaultConfig = {
261
380
  magicLink: {
262
381
  enabled: true,
263
382
  expiresInMinutes: 15
264
- }
265
- },
266
- agent: {
267
- type: "built-in",
268
- provider: "anthropic",
269
- model: "claude-sonnet-4-20250514",
270
- systemPrompt: "You are a helpful AI assistant.",
271
- maxTokens: 4096
383
+ },
384
+ gating: {
385
+ mode: "open",
386
+ waitlistEnabled: false
387
+ },
388
+ isAdmin: false
272
389
  },
273
390
  payments: {
274
391
  enabled: false,
275
- provider: "stripe",
276
- plans: [
277
- {
278
- id: "free",
279
- name: "Free",
280
- type: "free",
281
- params: {
282
- monthlyMessageLimit: 20
283
- }
284
- }
285
- ]
392
+ provider: "stripe"
286
393
  },
287
394
  legal: {
288
395
  privacyPolicyUrl: "/privacy",
@@ -311,7 +418,6 @@ const defaultConfig = {
311
418
  },
312
419
  promptTemplates: {
313
420
  enabled: true,
314
- builtIn: [],
315
421
  allowUserTemplates: true
316
422
  },
317
423
  teams: {
@@ -319,9 +425,6 @@ const defaultConfig = {
319
425
  },
320
426
  documents: {
321
427
  enabled: false,
322
- storage: {
323
- provider: "database"
324
- },
325
428
  maxFileSizeMB: 10,
326
429
  hybridThreshold: 1e3,
327
430
  acceptedTypes: ["text/plain", "text/markdown", "application/json"]
@@ -332,6 +435,15 @@ const defaultConfig = {
332
435
  },
333
436
  api: {
334
437
  enabled: false
438
+ },
439
+ credits: {
440
+ enabled: false,
441
+ expiryEnabled: false,
442
+ promoEnabled: false
443
+ },
444
+ metering: {
445
+ enabled: false,
446
+ recordPromptCompletion: true
335
447
  }
336
448
  };
337
449
  const ConfigContext = createContext({
@@ -1301,7 +1413,8 @@ ${mentions}` : mentions;
1301
1413
  ...pendingCall,
1302
1414
  result: data2.content || [],
1303
1415
  isError: data2.isError,
1304
- uiResource: data2.uiResource
1416
+ uiResource: data2.uiResource,
1417
+ structuredContent: data2.structuredContent
1305
1418
  } : {
1306
1419
  // Fallback: create from tool_result event data (server now includes name/serverId/input)
1307
1420
  id: data2.id,
@@ -1310,7 +1423,8 @@ ${mentions}` : mentions;
1310
1423
  input: data2.input || {},
1311
1424
  result: data2.content || [],
1312
1425
  isError: data2.isError,
1313
- uiResource: data2.uiResource
1426
+ uiResource: data2.uiResource,
1427
+ structuredContent: data2.structuredContent
1314
1428
  };
1315
1429
  return {
1316
1430
  pendingToolCalls: state2.pendingToolCalls.filter((tc) => tc.id !== data2.id),
@@ -1382,7 +1496,8 @@ ${mentions}` : mentions;
1382
1496
  toolCallId: tc.id,
1383
1497
  content: tc.result,
1384
1498
  isError: tc.isError,
1385
- uiResource: tc.uiResource
1499
+ uiResource: tc.uiResource,
1500
+ structuredContent: tc.structuredContent
1386
1501
  })) : void 0,
1387
1502
  createdAt: /* @__PURE__ */ new Date()
1388
1503
  };
@@ -27153,12 +27268,12 @@ function MCPCredentialsSection({ onCredentialChange }) {
27153
27268
  ] });
27154
27269
  }
27155
27270
  function OAuthAppsSection() {
27156
- var _a, _b, _c;
27271
+ var _a, _b;
27157
27272
  const config = useConfig();
27158
27273
  const [apps, setApps] = useState([]);
27159
27274
  const [isLoading, setIsLoading] = useState(false);
27160
27275
  const [revokingId, setRevokingId] = useState(null);
27161
- const oauthEnabled = (_c = (_b = (_a = config.mcp) == null ? void 0 : _a.server) == null ? void 0 : _b.oauth) == null ? void 0 : _c.enabled;
27276
+ const oauthEnabled = (_b = (_a = config.mcp) == null ? void 0 : _a.servers) == null ? void 0 : _b.some((server) => server.authMode === "user-oauth");
27162
27277
  useEffect(() => {
27163
27278
  if (oauthEnabled) {
27164
27279
  loadApps();
@@ -27979,16 +28094,16 @@ function ProjectModal({ isOpen, onClose, project }) {
27979
28094
  ] }),
27980
28095
  /* @__PURE__ */ jsxs("div", { children: [
27981
28096
  /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-text-secondary mb-2", children: "Color" }),
27982
- /* @__PURE__ */ jsx("div", { className: "flex gap-2 flex-wrap", children: projectColors.map((c3) => /* @__PURE__ */ jsx(
28097
+ /* @__PURE__ */ jsx("div", { className: "flex gap-2 flex-wrap", children: projectColors.map((c2) => /* @__PURE__ */ jsx(
27983
28098
  "button",
27984
28099
  {
27985
28100
  type: "button",
27986
- onClick: () => canEdit && setColor(c3),
27987
- className: `w-8 h-8 rounded-full border-2 transition-all ${color2 === c3 ? "border-text-primary scale-110" : "border-transparent hover:scale-105"} ${!canEdit ? "cursor-not-allowed opacity-50" : ""}`,
27988
- style: { backgroundColor: c3 },
28101
+ onClick: () => canEdit && setColor(c2),
28102
+ className: `w-8 h-8 rounded-full border-2 transition-all ${color2 === c2 ? "border-text-primary scale-110" : "border-transparent hover:scale-105"} ${!canEdit ? "cursor-not-allowed opacity-50" : ""}`,
28103
+ style: { backgroundColor: c2 },
27989
28104
  disabled: !canEdit
27990
28105
  },
27991
- c3
28106
+ c2
27992
28107
  )) })
27993
28108
  ] }),
27994
28109
  /* @__PURE__ */ jsxs("div", { children: [
@@ -28510,7 +28625,7 @@ function getIconComponent(iconName) {
28510
28625
  return Icon || Puzzle;
28511
28626
  }
28512
28627
  function Sidebar({ onClose, onOpenSearch }) {
28513
- var _a, _b, _c, _d, _e, _f;
28628
+ var _a, _b, _c, _d, _e;
28514
28629
  const navigate = useNavigate();
28515
28630
  const appPath = useAppPath();
28516
28631
  const basePath = useBasePath();
@@ -28760,12 +28875,7 @@ function Sidebar({ onClose, onOpenSearch }) {
28760
28875
  ]
28761
28876
  }
28762
28877
  ),
28763
- ((user == null ? void 0 : user.isAdmin) || ((_d = (_c = config.admin) == null ? void 0 : _c.emails) == null ? void 0 : _d.some(
28764
- (email) => {
28765
- var _a2;
28766
- return email.toLowerCase() === ((_a2 = user == null ? void 0 : user.email) == null ? void 0 : _a2.toLowerCase());
28767
- }
28768
- ))) && /* @__PURE__ */ jsxs(
28878
+ ((user == null ? void 0 : user.isAdmin) || ((_c = config.auth) == null ? void 0 : _c.isAdmin)) && /* @__PURE__ */ jsxs(
28769
28879
  Link$1,
28770
28880
  {
28771
28881
  to: appPath("/admin"),
@@ -28791,7 +28901,7 @@ function Sidebar({ onClose, onOpenSearch }) {
28791
28901
  page.id
28792
28902
  );
28793
28903
  }),
28794
- ((_e = config.documents) == null ? void 0 : _e.enabled) && /* @__PURE__ */ jsxs(
28904
+ ((_d = config.documents) == null ? void 0 : _d.enabled) && /* @__PURE__ */ jsxs(
28795
28905
  Link$1,
28796
28906
  {
28797
28907
  to: appPath("/documents"),
@@ -28802,7 +28912,7 @@ function Sidebar({ onClose, onOpenSearch }) {
28802
28912
  ]
28803
28913
  }
28804
28914
  ),
28805
- ((_f = config.scheduledPrompts) == null ? void 0 : _f.enabled) && /* @__PURE__ */ jsxs(
28915
+ ((_e = config.scheduledPrompts) == null ? void 0 : _e.enabled) && /* @__PURE__ */ jsxs(
28806
28916
  Link$1,
28807
28917
  {
28808
28918
  to: appPath("/automations"),
@@ -30391,11 +30501,11 @@ function index$1(style2, options) {
30391
30501
  match(WHITESPACE_REGEX);
30392
30502
  }
30393
30503
  function comments(rules) {
30394
- var c3;
30504
+ var c2;
30395
30505
  rules = rules || [];
30396
- while (c3 = comment()) {
30397
- if (c3 !== false) {
30398
- rules.push(c3);
30506
+ while (c2 = comment()) {
30507
+ if (c2 !== false) {
30508
+ rules.push(c2);
30399
30509
  }
30400
30510
  }
30401
30511
  return rules;
@@ -46216,10 +46326,10 @@ var hasRequiredC;
46216
46326
  function requireC() {
46217
46327
  if (hasRequiredC) return c_1;
46218
46328
  hasRequiredC = 1;
46219
- c_1 = c3;
46220
- c3.displayName = "c";
46221
- c3.aliases = [];
46222
- function c3(Prism2) {
46329
+ c_1 = c2;
46330
+ c2.displayName = "c";
46331
+ c2.aliases = [];
46332
+ function c2(Prism2) {
46223
46333
  Prism2.languages.c = Prism2.languages.extend("clike", {
46224
46334
  comment: {
46225
46335
  pattern: /\/\/(?:[^\r\n\\]|\\(?:\r\n?|\n|(?![\r\n])))*|\/\*[\s\S]*?(?:\*\/|$)/,
@@ -58748,7 +58858,7 @@ function requireNaniscript() {
58748
58858
  var tokens = env2.tokens;
58749
58859
  tokens.forEach(function(token) {
58750
58860
  if (typeof token !== "string" && token.type === "generic-text") {
58751
- var content2 = getTextContent(token);
58861
+ var content2 = getTextContent2(token);
58752
58862
  if (!isBracketsBalanced(content2)) {
58753
58863
  token.type = "bad-line";
58754
58864
  token.content = content2;
@@ -58772,13 +58882,13 @@ function requireNaniscript() {
58772
58882
  }
58773
58883
  return stack.length === 0;
58774
58884
  }
58775
- function getTextContent(token) {
58885
+ function getTextContent2(token) {
58776
58886
  if (typeof token === "string") {
58777
58887
  return token;
58778
58888
  } else if (Array.isArray(token)) {
58779
- return token.map(getTextContent).join("");
58889
+ return token.map(getTextContent2).join("");
58780
58890
  } else {
58781
- return getTextContent(token.content);
58891
+ return getTextContent2(token.content);
58782
58892
  }
58783
58893
  }
58784
58894
  })(Prism2);
@@ -67949,6 +68059,7 @@ const AUTO_APPROVE_LABELS = {
67949
68059
  user_always: "You always allowed this tool",
67950
68060
  thread_allowed: "Allowed for this chat"
67951
68061
  };
68062
+ const TOOL_UI_MESSAGE_SOURCE = "chaaskit-tool-ui";
67952
68063
  function generateOpenAiScript(toolInput, toolOutput, theme) {
67953
68064
  const openAiTheme = theme === "dark" ? "dark" : "light";
67954
68065
  const backgroundColor = theme === "dark" ? "#111827" : "#ffffff";
@@ -67991,6 +68102,46 @@ function generateOpenAiScript(toolInput, toolOutput, theme) {
67991
68102
  </style>
67992
68103
  <script>
67993
68104
  (function() {
68105
+ const MESSAGE_SOURCE = '${TOOL_UI_MESSAGE_SOURCE}';
68106
+ let requestId = 0;
68107
+ const pending = new Map();
68108
+
68109
+ function resolvePending(id, data) {
68110
+ const entry = pending.get(id);
68111
+ if (!entry) return;
68112
+ pending.delete(id);
68113
+ if (data && data.ok) {
68114
+ entry.resolve(data.result);
68115
+ return;
68116
+ }
68117
+ entry.resolve({ error: (data && data.error) || 'Tool UI bridge error' });
68118
+ }
68119
+
68120
+ window.addEventListener('message', (event) => {
68121
+ const data = event.data || {};
68122
+ if (data.source !== MESSAGE_SOURCE || !data.id) return;
68123
+ resolvePending(data.id, data);
68124
+ });
68125
+
68126
+ function sendToParent(type, payload, timeoutMs = 15000) {
68127
+ return new Promise((resolve) => {
68128
+ const id = String(Date.now()) + '-' + String(requestId++);
68129
+ pending.set(id, { resolve });
68130
+ try {
68131
+ window.parent.postMessage({ source: MESSAGE_SOURCE, type, id, payload }, '*');
68132
+ } catch (err) {
68133
+ pending.delete(id);
68134
+ resolve({ error: 'Unable to reach host window' });
68135
+ return;
68136
+ }
68137
+ setTimeout(() => {
68138
+ if (!pending.has(id)) return;
68139
+ pending.delete(id);
68140
+ resolve({ error: 'Tool UI request timed out' });
68141
+ }, timeoutMs);
68142
+ });
68143
+ }
68144
+
67994
68145
  // Initialize window.openai with the OpenAI Apps SDK spec
67995
68146
  window.openai = {
67996
68147
  // Core globals
@@ -68015,17 +68166,20 @@ function generateOpenAiScript(toolInput, toolOutput, theme) {
68015
68166
  // API methods
68016
68167
  callTool: async (name, args) => {
68017
68168
  console.log('window.openai.callTool called:', { name, args });
68018
- // TODO: Implement actual tool calling via parent window messaging
68019
- return {
68020
- content: [{ type: 'text', text: 'Tool calling not yet implemented' }],
68021
- isError: false
68022
- };
68169
+ const response = await sendToParent('callTool', { name, args });
68170
+ if (response && response.error) {
68171
+ return {
68172
+ content: [{ type: 'text', text: response.error }],
68173
+ isError: true
68174
+ };
68175
+ }
68176
+ return response || { content: [{ type: 'text', text: 'No response from host' }], isError: true };
68023
68177
  },
68024
68178
 
68025
68179
  sendFollowUpMessage: async (args) => {
68026
68180
  console.log('window.openai.sendFollowUpMessage called:', args);
68027
- // TODO: Implement via parent window messaging
68028
- return {};
68181
+ const response = await sendToParent('sendFollowUpMessage', args);
68182
+ return response && response.error ? { error: response.error } : (response || {});
68029
68183
  },
68030
68184
 
68031
68185
  openExternal: (payload) => {
@@ -68037,27 +68191,41 @@ function generateOpenAiScript(toolInput, toolOutput, theme) {
68037
68191
 
68038
68192
  requestDisplayMode: async (args) => {
68039
68193
  console.log('window.openai.requestDisplayMode called:', args);
68040
- return { mode: args.mode };
68194
+ const response = await sendToParent('requestDisplayMode', args);
68195
+ if (response && response.error) {
68196
+ return { mode: args.mode };
68197
+ }
68198
+ return response || { mode: args.mode };
68041
68199
  },
68042
68200
 
68043
68201
  setWidgetState: async (state) => {
68044
68202
  console.log('window.openai.setWidgetState called:', state);
68045
68203
  window.openai.widgetState = state;
68046
- return {};
68204
+ const response = await sendToParent('setWidgetState', state);
68205
+ return response && response.error ? {} : (response || {});
68047
68206
  },
68048
68207
 
68049
68208
  requestClose: () => {
68050
68209
  console.log('window.openai.requestClose called');
68210
+ sendToParent('requestClose', {});
68051
68211
  },
68052
68212
 
68053
68213
  getFileDownloadUrl: async ({ fileId }) => {
68054
68214
  console.log('window.openai.getFileDownloadUrl called:', fileId);
68055
- return { url: '' };
68215
+ const response = await sendToParent('getFileDownloadUrl', { fileId });
68216
+ if (response && response.error) {
68217
+ return { url: '' };
68218
+ }
68219
+ return response || { url: '' };
68056
68220
  },
68057
68221
 
68058
68222
  uploadFile: async (file) => {
68059
68223
  console.log('window.openai.uploadFile called:', file);
68060
- return { fileId: '' };
68224
+ const response = await sendToParent('uploadFile', { file });
68225
+ if (response && response.error) {
68226
+ return { fileId: '' };
68227
+ }
68228
+ return response || { fileId: '' };
68061
68229
  }
68062
68230
  };
68063
68231
 
@@ -68109,6 +68277,31 @@ function UIResourceWidget({ uiResource, theme }) {
68109
68277
  }
68110
68278
  function ToolCallDisplay({ toolCall, toolResult, isPending, uiResource, hideUiResource, autoApproveReason }) {
68111
68279
  var _a;
68280
+ useEffect(() => {
68281
+ async function handleMessage(event) {
68282
+ const data2 = event.data || {};
68283
+ if (data2.source !== TOOL_UI_MESSAGE_SOURCE || !data2.id) return;
68284
+ const targetOrigin = event.origin && event.origin !== "null" ? event.origin : "*";
68285
+ try {
68286
+ const handler = window.chaaskitToolUiHandler;
68287
+ const result = typeof handler === "function" ? await handler({ type: data2.type, payload: data2.payload }) : { error: "Tool UI bridge not configured" };
68288
+ const sourceWindow = event.source;
68289
+ sourceWindow == null ? void 0 : sourceWindow.postMessage(
68290
+ { source: TOOL_UI_MESSAGE_SOURCE, id: data2.id, ok: true, result },
68291
+ targetOrigin
68292
+ );
68293
+ } catch (err) {
68294
+ const message = err instanceof Error ? err.message : "Tool UI bridge error";
68295
+ const sourceWindow = event.source;
68296
+ sourceWindow == null ? void 0 : sourceWindow.postMessage(
68297
+ { source: TOOL_UI_MESSAGE_SOURCE, id: data2.id, ok: false, error: message },
68298
+ targetOrigin
68299
+ );
68300
+ }
68301
+ }
68302
+ window.addEventListener("message", handleMessage);
68303
+ return () => window.removeEventListener("message", handleMessage);
68304
+ }, []);
68112
68305
  const hasHtmlResource = !hideUiResource && (uiResource == null ? void 0 : uiResource.text) && (((_a = uiResource.mimeType) == null ? void 0 : _a.includes("html")) || uiResource.text.trim().startsWith("<"));
68113
68306
  const [isExpanded, setIsExpanded] = useState(hasHtmlResource);
68114
68307
  const [showRawResult, setShowRawResult] = useState(false);
@@ -68442,6 +68635,12 @@ function MessageItem({ message, isStreaming, messageIndex = 0, previousMessage }
68442
68635
  const config = useConfig();
68443
68636
  const navigate = useNavigate();
68444
68637
  const appPath = useAppPath();
68638
+ const extensionTools = useExtensionTools();
68639
+ const toolRendererMap = useMemo(() => {
68640
+ const map2 = /* @__PURE__ */ new Map();
68641
+ extensionTools.forEach((tool) => map2.set(tool.name, tool));
68642
+ return map2;
68643
+ }, [extensionTools]);
68445
68644
  const isUser = message.role === "user";
68446
68645
  const showToolCalls = ((_a = config.mcp) == null ? void 0 : _a.showToolCalls) !== false;
68447
68646
  const canBranch = messageIndex > 0;
@@ -68488,9 +68687,19 @@ function MessageItem({ message, isStreaming, messageIndex = 0, previousMessage }
68488
68687
  toolResult: (_a2 = message.toolResults) == null ? void 0 : _a2.find((r) => r.toolCallId === toolCall.id)
68489
68688
  };
68490
68689
  })) || [];
68690
+ const renderedToolCalls = toolCallsWithResults.filter(
68691
+ ({ toolCall, toolResult }) => {
68692
+ var _a2;
68693
+ return toolCall.serverId === "native" && !!toolResult && !!((_a2 = toolRendererMap.get(toolCall.toolName)) == null ? void 0 : _a2.resultRenderer);
68694
+ }
68695
+ ).map(({ toolCall, toolResult }) => {
68696
+ const renderer = toolRendererMap.get(toolCall.toolName).resultRenderer;
68697
+ return { toolCall, toolResult, Renderer: renderer };
68698
+ });
68699
+ const renderedToolCallIds = new Set(renderedToolCalls.map((entry) => entry.toolCall.id));
68491
68700
  const uiResources = toolCallsWithResults.filter((tc) => {
68492
68701
  var _a2, _b2;
68493
- return (_b2 = (_a2 = tc.toolResult) == null ? void 0 : _a2.uiResource) == null ? void 0 : _b2.text;
68702
+ return ((_b2 = (_a2 = tc.toolResult) == null ? void 0 : _a2.uiResource) == null ? void 0 : _b2.text) && !renderedToolCallIds.has(tc.toolCall.id);
68494
68703
  }).map((tc) => tc.toolResult.uiResource);
68495
68704
  if (!isUser && ((_c = message.toolCalls) == null ? void 0 : _c.length)) {
68496
68705
  console.log("[MessageItem] Rendering message with toolCalls:", message.toolCalls.length);
@@ -68561,6 +68770,7 @@ function MessageItem({ message, isStreaming, messageIndex = 0, previousMessage }
68561
68770
  },
68562
68771
  toolCall.id
68563
68772
  )) }),
68773
+ renderedToolCalls.length > 0 && /* @__PURE__ */ jsx("div", { className: "space-y-3", children: renderedToolCalls.map(({ toolCall, toolResult, Renderer }) => /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-border bg-background-secondary/40 p-3", children: /* @__PURE__ */ jsx(Renderer, { toolCall, toolResult }) }, toolCall.id)) }),
68564
68774
  uiResources.length > 0 && /* @__PURE__ */ jsx("div", { className: "space-y-3", children: uiResources.map((uiResource, index2) => /* @__PURE__ */ jsx(UIResourceWidget, { uiResource, theme }, index2)) }),
68565
68775
  (message.content || isStreaming) && /* @__PURE__ */ jsxs("div", { className: "group flex gap-3", children: [
68566
68776
  /* @__PURE__ */ jsx("div", { className: "flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-secondary overflow-hidden", children: ((_f = config.ui) == null ? void 0 : _f.logo) ? /* @__PURE__ */ jsx("img", { src: typeof config.ui.logo === "string" ? config.ui.logo : theme === "dark" ? config.ui.logo.dark : config.ui.logo.light, alt: "", className: "h-full w-full object-cover" }) : /* @__PURE__ */ jsx(Bot, { size: 12, className: "text-white" }) }),
@@ -68651,6 +68861,12 @@ function MessageList({
68651
68861
  const bottomRef = useRef(null);
68652
68862
  const { theme } = useTheme();
68653
68863
  const config = useConfig();
68864
+ const extensionTools = useExtensionTools();
68865
+ const toolRendererMap = useMemo(() => {
68866
+ const map2 = /* @__PURE__ */ new Map();
68867
+ extensionTools.forEach((tool) => map2.set(tool.name, tool));
68868
+ return map2;
68869
+ }, [extensionTools]);
68654
68870
  const showToolCalls = ((_a = config.mcp) == null ? void 0 : _a.showToolCalls) !== false;
68655
68871
  useEffect(() => {
68656
68872
  var _a2;
@@ -68658,9 +68874,33 @@ function MessageList({
68658
68874
  }, [messages2, streamingContent, pendingToolCalls, completedToolCalls]);
68659
68875
  const hasToolActivity = pendingToolCalls.length > 0 || completedToolCalls.length > 0;
68660
68876
  const isStreaming = Boolean(streamingContent) || hasToolActivity;
68877
+ const renderedToolCalls = completedToolCalls.filter(
68878
+ (call) => {
68879
+ var _a2;
68880
+ return call.serverId === "native" && !!((_a2 = toolRendererMap.get(call.name)) == null ? void 0 : _a2.resultRenderer);
68881
+ }
68882
+ ).map((call) => {
68883
+ const renderer = toolRendererMap.get(call.name).resultRenderer;
68884
+ const toolCall = {
68885
+ id: call.id,
68886
+ serverId: call.serverId,
68887
+ toolName: call.name,
68888
+ arguments: call.input,
68889
+ status: call.isError ? "error" : "completed"
68890
+ };
68891
+ const toolResult = {
68892
+ toolCallId: call.id,
68893
+ content: call.result,
68894
+ isError: call.isError,
68895
+ uiResource: call.uiResource,
68896
+ structuredContent: call.structuredContent
68897
+ };
68898
+ return { toolCall, toolResult, Renderer: renderer };
68899
+ });
68900
+ const renderedToolCallIds = new Set(renderedToolCalls.map((entry) => entry.toolCall.id));
68661
68901
  const uiResources = completedToolCalls.filter((tc) => {
68662
68902
  var _a2;
68663
- return (_a2 = tc.uiResource) == null ? void 0 : _a2.text;
68903
+ return ((_a2 = tc.uiResource) == null ? void 0 : _a2.text) && !renderedToolCallIds.has(tc.id);
68664
68904
  }).map((tc) => tc.uiResource);
68665
68905
  console.log("[MessageList] Rendering messages:", messages2.length, "isStreaming:", isStreaming);
68666
68906
  messages2.forEach((msg, i) => {
@@ -68707,7 +68947,8 @@ function MessageList({
68707
68947
  toolResult: {
68708
68948
  toolCallId: call.id,
68709
68949
  content: call.result,
68710
- isError: call.isError
68950
+ isError: call.isError,
68951
+ structuredContent: call.structuredContent
68711
68952
  },
68712
68953
  hideUiResource: true
68713
68954
  },
@@ -68728,6 +68969,7 @@ function MessageList({
68728
68969
  call.id
68729
68970
  ))
68730
68971
  ] }),
68972
+ renderedToolCalls.length > 0 && /* @__PURE__ */ jsx("div", { className: "space-y-3", children: renderedToolCalls.map(({ toolCall, toolResult, Renderer }) => /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-border bg-background-secondary/40 p-3", children: /* @__PURE__ */ jsx(Renderer, { toolCall, toolResult }) }, toolCall.id)) }),
68731
68973
  uiResources.length > 0 && /* @__PURE__ */ jsx("div", { className: "space-y-3", children: uiResources.map((uiResource, index2) => /* @__PURE__ */ jsx(UIResourceWidget, { uiResource, theme }, index2)) }),
68732
68974
  streamingContent && /* @__PURE__ */ jsxs("div", { className: "group flex gap-3", children: [
68733
68975
  /* @__PURE__ */ jsx("div", { className: "flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-secondary", children: /* @__PURE__ */ jsx(Bot, { size: 12, className: "text-white" }) }),
@@ -69763,11 +70005,20 @@ function RegisterPage() {
69763
70005
  const { register: register2 } = useAuth();
69764
70006
  const config = useConfig();
69765
70007
  const { theme } = useTheme();
70008
+ const [searchParams] = useSearchParams();
70009
+ const inviteToken = searchParams.get("invite") || void 0;
70010
+ const referralCode = searchParams.get("ref") || void 0;
70011
+ const gating = config.auth.gating;
70012
+ const signupsRestricted = (gating == null ? void 0 : gating.mode) && gating.mode !== "open" && !inviteToken;
70013
+ const waitlistEnabled = (gating == null ? void 0 : gating.waitlistEnabled) ?? false;
70014
+ const showWaitlist = !!signupsRestricted && waitlistEnabled;
70015
+ const showRestrictedMessage = !!signupsRestricted && !waitlistEnabled;
69766
70016
  const [name2, setName] = useState("");
69767
70017
  const [email, setEmail] = useState("");
69768
70018
  const [password, setPassword] = useState("");
69769
70019
  const [error, setError] = useState("");
69770
70020
  const [isLoading, setIsLoading] = useState(false);
70021
+ const [waitlistStatus, setWaitlistStatus] = useState("idle");
69771
70022
  async function handleSubmit(e) {
69772
70023
  e.preventDefault();
69773
70024
  setError("");
@@ -69777,7 +70028,10 @@ function RegisterPage() {
69777
70028
  }
69778
70029
  setIsLoading(true);
69779
70030
  try {
69780
- const { requiresVerification } = await register2(email, password, name2 || void 0);
70031
+ const { requiresVerification } = await register2(email, password, name2 || void 0, {
70032
+ inviteToken,
70033
+ referralCode
70034
+ });
69781
70035
  if (requiresVerification) {
69782
70036
  navigate("/verify-email");
69783
70037
  } else {
@@ -69789,6 +70043,16 @@ function RegisterPage() {
69789
70043
  setIsLoading(false);
69790
70044
  }
69791
70045
  }
70046
+ async function handleWaitlistSubmit(e) {
70047
+ e.preventDefault();
70048
+ setError("");
70049
+ try {
70050
+ await api.post("/api/auth/waitlist", { email, name: name2 || void 0 });
70051
+ setWaitlistStatus("submitted");
70052
+ } catch (err) {
70053
+ setError(err instanceof Error ? err.message : "Failed to join waitlist");
70054
+ }
70055
+ }
69792
70056
  return /* @__PURE__ */ jsx("div", { className: "flex min-h-screen items-center justify-center bg-background p-4", children: /* @__PURE__ */ jsxs("div", { className: "w-full max-w-md", children: [
69793
70057
  /* @__PURE__ */ jsxs("div", { className: "mb-8 text-center", children: [
69794
70058
  config.ui.logo && /* @__PURE__ */ jsx(
@@ -69806,7 +70070,46 @@ function RegisterPage() {
69806
70070
  ] })
69807
70071
  ] }),
69808
70072
  error && /* @__PURE__ */ jsx("div", { className: "mb-4 rounded-lg bg-error/10 p-3 text-sm text-error", children: error }),
69809
- /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
70073
+ showRestrictedMessage ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-border bg-background-secondary p-4 text-sm text-text-secondary", children: "Signups are currently closed. Please check back later." }) : showWaitlist ? /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-border bg-background-secondary p-4", children: [
70074
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-text-secondary", children: "Signups are currently restricted. Join the waitlist to get an invite." }),
70075
+ waitlistStatus === "submitted" ? /* @__PURE__ */ jsx("div", { className: "mt-4 rounded-lg bg-success/10 p-3 text-sm text-success", children: "Thanks! You’re on the waitlist." }) : /* @__PURE__ */ jsxs("form", { onSubmit: handleWaitlistSubmit, className: "mt-4 space-y-3", children: [
70076
+ /* @__PURE__ */ jsxs("div", { children: [
70077
+ /* @__PURE__ */ jsx("label", { htmlFor: "waitlist-name", className: "block text-sm font-medium text-text-primary", children: "Name (optional)" }),
70078
+ /* @__PURE__ */ jsx(
70079
+ "input",
70080
+ {
70081
+ type: "text",
70082
+ id: "waitlist-name",
70083
+ value: name2,
70084
+ onChange: (e) => setName(e.target.value),
70085
+ className: "mt-1 w-full rounded-lg border border-input-border bg-input-background px-4 py-2 text-text-primary focus:border-primary focus:outline-none"
70086
+ }
70087
+ )
70088
+ ] }),
70089
+ /* @__PURE__ */ jsxs("div", { children: [
70090
+ /* @__PURE__ */ jsx("label", { htmlFor: "waitlist-email", className: "block text-sm font-medium text-text-primary", children: "Email" }),
70091
+ /* @__PURE__ */ jsx(
70092
+ "input",
70093
+ {
70094
+ type: "email",
70095
+ id: "waitlist-email",
70096
+ value: email,
70097
+ onChange: (e) => setEmail(e.target.value),
70098
+ required: true,
70099
+ className: "mt-1 w-full rounded-lg border border-input-border bg-input-background px-4 py-2 text-text-primary focus:border-primary focus:outline-none"
70100
+ }
70101
+ )
70102
+ ] }),
70103
+ /* @__PURE__ */ jsx(
70104
+ "button",
70105
+ {
70106
+ type: "submit",
70107
+ className: "w-full rounded-lg bg-primary px-4 py-2 font-medium text-white hover:bg-primary-hover",
70108
+ children: "Join waitlist"
70109
+ }
70110
+ )
70111
+ ] })
70112
+ ] }) : /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
69810
70113
  /* @__PURE__ */ jsxs("div", { children: [
69811
70114
  /* @__PURE__ */ jsx(
69812
70115
  "label",
@@ -69881,7 +70184,7 @@ function RegisterPage() {
69881
70184
  }
69882
70185
  )
69883
70186
  ] }),
69884
- /* @__PURE__ */ jsxs("p", { className: "mt-6 text-center text-sm text-text-secondary", children: [
70187
+ !signupsRestricted && /* @__PURE__ */ jsxs("p", { className: "mt-6 text-center text-sm text-text-secondary", children: [
69885
70188
  "Already have an account?",
69886
70189
  " ",
69887
70190
  /* @__PURE__ */ jsx(Link$1, { to: "/login", className: "text-primary hover:underline", children: "Sign in" })
@@ -73153,6 +73456,28 @@ function AdminDashboardPage() {
73153
73456
  "Teams"
73154
73457
  ]
73155
73458
  }
73459
+ ),
73460
+ /* @__PURE__ */ jsxs(
73461
+ Link$1,
73462
+ {
73463
+ to: appPath("/admin/waitlist"),
73464
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
73465
+ children: [
73466
+ /* @__PURE__ */ jsx(Mail, { size: 16 }),
73467
+ "Waitlist"
73468
+ ]
73469
+ }
73470
+ ),
73471
+ /* @__PURE__ */ jsxs(
73472
+ Link$1,
73473
+ {
73474
+ to: appPath("/admin/promo-codes"),
73475
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
73476
+ children: [
73477
+ /* @__PURE__ */ jsx(Tag, { size: 16 }),
73478
+ "Promo Codes"
73479
+ ]
73480
+ }
73156
73481
  )
73157
73482
  ] }),
73158
73483
  /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-8", children: [
@@ -73427,6 +73752,28 @@ function AdminUsersPage() {
73427
73752
  "Teams"
73428
73753
  ]
73429
73754
  }
73755
+ ),
73756
+ /* @__PURE__ */ jsxs(
73757
+ Link$1,
73758
+ {
73759
+ to: appPath("/admin/waitlist"),
73760
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
73761
+ children: [
73762
+ /* @__PURE__ */ jsx(Mail, { size: 16 }),
73763
+ "Waitlist"
73764
+ ]
73765
+ }
73766
+ ),
73767
+ /* @__PURE__ */ jsxs(
73768
+ Link$1,
73769
+ {
73770
+ to: appPath("/admin/promo-codes"),
73771
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
73772
+ children: [
73773
+ /* @__PURE__ */ jsx(Tag, { size: 16 }),
73774
+ "Promo Codes"
73775
+ ]
73776
+ }
73430
73777
  )
73431
73778
  ] }),
73432
73779
  error && /* @__PURE__ */ jsx("div", { className: "mb-6 rounded-lg bg-error/10 p-4 text-sm text-error", children: error }),
@@ -73709,6 +74056,28 @@ function AdminTeamsPage() {
73709
74056
  "Teams"
73710
74057
  ]
73711
74058
  }
74059
+ ),
74060
+ /* @__PURE__ */ jsxs(
74061
+ Link$1,
74062
+ {
74063
+ to: appPath("/admin/waitlist"),
74064
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
74065
+ children: [
74066
+ /* @__PURE__ */ jsx(Mail, { size: 16 }),
74067
+ "Waitlist"
74068
+ ]
74069
+ }
74070
+ ),
74071
+ /* @__PURE__ */ jsxs(
74072
+ Link$1,
74073
+ {
74074
+ to: appPath("/admin/promo-codes"),
74075
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
74076
+ children: [
74077
+ /* @__PURE__ */ jsx(Tag, { size: 16 }),
74078
+ "Promo Codes"
74079
+ ]
74080
+ }
73712
74081
  )
73713
74082
  ] }),
73714
74083
  error && /* @__PURE__ */ jsx("div", { className: "mb-6 rounded-lg bg-error/10 p-4 text-sm text-error", children: error }),
@@ -73884,6 +74253,28 @@ function AdminTeamPage() {
73884
74253
  "Teams"
73885
74254
  ]
73886
74255
  }
74256
+ ),
74257
+ /* @__PURE__ */ jsxs(
74258
+ Link$1,
74259
+ {
74260
+ to: appPath("/admin/waitlist"),
74261
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
74262
+ children: [
74263
+ /* @__PURE__ */ jsx(Mail, { size: 16 }),
74264
+ "Waitlist"
74265
+ ]
74266
+ }
74267
+ ),
74268
+ /* @__PURE__ */ jsxs(
74269
+ Link$1,
74270
+ {
74271
+ to: appPath("/admin/promo-codes"),
74272
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
74273
+ children: [
74274
+ /* @__PURE__ */ jsx(Tag, { size: 16 }),
74275
+ "Promo Codes"
74276
+ ]
74277
+ }
73887
74278
  )
73888
74279
  ] }),
73889
74280
  /* @__PURE__ */ jsx("div", { className: "rounded-lg bg-error/10 p-4 text-sm text-error", children: error || "Team not found" }),
@@ -73943,6 +74334,28 @@ function AdminTeamPage() {
73943
74334
  "Teams"
73944
74335
  ]
73945
74336
  }
74337
+ ),
74338
+ /* @__PURE__ */ jsxs(
74339
+ Link$1,
74340
+ {
74341
+ to: appPath("/admin/waitlist"),
74342
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
74343
+ children: [
74344
+ /* @__PURE__ */ jsx(Mail, { size: 16 }),
74345
+ "Waitlist"
74346
+ ]
74347
+ }
74348
+ ),
74349
+ /* @__PURE__ */ jsxs(
74350
+ Link$1,
74351
+ {
74352
+ to: appPath("/admin/promo-codes"),
74353
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
74354
+ children: [
74355
+ /* @__PURE__ */ jsx(Tag, { size: 16 }),
74356
+ "Promo Codes"
74357
+ ]
74358
+ }
73946
74359
  )
73947
74360
  ] }),
73948
74361
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 mb-6", children: [
@@ -74043,6 +74456,481 @@ function AdminTeamPage() {
74043
74456
  ] })
74044
74457
  ] }) });
74045
74458
  }
74459
+ function AdminWaitlistPage() {
74460
+ var _a;
74461
+ const config = useConfig();
74462
+ const appPath = useAppPath();
74463
+ const [entries, setEntries] = useState([]);
74464
+ const [isLoading, setIsLoading] = useState(true);
74465
+ const [error, setError] = useState("");
74466
+ const [success, setSuccess] = useState("");
74467
+ async function loadWaitlist() {
74468
+ setIsLoading(true);
74469
+ setError("");
74470
+ try {
74471
+ const response = await api.get("/api/admin/waitlist");
74472
+ setEntries(response.entries);
74473
+ } catch (err) {
74474
+ setError(err instanceof Error ? err.message : "Failed to load waitlist");
74475
+ } finally {
74476
+ setIsLoading(false);
74477
+ }
74478
+ }
74479
+ useEffect(() => {
74480
+ loadWaitlist();
74481
+ }, []);
74482
+ async function handleInvite(entryId, email) {
74483
+ setError("");
74484
+ setSuccess("");
74485
+ try {
74486
+ await api.post(`/api/admin/waitlist/${entryId}/invite`, {});
74487
+ setSuccess(`Invite sent to ${email}`);
74488
+ loadWaitlist();
74489
+ } catch (err) {
74490
+ setError(err instanceof Error ? err.message : "Failed to send invite");
74491
+ }
74492
+ }
74493
+ return /* @__PURE__ */ jsx("div", { className: "min-h-screen bg-background p-4 sm:p-8", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-6xl", children: [
74494
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-4", children: [
74495
+ /* @__PURE__ */ jsx("h1", { className: "text-xl sm:text-2xl font-bold text-text-primary", children: "Admin" }),
74496
+ /* @__PURE__ */ jsx(
74497
+ Link$1,
74498
+ {
74499
+ to: appPath("/"),
74500
+ className: "flex items-center justify-center rounded-lg p-2 text-text-muted hover:text-text-primary hover:bg-background-secondary",
74501
+ "aria-label": "Close",
74502
+ children: /* @__PURE__ */ jsx(X, { size: 20 })
74503
+ }
74504
+ )
74505
+ ] }),
74506
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-6 sm:mb-8", children: [
74507
+ /* @__PURE__ */ jsxs(
74508
+ Link$1,
74509
+ {
74510
+ to: appPath("/admin"),
74511
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
74512
+ children: [
74513
+ /* @__PURE__ */ jsx(LayoutDashboard, { size: 16 }),
74514
+ "Overview"
74515
+ ]
74516
+ }
74517
+ ),
74518
+ /* @__PURE__ */ jsxs(
74519
+ Link$1,
74520
+ {
74521
+ to: appPath("/admin/users"),
74522
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
74523
+ children: [
74524
+ /* @__PURE__ */ jsx(Users, { size: 16 }),
74525
+ "Users"
74526
+ ]
74527
+ }
74528
+ ),
74529
+ ((_a = config.teams) == null ? void 0 : _a.enabled) && /* @__PURE__ */ jsxs(
74530
+ Link$1,
74531
+ {
74532
+ to: appPath("/admin/teams"),
74533
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
74534
+ children: [
74535
+ /* @__PURE__ */ jsx(Building2, { size: 16 }),
74536
+ "Teams"
74537
+ ]
74538
+ }
74539
+ ),
74540
+ /* @__PURE__ */ jsxs(
74541
+ Link$1,
74542
+ {
74543
+ to: appPath("/admin/promo-codes"),
74544
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
74545
+ children: [
74546
+ /* @__PURE__ */ jsx(Tag, { size: 16 }),
74547
+ "Promo Codes"
74548
+ ]
74549
+ }
74550
+ ),
74551
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5 rounded-full bg-primary px-4 py-2 text-sm font-medium text-white", children: [
74552
+ /* @__PURE__ */ jsx(Mail, { size: 16 }),
74553
+ "Waitlist"
74554
+ ] })
74555
+ ] }),
74556
+ error && /* @__PURE__ */ jsx("div", { className: "mb-6 rounded-lg bg-error/10 p-4 text-sm text-error", children: error }),
74557
+ success && /* @__PURE__ */ jsx("div", { className: "mb-6 rounded-lg bg-success/10 p-4 text-sm text-success", children: success }),
74558
+ /* @__PURE__ */ jsxs("div", { className: "rounded-lg bg-background-secondary overflow-hidden", children: [
74559
+ /* @__PURE__ */ jsx("div", { className: "hidden md:block px-4 py-3 bg-background", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-12 gap-4 text-sm font-medium text-text-muted", children: [
74560
+ /* @__PURE__ */ jsx("div", { className: "col-span-4", children: "Email" }),
74561
+ /* @__PURE__ */ jsx("div", { className: "col-span-2", children: "Name" }),
74562
+ /* @__PURE__ */ jsx("div", { className: "col-span-2", children: "Status" }),
74563
+ /* @__PURE__ */ jsx("div", { className: "col-span-2", children: "Joined" }),
74564
+ /* @__PURE__ */ jsx("div", { className: "col-span-2", children: "Action" })
74565
+ ] }) }),
74566
+ /* @__PURE__ */ jsx("div", { className: "divide-y divide-background", children: isLoading ? /* @__PURE__ */ jsx("div", { className: "px-4 py-8 text-center text-text-muted", children: "Loading..." }) : entries.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-4 py-8 text-center text-text-muted", children: "No waitlist entries" }) : entries.map((entry) => /* @__PURE__ */ jsx("div", { className: "px-4 py-3 hover:bg-background/50", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 md:grid-cols-12 gap-2 md:gap-4 text-sm", children: [
74567
+ /* @__PURE__ */ jsx("div", { className: "md:col-span-4 text-text-primary", children: entry.email }),
74568
+ /* @__PURE__ */ jsx("div", { className: "md:col-span-2 text-text-secondary", children: entry.name || "-" }),
74569
+ /* @__PURE__ */ jsx("div", { className: "md:col-span-2 capitalize text-text-secondary", children: entry.status.replace("_", " ") }),
74570
+ /* @__PURE__ */ jsx("div", { className: "md:col-span-2 text-text-muted", children: new Date(entry.createdAt).toLocaleDateString() }),
74571
+ /* @__PURE__ */ jsx("div", { className: "md:col-span-2", children: /* @__PURE__ */ jsx(
74572
+ "button",
74573
+ {
74574
+ type: "button",
74575
+ onClick: () => handleInvite(entry.id, entry.email),
74576
+ disabled: entry.status === "invited",
74577
+ className: "rounded-lg bg-primary px-3 py-1.5 text-xs font-medium text-white hover:bg-primary-hover disabled:opacity-50",
74578
+ children: entry.status === "invited" ? "Invited" : "Invite"
74579
+ }
74580
+ ) })
74581
+ ] }) }, entry.id)) })
74582
+ ] })
74583
+ ] }) });
74584
+ }
74585
+ function AdminPromoCodesPage() {
74586
+ var _a, _b, _c;
74587
+ const config = useConfig();
74588
+ const appPath = useAppPath();
74589
+ const [promoCodes, setPromoCodes] = useState([]);
74590
+ const [isLoading, setIsLoading] = useState(true);
74591
+ const [error, setError] = useState("");
74592
+ const [success, setSuccess] = useState("");
74593
+ const [search2, setSearch] = useState("");
74594
+ const [statusFilter, setStatusFilter] = useState("all");
74595
+ const [copiedCode, setCopiedCode] = useState(null);
74596
+ const [form, setForm] = useState({
74597
+ code: "",
74598
+ credits: 10,
74599
+ maxUses: 100,
74600
+ startsAt: "",
74601
+ endsAt: "",
74602
+ creditsExpiresAt: ""
74603
+ });
74604
+ async function loadPromoCodes() {
74605
+ setIsLoading(true);
74606
+ setError("");
74607
+ try {
74608
+ const params = new URLSearchParams();
74609
+ if (search2.trim()) {
74610
+ params.set("search", search2.trim());
74611
+ }
74612
+ if (statusFilter !== "all") {
74613
+ params.set("status", statusFilter);
74614
+ }
74615
+ const query = params.toString();
74616
+ const response = await api.get(`/api/admin/promo-codes${query ? `?${query}` : ""}`);
74617
+ setPromoCodes(response.promoCodes);
74618
+ } catch (err) {
74619
+ setError(err instanceof Error ? err.message : "Failed to load promo codes");
74620
+ } finally {
74621
+ setIsLoading(false);
74622
+ }
74623
+ }
74624
+ useEffect(() => {
74625
+ loadPromoCodes();
74626
+ }, [search2, statusFilter]);
74627
+ async function handleCreatePromoCode(e) {
74628
+ e.preventDefault();
74629
+ setError("");
74630
+ setSuccess("");
74631
+ try {
74632
+ const payload = {
74633
+ code: form.code.trim(),
74634
+ credits: Number(form.credits),
74635
+ maxUses: Number(form.maxUses),
74636
+ startsAt: form.startsAt || void 0,
74637
+ endsAt: form.endsAt || void 0,
74638
+ creditsExpiresAt: form.creditsExpiresAt || void 0
74639
+ };
74640
+ const response = await api.post("/api/admin/promo-codes", payload);
74641
+ setPromoCodes((prev) => [response.promoCode, ...prev]);
74642
+ setSuccess(`Promo code ${response.promoCode.code} created`);
74643
+ setForm({
74644
+ code: "",
74645
+ credits: 10,
74646
+ maxUses: 100,
74647
+ startsAt: "",
74648
+ endsAt: "",
74649
+ creditsExpiresAt: ""
74650
+ });
74651
+ } catch (err) {
74652
+ setError(err instanceof Error ? err.message : "Failed to create promo code");
74653
+ }
74654
+ }
74655
+ const promoEnabled = ((_a = config.credits) == null ? void 0 : _a.enabled) && ((_b = config.credits) == null ? void 0 : _b.promoEnabled) !== false;
74656
+ async function handleDeactivate(promo) {
74657
+ setError("");
74658
+ setSuccess("");
74659
+ try {
74660
+ const response = await api.patch(`/api/admin/promo-codes/${promo.id}`, {
74661
+ endsAt: (/* @__PURE__ */ new Date()).toISOString()
74662
+ });
74663
+ setPromoCodes((prev) => prev.map((p) => p.id === promo.id ? response.promoCode : p));
74664
+ setSuccess(`Promo code ${promo.code} deactivated`);
74665
+ } catch (err) {
74666
+ setError(err instanceof Error ? err.message : "Failed to deactivate promo code");
74667
+ }
74668
+ }
74669
+ async function handleReactivate(promo) {
74670
+ setError("");
74671
+ setSuccess("");
74672
+ try {
74673
+ const response = await api.patch(`/api/admin/promo-codes/${promo.id}`, {
74674
+ endsAt: null
74675
+ });
74676
+ setPromoCodes((prev) => prev.map((p) => p.id === promo.id ? response.promoCode : p));
74677
+ setSuccess(`Promo code ${promo.code} reactivated`);
74678
+ } catch (err) {
74679
+ setError(err instanceof Error ? err.message : "Failed to reactivate promo code");
74680
+ }
74681
+ }
74682
+ async function handleCopy(code2) {
74683
+ try {
74684
+ await navigator.clipboard.writeText(code2);
74685
+ setCopiedCode(code2);
74686
+ setTimeout(() => setCopiedCode((current) => current === code2 ? null : current), 1500);
74687
+ } catch (err) {
74688
+ setError(err instanceof Error ? err.message : "Failed to copy code");
74689
+ }
74690
+ }
74691
+ return /* @__PURE__ */ jsx("div", { className: "min-h-screen bg-background p-4 sm:p-8", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-6xl", children: [
74692
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-4", children: [
74693
+ /* @__PURE__ */ jsx("h1", { className: "text-xl sm:text-2xl font-bold text-text-primary", children: "Admin" }),
74694
+ /* @__PURE__ */ jsx(
74695
+ Link$1,
74696
+ {
74697
+ to: appPath("/"),
74698
+ className: "flex items-center justify-center rounded-lg p-2 text-text-muted hover:text-text-primary hover:bg-background-secondary",
74699
+ "aria-label": "Close",
74700
+ children: /* @__PURE__ */ jsx(X, { size: 20 })
74701
+ }
74702
+ )
74703
+ ] }),
74704
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-6 sm:mb-8", children: [
74705
+ /* @__PURE__ */ jsxs(
74706
+ Link$1,
74707
+ {
74708
+ to: appPath("/admin"),
74709
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
74710
+ children: [
74711
+ /* @__PURE__ */ jsx(LayoutDashboard, { size: 16 }),
74712
+ "Overview"
74713
+ ]
74714
+ }
74715
+ ),
74716
+ /* @__PURE__ */ jsxs(
74717
+ Link$1,
74718
+ {
74719
+ to: appPath("/admin/users"),
74720
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
74721
+ children: [
74722
+ /* @__PURE__ */ jsx(Users, { size: 16 }),
74723
+ "Users"
74724
+ ]
74725
+ }
74726
+ ),
74727
+ ((_c = config.teams) == null ? void 0 : _c.enabled) && /* @__PURE__ */ jsxs(
74728
+ Link$1,
74729
+ {
74730
+ to: appPath("/admin/teams"),
74731
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
74732
+ children: [
74733
+ /* @__PURE__ */ jsx(Building2, { size: 16 }),
74734
+ "Teams"
74735
+ ]
74736
+ }
74737
+ ),
74738
+ /* @__PURE__ */ jsxs(
74739
+ Link$1,
74740
+ {
74741
+ to: appPath("/admin/waitlist"),
74742
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
74743
+ children: [
74744
+ /* @__PURE__ */ jsx(Mail, { size: 16 }),
74745
+ "Waitlist"
74746
+ ]
74747
+ }
74748
+ ),
74749
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5 rounded-full bg-primary px-4 py-2 text-sm font-medium text-white", children: [
74750
+ /* @__PURE__ */ jsx(Tag, { size: 16 }),
74751
+ "Promo Codes"
74752
+ ] })
74753
+ ] }),
74754
+ !promoEnabled && /* @__PURE__ */ jsx("div", { className: "mb-6 rounded-lg bg-warning/10 p-4 text-sm text-warning", children: "Promo codes are disabled in config. Enable `credits.promoEnabled` to use this page." }),
74755
+ error && /* @__PURE__ */ jsx("div", { className: "mb-6 rounded-lg bg-error/10 p-4 text-sm text-error", children: error }),
74756
+ success && /* @__PURE__ */ jsx("div", { className: "mb-6 rounded-lg bg-success/10 p-4 text-sm text-success", children: success }),
74757
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-3 gap-6", children: [
74758
+ /* @__PURE__ */ jsxs("div", { className: "lg:col-span-1 rounded-lg bg-background-secondary p-4", children: [
74759
+ /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold text-text-primary mb-4", children: "Create Promo Code" }),
74760
+ /* @__PURE__ */ jsxs("form", { onSubmit: handleCreatePromoCode, className: "space-y-3", children: [
74761
+ /* @__PURE__ */ jsxs("div", { children: [
74762
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-text-primary", htmlFor: "promo-code", children: "Code" }),
74763
+ /* @__PURE__ */ jsx(
74764
+ "input",
74765
+ {
74766
+ id: "promo-code",
74767
+ value: form.code,
74768
+ onChange: (e) => setForm((prev) => ({ ...prev, code: e.target.value })),
74769
+ className: "mt-1 w-full rounded-lg border border-input-border bg-input-background px-3 py-2 text-text-primary focus:border-primary focus:outline-none",
74770
+ placeholder: "WELCOME10",
74771
+ required: true
74772
+ }
74773
+ )
74774
+ ] }),
74775
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
74776
+ /* @__PURE__ */ jsxs("div", { children: [
74777
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-text-primary", htmlFor: "promo-credits", children: "Credits" }),
74778
+ /* @__PURE__ */ jsx(
74779
+ "input",
74780
+ {
74781
+ id: "promo-credits",
74782
+ type: "number",
74783
+ min: 1,
74784
+ value: form.credits,
74785
+ onChange: (e) => setForm((prev) => ({ ...prev, credits: Number(e.target.value) })),
74786
+ className: "mt-1 w-full rounded-lg border border-input-border bg-input-background px-3 py-2 text-text-primary focus:border-primary focus:outline-none",
74787
+ required: true
74788
+ }
74789
+ )
74790
+ ] }),
74791
+ /* @__PURE__ */ jsxs("div", { children: [
74792
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-text-primary", htmlFor: "promo-uses", children: "Max Uses" }),
74793
+ /* @__PURE__ */ jsx(
74794
+ "input",
74795
+ {
74796
+ id: "promo-uses",
74797
+ type: "number",
74798
+ min: 1,
74799
+ value: form.maxUses,
74800
+ onChange: (e) => setForm((prev) => ({ ...prev, maxUses: Number(e.target.value) })),
74801
+ className: "mt-1 w-full rounded-lg border border-input-border bg-input-background px-3 py-2 text-text-primary focus:border-primary focus:outline-none",
74802
+ required: true
74803
+ }
74804
+ )
74805
+ ] })
74806
+ ] }),
74807
+ /* @__PURE__ */ jsxs("div", { children: [
74808
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-text-primary", htmlFor: "promo-start", children: "Starts At (optional)" }),
74809
+ /* @__PURE__ */ jsx(
74810
+ "input",
74811
+ {
74812
+ id: "promo-start",
74813
+ type: "datetime-local",
74814
+ value: form.startsAt,
74815
+ onChange: (e) => setForm((prev) => ({ ...prev, startsAt: e.target.value })),
74816
+ className: "mt-1 w-full rounded-lg border border-input-border bg-input-background px-3 py-2 text-text-primary focus:border-primary focus:outline-none"
74817
+ }
74818
+ )
74819
+ ] }),
74820
+ /* @__PURE__ */ jsxs("div", { children: [
74821
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-text-primary", htmlFor: "promo-end", children: "Ends At (optional)" }),
74822
+ /* @__PURE__ */ jsx(
74823
+ "input",
74824
+ {
74825
+ id: "promo-end",
74826
+ type: "datetime-local",
74827
+ value: form.endsAt,
74828
+ onChange: (e) => setForm((prev) => ({ ...prev, endsAt: e.target.value })),
74829
+ className: "mt-1 w-full rounded-lg border border-input-border bg-input-background px-3 py-2 text-text-primary focus:border-primary focus:outline-none"
74830
+ }
74831
+ )
74832
+ ] }),
74833
+ /* @__PURE__ */ jsxs("div", { children: [
74834
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-text-primary", htmlFor: "promo-expires", children: "Credits Expire At (optional)" }),
74835
+ /* @__PURE__ */ jsx(
74836
+ "input",
74837
+ {
74838
+ id: "promo-expires",
74839
+ type: "datetime-local",
74840
+ value: form.creditsExpiresAt,
74841
+ onChange: (e) => setForm((prev) => ({ ...prev, creditsExpiresAt: e.target.value })),
74842
+ className: "mt-1 w-full rounded-lg border border-input-border bg-input-background px-3 py-2 text-text-primary focus:border-primary focus:outline-none"
74843
+ }
74844
+ )
74845
+ ] }),
74846
+ /* @__PURE__ */ jsx(
74847
+ "button",
74848
+ {
74849
+ type: "submit",
74850
+ disabled: !promoEnabled,
74851
+ className: "w-full rounded-lg bg-primary px-4 py-2 font-medium text-white hover:bg-primary-hover disabled:opacity-50",
74852
+ children: "Create promo code"
74853
+ }
74854
+ )
74855
+ ] })
74856
+ ] }),
74857
+ /* @__PURE__ */ jsxs("div", { className: "lg:col-span-2 rounded-lg bg-background-secondary overflow-hidden", children: [
74858
+ /* @__PURE__ */ jsxs("div", { className: "px-4 py-3 bg-background flex flex-wrap items-center gap-3", children: [
74859
+ /* @__PURE__ */ jsx(
74860
+ "input",
74861
+ {
74862
+ type: "text",
74863
+ placeholder: "Search code...",
74864
+ value: search2,
74865
+ onChange: (e) => setSearch(e.target.value),
74866
+ className: "flex-1 min-w-[160px] rounded-lg border border-input-border bg-input-background px-3 py-2 text-sm text-text-primary focus:border-primary focus:outline-none"
74867
+ }
74868
+ ),
74869
+ /* @__PURE__ */ jsxs(
74870
+ "select",
74871
+ {
74872
+ value: statusFilter,
74873
+ onChange: (e) => setStatusFilter(e.target.value),
74874
+ className: "rounded-lg border border-input-border bg-input-background px-3 py-2 text-sm text-text-primary focus:border-primary focus:outline-none",
74875
+ children: [
74876
+ /* @__PURE__ */ jsx("option", { value: "all", children: "All" }),
74877
+ /* @__PURE__ */ jsx("option", { value: "active", children: "Active" }),
74878
+ /* @__PURE__ */ jsx("option", { value: "scheduled", children: "Scheduled" }),
74879
+ /* @__PURE__ */ jsx("option", { value: "expired", children: "Expired" })
74880
+ ]
74881
+ }
74882
+ )
74883
+ ] }),
74884
+ /* @__PURE__ */ jsx("div", { className: "hidden md:block px-4 py-3 bg-background", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-12 gap-4 text-sm font-medium text-text-muted", children: [
74885
+ /* @__PURE__ */ jsx("div", { className: "col-span-3", children: "Code" }),
74886
+ /* @__PURE__ */ jsx("div", { className: "col-span-2", children: "Credits" }),
74887
+ /* @__PURE__ */ jsx("div", { className: "col-span-2", children: "Uses" }),
74888
+ /* @__PURE__ */ jsx("div", { className: "col-span-2", children: "Active" }),
74889
+ /* @__PURE__ */ jsx("div", { className: "col-span-3", children: "Actions" })
74890
+ ] }) }),
74891
+ /* @__PURE__ */ jsx("div", { className: "divide-y divide-background", children: isLoading ? /* @__PURE__ */ jsx("div", { className: "px-4 py-8 text-center text-text-muted", children: "Loading..." }) : promoCodes.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-4 py-8 text-center text-text-muted", children: "No promo codes yet" }) : promoCodes.map((promo) => {
74892
+ const now = /* @__PURE__ */ new Date();
74893
+ const startsAt = promo.startsAt ? new Date(promo.startsAt) : null;
74894
+ const endsAt = promo.endsAt ? new Date(promo.endsAt) : null;
74895
+ const active = (!startsAt || startsAt <= now) && (!endsAt || endsAt >= now);
74896
+ return /* @__PURE__ */ jsx("div", { className: "px-4 py-3 hover:bg-background/50", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 md:grid-cols-12 gap-2 md:gap-4 text-sm", children: [
74897
+ /* @__PURE__ */ jsxs("div", { className: "md:col-span-3 font-medium text-text-primary flex items-center gap-2", children: [
74898
+ /* @__PURE__ */ jsx("span", { children: promo.code }),
74899
+ /* @__PURE__ */ jsx(
74900
+ "button",
74901
+ {
74902
+ type: "button",
74903
+ onClick: () => handleCopy(promo.code),
74904
+ className: "text-xs text-primary hover:underline",
74905
+ children: copiedCode === promo.code ? "Copied" : "Copy"
74906
+ }
74907
+ )
74908
+ ] }),
74909
+ /* @__PURE__ */ jsx("div", { className: "md:col-span-2 text-text-secondary", children: promo.credits }),
74910
+ /* @__PURE__ */ jsxs("div", { className: "md:col-span-2 text-text-secondary", children: [
74911
+ promo.redeemedCount,
74912
+ " / ",
74913
+ promo.maxUses
74914
+ ] }),
74915
+ /* @__PURE__ */ jsx("div", { className: `md:col-span-2 ${active ? "text-success" : "text-text-muted"}`, children: active ? "Active" : "Inactive" }),
74916
+ /* @__PURE__ */ jsxs("div", { className: "md:col-span-3 text-text-muted flex items-center gap-2", children: [
74917
+ /* @__PURE__ */ jsx("span", { children: new Date(promo.createdAt).toLocaleDateString() }),
74918
+ /* @__PURE__ */ jsx(
74919
+ "button",
74920
+ {
74921
+ type: "button",
74922
+ onClick: () => active ? handleDeactivate(promo) : handleReactivate(promo),
74923
+ className: "rounded-lg border border-input-border px-2 py-1 text-xs text-text-primary hover:bg-background",
74924
+ children: active ? "Deactivate" : "Reactivate"
74925
+ }
74926
+ )
74927
+ ] })
74928
+ ] }) }, promo.id);
74929
+ }) })
74930
+ ] })
74931
+ ] })
74932
+ ] }) });
74933
+ }
74046
74934
  const styles = "@chaaskit/client/src/styles/index.css";
74047
74935
  function ChatProviders({ children, initialConfig }) {
74048
74936
  return /* @__PURE__ */ jsx(ConfigProvider, { initialConfig, children: /* @__PURE__ */ jsx(ThemeProvider, { children: /* @__PURE__ */ jsx(AuthProvider, { children: /* @__PURE__ */ jsx(TeamProvider, { children: /* @__PURE__ */ jsx(ProjectProvider, { children }) }) }) }) });
@@ -74050,9 +74938,11 @@ function ChatProviders({ children, initialConfig }) {
74050
74938
  export {
74051
74939
  AcceptInvitePage,
74052
74940
  AdminDashboardPage,
74941
+ AdminPromoCodesPage,
74053
74942
  AdminTeamPage,
74054
74943
  AdminTeamsPage,
74055
74944
  AdminUsersPage,
74945
+ AdminWaitlistPage,
74056
74946
  ApiKeysPage,
74057
74947
  AuthProvider,
74058
74948
  C as ChatLoadingSkeleton,
@@ -74081,7 +74971,7 @@ export {
74081
74971
  TermsPage,
74082
74972
  ThemeProvider,
74083
74973
  VerifyEmailPage,
74084
- c2 as clientRegistry,
74974
+ clientRegistry,
74085
74975
  styles,
74086
74976
  useAppPath,
74087
74977
  useAuth,