@decido/discovery-studio 0.1.0 → 4.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # 🚀 @decido/discovery-studio
2
+
3
+ > WhatsApp Discovery Studio — Enterprise-grade conversational analytics, flow design, and live chat
4
+
5
+ Bienvenido a la documentación oficial de **@decido/discovery-studio**, un componente integral del ecosistema **Decido OS**.
6
+
7
+ ## 📦 Instalación
8
+
9
+ Para aprovisionar este módulo dentro de otra área del monorepo o consumirlo remotamente:
10
+
11
+ ```bash
12
+ npm install @decido/discovery-studio
13
+ # o mediante el gestor oficial del monorepo
14
+ pnpm add @decido/discovery-studio
15
+ ```
16
+
17
+ ## 🔧 Estructura y Dependencias
18
+
19
+ Este paquete está diseñado para interoperar de forma nativa con la infraestructura central.
20
+ Para su correcto funcionamiento en un entorno aislado (Sandboxed), se apoya en los siguientes cimientos tecnológicos:
21
+
22
+ - `react`
23
+ - `react-dom`
24
+ - `@decido/canvas-ai`
25
+ - `@decido/kernel-bridge`
26
+ - `@decido/macia-core`
27
+
28
+ ## 🔐 Licencia y Privacidad
29
+ El código de este componente se encuentra auditado y restringido (Sin Sourcemaps).
30
+ Propiedad Intelectual Protegida - Framework Decido OS.
31
+ Distribuido bajo licencia **UNLICENSED**.
package/dist/index.d.mts CHANGED
@@ -67,7 +67,7 @@ interface FlowState {
67
67
  edges: any[];
68
68
  }
69
69
  interface StudioState {
70
- activeTab: 'chat' | 'flow' | 'analytics';
70
+ activeTab: 'chat' | 'flow' | 'analytics' | 'architect';
71
71
  selectedContact: string | null;
72
72
  explorerOpen: boolean;
73
73
  inspectorOpen: boolean;
package/dist/index.d.ts CHANGED
@@ -67,7 +67,7 @@ interface FlowState {
67
67
  edges: any[];
68
68
  }
69
69
  interface StudioState {
70
- activeTab: 'chat' | 'flow' | 'analytics';
70
+ activeTab: 'chat' | 'flow' | 'analytics' | 'architect';
71
71
  selectedContact: string | null;
72
72
  explorerOpen: boolean;
73
73
  inspectorOpen: boolean;
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var React8 = require('react');
3
+ var React9 = require('react');
4
4
  var socket_ioClient = require('socket.io-client');
5
5
  var jsxRuntime = require('react/jsx-runtime');
6
6
  var kernelBridge = require('@decido/kernel-bridge');
@@ -11,7 +11,7 @@ var lucideReact = require('lucide-react');
11
11
 
12
12
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
13
13
 
14
- var React8__default = /*#__PURE__*/_interopDefault(React8);
14
+ var React9__default = /*#__PURE__*/_interopDefault(React9);
15
15
 
16
16
  // src/DiscoveryStudio.tsx
17
17
  var API_URL = undefined?.VITE_API_BASE_URL || "http://localhost:3001";
@@ -34,7 +34,7 @@ var Section = ({
34
34
  defaultOpen = true,
35
35
  children
36
36
  }) => {
37
- const [open, setOpen] = React8.useState(defaultOpen);
37
+ const [open, setOpen] = React9.useState(defaultOpen);
38
38
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-b", style: { borderColor: "var(--border-color)" }, children: [
39
39
  /* @__PURE__ */ jsxRuntime.jsxs(
40
40
  "button",
@@ -54,9 +54,9 @@ var Section = ({
54
54
  ] });
55
55
  };
56
56
  var SideExplorer = ({ selectedContact, onSelectContact }) => {
57
- const [filter, setFilter] = React8.useState("");
58
- const [conversations, setConversations] = React8.useState([]);
59
- React8.useEffect(() => {
57
+ const [filter, setFilter] = React9.useState("");
58
+ const [conversations, setConversations] = React9.useState([]);
59
+ React9.useEffect(() => {
60
60
  const fetchConversations = async () => {
61
61
  try {
62
62
  const res = await fetch(`${API_URL}/api/whatsapp/conversations`);
@@ -129,10 +129,7 @@ var SideExplorer = ({ selectedContact, onSelectContact }) => {
129
129
  ),
130
130
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
131
131
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
132
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-bold truncate", style: { color: "var(--text-primary)" }, children: [
133
- "+",
134
- conv.phone
135
- ] }),
132
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-bold truncate", style: { color: "var(--text-primary)" }, children: conv.profile_name || "+" + conv.phone }),
136
133
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[9px] shrink-0 ml-1", style: { color: "var(--text-secondary)" }, children: conv.last_activity.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) })
137
134
  ] }),
138
135
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[10px] truncate mt-0.5", style: { color: "var(--text-secondary)" }, children: conv.last_message || "Media" })
@@ -189,9 +186,9 @@ var CONTACT_DETAILS = {
189
186
  tags: ["VIP", "Frecuente", "Muebler\xEDa"],
190
187
  timeline: [
191
188
  { time: "2m", event: "Pregunt\xF3 por estado de pedido", icon: "fas fa-comment" },
192
- { time: "1d", event: "Pedido PED-1847 en PRODUCCI\xD3N", icon: "fas fa-industry" },
193
- { time: "3d", event: "Confirm\xF3 cotizaci\xF3n mesa roble", icon: "fas fa-check" },
194
- { time: "1w", event: "Solicit\xF3 cat\xE1logo digital", icon: "fas fa-book" }
189
+ { time: "1d", event: "Pedido PED-1847 en PRODUCTION", icon: "fas fa-industry" },
190
+ { time: "3d", event: "Transici\xF3n a IN_CUTTING (Control)", icon: "fas fa-check-circle" },
191
+ { time: "1w", event: "Tracking Link Generado", icon: "fas fa-link" }
195
192
  ]
196
193
  },
197
194
  c2: {
@@ -206,7 +203,7 @@ var CONTACT_DETAILS = {
206
203
  tags: ["Empresa", "Nuevo"],
207
204
  timeline: [
208
205
  { time: "15m", event: "Solicit\xF3 cotizaci\xF3n de mesa", icon: "fas fa-comment" },
209
- { time: "2d", event: "Primer contacto v\xEDa WhatsApp", icon: "fas fa-phone" }
206
+ { time: "2d", event: "RuleEngine Trigger: NEW Order", icon: "fas fa-bolt" }
210
207
  ]
211
208
  }
212
209
  };
@@ -306,25 +303,64 @@ var InspectorPanel = ({ selectedContact }) => {
306
303
  ] });
307
304
  };
