@infuro/cms-core 1.0.31 → 1.0.33

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/dist/admin.js CHANGED
@@ -775,7 +775,7 @@ __export(ContactDetailPage_exports, {
775
775
  import { useEffect as useEffect46, useState as useState49 } from "react";
776
776
  import Link15 from "next/link";
777
777
  import { useRouter as useRouter20, useSearchParams as useSearchParams19 } from "next/navigation";
778
- import { Plus as Plus15, Trash2 as Trash214, X as X22 } from "lucide-react";
778
+ import { Plus as Plus15, Trash2 as Trash215, X as X22 } from "lucide-react";
779
779
  import { toast as toast11 } from "sonner";
780
780
  import { Country as Country3, State as State3 } from "country-state-city";
781
781
  import { Fragment as Fragment23, jsx as jsx82, jsxs as jsxs69 } from "react/jsx-runtime";
@@ -1023,7 +1023,7 @@ function ContactDetailPage({ contactId }) {
1023
1023
  title: "Delete address",
1024
1024
  "aria-label": `Delete address ${a.id}`,
1025
1025
  onClick: () => setAddressPendingDelete(a),
1026
- children: /* @__PURE__ */ jsx82(Trash214, { className: "h-4 w-4" })
1026
+ children: /* @__PURE__ */ jsx82(Trash215, { className: "h-4 w-4" })
1027
1027
  }
1028
1028
  ) })
1029
1029
  ] }, a.id)) })
@@ -1542,7 +1542,7 @@ var defaultValue = {
1542
1542
  var AdminConfigContext = createContext(defaultValue);
1543
1543
 
1544
1544
  // src/lib/cms-version.ts
1545
- var CMS_VERSION = true ? "1.0.31" : "0.0.0";
1545
+ var CMS_VERSION = true ? "1.0.33" : "0.0.0";
1546
1546
 
1547
1547
  // src/components/Admin/Sidebar.tsx
1548
1548
  import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
@@ -2036,6 +2036,34 @@ var BUILTIN_PLUGIN_DESCRIPTORS = [
2036
2036
  }
2037
2037
  ];
2038
2038
 
2039
+ // src/auth/auth-debug.ts
2040
+ var LOG_PREFIX = "[cms-auth]";
2041
+ function isAuthDebugClientEnabled() {
2042
+ if (typeof window === "undefined") return false;
2043
+ const v = process.env.NEXT_PUBLIC_CMS_AUTH_DEBUG?.trim().toLowerCase();
2044
+ if (v === "1" || v === "true" || v === "yes") return true;
2045
+ if (v === "0" || v === "false" || v === "no") return false;
2046
+ return process.env.NODE_ENV === "development";
2047
+ }
2048
+ function logAuthClient(message, data) {
2049
+ if (!isAuthDebugClientEnabled()) return;
2050
+ if (data) console.info(LOG_PREFIX, message, data);
2051
+ else console.info(LOG_PREFIX, message);
2052
+ }
2053
+ function summarizeSessionUserForLog(user) {
2054
+ if (!user || typeof user !== "object") return { present: false };
2055
+ const u = user;
2056
+ return {
2057
+ present: true,
2058
+ email: u.email ?? null,
2059
+ id: u.id ?? null,
2060
+ adminAccess: u.adminAccess ?? null,
2061
+ isRBACAdmin: u.isRBACAdmin ?? null,
2062
+ isVendorPortal: u.isVendorPortal ?? null,
2063
+ groupName: u.groupName ?? null
2064
+ };
2065
+ }
2066
+
2039
2067
  // src/admin/pages/AdminLayout.tsx
2040
2068
  import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
