callix-dialer-widget 2.8.0 → 2.8.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/WIDGET-README.md CHANGED
@@ -141,6 +141,19 @@ Entrar, avisado de que encerra a sessão de lá. Se o Callix derrubar a sessão
141
141
  Callix web, supervisor, bloqueio), o widget volta à tela de login com o motivo e não reentra
142
142
  sozinho.
143
143
 
144
+ Pausas no modo motor (2.8.1): a lista de pausas é a do serviço no Callix, trazida pela vista
145
+ do motor; a pausa escolhida vai no comando `pause` e o discador mostra o nome dela. Com um
146
+ motor anterior ao 2.8.1 a lista cai numa pausa única ("Pausa") e o motor usa a configurada
147
+ nele. O widget também informa a própria versão ao host, que a registra no log: é o jeito de
148
+ achar navegadores com um build antigo em cache. No console da página, `CallixDialer.version`
149
+ diz qual build está carregado.
150
+
151
+ Comandos durante uma queda da conexão (2.8.2): um clique dado enquanto a conexão com o host
152
+ está caída ou reconectando (rede instável) não se perde mais em silêncio. O comando fica
153
+ guardado, a tela avisa que a conexão está voltando, e ele é enviado no reconectar, se ainda
154
+ tiver menos de 15 s; mais velho que isso é descartado, para não atingir outra chamada. Antes,
155
+ um "encerrar" dado nesse momento deixava a chamada ativa no motor.
156
+
144
157
  Sem `engineUrl` nada muda: o SDK continua rodando no navegador como sempre.
145
158
 
146
159
  ## 🔐 Login e usuário conhecido