308
305
  var AgentPanel = () => {
309
- const [messages, setMessages] = React8.useState([
306
+ const [messages, setMessages] = React9.useState([
310
307
  { id: "1", text: "Soy tu Agente Vocal asignado. \xBFEn qu\xE9 te ayudo con tus flujos?", type: "system" }
311
308
  ]);
312
- const [input, setInput] = React8.useState("");
313
- const [isTyping, setIsTyping] = React8.useState(false);
314
- const scrollRef = React8.useRef(null);
315
- React8.useEffect(() => {
309
+ const [input, setInput] = React9.useState("");
310
+ const [isTyping, setIsTyping] = React9.useState(false);
311
+ const scrollRef = React9.useRef(null);
312
+ React9.useEffect(() => {
316
313
  if (scrollRef.current) {
317
314
  scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
318
315
  }
319
316
  }, [messages]);
320
- React8.useEffect(() => {
317
+ React9.useEffect(() => {
321
318
  const unsubscribe = kernelBridge.kernel.onEvent((payload) => {
322
319
  if (payload.event_type === "message_sent" && payload.data?.target === "vocal_domain_agent") ; else if (payload.event_type === "message_sent" && payload.data?.intent === "vocal_response") {
323
320
  setMessages((prev) => [...prev, { id: Date.now().toString(), text: payload.data.payload, type: "agent" }]);
324
321
  setIsTyping(false);
325
322
  }
326
323
  });
327
- return () => unsubscribe();
324
+ let es = null;
325
+ try {
326
+ es = new EventSource("http://localhost:3001/api/events");
327
+ es.addEventListener("whatsapp:message_received", (e) => {
328
+ try {
329
+ const data = JSON.parse(e.data);
330
+ setMessages((prev) => [...prev, {
331
+ id: Date.now().toString(),
332
+ text: `\u{1F4F1} [WA] ${data.profileName || data.from}: ${data.text || "Archivo Adjunto"}`,
333
+ type: "user"
334
+ }]);
335
+ setIsTyping(true);
336
+ setTimeout(() => {
337
+ setMessages((p) => [...p, { id: Date.now().toString(), text: "Regla disparada: Mensaje recibido. Evaluando intenci\xF3n...", type: "system" }]);
338
+ setIsTyping(false);
339
+ }, 1e3);
340
+ } catch (e2) {
341
+ }
342
+ });
343
+ es.addEventListener("whatsapp:status_update", (e) => {
344
+ try {
345
+ const data = JSON.parse(e.data);
346
+ setMessages((prev) => [...prev, {
347
+ id: Date.now().toString(),
348
+ text: `\u2714\uFE0F [Estado WA] ${data.status} a ${data.recipient_id}`,
349
+ type: "system"
350
+ }]);
351
+ } catch (e2) {
352
+ }
353
+ });
354
+ es.onerror = () => {
355
+ es?.close();
356
+ };
357
+ } catch (e) {
358
+ console.log("SSE Error, falling back to local kernel");
359
+ }
360
+ return () => {
361
+ unsubscribe();
362
+ if (es) es.close();
363
+ };
328
364
  }, []);
329
365
  const handleSend = async () => {
330
366
  if (!input.trim()) return;
@@ -412,12 +448,12 @@ var AgentPanel = () => {
412
448
  };
413
449
  var API_URL2 = undefined?.VITE_API_BASE_URL || "http://localhost:3001";
414
450
  var ChatTab = ({ selectedContact }) => {
415
- const [messages, setMessages] = React8.useState([]);
416
- const [input, setInput] = React8.useState("");
417
- const [opMode, setOpMode] = React8.useState("COPILOT");
418
- const [socket, setSocket] = React8.useState(null);
419
- const chatEndRef = React8.useRef(null);
420
- const fetchMessages = React8.useCallback(async () => {
451
+ const [messages, setMessages] = React9.useState([]);
452
+ const [input, setInput] = React9.useState("");
453
+ const [opMode, setOpMode] = React9.useState("COPILOT");
454
+ const [socket, setSocket] = React9.useState(null);
455
+ const chatEndRef = React9.useRef(null);
456
+ const fetchMessages = React9.useCallback(async () => {
421
457
  if (!selectedContact) {
422
458
  setMessages([]);
423
459
  return;
@@ -438,7 +474,7 @@ var ChatTab = ({ selectedContact }) => {
438
474
  console.error("Failed to fetch messages:", error);
439
475
  }
440
476
  }, [selectedContact]);
441
- React8.useEffect(() => {
477
+ React9.useEffect(() => {
442
478
  fetchMessages();
443
479
  const s = socket_ioClient.io(API_URL2, { transports: ["websocket", "polling"] });
444
480
  s.on("connect", () => console.log("[ChatTab] Connected to WebSocket"));
@@ -460,7 +496,7 @@ var ChatTab = ({ selectedContact }) => {
460
496
  s.disconnect();
461
497
  };
462
498
  }, [selectedContact, fetchMessages]);
463
- React8.useEffect(() => {
499
+ React9.useEffect(() => {
464
500
  chatEndRef.current?.scrollIntoView({ behavior: "smooth" });
465
501
  }, [messages]);
466
502
  const handleSend = async () => {
@@ -666,7 +702,7 @@ var MESSAGE_CONFIG = {
666
702
  outgoing: { icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Reply, { size: 14 }), colorClass: "text-blue-400", bgClass: "from-blue-500/10 to-transparent" },
667
703
  system: { icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Info, { size: 14 }), colorClass: "text-purple-400", bgClass: "from-purple-500/10 to-transparent" }
668
704
  };
669
- var MessageNode = React8.memo(({ id, data, selected }) => {
705
+ var MessageNode = React9.memo(({ id, data, selected }) => {
670
706
  const nodeData = data || {};
671
707
  const config = MESSAGE_CONFIG[nodeData.type] || MESSAGE_CONFIG.incoming;
672
708
  const titleElement = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center w-full min-w-[140px]", children: [
@@ -699,7 +735,7 @@ var ACTION_CONFIG = {
699
735
  analyze: { icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Brain, { size: 14 }), colorClass: "text-violet-400", bgClass: "from-violet-500/10 to-transparent" },
700
736
  decision: { icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.GitBranch, { size: 14 }), colorClass: "text-cyan-400", bgClass: "from-cyan-500/10 to-transparent" }
701
737
  };
702
- var BotActionNode = React8.memo(({ id, data, selected }) => {
738
+ var BotActionNode = React9.memo(({ id, data, selected }) => {
703
739
  const nodeData = data || {};
704
740
  const config = ACTION_CONFIG[nodeData?.actionType] || ACTION_CONFIG.reply;
705
741
  const titleElement = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
@@ -725,6 +761,56 @@ var BotActionNode = React8.memo(({ id, data, selected }) => {
725
761
  );
726
762
  });
727
763
  BotActionNode.displayName = "BotActionNode";
764
+ var TrackingPreview = ({ token = "demo-token-123", onClose }) => {
765
+ const trackingUrl = `http://localhost:3001/track/${token}`;
766
+ const [isLoading, setIsLoading] = React9.useState(true);
767
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 z-[100] flex items-center justify-center bg-black/60 backdrop-blur-sm p-4 animate-in fade-in", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full max-w-[400px] bg-[#0E1525] rounded-[24px] overflow-hidden border border-white/10 shadow-2xl flex flex-col h-[700px] max-h-[90vh] relative animate-in zoom-in-95", children: [
768
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute top-4 right-4 z-10 flex gap-2", children: [
769
+ /* @__PURE__ */ jsxRuntime.jsx(
770
+ "button",
771
+ {
772
+ onClick: () => window.open(trackingUrl, "_blank"),
773
+ className: "w-8 h-8 rounded-full bg-white/10 backdrop-blur-md flex items-center justify-center text-white/70 hover:text-white hover:bg-white/20 transition-colors",
774
+ title: "Abrir en nueva pesta\xF1a",
775
+ children: /* @__PURE__ */ jsxRuntime.jsx("i", { className: "fas fa-external-link-alt text-xs" })
776
+ }
777
+ ),
778
+ /* @__PURE__ */ jsxRuntime.jsx(
779
+ "button",
780
+ {
781
+ onClick: onClose,
782
+ className: "w-8 h-8 rounded-full bg-white/10 backdrop-blur-md flex items-center justify-center text-white/70 hover:text-white hover:bg-white/20 transition-colors",
783
+ title: "Cerrar vista previa",
784
+ children: /* @__PURE__ */ jsxRuntime.jsx("i", { className: "fas fa-times text-xs" })
785
+ }
786
+ )
787
+ ] }),
788
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-7 w-full bg-black flex justify-center items-start shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-[120px] h-5 bg-black rounded-b-xl" }) }),
789
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-2 bg-black/50 border-b border-white/5 flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white/5 rounded-lg px-3 py-1 flex items-center gap-2 text-[10px] text-white/40 w-fit", children: [
790
+ /* @__PURE__ */ jsxRuntime.jsx("i", { className: "fas fa-lock text-[8px]" }),
791
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
792
+ "decido.com/track/",
793
+ token
794
+ ] })
795
+ ] }) }),
796
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 relative bg-black", children: [
797
+ isLoading && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute inset-0 flex flex-col items-center justify-center bg-[#0E1525] text-white/50", children: [
798
+ /* @__PURE__ */ jsxRuntime.jsx("i", { className: "fas fa-circle-notch fa-spin text-2xl text-emerald-500 mb-3" }),
799
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium", children: "Cargando vista m\xF3vil..." })
800
+ ] }),
801
+ /* @__PURE__ */ jsxRuntime.jsx(
802
+ "iframe",
803
+ {
804
+ src: trackingUrl,
805
+ className: `w-full h-full border-none transition-opacity duration-300 ${isLoading ? "opacity-0" : "opacity-100"}`,
806
+ onLoad: () => setIsLoading(false),
807
+ title: "Tracking Portal Preview"
808
+ }
809
+ )
810
+ ] }),
811
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-6 w-full bg-black flex justify-center items-center shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-[120px] h-1 bg-white/20 rounded-full" }) })
812
+ ] }) });
813
+ };
728
814
  var nodeTypes = {
729
815
  messageNode: MessageNode,
730
816
  botAction: BotActionNode
@@ -742,14 +828,14 @@ var initialNodes = [
742
828
  id: "ai-intent",
743
829
  type: "botAction",
744
830
  position: { x: 400, y: 280 },
745
- data: { label: "Agente IA: Reconocimiento", actionType: "analyze", description: 'Detectar Intenci\xF3n: "Estado de Pedido"' }
831
+ data: { label: "Reconocimiento Intenci\xF3n IA", actionType: "analyze", description: 'Detectar Intenci\xF3n: "Estado de Pedido"' }
746
832
  },
747
833
  // Database Check
748
834
  {
749
835
  id: "db-check",
750
836
  type: "botAction",
751
837
  position: { x: 750, y: 150 },
752
- data: { label: "Consultar ERP", actionType: "decision", description: "API REST: /orders/XJ-901" }
838
+ data: { label: "Acci\xF3n Ejecutada", actionType: "decision", description: "Action: GENERATE_TRACKING_LINK" }
753
839
  },
754
840
  {
755
841
  id: "escalate-human",
@@ -762,13 +848,13 @@ var initialNodes = [
762
848
  id: "reply-status",
763
849
  type: "messageNode",
764
850
  position: { x: 1050, y: 150 },
765
- data: { label: "Respuesta Autom\xE1tica WhatsApp", type: "outgoing", text: 'Tu pedido XJ-901 est\xE1 "En Producci\xF3n" y se entregar\xE1 el Viernes.', time: "10:06 AM" }
851
+ data: { label: "Regla: SEND_WA_TEMPLATE", type: "outgoing", text: "Template: pedido_produccion\nPar\xE1metros: [legacyId, trackUrl]", time: "10:06 AM" }
766
852
  },
767
853
  {
768
854
  id: "assign-agent",
769
855
  type: "messageNode",
770
856
  position: { x: 1050, y: 400 },
771
- data: { label: "Soporte Asignado", type: "system", text: "Asignando Chat #3421 al Asesor de Ventas: Carlos" }
857
+ data: { label: "Soporte Asignado", type: "system", text: "Asignando Chat #3421 (Humano en la ruta)" }
772
858
  }
773
859
  ];
774
860
  var initialEdges = [
@@ -780,15 +866,21 @@ var initialEdges = [
780
866
  { id: "e6", source: "escalate-human", target: "assign-agent", style: { stroke: "#f59e0b" }, markerEnd: { type: react.MarkerType.ArrowClosed, color: "#f59e0b" } }
781
867
  ];
782
868
  var FlowCanvasTab = () => {
783
- const [nodes, setNodes] = React8.useState(initialNodes);
784
- const [edges, setEdges] = React8.useState(initialEdges);
785
- const onNodesChange = React8.useCallback((changes) => {
869
+ const [nodes, setNodes] = React9.useState(initialNodes);
870
+ const [edges, setEdges] = React9.useState(initialEdges);
871
+ const [previewOpen, setPreviewOpen] = React9.useState(false);
872
+ const onNodesChange = React9.useCallback((changes) => {
786
873
  }, []);
787
- const onEdgesChange = React8.useCallback((changes) => {
874
+ const onEdgesChange = React9.useCallback((changes) => {
788
875
  }, []);
789
- const onConnect = React8.useCallback((params) => {
876
+ const onConnect = React9.useCallback((params) => {
790
877
  setEdges((eds) => [...eds, { ...params, id: `e-${Date.now()}`, animated: true }]);
791
878
  }, [setEdges]);
879
+ const onNodeClick = React9.useCallback((event, node) => {
880
+ if (node.data?.description?.includes("GENERATE_TRACKING_LINK") || node.data?.text?.includes("trackUrl")) {
881
+ setPreviewOpen(true);
882
+ }
883
+ }, []);
792
884
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-h-0 flex flex-col overflow-hidden", children: [
793
885
  /* @__PURE__ */ jsxRuntime.jsxs(
794
886
  "div",
@@ -840,9 +932,11 @@ var FlowCanvasTab = () => {
840
932
  onNodesChange,
841
933
  onEdgesChange,
842
934
  onConnect,
935
+ onNodeClick,
843
936
  nodeTypes
844
937
  }
845
- ) })
938
+ ) }),
939
+ previewOpen && /* @__PURE__ */ jsxRuntime.jsx(TrackingPreview, { onClose: () => setPreviewOpen(false) })
846
940
  ] });
847
941
  };