2041
2069
  var PUBLIC_ADMIN_PATHS = [
@@ -2069,6 +2097,14 @@ function AdminLayoutInner({ children }) {
2069
2097
  useEffect2(() => {
2070
2098
  if (isMobile) setMoreSheetOpen(false);
2071
2099
  }, [isMobile, pathname]);
2100
+ useEffect2(() => {
2101
+ logAuthClient("AdminLayout session state", {
2102
+ pathname,
2103
+ isPublicPath,
2104
+ status,
2105
+ sessionUser: summarizeSessionUserForLog(session?.user)
2106
+ });
2107
+ }, [pathname, isPublicPath, status, session?.user]);
2072
2108
  useEffect2(() => {
2073
2109
  if (isPublicPath) return;
2074
2110
  if (status === "loading") {
@@ -2076,6 +2112,7 @@ function AdminLayoutInner({ children }) {
2076
2112
  return () => clearTimeout(timeout);
2077
2113
  }
2078
2114
  if (status === "unauthenticated") {
2115
+ logAuthClient("AdminLayout redirect \u2192 /admin/signin (unauthenticated)", { pathname });
2079
2116
  router.replace("/admin/signin");
2080
2117
  } else {
2081
2118
  setLoadingTimeout(false);
@@ -9043,7 +9080,9 @@ var SigninPage = () => {
9043
9080
  const router = useRouter5();
9044
9081
  const { status } = useSession5();
9045
9082
  useEffect18(() => {
9083
+ logAuthClient("SignInPage session status", { status });
9046
9084
  if (status === "authenticated") {
9085
+ logAuthClient("SignInPage redirect \u2192 /admin/dashboard (already authenticated)");
9047
9086
  router.replace("/admin/dashboard");
9048
9087
  }
9049
9088
  }, [status, router]);
@@ -9052,14 +9091,22 @@ var SigninPage = () => {
9052
9091
  setLoading(true);
9053
9092
  setError("");
9054
9093
  try {
9094
+ logAuthClient("SignInPage signIn start", { email });
9055
9095
  const result = await signIn("credentials", {
9056
9096
  email,
9057
9097
  password,
9058
9098
  redirect: false
9059
9099
  });
9100
+ logAuthClient("SignInPage signIn result", {
9101
+ ok: result?.ok ?? null,
9102
+ error: result?.error ?? null,
9103
+ status: result?.status ?? null,
9104
+ url: result?.url ?? null
9105
+ });
9060
9106
  if (result?.error) {
9061
9107
  setError("Invalid email or password");
9062
9108
  } else {
9109
+ logAuthClient("SignInPage navigate \u2192 /admin/dashboard");
9063
9110
  window.location.assign("/admin/dashboard");
9064
9111
  }
9065
9112
  } catch (error2) {
@@ -10178,6 +10225,7 @@ import { useEffect as useEffect23, useState as useState26 } from "react";
10178
10225
  import Link10 from "next/link";
10179
10226
  import { useRouter as useRouter8, useSearchParams as useSearchParams5 } from "next/navigation";
10180
10227
  import { X as X15, RefreshCw as RefreshCw2, Pencil } from "lucide-react";
10228
+ import { QRCode } from "react-qr-code";
10181
10229
  import { Fragment as Fragment10, jsx as jsx52, jsxs as jsxs42 } from "react/jsx-runtime";
10182
10230
  function formatMoney(amount, currency = "INR") {
10183
10231
  return new Intl.NumberFormat("en-IN", {
@@ -10535,6 +10583,19 @@ function OrderDetailPage({ orderId }) {
10535
10583
  },
10536
10584
  p.id
10537
10585
  )) })
10586
+ ] }),
10587
+ order.qrToken && /* @__PURE__ */ jsxs42("section", { children: [
10588
+ /* @__PURE__ */ jsx52("h2", { className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2", children: "Order QR Code" }),
10589
+ /* @__PURE__ */ jsxs42("div", { className: "min-w-0 border border-gray-200 rounded-lg p-4 bg-gray-50/50 flex flex-col items-center gap-2", children: [
10590
+ /* @__PURE__ */ jsx52(
10591
+ QRCode,
10592
+ {
10593
+ value: `${window.location.origin}/track/${order.qrToken}`,
10594
+ size: 180
10595
+ }
10596
+ ),
10597
+ /* @__PURE__ */ jsx52("p", { className: "text-xs text-gray-400 break-all text-center", children: order.qrToken })
10598
+ ] })
10538
10599
  ] })
10539
10600
  ] })
10540
10601
  }
@@ -15183,6 +15244,7 @@ import {
15183
15244
  Save as Save6,
15184
15245
  X as X20,
15185
15246
  Plus as Plus10,
15247
+ Trash2 as Trash29,
15186
15248
  Smartphone,
15187
15249
  Bot,
15188
15250
  FileUp,
@@ -16131,6 +16193,155 @@ function serializeEmailRecipients(emails) {
16131
16193
  init_button();
16132
16194
  init_input();
16133
16195
  import { toast as toast10 } from "sonner";
16196
+
16197
+ // src/plugins/llm/chat-email-intent.ts
16198
+ function normalizeIntentKey(raw) {
16199
+ return raw.trim().toUpperCase().replace(/[^A-Z0-9]+/g, "_").replace(/^_+|_+$/g, "");
16200
+ }
16201
+ function parseIntentsJson(raw) {
16202
+ if (!raw?.trim()) return null;
16203
+ try {
16204
+ const parsed = JSON.parse(raw);
16205
+ if (!Array.isArray(parsed)) return null;
16206
+ const out = [];
16207
+ for (const row of parsed) {
16208
+ if (!row || typeof row !== "object") continue;
16209
+ const o = row;
16210
+ const intent = normalizeIntentKey(String(o.intent ?? o.id ?? ""));
16211
+ const description = String(o.description ?? "").trim();
16212
+ const emailTo = String(o.emailTo ?? o.email ?? "").trim();
16213
+ if (!intent || !description || !emailTo) continue;
16214
+ out.push({ intent, description, emailTo });
16215
+ }
16216
+ return out.length > 0 ? out : null;
16217
+ } catch {
16218
+ return null;
16219
+ }
16220
+ }
16221
+ function dedupeIntents(intents) {
16222
+ const seen = /* @__PURE__ */ new Set();
16223
+ const out = [];
16224
+ for (const row of intents) {
16225
+ const key = normalizeIntentKey(row.intent);
16226
+ if (!key || seen.has(key)) continue;
16227
+ seen.add(key);
16228
+ out.push({
16229
+ intent: key,
16230
+ description: row.description.trim(),
16231
+ emailTo: row.emailTo.trim()
16232
+ });
16233
+ }
16234
+ return out;
16235
+ }
16236
+ function parseEmailIntentsFromSettings(raw) {
16237
+ return dedupeIntents(parseIntentsJson(raw) ?? []);
16238
+ }
16239
+ function serializeChatLeadIntents(intents) {
16240
+ return JSON.stringify(
16241
+ dedupeIntents(intents).map((r) => ({
16242
+ intent: r.intent,
16243
+ description: r.description,
16244
+ emailTo: r.emailTo
16245
+ }))
16246
+ );
16247
+ }
16248
+ var EMAIL_TOOL_VALIDATION_KEY = "emailTool";
16249
+ function parseEmailToolObject(raw) {
16250
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
16251
+ const o = raw;
16252
+ const intentsRaw = o.intents;
16253
+ let intents;
16254
+ if (Array.isArray(intentsRaw)) {
16255
+ const parsed = parseIntentsJson(JSON.stringify(intentsRaw));
16256
+ if (parsed?.length) intents = parsed;
16257
+ }
16258
+ return {
16259
+ enabled: o.enabled === true,
16260
+ classifierInstructions: typeof o.classifierInstructions === "string" ? o.classifierInstructions.trim() : void 0,
16261
+ toolPrompt: typeof o.toolPrompt === "string" ? o.toolPrompt.trim() : void 0,
16262
+ intents
16263
+ };
16264
+ }
16265
+ function splitValidationRulesForAdmin(validationRulesText) {
16266
+ const raw = validationRulesText?.trim() ?? "";
16267
+ if (!raw) return { guardrailsJson: "", emailTool: null };
16268
+ try {
16269
+ const parsed = JSON.parse(raw);
16270
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
16271
+ return { guardrailsJson: raw, emailTool: null };
16272
+ }
16273
+ const emailTool = parseEmailToolObject(parsed[EMAIL_TOOL_VALIDATION_KEY]);
16274
+ const rest = { ...parsed };
16275
+ delete rest[EMAIL_TOOL_VALIDATION_KEY];
16276
+ const guardrailsJson = Object.keys(rest).length > 0 ? JSON.stringify(rest, null, 2) : "";
16277
+ return { guardrailsJson, emailTool };
16278
+ } catch {
16279
+ return { guardrailsJson: raw, emailTool: null };
16280
+ }
16281
+ }
16282
+ function mergeEmailToolIntoValidationRules(guardrailsJson, emailTool) {
16283
+ const intents = dedupeIntents(emailTool.intents);
16284
+ const hasEmail = intents.length > 0 || emailTool.classifierInstructions.trim() !== "" || emailTool.toolPrompt.trim() !== "";
16285
+ let base = {};
16286
+ const raw = guardrailsJson?.trim();
16287
+ if (raw) {
16288
+ try {
16289
+ const parsed = JSON.parse(raw);
16290
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
16291
+ base = { ...parsed };
16292
+ delete base[EMAIL_TOOL_VALIDATION_KEY];
16293
+ } else {
16294
+ return raw;
16295
+ }
16296
+ } catch {
16297
+ return raw;
16298
+ }
16299
+ }
16300
+ if (!hasEmail) {
16301
+ return Object.keys(base).length > 0 ? JSON.stringify(base, null, 2) : null;
16302
+ }
16303
+ base[EMAIL_TOOL_VALIDATION_KEY] = {
16304
+ enabled: true,
16305
+ classifierInstructions: emailTool.classifierInstructions.trim(),
16306
+ toolPrompt: emailTool.toolPrompt.trim(),
16307
+ intents: intents.map((r) => ({
16308
+ intent: r.intent,
16309
+ description: r.description,
16310
+ emailTo: r.emailTo
16311
+ }))
16312
+ };
16313
+ return JSON.stringify(base, null, 2);
16314
+ }
16315
+
16316
+ // src/plugins/llm/llm-agent-scope.ts
16317
+ var LLM_AGENT_SCOPE_CHATBOT = "chatbot";
16318
+ var LLM_AGENT_SCOPE_BLOG_CREATION = "blog_creation";
16319
+ var LLM_AGENT_SCOPE_SOCIAL_MEDIA_POST = "social_media_post";
16320
+ var LLM_AGENT_SCOPE_EMAIL_INTENT_CHATBOT = "email_intent_chatbot";
16321
+ var LLM_AGENT_SCOPE_BLOG_METADATA = "blog_metadata";
16322
+ var LLM_AGENT_DEFAULT_SLUG_BY_SCOPE = {
16323
+ [LLM_AGENT_SCOPE_CHATBOT]: "site-chat-assistant",
16324
+ [LLM_AGENT_SCOPE_BLOG_CREATION]: "blog-generator",
16325
+ [LLM_AGENT_SCOPE_SOCIAL_MEDIA_POST]: "blog-generator-social",
16326
+ [LLM_AGENT_SCOPE_EMAIL_INTENT_CHATBOT]: "email-intent-chatbot",
16327
+ [LLM_AGENT_SCOPE_BLOG_METADATA]: "blog-generator-metadata"
16328
+ };
16329
+ var LLM_AGENT_DEFAULT_NAME_BY_SCOPE = {
16330
+ [LLM_AGENT_SCOPE_CHATBOT]: "Site Chat Assistant",
16331
+ [LLM_AGENT_SCOPE_BLOG_CREATION]: "Blog Generator",
16332
+ [LLM_AGENT_SCOPE_SOCIAL_MEDIA_POST]: "Blog Generator (Social)",
16333
+ [LLM_AGENT_SCOPE_EMAIL_INTENT_CHATBOT]: "Chat Lead Intent Classifier",
16334
+ [LLM_AGENT_SCOPE_BLOG_METADATA]: "Blog Generator (Metadata)"
16335
+ };
16336
+ var SITE_CHAT_ASSISTANT_SLUG = LLM_AGENT_DEFAULT_SLUG_BY_SCOPE[LLM_AGENT_SCOPE_CHATBOT];
16337
+ var SITE_CHAT_ASSISTANT_NAME = LLM_AGENT_DEFAULT_NAME_BY_SCOPE[LLM_AGENT_SCOPE_CHATBOT];
16338
+ var BLOG_LLM_AGENT_SLUGS = [
16339
+ LLM_AGENT_DEFAULT_SLUG_BY_SCOPE[LLM_AGENT_SCOPE_BLOG_CREATION],
16340
+ LLM_AGENT_DEFAULT_SLUG_BY_SCOPE[LLM_AGENT_SCOPE_BLOG_METADATA],
16341
+ LLM_AGENT_DEFAULT_SLUG_BY_SCOPE[LLM_AGENT_SCOPE_SOCIAL_MEDIA_POST]
16342
+ ];
16343
+
16344
+ // src/admin/pages/PluginsPage.tsx
16134
16345
  import { Fragment as Fragment16, jsx as jsx66, jsxs as jsxs55 } from "react/jsx-runtime";
16135
16346
  function normalizeLinkedInOrganizations(payload) {
16136
16347
  if (!payload || typeof payload !== "object") return [];
@@ -16170,10 +16381,6 @@ function normalizeFacebookGraphPages(graph) {
16170
16381
  }
16171
16382
  return out;
16172
16383
  }
16173
- function slugifyAgentKey(name) {
16174
- const s = name.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
16175
- return s || "agent";
16176
- }
16177
16384
  function normalizeChatMode(raw) {
16178
16385
  if (raw === "external" || raw === "llm") return raw;
16179
16386
  return "whatsapp";
@@ -16312,6 +16519,8 @@ function PluginSettingsPanel({
16312
16519
  descriptor,
16313
16520
  onSaved
16314
16521
  }) {
16522
+ const { pluginDescriptors = [] } = useContext7(AdminConfigContext);
16523
+ const emailPluginAvailable = pluginDescriptors.some((p) => p.name === "email" && p.enabled !== false);
16315
16524
  const settingsGroup = descriptor.settingsGroup;
16316
16525
  const isLlm = settingsGroup === "llm";
16317
16526
  const isEmail = settingsGroup === "email";
@@ -16347,9 +16556,19 @@ function PluginSettingsPanel({
16347
16556
  const [agentTemp, setAgentTemp] = useState37("");
16348
16557
  const [agentMaxTokens, setAgentMaxTokens] = useState37("");
16349
16558
  const [agentValidationJson, setAgentValidationJson] = useState37("");
16559
+ const [emailToolEnabled, setEmailToolEnabled] = useState37(false);
16560
+ const [emailIntents, setEmailIntents] = useState37([]);
16561
+ const [emailToolPrompt, setEmailToolPrompt] = useState37("");
16350
16562
  const [agentLoading, setAgentLoading] = useState37(false);
16351
16563
  const [agentSaving, setAgentSaving] = useState37(false);
16352
16564
  const [agentProvisionError, setAgentProvisionError] = useState37(null);
16565
+ const [llmConfigTab, setLlmConfigTab] = useState37("chat");
16566
+ const [notifyEmailAgentId, setNotifyEmailAgentId] = useState37(null);
16567
+ const [notifyEmailName, setNotifyEmailName] = useState37("");
16568
+ const [notifyEmailSystem, setNotifyEmailSystem] = useState37("");
16569
+ const [notifyEmailLoading, setNotifyEmailLoading] = useState37(false);
16570
+ const [notifyEmailSaving, setNotifyEmailSaving] = useState37(false);
16571
+ const [notifyEmailProvisionError, setNotifyEmailProvisionError] = useState37(null);
16353
16572
  const [kbCatalog, setKbCatalog] = useState37([]);
16354
16573
  const [attachedAgentKnowledge, setAttachedAgentKnowledge] = useState37([]);
16355
16574
  const [attachedKbLoading, setAttachedKbLoading] = useState37(false);
@@ -16571,7 +16790,12 @@ function PluginSettingsPanel({
16571
16790
  setIconImageUrl(data.iconImageUrl ?? "");
16572
16791
  setIconBackgroundColor(data.iconBackgroundColor ?? "#6366f1");
16573
16792
  setHeaderColor(data.headerColor ?? "#6366f1");
16574
- setAttachedAgentSlug(data.attachedAgentSlug ?? "");
16793
+ setAttachedAgentSlug((data.attachedAgentSlug ?? "").trim());
16794
+ setEmailToolEnabled(data.emailToolEnabled === "true");
16795
+ setEmailIntents(parseEmailIntentsFromSettings(data.emailIntents));
16796
+ const legacyClassifier = data.emailClassifierInstructions ?? [data.emailIntentPrompt, data.emailNegativeIntentPrompt].filter(Boolean).join("\n\n") ?? "";
16797
+ if (legacyClassifier) setNotifyEmailSystem(legacyClassifier);
16798
+ setEmailToolPrompt(data.emailToolPrompt ?? "");
16575
16799
  }
16576
16800
  if (isErp) {
16577
16801
  setErpPipelineName(data.pipelineName ?? data.pipelineId ?? "");
@@ -16657,33 +16881,116 @@ function PluginSettingsPanel({
16657
16881
  }).catch(() => setErpFormsCatalog([]));
16658
16882
  }, [isErp, loading]);
16659
16883
  const fetchLlmAgents = useCallback12(async () => {
16660
- const res = await fetch("/api/llm_agents?limit=100&sortField=name&sortOrder=asc");
16884
+ const res = await fetch(
16885
+ `/api/llm_agents?scope=${LLM_AGENT_SCOPE_CHATBOT}&limit=5&sortField=name&sortOrder=asc`
16886
+ );
16661
16887
  if (!res.ok) {
16662
16888
  setLlmAgents([]);
16889
+ setAgentId(null);
16663
16890
  return;
16664
16891
  }
16665
16892
  const j = await res.json();
16666
- const list = (j.data ?? []).map((r) => ({
16893
+ const chatRows = (j.data ?? []).filter(
16894
+ (r) => String(r.scope ?? "").trim() === LLM_AGENT_SCOPE_CHATBOT || String(r.slug ?? "").trim() === LLM_AGENT_DEFAULT_SLUG_BY_SCOPE[LLM_AGENT_SCOPE_CHATBOT]
16895
+ );
16896
+ const list = chatRows.map((r) => ({
16667
16897
  id: r.id,
16668
16898
  name: String(r.name ?? ""),
16669
16899
  slug: String(r.slug ?? ""),
16900
+ scope: LLM_AGENT_SCOPE_CHATBOT,
16670
16901
  enabled: r.enabled !== false
16671
16902
  }));
16672
16903
  setLlmAgents(list);
16673
16904
  const pick = list.find((a) => a.enabled) ?? list[0];
16674
- const fullRow = (j.data ?? []).find((r) => r.id === pick?.id);
16905
+ const fullRow = pick ? chatRows.find((r) => r.id === pick.id) : void 0;
16675
16906
  if (pick && fullRow) {
16676
16907
  setAgentId(pick.id);
16677
16908
  setAgentName(pick.name);
16678
16909
  setAgentSlug(pick.slug);
16910
+ setAttachedAgentSlug(pick.slug);
16679
16911
  setAgentSystem(String(fullRow.systemInstruction ?? ""));
16680
16912
  setAgentModel(String(fullRow.model ?? ""));
16681
16913
  setAgentTemp(fullRow.temperature != null ? String(fullRow.temperature) : "");
16682
16914
  setAgentMaxTokens(fullRow.maxTokens != null ? String(fullRow.maxTokens) : "");
16683
- setAgentValidationJson(String(fullRow.validationRules ?? ""));
16684
- setAttachedAgentSlug(pick.slug);
16915
+ const { guardrailsJson } = splitValidationRulesForAdmin(fullRow.validationRules ?? null);
16916
+ setAgentValidationJson(guardrailsJson);
16917
+ } else {
16918
+ setAgentId(null);
16919
+ setAgentName(botName.trim() || LLM_AGENT_DEFAULT_NAME_BY_SCOPE[LLM_AGENT_SCOPE_CHATBOT]);
16920
+ setAgentSlug(LLM_AGENT_DEFAULT_SLUG_BY_SCOPE[LLM_AGENT_SCOPE_CHATBOT]);
16921
+ setAgentSystem("");
16922
+ setAgentModel("");
16923
+ setAgentTemp("");
16924
+ setAgentMaxTokens("");
16925
+ setAgentValidationJson("");
16926
+ }
16927
+ }, [botName]);
16928
+ const fetchNotifyEmailAgent = useCallback12(async () => {
16929
+ const res = await fetch(
16930
+ `/api/llm_agents?scope=${LLM_AGENT_SCOPE_EMAIL_INTENT_CHATBOT}&limit=1&sortField=name&sortOrder=asc`
16931
+ );
16932
+ if (!res.ok) {
16933
+ setNotifyEmailAgentId(null);
16934
+ return;
16935
+ }
16936
+ const j = await res.json();
16937
+ const row = j.data?.find((r) => String(r.scope ?? "").trim() === LLM_AGENT_SCOPE_EMAIL_INTENT_CHATBOT) ?? j.data?.find(
16938
+ (r) => String(r.slug ?? "").trim() === LLM_AGENT_DEFAULT_SLUG_BY_SCOPE[LLM_AGENT_SCOPE_EMAIL_INTENT_CHATBOT]
16939
+ ) ?? j.data?.[0];
16940
+ if (!row || String(row.scope ?? "").trim() !== LLM_AGENT_SCOPE_EMAIL_INTENT_CHATBOT) {
16941
+ setNotifyEmailAgentId(null);
16942
+ setNotifyEmailName(LLM_AGENT_DEFAULT_NAME_BY_SCOPE[LLM_AGENT_SCOPE_EMAIL_INTENT_CHATBOT]);
16943
+ setNotifyEmailSystem("");
16944
+ return;
16945
+ }
16946
+ setNotifyEmailAgentId(row.id);
16947
+ setNotifyEmailName(String(row.name ?? ""));
16948
+ setNotifyEmailSystem(String(row.systemInstruction ?? ""));
16949
+ const { emailTool } = splitValidationRulesForAdmin(row.validationRules ?? null);
16950
+ if (emailTool?.intents?.length) setEmailIntents(emailTool.intents);
16951
+ if (emailTool?.toolPrompt) setEmailToolPrompt(emailTool.toolPrompt);
16952
+ if (!row.systemInstruction?.trim() && emailTool?.classifierInstructions) {
16953
+ setNotifyEmailSystem(emailTool.classifierInstructions);
16685
16954
  }
16686
16955
  }, []);
16956
+ const bootstrapNotifyEmailAgent = useCallback12(async () => {
16957
+ setNotifyEmailProvisionError(null);
16958
+ const listRes = await fetch(
16959
+ `/api/llm_agents?scope=${LLM_AGENT_SCOPE_EMAIL_INTENT_CHATBOT}&limit=1`
16960
+ );
16961
+ if (!listRes.ok) {
16962
+ const message = `Could not load notify email agent (HTTP ${listRes.status}).`;
16963
+ setNotifyEmailProvisionError(message);
16964
+ await fetchNotifyEmailAgent();
16965
+ return { ok: false, message };
16966
+ }
16967
+ const listJ = await listRes.json();
16968
+ const notifyRow = listJ.data?.find((a) => a.scope === LLM_AGENT_SCOPE_EMAIL_INTENT_CHATBOT);
16969
+ if (notifyRow) {
16970
+ await fetchNotifyEmailAgent();
16971
+ return { ok: true };
16972
+ }
16973
+ const createRes = await fetch("/api/llm_agents", {
16974
+ method: "POST",
16975
+ headers: { "Content-Type": "application/json" },
16976
+ body: JSON.stringify({
16977
+ name: LLM_AGENT_DEFAULT_NAME_BY_SCOPE[LLM_AGENT_SCOPE_EMAIL_INTENT_CHATBOT],
16978
+ slug: LLM_AGENT_DEFAULT_SLUG_BY_SCOPE[LLM_AGENT_SCOPE_EMAIL_INTENT_CHATBOT],
16979
+ scope: LLM_AGENT_SCOPE_EMAIL_INTENT_CHATBOT,
16980
+ systemInstruction: "",
16981
+ enabled: true
16982
+ })
16983
+ });
16984
+ if (!createRes.ok) {
16985
+ const errBody = await createRes.json().catch(() => ({}));
16986
+ const message = errBody.error ?? `Could not create notify email agent (HTTP ${createRes.status}).`;
16987
+ setNotifyEmailProvisionError(message);
16988
+ await fetchNotifyEmailAgent();
16989
+ return { ok: false, message };
16990
+ }
16991
+ await fetchNotifyEmailAgent();
16992
+ return { ok: true };
16993
+ }, [fetchNotifyEmailAgent]);
16687
16994
  const fetchKbCatalog = useCallback12(async () => {
16688
16995
  try {
16689
16996
  const res = await fetch("/api/knowledge_base_documents?limit=300&sortField=name&sortOrder=asc");
@@ -16729,7 +17036,9 @@ function PluginSettingsPanel({
16729
17036
  }, []);
16730
17037
  const bootstrapLlmAgentForPlugins = useCallback12(async () => {
16731
17038
  setAgentProvisionError(null);
16732
- const listRes = await fetch("/api/llm_agents?limit=100&sortField=name&sortOrder=asc");
17039
+ const listRes = await fetch(
17040
+ `/api/llm_agents?scope=${LLM_AGENT_SCOPE_CHATBOT}&limit=5&sortField=name&sortOrder=asc`
17041
+ );
16733
17042
  if (!listRes.ok) {
16734
17043
  const errBody = await listRes.json().catch(() => ({}));
16735
17044
  const message = errBody.error ?? `Could not load agents (HTTP ${listRes.status}). Ensure your app uses the latest @infuro/cms-core and your admin user can read entity "llm_agents".`;
@@ -16738,16 +17047,25 @@ function PluginSettingsPanel({
16738
17047
  return { ok: false, message };
16739
17048
  }
16740
17049
  const listJ = await listRes.json();
16741
- if (listJ.data?.length) {
17050
+ const chatRow = listJ.data?.find(
17051
+ (a) => String(a.scope ?? "").trim() === LLM_AGENT_SCOPE_CHATBOT || String(a.slug ?? "").trim() === LLM_AGENT_DEFAULT_SLUG_BY_SCOPE[LLM_AGENT_SCOPE_CHATBOT]
17052
+ );
17053
+ if (chatRow) {
16742
17054
  await fetchLlmAgents();
16743
17055
  return { ok: true };
16744
17056
  }
16745
- const defaultName = botName.trim() || "Assistant";
16746
- const defaultSlug = slugifyAgentKey(defaultName);
17057
+ const defaultName = botName.trim() || LLM_AGENT_DEFAULT_NAME_BY_SCOPE[LLM_AGENT_SCOPE_CHATBOT];
17058
+ const defaultSlug = LLM_AGENT_DEFAULT_SLUG_BY_SCOPE[LLM_AGENT_SCOPE_CHATBOT];
16747
17059
  const createRes = await fetch("/api/llm_agents", {
16748
17060
  method: "POST",
16749
17061
  headers: { "Content-Type": "application/json" },
16750
- body: JSON.stringify({ name: defaultName, slug: defaultSlug, systemInstruction: "", enabled: true })
17062
+ body: JSON.stringify({
17063
+ name: defaultName,
17064
+ slug: defaultSlug,
17065
+ scope: LLM_AGENT_SCOPE_CHATBOT,
17066
+ systemInstruction: "",
17067
+ enabled: true
17068
+ })
16751
17069
  });
16752
17070
  if (!createRes.ok) {
16753
17071
  const errBody = await createRes.json().catch(() => ({}));
@@ -16773,11 +17091,7 @@ function PluginSettingsPanel({
16773
17091
  }
16774
17092
  };
16775
17093
  const handleCreateAssistantFromForm = async () => {
16776
- const name = agentName.trim() || botName.trim();
16777
- if (!name) {
16778
- toast10.error("Enter an assistant name");
16779
- return;
16780
- }
17094
+ const name = agentName.trim() || botName.trim() || LLM_AGENT_DEFAULT_NAME_BY_SCOPE[LLM_AGENT_SCOPE_CHATBOT];
16781
17095
  const tempRaw = agentTemp.trim();
16782
17096
  const maxRaw = agentMaxTokens.trim();
16783
17097
  const temperature = tempRaw === "" ? null : Number(tempRaw);
@@ -16790,7 +17104,7 @@ function PluginSettingsPanel({
16790
17104
  toast10.error("Max tokens must be a positive integer");
16791
17105
  return;
16792
17106
  }
16793
- const slug = slugifyAgentKey(name);
17107
+ const slug = LLM_AGENT_DEFAULT_SLUG_BY_SCOPE[LLM_AGENT_SCOPE_CHATBOT];
16794
17108
  setAgentSaving(true);
16795
17109
  try {
16796
17110
  const res = await fetch("/api/llm_agents", {
@@ -16799,6 +17113,7 @@ function PluginSettingsPanel({
16799
17113
  body: JSON.stringify({
16800
17114
  name,
16801
17115
  slug,
17116
+ scope: LLM_AGENT_SCOPE_CHATBOT,
16802
17117
  systemInstruction: agentSystem.trim(),
16803
17118
  model: agentModel.trim() || null,
16804
17119
  temperature,
@@ -16825,18 +17140,19 @@ function PluginSettingsPanel({
16825
17140
  useEffect34(() => {
16826
17141
  if (!isLlm || loading || chatMode !== "llm") return;
16827
17142
  setAgentLoading(true);
17143
+ setNotifyEmailLoading(true);
16828
17144
  void (async () => {
16829
17145
  try {
16830
17146
  await bootstrapLlmAgentForPlugins();
17147
+ await bootstrapNotifyEmailAgent();
17148
+ await fetchNotifyEmailAgent();
17149
+ await fetchKbCatalog();
16831
17150
  } finally {
16832
17151
  setAgentLoading(false);
17152
+ setNotifyEmailLoading(false);
16833
17153
  }
16834
17154
  })();
16835
- }, [isLlm, loading, chatMode, bootstrapLlmAgentForPlugins]);
16836
- useEffect34(() => {
16837
- if (!isLlm || loading || chatMode !== "llm") return;
16838
- void fetchKbCatalog();
16839
- }, [isLlm, loading, chatMode, fetchKbCatalog]);
17155
+ }, [isLlm, loading, chatMode, bootstrapLlmAgentForPlugins, bootstrapNotifyEmailAgent, fetchNotifyEmailAgent, fetchKbCatalog]);
16840
17156
  useEffect34(() => {
16841
17157
  if (!isLlm || loading || chatMode !== "llm" || !attachedAgentSlug.trim()) {
16842
17158
  setAttachedAgentKnowledge([]);
@@ -16927,7 +17243,13 @@ function PluginSettingsPanel({
16927
17243
  payload.iconImageUrl = { value: iconImageUrl, type: "public" };
16928
17244
  payload.iconBackgroundColor = { value: iconBackgroundColor, type: "public" };
16929
17245
  payload.headerColor = { value: headerColor, type: "public" };
16930
- payload.attachedAgentSlug = { value: attachedAgentSlug.trim(), type: "public" };
17246
+ payload.attachedAgentSlug = {
17247
+ value: agentSlug.trim() || attachedAgentSlug.trim() || LLM_AGENT_DEFAULT_SLUG_BY_SCOPE[LLM_AGENT_SCOPE_CHATBOT],
17248
+ type: "public"
17249
+ };
17250
+ payload.emailToolEnabled = { value: emailToolEnabled ? "true" : "false", type: "public" };
17251
+ payload.emailIntents = { value: serializeChatLeadIntents(emailIntents), type: "public" };
17252
+ payload.emailToolPrompt = { value: emailToolPrompt, type: "public" };
16931
17253
  }
16932
17254
  if (isEmail) {
16933
17255
  payload.salesTeamEmails = { value: serializeEmailRecipients(salesTeamEmails), type: "public" };
@@ -16943,65 +17265,169 @@ function PluginSettingsPanel({
16943
17265
  }
16944
17266
  return payload;
16945
17267
  };
16946
- const handleSaveAgent = async () => {
16947
- if (!agentId) {
16948
- toast10.error("No agent to save");
16949
- return;
16950
- }
16951
- const name = agentName.trim();
16952
- if (!name) {
16953
- toast10.error("Agent name is required");
16954
- return;
16955
- }
17268
+ const savePluginSettings = async () => {
17269
+ const res = await fetch(`/api/settings/${settingsGroup}`, {
17270
+ method: "PUT",
17271
+ headers: { "Content-Type": "application/json" },
17272
+ body: JSON.stringify(buildPayload())
17273
+ });
17274
+ return res.ok;
17275
+ };
17276
+ const validateChatAgentNumbers = () => {
16956
17277
  const tempRaw = agentTemp.trim();
16957
17278
  const maxRaw = agentMaxTokens.trim();
16958
17279
  const temperature = tempRaw === "" ? null : Number(tempRaw);
16959
17280
  const maxTokens = maxRaw === "" ? null : parseInt(maxRaw, 10);
16960
17281
  if (tempRaw !== "" && !Number.isFinite(temperature)) {
16961
17282
  toast10.error("Temperature must be a number");
16962
- return;
17283
+ return null;
16963
17284
  }
16964
17285
  if (maxRaw !== "" && (!Number.isFinite(maxTokens) || maxTokens < 1)) {
16965
17286
  toast10.error("Max tokens must be a positive integer");
17287
+ return null;
17288
+ }
17289
+ return { temperature, maxTokens };
17290
+ };
17291
+ const persistChatAgent = async () => {
17292
+ if (!agentId) return false;
17293
+ const nums = validateChatAgentNumbers();
17294
+ if (!nums) return false;
17295
+ const name = agentName.trim() || botName.trim() || LLM_AGENT_DEFAULT_NAME_BY_SCOPE[LLM_AGENT_SCOPE_CHATBOT];
17296
+ const res = await fetch(`/api/llm_agents/${agentId}`, {
17297
+ method: "PUT",
17298
+ headers: { "Content-Type": "application/json" },
17299
+ body: JSON.stringify({
17300
+ name,
17301
+ systemInstruction: agentSystem.trim(),
17302
+ model: agentModel.trim() || null,
17303
+ temperature: nums.temperature,
17304
+ maxTokens: nums.maxTokens,
17305
+ validationRules: agentValidationJson.trim() || null,
17306
+ enabled: true
17307
+ })
17308
+ });
17309
+ if (!res.ok) return false;
17310
+ setAttachedAgentSlug(agentSlug);
17311
+ return true;
17312
+ };
17313
+ const persistNotifyEmailAgent = async (agentIdOverride) => {
17314
+ const id = agentIdOverride ?? notifyEmailAgentId;
17315
+ if (!id) return false;
17316
+ const validationRules = mergeEmailToolIntoValidationRules(null, {
17317
+ classifierInstructions: "",
17318
+ toolPrompt: emailToolPrompt,
17319
+ intents: emailIntents
17320
+ });
17321
+ const res = await fetch(`/api/llm_agents/${id}`, {
17322
+ method: "PUT",
17323
+ headers: { "Content-Type": "application/json" },
17324
+ body: JSON.stringify({
17325
+ name: notifyEmailName.trim() || LLM_AGENT_DEFAULT_NAME_BY_SCOPE[LLM_AGENT_SCOPE_EMAIL_INTENT_CHATBOT],
17326
+ systemInstruction: notifyEmailSystem.trim(),
17327
+ validationRules,
17328
+ enabled: true
17329
+ })
17330
+ });
17331
+ return res.ok;
17332
+ };
17333
+ const saveLlmPluginSettingsOnly = async () => {
17334
+ const res = await fetch(`/api/settings/${settingsGroup}`, {
17335
+ method: "PUT",
17336
+ headers: { "Content-Type": "application/json" },
17337
+ body: JSON.stringify(buildPayload())
17338
+ });
17339
+ return res.ok;
17340
+ };
17341
+ const handleSaveLlmChatTab = async () => {
17342
+ if (!agentId) {
17343
+ toast10.error("Wait for the assistant to load, or click Create assistant");
16966
17344
  return;
16967
17345
  }
16968
- setAgentSaving(true);
17346
+ if (!validateChatAgentNumbers()) return;
17347
+ setSaving(true);
16969
17348
  try {
16970
- const res = await fetch(`/api/llm_agents/${agentId}`, {
16971
- method: "PUT",
16972
- headers: { "Content-Type": "application/json" },
16973
- body: JSON.stringify({
16974
- name,
16975
- systemInstruction: agentSystem.trim(),
16976
- model: agentModel.trim() || null,
16977
- temperature,
16978
- maxTokens,
16979
- validationRules: agentValidationJson.trim() || null,
16980
- enabled: true
16981
- })
16982
- });
16983
- if (!res.ok) {
16984
- const err = await res.json().catch(() => ({}));
16985
- toast10.error(err.error || "Failed to update agent");
17349
+ const chatOk = await persistChatAgent();
17350
+ if (!chatOk) {
17351
+ toast10.error("Failed to save assistant");
17352
+ return;
17353
+ }
17354
+ const settingsOk = await saveLlmPluginSettingsOnly();
17355
+ if (!settingsOk) {
17356
+ toast10.error("Failed to save widget settings");
16986
17357
  return;
16987
17358
  }
16988
- setAttachedAgentSlug(agentSlug);
16989
- toast10.success("Agent saved");
17359
+ toast10.success("Chat settings saved");
16990
17360
  } catch {
16991
- toast10.error("Failed to update agent");
17361
+ toast10.error("Failed to save");
16992
17362
  } finally {
16993
- setAgentSaving(false);
17363
+ setSaving(false);
17364
+ }
17365
+ };
17366
+ const handleSaveLlmNotifyTab = async () => {
17367
+ if (!emailPluginAvailable) {
17368
+ toast10.error("Enable the Email plugin (SMTP) first");
17369
+ return;
17370
+ }
17371
+ const filledIntents = emailIntents.filter(
17372
+ (r) => r.intent.trim() && r.description.trim() && r.emailTo.trim()
17373
+ );
17374
+ if (filledIntents.length === 0) {
17375
+ toast10.error("Add at least one intent with Intent, Description, and Email To");
17376
+ return;
17377
+ }
17378
+ setEmailIntents(filledIntents);
17379
+ setNotifyEmailSaving(true);
17380
+ try {
17381
+ let notifyId = notifyEmailAgentId;
17382
+ if (!notifyId) {
17383
+ await bootstrapNotifyEmailAgent();
17384
+ const nr = await fetch(
17385
+ `/api/llm_agents?scope=${LLM_AGENT_SCOPE_EMAIL_INTENT_CHATBOT}&limit=1`
17386
+ );
17387
+ if (nr.ok) {
17388
+ const nj = await nr.json();
17389
+ const row = nj.data?.find(
17390
+ (r) => String(r.scope ?? "").trim() === LLM_AGENT_SCOPE_EMAIL_INTENT_CHATBOT
17391
+ );
17392
+ if (row) {
17393
+ notifyId = row.id;
17394
+ setNotifyEmailAgentId(row.id);
17395
+ }
17396
+ }
17397
+ }
17398
+ if (!notifyId) {
17399
+ toast10.error("Could not set up lead email \u2014 try again");
17400
+ return;
17401
+ }
17402
+ setEmailToolEnabled(true);
17403
+ const notifyOk = await persistNotifyEmailAgent(notifyId);
17404
+ if (!notifyOk) {
17405
+ toast10.error("Failed to save lead email settings");
17406
+ return;
17407
+ }
17408
+ const settingsOk = await saveLlmPluginSettingsOnly();
17409
+ if (!settingsOk) {
17410
+ toast10.warning("Routing saved, but enable flag failed to save in settings");
17411
+ return;
17412
+ }
17413
+ toast10.success("Lead email routing saved");
17414
+ await fetchNotifyEmailAgent();
17415
+ } catch {
17416
+ toast10.error("Failed to save");
17417
+ } finally {
17418
+ setNotifyEmailSaving(false);
16994
17419
  }
16995
17420
  };
16996
17421
  const handleSave = async () => {
17422
+ if (isLlm && chatMode === "llm") {
17423
+ if (llmConfigTab === "notify") await handleSaveLlmNotifyTab();
17424
+ else await handleSaveLlmChatTab();
17425
+ return;
17426
+ }
16997
17427
  setSaving(true);
16998
17428
  try {
16999
- const res = await fetch(`/api/settings/${settingsGroup}`, {
17000
- method: "PUT",
17001
- headers: { "Content-Type": "application/json" },
17002
- body: JSON.stringify(buildPayload())
17003
- });
17004
- if (!res.ok) throw new Error();
17429
+ const ok = await savePluginSettings();
17430
+ if (!ok) throw new Error();
17005
17431
  if (isSocialMedia && (linkedinAccessToken.trim() || linkedinHasSavedToken)) {
17006
17432
  if (linkedinAccessToken.trim()) {
17007
17433
  setLinkedinHasSavedToken(true);
@@ -17923,328 +18349,458 @@ function PluginSettingsPanel({
17923
18349
  /* @__PURE__ */ jsx66("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: "Only paste code from sources you trust." })
17924
18350
  ] }),
17925
18351
  chatMode === "llm" && /* @__PURE__ */ jsxs55(Fragment16, { children: [
17926
- /* @__PURE__ */ jsxs55("div", { className: "rounded-lg border border-gray-200 dark:border-gray-600 bg-gray-50/80 dark:bg-gray-800/40 p-3 space-y-3", children: [
17927
- /* @__PURE__ */ jsxs55("div", { className: "space-y-1", children: [
17928
- /* @__PURE__ */ jsxs55("div", { className: "flex items-center gap-2 text-sm font-medium text-gray-900 dark:text-white", children: [
17929
- /* @__PURE__ */ jsx66(Bot, { className: "h-4 w-4" }),
17930
- "Chat assistant (single agent)"
17931
- ] }),
17932
- /* @__PURE__ */ jsx66("p", { className: "text-[11px] text-gray-500 dark:text-gray-400", children: "Configure one assistant for this site here. After it exists, attach knowledge files in the section below." })
17933
- ] }),
17934
- agentLoading ? /* @__PURE__ */ jsxs55("div", { className: "flex items-center gap-2 text-sm text-gray-500", children: [
17935
- /* @__PURE__ */ jsx66(Loader25, { className: "h-4 w-4 animate-spin" }),
17936
- "Loading assistant\u2026"
17937
- ] }) : /* @__PURE__ */ jsxs55(Fragment16, { children: [
17938
- agentProvisionError ? /* @__PURE__ */ jsx66("p", { className: "rounded border border-amber-200 bg-amber-50 p-2 text-xs text-amber-900 dark:border-amber-800 dark:bg-amber-950/40 dark:text-amber-100", children: agentProvisionError }) : null,
18352
+ /* @__PURE__ */ jsxs55("div", { className: "inline-flex rounded-lg border border-gray-200 bg-gray-50 p-0.5 dark:border-gray-600 dark:bg-gray-800/80", children: [
18353
+ /* @__PURE__ */ jsxs55(
18354
+ "button",
18355
+ {
18356
+ type: "button",
18357
+ className: `flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors ${llmConfigTab === "chat" ? "bg-white text-gray-900 shadow-sm dark:bg-gray-900 dark:text-white" : "text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white"}`,
18358
+ onClick: () => setLlmConfigTab("chat"),
18359
+ children: [
18360
+ /* @__PURE__ */ jsx66(MessageCircle, { className: "h-3.5 w-3.5 shrink-0" }),
18361
+ "Chat"
18362
+ ]
18363
+ }
18364
+ ),
18365
+ /* @__PURE__ */ jsxs55(
18366
+ "button",
18367
+ {
18368
+ type: "button",
18369
+ className: `flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors ${llmConfigTab === "notify" ? "bg-white text-gray-900 shadow-sm dark:bg-gray-900 dark:text-white" : "text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white"}`,
18370
+ onClick: () => setLlmConfigTab("notify"),
18371
+ children: [
18372
+ /* @__PURE__ */ jsx66(Mail2, { className: "h-3.5 w-3.5 shrink-0" }),
18373
+ "Notify"
18374
+ ]
18375
+ }
18376
+ )
18377
+ ] }),
18378
+ llmConfigTab === "chat" ? /* @__PURE__ */ jsxs55(Fragment16, { children: [
18379
+ /* @__PURE__ */ jsxs55("div", { className: "rounded-lg border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-900/40 p-3 space-y-3", children: [
18380
+ /* @__PURE__ */ jsx66("p", { className: "text-sm font-medium text-gray-900 dark:text-white", children: "Widget on your site" }),
18381
+ /* @__PURE__ */ jsx66("p", { className: "text-[11px] text-gray-500 dark:text-gray-400", children: "Title, icon, and colors visitors see on the chat bubble." }),
17939
18382
  /* @__PURE__ */ jsxs55("div", { className: "space-y-1", children: [
17940
- /* @__PURE__ */ jsx66(Label3, { htmlFor: "agent-name", className: "text-sm", children: "Assistant name" }),
18383
+ /* @__PURE__ */ jsx66(Label3, { htmlFor: `${settingsGroup}-botName`, className: "text-sm", children: "Widget title" }),
17941
18384
  /* @__PURE__ */ jsx66(
17942
18385
  Input,
17943
18386
  {
17944
- id: "agent-name",
17945
- value: agentName,
17946
- onChange: (e) => setAgentName(e.target.value),
18387
+ id: `${settingsGroup}-botName`,
18388
+ value: botName,
18389
+ onChange: (e) => setBotName(e.target.value),
17947
18390
  placeholder: "e.g. JM Buddy",
17948
18391
  className: "h-8 text-sm"
17949
18392
  }
17950
18393
  )
17951
18394
  ] }),
17952
18395
  /* @__PURE__ */ jsxs55("div", { className: "space-y-1", children: [
17953
- /* @__PURE__ */ jsx66(Label3, { htmlFor: "agent-slug", className: "text-sm", children: "Slug" }),
18396
+ /* @__PURE__ */ jsx66(Label3, { htmlFor: `${settingsGroup}-iconImageUrl`, className: "text-sm", children: "Icon image URL" }),
17954
18397
  /* @__PURE__ */ jsx66(
17955
18398
  Input,
17956
18399
  {
17957
- id: "agent-slug",
17958
- value: agentId ? agentSlug : slugifyAgentKey((agentName || botName).trim() || "assistant"),
17959
- disabled: true,
17960
- className: "h-8 text-sm font-mono bg-gray-100 dark:bg-gray-700"
18400
+ id: `${settingsGroup}-iconImageUrl`,
18401
+ value: iconImageUrl,
18402
+ onChange: (e) => setIconImageUrl(e.target.value),
18403
+ placeholder: "https://\u2026 or /images/chat-icon.png",
18404
+ className: "h-8 text-sm"
17961
18405
  }
17962
18406
  ),
17963
- /* @__PURE__ */ jsx66("p", { className: "text-[11px] text-gray-500", children: "Derived from the name. Used in API routes." })
18407
+ /* @__PURE__ */ jsx66("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: "PNG or image URL. Leave empty to use emoji below." })
17964
18408
  ] }),
17965
18409
  /* @__PURE__ */ jsxs55("div", { className: "space-y-1", children: [
17966
- /* @__PURE__ */ jsx66(Label3, { htmlFor: "agent-system", className: "text-sm", children: "System instruction" }),
18410
+ /* @__PURE__ */ jsx66(Label3, { htmlFor: `${settingsGroup}-icon`, className: "text-sm", children: "Icon fallback (emoji)" }),
17967
18411
  /* @__PURE__ */ jsx66(
17968
- Textarea,
18412
+ Input,
17969
18413
  {
17970
- id: "agent-system",
17971
- value: agentSystem,
17972
- onChange: (e) => setAgentSystem(e.target.value),
17973
- rows: 5,
17974
- placeholder: "How the model should behave\u2026",
17975
- className: "text-sm"
18414
+ id: `${settingsGroup}-icon`,
18415
+ value: icon,
18416
+ onChange: (e) => setIcon(e.target.value),
18417
+ placeholder: "e.g. \u{1F4AC}",
18418
+ className: "h-8 text-sm w-20"
17976
18419
  }
17977
18420
  )
17978
18421
  ] }),
17979
- /* @__PURE__ */ jsxs55("div", { className: "grid grid-cols-2 gap-2", children: [
18422
+ /* @__PURE__ */ jsxs55("div", { className: "flex gap-4", children: [
18423
+ /* @__PURE__ */ jsxs55("div", { className: "space-y-1", children: [
18424
+ /* @__PURE__ */ jsx66(Label3, { htmlFor: `${settingsGroup}-iconBg`, className: "text-sm", children: "Icon background" }),
18425
+ /* @__PURE__ */ jsxs55("div", { className: "flex items-center gap-2", children: [
18426
+ /* @__PURE__ */ jsx66(
18427
+ "input",
18428
+ {
18429
+ type: "color",
18430
+ id: `${settingsGroup}-iconBg`,
18431
+ value: iconBackgroundColor,
18432
+ onChange: (e) => setIconBackgroundColor(e.target.value),
18433
+ className: "h-8 w-10 cursor-pointer rounded border border-gray-300 dark:border-gray-600"
18434
+ }
18435
+ ),
18436
+ /* @__PURE__ */ jsx66(
18437
+ Input,
18438
+ {
18439
+ value: iconBackgroundColor,
18440
+ onChange: (e) => setIconBackgroundColor(e.target.value),
18441
+ className: "h-8 w-24 text-sm font-mono"
18442
+ }
18443
+ )
18444
+ ] })
18445
+ ] }),
18446
+ /* @__PURE__ */ jsxs55("div", { className: "space-y-1", children: [
18447
+ /* @__PURE__ */ jsx66(Label3, { htmlFor: `${settingsGroup}-headerColor`, className: "text-sm", children: "Header color" }),
18448
+ /* @__PURE__ */ jsxs55("div", { className: "flex items-center gap-2", children: [
18449
+ /* @__PURE__ */ jsx66(
18450
+ "input",
18451
+ {
18452
+ type: "color",
18453
+ id: `${settingsGroup}-headerColor`,
18454
+ value: headerColor,
18455
+ onChange: (e) => setHeaderColor(e.target.value),
18456
+ className: "h-8 w-10 cursor-pointer rounded border border-gray-300 dark:border-gray-600"
18457
+ }
18458
+ ),
18459
+ /* @__PURE__ */ jsx66(
18460
+ Input,
18461
+ {
18462
+ value: headerColor,
18463
+ onChange: (e) => setHeaderColor(e.target.value),
18464
+ className: "h-8 w-24 text-sm font-mono"
18465
+ }
18466
+ )
18467
+ ] })
18468
+ ] })
18469
+ ] })
18470
+ ] }),
18471
+ /* @__PURE__ */ jsxs55("div", { className: "rounded-lg border border-gray-200 dark:border-gray-600 bg-gray-50/80 dark:bg-gray-800/40 p-3 space-y-4", children: [
18472
+ /* @__PURE__ */ jsxs55("div", { className: "space-y-1", children: [
18473
+ /* @__PURE__ */ jsxs55("div", { className: "flex items-center gap-2 text-sm font-medium text-gray-900 dark:text-white", children: [
18474
+ /* @__PURE__ */ jsx66(Bot, { className: "h-4 w-4" }),
18475
+ "Assistant"
18476
+ ] }),
18477
+ /* @__PURE__ */ jsx66("p", { className: "text-[11px] text-gray-500 dark:text-gray-400", children: "Storefront assistant \u2014 model, instructions, and guardrails." })
18478
+ ] }),
18479
+ agentLoading ? /* @__PURE__ */ jsxs55("div", { className: "flex items-center gap-2 text-sm text-gray-500", children: [
18480
+ /* @__PURE__ */ jsx66(Loader25, { className: "h-4 w-4 animate-spin" }),
18481
+ "Loading\u2026"
18482
+ ] }) : /* @__PURE__ */ jsxs55(Fragment16, { children: [
18483
+ agentProvisionError ? /* @__PURE__ */ jsx66("p", { className: "rounded border border-amber-200 bg-amber-50 p-2 text-xs text-amber-900 dark:border-amber-800 dark:bg-amber-950/40 dark:text-amber-100", children: agentProvisionError }) : null,
17980
18484
  /* @__PURE__ */ jsxs55("div", { className: "space-y-1", children: [
17981
- /* @__PURE__ */ jsx66(Label3, { htmlFor: "agent-model", className: "text-sm", children: "Model (optional)" }),
18485
+ /* @__PURE__ */ jsx66(Label3, { htmlFor: "agent-system", className: "text-sm", children: "System instruction" }),
17982
18486
  /* @__PURE__ */ jsx66(
17983
- Input,
18487
+ Textarea,
17984
18488
  {
17985
- id: "agent-model",
17986
- value: agentModel,
17987
- onChange: (e) => setAgentModel(e.target.value),
17988
- placeholder: "Gateway model id",
17989
- className: "h-8 text-sm font-mono"
18489
+ id: "agent-system",
18490
+ value: agentSystem,
18491
+ onChange: (e) => setAgentSystem(e.target.value),
18492
+ rows: 5,
18493
+ placeholder: "How the model should behave\u2026",
18494
+ className: "text-sm"
17990
18495
  }
17991
18496
  )
17992
18497
  ] }),
18498
+ /* @__PURE__ */ jsxs55("div", { className: "grid grid-cols-2 gap-2", children: [
18499
+ /* @__PURE__ */ jsxs55("div", { className: "space-y-1", children: [
18500
+ /* @__PURE__ */ jsx66(Label3, { htmlFor: "agent-model", className: "text-sm", children: "Model (optional)" }),
18501
+ /* @__PURE__ */ jsx66(
18502
+ Input,
18503
+ {
18504
+ id: "agent-model",
18505
+ value: agentModel,
18506
+ onChange: (e) => setAgentModel(e.target.value),
18507
+ placeholder: "Gateway model id",
18508
+ className: "h-8 text-sm font-mono"
18509
+ }
18510
+ )
18511
+ ] }),
18512
+ /* @__PURE__ */ jsxs55("div", { className: "space-y-1", children: [
18513
+ /* @__PURE__ */ jsx66(Label3, { htmlFor: "agent-temp", className: "text-sm", children: "Temperature" }),
18514
+ /* @__PURE__ */ jsx66(
18515
+ Input,
18516
+ {
18517
+ id: "agent-temp",
18518
+ value: agentTemp,
18519
+ onChange: (e) => setAgentTemp(e.target.value),
18520
+ placeholder: "e.g. 0.7",
18521
+ className: "h-8 text-sm"
18522
+ }
18523
+ )
18524
+ ] })
18525
+ ] }),
17993
18526
  /* @__PURE__ */ jsxs55("div", { className: "space-y-1", children: [
17994
- /* @__PURE__ */ jsx66(Label3, { htmlFor: "agent-temp", className: "text-sm", children: "Temperature" }),
18527
+ /* @__PURE__ */ jsx66(Label3, { htmlFor: "agent-max", className: "text-sm", children: "Max tokens" }),
17995
18528
  /* @__PURE__ */ jsx66(
17996
18529
  Input,
17997
18530
  {
17998
- id: "agent-temp",
17999
- value: agentTemp,
18000
- onChange: (e) => setAgentTemp(e.target.value),
18001
- placeholder: "e.g. 0.7",
18531
+ id: "agent-max",
18532
+ value: agentMaxTokens,
18533
+ onChange: (e) => setAgentMaxTokens(e.target.value.replace(/\D/g, "")),
18534
+ placeholder: "e.g. 1024",
18002
18535
  className: "h-8 text-sm"
18003
18536
  }
18004
18537
  )
18005
- ] })
18006
- ] }),
18007
- /* @__PURE__ */ jsxs55("div", { className: "space-y-1", children: [
18008
- /* @__PURE__ */ jsx66(Label3, { htmlFor: "agent-max", className: "text-sm", children: "Max tokens" }),
18009
- /* @__PURE__ */ jsx66(
18010
- Input,
18011
- {
18012
- id: "agent-max",
18013
- value: agentMaxTokens,
18014
- onChange: (e) => setAgentMaxTokens(e.target.value.replace(/\D/g, "")),
18015
- placeholder: "e.g. 1024",
18016
- className: "h-8 text-sm"
18017
- }
18018
- )
18538
+ ] }),
18539
+ /* @__PURE__ */ jsxs55("div", { className: "space-y-1", children: [
18540
+ /* @__PURE__ */ jsx66(Label3, { htmlFor: "agent-validation", className: "text-sm", children: "Validation & output guardrails" }),
18541
+ /* @__PURE__ */ jsx66(
18542
+ Textarea,
18543
+ {
18544
+ id: "agent-validation",
18545
+ value: agentValidationJson,
18546
+ onChange: (e) => setAgentValidationJson(e.target.value),
18547
+ rows: 4,
18548
+ placeholder: 'Plain text or JSON: {"guardrails":"Never promise refunds.","maxUserChars":2000}',
18549
+ className: "text-xs font-mono"
18550
+ }
18551
+ )
18552
+ ] }),
18553
+ !agentId ? /* @__PURE__ */ jsxs55("div", { className: "flex flex-wrap items-center gap-2", children: [
18554
+ /* @__PURE__ */ jsx66(
18555
+ Button,
18556
+ {
18557
+ type: "button",
18558
+ size: "sm",
18559
+ className: "gap-1",
18560
+ disabled: agentSaving,
18561
+ onClick: () => void handleCreateAssistantFromForm(),
18562
+ children: agentSaving ? /* @__PURE__ */ jsxs55("span", { className: "inline-flex items-center gap-1.5", children: [
18563
+ /* @__PURE__ */ jsx66(Loader25, { className: "h-3.5 w-3.5 animate-spin" }),
18564
+ "Creating\u2026"
18565
+ ] }) : /* @__PURE__ */ jsxs55(Fragment16, { children: [
18566
+ /* @__PURE__ */ jsx66(Bot, { className: "h-3.5 w-3.5" }),
18567
+ "Create assistant"
18568
+ ] })
18569
+ }
18570
+ ),
18571
+ /* @__PURE__ */ jsx66(
18572
+ Button,
18573
+ {
18574
+ type: "button",
18575
+ size: "sm",
18576
+ variant: "secondary",
18577
+ className: "gap-1",
18578
+ disabled: agentSaving || agentLoading,
18579
+ onClick: () => void handleRetryBootstrapAgent(),
18580
+ children: "Retry auto-setup"
18581
+ }
18582
+ )
18583
+ ] }) : null
18019
18584
  ] }),
18585
+ agentId && agentSlug.trim() ? /* @__PURE__ */ jsxs55("div", { className: "rounded-md border border-dashed border-gray-300 dark:border-gray-600 bg-white/60 dark:bg-gray-900/30 p-3 space-y-3", children: [
18586
+ /* @__PURE__ */ jsxs55("div", { className: "flex items-center gap-2 text-xs font-medium text-gray-800 dark:text-gray-200", children: [
18587
+ /* @__PURE__ */ jsx66(FileUp, { className: "h-3.5 w-3.5 shrink-0" }),
18588
+ "Knowledge for this agent"
18589
+ ] }),
18590
+ /* @__PURE__ */ jsx66("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: "Upload text (.txt, .md, .json) or PDF (.pdf), or link documents already in the knowledge base." }),
18591
+ attachedKbLoading ? /* @__PURE__ */ jsx66("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: "Loading linked documents\u2026" }) : attachedAgentKnowledge.length === 0 ? /* @__PURE__ */ jsx66("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: "No documents linked yet." }) : /* @__PURE__ */ jsx66("ul", { className: "max-h-32 space-y-1.5 overflow-y-auto", children: attachedAgentKnowledge.map((d) => /* @__PURE__ */ jsxs55("li", { className: "flex items-center justify-between gap-2 text-sm", children: [
18592
+ /* @__PURE__ */ jsx66("span", { className: "min-w-0 truncate", title: d.name, children: d.name }),
18593
+ /* @__PURE__ */ jsx66(
18594
+ Button,
18595
+ {
18596
+ type: "button",
18597
+ variant: "ghost",
18598
+ size: "sm",
18599
+ className: "h-7 shrink-0 text-xs text-red-600 hover:text-red-700 dark:text-red-400",
18600
+ onClick: () => void handleUnlinkKbDoc(d.id),
18601
+ children: "Remove"
18602
+ }
18603
+ )
18604
+ ] }, d.id)) }),
18605
+ /* @__PURE__ */ jsxs55("div", { className: "min-w-[180px] space-y-1", children: [
18606
+ /* @__PURE__ */ jsx66(Label3, { className: "text-xs", children: "Upload file" }),
18607
+ /* @__PURE__ */ jsxs55("div", { className: "relative", children: [
18608
+ /* @__PURE__ */ jsx66(
18609
+ Input,
18610
+ {
18611
+ type: "file",
18612
+ accept: ".txt,.md,.json,.pdf,text/plain,text/markdown,application/json,application/pdf",
18613
+ disabled: uploadingAttachedKbFile || uploadingAttachedKbLink,
18614
+ className: "h-8 cursor-pointer text-xs disabled:opacity-60",
18615
+ onChange: onAttachedKbFileChange
18616
+ },
18617
+ attachedKbInputKey
18618
+ ),
18619
+ uploadingAttachedKbFile ? /* @__PURE__ */ jsxs55(
18620
+ "div",
18621
+ {
18622
+ className: "pointer-events-none absolute inset-0 flex items-center justify-center gap-2 rounded-md bg-background/85 text-xs font-medium text-gray-700 dark:text-gray-200",
18623
+ "aria-live": "polite",
18624
+ children: [
18625
+ /* @__PURE__ */ jsx66(Loader25, { className: "h-4 w-4 shrink-0 animate-spin" }),
18626
+ "Saving & linking\u2026"
18627
+ ]
18628
+ }
18629
+ ) : null
18630
+ ] }),
18631
+ /* @__PURE__ */ jsx66("p", { className: "text-[11px] text-gray-500 dark:text-gray-400", children: "Pick a file to upload immediately (chunking, embeddings if configured, then link to this agent)." })
18632
+ ] }),
18633
+ /* @__PURE__ */ jsxs55("div", { className: "flex flex-wrap items-end gap-2", children: [
18634
+ /* @__PURE__ */ jsxs55("div", { className: "min-w-[200px] flex-1 space-y-1", children: [
18635
+ /* @__PURE__ */ jsx66(Label3, { className: "text-xs", children: "Attach existing document" }),
18636
+ /* @__PURE__ */ jsxs55(Select, { value: attachExistingDocId, onValueChange: setAttachExistingDocId, children: [
18637
+ /* @__PURE__ */ jsx66(SelectTrigger, { className: "h-8 text-xs", children: /* @__PURE__ */ jsx66(SelectValue, { placeholder: "Choose a document" }) }),
18638
+ /* @__PURE__ */ jsxs55(SelectContent, { children: [
18639
+ /* @__PURE__ */ jsx66(SelectItem, { value: "__none__", children: "\u2014 Select \u2014" }),
18640
+ kbCatalog.filter((d) => !attachedAgentKnowledge.some((a) => a.id === d.id)).map((d) => /* @__PURE__ */ jsx66(SelectItem, { value: String(d.id), children: d.name }, d.id))
18641
+ ] })
18642
+ ] })
18643
+ ] }),
18644
+ /* @__PURE__ */ jsx66(
18645
+ Button,
18646
+ {
18647
+ type: "button",
18648
+ size: "sm",
18649
+ className: "h-8",
18650
+ disabled: uploadingAttachedKbFile || uploadingAttachedKbLink || attachExistingDocId === "__none__",
18651
+ onClick: () => void handleAttachExistingToAttached(),
18652
+ children: uploadingAttachedKbLink ? /* @__PURE__ */ jsxs55("span", { className: "inline-flex items-center gap-1.5", children: [
18653
+ /* @__PURE__ */ jsx66(Loader25, { className: "h-3.5 w-3.5 animate-spin" }),
18654
+ "Linking\u2026"
18655
+ ] }) : "Attach"
18656
+ }
18657
+ )
18658
+ ] })
18659
+ ] }) : null
18660
+ ] }),
18661
+ /* @__PURE__ */ jsxs55(
18662
+ Button,
18663
+ {
18664
+ type: "button",
18665
+ size: "sm",
18666
+ onClick: () => void handleSaveLlmChatTab(),
18667
+ disabled: saving,
18668
+ className: "gap-1",
18669
+ children: [
18670
+ saving ? /* @__PURE__ */ jsx66(Loader25, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx66(Save6, { className: "h-3.5 w-3.5" }),
18671
+ "Save"
18672
+ ]
18673
+ }
18674
+ )
18675
+ ] }) : /* @__PURE__ */ jsxs55("div", { className: "rounded-lg border border-gray-200 dark:border-gray-600 bg-gray-50/80 dark:bg-gray-800/40 p-3 space-y-4", children: [
18676
+ /* @__PURE__ */ jsxs55("div", { children: [
18677
+ /* @__PURE__ */ jsx66("p", { className: "text-sm font-medium text-gray-900 dark:text-white", children: "Lead email routing" }),
18678
+ /* @__PURE__ */ jsx66("p", { className: "mt-0.5 text-[11px] text-gray-500 dark:text-gray-400", children: "Routing instructions and intents. When a message matches, your team gets one email per conversation." })
18679
+ ] }),
18680
+ !emailPluginAvailable ? /* @__PURE__ */ jsx66("p", { className: "rounded border border-amber-200 bg-amber-50/80 p-2 text-[11px] text-amber-700 dark:border-amber-800 dark:bg-amber-950/40 dark:text-amber-300", children: "Enable the Email plugin (SMTP) to send lead alert emails." }) : notifyEmailLoading ? /* @__PURE__ */ jsxs55("div", { className: "flex items-center gap-2 text-sm text-gray-500", children: [
18681
+ /* @__PURE__ */ jsx66(Loader25, { className: "h-4 w-4 animate-spin" }),
18682
+ "Loading\u2026"
18683
+ ] }) : /* @__PURE__ */ jsxs55(Fragment16, { children: [
18684
+ notifyEmailProvisionError ? /* @__PURE__ */ jsx66("p", { className: "rounded border border-amber-200 bg-amber-50 p-2 text-xs text-amber-900 dark:border-amber-800 dark:bg-amber-950/40 dark:text-amber-100", children: notifyEmailProvisionError }) : null,
18020
18685
  /* @__PURE__ */ jsxs55("div", { className: "space-y-1", children: [
18021
- /* @__PURE__ */ jsx66(Label3, { htmlFor: "agent-validation", className: "text-sm", children: "Validation & output guardrails" }),
18686
+ /* @__PURE__ */ jsx66(Label3, { htmlFor: "notify-email-system", className: "text-sm", children: "Routing instructions" }),
18022
18687
  /* @__PURE__ */ jsx66(
18023
18688
  Textarea,
18024
18689
  {
18025
- id: "agent-validation",
18026
- value: agentValidationJson,
18027
- onChange: (e) => setAgentValidationJson(e.target.value),
18690
+ id: "notify-email-system",
18691
+ value: notifyEmailSystem,
18692
+ onChange: (e) => setNotifyEmailSystem(e.target.value),
18028
18693
  rows: 4,
18029
- placeholder: 'Plain text or JSON: {"guardrails":"Never promise refunds.","maxUserChars":2000}',
18030
- className: "text-xs font-mono"
18031
- }
18032
- )
18033
- ] }),
18034
- !agentId ? /* @__PURE__ */ jsxs55("div", { className: "flex flex-wrap items-center gap-2", children: [
18035
- /* @__PURE__ */ jsx66(
18036
- Button,
18037
- {
18038
- type: "button",
18039
- size: "sm",
18040
- className: "gap-1",
18041
- disabled: agentSaving,
18042
- onClick: () => void handleCreateAssistantFromForm(),
18043
- children: agentSaving ? /* @__PURE__ */ jsxs55("span", { className: "inline-flex items-center gap-1.5", children: [
18044
- /* @__PURE__ */ jsx66(Loader25, { className: "h-3.5 w-3.5 animate-spin" }),
18045
- "Creating\u2026"
18046
- ] }) : /* @__PURE__ */ jsxs55(Fragment16, { children: [
18047
- /* @__PURE__ */ jsx66(Bot, { className: "h-3.5 w-3.5" }),
18048
- "Create assistant"
18049
- ] })
18050
- }
18051
- ),
18052
- /* @__PURE__ */ jsx66(
18053
- Button,
18054
- {
18055
- type: "button",
18056
- size: "sm",
18057
- variant: "secondary",
18058
- className: "gap-1",
18059
- disabled: agentSaving || agentLoading,
18060
- onClick: () => void handleRetryBootstrapAgent(),
18061
- children: "Retry auto-setup"
18694
+ placeholder: "When to treat a message as a lead, priority, when not to email\u2026",
18695
+ className: "text-sm"
18062
18696
  }
18063
18697
  )
18064
- ] }) : /* @__PURE__ */ jsx66(
18065
- Button,
18066
- {
18067
- type: "button",
18068
- size: "sm",
18069
- className: "gap-1",
18070
- disabled: agentSaving,
18071
- onClick: () => void handleSaveAgent(),
18072
- children: agentSaving ? /* @__PURE__ */ jsxs55("span", { className: "inline-flex items-center gap-1.5", children: [
18073
- /* @__PURE__ */ jsx66(Loader25, { className: "h-3.5 w-3.5 animate-spin" }),
18074
- "Saving\u2026"
18075
- ] }) : /* @__PURE__ */ jsxs55(Fragment16, { children: [
18076
- /* @__PURE__ */ jsx66(Save6, { className: "h-3.5 w-3.5" }),
18077
- "Save assistant"
18078
- ] })
18079
- }
18080
- )
18081
- ] }),
18082
- agentId && agentSlug.trim() ? /* @__PURE__ */ jsxs55("div", { className: "rounded-md border border-dashed border-gray-300 dark:border-gray-600 bg-white/60 dark:bg-gray-900/30 p-3 space-y-3", children: [
18083
- /* @__PURE__ */ jsxs55("div", { className: "flex items-center gap-2 text-xs font-medium text-gray-800 dark:text-gray-200", children: [
18084
- /* @__PURE__ */ jsx66(FileUp, { className: "h-3.5 w-3.5 shrink-0" }),
18085
- "Knowledge for this agent"
18086
18698
  ] }),
18087
- /* @__PURE__ */ jsx66("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: "Upload text (.txt, .md, .json) or PDF (.pdf), or link documents already in the knowledge base." }),
18088
- attachedKbLoading ? /* @__PURE__ */ jsx66("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: "Loading linked documents\u2026" }) : attachedAgentKnowledge.length === 0 ? /* @__PURE__ */ jsx66("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: "No documents linked yet." }) : /* @__PURE__ */ jsx66("ul", { className: "max-h-32 space-y-1.5 overflow-y-auto", children: attachedAgentKnowledge.map((d) => /* @__PURE__ */ jsxs55("li", { className: "flex items-center justify-between gap-2 text-sm", children: [
18089
- /* @__PURE__ */ jsx66("span", { className: "min-w-0 truncate", title: d.name, children: d.name }),
18090
- /* @__PURE__ */ jsx66(
18091
- Button,
18092
- {
18093
- type: "button",
18094
- variant: "ghost",
18095
- size: "sm",
18096
- className: "h-7 shrink-0 text-xs text-red-600 hover:text-red-700 dark:text-red-400",
18097
- onClick: () => void handleUnlinkKbDoc(d.id),
18098
- children: "Remove"
18099
- }
18100
- )
18101
- ] }, d.id)) }),
18102
- /* @__PURE__ */ jsxs55("div", { className: "min-w-[180px] space-y-1", children: [
18103
- /* @__PURE__ */ jsx66(Label3, { className: "text-xs", children: "Upload file" }),
18104
- /* @__PURE__ */ jsxs55("div", { className: "relative", children: [
18105
- /* @__PURE__ */ jsx66(
18106
- Input,
18107
- {
18108
- type: "file",
18109
- accept: ".txt,.md,.json,.pdf,text/plain,text/markdown,application/json,application/pdf",
18110
- disabled: uploadingAttachedKbFile || uploadingAttachedKbLink,
18111
- className: "h-8 cursor-pointer text-xs disabled:opacity-60",
18112
- onChange: onAttachedKbFileChange
18113
- },
18114
- attachedKbInputKey
18115
- ),
18116
- uploadingAttachedKbFile ? /* @__PURE__ */ jsxs55(
18117
- "div",
18699
+ /* @__PURE__ */ jsxs55("div", { className: "space-y-2", children: [
18700
+ /* @__PURE__ */ jsxs55("div", { className: "flex items-center justify-between gap-2", children: [
18701
+ /* @__PURE__ */ jsx66(Label3, { className: "text-sm", children: "Intents" }),
18702
+ /* @__PURE__ */ jsxs55(
18703
+ Button,
18118
18704
  {
18119
- className: "pointer-events-none absolute inset-0 flex items-center justify-center gap-2 rounded-md bg-background/85 text-xs font-medium text-gray-700 dark:text-gray-200",
18120
- "aria-live": "polite",
18705
+ type: "button",
18706
+ size: "sm",
18707
+ variant: "outline",
18708
+ className: "h-7 gap-1 text-xs",
18709
+ onClick: () => setEmailIntents((rows) => [
18710
+ ...rows,
18711
+ { intent: "", description: "", emailTo: "" }
18712
+ ]),
18121
18713
  children: [
18122
- /* @__PURE__ */ jsx66(Loader25, { className: "h-4 w-4 shrink-0 animate-spin" }),
18123
- "Saving & linking\u2026"
18714
+ /* @__PURE__ */ jsx66(Plus10, { className: "h-3 w-3" }),
18715
+ "Add intent"
18124
18716
  ]
18125
18717
  }
18126
- ) : null
18718
+ )
18127
18719
  ] }),
18128
- /* @__PURE__ */ jsx66("p", { className: "text-[11px] text-gray-500 dark:text-gray-400", children: "Pick a file to upload immediately (chunking, embeddings if configured, then link to this agent)." })
18129
- ] }),
18130
- /* @__PURE__ */ jsxs55("div", { className: "flex flex-wrap items-end gap-2", children: [
18131
- /* @__PURE__ */ jsxs55("div", { className: "min-w-[200px] flex-1 space-y-1", children: [
18132
- /* @__PURE__ */ jsx66(Label3, { className: "text-xs", children: "Attach existing document" }),
18133
- /* @__PURE__ */ jsxs55(Select, { value: attachExistingDocId, onValueChange: setAttachExistingDocId, children: [
18134
- /* @__PURE__ */ jsx66(SelectTrigger, { className: "h-8 text-xs", children: /* @__PURE__ */ jsx66(SelectValue, { placeholder: "Choose a document" }) }),
18135
- /* @__PURE__ */ jsxs55(SelectContent, { children: [
18136
- /* @__PURE__ */ jsx66(SelectItem, { value: "__none__", children: "\u2014 Select \u2014" }),
18137
- kbCatalog.filter((d) => !attachedAgentKnowledge.some((a) => a.id === d.id)).map((d) => /* @__PURE__ */ jsx66(SelectItem, { value: String(d.id), children: d.name }, d.id))
18138
- ] })
18720
+ /* @__PURE__ */ jsx66("div", { className: "overflow-x-auto rounded border border-gray-200 dark:border-gray-600", children: /* @__PURE__ */ jsxs55("table", { className: "w-full text-xs", children: [
18721
+ /* @__PURE__ */ jsx66("thead", { children: /* @__PURE__ */ jsxs55("tr", { className: "bg-gray-100/80 text-left dark:bg-gray-900/60", children: [
18722
+ /* @__PURE__ */ jsx66("th", { className: "w-[28%] p-2 font-medium", children: "Intent" }),
18723
+ /* @__PURE__ */ jsx66("th", { className: "w-[42%] p-2 font-medium", children: "Description" }),
18724
+ /* @__PURE__ */ jsx66("th", { className: "w-[26%] p-2 font-medium", children: "Email To" }),
18725
+ /* @__PURE__ */ jsx66("th", { className: "w-8 p-2" })
18726
+ ] }) }),
18727
+ /* @__PURE__ */ jsxs55("tbody", { children: [
18728
+ emailIntents.length === 0 ? /* @__PURE__ */ jsx66("tr", { children: /* @__PURE__ */ jsx66("td", { colSpan: 4, className: "p-4 text-center text-gray-500 dark:text-gray-400", children: "Add at least one intent, then Save." }) }) : null,
18729
+ emailIntents.map((row, idx) => /* @__PURE__ */ jsxs55("tr", { className: "border-t border-gray-200 dark:border-gray-700", children: [
18730
+ /* @__PURE__ */ jsx66("td", { className: "p-1.5 align-top", children: /* @__PURE__ */ jsx66(
18731
+ Input,
18732
+ {
18733
+ value: row.intent,
18734
+ onChange: (e) => {
18735
+ const v = e.target.value.toUpperCase().replace(/[^A-Z0-9_]/g, "");
18736
+ setEmailIntents(
18737
+ (rows) => rows.map((r, i) => i === idx ? { ...r, intent: v } : r)
18738
+ );
18739
+ },
18740
+ placeholder: "SALES_LEAD",
18741
+ className: "h-8 font-mono text-xs"
18742
+ }
18743
+ ) }),
18744
+ /* @__PURE__ */ jsx66("td", { className: "p-1.5 align-top", children: /* @__PURE__ */ jsx66(
18745
+ Input,
18746
+ {
18747
+ value: row.description,
18748
+ onChange: (e) => setEmailIntents(
18749
+ (rows) => rows.map(
18750
+ (r, i) => i === idx ? { ...r, description: e.target.value } : r
18751
+ )
18752
+ ),
18753
+ placeholder: "What this intent means",
18754
+ className: "h-8 text-xs"
18755
+ }
18756
+ ) }),
18757
+ /* @__PURE__ */ jsx66("td", { className: "p-1.5 align-top", children: /* @__PURE__ */ jsx66(
18758
+ Input,
18759
+ {
18760
+ value: row.emailTo,
18761
+ onChange: (e) => setEmailIntents(
18762
+ (rows) => rows.map(
18763
+ (r, i) => i === idx ? { ...r, emailTo: e.target.value } : r
18764
+ )
18765
+ ),
18766
+ placeholder: "team@company.com",
18767
+ className: "h-8 text-xs"
18768
+ }
18769
+ ) }),
18770
+ /* @__PURE__ */ jsx66("td", { className: "p-1.5 align-top", children: /* @__PURE__ */ jsx66(
18771
+ Button,
18772
+ {
18773
+ type: "button",
18774
+ size: "icon",
18775
+ variant: "ghost",
18776
+ className: "h-8 w-8 text-red-600",
18777
+ onClick: () => setEmailIntents((rows) => rows.filter((_, i) => i !== idx)),
18778
+ "aria-label": "Remove intent",
18779
+ children: /* @__PURE__ */ jsx66(Trash29, { className: "h-3.5 w-3.5" })
18780
+ }
18781
+ ) })
18782
+ ] }, idx))
18139
18783
  ] })
18140
- ] }),
18141
- /* @__PURE__ */ jsx66(
18142
- Button,
18143
- {
18144
- type: "button",
18145
- size: "sm",
18146
- className: "h-8",
18147
- disabled: uploadingAttachedKbFile || uploadingAttachedKbLink || attachExistingDocId === "__none__",
18148
- onClick: () => void handleAttachExistingToAttached(),
18149
- children: uploadingAttachedKbLink ? /* @__PURE__ */ jsxs55("span", { className: "inline-flex items-center gap-1.5", children: [
18150
- /* @__PURE__ */ jsx66(Loader25, { className: "h-3.5 w-3.5 animate-spin" }),
18151
- "Linking\u2026"
18152
- ] }) : "Attach"
18153
- }
18154
- )
18784
+ ] }) })
18155
18785
  ] })
18156
- ] }) : null
18157
- ] }),
18158
- /* @__PURE__ */ jsxs55("div", { className: "space-y-1", children: [
18159
- /* @__PURE__ */ jsx66(Label3, { htmlFor: `${settingsGroup}-botName`, className: "text-sm", children: "Widget title" }),
18160
- /* @__PURE__ */ jsx66(
18161
- Input,
18162
- {
18163
- id: `${settingsGroup}-botName`,
18164
- value: botName,
18165
- onChange: (e) => setBotName(e.target.value),
18166
- placeholder: "e.g. Support Bot",
18167
- className: "h-8 text-sm"
18168
- }
18169
- )
18170
- ] }),
18171
- /* @__PURE__ */ jsxs55("div", { className: "space-y-1", children: [
18172
- /* @__PURE__ */ jsx66(Label3, { htmlFor: `${settingsGroup}-iconImageUrl`, className: "text-sm", children: "Icon image URL" }),
18173
- /* @__PURE__ */ jsx66(
18174
- Input,
18175
- {
18176
- id: `${settingsGroup}-iconImageUrl`,
18177
- value: iconImageUrl,
18178
- onChange: (e) => setIconImageUrl(e.target.value),
18179
- placeholder: "https://\u2026 or /images/chat-icon.png",
18180
- className: "h-8 text-sm"
18181
- }
18182
- ),
18183
- /* @__PURE__ */ jsx66("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: "PNG or image URL. Leave empty to use emoji below." })
18184
- ] }),
18185
- /* @__PURE__ */ jsxs55("div", { className: "space-y-1", children: [
18186
- /* @__PURE__ */ jsx66(Label3, { htmlFor: `${settingsGroup}-icon`, className: "text-sm", children: "Icon fallback (emoji)" }),
18187
- /* @__PURE__ */ jsx66(
18188
- Input,
18786
+ ] }),
18787
+ /* @__PURE__ */ jsxs55(
18788
+ Button,
18189
18789
  {
18190
- id: `${settingsGroup}-icon`,
18191
- value: icon,
18192
- onChange: (e) => setIcon(e.target.value),
18193
- placeholder: "e.g. \u{1F4AC}",
18194
- className: "h-8 text-sm w-20"
18790
+ type: "button",
18791
+ size: "sm",
18792
+ onClick: () => void handleSaveLlmNotifyTab(),
18793
+ disabled: notifyEmailSaving || !emailPluginAvailable,
18794
+ className: "gap-1",
18795
+ children: [
18796
+ notifyEmailSaving ? /* @__PURE__ */ jsx66(Loader25, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx66(Save6, { className: "h-3.5 w-3.5" }),
18797
+ "Save"
18798
+ ]
18195
18799
  }
18196
18800
  )
18197
- ] }),
18198
- /* @__PURE__ */ jsxs55("div", { className: "flex gap-4", children: [
18199
- /* @__PURE__ */ jsxs55("div", { className: "space-y-1", children: [
18200
- /* @__PURE__ */ jsx66(Label3, { htmlFor: `${settingsGroup}-iconBg`, className: "text-sm", children: "Icon background" }),
18201
- /* @__PURE__ */ jsxs55("div", { className: "flex items-center gap-2", children: [
18202
- /* @__PURE__ */ jsx66(
18203
- "input",
18204
- {
18205
- type: "color",
18206
- id: `${settingsGroup}-iconBg`,
18207
- value: iconBackgroundColor,
18208
- onChange: (e) => setIconBackgroundColor(e.target.value),
18209
- className: "h-8 w-10 cursor-pointer rounded border border-gray-300 dark:border-gray-600"
18210
- }
18211
- ),
18212
- /* @__PURE__ */ jsx66(
18213
- Input,
18214
- {
18215
- value: iconBackgroundColor,
18216
- onChange: (e) => setIconBackgroundColor(e.target.value),
18217
- className: "h-8 w-24 text-sm font-mono"
18218
- }
18219
- )
18220
- ] })
18221
- ] }),
18222
- /* @__PURE__ */ jsxs55("div", { className: "space-y-1", children: [
18223
- /* @__PURE__ */ jsx66(Label3, { htmlFor: `${settingsGroup}-headerColor`, className: "text-sm", children: "Header color" }),
18224
- /* @__PURE__ */ jsxs55("div", { className: "flex items-center gap-2", children: [
18225
- /* @__PURE__ */ jsx66(
18226
- "input",
18227
- {
18228
- type: "color",
18229
- id: `${settingsGroup}-headerColor`,
18230
- value: headerColor,
18231
- onChange: (e) => setHeaderColor(e.target.value),
18232
- className: "h-8 w-10 cursor-pointer rounded border border-gray-300 dark:border-gray-600"
18233
- }
18234
- ),
18235
- /* @__PURE__ */ jsx66(
18236
- Input,
18237
- {
18238
- value: headerColor,
18239
- onChange: (e) => setHeaderColor(e.target.value),
18240
- className: "h-8 w-24 text-sm font-mono"
18241
- }
18242
- )
18243
- ] })
18244
- ] })
18245
18801
  ] })
18246
18802
  ] }),
18247
- /* @__PURE__ */ jsxs55(Button, { size: "sm", onClick: handleSave, disabled: saving, className: "gap-1", children: [
18803
+ chatMode !== "llm" && /* @__PURE__ */ jsxs55(Button, { size: "sm", onClick: handleSave, disabled: saving, className: "gap-1", children: [
18248
18804
  /* @__PURE__ */ jsx66(Save6, { className: "h-3.5 w-3.5" }),
18249
18805
  "Save"
18250
18806
  ] })
@@ -18747,7 +19303,7 @@ function BrandEditPage({ brandId }) {
18747
19303
  init_admin_list_return_url();
18748
19304
  import { useState as useState40, useEffect as useEffect37, useMemo as useMemo8, useContext as useContext8 } from "react";
18749
19305
  import { useRouter as useRouter15, useSearchParams as useSearchParams15 } from "next/navigation";
18750
- import { AlertCircle as AlertCircle7, Plus as Plus11, Trash2 as Trash29, Star, Save as Save8 } from "lucide-react";
19306
+ import { AlertCircle as AlertCircle7, Plus as Plus11, Trash2 as Trash210, Star, Save as Save8 } from "lucide-react";
18751
19307
  init_DetailPageLayout();
18752
19308
  init_DetailPageHeader();
18753
19309
 
@@ -19211,12 +19767,10 @@ function ProductEditPage({ productId }) {
19211
19767
  setSaving(true);
19212
19768
  try {
19213
19769
  const activeCurrency = String(currency || "INR");
19214
- console.log("DEBUG save:", { activeCurrency, price, compareAtPrice });
19215
19770
  let saveRate = 1;
19216
19771
  if (activeCurrency !== "INR") {
19217
19772
  const displayRate = await fetchDisplayRate(activeCurrency);
19218
19773
  saveRate = displayRate !== 0 && displayRate !== 1 ? 1 / displayRate : 1;
19219
- console.log("DEBUG rate:", { displayRate, saveRate, priceInBase: Math.round(Number(price) * saveRate * 100) / 100 });
19220
19774
  }
19221
19775
  const priceInBase = Math.round(Number(price) * saveRate * 100) / 100;
19222
19776
  const compareAtPriceInBase = compareAtPrice ? Math.round(Number(compareAtPrice) * saveRate * 100) / 100 : null;
@@ -19623,7 +20177,7 @@ function ProductEditPage({ productId }) {
19623
20177
  type: "button",
19624
20178
  onClick: () => removeTaxRow(i),
19625
20179
  className: "p-2 text-gray-400 hover:text-red-600 rounded shrink-0 mb-0.5",
19626
- children: /* @__PURE__ */ jsx70(Trash29, { className: "h-4 w-4" })
20180
+ children: /* @__PURE__ */ jsx70(Trash210, { className: "h-4 w-4" })
19627
20181
  }
19628
20182
  )
19629
20183
  ] }, i);
@@ -19665,7 +20219,7 @@ function ProductEditPage({ productId }) {
19665
20219
  className: `${inputCls} flex-1 min-w-[120px]`
19666
20220
  }
19667
20221
  ),
19668
- /* @__PURE__ */ jsx70("button", { type: "button", onClick: () => removeFacet(i), className: "p-2 text-gray-400 hover:text-red-600 rounded shrink-0 mt-0.5", children: /* @__PURE__ */ jsx70(Trash29, { className: "h-4 w-4" }) })
20222
+ /* @__PURE__ */ jsx70("button", { type: "button", onClick: () => removeFacet(i), className: "p-2 text-gray-400 hover:text-red-600 rounded shrink-0 mt-0.5", children: /* @__PURE__ */ jsx70(Trash210, { className: "h-4 w-4" }) })
19669
20223
  ] }, i)),
19670
20224
  /* @__PURE__ */ jsxs59("button", { type: "button", onClick: addFacet, className: "inline-flex items-center gap-1 rounded border border-gray-300 bg-white px-2 py-1.5 text-xs font-medium text-gray-700 hover:bg-gray-50", children: [
19671
20225
  /* @__PURE__ */ jsx70(Plus11, { className: "h-3.5 w-3.5" }),
@@ -19712,7 +20266,7 @@ function ProductEditPage({ productId }) {
19712
20266
  children: /* @__PURE__ */ jsx70(Star, { className: `h-4 w-4 ${row.isDefault ? "fill-amber-400 text-amber-500" : "text-gray-400"}` })
19713
20267
  }
19714
20268
  ),
19715
- /* @__PURE__ */ jsx70("button", { type: "button", onClick: () => removeImage(i), className: "mt-6 p-2 text-gray-400 hover:text-red-600 rounded", children: /* @__PURE__ */ jsx70(Trash29, { className: "h-4 w-4" }) })
20269
+ /* @__PURE__ */ jsx70("button", { type: "button", onClick: () => removeImage(i), className: "mt-6 p-2 text-gray-400 hover:text-red-600 rounded", children: /* @__PURE__ */ jsx70(Trash210, { className: "h-4 w-4" }) })
19716
20270
  ] }, i)),
19717
20271
  /* @__PURE__ */ jsxs59("button", { type: "button", onClick: addImage, className: "inline-flex items-center gap-1 rounded border border-gray-300 bg-white px-2 py-1.5 text-xs font-medium text-gray-700 hover:bg-gray-50", children: [
19718
20272
  /* @__PURE__ */ jsx70(Plus11, { className: "h-3.5 w-3.5" }),
@@ -19757,7 +20311,7 @@ function ProductEditPage({ productId }) {
19757
20311
  className: `${inputCls} flex-1`
19758
20312
  }
19759
20313
  ),
19760
- /* @__PURE__ */ jsx70("button", { type: "button", onClick: () => removeSpec(i), className: "p-2 text-gray-400 hover:text-red-600 rounded shrink-0", children: /* @__PURE__ */ jsx70(Trash29, { className: "h-4 w-4" }) })
20314
+ /* @__PURE__ */ jsx70("button", { type: "button", onClick: () => removeSpec(i), className: "p-2 text-gray-400 hover:text-red-600 rounded shrink-0", children: /* @__PURE__ */ jsx70(Trash210, { className: "h-4 w-4" }) })
19761
20315
  ] }, i)),
19762
20316
  /* @__PURE__ */ jsxs59("button", { type: "button", onClick: addSpec, className: "inline-flex items-center gap-1 rounded border border-gray-300 bg-white px-2 py-1.5 text-xs font-medium text-gray-700 hover:bg-gray-50", children: [
19763
20317
  /* @__PURE__ */ jsx70(Plus11, { className: "h-3.5 w-3.5" }),
@@ -19779,7 +20333,7 @@ function ProductEditPage({ productId }) {
19779
20333
  init_admin_list_return_url();
19780
20334
  import { useState as useState41, useEffect as useEffect38 } from "react";
19781
20335
  import { useRouter as useRouter16, useSearchParams as useSearchParams16 } from "next/navigation";
19782
- import { AlertCircle as AlertCircle8, Plus as Plus12, Trash2 as Trash210, ChevronDown as ChevronDown6, ChevronUp as ChevronUp2, Save as Save9, Power as Power2 } from "lucide-react";
20336
+ import { AlertCircle as AlertCircle8, Plus as Plus12, Trash2 as Trash211, ChevronDown as ChevronDown6, ChevronUp as ChevronUp2, Save as Save9, Power as Power2 } from "lucide-react";
19783
20337
  init_DetailPageLayout();
19784
20338
  init_DetailPageHeader();
19785
20339
  import { Fragment as Fragment19, jsx as jsx71, jsxs as jsxs60 } from "react/jsx-runtime";
@@ -20106,7 +20660,7 @@ function CollectionEditPage({ collectionId }) {
20106
20660
  /* @__PURE__ */ jsx71("option", { value: "video", children: "Video" })
20107
20661
  ] }),
20108
20662
  /* @__PURE__ */ jsx71("input", { type: "text", value: slide.caption, onChange: (e) => updateHeroSlide(i, "caption", e.target.value), placeholder: "Caption", className: `${inputCls2} flex-1 min-w-[120px]` }),
20109
- /* @__PURE__ */ jsx71("button", { type: "button", onClick: () => removeHeroSlide(i), className: "p-2 text-gray-400 hover:text-red-600 rounded shrink-0", children: /* @__PURE__ */ jsx71(Trash210, { className: "h-4 w-4" }) })
20663
+ /* @__PURE__ */ jsx71("button", { type: "button", onClick: () => removeHeroSlide(i), className: "p-2 text-gray-400 hover:text-red-600 rounded shrink-0", children: /* @__PURE__ */ jsx71(Trash211, { className: "h-4 w-4" }) })
20110
20664
  ] }, i)),
20111
20665
  /* @__PURE__ */ jsxs60("button", { type: "button", onClick: addHeroSlide, className: "inline-flex items-center gap-1 rounded border border-gray-300 bg-white px-2 py-1.5 text-xs font-medium text-gray-700 hover:bg-gray-50", children: [
20112
20666
  /* @__PURE__ */ jsx71(Plus12, { className: "h-3.5 w-3.5" }),
@@ -20119,12 +20673,12 @@ function CollectionEditPage({ collectionId }) {
20119
20673
  /* @__PURE__ */ jsxs60("div", { className: "flex gap-2", children: [
20120
20674
  /* @__PURE__ */ jsx71("input", { type: "text", value: v.name, onChange: (e) => updateVariant(i, "name", e.target.value), placeholder: "Name", className: `${inputCls2} w-40` }),
20121
20675
  /* @__PURE__ */ jsx71("input", { type: "number", value: v.price, onChange: (e) => updateVariant(i, "price", e.target.value), placeholder: "Price", className: `${inputCls2} w-28` }),
20122
- /* @__PURE__ */ jsx71("button", { type: "button", onClick: () => removeVariant(i), className: "p-2 text-gray-400 hover:text-red-600 rounded shrink-0", children: /* @__PURE__ */ jsx71(Trash210, { className: "h-4 w-4" }) })
20676
+ /* @__PURE__ */ jsx71("button", { type: "button", onClick: () => removeVariant(i), className: "p-2 text-gray-400 hover:text-red-600 rounded shrink-0", children: /* @__PURE__ */ jsx71(Trash211, { className: "h-4 w-4" }) })
20123
20677
  ] }),
20124
20678
  v.extraSpecs.map((s, si) => /* @__PURE__ */ jsxs60("div", { className: "flex gap-2", children: [
20125
20679
  /* @__PURE__ */ jsx71("input", { type: "text", value: s.key, onChange: (e) => updateVariantExtraSpec(i, si, "key", e.target.value), placeholder: "Key", className: `${inputCls2} flex-1` }),
20126
20680
  /* @__PURE__ */ jsx71("input", { type: "text", value: s.value, onChange: (e) => updateVariantExtraSpec(i, si, "value", e.target.value), placeholder: "Value", className: `${inputCls2} flex-1` }),
20127
- /* @__PURE__ */ jsx71("button", { type: "button", onClick: () => removeVariantExtraSpec(i, si), className: "p-2 text-gray-400 hover:text-red-600 rounded shrink-0", children: /* @__PURE__ */ jsx71(Trash210, { className: "h-4 w-4" }) })
20681
+ /* @__PURE__ */ jsx71("button", { type: "button", onClick: () => removeVariantExtraSpec(i, si), className: "p-2 text-gray-400 hover:text-red-600 rounded shrink-0", children: /* @__PURE__ */ jsx71(Trash211, { className: "h-4 w-4" }) })
20128
20682
  ] }, si)),
20129
20683
  /* @__PURE__ */ jsxs60("button", { type: "button", onClick: () => addVariantExtraSpec(i), className: "inline-flex items-center gap-1 rounded border border-gray-300 bg-white px-2 py-1 text-xs text-gray-700 hover:bg-gray-50", children: [
20130
20684
  /* @__PURE__ */ jsx71(Plus12, { className: "h-3 w-3" }),
@@ -20170,7 +20724,7 @@ init_permission_entities();
20170
20724
  init_vendor_scope();
20171
20725
  import { useCallback as useCallback13, useEffect as useEffect39, useState as useState42 } from "react";
20172
20726
  import { useSession as useSession8 } from "next-auth/react";
20173
- import { Shield as Shield2, Save as Save10, Trash2 as Trash211 } from "lucide-react";
20727
+ import { Shield as Shield2, Save as Save10, Trash2 as Trash212 } from "lucide-react";
20174
20728
 
20175
20729
  // src/auth/helpers.ts
20176
20730
  init_rbac_debug();
@@ -20422,7 +20976,7 @@ function RolesPage() {
20422
20976
  saving ? "Saving\u2026" : "Save"
20423
20977
  ] }),
20424
20978
  !vendorScoped && selected && !isSuperAdminGroupName(selected.name) && /* @__PURE__ */ jsxs61(Button, { size: "sm", variant: "outline", className: "gap-1 text-red-600", onClick: () => setDeleteRoleOpen(true), children: [
20425
- /* @__PURE__ */ jsx72(Trash211, { className: "h-3.5 w-3.5" }),
20979
+ /* @__PURE__ */ jsx72(Trash212, { className: "h-3.5 w-3.5" }),
20426
20980
  "Delete role"
20427
20981
  ] })
20428
20982
  ] }),
@@ -21375,7 +21929,7 @@ function RuleTreeNode({ node, depth = 0 }) {
21375
21929
  node.type && /* @__PURE__ */ jsx78("span", { className: "rounded bg-white border border-gray-200 px-2 py-0.5 text-xs text-gray-700", children: RULE_TYPE_LABEL[node.type] ?? node.type }),
21376
21930
  node.subType && /* @__PURE__ */ jsx78("span", { className: "text-gray-400 text-xs", children: node.subType }),
21377
21931
  node.comparisonOperator && /* @__PURE__ */ jsx78("span", { className: "font-mono text-xs font-semibold text-gray-700", children: node.comparisonOperator }),
21378
- node.value && Object.keys(node.value).length > 0 && /* @__PURE__ */ jsx78("span", { className: "font-mono text-xs bg-white border border-gray-200 rounded px-2 py-0.5 text-gray-800 break-all", children: Object.keys(node.value).length === 1 && "v" in node.value ? String(node.value.v) : JSON.stringify(node.value) })
21932
+ node.value && Object.keys(node.value).length > 0 && /* @__PURE__ */ jsx78("span", { className: "font-mono text-xs bg-white border border-gray-200 rounded px-2 py-0.5 text-gray-800 break-all", children: node.type === "quantity" && node.subType === "productId" && "v" in node.value ? `product #${node.value.productId} >= ${node.value.v}` : Object.keys(node.value).length === 1 && "v" in node.value ? String(node.value.v) : JSON.stringify(node.value) })
21379
21933
  ] }) }),
21380
21934
  isGroup && open && (node.children ?? []).length > 0 && /* @__PURE__ */ jsx78("div", { className: "mt-2 pl-4 space-y-2", children: (node.children ?? []).map((child) => /* @__PURE__ */ jsx78(RuleTreeNode, { node: child, depth: depth + 1 }, child.id)) })
21381
21935
  ] });
@@ -21619,7 +22173,7 @@ import { AlertCircle as AlertCircle9, Save as Save11, Zap as Zap3 } from "lucide
21619
22173
  // src/admin/pages/DiscountsConditionsBuilder.tsx
21620
22174
  init_button();
21621
22175
  import { useState as useState46, useEffect as useEffect43 } from "react";
21622
- import { Plus as Plus13, Trash2 as Trash212, Search as Search5, Package as Package2, ShoppingCart as ShoppingCart5, User as User3, Hash, DollarSign, Gift } from "lucide-react";
22176
+ import { Plus as Plus13, Trash2 as Trash213, Search as Search5, Package as Package2, ShoppingCart as ShoppingCart5, User as User3, Hash, DollarSign, Gift } from "lucide-react";
21623
22177
  import { jsx as jsx79, jsxs as jsxs66 } from "react/jsx-runtime";
21624
22178
  function uid() {
21625
22179
  return Math.random().toString(36).slice(2, 10);
@@ -21635,10 +22189,10 @@ function conditionToRule(c) {
21635
22189
  return { ...base, type: "minAmount", comparisonOperator: "gte", value: { v: c.amount ?? "0" } };
21636
22190
  case "minQuantity":
21637
22191
  return { ...base, type: "quantity", comparisonOperator: "gte", value: { v: c.quantity ?? "1" } };
21638
- case "Product":
21639
- return { ...base, type: "product", subType: "productId", comparisonOperator: "eq", value: { v: String(c.productId ?? "") } };
22192
+ case "productMinQuantity":
22193
+ return { ...base, type: "quantity", subType: "productId", comparisonOperator: "gte", value: { v: c.quantity ?? "1", productId: String(c.productId ?? "") } };
21640
22194
  case "nthOrder":
21641
- return { ...base, type: "order", subType: "nthOrder", comparisonOperator: "gte", value: { v: c.nthOrder ?? "1" } };
22195
+ return { ...base, type: "order", subType: "nthOrder", comparisonOperator: "eq", value: { v: c.nthOrder ?? "1" } };
21642
22196
  case "userType":
21643
22197
  return { ...base, type: "user", subType: "userType", comparisonOperator: "eq", value: { v: c.userType ?? "" } };
21644
22198
  case "paymentMethod":
@@ -21704,9 +22258,16 @@ function ruleTreeToFriendly(rules) {
21704
22258
  case "minAmount":
21705
22259
  return { id: rule._clientId ?? uid(), kind: "minAmount", amount: v };
21706
22260
  case "quantity":
22261
+ if (rule.subType === "productId") {
22262
+ return {
22263
+ id: rule._clientId ?? uid(),
22264
+ kind: "productMinQuantity",
22265
+ quantity: v,
22266
+ productId: Number(rule.value?.productId) || null,
22267
+ productName: void 0
22268
+ };
22269
+ }
21707
22270
  return { id: rule._clientId ?? uid(), kind: "minQuantity", quantity: v };
21708
- case "product":
21709
- return { id: rule._clientId ?? uid(), kind: "Product", productId: Number(v) || null, productName: void 0 };
21710
22271
  case "order":
21711
22272
  return { id: rule._clientId ?? uid(), kind: "nthOrder", nthOrder: v };
21712
22273
  case "user":
@@ -22030,7 +22591,7 @@ function ConditionCard({
22030
22591
  const iconMap = {
22031
22592
  minAmount: /* @__PURE__ */ jsx79(DollarSign, { className: "h-3.5 w-3.5 text-blue-500" }),
22032
22593
  minQuantity: /* @__PURE__ */ jsx79(Hash, { className: "h-3.5 w-3.5 text-purple-500" }),
22033
- Product: /* @__PURE__ */ jsx79(Package2, { className: "h-3.5 w-3.5 text-green-500" }),
22594
+ productMinQuantity: /* @__PURE__ */ jsx79(Hash, { className: "h-3.5 w-3.5 text-green-600" }),
22034
22595
  nthOrder: /* @__PURE__ */ jsx79(ShoppingCart5, { className: "h-3.5 w-3.5 text-orange-500" }),
22035
22596
  userType: /* @__PURE__ */ jsx79(User3, { className: "h-3.5 w-3.5 text-pink-500" }),
22036
22597
  paymentMethod: /* @__PURE__ */ jsx79(DollarSign, { className: "h-3.5 w-3.5 text-indigo-500" }),
@@ -22052,7 +22613,7 @@ function ConditionCard({
22052
22613
  /* @__PURE__ */ jsxs66(SelectContent, { children: [
22053
22614
  /* @__PURE__ */ jsx79(SelectItem, { value: "minAmount", children: "Minimum order amount" }),
22054
22615
  /* @__PURE__ */ jsx79(SelectItem, { value: "minQuantity", children: "Minimum quantity" }),
22055
- /* @__PURE__ */ jsx79(SelectItem, { value: "Product", children: "Product" }),
22616
+ /* @__PURE__ */ jsx79(SelectItem, { value: "productMinQuantity", children: "Product" }),
22056
22617
  /* @__PURE__ */ jsx79(SelectItem, { value: "nthOrder", children: "Customer's Nth order" }),
22057
22618
  /* @__PURE__ */ jsx79(SelectItem, { value: "userType", children: "User type" }),
22058
22619
  /* @__PURE__ */ jsx79(SelectItem, { value: "paymentMethod", children: "Payment method" }),
@@ -22072,9 +22633,33 @@ function ConditionCard({
22072
22633
  /* @__PURE__ */ jsx79("span", { className: "text-xs text-gray-500 shrink-0", children: "Qty \u2265" }),
22073
22634
  /* @__PURE__ */ jsx79("input", { type: "number", min: 1, step: "1", value: condition.quantity ?? "", onChange: (e) => onChange({ ...condition, quantity: e.target.value }), className: inputCls3, placeholder: "2" })
22074
22635
  ] }),
22075
- condition.kind === "Product" && /* @__PURE__ */ jsx79(ProductPicker, { value: condition.productId, label: condition.productName, onChange: (id, name) => onChange({ ...condition, productId: id, productName: name }) }),
22636
+ condition.kind === "productMinQuantity" && /* @__PURE__ */ jsxs66("div", { className: "space-y-2", children: [
22637
+ /* @__PURE__ */ jsx79(
22638
+ ProductPicker,
22639
+ {
22640
+ value: condition.productId,
22641
+ label: condition.productName,
22642
+ onChange: (id, name) => onChange({ ...condition, productId: id, productName: name })
22643
+ }
22644
+ ),
22645
+ /* @__PURE__ */ jsxs66("div", { className: "flex items-center gap-2", children: [
22646
+ /* @__PURE__ */ jsx79("span", { className: "text-xs text-gray-500 shrink-0", children: "Minimum quantity of this product" }),
22647
+ /* @__PURE__ */ jsx79(
22648
+ "input",
22649
+ {
22650
+ type: "number",
22651
+ min: 1,
22652
+ step: "1",
22653
+ value: condition.quantity ?? "",
22654
+ onChange: (e) => onChange({ ...condition, quantity: e.target.value }),
22655
+ className: inputCls3,
22656
+ placeholder: "2"
22657
+ }
22658
+ )
22659
+ ] })
22660
+ ] }),
22076
22661
  condition.kind === "nthOrder" && /* @__PURE__ */ jsxs66("div", { className: "flex items-center gap-2", children: [
22077
- /* @__PURE__ */ jsx79("span", { className: "text-xs text-gray-500 shrink-0", children: "Order number \u2265" }),
22662
+ /* @__PURE__ */ jsx79("span", { className: "text-xs text-gray-500 shrink-0", children: "Order number =" }),
22078
22663
  /* @__PURE__ */ jsx79("input", { type: "number", min: 1, step: "1", value: condition.nthOrder ?? "", onChange: (e) => onChange({ ...condition, nthOrder: e.target.value }), className: inputCls3, placeholder: "2" }),
22079
22664
  /* @__PURE__ */ jsx79("span", { className: "text-xs text-gray-400 shrink-0", children: "(e.g. 2 = 2nd+ order)" })
22080
22665
  ] }),
@@ -22102,7 +22687,7 @@ function ConditionCard({
22102
22687
  condition.kind === "shipping" && /* @__PURE__ */ jsx79("p", { className: "text-xs text-gray-400 italic", children: "Applied when any shipping method is selected." }),
22103
22688
  condition.kind === "referral" && /* @__PURE__ */ jsx79("p", { className: "text-xs text-gray-400 italic", children: "Applied when order comes from a referral." })
22104
22689
  ] }),
22105
- /* @__PURE__ */ jsx79("button", { type: "button", onClick: onRemove, className: "shrink-0 text-gray-300 hover:text-red-500 transition-colors mt-0.5", children: /* @__PURE__ */ jsx79(Trash212, { className: "h-4 w-4" }) })
22690
+ /* @__PURE__ */ jsx79("button", { type: "button", onClick: onRemove, className: "shrink-0 text-gray-300 hover:text-red-500 transition-colors mt-0.5", children: /* @__PURE__ */ jsx79(Trash213, { className: "h-4 w-4" }) })
22106
22691
  ] });
22107
22692
  }
22108
22693
  function RewardCard({
@@ -22127,7 +22712,7 @@ function RewardCard({
22127
22712
  ] }),
22128
22713
  /* @__PURE__ */ jsx79("p", { className: "text-[11px] text-purple-600", children: "This product will be automatically added to cart when the discount is applied." })
22129
22714
  ] }),
22130
- /* @__PURE__ */ jsx79("button", { type: "button", onClick: onRemove, className: "shrink-0 text-gray-300 hover:text-red-500 transition-colors mt-0.5", children: /* @__PURE__ */ jsx79(Trash212, { className: "h-4 w-4" }) })
22715
+ /* @__PURE__ */ jsx79("button", { type: "button", onClick: onRemove, className: "shrink-0 text-gray-300 hover:text-red-500 transition-colors mt-0.5", children: /* @__PURE__ */ jsx79(Trash213, { className: "h-4 w-4" }) })
22131
22716
  ] });
22132
22717
  }
22133
22718
  function DiscountConditionsBuilder({
@@ -22144,7 +22729,7 @@ function DiscountConditionsBuilder({
22144
22729
  const missingNameIds = [];
22145
22730
  for (const g of groups) {
22146
22731
  for (const c of g.conditions) {
22147
- if (c.kind === "Product" && c.productId && !c.productName)
22732
+ if (c.kind === "productMinQuantity" && c.productId && !c.productName)
22148
22733
  missingNameIds.push({ type: "product", id: c.id, entityId: c.productId, groupId: g.id });
22149
22734
  if (c.kind === "category" && c.categoryId && !c.categoryName)
22150
22735
  missingNameIds.push({ type: "category", id: c.id, entityId: c.categoryId, groupId: g.id });
@@ -22284,7 +22869,7 @@ function DiscountConditionsBuilder({
22284
22869
  type: "button",
22285
22870
  onClick: () => removeGroup(group.id),
22286
22871
  className: "text-gray-300 hover:text-red-500 transition-colors",
22287
- children: /* @__PURE__ */ jsx79(Trash212, { className: "h-3.5 w-3.5" })
22872
+ children: /* @__PURE__ */ jsx79(Trash213, { className: "h-3.5 w-3.5" })
22288
22873
  }
22289
22874
  )
22290
22875
  ] }),