@infuro/cms-core 1.0.19 → 1.0.20

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
@@ -338,7 +338,8 @@ import {
338
338
  ShoppingCart,
339
339
  CreditCard,
340
340
  Receipt,
341
- FolderTree
341
+ FolderTree,
342
+ Bot
342
343
  } from "lucide-react";
343
344
 
344
345
  // src/admin/admin-config-context.tsx
@@ -351,7 +352,7 @@ var defaultValue = {
351
352
  var AdminConfigContext = createContext(defaultValue);
352
353
 
353
354
  // src/lib/cms-version.ts
354
- var CMS_VERSION = true ? "1.0.19" : "0.0.0";
355
+ var CMS_VERSION = true ? "1.0.20" : "0.0.0";
355
356
 
356
357
  // src/components/Admin/Sidebar.tsx
357
358
  import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
@@ -496,6 +497,10 @@ function AdminSidebar({ variant = "sidebar" }) {
496
497
  /* @__PURE__ */ jsx4(Shield, { className: `h-4 w-4 mr-2 ${isActive("/admin/roles") ? iconActive : iconInactive}` }),
497
498
  "Roles"
498
499
  ] }) }),
500
+ /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsxs3(Link2, { href: "/admin/llm_agents", className: `${linkCls} ${isActive("/admin/llm_agents") ? linkActive : linkInactive}`, children: [
501
+ /* @__PURE__ */ jsx4(Bot, { className: `h-4 w-4 mr-2 ${isActive("/admin/llm_agents") ? iconActive : iconInactive}` }),
502
+ "LLM agents"
503
+ ] }) }),
499
504
  /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsxs3(Link2, { href: "/admin/plugins", className: `${linkCls} ${isActive("/admin/plugins") ? linkActive : linkInactive}`, children: [
500
505
  /* @__PURE__ */ jsx4(Puzzle, { className: `h-4 w-4 mr-2 ${isActive("/admin/plugins") ? iconActive : iconInactive}` }),
501
506
  "Plugins"
@@ -880,7 +885,7 @@ function AdminShell({ children }) {
880
885
  }
881
886
 
882
887
  // src/components/Admin/CRUD.tsx
883
- import { useEffect as useEffect6, useRef as useRef3, useState as useState8 } from "react";
888
+ import { useEffect as useEffect6, useMemo as useMemo2, useRef as useRef3, useState as useState8 } from "react";
884
889
 
885
890
  // src/components/Admin/CreateEditForm.tsx
886
891
  import { useState as useState6, useEffect as useEffect4 } from "react";
@@ -1363,7 +1368,9 @@ function CreateEditForm({ isOpen, onClose, apiEndpoint, columns, existingData })
1363
1368
  if (existingData) {
1364
1369
  setFormData(existingData);
1365
1370
  } else {
1366
- setFormData(columns.reduce((acc, col) => ({ ...acc, [col.field]: col.defaultValue || "" }), {}));
1371
+ setFormData(
1372
+ columns.filter((col) => !col.hideInForm).reduce((acc, col) => ({ ...acc, [col.field]: col.defaultValue || "" }), {})
1373
+ );
1367
1374
  }
1368
1375
  }, [existingData, columns]);
1369
1376
  const handleChange = (e, field) => {
@@ -1377,7 +1384,7 @@ function CreateEditForm({ isOpen, onClose, apiEndpoint, columns, existingData })
1377
1384
  };
1378
1385
  const validateForm = () => {
1379
1386
  let newErrors = {};
1380
- columns.forEach((col) => {
1387
+ columns.filter((col) => !col.hideInForm).forEach((col) => {
1381
1388
  if (col.validation?.required && !formData[col.field]) {
1382
1389
  newErrors[col.field] = `${col.displayName} is required`;
1383
1390
  }
@@ -1417,7 +1424,7 @@ Note: ${result.note}`);
1417
1424
  /* @__PURE__ */ jsx17(Button, { type: "button", variant: "ghost", size: "icon", onClick: onClose, className: "h-8 w-8", "aria-label": "Close", children: /* @__PURE__ */ jsx17(X2, { className: "h-5 w-5" }) })
1418
1425
  ] }),
1419
1426
  /* @__PURE__ */ jsxs11("form", { onSubmit: handleSubmit, className: "flex flex-col flex-1 min-h-0", children: [
1420
- /* @__PURE__ */ jsx17("div", { className: "flex-1 min-h-0 overflow-y-auto p-6 space-y-4", children: columns.map((col) => /* @__PURE__ */ jsxs11("div", { children: [
1427
+ /* @__PURE__ */ jsx17("div", { className: "flex-1 min-h-0 overflow-y-auto p-6 space-y-4", children: columns.filter((col) => !col.hideInForm).map((col) => /* @__PURE__ */ jsxs11("div", { children: [
1421
1428
  /* @__PURE__ */ jsx17(Label3, { className: "block text-sm font-medium mb-1", children: col.displayName }),
1422
1429
  col.relationApi ? /* @__PURE__ */ jsx17(
1423
1430
  RelationAutocomplete,
@@ -1429,10 +1436,35 @@ Note: ${result.note}`);
1429
1436
  valueField: col.relationValueField ?? "id",
1430
1437
  placeholder: `Select ${col.displayName}`
1431
1438
  }
1432
- ) : col.type === "textarea" ? /* @__PURE__ */ jsx17(Textarea, { value: formData[col.field] || "", onChange: (e) => handleChange(e, col.field) }) : col.type === "select" ? /* @__PURE__ */ jsxs11(Select, { onValueChange: (value) => setFormData({ ...formData, [col.field]: value }), children: [
1439
+ ) : col.type === "textarea" ? /* @__PURE__ */ jsx17(
1440
+ Textarea,
1441
+ {
1442
+ rows: typeof col.textareaRows === "number" ? col.textareaRows : 4,
1443
+ className: "min-h-[80px] font-mono text-sm",
1444
+ value: formData[col.field] ?? "",
1445
+ onChange: (e) => handleChange(e, col.field),
1446
+ placeholder: col.placeholder
1447
+ }
1448
+ ) : col.type === "select" ? /* @__PURE__ */ jsxs11(Select, { onValueChange: (value) => setFormData({ ...formData, [col.field]: value }), children: [
1433
1449
  /* @__PURE__ */ jsx17(SelectTrigger, { children: /* @__PURE__ */ jsx17(SelectValue, { placeholder: formData[col.field] || "Select an option" }) }),
1434
1450
  /* @__PURE__ */ jsx17(SelectContent, { children: col.options?.map((option) => /* @__PURE__ */ jsx17(SelectItem, { value: option.value, children: option.label }, option.value)) })
1435
- ] }) : col.type === "boolean" ? /* @__PURE__ */ jsx17(Switch, { checked: formData[col.field] || false, onCheckedChange: () => handleToggleChange(col.field) }) : col.type === "date" ? /* @__PURE__ */ jsx17(Input, { type: "date", value: formData[col.field] || "", onChange: (e) => handleChange(e, col.field) }) : col.type === "password" ? /* @__PURE__ */ jsx17(Input, { type: "password", value: formData[col.field] || "", onChange: (e) => handleChange(e, col.field) }) : col.type === "number" ? /* @__PURE__ */ jsx17(Input, { type: "number", value: formData[col.field] || "", onChange: (e) => handleChange(e, col.field) }) : col.type === "file" ? /* @__PURE__ */ jsx17(FileUpload, { onUploadSuccess: (url) => handleFileUpload(col.field, url) }) : /* @__PURE__ */ jsx17(Input, { type: col.type || "text", value: formData[col.field] || "", onChange: (e) => handleChange(e, col.field) }),
1451
+ ] }) : col.type === "boolean" ? /* @__PURE__ */ jsx17(Switch, { checked: formData[col.field] || false, onCheckedChange: () => handleToggleChange(col.field) }) : col.type === "date" ? /* @__PURE__ */ jsx17(Input, { type: "date", value: formData[col.field] || "", onChange: (e) => handleChange(e, col.field) }) : col.type === "password" ? /* @__PURE__ */ jsx17(Input, { type: "password", value: formData[col.field] || "", onChange: (e) => handleChange(e, col.field) }) : col.type === "number" ? /* @__PURE__ */ jsx17(
1452
+ Input,
1453
+ {
1454
+ type: "number",
1455
+ placeholder: col.placeholder,
1456
+ value: formData[col.field] ?? "",
1457
+ onChange: (e) => handleChange(e, col.field)
1458
+ }
1459
+ ) : col.type === "file" ? /* @__PURE__ */ jsx17(FileUpload, { onUploadSuccess: (url) => handleFileUpload(col.field, url) }) : /* @__PURE__ */ jsx17(
1460
+ Input,
1461
+ {
1462
+ type: col.type || "text",
1463
+ placeholder: col.placeholder,
1464
+ value: formData[col.field] ?? "",
1465
+ onChange: (e) => handleChange(e, col.field)
1466
+ }
1467
+ ),
1436
1468
  errors[col.field] && /* @__PURE__ */ jsx17("p", { className: "text-red-500 text-sm mt-1", children: errors[col.field] })
1437
1469
  ] }, col.field)) }),