848
942
  var HOURLY_DATA = [
@@ -901,8 +995,8 @@ var BarChart = ({ data }) => {
901
995
  ] }, i)) });
902
996
  };
903
997
  var AnalyticsTab = () => {
904
- const [animatedValues, setAnimatedValues] = React8.useState({});
905
- React8.useEffect(() => {
998
+ const [animatedValues, setAnimatedValues] = React9.useState({});
999
+ React9.useEffect(() => {
906
1000
  const timer = setTimeout(() => {
907
1001
  const vals = {};
908
1002
  TOP_INTENTS.forEach((intent) => {
@@ -999,7 +1093,7 @@ var AnalyticsTab = () => {
999
1093
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-1", style: { gridTemplateColumns: "auto repeat(24, 1fr)" }, children: [
1000
1094
  /* @__PURE__ */ jsxRuntime.jsx("div", {}),
1001
1095
  Array.from({ length: 24 }, (_, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[7px] text-center font-bold", style: { color: "var(--text-secondary)" }, children: i }, i)),
1002
- ["Lun", "Mar", "Mi\xE9", "Jue", "Vie", "S\xE1b", "Dom"].map((day, di) => /* @__PURE__ */ jsxRuntime.jsxs(React8__default.default.Fragment, { children: [
1096
+ ["Lun", "Mar", "Mi\xE9", "Jue", "Vie", "S\xE1b", "Dom"].map((day, di) => /* @__PURE__ */ jsxRuntime.jsxs(React9__default.default.Fragment, { children: [
1003
1097
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[9px] font-bold pr-2 flex items-center", style: { color: "var(--text-secondary)" }, children: day }),
1004
1098
  Array.from({ length: 24 }, (_, hi) => {
1005
1099
  const intensity = Math.random();
@@ -1024,20 +1118,20 @@ var AnalyticsTab = () => {
1024
1118
  ] });
1025
1119
  };
1026
1120
  var ApiTesterTab = () => {
1027
- const [phone, setPhone] = React8.useState("");
1028
- const [msgType, setMsgType] = React8.useState("text");
1029
- const [payloadText, setPayloadText] = React8.useState("Hola desde Decido OS \u{1F680}");
1030
- const [templateName, setTemplateName] = React8.useState("hello_world");
1031
- const [templateLanguage, setTemplateLanguage] = React8.useState("en_US");
1032
- const [loading, setLoading] = React8.useState(false);
1033
- const [result, setResult] = React8.useState(null);
1034
- const [savedPhones, setSavedPhones] = React8.useState([]);
1035
- const [newPhoneName, setNewPhoneName] = React8.useState("");
1036
- const [showPhoneSave, setShowPhoneSave] = React8.useState(false);
1037
- const [templateVariables, setTemplateVariables] = React8.useState([]);
1038
- const [templates, setTemplates] = React8.useState([]);
1039
- const [fetchingTemplates, setFetchingTemplates] = React8.useState(false);
1040
- React8__default.default.useEffect(() => {
1121
+ const [phone, setPhone] = React9.useState("");
1122
+ const [msgType, setMsgType] = React9.useState("text");
1123
+ const [payloadText, setPayloadText] = React9.useState("Hola desde Decido OS \u{1F680}");
1124
+ const [templateName, setTemplateName] = React9.useState("hello_world");
1125
+ const [templateLanguage, setTemplateLanguage] = React9.useState("en_US");
1126
+ const [loading, setLoading] = React9.useState(false);
1127
+ const [result, setResult] = React9.useState(null);
1128
+ const [savedPhones, setSavedPhones] = React9.useState([]);
1129
+ const [newPhoneName, setNewPhoneName] = React9.useState("");
1130
+ const [showPhoneSave, setShowPhoneSave] = React9.useState(false);
1131
+ const [templateVariables, setTemplateVariables] = React9.useState([]);
1132
+ const [templates, setTemplates] = React9.useState([]);
1133
+ const [fetchingTemplates, setFetchingTemplates] = React9.useState(false);
1134
+ React9__default.default.useEffect(() => {
1041
1135
  const saved = localStorage.getItem("wa_api_tester_phones");
1042
1136
  if (saved) {
1043
1137
  try {
@@ -1047,7 +1141,7 @@ var ApiTesterTab = () => {
1047
1141
  }
1048
1142
  fetchTemplates();
1049
1143
  }, []);
1050
- React8__default.default.useEffect(() => {
1144
+ React9__default.default.useEffect(() => {
1051
1145
  const selected = templates.find((t) => t.name === templateName && t.language === templateLanguage);
1052
1146
  if (selected) {
1053
1147
  const bodyComponent = selected.components?.find((c) => c.type === "BODY" || c.type === "body");
@@ -1340,18 +1434,18 @@ var ApiTesterTab = () => {
1340
1434
  ] }) });
1341
1435
  };
1342
1436
  var TemplateManagerTab = () => {
1343
- const [templates, setTemplates] = React8.useState([]);
1344
- const [loading, setLoading] = React8.useState(true);
1345
- const [error, setError] = React8.useState(null);
1346
- const [creating, setCreating] = React8.useState(false);
1347
- const [name, setName] = React8.useState("");
1348
- const [category, setCategory] = React8.useState("UTILITY");
1349
- const [language, setLanguage] = React8.useState("es");
1350
- const [headerText, setHeaderText] = React8.useState("");
1351
- const [bodyText, setBodyText] = React8.useState("Hola {{1}}, tu pedido est\xE1 listo.");
1352
- const [footerText, setFooterText] = React8.useState("");
1353
- const [buttons, setButtons] = React8.useState([]);
1354
- React8.useEffect(() => {
1437
+ const [templates, setTemplates] = React9.useState([]);
1438
+ const [loading, setLoading] = React9.useState(true);
1439
+ const [error, setError] = React9.useState(null);
1440
+ const [creating, setCreating] = React9.useState(false);
1441
+ const [name, setName] = React9.useState("");
1442
+ const [category, setCategory] = React9.useState("UTILITY");
1443
+ const [language, setLanguage] = React9.useState("es");
1444
+ const [headerText, setHeaderText] = React9.useState("");
1445
+ const [bodyText, setBodyText] = React9.useState("Hola {{1}}, tu pedido est\xE1 listo.");
1446
+ const [footerText, setFooterText] = React9.useState("");
1447
+ const [buttons, setButtons] = React9.useState([]);
1448
+ React9.useEffect(() => {
1355
1449
  fetchTemplates();
1356
1450
  }, []);
1357
1451
  const fetchTemplates = async () => {
@@ -1693,15 +1787,112 @@ var TemplateManagerTab = () => {
1693
1787
  ] })
1694
1788
  ] }) });
1695
1789
  };
1790
+ var SUGGESTIONS = [
1791
+ { icon: "fas fa-comment-dots", color: "text-indigo-400", bg: "bg-indigo-500/10", text: "Saluda al cliente cuando se cree un pedido" },
1792
+ { icon: "fas fa-camera", color: "text-amber-400", bg: "bg-amber-500/10", text: "Detecta pagos en fotos de comprobante" },
1793
+ { icon: "fas fa-industry", color: "text-emerald-400", bg: "bg-emerald-500/10", text: "Avisa por WhatsApp al pasar a Producci\xF3n" },
1794
+ { icon: "fas fa-truck", color: "text-cyan-400", bg: "bg-cyan-500/10", text: "Pedido listo: avisa si >$1M" }
1795
+ ];
1796
+ var RulesArchitectTab = () => {
1797
+ const [prompt, setPrompt] = React9.useState("");
1798
+ const [loading, setLoading] = React9.useState(false);
1799
+ const [result, setResult] = React9.useState(null);
1800
+ const [error, setError] = React9.useState(null);
1801
+ const generateRule = async (textToUse) => {
1802
+ const text = textToUse || prompt;
1803
+ if (!text.trim()) return;
1804
+ setLoading(true);
1805
+ setError(null);
1806
+ setResult(null);
1807
+ try {
1808
+ const res = await fetch("http://localhost:3001/api/whatsapp/rules/generate", {
1809
+ method: "POST",
1810
+ headers: {
1811
+ "Content-Type": "application/json",
1812
+ // Note: Mocking tenant matching local dev
1813
+ "x-tenant-id": "dev-tenant"
1814
+ },
1815
+ body: JSON.stringify({ prompt: text })
1816
+ });
1817
+ if (!res.ok) throw new Error("Error al generar regla IA");
1818
+ const data = await res.json();
1819
+ setResult(JSON.stringify(data.rule || data, null, 2));
1820
+ } catch (err) {
1821
+ setError(err.message || "Error de conexi\xF3n con el AI Service");
1822
+ } finally {
1823
+ setLoading(false);
1824
+ }
1825
+ };
1826
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 h-full flex flex-col items-center justify-center p-8 bg-black/20 overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full max-w-3xl", children: [
1827
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center mb-10 text-white", children: [
1828
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center justify-center w-16 h-16 rounded-2xl bg-indigo-500/20 text-indigo-400 mb-6 border border-indigo-500/30 shadow-[0_0_30px_rgba(99,102,241,0.2)]", children: /* @__PURE__ */ jsxRuntime.jsx("i", { className: "fas fa-wand-magic-sparkles text-2xl" }) }),
1829
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-3xl font-black tracking-tight mb-3", children: "Workflow Architect" }),
1830
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-zinc-400 max-w-lg mx-auto leading-relaxed", children: "Escribe una instrucci\xF3n en lenguaje natural y Sentinal AI generar\xE1 la regla JSON requerida para la automatizaci\xF3n de WhatsApp." })
1831
+ ] }),
1832
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-3 mb-8", children: SUGGESTIONS.map((s, i) => /* @__PURE__ */ jsxRuntime.jsxs(
1833
+ "button",
1834
+ {
1835
+ onClick: () => {
1836
+ setPrompt(s.text);
1837
+ generateRule(s.text);
1838
+ },
1839
+ className: `flex items-center gap-3 p-4 text-left rounded-xl transition-all border border-white/5 bg-zinc-900/50 hover:bg-zinc-800/80 hover:border-white/10 group`,
1840
+ children: [
1841
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-8 h-8 shrink-0 flex items-center justify-center rounded-lg ${s.bg} ${s.color}`, children: /* @__PURE__ */ jsxRuntime.jsx("i", { className: s.icon }) }),
1842
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-zinc-300 group-hover:text-white leading-tight", children: s.text })
1843
+ ]
1844
+ },
1845
+ i
1846
+ )) }),
1847
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
1848
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative group", children: [
1849
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsx("i", { className: "fas fa-terminal text-zinc-500" }) }),
1850
+ /* @__PURE__ */ jsxRuntime.jsx(
1851
+ "input",
1852
+ {
1853
+ type: "text",
1854
+ value: prompt,
1855
+ onChange: (e) => setPrompt(e.target.value),
1856
+ onKeyDown: (e) => e.key === "Enter" && generateRule(),
1857
+ placeholder: "Ej: Cuando un pedido pase de Producci\xF3n a Dispatch, env\xEDa una plantilla...",
1858
+ className: "block w-full pl-11 pr-32 py-4 bg-zinc-950/80 border-2 border-zinc-800 rounded-xl text-sm outline-none text-zinc-200 placeholder-zinc-600 focus:border-indigo-500/50 focus:bg-zinc-950 transition-all shadow-inner"
1859
+ }
1860
+ ),
1861
+ /* @__PURE__ */ jsxRuntime.jsx(
1862
+ "button",
1863
+ {
1864
+ onClick: () => generateRule(),
1865
+ disabled: loading || !prompt.trim(),
1866
+ className: "absolute right-2 top-2 bottom-2 px-6 bg-indigo-600 hover:bg-indigo-500 disabled:bg-zinc-800 disabled:text-zinc-600 text-white font-bold rounded-lg text-sm transition-all shadow-lg active:scale-95 flex items-center gap-2",
1867
+ children: loading ? /* @__PURE__ */ jsxRuntime.jsx("i", { className: "fas fa-circle-notch fa-spin" }) : "Generar"
1868
+ }
1869
+ )
1870
+ ] }),
1871
+ error && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 rounded-xl bg-rose-500/10 border border-rose-500/20 text-rose-400 text-sm flex items-start gap-3 animate-in fade-in slide-in-from-top-2", children: [
1872
+ /* @__PURE__ */ jsxRuntime.jsx("i", { className: "fas fa-exclamation-circle mt-0.5" }),
1873
+ /* @__PURE__ */ jsxRuntime.jsx("div", { children: error })
1874
+ ] }),
1875
+ result && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-xl overflow-hidden border border-zinc-800 bg-[#0d1117] relative animate-in zoom-in-95 fade-in duration-300 shadow-2xl", children: [
1876
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-2 border-b border-zinc-800 bg-[#161b22] flex items-center gap-2", children: [
1877
+ /* @__PURE__ */ jsxRuntime.jsx("i", { className: "fab fa-js text-yellow-400/80 text-xs" }),
1878
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-mono text-zinc-400", children: "generated_rule.json" }),
1879
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1" }),
1880
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] uppercase font-bold text-emerald-400 bg-emerald-500/10 px-2 py-0.5 rounded", children: "V\xE1lido" })
1881
+ ] }),
1882
+ /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "p-5 text-xs font-mono leading-relaxed text-[#c9d1d9] overflow-x-auto whitespace-pre-wrap max-h-[300px] overflow-y-auto custom-scrollbar", children: result })
1883
+ ] })
1884
+ ] })
1885
+ ] }) });
1886
+ };
1696
1887
  var useStatusMetrics = () => {
1697
- const [metrics, setMetrics] = React8.useState({
1888
+ const [metrics, setMetrics] = React9.useState({
1698
1889
  activeChats: 12,
1699
1890
  avgResponse: "2.4s",
1700
1891
  todayMessages: 347,
1701
1892
  botUptime: "99.8%",
1702
1893
  queuedMessages: 3
1703
1894
  });
1704
- React8.useEffect(() => {
1895
+ React9.useEffect(() => {
1705
1896
  const interval = setInterval(() => {
1706
1897
  setMetrics((prev) => ({
1707
1898
  ...prev,
@@ -1714,7 +1905,7 @@ var useStatusMetrics = () => {
1714
1905
  }, []);
1715
1906
  return metrics;
1716
1907
  };
1717
- var StatusBarMetrics = React8__default.default.memo(() => {
1908
+ var StatusBarMetrics = React9__default.default.memo(() => {
1718
1909
  const metrics = useStatusMetrics();
1719
1910
  return /* @__PURE__ */ jsxRuntime.jsxs(
1720
1911
  "div",
@@ -1762,20 +1953,21 @@ var StatusBarMetrics = React8__default.default.memo(() => {
1762
1953
  var TABS = [
1763
1954
  { id: "chat", label: "Live Chat", icon: "fas fa-comments" },
1764
1955
  { id: "flow", label: "Flow Canvas", icon: "fas fa-project-diagram" },
1956
+ { id: "architect", label: "Workflow Architect", icon: "fas fa-wand-magic-sparkles" },
1765
1957
  { id: "analytics", label: "Analytics", icon: "fas fa-chart-bar" },
1766
1958
  { id: "api_tester", label: "API Tester", icon: "fas fa-vial" },
1767
1959
  { id: "templates", label: "Templates", icon: "fas fa-layer-group" }
1768
1960
  ];
1769
1961
  var DiscoveryStudio = ({ gatewayStatus = "connected", activeProvider = "Meta Cloud API" }) => {
1770
- const [activeTab, setActiveTab] = React8.useState("chat");
1771
- const [explorerOpen, setExplorerOpen] = React8.useState(true);
1772
- const [inspectorOpen, setInspectorOpen] = React8.useState(false);
1773
- const [agentOpen, setAgentOpen] = React8.useState(true);
1774
- const [selectedContact, setSelectedContact] = React8.useState(null);
1775
- const [cmdOpen, setCmdOpen] = React8.useState(false);
1776
- const [cmdQuery, setCmdQuery] = React8.useState("");
1777
- const cmdInputRef = React8.useRef(null);
1778
- React8.useEffect(() => {
1962
+ const [activeTab, setActiveTab] = React9.useState("chat");
1963
+ const [explorerOpen, setExplorerOpen] = React9.useState(true);
1964
+ const [inspectorOpen, setInspectorOpen] = React9.useState(false);
1965
+ const [agentOpen, setAgentOpen] = React9.useState(true);
1966
+ const [selectedContact, setSelectedContact] = React9.useState(null);
1967
+ const [cmdOpen, setCmdOpen] = React9.useState(false);
1968
+ const [cmdQuery, setCmdQuery] = React9.useState("");
1969
+ const cmdInputRef = React9.useRef(null);
1970
+ React9.useEffect(() => {
1779
1971
  const handleKeyDown = (e) => {
1780
1972
  if ((e.metaKey || e.ctrlKey) && e.key === "k") {
1781
1973
  e.preventDefault();
@@ -1787,7 +1979,7 @@ var DiscoveryStudio = ({ gatewayStatus = "connected", activeProvider = "Meta Clo
1787
1979
  window.addEventListener("keydown", handleKeyDown);
1788
1980
  return () => window.removeEventListener("keydown", handleKeyDown);
1789
1981
  }, []);
1790
- React8.useEffect(() => {
1982
+ React9.useEffect(() => {
1791
1983
  if (cmdOpen && cmdInputRef.current) {
1792
1984
  cmdInputRef.current.focus();
1793
1985
  }
@@ -1795,6 +1987,7 @@ var DiscoveryStudio = ({ gatewayStatus = "connected", activeProvider = "Meta Clo
1795
1987
  const commands = [
1796
1988
  { id: "chat", label: "Abrir Chat en Vivo", icon: "fas fa-comments", action: () => setActiveTab("chat") },
1797
1989
  { id: "flow", label: "Abrir Flow Canvas", icon: "fas fa-project-diagram", action: () => setActiveTab("flow") },
1990
+ { id: "architect", label: "Abrir Workflow Architect (IA)", icon: "fas fa-wand-magic-sparkles", action: () => setActiveTab("architect") },
1798
1991
  { id: "analytics", label: "Abrir Analytics", icon: "fas fa-chart-bar", action: () => setActiveTab("analytics") },
1799
1992
  { id: "explorer", label: explorerOpen ? "Cerrar Explorer" : "Abrir Explorer", icon: "fas fa-folder-tree", action: () => setExplorerOpen((p) => !p) },
1800
1993
  { id: "inspector", label: inspectorOpen ? "Cerrar Inspector" : "Abrir Inspector", icon: "fas fa-info-circle", action: () => setInspectorOpen((p) => !p) },
@@ -1952,6 +2145,7 @@ var DiscoveryStudio = ({ gatewayStatus = "connected", activeProvider = "Meta Clo
1952
2145
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0 flex flex-col overflow-hidden", children: [
1953
2146
  activeTab === "chat" && /* @__PURE__ */ jsxRuntime.jsx(ChatTab, { selectedContact }),
1954
2147
  activeTab === "flow" && /* @__PURE__ */ jsxRuntime.jsx(FlowCanvasTab, {}),
2148
+ activeTab === "architect" && /* @__PURE__ */ jsxRuntime.jsx(RulesArchitectTab, {}),
1955
2149
  activeTab === "analytics" && /* @__PURE__ */ jsxRuntime.jsx(AnalyticsTab, {}),
1956
2150
  activeTab === "api_tester" && /* @__PURE__ */ jsxRuntime.jsx(ApiTesterTab, {}),
1957
2151
  activeTab === "templates" && /* @__PURE__ */ jsxRuntime.jsx(TemplateManagerTab, {})