@@ -43754,6 +43754,8 @@ const INITIAL_TRANSCRIPT = { segments: [], status: null, insight: null, interims
43754
43754
  const MAX_SEGMENTS = 300;
43755
43755
  const RECONNECT_MIN_MS = 500;
43756
43756
  const RECONNECT_MAX_MS = 5e3;
43757
+ const PENDING_COMMANDS_MAX = 10;
43758
+ const PENDING_COMMANDS_TTL_MS = 15e3;
43757
43759
  function engineWsUrl(baseUrl) {
43758
43760
  const url = new URL(baseUrl, typeof window !== "undefined" ? window.location.href : "http://localhost");
43759
43761
  url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
@@ -43771,6 +43773,7 @@ class EngineClient {
43771
43773
  this.viewWaiters = [];
43772
43774
  this.ws = null;
43773
43775
  this.reconnectDelay = RECONNECT_MIN_MS;
43776
+ this.pendingCommands = [];
43774
43777
  this.reconnectTimer = null;
43775
43778
  this.closed = false;
43776
43779
  this.iceServers = [{ urls: "stun:stun.l.google.com:19302" }];
@@ -43838,6 +43841,7 @@ class EngineClient {
43838
43841
  role: "tab",
43839
43842
  agentId: this.agentId,
43840
43843
  ...this.options.token ? { token: this.options.token } : {},
43844
+ ...this.options.version ? { version: this.options.version } : {},
43841
43845
  ...sessionToken ? { sessionToken } : {},
43842
43846
  label: this.options.label ?? "Discador",
43843
43847
  ...this.session ? { session: this.session } : {}
@@ -43920,6 +43924,7 @@ class EngineClient {
43920
43924
  case "welcome": {
43921
43925
  if (Array.isArray(message.iceServers) && message.iceServers.length > 0) this.iceServers = message.iceServers;
43922
43926
  this.setState({ tabId: message.tabId, rejected: null });
43927
+ this.flushPendingCommands();
43923
43928
  this.applyView(message.view);
43924
43929
  this.log(`sou a aba ${message.tabId}`);
43925
43930
  if (this.options.autoEngage !== false && message.view.engagedTabId !== message.tabId) this.engage();
@@ -44012,8 +44017,17 @@ class EngineClient {
44012
44017
  this.session = session;
44013
44018
  this.setState({ rejected: null, sessionLost: null });
44014
44019
  if (this.ws && this.ws.readyState === WebSocket.OPEN) {
44015
- this.send({ type: "session", session });
44016
- return;
44020
+ if (this.state.tabId) {
44021
+ this.send({ type: "session", session });
44022
+ return;
44023
+ }
44024
+ const stale = this.ws;
44025
+ this.ws = null;
44026
+ stale.onopen = null;
44027
+ stale.onmessage = null;
44028
+ stale.onclose = null;
44029
+ stale.close();
44030
+ this.setState({ wsState: "closed" });
44017
44031
  }
44018
44032
  if (this.reconnectTimer) {
44019
44033
  clearTimeout(this.reconnectTimer);
@@ -44029,8 +44043,29 @@ class EngineClient {
44029
44043
  this.send({ type: "release" });
44030
44044
  }
44031
44045
  command(command) {
44032
- this.send({ type: "command", command });
44033
- this.log(`comando: ${command.name}${command.phone ? ` ${command.phone}` : ""}`);
44046
+ if (this.ws && this.ws.readyState === WebSocket.OPEN && this.state.tabId) {
44047
+ this.send({ type: "command", command });
44048
+ this.log(`comando: ${command.name}${command.phone ? ` ${command.phone}` : ""}`);
44049
+ return;
44050
+ }
44051
+ this.pendingCommands.push({ command, at: Date.now() });
44052
+ if (this.pendingCommands.length > PENDING_COMMANDS_MAX) this.pendingCommands.shift();
44053
+ this.log(`comando: ${command.name} guardado até a conexão com o motor voltar`);
44054
+ this.setState({ lastError: "Sem conexão com o motor neste momento; o comando será enviado assim que ela voltar." });
44055
+ this.connect();
44056
+ }
44057
+ /** manda os comandos guardados que ainda fazem sentido (os velhos poderiam atingir outra chamada) */
44058
+ flushPendingCommands() {
44059
+ const now = Date.now();
44060
+ const pending = this.pendingCommands.splice(0);
44061
+ for (const entry of pending) {
44062
+ if (now - entry.at > PENDING_COMMANDS_TTL_MS) {
44063
+ this.log(`comando ${entry.command.name} descartado: a conexão demorou demais para voltar`);
44064
+ continue;
44065
+ }
44066
+ this.send({ type: "command", command: entry.command });
44067
+ this.log(`comando: ${entry.command.name} (enviado depois da reconexão)`);
44068
+ }
44034
44069
  }
44035
44070
  // ------------------------------------------------------------------ áudio
44036
44071
  setInputMuted(muted) {
@@ -44219,6 +44254,18 @@ function useEngineState(client2) {
44219
44254
  return useSyncExternalStore(client2.subscribe.bind(client2), client2.getState.bind(client2), client2.getState.bind(client2));
44220
44255
  }
44221
44256
  const ENGINE_BREAKS = [{ id: 0, name: "Pausa" }];
44257
+ function breaksFromView(view) {
44258
+ var _a2;
44259
+ const breaks = (_a2 = view == null ? void 0 : view.snapshot) == null ? void 0 : _a2.leg.breaks;
44260
+ return breaks && breaks.length > 0 ? breaks.map((b) => ({ id: b.id, name: b.name })) : ENGINE_BREAKS;
44261
+ }
44262
+ function currentBreakFromView(view) {
44263
+ var _a2;
44264
+ return ((_a2 = view.snapshot) == null ? void 0 : _a2.leg.break) ?? breaksFromView(view)[0];
44265
+ }
44266
+ function pauseCommand(breakId) {
44267
+ return typeof breakId === "number" ? { name: "pause", breakId } : { name: "pause" };
44268
+ }
44222
44269
  function toCallQualification(q) {
44223
44270
  return { id: q.id, name: q.name, isSystemQualification: false, systemQualificationType: 0 };
44224
44271
  }
@@ -44351,7 +44398,7 @@ function operatorStateFromView(view, call) {
44351
44398
  case "afterCall":
44352
44399
  return { state: "afterCall" };
44353
44400
  case "onBreak":
44354
- return { state: "onBreak", break: ENGINE_BREAKS[0] };
44401
+ return { state: "onBreak", break: currentBreakFromView(view) };
44355
44402
  case "offline":
44356
44403
  return { state: "offline" };
44357
44404
  case "error":
@@ -44434,7 +44481,7 @@ function useEngineControls(client2) {
44434
44481
  if (status === "onBreak") client2.command({ name: "resume" });
44435
44482
  },
44436
44483
  goOffline: () => void 0,
44437
- enterOnBreak: () => client2.command({ name: "pause" }),
44484
+ enterOnBreak: (breakId) => client2.command(pauseCommand(breakId)),
44438
44485
  makeManualCall: (phoneNumber) => client2.command({ name: "dial", phone: phoneNumber }),
44439
44486
  finishAfterCall: (data) => {
44440
44487
  if (typeof data.qualificationId === "number") client2.command({ name: "qualify", qualificationId: data.qualificationId, result: data.result === "failure" ? "discard" : "success" });
@@ -44488,7 +44535,8 @@ const operators = /* @__PURE__ */ new WeakMap();
44488
44535
  function engineOperator(client2) {
44489
44536
  let operator = operators.get(client2);
44490
44537
  if (!operator) {
44491
- const breaks = new EngineSignal(ENGINE_BREAKS);
44538
+ const breaks = new EngineSignal(breaksFromView(client2.getState().view));
44539
+ client2.subscribe(() => breaks.set(breaksFromView(client2.getState().view)));
44492
44540
  operator = {
44493
44541
  breaks,
44494
44542
  user: client2.userSignal,
@@ -44503,7 +44551,7 @@ function engineOperator(client2) {
44503
44551
  var _a2, _b;
44504
44552
  if (((_b = (_a2 = client2.getState().view) == null ? void 0 : _a2.snapshot) == null ? void 0 : _b.leg.status) === "onBreak") client2.command({ name: "resume" });
44505
44553
  },
44506
- enterOnBreak: () => client2.command({ name: "pause" }),
44554
+ enterOnBreak: (breakId) => client2.command(pauseCommand(breakId)),
44507
44555
  makeManualCall: (phoneNumber) => client2.command({ name: "dial", phone: phoneNumber }),
44508
44556
  finishAfterCall: (data) => {
44509
44557
  if (typeof data.qualificationId === "number") client2.command({ name: "qualify", qualificationId: data.qualificationId, result: data.result === "failure" ? "discard" : "success" });
@@ -58687,7 +58735,7 @@ function AuthenticatedMobileDialer({
58687
58735
  showClientInfoModal = false,
58688
58736
  density
58689
58737
  }) {
58690
- var _a2, _b, _c, _d;
58738
+ var _a2, _b, _c, _d, _e;
58691
58739
  const { skipManualQualification } = useDialerConfig();
58692
58740
  const [dialValue, setDialValue] = useState("");
58693
58741
  const [activeScreen, setActiveScreen] = useState("dialer");
@@ -59400,11 +59448,12 @@ function AuthenticatedMobileDialer({
59400
59448
  if (isOnBreak) return "paused";
59401
59449
  return "available";
59402
59450
  }, [isError, isOnBreak]);
59451
+ const activeBreakName = (operatorState == null ? void 0 : operatorState.state) === "onBreak" ? (_a2 = operatorState.break) == null ? void 0 : _a2.name : void 0;
59403
59452
  const statusBadgeText = useMemo(() => {
59404
59453
  if (isError) return (dialerNotice == null ? void 0 : dialerNotice.source) === "device" ? dialerNotice.title : "Erro";
59405
- if (isOnBreak) return currentBreakName || "Em pausa";
59454
+ if (isOnBreak) return currentBreakName || activeBreakName || "Em pausa";
59406
59455
  return "Disponível";
59407
- }, [dialerNotice, isError, isOnBreak, currentBreakName]);
59456
+ }, [dialerNotice, isError, isOnBreak, currentBreakName, activeBreakName]);
59408
59457
  const pauseDurationLabel = useMemo(() => {
59409
59458
  const hours = Math.floor(pauseDurationSeconds / 3600);
59410
59459
  const minutes = Math.floor(pauseDurationSeconds % 3600 / 60).toString().padStart(2, "0");
@@ -59542,15 +59591,15 @@ function AuthenticatedMobileDialer({
59542
59591
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "client-card-header", children: [
59543
59592
  /* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fas fa-user-circle client-avatar" }),
59544
59593
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "client-main-info", children: [
59545
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "client-name", id: "clientName", children: findValue(contactData.finalData, ["DECISOR", "NOME", "CLIENTE", "PROSPECT"]) ?? ((_a2 = callInfo.info.campaignContact) == null ? void 0 : _a2.label) ?? "--" }),
59546
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "client-company", id: "clientCompany", children: findValue(contactData.finalData, ["EMPRESA", "RAZAO SOCIAL", "RAZÃO SOCIAL", "RAZAO_SOCIAL"]) ?? ((_b = callInfo.info.campaign) == null ? void 0 : _b.name) ?? "--" })
59594
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "client-name", id: "clientName", children: findValue(contactData.finalData, ["DECISOR", "NOME", "CLIENTE", "PROSPECT"]) ?? ((_b = callInfo.info.campaignContact) == null ? void 0 : _b.label) ?? "--" }),
59595
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "client-company", id: "clientCompany", children: findValue(contactData.finalData, ["EMPRESA", "RAZAO SOCIAL", "RAZÃO SOCIAL", "RAZAO_SOCIAL"]) ?? ((_c = callInfo.info.campaign) == null ? void 0 : _c.name) ?? "--" })
59547
59596
  ] })
59548
59597
  ] }),
59549
59598
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "client-card-divider" }),
59550
59599
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "client-info-list", children: [
59551
59600
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "client-info-row", children: [
59552
59601
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "client-info-label", children: "Socio" }),
59553
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "client-info-value", children: findValue(contactData.finalData, ["SOCIO", "PROSPECT", "DECISOR", "NOME", "CLIENTE"]) ?? ((_c = callInfo.info.campaignContact) == null ? void 0 : _c.label) ?? "--" })
59602
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "client-info-value", children: findValue(contactData.finalData, ["SOCIO", "PROSPECT", "DECISOR", "NOME", "CLIENTE"]) ?? ((_d = callInfo.info.campaignContact) == null ? void 0 : _d.label) ?? "--" })
59554
59603
  ] }),
59555
59604
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "client-info-row", children: [
59556
59605
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "client-info-label", children: "Empresa" }),
@@ -59747,7 +59796,7 @@ function AuthenticatedMobileDialer({
59747
59796
  {
59748
59797
  isOpen: isClientInfoModalOpen,
59749
59798
  campaignCallInfo,
59750
- campaignContactPhoneId: ((_d = currentCall == null ? void 0 : currentCall.data) == null ? void 0 : _d.campaignContactPhoneId) ?? null,
59799
+ campaignContactPhoneId: ((_e = currentCall == null ? void 0 : currentCall.data) == null ? void 0 : _e.campaignContactPhoneId) ?? null,
59751
59800
  callDuration: callDurationSeconds,
59752
59801
  isCallActive: isInCall || isConnecting,
59753
59802
  onClose: () => setIsClientInfoModalOpen(false)
@@ -60731,7 +60780,7 @@ function ClickToCallAgent({ enabled = true, pollIntervalMs, sessionToken, onTask
60731
60780
  }, [enabled, pollIntervalMs, sessionToken]);
60732
60781
  return null;
60733
60782
  }
60734
- const WIDGET_VERSION = "2.8.0";
60783
+ const WIDGET_VERSION = "2.8.2";
60735
60784
  let currentSipWebSocket = null;
60736
60785
  function sendSipUnregisterIfPossible(ws) {
60737
60786
  if (ws.readyState !== WebSocket.OPEN) return;
@@ -61131,6 +61180,7 @@ function DialerWrapper({ options, containerEl }) {
61131
61180
  const client2 = new EngineClient({
61132
61181
  url: options.engineUrl ?? "",
61133
61182
  token: options.engineToken || void 0,
61183
+ version: WIDGET_VERSION,
61134
61184
  // credencial da aba: o token da sessão do Callix guardado neste navegador (lido a cada
61135
61185
  // hello, para a reconexão depois de uma troca de sessão pegar o token novo)
61136
61186
  sessionToken: () => readStoredToken(user),