1438
1470
  /* @__PURE__ */ jsxs11("div", { className: "flex gap-2 p-4 border-t border-gray-200 shrink-0 bg-white", children: [
@@ -2035,6 +2067,10 @@ function AdminCRUD({
2035
2067
  const hasLoadedRef = useRef3(false);
2036
2068
  const isMobile = useIsMobile();
2037
2069
  const showGroupColumn = !!manageUserGroups && roleOptions.length > 0;
2070
+ const listColumns = useMemo2(
2071
+ () => Array.isArray(columns) ? columns.filter((c) => !c.hideInTable) : [],
2072
+ [columns]
2073
+ );
2038
2074
  useEffect6(() => {
2039
2075
  const timeoutId = setTimeout(() => {
2040
2076
  if (searchInput !== searchQuery) {
@@ -2485,7 +2521,7 @@ function AdminCRUD({
2485
2521
  /* @__PURE__ */ jsx22("span", { className: "ml-2", children: "Refreshing list..." })
2486
2522
  ] }),
2487
2523
  isMobile ? /* @__PURE__ */ jsx22("div", { className: "flex flex-col gap-2 min-w-0", children: data && data.length > 0 ? data.map((item, index) => {
2488
- const displayCols = columns?.slice(0, 4) ?? [];
2524
+ const displayCols = listColumns?.slice(0, 4) ?? [];
2489
2525
  const primary = displayCols[0];
2490
2526
  const primaryVal = primary ? getNestedValue(item, primary.field || primary.key) : null;
2491
2527
  return /* @__PURE__ */ jsxs15(
@@ -2559,7 +2595,7 @@ function AdminCRUD({
2559
2595
  );
2560
2596
  }) : /* @__PURE__ */ jsx22("div", { className: "rounded-md border border-gray-200 p-6 text-center text-gray-500", children: "No data found" }) }) : /* @__PURE__ */ jsx22("div", { className: "overflow-x-auto min-w-0 rounded-md border border-gray-200", children: /* @__PURE__ */ jsxs15(Table, { children: [
2561
2597
  /* @__PURE__ */ jsx22(TableHeader, { children: /* @__PURE__ */ jsxs15(TableRow, { children: [
2562
- columns && columns.map((col) => /* @__PURE__ */ jsxs15(
2598
+ listColumns && listColumns.map((col) => /* @__PURE__ */ jsxs15(
2563
2599
  TableHead,
2564
2600
  {
2565
2601
  className: "cursor-pointer",
@@ -2581,7 +2617,7 @@ function AdminCRUD({
2581
2617
  className: "cursor-pointer",
2582
2618
  onClick: () => handleRowClick(item),
2583
2619
  children: [
2584
- columns && columns.map((col, colIndex) => {
2620
+ listColumns && listColumns.map((col, colIndex) => {
2585
2621
  const fieldKey = col.field || col.key;
2586
2622
  const value = getNestedValue(item, fieldKey);
2587
2623
  return /* @__PURE__ */ jsx22(TableCell, { children: formatCellValue(value, col) }, `${item.id}-${colIndex}-${fieldKey}`);
@@ -2665,7 +2701,7 @@ function AdminCRUD({
2665
2701
  )) : /* @__PURE__ */ jsx22(TableRow, { children: /* @__PURE__ */ jsx22(
2666
2702
  TableCell,
2667
2703
  {
2668
- colSpan: columns ? columns.length + 1 + (showGroupColumn ? 1 : 0) : 1,
2704
+ colSpan: listColumns.length ? listColumns.length + 1 + (showGroupColumn ? 1 : 0) : 1,
2669
2705
  className: "text-center py-8",
2670
2706
  children: "No data found"
2671
2707
  }
@@ -3939,7 +3975,7 @@ function TagAutocomplete({
3939
3975
 
3940
3976
  // src/components/Admin/JoditRichText.tsx
3941
3977
  import dynamic from "next/dynamic";
3942
- import { useMemo as useMemo2 } from "react";
3978
+ import { useMemo as useMemo3 } from "react";
3943
3979
  import "jodit/es2021/jodit.min.css";
3944
3980
  import { jsx as jsx28 } from "react/jsx-runtime";
3945
3981
  var JoditEditor = dynamic(() => import("jodit-react").then((m) => m.default), {
@@ -3947,7 +3983,7 @@ var JoditEditor = dynamic(() => import("jodit-react").then((m) => m.default), {
3947
3983
  loading: () => /* @__PURE__ */ jsx28("div", { className: "min-h-[300px] rounded-md border border-gray-200 bg-gray-50 animate-pulse" })
3948
3984
  });
3949
3985
  function JoditRichText({ value, onChange, placeholder, minHeight = 400 }) {
3950
- const config = useMemo2(
3986
+ const config = useMemo3(
3951
3987
  () => ({
3952
3988
  readonly: false,
3953
3989
  placeholder: placeholder ?? "",
@@ -5763,7 +5799,7 @@ function InvitePage() {
5763
5799
 
5764
5800
  // src/admin/pages/DashboardPage.tsx
5765
5801
  import { useSession as useSession4 } from "next-auth/react";
5766
- import { useContext as useContext3, useEffect as useEffect19, useMemo as useMemo3, useState as useState22 } from "react";
5802
+ import { useContext as useContext3, useEffect as useEffect19, useMemo as useMemo4, useState as useState22 } from "react";
5767
5803
  import { useRouter as useRouter6 } from "next/navigation";
5768
5804
  import { Chart as ChartJS2, ArcElement, Tooltip as Tooltip2, Legend as Legend2 } from "chart.js";
5769
5805
  import { Doughnut } from "react-chartjs-2";
@@ -5781,7 +5817,7 @@ function DashboardPage() {
5781
5817
  const [analyticsEnabled, setAnalyticsEnabled] = useState22(false);
5782
5818
  const [days, setDays] = useState22(30);
5783
5819
  const [activeTab, setActiveTab] = useState22("overview");
5784
- const formatMoney4 = useMemo3(
5820
+ const formatMoney4 = useMemo4(
5785
5821
  () => (value) => new Intl.NumberFormat(void 0, {
5786
5822
  style: "currency",
5787
5823
  currency: "INR",
@@ -6460,7 +6496,7 @@ function DashboardPage() {
6460
6496
  }
6461
6497
 
6462
6498
  // src/admin/pages/AdminPageResolver.tsx
6463
- import { useState as useState36, useEffect as useEffect33, useContext as useContext7, useMemo as useMemo4 } from "react";
6499
+ import { useState as useState36, useEffect as useEffect33, useContext as useContext7, useMemo as useMemo5 } from "react";
6464
6500
  import { useRouter as useRouter15 } from "next/navigation";
6465
6501
 
6466
6502
  // src/admin/pages/SubmissionDetailPage.tsx
@@ -9204,8 +9240,25 @@ function PageBuilderPage({ pageId }) {
9204
9240
  }
9205
9241
 
9206
9242
  // src/admin/pages/PluginsPage.tsx
9207
- import { useContext as useContext6, useState as useState30, useEffect as useEffect27 } from "react";
9208
- import { HardDrive, Mail, CreditCard as CreditCard2, MessageCircle, BarChart3 as BarChart32, Building2 as Building22, Puzzle as Puzzle2, CheckCircle2 as CheckCircle22, XCircle, Save as Save5, X as X18, Plus as Plus8, Smartphone } from "lucide-react";
9243
+ import { useContext as useContext6, useState as useState30, useEffect as useEffect27, useCallback as useCallback7 } from "react";
9244
+ import {
9245
+ HardDrive,
9246
+ Mail,
9247
+ CreditCard as CreditCard2,
9248
+ MessageCircle,
9249
+ BarChart3 as BarChart32,
9250
+ Building2 as Building22,
9251
+ Puzzle as Puzzle2,
9252
+ CheckCircle2 as CheckCircle22,
9253
+ XCircle,
9254
+ Save as Save5,
9255
+ X as X18,
9256
+ Plus as Plus8,
9257
+ Smartphone,
9258
+ Bot as Bot2,
9259
+ FileUp,
9260
+ Loader2
9261
+ } from "lucide-react";
9209
9262
 
9210
9263
  // src/lib/email-recipients.ts
9211
9264
  function parseEmailRecipientsFromConfig(raw) {
@@ -9254,6 +9307,10 @@ Checkbox.displayName = CheckboxPrimitive.Root.displayName;
9254
9307
  // src/admin/pages/PluginsPage.tsx
9255
9308
  import { toast as toast5 } from "sonner";
9256
9309
  import { Fragment as Fragment14, jsx as jsx56, jsxs as jsxs46 } from "react/jsx-runtime";
9310
+ function slugifyAgentKey(name) {
9311
+ const s = name.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
9312
+ return s || "agent";
9313
+ }
9257
9314
  function normalizeChatMode(raw) {
9258
9315
  if (raw === "external" || raw === "llm") return raw;
9259
9316
  return "whatsapp";
@@ -9413,6 +9470,25 @@ function PluginSettingsPanel({
9413
9470
  const [chatMode, setChatMode] = useState30("whatsapp");
9414
9471
  const [whatsappPhone, setWhatsappPhone] = useState30("");
9415
9472
  const [externalChatSnippet, setExternalChatSnippet] = useState30("");
9473
+ const [attachedAgentSlug, setAttachedAgentSlug] = useState30("");
9474
+ const [llmAgents, setLlmAgents] = useState30([]);
9475
+ const [agentId, setAgentId] = useState30(null);
9476
+ const [agentName, setAgentName] = useState30("");
9477
+ const [agentSlug, setAgentSlug] = useState30("");
9478
+ const [agentSystem, setAgentSystem] = useState30("");
9479
+ const [agentModel, setAgentModel] = useState30("");
9480
+ const [agentTemp, setAgentTemp] = useState30("");
9481
+ const [agentMaxTokens, setAgentMaxTokens] = useState30("");
9482
+ const [agentValidationJson, setAgentValidationJson] = useState30("");
9483
+ const [agentLoading, setAgentLoading] = useState30(false);
9484
+ const [agentSaving, setAgentSaving] = useState30(false);
9485
+ const [kbCatalog, setKbCatalog] = useState30([]);
9486
+ const [attachedAgentKnowledge, setAttachedAgentKnowledge] = useState30([]);
9487
+ const [attachedKbLoading, setAttachedKbLoading] = useState30(false);
9488
+ const [attachExistingDocId, setAttachExistingDocId] = useState30("__none__");
9489
+ const [uploadingAttachedKbFile, setUploadingAttachedKbFile] = useState30(false);
9490
+ const [uploadingAttachedKbLink, setUploadingAttachedKbLink] = useState30(false);
9491
+ const [attachedKbInputKey, setAttachedKbInputKey] = useState30(0);
9416
9492
  const [erpPipelineName, setErpPipelineName] = useState30("");
9417
9493
  const [erpPipelineStageName, setErpPipelineStageName] = useState30("");
9418
9494
  const [erpFormsCatalog, setErpFormsCatalog] = useState30([]);
@@ -9436,6 +9512,7 @@ function PluginSettingsPanel({
9436
9512
  setIconImageUrl(data.iconImageUrl ?? "");
9437
9513
  setIconBackgroundColor(data.iconBackgroundColor ?? "#6366f1");
9438
9514
  setHeaderColor(data.headerColor ?? "#6366f1");
9515
+ setAttachedAgentSlug(data.attachedAgentSlug ?? "");
9439
9516
  }
9440
9517
  if (isErp) {
9441
9518
  setErpPipelineName(data.pipelineName ?? data.pipelineId ?? "");
@@ -9490,6 +9567,116 @@ function PluginSettingsPanel({
9490
9567
  setErpFormsCatalog(rows);
9491
9568
  }).catch(() => setErpFormsCatalog([]));
9492
9569
  }, [isErp, loading]);
9570
+ const fetchLlmAgents = useCallback7(async () => {
9571
+ const res = await fetch("/api/llm_agents?limit=100&sortField=name&sortOrder=asc");
9572
+ if (!res.ok) {
9573
+ setLlmAgents([]);
9574
+ return;
9575
+ }
9576
+ const j = await res.json();
9577
+ const list = (j.data ?? []).map((r) => ({
9578
+ id: r.id,
9579
+ name: String(r.name ?? ""),
9580
+ slug: String(r.slug ?? ""),
9581
+ enabled: r.enabled !== false
9582
+ }));
9583
+ setLlmAgents(list);
9584
+ const pick = list.find((a) => a.enabled) ?? list[0];
9585
+ const fullRow = (j.data ?? []).find((r) => r.id === pick?.id);
9586
+ if (pick && fullRow) {
9587
+ setAgentId(pick.id);
9588
+ setAgentName(pick.name);
9589
+ setAgentSlug(pick.slug);
9590
+ setAgentSystem(String(fullRow.systemInstruction ?? ""));
9591
+ setAgentModel(String(fullRow.model ?? ""));
9592
+ setAgentTemp(fullRow.temperature != null ? String(fullRow.temperature) : "");
9593
+ setAgentMaxTokens(fullRow.maxTokens != null ? String(fullRow.maxTokens) : "");
9594
+ setAgentValidationJson(String(fullRow.validationRules ?? ""));
9595
+ setAttachedAgentSlug(pick.slug);
9596
+ }
9597
+ }, []);
9598
+ const fetchKbCatalog = useCallback7(async () => {
9599
+ try {
9600
+ const res = await fetch("/api/knowledge_base_documents?limit=300&sortField=name&sortOrder=asc");
9601
+ if (!res.ok) {
9602
+ setKbCatalog([]);
9603
+ return;
9604
+ }
9605
+ const j = await res.json();
9606
+ setKbCatalog(
9607
+ (j.data ?? []).map((r) => ({
9608
+ id: typeof r.id === "number" ? r.id : Number(r.id),
9609
+ name: String(r.name ?? "")
9610
+ })).filter((r) => Number.isInteger(r.id) && r.id > 0)
9611
+ );
9612
+ } catch {
9613
+ setKbCatalog([]);
9614
+ }
9615
+ }, []);
9616
+ const fetchAttachedKnowledge = useCallback7(async (slug) => {
9617
+ if (!slug.trim()) {
9618
+ setAttachedAgentKnowledge([]);
9619
+ return;
9620
+ }
9621
+ setAttachedKbLoading(true);
9622
+ try {
9623
+ const res = await fetch(`/api/llm_agents/${encodeURIComponent(slug.trim())}/knowledge`);
9624
+ if (!res.ok) {
9625
+ setAttachedAgentKnowledge([]);
9626
+ return;
9627
+ }
9628
+ const j = await res.json();
9629
+ setAttachedAgentKnowledge(
9630
+ (j.documents ?? []).map((d) => ({
9631
+ id: typeof d.id === "number" ? d.id : Number(d.id),
9632
+ name: String(d.name ?? "")
9633
+ }))
9634
+ );
9635
+ } catch {
9636
+ setAttachedAgentKnowledge([]);
9637
+ } finally {
9638
+ setAttachedKbLoading(false);
9639
+ }
9640
+ }, []);
9641
+ useEffect27(() => {
9642
+ if (!isLlm || loading || chatMode !== "llm") return;
9643
+ setAgentLoading(true);
9644
+ void (async () => {
9645
+ try {
9646
+ await fetchLlmAgents();
9647
+ const listRes = await fetch("/api/llm_agents?limit=1");
9648
+ const listJ = listRes.ok ? await listRes.json() : { data: [] };
9649
+ if (!listJ.data?.length) {
9650
+ const defaultName = botName.trim() || "Assistant";
9651
+ const defaultSlug = slugifyAgentKey(defaultName);
9652
+ const createRes = await fetch("/api/llm_agents", {
9653
+ method: "POST",
9654
+ headers: { "Content-Type": "application/json" },
9655
+ body: JSON.stringify({ name: defaultName, slug: defaultSlug, systemInstruction: "", enabled: true })
9656
+ });
9657
+ if (createRes.ok) {
9658
+ await fetchLlmAgents();
9659
+ }
9660
+ }
9661
+ } finally {
9662
+ setAgentLoading(false);
9663
+ }
9664
+ })();
9665
+ }, [isLlm, loading, chatMode]);
9666
+ useEffect27(() => {
9667
+ if (!isLlm || loading || chatMode !== "llm") return;
9668
+ void fetchKbCatalog();
9669
+ }, [isLlm, loading, chatMode, fetchKbCatalog]);
9670
+ useEffect27(() => {
9671
+ if (!isLlm || loading || chatMode !== "llm" || !attachedAgentSlug.trim()) {
9672
+ setAttachedAgentKnowledge([]);
9673
+ return;
9674
+ }
9675
+ void fetchAttachedKnowledge(attachedAgentSlug);
9676
+ }, [isLlm, loading, chatMode, attachedAgentSlug, fetchAttachedKnowledge]);
9677
+ useEffect27(() => {
9678
+ setAttachExistingDocId("__none__");
9679
+ }, [attachedAgentSlug]);
9493
9680
  const buildPayload = () => {
9494
9681
  if (isErp) {
9495
9682
  const sortedIds = [...new Set(erpOpportunityFormIds.filter((n) => Number.isInteger(n) && n > 0))].sort(
@@ -9524,6 +9711,7 @@ function PluginSettingsPanel({
9524
9711
  payload.iconImageUrl = { value: iconImageUrl, type: "public" };
9525
9712
  payload.iconBackgroundColor = { value: iconBackgroundColor, type: "public" };
9526
9713
  payload.headerColor = { value: headerColor, type: "public" };
9714
+ payload.attachedAgentSlug = { value: attachedAgentSlug.trim(), type: "public" };
9527
9715
  }
9528
9716
  if (isEmail) {
9529
9717
  payload.salesTeamEmails = { value: serializeEmailRecipients(salesTeamEmails), type: "public" };
@@ -9539,6 +9727,56 @@ function PluginSettingsPanel({
9539
9727
  }
9540
9728
  return payload;
9541
9729
  };
9730
+ const handleSaveAgent = async () => {
9731
+ if (!agentId) {
9732
+ toast5.error("No agent to save");
9733
+ return;
9734
+ }
9735
+ const name = agentName.trim();
9736
+ if (!name) {
9737
+ toast5.error("Agent name is required");
9738
+ return;
9739
+ }
9740
+ const tempRaw = agentTemp.trim();
9741
+ const maxRaw = agentMaxTokens.trim();
9742
+ const temperature = tempRaw === "" ? null : Number(tempRaw);
9743
+ const maxTokens = maxRaw === "" ? null : parseInt(maxRaw, 10);
9744
+ if (tempRaw !== "" && !Number.isFinite(temperature)) {
9745
+ toast5.error("Temperature must be a number");
9746
+ return;
9747
+ }
9748
+ if (maxRaw !== "" && (!Number.isFinite(maxTokens) || maxTokens < 1)) {
9749
+ toast5.error("Max tokens must be a positive integer");
9750
+ return;
9751
+ }
9752
+ setAgentSaving(true);
9753
+ try {
9754
+ const res = await fetch(`/api/llm_agents/${agentId}`, {
9755
+ method: "PUT",
9756
+ headers: { "Content-Type": "application/json" },
9757
+ body: JSON.stringify({
9758
+ name,
9759
+ systemInstruction: agentSystem.trim(),
9760
+ model: agentModel.trim() || null,
9761
+ temperature,
9762
+ maxTokens,
9763
+ validationRules: agentValidationJson.trim() || null,
9764
+ enabled: true
9765
+ })
9766
+ });
9767
+ if (!res.ok) {
9768
+ const err = await res.json().catch(() => ({}));
9769
+ toast5.error(err.error || "Failed to update agent");
9770
+ return;
9771
+ }
9772
+ setAttachedAgentSlug(agentSlug);
9773
+ toast5.success("Agent saved");
9774
+ } catch {
9775
+ toast5.error("Failed to update agent");
9776
+ } finally {
9777
+ setAgentSaving(false);
9778
+ }
9779
+ };
9542
9780
  const handleSave = async () => {
9543
9781
  setSaving(true);
9544
9782
  try {
@@ -9572,6 +9810,78 @@ function PluginSettingsPanel({
9572
9810
  setSaving(false);
9573
9811
  }
9574
9812
  };
9813
+ const uploadKbFileToAttached = async (file) => {
9814
+ const slug = agentSlug.trim() || attachedAgentSlug.trim();
9815
+ if (!slug) {
9816
+ toast5.error("No agent configured");
9817
+ return;
9818
+ }
9819
+ setUploadingAttachedKbFile(true);
9820
+ try {
9821
+ const fd = new FormData();
9822
+ const stem = file.name.replace(/\.[^/.]+$/, "").trim() || "Upload";
9823
+ fd.append("name", stem);
9824
+ fd.append("file", file);
9825
+ const r = await fetch(`/api/llm_agents/${encodeURIComponent(slug)}/knowledge`, { method: "POST", body: fd });
9826
+ if (!r.ok) {
9827
+ const err = await r.json().catch(() => ({}));
9828
+ toast5.error(err.error || "Upload failed");
9829
+ return;
9830
+ }
9831
+ toast5.success("Knowledge added and linked");
9832
+ setAttachedKbInputKey((k) => k + 1);
9833
+ await fetchAttachedKnowledge(slug);
9834
+ await fetchKbCatalog();
9835
+ } finally {
9836
+ setUploadingAttachedKbFile(false);
9837
+ }
9838
+ };
9839
+ const onAttachedKbFileChange = (e) => {
9840
+ const file = e.target.files?.[0];
9841
+ if (!file) return;
9842
+ const slug = agentSlug.trim() || attachedAgentSlug.trim();
9843
+ if (!slug) {
9844
+ toast5.error("No agent configured");
9845
+ e.target.value = "";
9846
+ return;
9847
+ }
9848
+ void uploadKbFileToAttached(file);
9849
+ };
9850
+ const handleAttachExistingToAttached = async () => {
9851
+ const slug = agentSlug.trim() || attachedAgentSlug.trim();
9852
+ if (!slug || attachExistingDocId === "__none__") return;
9853
+ const docId = parseInt(attachExistingDocId, 10);
9854
+ if (!Number.isFinite(docId)) return;
9855
+ setUploadingAttachedKbLink(true);
9856
+ try {
9857
+ const r = await fetch(`/api/llm_agents/${encodeURIComponent(slug)}/knowledge`, {
9858
+ method: "POST",
9859
+ headers: { "Content-Type": "application/json" },
9860
+ body: JSON.stringify({ documentId: docId })
9861
+ });
9862
+ if (!r.ok) {
9863
+ const err = await r.json().catch(() => ({}));
9864
+ toast5.error(err.error || "Attach failed");
9865
+ return;
9866
+ }
9867
+ toast5.success("Document attached");
9868
+ setAttachExistingDocId("__none__");
9869
+ await fetchAttachedKnowledge(slug);
9870
+ } finally {
9871
+ setUploadingAttachedKbLink(false);
9872
+ }
9873
+ };
9874
+ const handleUnlinkKbDoc = async (docId) => {
9875
+ const slug = agentSlug.trim() || attachedAgentSlug.trim();
9876
+ if (!slug) return;
9877
+ const r = await fetch(`/api/llm_agents/${encodeURIComponent(slug)}/knowledge/${docId}`, { method: "DELETE" });
9878
+ if (!r.ok) {
9879
+ toast5.error("Could not remove link");
9880
+ return;
9881
+ }
9882
+ toast5.success("Removed from agent");
9883
+ await fetchAttachedKnowledge(slug);
9884
+ };
9575
9885
  if (loading) return /* @__PURE__ */ jsx56("div", { className: "text-sm text-gray-500 dark:text-gray-400", children: "Loading..." });
9576
9886
  if (isErp) {
9577
9887
  return /* @__PURE__ */ jsxs46("div", { className: "space-y-4", children: [
@@ -10013,8 +10323,206 @@ function PluginSettingsPanel({
10013
10323
  /* @__PURE__ */ jsx56("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: "Only paste code from sources you trust." })
10014
10324
  ] }),
10015
10325
  chatMode === "llm" && /* @__PURE__ */ jsxs46(Fragment14, { children: [
10326
+ /* @__PURE__ */ jsxs46("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: [
10327
+ /* @__PURE__ */ jsxs46("div", { className: "flex items-center gap-2 text-sm font-medium text-gray-900 dark:text-white", children: [
10328
+ /* @__PURE__ */ jsx56(Bot2, { className: "h-4 w-4" }),
10329
+ "Chat assistant agent"
10330
+ ] }),
10331
+ agentLoading ? /* @__PURE__ */ jsxs46("div", { className: "flex items-center gap-2 text-sm text-gray-500", children: [
10332
+ /* @__PURE__ */ jsx56(Loader2, { className: "h-4 w-4 animate-spin" }),
10333
+ "Loading agent\u2026"
10334
+ ] }) : !agentId ? /* @__PURE__ */ jsx56("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: "No agent found. Save settings to auto-create one." }) : /* @__PURE__ */ jsxs46(Fragment14, { children: [
10335
+ /* @__PURE__ */ jsxs46("div", { className: "space-y-1", children: [
10336
+ /* @__PURE__ */ jsx56(Label3, { htmlFor: "agent-name", className: "text-sm", children: "Name" }),
10337
+ /* @__PURE__ */ jsx56(
10338
+ Input,
10339
+ {
10340
+ id: "agent-name",
10341
+ value: agentName,
10342
+ onChange: (e) => setAgentName(e.target.value),
10343
+ placeholder: "e.g. Sales assistant",
10344
+ className: "h-8 text-sm"
10345
+ }
10346
+ )
10347
+ ] }),
10348
+ /* @__PURE__ */ jsxs46("div", { className: "space-y-1", children: [
10349
+ /* @__PURE__ */ jsx56(Label3, { htmlFor: "agent-slug", className: "text-sm", children: "Slug" }),
10350
+ /* @__PURE__ */ jsx56(
10351
+ Input,
10352
+ {
10353
+ id: "agent-slug",
10354
+ value: agentSlug,
10355
+ disabled: true,
10356
+ className: "h-8 text-sm font-mono bg-gray-100 dark:bg-gray-700"
10357
+ }
10358
+ ),
10359
+ /* @__PURE__ */ jsx56("p", { className: "text-[11px] text-gray-500", children: "Auto-generated. Used in API routes." })
10360
+ ] }),
10361
+ /* @__PURE__ */ jsxs46("div", { className: "space-y-1", children: [
10362
+ /* @__PURE__ */ jsx56(Label3, { htmlFor: "agent-system", className: "text-sm", children: "System instruction" }),
10363
+ /* @__PURE__ */ jsx56(
10364
+ Textarea,
10365
+ {
10366
+ id: "agent-system",
10367
+ value: agentSystem,
10368
+ onChange: (e) => setAgentSystem(e.target.value),
10369
+ rows: 5,
10370
+ placeholder: "How the model should behave\u2026",
10371
+ className: "text-sm"
10372
+ }
10373
+ )
10374
+ ] }),
10375
+ /* @__PURE__ */ jsxs46("div", { className: "grid grid-cols-2 gap-2", children: [
10376
+ /* @__PURE__ */ jsxs46("div", { className: "space-y-1", children: [
10377
+ /* @__PURE__ */ jsx56(Label3, { htmlFor: "agent-model", className: "text-sm", children: "Model (optional)" }),
10378
+ /* @__PURE__ */ jsx56(
10379
+ Input,
10380
+ {
10381
+ id: "agent-model",
10382
+ value: agentModel,
10383
+ onChange: (e) => setAgentModel(e.target.value),
10384
+ placeholder: "Gateway model id",
10385
+ className: "h-8 text-sm font-mono"
10386
+ }
10387
+ )
10388
+ ] }),
10389
+ /* @__PURE__ */ jsxs46("div", { className: "space-y-1", children: [
10390
+ /* @__PURE__ */ jsx56(Label3, { htmlFor: "agent-temp", className: "text-sm", children: "Temperature" }),
10391
+ /* @__PURE__ */ jsx56(
10392
+ Input,
10393
+ {
10394
+ id: "agent-temp",
10395
+ value: agentTemp,
10396
+ onChange: (e) => setAgentTemp(e.target.value),
10397
+ placeholder: "e.g. 0.7",
10398
+ className: "h-8 text-sm"
10399
+ }
10400
+ )
10401
+ ] })
10402
+ ] }),
10403
+ /* @__PURE__ */ jsxs46("div", { className: "space-y-1", children: [
10404
+ /* @__PURE__ */ jsx56(Label3, { htmlFor: "agent-max", className: "text-sm", children: "Max tokens" }),
10405
+ /* @__PURE__ */ jsx56(
10406
+ Input,
10407
+ {
10408
+ id: "agent-max",
10409
+ value: agentMaxTokens,
10410
+ onChange: (e) => setAgentMaxTokens(e.target.value.replace(/\D/g, "")),
10411
+ placeholder: "e.g. 1024",
10412
+ className: "h-8 text-sm"
10413
+ }
10414
+ )
10415
+ ] }),
10416
+ /* @__PURE__ */ jsxs46("div", { className: "space-y-1", children: [
10417
+ /* @__PURE__ */ jsx56(Label3, { htmlFor: "agent-validation", className: "text-sm", children: "Validation & output guardrails" }),
10418
+ /* @__PURE__ */ jsx56(
10419
+ Textarea,
10420
+ {
10421
+ id: "agent-validation",
10422
+ value: agentValidationJson,
10423
+ onChange: (e) => setAgentValidationJson(e.target.value),
10424
+ rows: 4,
10425
+ placeholder: 'Plain text or JSON: {"guardrails":"Never promise refunds.","maxUserChars":2000}',
10426
+ className: "text-xs font-mono"
10427
+ }
10428
+ )
10429
+ ] }),
10430
+ /* @__PURE__ */ jsx56(
10431
+ Button,
10432
+ {
10433
+ type: "button",
10434
+ size: "sm",
10435
+ className: "gap-1",
10436
+ disabled: agentSaving,
10437
+ onClick: () => void handleSaveAgent(),
10438
+ children: agentSaving ? /* @__PURE__ */ jsxs46("span", { className: "inline-flex items-center gap-1.5", children: [
10439
+ /* @__PURE__ */ jsx56(Loader2, { className: "h-3.5 w-3.5 animate-spin" }),
10440
+ "Saving\u2026"
10441
+ ] }) : /* @__PURE__ */ jsxs46(Fragment14, { children: [
10442
+ /* @__PURE__ */ jsx56(Save5, { className: "h-3.5 w-3.5" }),
10443
+ "Save agent"
10444
+ ] })
10445
+ }
10446
+ )
10447
+ ] }),
10448
+ agentId && agentSlug.trim() ? /* @__PURE__ */ jsxs46("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: [
10449
+ /* @__PURE__ */ jsxs46("div", { className: "flex items-center gap-2 text-xs font-medium text-gray-800 dark:text-gray-200", children: [
10450
+ /* @__PURE__ */ jsx56(FileUp, { className: "h-3.5 w-3.5 shrink-0" }),
10451
+ "Knowledge for this agent"
10452
+ ] }),
10453
+ /* @__PURE__ */ jsx56("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." }),
10454
+ attachedKbLoading ? /* @__PURE__ */ jsx56("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: "Loading linked documents\u2026" }) : attachedAgentKnowledge.length === 0 ? /* @__PURE__ */ jsx56("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: "No documents linked yet." }) : /* @__PURE__ */ jsx56("ul", { className: "max-h-32 space-y-1.5 overflow-y-auto", children: attachedAgentKnowledge.map((d) => /* @__PURE__ */ jsxs46("li", { className: "flex items-center justify-between gap-2 text-sm", children: [
10455
+ /* @__PURE__ */ jsx56("span", { className: "min-w-0 truncate", title: d.name, children: d.name }),
10456
+ /* @__PURE__ */ jsx56(
10457
+ Button,
10458
+ {
10459
+ type: "button",
10460
+ variant: "ghost",
10461
+ size: "sm",
10462
+ className: "h-7 shrink-0 text-xs text-red-600 hover:text-red-700 dark:text-red-400",
10463
+ onClick: () => void handleUnlinkKbDoc(d.id),
10464
+ children: "Remove"
10465
+ }
10466
+ )
10467
+ ] }, d.id)) }),
10468
+ /* @__PURE__ */ jsxs46("div", { className: "min-w-[180px] space-y-1", children: [
10469
+ /* @__PURE__ */ jsx56(Label3, { className: "text-xs", children: "Upload file" }),
10470
+ /* @__PURE__ */ jsxs46("div", { className: "relative", children: [
10471
+ /* @__PURE__ */ jsx56(
10472
+ Input,
10473
+ {
10474
+ type: "file",
10475
+ accept: ".txt,.md,.json,.pdf,text/plain,text/markdown,application/json,application/pdf",
10476
+ disabled: uploadingAttachedKbFile || uploadingAttachedKbLink,
10477
+ className: "h-8 cursor-pointer text-xs disabled:opacity-60",
10478
+ onChange: onAttachedKbFileChange
10479
+ },
10480
+ attachedKbInputKey
10481
+ ),
10482
+ uploadingAttachedKbFile ? /* @__PURE__ */ jsxs46(
10483
+ "div",
10484
+ {
10485
+ 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",
10486
+ "aria-live": "polite",
10487
+ children: [
10488
+ /* @__PURE__ */ jsx56(Loader2, { className: "h-4 w-4 shrink-0 animate-spin" }),
10489
+ "Saving & linking\u2026"
10490
+ ]
10491
+ }
10492
+ ) : null
10493
+ ] }),
10494
+ /* @__PURE__ */ jsx56("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)." })
10495
+ ] }),
10496
+ /* @__PURE__ */ jsxs46("div", { className: "flex flex-wrap items-end gap-2", children: [
10497
+ /* @__PURE__ */ jsxs46("div", { className: "min-w-[200px] flex-1 space-y-1", children: [
10498
+ /* @__PURE__ */ jsx56(Label3, { className: "text-xs", children: "Attach existing document" }),
10499
+ /* @__PURE__ */ jsxs46(Select, { value: attachExistingDocId, onValueChange: setAttachExistingDocId, children: [
10500
+ /* @__PURE__ */ jsx56(SelectTrigger, { className: "h-8 text-xs", children: /* @__PURE__ */ jsx56(SelectValue, { placeholder: "Choose a document" }) }),
10501
+ /* @__PURE__ */ jsxs46(SelectContent, { children: [
10502
+ /* @__PURE__ */ jsx56(SelectItem, { value: "__none__", children: "\u2014 Select \u2014" }),
10503
+ kbCatalog.filter((d) => !attachedAgentKnowledge.some((a) => a.id === d.id)).map((d) => /* @__PURE__ */ jsx56(SelectItem, { value: String(d.id), children: d.name }, d.id))
10504
+ ] })
10505
+ ] })
10506
+ ] }),
10507
+ /* @__PURE__ */ jsx56(
10508
+ Button,
10509
+ {
10510
+ type: "button",
10511
+ size: "sm",
10512
+ className: "h-8",
10513
+ disabled: uploadingAttachedKbFile || uploadingAttachedKbLink || attachExistingDocId === "__none__",
10514
+ onClick: () => void handleAttachExistingToAttached(),
10515
+ children: uploadingAttachedKbLink ? /* @__PURE__ */ jsxs46("span", { className: "inline-flex items-center gap-1.5", children: [
10516
+ /* @__PURE__ */ jsx56(Loader2, { className: "h-3.5 w-3.5 animate-spin" }),
10517
+ "Linking\u2026"
10518
+ ] }) : "Attach"
10519
+ }
10520
+ )
10521
+ ] })
10522
+ ] }) : null
10523
+ ] }),
10016
10524
  /* @__PURE__ */ jsxs46("div", { className: "space-y-1", children: [
10017
- /* @__PURE__ */ jsx56(Label3, { htmlFor: `${settingsGroup}-botName`, className: "text-sm", children: "Name" }),
10525
+ /* @__PURE__ */ jsx56(Label3, { htmlFor: `${settingsGroup}-botName`, className: "text-sm", children: "Widget title" }),
10018
10526
  /* @__PURE__ */ jsx56(
10019
10527
  Input,
10020
10528
  {
@@ -11770,7 +12278,7 @@ function CollectionEditPage({ collectionId }) {
11770
12278
  }
11771
12279
 
11772
12280
  // src/admin/pages/RolesPage.tsx
11773
- import { useCallback as useCallback7, useEffect as useEffect32, useState as useState35 } from "react";
12281
+ import { useCallback as useCallback8, useEffect as useEffect32, useState as useState35 } from "react";
11774
12282
  import { useSession as useSession5 } from "next-auth/react";
11775
12283
  import { Shield as Shield2, Save as Save9, Trash2 as Trash27 } from "lucide-react";
11776
12284
 
@@ -11814,7 +12322,7 @@ function RolesPage() {
11814
12322
  const [newName, setNewName] = useState35("");
11815
12323
  const [deleteRoleOpen, setDeleteRoleOpen] = useState35(false);
11816
12324
  const [error, setError] = useState35(null);
11817
- const load = useCallback7(async () => {
12325
+ const load = useCallback8(async () => {
11818
12326
  setLoading(true);
11819
12327
  setError(null);
11820
12328
  try {
@@ -12350,6 +12858,39 @@ var CRUD_CONFIGS = {
12350
12858
  { field: "createdAt", displayName: "Created", type: "date" }
12351
12859
  ],
12352
12860
  addEditPageUrl: ""
12861
+ },
12862
+ llm_agents: {
12863
+ title: "LLM agents",
12864
+ apiEndpoint: "/api/llm_agents",
12865
+ defaultSortField: "name",
12866
+ defaultSortOrder: "asc",
12867
+ columns: [
12868
+ { field: "name", displayName: "Name" },
12869
+ { field: "slug", displayName: "Slug" },
12870
+ {
12871
+ field: "systemInstruction",
12872
+ displayName: "System instruction",
12873
+ type: "textarea",
12874
+ hideInTable: true,
12875
+ textareaRows: 10,
12876
+ placeholder: "How the model should behave for this agent (sent as system prompt to the LLM)."
12877
+ },
12878
+ { field: "model", displayName: "Model", placeholder: "Optional gateway model id" },
12879
+ { field: "temperature", displayName: "Temperature", type: "number" },
12880
+ { field: "maxTokens", displayName: "Max tokens", type: "number" },
12881
+ {
12882
+ field: "validationRules",
12883
+ displayName: "Validation & output guardrails",
12884
+ type: "textarea",
12885
+ hideInTable: true,
12886
+ textareaRows: 8,
12887
+ placeholder: "Plain text: rules appended to the system prompt. Or JSON: guardrails, maxUserChars, blockedSubstrings, etc."
12888
+ },
12889
+ { field: "enabled", displayName: "Enabled", type: "boolean" },
12890
+ { field: "createdAt", displayName: "Created", type: "date", hideInForm: true },
12891
+ { field: "updatedAt", displayName: "Updated", type: "datetime", hideInForm: true }
12892
+ ],
12893
+ addEditPageUrl: ""
12353
12894
  }
12354
12895
  };
12355
12896
  function BlogEditorWrapper({ blogId }) {
@@ -12431,7 +12972,7 @@ function AdminPageResolver({ slug }) {
12431
12972
  { field: "orderCount", displayName: "Orders" },
12432
12973
  { field: "totalPaid", displayName: "Total paid" }
12433
12974
  ] : crud.columns;
12434
- const extraListParams = useMemo4(
12975
+ const extraListParams = useMemo5(
12435
12976
  () => isContactsWithStore ? { includeSummary: "1" } : void 0,
12436
12977
  [isContactsWithStore]
12437
12978
  );