callix-dialer-widget 2.2.0 → 2.3.0

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.
@@ -6,7 +6,7 @@ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read fr
6
6
  var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
7
7
  var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
8
8
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
9
- import React, { useEffect, useState, useMemo, useRef, useCallback, forwardRef, useLayoutEffect, createContext, useImperativeHandle, StrictMode, useContext } from "react";
9
+ import React, { useState, useMemo, useRef, useEffect, useCallback, forwardRef, useLayoutEffect, createContext, useImperativeHandle, StrictMode, useContext } from "react";
10
10
  import require$$0$2, { createPortal } from "react-dom";
11
11
  var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
12
12
  function getAugmentedNamespace(n) {
@@ -101,9 +101,9 @@ function requireAxios() {
101
101
  const { toString } = Object.prototype;
102
102
  const { getPrototypeOf } = Object;
103
103
  const { iterator, toStringTag } = Symbol;
104
- const kindOf = /* @__PURE__ */ ((cache) => (thing) => {
104
+ const kindOf = /* @__PURE__ */ ((cache2) => (thing) => {
105
105
  const str = toString.call(thing);
106
- return cache[str] || (cache[str] = str.slice(8, -1).toLowerCase());
106
+ return cache2[str] || (cache2[str] = str.slice(8, -1).toLowerCase());
107
107
  })(/* @__PURE__ */ Object.create(null));
108
108
  const kindOfTest = (type) => {
109
109
  type = type.toLowerCase();
@@ -307,10 +307,10 @@ function requireAxios() {
307
307
  }
308
308
  };
309
309
  const matchAll = (regExp, str) => {
310
- let matches;
310
+ let matches2;
311
311
  const arr = [];
312
- while ((matches = regExp.exec(str)) !== null) {
313
- arr.push(matches);
312
+ while ((matches2 = regExp.exec(str)) !== null) {
313
+ arr.push(matches2);
314
314
  }
315
315
  return arr;
316
316
  };
@@ -5774,11 +5774,11 @@ var StateNode = (
5774
5774
  if (!afterConfig) {
5775
5775
  return [];
5776
5776
  }
5777
- var mutateEntryExit = function(delay, i2) {
5778
- var delayRef = isFunction(delay) ? "".concat(_this.id, ":delay[").concat(i2, "]") : delay;
5777
+ var mutateEntryExit = function(delay2, i2) {
5778
+ var delayRef = isFunction(delay2) ? "".concat(_this.id, ":delay[").concat(i2, "]") : delay2;
5779
5779
  var eventType = after(delayRef, _this.id);
5780
5780
  _this.onEntry.push(send$1(eventType, {
5781
- delay
5781
+ delay: delay2
5782
5782
  }));
5783
5783
  _this.onExit.push(cancel$1(eventType));
5784
5784
  return eventType;
@@ -5788,12 +5788,12 @@ var StateNode = (
5788
5788
  return __assign(__assign({}, transition), {
5789
5789
  event: eventType
5790
5790
  });
5791
- }) : flatten(Object.keys(afterConfig).map(function(delay, i2) {
5792
- var configTransition = afterConfig[delay];
5791
+ }) : flatten(Object.keys(afterConfig).map(function(delay2, i2) {
5792
+ var configTransition = afterConfig[delay2];
5793
5793
  var resolvedTransition = isString(configTransition) ? {
5794
5794
  target: configTransition
5795
5795
  } : configTransition;
5796
- var resolvedDelay = !isNaN(+delay) ? +delay : delay;
5796
+ var resolvedDelay = !isNaN(+delay2) ? +delay2 : delay2;
5797
5797
  var eventType = mutateEntryExit(resolvedDelay, i2);
5798
5798
  return toArray(resolvedTransition).map(function(transition) {
5799
5799
  return __assign(__assign({}, transition), {
@@ -5803,9 +5803,9 @@ var StateNode = (
5803
5803
  });
5804
5804
  }));
5805
5805
  return delayedTransitions.map(function(delayedTransition) {
5806
- var delay = delayedTransition.delay;
5806
+ var delay2 = delayedTransition.delay;
5807
5807
  return __assign(__assign({}, _this.formatTransition(delayedTransition)), {
5808
- delay
5808
+ delay: delay2
5809
5809
  });
5810
5810
  });
5811
5811
  };
@@ -7785,13 +7785,13 @@ function requireExtractExtension() {
7785
7785
  return {};
7786
7786
  }
7787
7787
  var numberWithoutExtension = number.slice(0, start2);
7788
- var matches = number.match(EXTN_PATTERN2);
7788
+ var matches2 = number.match(EXTN_PATTERN2);
7789
7789
  var i2 = 1;
7790
- while (i2 < matches.length) {
7791
- if (matches[i2]) {
7790
+ while (i2 < matches2.length) {
7791
+ if (matches2[i2]) {
7792
7792
  return {
7793
7793
  number: numberWithoutExtension,
7794
- ext: matches[i2]
7794
+ ext: matches2[i2]
7795
7795
  };
7796
7796
  }
7797
7797
  i2++;
@@ -10797,10 +10797,10 @@ function requirePhoneNumberMatcher() {
10797
10797
  return _createClass2(PhoneNumberMatcher2, [{
10798
10798
  key: "find",
10799
10799
  value: function find() {
10800
- var matches;
10801
- while (this.maxTries > 0 && (matches = this.PATTERN.exec(this.text)) !== null) {
10802
- var candidate = matches[0];
10803
- var offset = matches.index;
10800
+ var matches2;
10801
+ while (this.maxTries > 0 && (matches2 = this.PATTERN.exec(this.text)) !== null) {
10802
+ var candidate = matches2[0];
10803
+ var offset = matches2.index;
10804
10804
  candidate = (0, _parsePreCandidate["default"])(candidate);
10805
10805
  if ((0, _isValidPreCandidate["default"])(candidate, offset, this.text)) {
10806
10806
  var match2 = (
@@ -14024,12 +14024,12 @@ function requireMemoize() {
14024
14024
  throw new TypeError(FUNC_ERROR_TEXT);
14025
14025
  }
14026
14026
  var memoized = function() {
14027
- var args = arguments, key = resolver ? resolver.apply(this, args) : args[0], cache = memoized.cache;
14028
- if (cache.has(key)) {
14029
- return cache.get(key);
14027
+ var args = arguments, key = resolver ? resolver.apply(this, args) : args[0], cache2 = memoized.cache;
14028
+ if (cache2.has(key)) {
14029
+ return cache2.get(key);
14030
14030
  }
14031
14031
  var result2 = func.apply(this, args);
14032
- memoized.cache = cache.set(key, result2) || cache;
14032
+ memoized.cache = cache2.set(key, result2) || cache2;
14033
14033
  return result2;
14034
14034
  };
14035
14035
  memoized.cache = new (memoize.Cache || MapCache)();
@@ -14503,14 +14503,14 @@ function requirePhoneNumberSearch() {
14503
14503
  return _createClass2(PhoneNumberSearch2, [{
14504
14504
  key: "find",
14505
14505
  value: function find() {
14506
- var matches = this.regexp.exec(this.text);
14507
- if (!matches) {
14506
+ var matches2 = this.regexp.exec(this.text);
14507
+ if (!matches2) {
14508
14508
  return;
14509
14509
  }
14510
- var number = matches[0];
14511
- var startsAt = matches.index;
14510
+ var number = matches2[0];
14511
+ var startsAt = matches2.index;
14512
14512
  number = number.replace(WHITESPACE_IN_THE_BEGINNING_PATTERN, "");
14513
- startsAt += matches[0].length - number.length;
14513
+ startsAt += matches2[0].length - number.length;
14514
14514
  number = number.replace(PUNCTUATION_IN_THE_END_PATTERN, "");
14515
14515
  number = (0, _parsePreCandidate["default"])(number);
14516
14516
  var result2 = this.parseCandidate(number, startsAt);
@@ -42753,73 +42753,73 @@ function requireChunk52ALM2Y4() {
42753
42753
  function isInjector(value) {
42754
42754
  return __isInternalType(value, Injector);
42755
42755
  }
42756
- var getWeakCacheItem = (cache, deps) => {
42756
+ var getWeakCacheItem = (cache2, deps) => {
42757
42757
  while (true) {
42758
42758
  const [dep, ...rest] = deps;
42759
- const entry = cache.get(dep);
42759
+ const entry = cache2.get(dep);
42760
42760
  if (!entry) {
42761
42761
  return;
42762
42762
  }
42763
42763
  if (!rest.length) {
42764
42764
  return entry[1];
42765
42765
  }
42766
- cache = entry[0];
42766
+ cache2 = entry[0];
42767
42767
  deps = rest;
42768
42768
  }
42769
42769
  };
42770
- var setWeakCacheItem = (cache, deps, item) => {
42770
+ var setWeakCacheItem = (cache2, deps, item) => {
42771
42771
  while (true) {
42772
42772
  const [dep, ...rest] = deps;
42773
- let entry = cache.get(dep);
42773
+ let entry = cache2.get(dep);
42774
42774
  if (!entry) {
42775
42775
  entry = [/* @__PURE__ */ new WeakMap()];
42776
- cache.set(dep, entry);
42776
+ cache2.set(dep, entry);
42777
42777
  }
42778
42778
  if (!rest.length) {
42779
42779
  entry[1] = item;
42780
42780
  return;
42781
42781
  }
42782
- cache = entry[0];
42782
+ cache2 = entry[0];
42783
42783
  deps = rest;
42784
42784
  }
42785
42785
  };
42786
42786
  var createDeepCache = () => {
42787
- let cache = /* @__PURE__ */ new WeakMap();
42787
+ let cache2 = /* @__PURE__ */ new WeakMap();
42788
42788
  const deepCache = (createFn, foundFn, deps) => {
42789
42789
  if (!deps.length)
42790
42790
  throw new Error("Dependencies need to exist.");
42791
- const cachedValue = getWeakCacheItem(cache, deps);
42791
+ const cachedValue = getWeakCacheItem(cache2, deps);
42792
42792
  if (cachedValue) {
42793
42793
  foundFn(cachedValue);
42794
42794
  return cachedValue;
42795
42795
  }
42796
42796
  const newObject = createFn();
42797
- setWeakCacheItem(cache, deps, newObject);
42797
+ setWeakCacheItem(cache2, deps, newObject);
42798
42798
  return newObject;
42799
42799
  };
42800
42800
  const upsert = (createFn, deps) => {
42801
42801
  if (!deps.length)
42802
42802
  throw new Error("Dependencies need to exist.");
42803
- const cachedValue = getWeakCacheItem(cache, deps);
42803
+ const cachedValue = getWeakCacheItem(cache2, deps);
42804
42804
  const newObject = createFn(cachedValue);
42805
- setWeakCacheItem(cache, deps, newObject);
42805
+ setWeakCacheItem(cache2, deps, newObject);
42806
42806
  };
42807
42807
  const remove = (...deps) => {
42808
- removeWeakCacheItem(cache, deps);
42808
+ removeWeakCacheItem(cache2, deps);
42809
42809
  };
42810
- const get = (deps) => getWeakCacheItem(cache, deps);
42810
+ const get = (deps) => getWeakCacheItem(cache2, deps);
42811
42811
  return {
42812
42812
  get,
42813
- cache,
42813
+ cache: cache2,
42814
42814
  deepCache,
42815
42815
  upsert,
42816
42816
  remove
42817
42817
  };
42818
42818
  };
42819
- var removeWeakCacheItem = (cache, deps) => {
42819
+ var removeWeakCacheItem = (cache2, deps) => {
42820
42820
  while (true) {
42821
42821
  const [dep, ...rest] = deps;
42822
- let entry = cache.get(dep);
42822
+ let entry = cache2.get(dep);
42823
42823
  if (!entry) {
42824
42824
  return;
42825
42825
  }
@@ -42828,7 +42828,7 @@ function requireChunk52ALM2Y4() {
42828
42828
  entry[1] = void 0;
42829
42829
  return;
42830
42830
  } else {
42831
- cache = entry[0];
42831
+ cache2 = entry[0];
42832
42832
  deps = rest;
42833
42833
  }
42834
42834
  }
@@ -44185,11 +44185,11 @@ var directive = (factory, data) => {
44185
44185
  const key = stringify(data);
44186
44186
  let directive2;
44187
44187
  if (key) {
44188
- var cache = cacheByFactory.get(factory);
44189
- if (!cache) {
44190
- cacheByFactory.set(factory, cache = /* @__PURE__ */ new Map());
44188
+ var cache2 = cacheByFactory.get(factory);
44189
+ if (!cache2) {
44190
+ cacheByFactory.set(factory, cache2 = /* @__PURE__ */ new Map());
44191
44191
  }
44192
- directive2 = cache.get(key);
44192
+ directive2 = cache2.get(key);
44193
44193
  }
44194
44194
  if (!directive2) {
44195
44195
  directive2 = Object.defineProperty((params, context) => {
@@ -44198,9 +44198,9 @@ var directive = (factory, data) => {
44198
44198
  }, "toJSON", {
44199
44199
  value: () => key || data
44200
44200
  });
44201
- if (cache) {
44202
- cache.set(key, directive2);
44203
- ensureMaxSize(cache, 1e4);
44201
+ if (cache2) {
44202
+ cache2.set(key, directive2);
44203
+ ensureMaxSize(cache2, 1e4);
44204
44204
  }
44205
44205
  }
44206
44206
  return directive2;
@@ -45472,7 +45472,7 @@ var resolveContext = {
45472
45472
  };
45473
45473
  var handleArbitraryValues = (section, key) => (key = key[0] == "[" && key.slice(-1) == "]" && key.slice(1, -1)) && includes(section, "olor") == /^(#|(hsl|rgb)a?\(|[a-z]+$)/.test(key) && (includes(key, "calc(") ? key.replace(/(-?\d*\.?\d(?!\b-.+[,)](?![^+\-/*])\D)(?:%|[a-z]+)?|\))([+\-/*])/g, "$1 $2 ") : key);
45474
45474
  var makeThemeResolver = (config) => {
45475
- const cache = /* @__PURE__ */ new Map();
45475
+ const cache2 = /* @__PURE__ */ new Map();
45476
45476
  const theme2 = { ...defaultTheme, ...config };
45477
45477
  const deref = (theme3, section) => {
45478
45478
  const base = theme3 && theme3[section];
@@ -45486,9 +45486,9 @@ var makeThemeResolver = (config) => {
45486
45486
  defaultValue = key;
45487
45487
  key = join$1(tail(keypath), ".");
45488
45488
  }
45489
- let base = cache.get(section);
45489
+ let base = cache2.get(section);
45490
45490
  if (!base) {
45491
- cache.set(section, base = { ...deref(theme2, section) });
45491
+ cache2.set(section, base = { ...deref(theme2, section) });
45492
45492
  Object.assign(base, deref(theme2.extend, section));
45493
45493
  }
45494
45494
  if (key != null) {
@@ -48472,96 +48472,133 @@ function getAuthHeaders() {
48472
48472
  }
48473
48473
  return headers;
48474
48474
  }
48475
- async function getCampaignConfig$2(contactId) {
48476
- const url = `${API_BASE_URL}/api/campaign-config/${contactId}`;
48477
- const response = await fetch(url, {
48478
- method: "GET",
48479
- headers: getAuthHeaders(),
48480
- credentials: "omit"
48481
- });
48482
- if (!response.ok) {
48483
- const error2 = await response.json().catch(() => ({ error: "Erro desconhecido" }));
48484
- throw new Error(error2.error || `Falha ao buscar config: ${response.status}`);
48485
- }
48486
- const json = await response.json();
48487
- return json;
48488
- }
48489
- async function getCodeToNameMapping(campaignModelId) {
48490
- const url = `${API_BASE_URL}/api/campaign-fields/${campaignModelId}`;
48491
- const response = await fetch(url, {
48492
- method: "GET",
48493
- headers: getAuthHeaders(),
48494
- credentials: "omit"
48495
- });
48496
- if (!response.ok) {
48497
- const error2 = await response.json().catch(() => ({ error: "Erro desconhecido" }));
48498
- throw new Error(error2.error || `Falha ao buscar mapeamento: ${response.status}`);
48499
- }
48500
- const json = await response.json();
48501
- return json;
48502
- }
48503
- async function getCustomerCodeToNameMapping(formId) {
48504
- const url = `${API_BASE_URL}/api/campaign-contact/0?source=customer&formId=${encodeURIComponent(String(formId))}`;
48505
- const response = await fetch(url, {
48506
- method: "GET",
48507
- headers: getAuthHeaders(),
48508
- credentials: "omit"
48509
- });
48510
- if (!response.ok) {
48511
- const error2 = await response.json().catch(() => ({ error: "Erro desconhecido" }));
48512
- throw new Error(error2.error || `Falha ao buscar campos (cliente): ${response.status}`);
48475
+ const CAMPAIGN_FORM_CACHE_TTL_MS = 5 * 60 * 1e3;
48476
+ const DEFAULT_RETRIES = 2;
48477
+ const RETRY_DELAY_MS = 800;
48478
+ const REQUEST_TIMEOUT_MS$1 = 15e3;
48479
+ const cache = /* @__PURE__ */ new Map();
48480
+ function campaignFormCacheKey(request) {
48481
+ return `${request.source}:${request.id}`;
48482
+ }
48483
+ function campaignFormRequestFromCallInfo(info, phoneId) {
48484
+ var _a2, _b;
48485
+ const contact = info == null ? void 0 : info.campaignContact;
48486
+ if (!(contact == null ? void 0 : contact.id)) {
48487
+ return null;
48513
48488
  }
48514
- const json = await response.json();
48515
- const mapping = {};
48516
- if (json && Array.isArray(json.rows)) {
48517
- for (const row of json.rows) {
48518
- for (const field of row) {
48519
- if (field && field.code && field.name) {
48520
- mapping[field.code] = field.name;
48521
- }
48489
+ const customer = contact.customer;
48490
+ if ((customer == null ? void 0 : customer.id) && (customer == null ? void 0 : customer.formId)) {
48491
+ return { source: "customer", id: customer.id, formId: customer.formId, campaignId: ((_a2 = info == null ? void 0 : info.campaign) == null ? void 0 : _a2.id) ?? null, phoneId: phoneId ?? null };
48492
+ }
48493
+ return { source: "campaign", id: contact.id, campaignId: ((_b = info == null ? void 0 : info.campaign) == null ? void 0 : _b.id) ?? null, phoneId: phoneId ?? null };
48494
+ }
48495
+ function buildCampaignFormUrl(apiBaseUrl, request) {
48496
+ const params = new URLSearchParams();
48497
+ if (request.source === "customer") params.set("source", "customer");
48498
+ if (request.campaignId) params.set("campaignId", String(request.campaignId));
48499
+ if (request.formId) params.set("formId", String(request.formId));
48500
+ if (request.phoneId) params.set("phoneId", String(request.phoneId));
48501
+ if (request.signature && request.expiresAt) {
48502
+ params.set("sig", request.signature);
48503
+ params.set("exp", String(request.expiresAt));
48504
+ }
48505
+ const query = params.toString();
48506
+ return `${apiBaseUrl}/api/campaign-form/${encodeURIComponent(String(request.id))}${query ? `?${query}` : ""}`;
48507
+ }
48508
+ function isFormResponse(value) {
48509
+ if (!value || typeof value !== "object") return false;
48510
+ const form = value;
48511
+ return typeof form.id === "number" && !!form.values && typeof form.values === "object" && Array.isArray(form.fields);
48512
+ }
48513
+ async function requestOnce(url, headers) {
48514
+ const controller = new AbortController();
48515
+ const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS$1);
48516
+ try {
48517
+ const response = await fetch(url, { method: "GET", headers, credentials: "omit", signal: controller.signal });
48518
+ if (!response.ok) {
48519
+ const body = await response.json().catch(() => ({}));
48520
+ const message = body.message || body.error || `Erro ${response.status}`;
48521
+ const error2 = new Error(response.status === 401 ? "Sessão expirada. Entre novamente no discador." : message);
48522
+ error2.status = response.status;
48523
+ throw error2;
48524
+ }
48525
+ const data = await response.json();
48526
+ if (!isFormResponse(data)) {
48527
+ throw new Error("Resposta inesperada do servidor");
48528
+ }
48529
+ return data;
48530
+ } catch (error2) {
48531
+ if (error2 instanceof Error && error2.name === "AbortError") {
48532
+ throw new Error("Tempo esgotado ao buscar o formulário");
48533
+ }
48534
+ throw error2;
48535
+ } finally {
48536
+ clearTimeout(timer);
48537
+ }
48538
+ }
48539
+ function shouldRetry(error2) {
48540
+ const status = error2 == null ? void 0 : error2.status;
48541
+ if (status === void 0) return true;
48542
+ return status >= 500 || status === 408 || status === 429;
48543
+ }
48544
+ function fetchCampaignForm(request, options = {}) {
48545
+ const key = campaignFormCacheKey(request);
48546
+ const now = Date.now();
48547
+ const cached = cache.get(key);
48548
+ if (cached && cached.until > now && !options.force) {
48549
+ return cached.promise;
48550
+ }
48551
+ const url = buildCampaignFormUrl(options.apiBaseUrl ?? API_BASE_URL, request);
48552
+ const headers = options.headers ?? getAuthHeaders();
48553
+ const retries = Math.max(0, options.retries ?? DEFAULT_RETRIES);
48554
+ const promise = (async () => {
48555
+ let lastError;
48556
+ for (let attempt = 0; attempt <= retries; attempt++) {
48557
+ try {
48558
+ return await requestOnce(url, headers);
48559
+ } catch (error2) {
48560
+ lastError = error2;
48561
+ if (attempt === retries || !shouldRetry(error2)) break;
48562
+ await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY_MS * 2 ** attempt));
48522
48563
  }
48523
48564
  }
48524
- }
48525
- return mapping;
48526
- }
48527
- async function getContactValues$2(contactId) {
48528
- const url = `${API_BASE_URL}/api/campaign-contact/${contactId}`;
48529
- const response = await fetch(url, {
48530
- method: "GET",
48531
- headers: getAuthHeaders(),
48532
- credentials: "omit"
48533
- });
48534
- if (!response.ok) {
48535
- const error2 = await response.json().catch(() => ({ error: "Erro desconhecido" }));
48536
- throw new Error(error2.error || `Falha ao buscar valores: ${response.status}`);
48537
- }
48538
- const json = await response.json();
48539
- return json;
48540
- }
48541
- async function getCustomerContactValues$1(customerId) {
48542
- const url = `${API_BASE_URL}/api/campaign-fields/0?source=customer&customerId=${encodeURIComponent(String(customerId))}`;
48543
- const response = await fetch(url, {
48544
- method: "GET",
48545
- headers: getAuthHeaders(),
48546
- credentials: "omit"
48565
+ throw lastError instanceof Error ? lastError : new Error("Erro ao buscar o formulário");
48566
+ })();
48567
+ cache.set(key, { promise, until: now + CAMPAIGN_FORM_CACHE_TTL_MS });
48568
+ promise.then((form) => {
48569
+ var _a2;
48570
+ if (form.partial && ((_a2 = cache.get(key)) == null ? void 0 : _a2.promise) === promise) cache.delete(key);
48571
+ }).catch(() => {
48572
+ var _a2;
48573
+ if (((_a2 = cache.get(key)) == null ? void 0 : _a2.promise) === promise) cache.delete(key);
48547
48574
  });
48548
- if (!response.ok) {
48549
- const error2 = await response.json().catch(() => ({ error: "Erro desconhecido" }));
48550
- throw new Error(error2.error || `Falha ao buscar valores (cliente): ${response.status}`);
48551
- }
48552
- const json = await response.json();
48553
- return json;
48575
+ return promise;
48554
48576
  }
48555
- function findValue$1(finalData, candidates) {
48556
- const entries = Object.entries(finalData);
48557
- const lowerCandidates = candidates.map((item) => item.toLowerCase());
48558
- for (const [key, value] of entries) {
48559
- if (lowerCandidates.includes(key.toLowerCase())) {
48577
+ function normalizeFormKey(value) {
48578
+ return String(value ?? "").normalize("NFC").trim().toLowerCase().replace(/\s+/g, " ");
48579
+ }
48580
+ function findFormValue(form, candidates) {
48581
+ if (!(form == null ? void 0 : form.values)) {
48582
+ return void 0;
48583
+ }
48584
+ const byNormalizedKey = /* @__PURE__ */ new Map();
48585
+ for (const [key, value] of Object.entries(form.values)) {
48586
+ const normalized = stripAccents(normalizeFormKey(key));
48587
+ if (!byNormalizedKey.has(normalized) && value !== void 0 && value !== null && String(value).trim() !== "") {
48588
+ byNormalizedKey.set(normalized, String(value));
48589
+ }
48590
+ }
48591
+ for (const candidate of candidates) {
48592
+ const value = byNormalizedKey.get(stripAccents(normalizeFormKey(candidate)));
48593
+ if (value !== void 0) {
48560
48594
  return value;
48561
48595
  }
48562
48596
  }
48563
48597
  return void 0;
48564
48598
  }
48599
+ function stripAccents(value) {
48600
+ return value.normalize("NFD").replace(/[̀-ͯ]/g, "");
48601
+ }
48565
48602
  function formatPhoneValue(raw) {
48566
48603
  if (!raw) return "-";
48567
48604
  const digits = raw.replace(/\D/g, "");
@@ -48575,18 +48612,12 @@ function formatPhoneValue(raw) {
48575
48612
  return raw.trim();
48576
48613
  }
48577
48614
  function CallInfoDisplay() {
48615
+ var _a2;
48578
48616
  const callInfo = clientSdkReactExports.useCallOperatorCurrentCallInfo();
48579
- useEffect(() => {
48580
- console.log("[CallInfoDisplay] Call info atualizado:", {
48581
- callInfo,
48582
- type: callInfo == null ? void 0 : callInfo.type,
48583
- hasCampaignInfo: (callInfo == null ? void 0 : callInfo.type) === "campaign",
48584
- info: (callInfo == null ? void 0 : callInfo.type) === "campaign" ? callInfo.info : null,
48585
- campaignContact: (callInfo == null ? void 0 : callInfo.type) === "campaign" ? callInfo.info.campaignContact : null
48586
- });
48587
- }, [callInfo]);
48617
+ const currentCall = clientSdkReactExports.useCallOperatorCurrentCall();
48618
+ const campaignContactPhoneId = ((_a2 = currentCall == null ? void 0 : currentCall.data) == null ? void 0 : _a2.campaignContactPhoneId) ?? null;
48588
48619
  if ((callInfo == null ? void 0 : callInfo.type) === "campaign") {
48589
- return /* @__PURE__ */ jsxRuntimeExports.jsx(CampaignCallInfoDisplay, { campaignCallInfo: callInfo.info });
48620
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(CampaignCallInfoDisplay, { campaignCallInfo: callInfo.info, campaignContactPhoneId });
48590
48621
  }
48591
48622
  if (callInfo) {
48592
48623
  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-4 rounded-lg border border-amber-200 bg-amber-50 px-3 py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs font-semibold text-amber-700", children: "Chamada manual (sem informações de campanha)" }) });
@@ -48596,83 +48627,63 @@ function CallInfoDisplay() {
48596
48627
  function CampaignCallInfoDisplay(props) {
48597
48628
  const { campaign, campaignContact } = props.campaignCallInfo;
48598
48629
  const [contactData, setContactData] = useState({
48599
- finalData: {},
48630
+ form: null,
48600
48631
  isLoading: false,
48601
48632
  error: null
48602
48633
  });
48603
- const [fetchedContactId, setFetchedContactId] = useState(null);
48634
+ const [retry, setRetry] = useState({ key: null, n: 0 });
48635
+ const request = useMemo(
48636
+ () => campaignFormRequestFromCallInfo(props.campaignCallInfo, props.campaignContactPhoneId),
48637
+ [props.campaignCallInfo, props.campaignContactPhoneId]
48638
+ );
48639
+ const requestKey = request ? campaignFormCacheKey(request) : null;
48640
+ const requestRef = useRef(request);
48641
+ requestRef.current = request;
48642
+ const partialRefreshKeyRef = useRef(null);
48604
48643
  useEffect(() => {
48605
- if (!(campaignContact == null ? void 0 : campaignContact.id)) {
48606
- return;
48607
- }
48608
- if (fetchedContactId === campaignContact.id) {
48644
+ const current = requestRef.current;
48645
+ if (!current || !requestKey) {
48646
+ setContactData({ form: null, isLoading: false, error: null });
48609
48647
  return;
48610
48648
  }
48611
48649
  let cancelled = false;
48612
- const fetchContactData = async () => {
48613
- setContactData({ finalData: {}, isLoading: true, error: null });
48614
- try {
48615
- const customer = campaignContact.customer;
48616
- let codeToNameMap = {};
48617
- let contactValues = {};
48618
- if ((customer == null ? void 0 : customer.id) && (customer == null ? void 0 : customer.formId)) {
48619
- [codeToNameMap, contactValues] = await Promise.all([
48620
- getCustomerCodeToNameMapping(customer.formId),
48621
- getCustomerContactValues$1(customer.id)
48622
- ]);
48623
- } else {
48624
- const config = await getCampaignConfig$2(campaignContact.id);
48625
- if (!config.campaignModelId) {
48626
- throw new Error("campaignModelId não encontrado");
48627
- }
48628
- [codeToNameMap, contactValues] = await Promise.all([
48629
- getCodeToNameMapping(config.campaignModelId),
48630
- getContactValues$2(campaignContact.id)
48631
- ]);
48650
+ let refreshTimer = null;
48651
+ setContactData({ form: null, isLoading: true, error: null });
48652
+ fetchCampaignForm(current, { force: retry.key === requestKey && retry.n > 0 }).then((form) => {
48653
+ if (!cancelled) {
48654
+ setContactData({ form, isLoading: false, error: null });
48655
+ if (form.partial && partialRefreshKeyRef.current !== requestKey) {
48656
+ partialRefreshKeyRef.current = requestKey;
48657
+ refreshTimer = setTimeout(() => setRetry((prev) => ({ key: requestKey, n: prev.n + 1 })), 3e3);
48632
48658
  }
48633
- const finalData = {};
48634
- Object.entries(contactValues).forEach(([code, value]) => {
48635
- if (code in codeToNameMap) {
48636
- finalData[codeToNameMap[code]] = value;
48637
- }
48659
+ }
48660
+ }).catch((error2) => {
48661
+ if (!cancelled) {
48662
+ console.error("[CampaignCallInfoDisplay] Erro ao buscar dados:", error2);
48663
+ setContactData({
48664
+ form: null,
48665
+ isLoading: false,
48666
+ error: error2 instanceof Error ? error2.message : "Erro ao buscar dados do contato"
48638
48667
  });
48639
- if (!cancelled) {
48640
- setContactData({
48641
- finalData,
48642
- isLoading: false,
48643
- error: null
48644
- });
48645
- setFetchedContactId(campaignContact.id);
48646
- }
48647
- } catch (error2) {
48648
- if (!cancelled) {
48649
- console.error("[CampaignCallInfoDisplay] Erro ao buscar dados:", error2);
48650
- const errorMessage = error2 instanceof Error ? error2.message : "Erro ao buscar dados do contato";
48651
- setContactData({
48652
- finalData: {},
48653
- isLoading: false,
48654
- error: errorMessage
48655
- });
48656
- }
48657
48668
  }
48658
- };
48659
- void fetchContactData();
48669
+ });
48660
48670
  return () => {
48661
48671
  cancelled = true;
48672
+ if (refreshTimer) clearTimeout(refreshTimer);
48662
48673
  };
48663
- }, [campaignContact, campaignContact == null ? void 0 : campaignContact.id, fetchedContactId]);
48674
+ }, [requestKey, retry]);
48664
48675
  const prioritizedFields = useMemo(
48665
48676
  () => [
48666
- { label: "Decisor", keys: ["DECISOR"], icon: /* @__PURE__ */ jsxRuntimeExports.jsx(FiUser, { className: "text-sm" }) },
48667
- { label: "Empresa", keys: ["EMPRESA", "RAZAO SOCIAL"], icon: /* @__PURE__ */ jsxRuntimeExports.jsx(FiBriefcase, { className: "text-sm" }) },
48668
- { label: "Dívida ativa", keys: ["DIVIDA ATIVA", "DÍVIDA ATIVA"], icon: /* @__PURE__ */ jsxRuntimeExports.jsx(FiHash, { className: "text-sm" }) },
48677
+ { label: "Decisor", keys: ["DECISOR", "CONTATO", "NOME"], icon: /* @__PURE__ */ jsxRuntimeExports.jsx(FiUser, { className: "text-sm" }) },
48678
+ { label: "Empresa", keys: ["EMPRESA", "RAZAO SOCIAL", "RAZÃO SOCIAL", "CLIENTE"], icon: /* @__PURE__ */ jsxRuntimeExports.jsx(FiBriefcase, { className: "text-sm" }) },
48679
+ { label: "Dívida ativa", keys: ["DIVIDA ATIVA", "DÍVIDA ATIVA", "VALOR TOTAL DEVIDO"], icon: /* @__PURE__ */ jsxRuntimeExports.jsx(FiHash, { className: "text-sm" }) },
48669
48680
  { label: "Telefone", keys: ["TELEFONE", "CELULAR", "WHATSAPP"], icon: /* @__PURE__ */ jsxRuntimeExports.jsx(FiSmartphone, { className: "text-sm" }) }
48670
48681
  ],
48671
48682
  []
48672
48683
  );
48673
48684
  const highlightData = useMemo(() => {
48674
48685
  return prioritizedFields.map((field) => {
48675
- const rawValue = (field.label === "Telefone" ? findValue$1(contactData.finalData, field.keys) ?? (campaignContact == null ? void 0 : campaignContact.phoneNumber) : findValue$1(contactData.finalData, field.keys)) || "";
48686
+ const rawValue = (field.label === "Telefone" ? findFormValue(contactData.form, field.keys) ?? (campaignContact == null ? void 0 : campaignContact.phoneNumber) : findFormValue(contactData.form, field.keys)) || "";
48676
48687
  const display2 = field.label === "Telefone" ? formatPhoneValue(rawValue) : (rawValue || "-").toString().trim() || "-";
48677
48688
  return {
48678
48689
  ...field,
@@ -48680,7 +48691,7 @@ function CampaignCallInfoDisplay(props) {
48680
48691
  isEmpty: !rawValue
48681
48692
  };
48682
48693
  });
48683
- }, [campaignContact == null ? void 0 : campaignContact.phoneNumber, contactData.finalData, prioritizedFields]);
48694
+ }, [campaignContact == null ? void 0 : campaignContact.phoneNumber, contactData.form, prioritizedFields]);
48684
48695
  const hasData = highlightData.some((item) => !item.isEmpty);
48685
48696
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-4 rounded-2xl border border-slate-200 bg-white/90 p-3 shadow-sm", children: [
48686
48697
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-3 flex items-center justify-between gap-2", children: [
@@ -48696,7 +48707,7 @@ function CampaignCallInfoDisplay(props) {
48696
48707
  "Em chamada"
48697
48708
  ] })
48698
48709
  ] }),
48699
- contactData.isLoading && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-xl border border-slate-200 bg-slate-50 p-4 text-center", children: [
48710
+ contactData.isLoading && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-xl border border-slate-200 bg-slate-50 p-4 text-center", role: "status", children: [
48700
48711
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mx-auto mb-2 inline-block h-7 w-7 animate-spin rounded-full border-2 border-emerald-500 border-t-transparent" }),
48701
48712
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs font-medium text-slate-600", children: "Carregando dados do contato..." })
48702
48713
  ] }),
@@ -48706,10 +48717,11 @@ function CampaignCallInfoDisplay(props) {
48706
48717
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs font-semibold text-rose-700", children: "Erro ao carregar dados" })
48707
48718
  ] }),
48708
48719
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "ml-6 text-[11px] text-rose-600", children: contactData.error }),
48709
- contactData.error.includes("Muitas requisições") && /* @__PURE__ */ jsxRuntimeExports.jsx(
48720
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
48710
48721
  "button",
48711
48722
  {
48712
- onClick: () => setFetchedContactId(null),
48723
+ type: "button",
48724
+ onClick: () => setRetry((prev) => ({ key: requestKey, n: prev.n + 1 })),
48713
48725
  className: "mt-2 ml-6 text-[11px] font-semibold text-rose-700 underline hover:text-rose-800",
48714
48726
  children: "Tentar novamente"
48715
48727
  }
@@ -50237,13 +50249,13 @@ function extractExtension(number) {
50237
50249
  return {};
50238
50250
  }
50239
50251
  var numberWithoutExtension = number.slice(0, start2);
50240
- var matches = number.match(EXTN_PATTERN);
50252
+ var matches2 = number.match(EXTN_PATTERN);
50241
50253
  var i2 = 1;
50242
- while (i2 < matches.length) {
50243
- if (matches[i2]) {
50254
+ while (i2 < matches2.length) {
50255
+ if (matches2[i2]) {
50244
50256
  return {
50245
50257
  number: numberWithoutExtension,
50246
- ext: matches[i2]
50258
+ ext: matches2[i2]
50247
50259
  };
50248
50260
  }
50249
50261
  i2++;
@@ -51141,6 +51153,266 @@ function buildDialPlan(rawValue, defaultPrefix = DEFAULT_COUNTRY_PREFIX) {
51141
51153
  const differs = Boolean(e164) && onlyDigits(e164 ?? "") !== typedDigits;
51142
51154
  return { raw, display: display2, e164, dialDisplay, isValid: isValid2, differs };
51143
51155
  }
51156
+ const QUERIES = {
51157
+ fine: "(pointer: fine)",
51158
+ coarse: "(pointer: coarse)",
51159
+ hover: "(hover: hover)"
51160
+ };
51161
+ function matches(query) {
51162
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
51163
+ return null;
51164
+ }
51165
+ try {
51166
+ return window.matchMedia(query).matches;
51167
+ } catch {
51168
+ return null;
51169
+ }
51170
+ }
51171
+ function getInputProfile() {
51172
+ const fine = matches(QUERIES.fine);
51173
+ const coarse = matches(QUERIES.coarse);
51174
+ const hover = matches(QUERIES.hover);
51175
+ const pointer = coarse ? "coarse" : fine ? "fine" : "unknown";
51176
+ const touchLike = pointer === "coarse" || pointer !== "fine" && hover === false;
51177
+ return { pointer, hover: hover === true, touchLike };
51178
+ }
51179
+ function resolveDensity(density, profile) {
51180
+ if (density === "compact" || density === "comfortable") {
51181
+ return density;
51182
+ }
51183
+ return profile.touchLike ? "comfortable" : "compact";
51184
+ }
51185
+ function parseDensity(value) {
51186
+ const normalized = (value ?? "").trim().toLowerCase();
51187
+ if (normalized === "compact" || normalized === "compacta" || normalized === "compacto") return "compact";
51188
+ if (normalized === "comfortable" || normalized === "confortavel" || normalized === "confortável" || normalized === "touch") return "comfortable";
51189
+ if (normalized === "auto") return "auto";
51190
+ return void 0;
51191
+ }
51192
+ function useInputProfile() {
51193
+ const [profile, setProfile] = useState(() => getInputProfile());
51194
+ useEffect(() => {
51195
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
51196
+ return;
51197
+ }
51198
+ const update2 = () => setProfile(getInputProfile());
51199
+ const lists = [];
51200
+ for (const query of Object.values(QUERIES)) {
51201
+ try {
51202
+ const list = window.matchMedia(query);
51203
+ if (typeof list.addEventListener === "function") {
51204
+ list.addEventListener("change", update2);
51205
+ } else if (typeof list.addListener === "function") {
51206
+ list.addListener(update2);
51207
+ }
51208
+ lists.push(list);
51209
+ } catch {
51210
+ }
51211
+ }
51212
+ update2();
51213
+ return () => {
51214
+ for (const list of lists) {
51215
+ if (typeof list.removeEventListener === "function") {
51216
+ list.removeEventListener("change", update2);
51217
+ } else if (typeof list.removeListener === "function") {
51218
+ list.removeListener(update2);
51219
+ }
51220
+ }
51221
+ };
51222
+ }, []);
51223
+ return profile;
51224
+ }
51225
+ const CLICK_TO_CALL_EVENT = "callix:click-to-call";
51226
+ function presenceFromOperatorState(state) {
51227
+ if (!state) {
51228
+ return "unknown";
51229
+ }
51230
+ switch (state) {
51231
+ case "idle":
51232
+ return "available";
51233
+ case "onBreak":
51234
+ return "on_break";
51235
+ case "afterCall":
51236
+ case "manualCallFailed":
51237
+ return "after_call";
51238
+ case "offline":
51239
+ case "stopped":
51240
+ case "startupError":
51241
+ case "startup.error":
51242
+ case "networkDown":
51243
+ return "offline";
51244
+ case "starting":
51245
+ return "unknown";
51246
+ case "callRinging":
51247
+ case "manualCallSetup":
51248
+ case "manualCallRinging":
51249
+ case "callInProgress":
51250
+ return "in_call";
51251
+ default:
51252
+ return /call|ringing|dial/i.test(state) ? "in_call" : "unknown";
51253
+ }
51254
+ }
51255
+ function canDispatchTo(presence) {
51256
+ return presence === "available" || presence === "on_break";
51257
+ }
51258
+ const REQUEST_TIMEOUT_MS = 1e4;
51259
+ const DEFAULT_PRESENCE_TTL_SECONDS = 45;
51260
+ function withTimeout(signal) {
51261
+ const controller = new AbortController();
51262
+ const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
51263
+ if (signal) {
51264
+ if (signal.aborted) {
51265
+ controller.abort();
51266
+ } else {
51267
+ signal.addEventListener("abort", () => controller.abort(), { once: true });
51268
+ }
51269
+ }
51270
+ return { signal: controller.signal, clear: () => clearTimeout(timer) };
51271
+ }
51272
+ async function pollClickToCall(options) {
51273
+ const { signal, clear } = withTimeout(options.signal);
51274
+ try {
51275
+ const response = await fetch(`${options.apiBaseUrl}/api/click-to-call/poll`, {
51276
+ method: "POST",
51277
+ headers: options.headers,
51278
+ body: JSON.stringify({ state: options.state ?? null, claim: options.claim }),
51279
+ credentials: "omit",
51280
+ signal
51281
+ });
51282
+ if (!response.ok) {
51283
+ let code = null;
51284
+ try {
51285
+ const data2 = await response.json();
51286
+ code = typeof (data2 == null ? void 0 : data2.error) === "string" ? data2.error : null;
51287
+ } catch {
51288
+ }
51289
+ return { ok: false, status: response.status, code };
51290
+ }
51291
+ const data = await response.json();
51292
+ return {
51293
+ ok: true,
51294
+ presence: data.presence ?? "unknown",
51295
+ task: data.task && typeof data.task === "object" && typeof data.task.phone === "string" ? data.task : null,
51296
+ pollIntervalMs: typeof data.pollIntervalMs === "number" ? data.pollIntervalMs : 0,
51297
+ presenceTtlSeconds: typeof data.presenceTtlSeconds === "number" && data.presenceTtlSeconds > 0 ? data.presenceTtlSeconds : DEFAULT_PRESENCE_TTL_SECONDS
51298
+ };
51299
+ } finally {
51300
+ clear();
51301
+ }
51302
+ }
51303
+ async function ackClickToCall(options) {
51304
+ const { signal, clear } = withTimeout();
51305
+ try {
51306
+ const response = await fetch(`${options.apiBaseUrl}/api/click-to-call/${encodeURIComponent(options.taskId)}`, {
51307
+ method: "PATCH",
51308
+ headers: options.headers,
51309
+ body: JSON.stringify({ status: options.status, error: options.error, retry: options.retry === true }),
51310
+ credentials: "omit",
51311
+ keepalive: true,
51312
+ signal
51313
+ });
51314
+ if (!response.ok) {
51315
+ let code = null;
51316
+ try {
51317
+ const data2 = await response.json();
51318
+ code = typeof (data2 == null ? void 0 : data2.error) === "string" ? data2.error : null;
51319
+ } catch {
51320
+ }
51321
+ return { ok: false, status: response.status, code };
51322
+ }
51323
+ const data = await response.json();
51324
+ return { ok: true, task: data.task ?? null };
51325
+ } finally {
51326
+ clear();
51327
+ }
51328
+ }
51329
+ function reportClickToCallOffline(config) {
51330
+ try {
51331
+ void fetch(`${config.apiBaseUrl}/api/click-to-call/poll`, {
51332
+ method: "POST",
51333
+ headers: config.headers,
51334
+ body: JSON.stringify({ state: "offline", claim: false }),
51335
+ credentials: "omit",
51336
+ keepalive: true
51337
+ }).catch(() => void 0);
51338
+ } catch {
51339
+ }
51340
+ }
51341
+ const WORKER_SOURCE = `
51342
+ let timer = null;
51343
+ self.onmessage = (event) => {
51344
+ const interval = Number(event && event.data && event.data.interval) || 0;
51345
+ if (timer) { clearInterval(timer); timer = null; }
51346
+ if (interval > 0) { timer = setInterval(() => self.postMessage('tick'), interval); }
51347
+ };
51348
+ `;
51349
+ function createBackgroundTicker(callback, intervalMs) {
51350
+ let current = Math.max(250, Math.round(intervalMs));
51351
+ let fallbackTimer = null;
51352
+ let worker = null;
51353
+ let objectUrl = null;
51354
+ let stopped = false;
51355
+ const startFallback = () => {
51356
+ if (stopped) return;
51357
+ if (fallbackTimer) clearInterval(fallbackTimer);
51358
+ fallbackTimer = setInterval(callback, current);
51359
+ };
51360
+ try {
51361
+ if (typeof Worker !== "undefined" && typeof Blob !== "undefined" && typeof URL !== "undefined" && URL.createObjectURL) {
51362
+ objectUrl = URL.createObjectURL(new Blob([WORKER_SOURCE], { type: "application/javascript" }));
51363
+ worker = new Worker(objectUrl);
51364
+ worker.onmessage = () => {
51365
+ if (!stopped) callback();
51366
+ };
51367
+ worker.onerror = () => {
51368
+ worker == null ? void 0 : worker.terminate();
51369
+ worker = null;
51370
+ startFallback();
51371
+ };
51372
+ worker.postMessage({ interval: current });
51373
+ } else {
51374
+ startFallback();
51375
+ }
51376
+ } catch {
51377
+ worker = null;
51378
+ startFallback();
51379
+ }
51380
+ return {
51381
+ setInterval(ms2) {
51382
+ if (stopped) return;
51383
+ const next = Math.max(250, Math.round(ms2));
51384
+ if (next === current) return;
51385
+ current = next;
51386
+ if (worker) {
51387
+ worker.postMessage({ interval: current });
51388
+ } else {
51389
+ startFallback();
51390
+ }
51391
+ },
51392
+ stop() {
51393
+ stopped = true;
51394
+ if (worker) {
51395
+ worker.terminate();
51396
+ worker = null;
51397
+ }
51398
+ if (objectUrl) {
51399
+ URL.revokeObjectURL(objectUrl);
51400
+ objectUrl = null;
51401
+ }
51402
+ if (fallbackTimer) {
51403
+ clearInterval(fallbackTimer);
51404
+ fallbackTimer = null;
51405
+ }
51406
+ }
51407
+ };
51408
+ }
51409
+ function nationalDigitsFromE164(e164) {
51410
+ const digits = (e164 ?? "").replace(/\D/g, "");
51411
+ if (digits.startsWith("55") && (digits.length === 12 || digits.length === 13)) {
51412
+ return digits.slice(2);
51413
+ }
51414
+ return e164.startsWith("+") ? `+${digits}` : digits;
51415
+ }
51144
51416
  const DEFAULT_DIALER_DIMENSIONS = { width: 448, height: 640 };
51145
51417
  const MIN_DIALER_WIDTH = 280;
51146
51418
  const MIN_DIALER_HEIGHT = 420;
@@ -51344,6 +51616,17 @@ function FloatingDialer({
51344
51616
  }
51345
51617
  prevAuthRef.current = isAuthenticated;
51346
51618
  }, [isAuthenticated]);
51619
+ useEffect(() => {
51620
+ const onClickToCall = (event) => {
51621
+ const task = event.detail;
51622
+ setIsOpen(true);
51623
+ if (task == null ? void 0 : task.phone) {
51624
+ updateDialValue(nationalDigitsFromE164(task.phone).slice(0, MAX_DIAL_LENGTH));
51625
+ }
51626
+ };
51627
+ window.addEventListener(CLICK_TO_CALL_EVENT, onClickToCall);
51628
+ return () => window.removeEventListener(CLICK_TO_CALL_EVENT, onClickToCall);
51629
+ }, [updateDialValue]);
51347
51630
  const prevLoginSignalRef = useRef(false);
51348
51631
  useEffect(() => {
51349
51632
  const hasLoginSignal = !isAuthenticated && Boolean(loginConfirmation || sessionError);
@@ -51845,7 +52128,8 @@ function AuthenticatedDialer({
51845
52128
  typeof window !== "undefined" ? window.innerWidth <= 640 : false
51846
52129
  );
51847
52130
  const isMobileVariant = variant === "mobile";
51848
- const isMobileLayout = isMobileVariant || isMobileViewport;
52131
+ const inputProfile = useInputProfile();
52132
+ const isMobileLayout = isMobileVariant || isMobileViewport && inputProfile.touchLike;
51849
52133
  const breaks = useSignal(operator == null ? void 0 : operator.breaks) || [];
51850
52134
  const { goOnBreak, goOffBreak } = useOperatorBreak();
51851
52135
  const sanitizedDialValue = useMemo(() => dialValue.slice(0, MAX_DIAL_LENGTH), [dialValue]);
@@ -51879,11 +52163,16 @@ function AuthenticatedDialer({
51879
52163
  [becomeAvailable]
51880
52164
  );
51881
52165
  useEffect(() => {
51882
- if (!hasRequestedAvailabilityRef.current) {
51883
- hasRequestedAvailabilityRef.current = true;
51884
- void requestAvailability("Nao foi possivel ficar disponivel automaticamente.");
52166
+ if (hasRequestedAvailabilityRef.current) {
52167
+ return;
51885
52168
  }
51886
- }, [requestAvailability]);
52169
+ hasRequestedAvailabilityRef.current = true;
52170
+ const busyStates = ["onBreak", "manualCallSetup", "manualCallRinging", "manualCallFailed", "callRinging", "callInProgress", "afterCall"];
52171
+ if (callState && busyStates.includes(callState)) {
52172
+ return;
52173
+ }
52174
+ void requestAvailability("Nao foi possivel ficar disponivel automaticamente.");
52175
+ }, [requestAvailability, callState]);
51887
52176
  useEffect(() => {
51888
52177
  if (callState === "idle") {
51889
52178
  setAvailabilityError(null);
@@ -51968,8 +52257,14 @@ function AuthenticatedDialer({
51968
52257
  }, 8e3);
51969
52258
  return () => window.clearTimeout(timer);
51970
52259
  }, [callState]);
52260
+ const normalizedNumberRef = useRef(normalizedNumber);
52261
+ normalizedNumberRef.current = normalizedNumber;
51971
52262
  useEffect(() => {
51972
52263
  if (callState === "manualCallRinging" || callState === "callInProgress") {
52264
+ const dialed = normalizedNumberRef.current;
52265
+ if (dialed) {
52266
+ setLastDialedNumber((previous) => previous ?? dialed);
52267
+ }
51973
52268
  setDialValue("");
51974
52269
  }
51975
52270
  if (callState === "idle") {
@@ -52368,6 +52663,16 @@ function AuthenticatedDialer({
52368
52663
  return newValue;
52369
52664
  });
52370
52665
  }, [setDialValue, cursorPosition]);
52666
+ useEffect(() => {
52667
+ const onClickToCall = (event) => {
52668
+ const task = event.detail;
52669
+ if (!(task == null ? void 0 : task.phone)) return;
52670
+ setCallError(null);
52671
+ setLastDialedNumber(task.phone);
52672
+ };
52673
+ window.addEventListener(CLICK_TO_CALL_EVENT, onClickToCall);
52674
+ return () => window.removeEventListener(CLICK_TO_CALL_EVENT, onClickToCall);
52675
+ }, []);
52371
52676
  const handleCall = useCallback(async () => {
52372
52677
  if (!isIdle && !isOnBreak) {
52373
52678
  return;
@@ -54056,30 +54361,65 @@ function historyPhoneDigits(item) {
54056
54361
  }
54057
54362
  return digits;
54058
54363
  }
54364
+ function normalizeSearchText(value) {
54365
+ return (value ?? "").toString().normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/\s+/g, " ").trim();
54366
+ }
54367
+ function classifyHistoryQuery(query) {
54368
+ const raw = (query ?? "").trim();
54369
+ if (!raw) {
54370
+ return { kind: "empty", digits: "", text: "" };
54371
+ }
54372
+ const digits = onlyDigits(raw);
54373
+ const nonPhoneChars = raw.replace(/[\d\s()+\-.]/g, "");
54374
+ if (digits.length >= 3 && nonPhoneChars.length === 0) {
54375
+ return { kind: "phone", digits, text: normalizeSearchText(raw) };
54376
+ }
54377
+ return { kind: "text", digits, text: normalizeSearchText(raw) };
54378
+ }
54379
+ function historySearchHaystack(item) {
54380
+ const parts = [
54381
+ item.formattedPhone,
54382
+ historyPhoneDigits(item),
54383
+ item.callTypeLabel,
54384
+ item.campaignName,
54385
+ item.contactLabel,
54386
+ item.qualificationName,
54387
+ item.failureCauseLabel,
54388
+ item.hangupCauseLabel,
54389
+ item.formattedDate,
54390
+ item.note,
54391
+ item.protocol !== void 0 ? String(item.protocol) : void 0
54392
+ ];
54393
+ if (item.fields) {
54394
+ for (const value of Object.values(item.fields)) {
54395
+ if (value !== void 0 && value !== null) {
54396
+ parts.push(String(value));
54397
+ }
54398
+ }
54399
+ }
54400
+ return normalizeSearchText(parts.filter(Boolean).join(" | "));
54401
+ }
54402
+ function itemMatchesHistoryQuery(item, query) {
54403
+ if (query.kind === "empty") {
54404
+ return true;
54405
+ }
54406
+ if (query.kind === "phone") {
54407
+ return historyPhoneDigits(item).includes(query.digits) || onlyDigits(item.phoneNumber).includes(query.digits) || item.protocol !== void 0 && String(item.protocol).includes(query.digits);
54408
+ }
54409
+ const haystack = historySearchHaystack(item);
54410
+ const terms = query.text.split(" ").filter(Boolean);
54411
+ return terms.every((term) => haystack.includes(term));
54412
+ }
54413
+ function filterHistoryItems(items, query) {
54414
+ const parsed = typeof query === "string" ? classifyHistoryQuery(query) : query;
54415
+ if (parsed.kind === "empty") {
54416
+ return items;
54417
+ }
54418
+ return items.filter((item) => itemMatchesHistoryQuery(item, parsed));
54419
+ }
54059
54420
  const DEFAULT_QUALIFICATION = "chamada sem qualificação";
54421
+ const SEARCH_DEBOUNCE_MS = 350;
54060
54422
  const DETAILS_TITLE = "Ver detalhes da chamada";
54061
- async function fetchJson(url, headers) {
54062
- const response = await fetch(url, { method: "GET", headers, credentials: "omit" });
54063
- if (!response.ok) {
54064
- const body = await response.json().catch(() => ({}));
54065
- throw new Error(body.error || `Erro ${response.status}`);
54066
- }
54067
- return await response.json();
54068
- }
54069
- async function fetchCampaignForm(apiBaseUrl, contactId, headers) {
54070
- const config = await fetchJson(`${apiBaseUrl}/api/campaign-config/${contactId}`, headers);
54071
- if (!(config == null ? void 0 : config.campaignModelId)) {
54072
- throw new Error("Formulário da campanha não encontrado");
54073
- }
54074
- const [fields, values] = await Promise.all([
54075
- fetchJson(`${apiBaseUrl}/api/campaign-fields/${config.campaignModelId}`, headers),
54076
- fetchJson(`${apiBaseUrl}/api/campaign-contact/${contactId}`, headers)
54077
- ]);
54078
- return {
54079
- rows: Array.isArray(fields == null ? void 0 : fields.rows) ? fields.rows.filter((row) => Array.isArray(row) && row.length > 0) : [],
54080
- values: values && typeof values === "object" ? values : {}
54081
- };
54082
- }
54083
54423
  function useRecordings(apiBaseUrl, getAuthHeaders2) {
54084
54424
  const [states, setStates] = useState({});
54085
54425
  const statesRef = useRef(states);
@@ -54335,7 +54675,7 @@ function CallDetailsSheet({
54335
54675
  onCall,
54336
54676
  onWhatsApp
54337
54677
  }) {
54338
- const [form, setForm] = useState({ status: "idle", rows: [], values: {} });
54678
+ const [form, setForm] = useState({ status: "idle", form: null });
54339
54679
  const closeButtonRef = useRef(null);
54340
54680
  const isCampaign = item.callType === "campaign";
54341
54681
  useEffect(() => {
@@ -54361,65 +54701,79 @@ function CallDetailsSheet({
54361
54701
  }, [onClose]);
54362
54702
  useEffect(() => {
54363
54703
  if (!isCampaign || !item.campaignContactId) {
54364
- setForm({ status: "idle", rows: [], values: {} });
54704
+ setForm({ status: "idle", form: null });
54365
54705
  return;
54366
54706
  }
54367
54707
  let cancelled = false;
54368
- setForm({ status: "loading", rows: [], values: {} });
54369
- fetchCampaignForm(apiBaseUrl, item.campaignContactId, getAuthHeaders2()).then((result2) => {
54708
+ setForm({ status: "loading", form: null });
54709
+ fetchCampaignForm(
54710
+ {
54711
+ source: "campaign",
54712
+ id: item.campaignContactId,
54713
+ campaignId: item.campaignId ?? null,
54714
+ signature: item.formSignature ?? null,
54715
+ expiresAt: item.formExpiresAt ?? null
54716
+ },
54717
+ { apiBaseUrl, headers: getAuthHeaders2(), retries: 0 }
54718
+ ).then((result2) => {
54370
54719
  if (!cancelled) {
54371
- setForm({ status: "ready", rows: result2.rows, values: result2.values });
54720
+ setForm({ status: "ready", form: result2 });
54372
54721
  }
54373
54722
  }).catch((error2) => {
54374
54723
  if (!cancelled) {
54375
54724
  console.warn("[CallHistoryList] Formulário da campanha indisponível, usando campos do histórico:", error2);
54376
- setForm({
54377
- status: "error",
54378
- rows: [],
54379
- values: {},
54380
- error: error2 instanceof Error ? error2.message : "Erro ao carregar formulário"
54381
- });
54725
+ setForm({ status: "error", form: null, error: error2 instanceof Error ? error2.message : "Erro ao carregar formulário" });
54382
54726
  }
54383
54727
  });
54384
54728
  return () => {
54385
54729
  cancelled = true;
54386
54730
  };
54387
- }, [apiBaseUrl, getAuthHeaders2, isCampaign, item.campaignContactId, item.id]);
54731
+ }, [apiBaseUrl, getAuthHeaders2, isCampaign, item.campaignContactId, item.campaignId, item.id]);
54388
54732
  const fallbackFields = useMemo(
54389
54733
  () => Object.entries(item.fields ?? {}).filter(
54390
54734
  ([key, value]) => value !== void 0 && value !== null && String(value).trim() !== "" && !isInternalFieldKey(key)
54391
54735
  ),
54392
54736
  [item.fields]
54393
54737
  );
54738
+ const fallbackByKey = useMemo(() => {
54739
+ const map = /* @__PURE__ */ new Map();
54740
+ for (const [key, value] of Object.entries(item.fields ?? {})) {
54741
+ if (value === void 0 || value === null || String(value).trim() === "") continue;
54742
+ map.set(stripAccents(normalizeFormKey(key)), String(value));
54743
+ }
54744
+ return map;
54745
+ }, [item.fields]);
54394
54746
  const resolveFieldValue = (field) => {
54395
- var _a2;
54396
- const byCode = form.values[field.code];
54397
- if (byCode !== void 0 && byCode !== null && String(byCode).trim() !== "") {
54398
- return String(byCode);
54399
- }
54400
- const byName = (_a2 = item.fields) == null ? void 0 : _a2[field.name];
54401
- if (byName !== void 0 && byName !== null && String(byName).trim() !== "") {
54402
- return String(byName);
54747
+ if (field.value && field.value.trim() !== "") {
54748
+ return field.value;
54403
54749
  }
54404
- return "-";
54750
+ const byName = fallbackByKey.get(stripAccents(normalizeFormKey(field.label))) ?? fallbackByKey.get(stripAccents(normalizeFormKey(field.key)));
54751
+ return byName ?? "-";
54405
54752
  };
54406
- const hasFormRows = form.status === "ready" && form.rows.length > 0;
54753
+ const formRows = form.status === "ready" && form.form ? form.form.rows && form.form.rows.length > 0 ? form.form.rows : form.form.fields.length > 0 ? [form.form.fields] : [] : [];
54754
+ const hasFormRows = formRows.length > 0;
54407
54755
  let formContent;
54408
- if (form.status === "loading") {
54756
+ if (form.status === "loading" && fallbackFields.length === 0) {
54409
54757
  formContent = /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "details-loading", role: "status", children: [
54410
54758
  /* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fas fa-spinner fa-spin", "aria-hidden": "true" }),
54411
54759
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Carregando formulário..." })
54412
54760
  ] });
54413
54761
  } else if (hasFormRows) {
54414
- formContent = form.rows.map((row, rowIndex) => /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "details-form-row", children: row.map((field, fieldIndex) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "details-field", children: [
54415
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "details-field-label", title: field.name, children: field.name }),
54762
+ formContent = formRows.map((row, rowIndex) => /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "details-form-row", children: row.map((field, fieldIndex) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "details-field", children: [
54763
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "details-field-label", title: field.label, children: field.label }),
54416
54764
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "details-field-value", children: resolveFieldValue(field) })
54417
- ] }, `${rowIndex}-${fieldIndex}-${field.code}`)) }, rowIndex));
54765
+ ] }, `${rowIndex}-${fieldIndex}-${field.key}`)) }, rowIndex));
54418
54766
  } else if (fallbackFields.length > 0) {
54419
- formContent = /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "details-form-row", children: fallbackFields.map(([key, value]) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "details-field", children: [
54420
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "details-field-label", title: key, children: key }),
54421
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "details-field-value", children: value })
54422
- ] }, key)) });
54767
+ formContent = /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
54768
+ form.status === "loading" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "details-loading details-loading-inline", role: "status", children: [
54769
+ /* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fas fa-spinner fa-spin", "aria-hidden": "true" }),
54770
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Atualizando formulário..." })
54771
+ ] }),
54772
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "details-form-row", children: fallbackFields.map(([key, value]) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "details-field", children: [
54773
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "details-field-label", title: key, children: key }),
54774
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "details-field-value", children: value })
54775
+ ] }, key)) })
54776
+ ] });
54423
54777
  } else {
54424
54778
  formContent = /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "details-empty", children: [
54425
54779
  /* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fas fa-user", "aria-hidden": "true" }),
@@ -54528,12 +54882,52 @@ function CallHistoryList({
54528
54882
  onCall,
54529
54883
  onWhatsApp,
54530
54884
  apiBaseUrl,
54531
- getAuthHeaders: getAuthHeaders2
54885
+ getAuthHeaders: getAuthHeaders2,
54886
+ query,
54887
+ onQueryChange,
54888
+ isSearching = false,
54889
+ searchHint = null
54532
54890
  }) {
54533
54891
  const [openPlayerId, setOpenPlayerId] = useState(null);
54534
54892
  const [selectedId, setSelectedId] = useState(null);
54535
54893
  const recordings = useRecordings(apiBaseUrl, getAuthHeaders2);
54536
54894
  const { retain } = recordings;
54895
+ const [draft, setDraft] = useState(query ?? "");
54896
+ const lastEmittedRef = useRef(query ?? "");
54897
+ const debounceRef = useRef(null);
54898
+ useEffect(() => {
54899
+ const external = query ?? "";
54900
+ if (external !== lastEmittedRef.current) {
54901
+ lastEmittedRef.current = external;
54902
+ setDraft(external);
54903
+ }
54904
+ }, [query]);
54905
+ useEffect(() => {
54906
+ return () => {
54907
+ if (debounceRef.current) clearTimeout(debounceRef.current);
54908
+ };
54909
+ }, []);
54910
+ const handleDraftChange = useCallback(
54911
+ (value) => {
54912
+ setDraft(value);
54913
+ if (!onQueryChange) return;
54914
+ if (debounceRef.current) clearTimeout(debounceRef.current);
54915
+ debounceRef.current = setTimeout(() => {
54916
+ lastEmittedRef.current = value;
54917
+ onQueryChange(value);
54918
+ }, SEARCH_DEBOUNCE_MS);
54919
+ },
54920
+ [onQueryChange]
54921
+ );
54922
+ const clearSearch = useCallback(() => {
54923
+ setDraft("");
54924
+ if (debounceRef.current) clearTimeout(debounceRef.current);
54925
+ lastEmittedRef.current = "";
54926
+ onQueryChange == null ? void 0 : onQueryChange("");
54927
+ }, [onQueryChange]);
54928
+ const parsedQuery = useMemo(() => classifyHistoryQuery(draft), [draft]);
54929
+ const visibleItems = useMemo(() => filterHistoryItems(items, parsedQuery), [items, parsedQuery]);
54930
+ const hasQuery = parsedQuery.kind !== "empty";
54537
54931
  const dayLabels = useMemo(() => {
54538
54932
  const now = /* @__PURE__ */ new Date();
54539
54933
  const labels = {};
@@ -54583,6 +54977,28 @@ function CallHistoryList({
54583
54977
  /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { className: "header-title", children: "Recentes" }),
54584
54978
  /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "history-refresh-btn", onClick: onRefresh, disabled: isLoading, "aria-label": "Atualizar histórico", children: /* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: `fas fa-arrows-rotate ${isLoading ? "fa-spin" : ""}`, "aria-hidden": "true" }) })
54585
54979
  ] }),
54980
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "history-search-bar", children: [
54981
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "history-search", children: [
54982
+ /* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fas fa-magnifying-glass history-search-icon", "aria-hidden": "true" }),
54983
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
54984
+ "input",
54985
+ {
54986
+ type: "search",
54987
+ className: "history-search-input",
54988
+ placeholder: "Buscar número, campanha, cliente...",
54989
+ "aria-label": "Buscar no histórico",
54990
+ autoComplete: "off",
54991
+ autoCapitalize: "none",
54992
+ autoCorrect: "off",
54993
+ spellCheck: false,
54994
+ value: draft,
54995
+ onChange: (event) => handleDraftChange(event.target.value)
54996
+ }
54997
+ ),
54998
+ isSearching ? /* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fas fa-spinner fa-spin history-search-spinner", "aria-hidden": "true", title: "Buscando..." }) : draft ? /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "history-search-clear", onClick: clearSearch, "aria-label": "Limpar busca", children: /* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fas fa-xmark", "aria-hidden": "true" }) }) : null
54999
+ ] }),
55000
+ hasQuery && searchHint && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "history-search-hint", children: searchHint })
55001
+ ] }),
54586
55002
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "call-history-list", id: "callHistoryList", children: isLoading && items.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "history-loading", role: "status", children: [
54587
55003
  /* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fas fa-spinner fa-spin", "aria-hidden": "true" }),
54588
55004
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Carregando..." })
@@ -54593,7 +55009,18 @@ function CallHistoryList({
54593
55009
  ] }) : items.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "history-empty", children: [
54594
55010
  /* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fas fa-phone-slash", "aria-hidden": "true" }),
54595
55011
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Nenhuma ligação recente" })
54596
- ] }) : items.map((item) => /* @__PURE__ */ jsxRuntimeExports.jsx(
55012
+ ] }) : visibleItems.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "history-empty history-empty-search", children: [
55013
+ /* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fas fa-magnifying-glass", "aria-hidden": "true" }),
55014
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
55015
+ "Nenhuma chamada encontrada para ",
55016
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("strong", { children: [
55017
+ "“",
55018
+ draft.trim(),
55019
+ "”"
55020
+ ] })
55021
+ ] }),
55022
+ isSearching && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "history-search-status", children: "Buscando no servidor..." })
55023
+ ] }) : visibleItems.map((item) => /* @__PURE__ */ jsxRuntimeExports.jsx(
54597
55024
  HistoryRow,
54598
55025
  {
54599
55026
  item,
@@ -56073,99 +56500,17 @@ function formatDuration(seconds) {
56073
56500
  }
56074
56501
  return `${mins}:${secs.toString().padStart(2, "0")}`;
56075
56502
  }
56076
- async function fetchWithRetry(url, options, retries = 3, backoff = 1e3) {
56077
- console.log(`[DraggableClientInfoModal] 🚀 Iniciando fetch: ${url}`);
56078
- for (let i2 = 0; i2 < retries; i2++) {
56079
- try {
56080
- console.log(`[DraggableClientInfoModal] 📡 Tentativa ${i2 + 1}/${retries}`);
56081
- const startTime = Date.now();
56082
- const response = await fetch(url, options);
56083
- const duration = Date.now() - startTime;
56084
- console.log(`[DraggableClientInfoModal] ⏱️ Resposta em ${duration}ms - Status: ${response.status}`);
56085
- if (!response.ok) {
56086
- if (response.status >= 400 && response.status < 500) {
56087
- const error2 = await response.json().catch(() => ({ error: "Erro desconhecido" }));
56088
- console.error(`[DraggableClientInfoModal] ❌ Erro do cliente (${response.status}):`, error2);
56089
- throw new Error(error2.error || `Falha: ${response.status}`);
56090
- }
56091
- console.warn(`[DraggableClientInfoModal] ⚠️ Erro do servidor (${response.status}), tentando novamente...`);
56092
- throw new Error(`HTTP ${response.status}`);
56093
- }
56094
- const json = await response.json();
56095
- console.log(`[DraggableClientInfoModal] ✅ Dados recebidos com sucesso`);
56096
- return json;
56097
- } catch (error2) {
56098
- const isLastAttempt = i2 === retries - 1;
56099
- if (isLastAttempt) {
56100
- console.error(`[DraggableClientInfoModal] 💥 Falha final após ${retries} tentativas:`, error2);
56101
- throw error2;
56102
- }
56103
- const delay = backoff * Math.pow(2, i2);
56104
- console.warn(`[DraggableClientInfoModal] ⏳ Aguardando ${delay}ms antes da próxima tentativa...`);
56105
- await new Promise((resolve) => setTimeout(resolve, delay));
56106
- }
56107
- }
56108
- throw new Error("Todas as tentativas falharam");
56109
- }
56110
- async function getCampaignConfig$1(contactId) {
56111
- const url = `${API_BASE_URL}/api/campaign-config/${contactId}`;
56112
- return fetchWithRetry(url, {
56113
- method: "GET",
56114
- headers: getAuthHeaders(),
56115
- credentials: "omit"
56116
- });
56117
- }
56118
- async function getCampaignFields(campaignModelId) {
56119
- const url = `${API_BASE_URL}/api/campaign-fields/${campaignModelId}`;
56120
- const json = await fetchWithRetry(url, {
56121
- method: "GET",
56122
- headers: getAuthHeaders(),
56123
- credentials: "omit"
56124
- });
56125
- console.log("[DraggableClientInfoModal] Estrutura de campos recebida:", json);
56126
- if (!json.rows || !Array.isArray(json.rows)) {
56127
- console.warn("[DraggableClientInfoModal] Estrutura de campos inválida, usando fallback");
56128
- return {
56129
- rows: [],
56130
- labelField: null,
56131
- phoneFields: []
56132
- };
56133
- }
56134
- return json;
56135
- }
56136
- async function getCustomerFields(formId) {
56137
- const url = `${API_BASE_URL}/api/campaign-contact/0?source=customer&formId=${encodeURIComponent(String(formId))}`;
56138
- const json = await fetchWithRetry(url, {
56139
- method: "GET",
56140
- headers: getAuthHeaders(),
56141
- credentials: "omit"
56142
- });
56143
- console.log("[DraggableClientInfoModal] (cliente) Estrutura de campos recebida:", json);
56144
- if (!json.rows || !Array.isArray(json.rows)) {
56145
- console.warn("[DraggableClientInfoModal] (cliente) Estrutura de campos inválida, usando fallback");
56146
- return { rows: [], labelField: null, phoneFields: [] };
56147
- }
56148
- return json;
56149
- }
56150
- async function getContactValues$1(contactId) {
56151
- const url = `${API_BASE_URL}/api/campaign-contact/${contactId}`;
56152
- return fetchWithRetry(url, {
56153
- method: "GET",
56154
- headers: getAuthHeaders(),
56155
- credentials: "omit"
56156
- });
56157
- }
56158
- async function getCustomerContactValues(customerId) {
56159
- const url = `${API_BASE_URL}/api/campaign-fields/0?source=customer&customerId=${encodeURIComponent(String(customerId))}`;
56160
- return fetchWithRetry(url, {
56161
- method: "GET",
56162
- headers: getAuthHeaders(),
56163
- credentials: "omit"
56164
- });
56503
+ function gridColsFor(count) {
56504
+ if (count === 2) return "grid-cols-2";
56505
+ if (count === 3) return "grid-cols-3";
56506
+ if (count === 4) return "grid-cols-4";
56507
+ if (count >= 5) return "grid-cols-5";
56508
+ return "grid-cols-1";
56165
56509
  }
56166
56510
  function DraggableClientInfoModal({
56167
56511
  isOpen,
56168
56512
  campaignCallInfo,
56513
+ campaignContactPhoneId,
56169
56514
  callDuration,
56170
56515
  isCallActive,
56171
56516
  onClose
@@ -56175,12 +56520,19 @@ function DraggableClientInfoModal({
56175
56520
  const dragMovedRef = useRef(false);
56176
56521
  const [position2, setPosition] = useState({ x: 100, y: 100 });
56177
56522
  const [isDragging, setIsDragging] = useState(false);
56178
- const [contactData, setContactData] = useState({});
56179
- const [fieldRows, setFieldRows] = useState([]);
56523
+ const [form, setForm] = useState(null);
56180
56524
  const [isLoading, setIsLoading] = useState(false);
56181
56525
  const [error2, setError] = useState(null);
56182
- const [fetchedContactId, setFetchedContactId] = useState(null);
56526
+ const [retry, setRetry] = useState({ key: null, n: 0 });
56183
56527
  const campaignContact = campaignCallInfo == null ? void 0 : campaignCallInfo.campaignContact;
56528
+ const request = useMemo(
56529
+ () => campaignFormRequestFromCallInfo(campaignCallInfo, campaignContactPhoneId),
56530
+ [campaignCallInfo, campaignContactPhoneId]
56531
+ );
56532
+ const requestKey = request ? campaignFormCacheKey(request) : null;
56533
+ const requestRef = useRef(request);
56534
+ requestRef.current = request;
56535
+ const partialRefreshKeyRef = useRef(null);
56184
56536
  const clampPositionToViewport = useCallback((proposed) => {
56185
56537
  if (typeof window === "undefined") {
56186
56538
  return proposed;
@@ -56224,62 +56576,40 @@ function DraggableClientInfoModal({
56224
56576
  };
56225
56577
  }, [isOpen, clampPositionToViewport]);
56226
56578
  useEffect(() => {
56227
- if (!(campaignContact == null ? void 0 : campaignContact.id)) {
56228
- return;
56229
- }
56230
- if (fetchedContactId === campaignContact.id) {
56579
+ const current = requestRef.current;
56580
+ if (!current || !requestKey) {
56581
+ setForm(null);
56582
+ setError(null);
56583
+ setIsLoading(false);
56231
56584
  return;
56232
56585
  }
56233
56586
  let cancelled = false;
56234
- const fetchContactData = async () => {
56235
- setIsLoading(true);
56236
- setError(null);
56237
- try {
56238
- const customer = campaignContact.customer;
56239
- console.log("[DraggableClientInfoModal] Verificando customer em campaignContact:", customer);
56240
- let campaignFields;
56241
- let contactValues;
56242
- if ((customer == null ? void 0 : customer.id) && (customer == null ? void 0 : customer.formId)) {
56243
- console.log("[DraggableClientInfoModal] Modo CUSTOMER: carregando form-rows (formId) e form-data (customerId)");
56244
- [campaignFields, contactValues] = await Promise.all([
56245
- getCustomerFields(customer.formId),
56246
- getCustomerContactValues(customer.id)
56247
- ]);
56248
- } else {
56249
- console.log("[DraggableClientInfoModal] Modo CAMPANHA: buscando config, campos e valores");
56250
- const config = await getCampaignConfig$1(campaignContact.id);
56251
- console.log("[DraggableClientInfoModal] Config recebida:", config);
56252
- if (!config.campaignModelId) {
56253
- throw new Error("campaignModelId não encontrado");
56254
- }
56255
- [campaignFields, contactValues] = await Promise.all([
56256
- getCampaignFields(config.campaignModelId),
56257
- getContactValues$1(campaignContact.id)
56258
- ]);
56259
- }
56260
- console.log("[DraggableClientInfoModal] Campos recebidos:", campaignFields);
56261
- console.log("[DraggableClientInfoModal] Valores recebidos:", contactValues);
56262
- if (!cancelled) {
56263
- console.log("[DraggableClientInfoModal] ✅ Dados carregados com sucesso");
56264
- setContactData(contactValues);
56265
- setFieldRows(campaignFields.rows || []);
56266
- setFetchedContactId(campaignContact.id);
56267
- setIsLoading(false);
56268
- }
56269
- } catch (err) {
56270
- if (!cancelled) {
56271
- console.error("[DraggableClientInfoModal] ❌ Erro ao buscar dados:", err);
56272
- const errorMessage = err instanceof Error ? err.message : "Erro ao buscar dados do contato";
56273
- setError(errorMessage);
56274
- setIsLoading(false);
56587
+ let refreshTimer = null;
56588
+ setIsLoading(true);
56589
+ setError(null);
56590
+ fetchCampaignForm(current, { force: retry.key === requestKey && retry.n > 0 }).then((result2) => {
56591
+ if (!cancelled) {
56592
+ console.log("[DraggableClientInfoModal] ✅ Formulário carregado:", result2.valuesSource, result2.layoutSource, result2.fields.length, "campos", result2.partial ? "(parcial)" : "");
56593
+ setForm(result2);
56594
+ setIsLoading(false);
56595
+ if (result2.partial && partialRefreshKeyRef.current !== requestKey) {
56596
+ partialRefreshKeyRef.current = requestKey;
56597
+ refreshTimer = setTimeout(() => setRetry((prev) => ({ key: requestKey, n: prev.n + 1 })), 3e3);
56275
56598
  }
56276
56599
  }
56277
- };
56278
- void fetchContactData();
56600
+ }).catch((err) => {
56601
+ if (!cancelled) {
56602
+ console.error("[DraggableClientInfoModal] ❌ Erro ao buscar o formulário:", err);
56603
+ setForm(null);
56604
+ setError(err instanceof Error ? err.message : "Erro ao buscar dados do contato");
56605
+ setIsLoading(false);
56606
+ }
56607
+ });
56279
56608
  return () => {
56280
56609
  cancelled = true;
56610
+ if (refreshTimer) clearTimeout(refreshTimer);
56281
56611
  };
56282
- }, [campaignContact, campaignContact == null ? void 0 : campaignContact.id, fetchedContactId]);
56612
+ }, [requestKey, retry]);
56283
56613
  useEffect(() => {
56284
56614
  if (!isDragging) {
56285
56615
  return;
@@ -56341,14 +56671,16 @@ function DraggableClientInfoModal({
56341
56671
  onClose();
56342
56672
  }
56343
56673
  }, [isCallActive, onClose]);
56674
+ const handleRetry = useCallback(() => {
56675
+ setRetry((prev) => ({ key: requestKey, n: prev.n + 1 }));
56676
+ }, [requestKey]);
56344
56677
  if (!isOpen) {
56345
56678
  return null;
56346
56679
  }
56347
- const hasData = Object.keys(contactData).length > 0;
56348
- const hasFieldStructure = fieldRows && fieldRows.length > 0;
56349
- const getFieldValue = (fieldCode) => {
56350
- return contactData[fieldCode] || "-";
56351
- };
56680
+ const rows = (form == null ? void 0 : form.rows) && form.rows.length > 0 ? form.rows : [];
56681
+ const hasRows = rows.length > 0;
56682
+ const hasFields = Boolean(form && form.fields.length > 0);
56683
+ const hasData = hasRows || hasFields;
56352
56684
  const modalContent = /* @__PURE__ */ jsxRuntimeExports.jsxs(
56353
56685
  "div",
56354
56686
  {
@@ -56403,46 +56735,36 @@ function DraggableClientInfoModal({
56403
56735
  ] })
56404
56736
  ] })
56405
56737
  ] }),
56406
- isLoading && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: tw`rounded-lg border border-blue-200 bg-blue-50 p-6 text-center`, children: [
56738
+ isLoading && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: tw`rounded-lg border border-blue-200 bg-blue-50 p-6 text-center`, role: "status", children: [
56407
56739
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: tw`mx-auto mb-2 inline-block h-6 w-6 animate-spin rounded-full border-2 border-blue-500 border-t-transparent` }),
56408
56740
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`text-xs font-medium text-blue-700`, children: "Carregando dados do contato..." })
56409
56741
  ] }),
56410
- error2 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: tw`rounded-lg border border-red-200 bg-red-50 p-3`, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: tw`flex items-center gap-2`, children: [
56742
+ error2 && !isLoading && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: tw`rounded-lg border border-red-200 bg-red-50 p-3`, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: tw`flex items-center gap-2`, children: [
56411
56743
  /* @__PURE__ */ jsxRuntimeExports.jsx(FiAlertCircle, { className: tw`h-4 w-4 flex-shrink-0 text-red-600` }),
56412
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
56744
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: tw`min-w-0 flex-1`, children: [
56413
56745
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`text-xs font-semibold text-red-700`, children: "Erro ao carregar dados" }),
56414
56746
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`text-[10px] text-red-600`, children: error2 })
56415
- ] })
56416
- ] }) }),
56417
- !isLoading && !error2 && hasData && hasFieldStructure && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: tw`space-y-2`, children: fieldRows.map((row, rowIndex) => {
56418
- const numFields = row.length;
56419
- let gridCols = "grid-cols-1";
56420
- if (numFields === 2) {
56421
- gridCols = "grid-cols-2";
56422
- } else if (numFields === 3) {
56423
- gridCols = "grid-cols-3";
56424
- } else if (numFields === 4) {
56425
- gridCols = "grid-cols-4";
56426
- } else if (numFields === 5) {
56427
- gridCols = "grid-cols-5";
56428
- }
56429
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
56430
- "div",
56747
+ ] }),
56748
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
56749
+ "button",
56431
56750
  {
56432
- className: tw`grid gap-2 ${gridCols}`,
56433
- children: row.map((field, fieldIndex) => /* @__PURE__ */ jsxRuntimeExports.jsx(
56434
- Field,
56435
- {
56436
- label: field.name,
56437
- value: getFieldValue(field.code),
56438
- fullWidth: field.width === "100%"
56439
- },
56440
- `${rowIndex}-${fieldIndex}-${field.code}`
56441
- ))
56442
- },
56443
- rowIndex
56444
- );
56445
- }) }),
56751
+ type: "button",
56752
+ onClick: handleRetry,
56753
+ className: tw`rounded-md border border-red-300 bg-white px-2 py-1 text-[11px] font-semibold text-red-700 transition hover:bg-red-100`,
56754
+ children: "Tentar novamente"
56755
+ }
56756
+ )
56757
+ ] }) }),
56758
+ !isLoading && !error2 && hasRows && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: tw`space-y-2`, children: rows.map((row, rowIndex) => /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: tw`grid gap-2 ${gridColsFor(row.length)}`, children: row.map((field, fieldIndex) => /* @__PURE__ */ jsxRuntimeExports.jsx(
56759
+ Field,
56760
+ {
56761
+ label: field.label,
56762
+ value: field.value || "-",
56763
+ fullWidth: field.width === "100%"
56764
+ },
56765
+ `${rowIndex}-${fieldIndex}-${field.key}`
56766
+ )) }, rowIndex)) }),
56767
+ !isLoading && !error2 && !hasRows && hasFields && form && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: tw`grid grid-cols-2 gap-2`, children: form.fields.map((field) => /* @__PURE__ */ jsxRuntimeExports.jsx(Field, { label: field.label, value: field.value || "-" }, field.key)) }),
56446
56768
  !isLoading && !error2 && !hasData && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: tw`flex flex-col items-center justify-center rounded-lg border border-slate-200 bg-slate-50 px-4 py-8 text-center`, children: [
56447
56769
  /* @__PURE__ */ jsxRuntimeExports.jsx(FiUser, { className: tw`mb-2 h-8 w-8 text-slate-400` }),
56448
56770
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`text-sm text-slate-500`, children: "Nenhum dado disponível" })
@@ -56563,104 +56885,8 @@ if (typeof window !== "undefined" && !window.__fetchIntercepted__) {
56563
56885
  };
56564
56886
  console.log("%c[FETCH INTERCEPTOR] ✅ Ativo - todas as requisições serão logadas", "color: #2196F3; font-weight: bold; font-size: 12px");
56565
56887
  }
56566
- async function getCampaignConfig(contactId) {
56567
- const url = `${API_BASE_URL}/api/campaign-config/${contactId}`;
56568
- const headers = getAuthHeaders();
56569
- console.log("[getCampaignConfig] URL:", url);
56570
- console.log("[getCampaignConfig] Headers:", JSON.stringify(headers));
56571
- const response = await fetch(url, { method: "GET", headers, credentials: "omit" });
56572
- if (!response.ok) {
56573
- const errorBody = await response.json().catch(() => ({ error: "Erro desconhecido" }));
56574
- console.error("[getCampaignConfig] Erro:", response.status, errorBody);
56575
- if (response.status === 401) {
56576
- const token = getSessionToken();
56577
- console.error("[getCampaignConfig] 401 - Token atual:", token ? `${token.substring(0, 20)}...` : "NULL");
56578
- console.error("[getCampaignConfig] 401 - Resposta completa:", JSON.stringify(errorBody));
56579
- }
56580
- throw new Error(errorBody.error || `Falha ao buscar config: ${response.status}`);
56581
- }
56582
- return response.json();
56583
- }
56584
- function processRowsToMapping(apiResponse) {
56585
- const mapping = {};
56586
- console.log("[processRowsToMapping] Processando estrutura:", typeof apiResponse);
56587
- if (!apiResponse || typeof apiResponse !== "object") {
56588
- console.warn("[processRowsToMapping] Resposta inválida:", apiResponse);
56589
- return mapping;
56590
- }
56591
- const data = apiResponse;
56592
- if (!data.rows || !Array.isArray(data.rows)) {
56593
- console.warn("[processRowsToMapping] rows não encontrado ou não é array:", data);
56594
- return mapping;
56595
- }
56596
- console.log("[processRowsToMapping] Total de linhas (rows):", data.rows.length);
56597
- for (let rowIndex = 0; rowIndex < data.rows.length; rowIndex++) {
56598
- const row = data.rows[rowIndex];
56599
- if (!Array.isArray(row)) {
56600
- console.warn(`[processRowsToMapping] Row ${rowIndex} não é array:`, row);
56601
- continue;
56602
- }
56603
- for (const field of row) {
56604
- if (field && typeof field === "object" && "code" in field && "name" in field) {
56605
- const fieldObj = field;
56606
- if (fieldObj.code && fieldObj.name) {
56607
- console.log(`[processRowsToMapping] ✓ Mapeando: "${fieldObj.code}" -> "${fieldObj.name}"`);
56608
- mapping[fieldObj.code] = fieldObj.name;
56609
- }
56610
- }
56611
- }
56612
- }
56613
- console.log("[processRowsToMapping] Mapeamento final:", mapping);
56614
- console.log("[processRowsToMapping] Total de campos mapeados:", Object.keys(mapping).length);
56615
- return mapping;
56616
- }
56617
- function applyMappingToValues(mapping, values) {
56618
- const finalData = {};
56619
- console.log("[applyMappingToValues] Aplicando mapeamento...");
56620
- console.log("[applyMappingToValues] Mapping keys:", Object.keys(mapping));
56621
- console.log("[applyMappingToValues] Values keys:", Object.keys(values));
56622
- for (const [code, value] of Object.entries(values)) {
56623
- const name = mapping[code];
56624
- if (name) {
56625
- console.log(`[applyMappingToValues] ✓ "${code}" -> "${name}" = "${value}"`);
56626
- finalData[name] = value;
56627
- } else {
56628
- console.log(`[applyMappingToValues] ✗ "${code}" não tem mapeamento (valor: "${value}")`);
56629
- }
56630
- }
56631
- console.log("[applyMappingToValues] Final data:", finalData);
56632
- return finalData;
56633
- }
56634
- async function fetchCampaignFieldsRaw(campaignModelId) {
56635
- const url = `${API_BASE_URL}/api/campaign-fields/${campaignModelId}`;
56636
- console.log("[fetchCampaignFieldsRaw] URL:", url);
56637
- const response = await fetch(url, { method: "GET", headers: getAuthHeaders(), credentials: "omit" });
56638
- if (!response.ok) {
56639
- const error2 = await response.json().catch(() => ({ error: "Erro desconhecido" }));
56640
- throw new Error(error2.error || `Falha ao buscar campos: ${response.status}`);
56641
- }
56642
- const json = await response.json();
56643
- console.log("[fetchCampaignFieldsRaw] Resposta recebida:", JSON.stringify(json, null, 2).substring(0, 500));
56644
- return json;
56645
- }
56646
- async function getContactValues(contactId) {
56647
- const url = `${API_BASE_URL}/api/campaign-contact/${contactId}`;
56648
- const response = await fetch(url, { method: "GET", headers: getAuthHeaders(), credentials: "omit" });
56649
- if (!response.ok) {
56650
- const error2 = await response.json().catch(() => ({ error: "Erro desconhecido" }));
56651
- throw new Error(error2.error || `Falha ao buscar valores: ${response.status}`);
56652
- }
56653
- return response.json();
56654
- }
56655
56888
  function findValue(finalData, candidates) {
56656
- const entries = Object.entries(finalData);
56657
- const lowerCandidates = candidates.map((item) => item.toLowerCase());
56658
- for (const [key, value] of entries) {
56659
- if (lowerCandidates.includes(key.toLowerCase())) {
56660
- return value;
56661
- }
56662
- }
56663
- return void 0;
56889
+ return findFormValue({ values: finalData }, candidates);
56664
56890
  }
56665
56891
  function MobileDialer(props) {
56666
56892
  useEffect(() => {
@@ -56673,10 +56899,12 @@ function MobileDialer(props) {
56673
56899
  document.head.appendChild(link);
56674
56900
  }
56675
56901
  }, []);
56902
+ const inputProfile = useInputProfile();
56903
+ const density = resolveDensity(props.density, inputProfile);
56676
56904
  if (!props.isAuthenticated) {
56677
- return /* @__PURE__ */ jsxRuntimeExports.jsx(MobileDialerLogin, { ...props });
56905
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(MobileDialerLogin, { ...props, density });
56678
56906
  }
56679
- return /* @__PURE__ */ jsxRuntimeExports.jsx(AuthenticatedMobileDialer, { ...props });
56907
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(AuthenticatedMobileDialer, { ...props, density });
56680
56908
  }
56681
56909
  function MobileDialerLogin({
56682
56910
  username,
@@ -56685,7 +56913,8 @@ function MobileDialerLogin({
56685
56913
  sessionError,
56686
56914
  sessionStatus,
56687
56915
  loginConfirmation,
56688
- onCancelLoginConfirmation
56916
+ onCancelLoginConfirmation,
56917
+ density
56689
56918
  }) {
56690
56919
  const [loginInput, setLoginInput] = useState(username ?? "");
56691
56920
  const [loginError, setLoginError] = useState(null);
@@ -56712,7 +56941,7 @@ function MobileDialerLogin({
56712
56941
  const handleLogin = useCallback(() => submitLogin(loginInput), [submitLogin, loginInput]);
56713
56942
  const showConfirmation = Boolean(loginConfirmation) && !isLoadingSession;
56714
56943
  const loadingUser = ((loginConfirmation == null ? void 0 : loginConfirmation.username) || loginInput || username || "").trim();
56715
- return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "phone-container", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "screen dialer-screen login-screen active", id: "dialerScreen", children: [
56944
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "phone-container", "data-density": density === "compact" ? "compact" : "comfortable", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "screen dialer-screen login-screen active", id: "dialerScreen", children: [
56716
56945
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "dialer-header", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "status-container", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { type: "button", className: `status-badge ${isLoadingSession ? "connecting" : "available"}`, children: [
56717
56946
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "status-dot" }),
56718
56947
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "status-text", children: isLoadingSession ? "Conectando" : "Login" })
@@ -56826,9 +57055,10 @@ Gostaríamos de fazer um *diagnóstico gratuito* para te apresentar os valores r
56826
57055
  }
56827
57056
  function AuthenticatedMobileDialer({
56828
57057
  username,
56829
- showClientInfoModal = false
57058
+ showClientInfoModal = false,
57059
+ density
56830
57060
  }) {
56831
- var _a2, _b, _c;
57061
+ var _a2, _b, _c, _d;
56832
57062
  const { skipManualQualification } = useDialerConfig();
56833
57063
  const [dialValue, setDialValue] = useState("");
56834
57064
  const [activeScreen, setActiveScreen] = useState("dialer");
@@ -56850,10 +57080,14 @@ function AuthenticatedMobileDialer({
56850
57080
  isLoading: false,
56851
57081
  error: null
56852
57082
  });
56853
- const [fetchedContactId, setFetchedContactId] = useState(null);
56854
57083
  const [callHistory, setCallHistory] = useState([]);
56855
57084
  const [isLoadingHistory, setIsLoadingHistory] = useState(false);
56856
57085
  const [historyError, setHistoryError] = useState(null);
57086
+ const [historyQuery, setHistoryQuery] = useState("");
57087
+ const [historySearchResults, setHistorySearchResults] = useState(null);
57088
+ const [isSearchingHistory, setIsSearchingHistory] = useState(false);
57089
+ const historyTextPoolRef = useRef(null);
57090
+ const historySearchSeqRef = useRef(0);
56857
57091
  const [isSpeakerOn, setIsSpeakerOn] = useState(false);
56858
57092
  const [audioOutputDevices, setAudioOutputDevices] = useState([]);
56859
57093
  const [speakerDeviceId, setSpeakerDeviceId] = useState(null);
@@ -57030,90 +57264,54 @@ function AuthenticatedMobileDialer({
57030
57264
  });
57031
57265
  }
57032
57266
  }, [callInfo]);
57033
- const campaignContact = (callInfo == null ? void 0 : callInfo.type) === "campaign" ? callInfo.info.campaignContact : null;
57034
- const campaignContactId = campaignContact == null ? void 0 : campaignContact.id;
57267
+ const campaignFormRequest = useMemo(
57268
+ () => {
57269
+ var _a3;
57270
+ return (callInfo == null ? void 0 : callInfo.type) === "campaign" ? campaignFormRequestFromCallInfo(callInfo.info, ((_a3 = currentCall == null ? void 0 : currentCall.data) == null ? void 0 : _a3.campaignContactPhoneId) ?? null) : null;
57271
+ },
57272
+ [callInfo, currentCall]
57273
+ );
57274
+ const campaignFormKey = campaignFormRequest ? campaignFormCacheKey(campaignFormRequest) : null;
57275
+ const campaignFormRequestRef = useRef(campaignFormRequest);
57276
+ campaignFormRequestRef.current = campaignFormRequest;
57035
57277
  useEffect(() => {
57036
- if (!campaignContactId) {
57278
+ const request = campaignFormRequestRef.current;
57279
+ if (!request || !campaignFormKey) {
57037
57280
  setContactData({ finalData: {}, isLoading: false, error: null });
57038
- setFetchedContactId(null);
57039
- return;
57040
- }
57041
- if (fetchedContactId === campaignContactId) {
57042
57281
  return;
57043
57282
  }
57044
57283
  let cancelled = false;
57045
- const fetchContactData = async () => {
57046
- console.log("[MobileDialer] ========================================");
57047
- console.log("[MobileDialer] Buscando dados do contato:", campaignContactId);
57048
- const tokenAtFetch = getSessionToken();
57049
- console.log("[MobileDialer] Token no momento da busca:", tokenAtFetch ? `${tokenAtFetch.substring(0, 20)}...` : "NULL");
57050
- console.log("[MobileDialer] localStorage length:", typeof window !== "undefined" ? localStorage.length : "N/A");
57051
- addLog("debug", "FetchContact", `Token: ${tokenAtFetch ? "presente" : "AUSENTE"}`, {
57052
- contactId: campaignContactId,
57053
- hasToken: !!tokenAtFetch,
57054
- localStorageLength: typeof window !== "undefined" ? localStorage.length : 0
57284
+ let refreshTimer = null;
57285
+ setContactData({ finalData: {}, isLoading: true, error: null });
57286
+ addLog("debug", "FetchContact", "Buscando formulário do contato", { contactId: request.id, source: request.source });
57287
+ const apply2 = (form) => {
57288
+ setContactData({ finalData: form.values, isLoading: false, error: null });
57289
+ addLog("info", "FetchContact", `✅ Formulário: ${form.fields.length} campos (${form.valuesSource}, layout ${form.layoutSource})`, {
57290
+ contactId: request.id,
57291
+ partial: form.partial === true
57055
57292
  });
57056
- console.log("[MobileDialer] ========================================");
57057
- setContactData({ finalData: {}, isLoading: true, error: null });
57058
- try {
57059
- const customer = campaignContact == null ? void 0 : campaignContact.customer;
57060
- let fieldsRawData;
57061
- let contactValues = {};
57062
- if ((customer == null ? void 0 : customer.id) && (customer == null ? void 0 : customer.formId)) {
57063
- console.log("[MobileDialer] 📋 Modo CLIENTE detectado:", customer);
57064
- const customerFieldsUrl = `${API_BASE_URL}/api/campaign-contact/0?source=customer&formId=${encodeURIComponent(String(customer.formId))}`;
57065
- const customerValuesUrl = `${API_BASE_URL}/api/campaign-fields/0?source=customer&customerId=${encodeURIComponent(String(customer.id))}`;
57066
- const [fieldsResponse, valuesResponse] = await Promise.all([
57067
- fetch(customerFieldsUrl, { method: "GET", headers: getAuthHeaders(), credentials: "omit" }),
57068
- fetch(customerValuesUrl, { method: "GET", headers: getAuthHeaders(), credentials: "omit" })
57069
- ]);
57070
- if (!fieldsResponse.ok) throw new Error(`Erro ao buscar campos cliente: ${fieldsResponse.status}`);
57071
- if (!valuesResponse.ok) throw new Error(`Erro ao buscar valores cliente: ${valuesResponse.status}`);
57072
- fieldsRawData = await fieldsResponse.json();
57073
- contactValues = await valuesResponse.json();
57074
- } else {
57075
- console.log("[MobileDialer] 📋 Modo CAMPANHA detectado");
57076
- const config = await getCampaignConfig(campaignContactId);
57077
- console.log("[MobileDialer] Config recebida:", config);
57078
- if (!config.campaignModelId) {
57079
- throw new Error("campaignModelId não encontrado");
57080
- }
57081
- const [rawFields, rawValues] = await Promise.all([
57082
- fetchCampaignFieldsRaw(config.campaignModelId),
57083
- getContactValues(campaignContactId)
57084
- ]);
57085
- fieldsRawData = rawFields;
57086
- contactValues = rawValues;
57087
- }
57088
- console.log("[MobileDialer] ======= DADOS BRUTOS =======");
57089
- console.log("[MobileDialer] fieldsRawData:", JSON.stringify(fieldsRawData, null, 2).substring(0, 1e3));
57090
- console.log("[MobileDialer] contactValues:", JSON.stringify(contactValues, null, 2));
57091
- const codeToNameMap = processRowsToMapping(fieldsRawData);
57092
- console.log("[MobileDialer] ======= MAPEAMENTO PROCESSADO =======");
57093
- console.log("[MobileDialer] codeToNameMap:", JSON.stringify(codeToNameMap, null, 2));
57094
- const finalData = applyMappingToValues(codeToNameMap, contactValues);
57095
- console.log("[MobileDialer] ======= RESULTADO FINAL =======");
57096
- console.log("[MobileDialer] finalData:", JSON.stringify(finalData, null, 2));
57097
- console.log("[MobileDialer] finalData keys:", Object.keys(finalData));
57098
- console.log("[MobileDialer] ================================");
57099
- if (!cancelled) {
57100
- setContactData({ finalData, isLoading: false, error: null });
57101
- setFetchedContactId(campaignContactId);
57102
- }
57103
- } catch (error2) {
57104
- if (!cancelled) {
57105
- console.error("[MobileDialer] Erro ao buscar dados:", error2);
57106
- const errorMessage = error2 instanceof Error ? error2.message : "Erro ao buscar dados";
57107
- setContactData({ finalData: {}, isLoading: false, error: errorMessage });
57108
- setFetchedContactId(campaignContactId);
57109
- }
57110
- }
57111
57293
  };
57112
- void fetchContactData();
57294
+ fetchCampaignForm(request).then((form) => {
57295
+ if (cancelled) return;
57296
+ apply2(form);
57297
+ if (form.partial) {
57298
+ refreshTimer = setTimeout(() => {
57299
+ if (cancelled) return;
57300
+ fetchCampaignForm(request, { force: true }).then((refreshed) => {
57301
+ if (!cancelled) apply2(refreshed);
57302
+ }).catch(() => void 0);
57303
+ }, 3e3);
57304
+ }
57305
+ }).catch((error2) => {
57306
+ if (cancelled) return;
57307
+ console.error("[MobileDialer] Erro ao buscar o formulário:", error2);
57308
+ setContactData({ finalData: {}, isLoading: false, error: error2 instanceof Error ? error2.message : "Erro ao buscar dados" });
57309
+ });
57113
57310
  return () => {
57114
57311
  cancelled = true;
57312
+ if (refreshTimer) clearTimeout(refreshTimer);
57115
57313
  };
57116
- }, [campaignContactId, fetchedContactId]);
57314
+ }, [campaignFormKey]);
57117
57315
  const callStateStr = callState;
57118
57316
  const isError = callStateStr === "startup.error" || callStateStr === "offline";
57119
57317
  const isIdle = !callStateStr || callStateStr === "starting" || callStateStr === "idle" || callStateStr === "manualCallSetup" || callStateStr === "onBreak";
@@ -57165,6 +57363,18 @@ function AuthenticatedMobileDialer({
57165
57363
  setPauseDurationSeconds(0);
57166
57364
  }
57167
57365
  }, [isOnBreak, currentBreakName]);
57366
+ useEffect(() => {
57367
+ const onClickToCall = (event) => {
57368
+ const task = event.detail;
57369
+ if (!(task == null ? void 0 : task.phone)) return;
57370
+ setCallError(null);
57371
+ setDialValue(nationalDigitsFromE164(task.phone).slice(0, 32));
57372
+ setActiveScreen("dialer");
57373
+ addLog("event", "Call", `🖱️ Click-to-call recebido: ${task.phoneDisplay}`, { taskId: task.id, source: task.source });
57374
+ };
57375
+ window.addEventListener(CLICK_TO_CALL_EVENT, onClickToCall);
57376
+ return () => window.removeEventListener(CLICK_TO_CALL_EVENT, onClickToCall);
57377
+ }, []);
57168
57378
  useEffect(() => {
57169
57379
  console.log("[MobileDialer] Screen control effect - callState:", callState, "isInCall:", isInCall, "isConnecting:", isConnecting, "isAfterCall:", isAfterCall);
57170
57380
  if (isInCall || isConnecting) {
@@ -57356,16 +57566,17 @@ function AuthenticatedMobileDialer({
57356
57566
  haptic.notificationError();
57357
57567
  }
57358
57568
  }, [haptic, speakerSupported, speakerDeviceId, defaultDeviceId, isSpeakerOn, operator]);
57359
- const fetchCallHistory = useCallback(async () => {
57360
- const agentId = userData == null ? void 0 : userData.id;
57361
- if (!agentId) {
57362
- console.log("[MobileDialer] Aguardando ID do usuário para buscar histórico");
57363
- return;
57364
- }
57365
- setIsLoadingHistory(true);
57366
- setHistoryError(null);
57367
- try {
57368
- const response = await fetch(`${API_BASE_URL}/api/call-history?agent_id=${encodeURIComponent(agentId)}&limit=50`, {
57569
+ const requestHistory = useCallback(
57570
+ async ({ phone, limit }) => {
57571
+ const agentId = userData == null ? void 0 : userData.id;
57572
+ if (!agentId) {
57573
+ throw new Error("Aguardando ID do usuário");
57574
+ }
57575
+ const params = new URLSearchParams({ agent_id: String(agentId), limit: String(limit) });
57576
+ if (phone) {
57577
+ params.set("phone", phone);
57578
+ }
57579
+ const response = await fetch(`${API_BASE_URL}/api/call-history?${params.toString()}`, {
57369
57580
  method: "GET",
57370
57581
  headers: getAuthHeaders(),
57371
57582
  credentials: "omit"
@@ -57376,8 +57587,22 @@ function AuthenticatedMobileDialer({
57376
57587
  );
57377
57588
  }
57378
57589
  const data = await response.json();
57379
- console.log("[MobileDialer] Histórico recebido:", data.total, "registros");
57380
- setCallHistory(data.items || []);
57590
+ return data.items || [];
57591
+ },
57592
+ [userData == null ? void 0 : userData.id]
57593
+ );
57594
+ const fetchCallHistory = useCallback(async () => {
57595
+ if (!(userData == null ? void 0 : userData.id)) {
57596
+ console.log("[MobileDialer] Aguardando ID do usuário para buscar histórico");
57597
+ return;
57598
+ }
57599
+ setIsLoadingHistory(true);
57600
+ setHistoryError(null);
57601
+ historyTextPoolRef.current = null;
57602
+ try {
57603
+ const items = await requestHistory({ limit: 50 });
57604
+ console.log("[MobileDialer] Histórico recebido:", items.length, "registros");
57605
+ setCallHistory(items);
57381
57606
  } catch (error2) {
57382
57607
  console.error("[MobileDialer] Erro ao buscar histórico:", error2);
57383
57608
  setHistoryError(error2 instanceof Error && error2.message.startsWith("Sessão") ? error2.message : "Erro ao carregar histórico");
@@ -57385,7 +57610,54 @@ function AuthenticatedMobileDialer({
57385
57610
  } finally {
57386
57611
  setIsLoadingHistory(false);
57387
57612
  }
57388
- }, [userData == null ? void 0 : userData.id]);
57613
+ }, [requestHistory, userData == null ? void 0 : userData.id]);
57614
+ const handleHistoryQueryChange = useCallback(
57615
+ async (query) => {
57616
+ setHistoryQuery(query);
57617
+ const parsed = classifyHistoryQuery(query);
57618
+ const seq = ++historySearchSeqRef.current;
57619
+ if (parsed.kind === "empty") {
57620
+ setHistorySearchResults(null);
57621
+ setIsSearchingHistory(false);
57622
+ return;
57623
+ }
57624
+ setIsSearchingHistory(true);
57625
+ try {
57626
+ let results;
57627
+ if (parsed.kind === "phone") {
57628
+ results = await requestHistory({ phone: parsed.digits, limit: 100 });
57629
+ } else {
57630
+ if (!historyTextPoolRef.current) {
57631
+ historyTextPoolRef.current = await requestHistory({ limit: 200 });
57632
+ }
57633
+ results = historyTextPoolRef.current;
57634
+ }
57635
+ if (seq === historySearchSeqRef.current) {
57636
+ setHistorySearchResults(results);
57637
+ }
57638
+ } catch (error2) {
57639
+ console.error("[MobileDialer] Erro na busca do histórico:", error2);
57640
+ if (seq === historySearchSeqRef.current) {
57641
+ setHistorySearchResults(null);
57642
+ }
57643
+ } finally {
57644
+ if (seq === historySearchSeqRef.current) {
57645
+ setIsSearchingHistory(false);
57646
+ }
57647
+ }
57648
+ },
57649
+ [requestHistory]
57650
+ );
57651
+ const historySearchHint = useMemo(() => {
57652
+ const parsed = classifyHistoryQuery(historyQuery);
57653
+ if (parsed.kind === "phone") {
57654
+ return "Busca por número em todo o histórico";
57655
+ }
57656
+ if (parsed.kind === "text") {
57657
+ return historySearchResults ? "Busca por texto nas últimas 200 chamadas" : "Busca por texto nas chamadas carregadas";
57658
+ }
57659
+ return null;
57660
+ }, [historyQuery, historySearchResults]);
57389
57661
  useEffect(() => {
57390
57662
  if (activeScreen === "history") {
57391
57663
  void fetchCallHistory();
@@ -57536,7 +57808,7 @@ function AuthenticatedMobileDialer({
57536
57808
  addLog("info", "Debug", "🔒 Painel de debug fechado");
57537
57809
  } });
57538
57810
  }
57539
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "phone-container", children: [
57811
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "phone-container", "data-density": density === "compact" ? "compact" : "comfortable", children: [
57540
57812
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `screen dialer-screen ${activeScreen === "dialer" ? "active" : ""}`, id: "dialerScreen", children: [
57541
57813
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "dialer-header", children: [
57542
57814
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "status-container", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: `status-badge ${statusBadgeClass}`, id: "statusBadge", children: [
@@ -57699,10 +57971,14 @@ function AuthenticatedMobileDialer({
57699
57971
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `screen call-history-screen ${activeScreen === "history" ? "active" : ""}`, id: "callHistoryScreen", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
57700
57972
  CallHistoryList,
57701
57973
  {
57702
- items: callHistory,
57974
+ items: historySearchResults ?? callHistory,
57703
57975
  isLoading: isLoadingHistory,
57704
57976
  error: historyError,
57705
57977
  onRefresh: fetchCallHistory,
57978
+ query: historyQuery,
57979
+ onQueryChange: handleHistoryQueryChange,
57980
+ isSearching: isSearchingHistory,
57981
+ searchHint: historySearchHint,
57706
57982
  onCall: (call) => {
57707
57983
  haptic.impactLight();
57708
57984
  setDialValue(historyPhoneDigits(call));
@@ -57820,6 +58096,7 @@ function AuthenticatedMobileDialer({
57820
58096
  {
57821
58097
  isOpen: isClientInfoModalOpen,
57822
58098
  campaignCallInfo,
58099
+ campaignContactPhoneId: ((_d = currentCall == null ? void 0 : currentCall.data) == null ? void 0 : _d.campaignContactPhoneId) ?? null,
57823
58100
  callDuration: callDurationSeconds,
57824
58101
  isCallActive: isInCall || isConnecting,
57825
58102
  onClose: () => setIsClientInfoModalOpen(false)
@@ -58096,9 +58373,11 @@ function AuthenticatedMiniDialer({
58096
58373
  isLoadingSession,
58097
58374
  sessionError
58098
58375
  }) {
58376
+ var _a2;
58099
58377
  const { skipManualQualification } = useDialerConfig();
58100
58378
  const operatorState = clientSdkReactExports.useCallOperatorState();
58101
58379
  const currentCallInfo = clientSdkReactExports.useCallOperatorCurrentCallInfo();
58380
+ const currentCall = clientSdkReactExports.useCallOperatorCurrentCall();
58102
58381
  const rawSuccessQualifications = clientSdkReactExports.useCallOperatorSuccessQualifications();
58103
58382
  const rawDiscardQualifications = clientSdkReactExports.useCallOperatorDiscardQualifications();
58104
58383
  const successQualifications = useMemo(() => rawSuccessQualifications ?? [], [rawSuccessQualifications]);
@@ -58354,6 +58633,7 @@ function AuthenticatedMiniDialer({
58354
58633
  {
58355
58634
  isOpen: isClientInfoModalOpen,
58356
58635
  campaignCallInfo,
58636
+ campaignContactPhoneId: ((_a2 = currentCall == null ? void 0 : currentCall.data) == null ? void 0 : _a2.campaignContactPhoneId) ?? null,
58357
58637
  callDuration: callDurationSeconds,
58358
58638
  isCallActive: isInCall || isConnecting,
58359
58639
  onClose: () => setIsClientInfoModalOpen(false)
@@ -58376,7 +58656,229 @@ function AuthenticatedMiniDialer({
58376
58656
  )
58377
58657
  ] });
58378
58658
  }
58379
- const WIDGET_VERSION = "2.2.0";
58659
+ const DEFAULT_CLICK_TO_CALL_INTERVAL_MS = 4e3;
58660
+ const BUSY_INTERVAL_FACTOR = 3;
58661
+ const DISPATCH_COOLDOWN_MS = 6e3;
58662
+ const CALL_START_TIMEOUT_MS = 4e3;
58663
+ const CALL_START_CHECK_MS = 100;
58664
+ const MAX_BACKOFF_MS = 6e4;
58665
+ const NOT_CONFIGURED_INTERVAL_MS = 5 * 6e4;
58666
+ const NOT_CONFIGURED_CODES = /* @__PURE__ */ new Set(["STORE_NOT_CONFIGURED", "SERVER_NOT_CONFIGURED"]);
58667
+ const ACK_ATTEMPTS = 3;
58668
+ const ACK_RETRY_DELAY_MS = 1500;
58669
+ const CALL_STARTED_STATES = /* @__PURE__ */ new Set(["manualCallSetup", "manualCallRinging", "callInProgress", "afterCall"]);
58670
+ function buildHeaders(sessionToken) {
58671
+ if (sessionToken) {
58672
+ return { "Content-Type": "application/json", "x-session-token": sessionToken, Authorization: `Bearer ${sessionToken}` };
58673
+ }
58674
+ return getAuthHeaders();
58675
+ }
58676
+ const delay = (ms2) => new Promise((resolve) => setTimeout(resolve, ms2));
58677
+ function ClickToCallAgent({ enabled = true, pollIntervalMs, sessionToken, onTask }) {
58678
+ const operatorState = clientSdkReactExports.useCallOperatorState();
58679
+ const { makeManualCall } = clientSdkReactExports.useCallOperatorControls();
58680
+ const stateRef = useRef(operatorState == null ? void 0 : operatorState.state);
58681
+ stateRef.current = operatorState == null ? void 0 : operatorState.state;
58682
+ const makeManualCallRef = useRef(makeManualCall);
58683
+ makeManualCallRef.current = makeManualCall;
58684
+ const onTaskRef = useRef(onTask);
58685
+ onTaskRef.current = onTask;
58686
+ const tickerRef = useRef(null);
58687
+ const applyIntervalRef = useRef(null);
58688
+ useEffect(() => {
58689
+ var _a2;
58690
+ (_a2 = applyIntervalRef.current) == null ? void 0 : _a2.call(applyIntervalRef);
58691
+ }, [operatorState == null ? void 0 : operatorState.state]);
58692
+ useEffect(() => {
58693
+ if (!enabled) {
58694
+ return void 0;
58695
+ }
58696
+ let disposed = false;
58697
+ let inFlight = false;
58698
+ let cooldownUntil = 0;
58699
+ let failureStreak = 0;
58700
+ let mode2 = "normal";
58701
+ let notConfiguredWarned = false;
58702
+ let presenceTtlMs = 45e3;
58703
+ let baseInterval = Math.max(1e3, pollIntervalMs ?? DEFAULT_CLICK_TO_CALL_INTERVAL_MS);
58704
+ const config = { apiBaseUrl: API_BASE_URL, headers: buildHeaders(sessionToken) };
58705
+ const computeInterval = () => {
58706
+ if (mode2 === "notConfigured") {
58707
+ return NOT_CONFIGURED_INTERVAL_MS;
58708
+ }
58709
+ const ceiling = Math.max(1e3, Math.floor(presenceTtlMs / 2));
58710
+ const base = Math.min(baseInterval, ceiling);
58711
+ if (mode2 === "backoff") {
58712
+ return Math.min(base * 2 ** failureStreak, MAX_BACKOFF_MS);
58713
+ }
58714
+ const busy = Math.min(base * BUSY_INTERVAL_FACTOR, ceiling);
58715
+ return canDispatchTo(presenceFromOperatorState(stateRef.current)) ? base : busy;
58716
+ };
58717
+ const applyInterval = () => {
58718
+ var _a2;
58719
+ return (_a2 = tickerRef.current) == null ? void 0 : _a2.setInterval(computeInterval());
58720
+ };
58721
+ applyIntervalRef.current = applyInterval;
58722
+ const ack = async (options) => {
58723
+ for (let attempt = 0; attempt < ACK_ATTEMPTS; attempt++) {
58724
+ try {
58725
+ const result2 = await ackClickToCall({ ...config, ...options });
58726
+ if (result2.ok) {
58727
+ return true;
58728
+ }
58729
+ const retryable = result2.status >= 500 || result2.status === 408 || result2.status === 429;
58730
+ if (!retryable) {
58731
+ console.warn("[ClickToCall] confirmação recusada pelo servidor:", result2.status, result2.code ?? "", "(tarefa", options.taskId, ")");
58732
+ return false;
58733
+ }
58734
+ } catch (error2) {
58735
+ console.warn("[ClickToCall] confirmação falhou (tentativa", attempt + 1, "):", error2 instanceof Error ? error2.message : error2);
58736
+ }
58737
+ if (disposed || attempt === ACK_ATTEMPTS - 1) {
58738
+ break;
58739
+ }
58740
+ await delay(ACK_RETRY_DELAY_MS * (attempt + 1));
58741
+ }
58742
+ console.error("[ClickToCall] não foi possível confirmar a tarefa", options.taskId, "; o servidor a marcará como não confirmada");
58743
+ return false;
58744
+ };
58745
+ const waitForCallStart = () => new Promise((resolve) => {
58746
+ const deadline = Date.now() + CALL_START_TIMEOUT_MS;
58747
+ const check = () => {
58748
+ if (disposed) return resolve("disposed");
58749
+ const state = stateRef.current;
58750
+ if (state && CALL_STARTED_STATES.has(state)) return resolve("started");
58751
+ if (state === "manualCallFailed") return resolve("manual_failed");
58752
+ if (state === "callRinging" || presenceFromOperatorState(state) === "offline") return resolve("interrupted");
58753
+ if (Date.now() >= deadline) return resolve("not_started");
58754
+ setTimeout(check, CALL_START_CHECK_MS);
58755
+ };
58756
+ check();
58757
+ });
58758
+ const dispatch = async (task) => {
58759
+ var _a2;
58760
+ const state = stateRef.current;
58761
+ cooldownUntil = Date.now() + DISPATCH_COOLDOWN_MS;
58762
+ if (!canDispatchTo(presenceFromOperatorState(state))) {
58763
+ console.warn("[ClickToCall] tarefa", task.id, "recebida com operador ocupado (", state, "): devolvendo à fila");
58764
+ await ack({ taskId: task.id, status: "failed", error: `Operador indisponível (${state ?? "desconhecido"})`, retry: true });
58765
+ return;
58766
+ }
58767
+ console.log("[ClickToCall] 🖱️ ligação pedida pela API:", task.phoneDisplay, "(tarefa", task.id, ")");
58768
+ try {
58769
+ window.dispatchEvent(new CustomEvent(CLICK_TO_CALL_EVENT, { detail: task }));
58770
+ } catch (error2) {
58771
+ console.warn("[ClickToCall] falha ao notificar o discador:", error2);
58772
+ }
58773
+ try {
58774
+ (_a2 = onTaskRef.current) == null ? void 0 : _a2.call(onTaskRef, task);
58775
+ } catch (error2) {
58776
+ console.warn("[ClickToCall] onTask lançou erro:", error2);
58777
+ }
58778
+ try {
58779
+ makeManualCallRef.current(task.phone);
58780
+ } catch (error2) {
58781
+ const message = error2 instanceof Error ? error2.message : String(error2);
58782
+ console.error("[ClickToCall] makeManualCall falhou:", message);
58783
+ await ack({ taskId: task.id, status: "failed", error: message, retry: false });
58784
+ return;
58785
+ }
58786
+ const outcome = await waitForCallStart();
58787
+ switch (outcome) {
58788
+ case "started":
58789
+ await ack({ taskId: task.id, status: "calling" });
58790
+ return;
58791
+ case "manual_failed":
58792
+ console.error("[ClickToCall] o SDK não conseguiu completar a chamada manual");
58793
+ await ack({ taskId: task.id, status: "failed", error: "Chamada manual falhou no SDK", retry: false });
58794
+ return;
58795
+ case "interrupted":
58796
+ console.warn("[ClickToCall] chamada manual não aconteceu (estado", stateRef.current, "): devolvendo à fila");
58797
+ await ack({ taskId: task.id, status: "failed", error: `Discador interrompido (${stateRef.current ?? "desconhecido"})`, retry: true });
58798
+ return;
58799
+ case "disposed":
58800
+ await ack({ taskId: task.id, status: "failed", error: "Discador encerrado durante a discagem", retry: true });
58801
+ return;
58802
+ default: {
58803
+ const message = `SDK não iniciou a chamada (estado continua ${stateRef.current ?? "desconhecido"})`;
58804
+ console.error("[ClickToCall]", message);
58805
+ await ack({ taskId: task.id, status: "failed", error: message, retry: false });
58806
+ }
58807
+ }
58808
+ };
58809
+ const tick = async () => {
58810
+ if (disposed || inFlight) {
58811
+ return;
58812
+ }
58813
+ inFlight = true;
58814
+ try {
58815
+ const state = stateRef.current;
58816
+ const claim = canDispatchTo(presenceFromOperatorState(state)) && Date.now() >= cooldownUntil;
58817
+ let result2;
58818
+ try {
58819
+ result2 = await pollClickToCall({ ...config, state, claim });
58820
+ } catch (error2) {
58821
+ result2 = { ok: false, status: 0, code: error2 instanceof Error && error2.name === "AbortError" ? "TIMEOUT" : "NETWORK" };
58822
+ }
58823
+ if (disposed) {
58824
+ if (result2.ok && result2.task) {
58825
+ await ack({ taskId: result2.task.id, status: "failed", error: "Discador encerrado antes de discar", retry: true });
58826
+ }
58827
+ return;
58828
+ }
58829
+ if (!result2.ok) {
58830
+ failureStreak += 1;
58831
+ const notConfigured = result2.code !== null && NOT_CONFIGURED_CODES.has(result2.code);
58832
+ mode2 = notConfigured ? "notConfigured" : "backoff";
58833
+ if (notConfigured) {
58834
+ if (!notConfiguredWarned) {
58835
+ notConfiguredWarned = true;
58836
+ console.warn("[ClickToCall] backend sem click-to-call configurado (", result2.code, "); nova tentativa em 5 min");
58837
+ }
58838
+ } else if (failureStreak === 1) {
58839
+ console.warn("[ClickToCall] polling falhou (HTTP", result2.status, result2.code ?? "", "); aplicando backoff");
58840
+ }
58841
+ applyInterval();
58842
+ return;
58843
+ }
58844
+ if (mode2 !== "normal") {
58845
+ mode2 = "normal";
58846
+ failureStreak = 0;
58847
+ }
58848
+ presenceTtlMs = result2.presenceTtlSeconds * 1e3;
58849
+ if (pollIntervalMs === void 0 && result2.pollIntervalMs > 0) {
58850
+ baseInterval = Math.max(1e3, result2.pollIntervalMs);
58851
+ }
58852
+ applyInterval();
58853
+ if (result2.task) {
58854
+ try {
58855
+ await dispatch(result2.task);
58856
+ } catch (error2) {
58857
+ console.error("[ClickToCall] erro ao tratar a tarefa", result2.task.id, ":", error2 instanceof Error ? error2.message : error2);
58858
+ }
58859
+ }
58860
+ } finally {
58861
+ inFlight = false;
58862
+ }
58863
+ };
58864
+ const ticker = createBackgroundTicker(() => void tick(), computeInterval());
58865
+ tickerRef.current = ticker;
58866
+ void tick();
58867
+ return () => {
58868
+ disposed = true;
58869
+ ticker.stop();
58870
+ if (tickerRef.current === ticker) {
58871
+ tickerRef.current = null;
58872
+ }
58873
+ if (applyIntervalRef.current === applyInterval) {
58874
+ applyIntervalRef.current = null;
58875
+ }
58876
+ reportClickToCallOffline(config);
58877
+ };
58878
+ }, [enabled, pollIntervalMs, sessionToken]);
58879
+ return null;
58880
+ }
58881
+ const WIDGET_VERSION = "2.3.0";
58380
58882
  let currentSipWebSocket = null;
58381
58883
  function sendSipUnregisterIfPossible(ws) {
58382
58884
  if (ws.readyState !== WebSocket.OPEN) return;
@@ -58576,8 +59078,25 @@ function deriveOptionsFromElement(element) {
58576
59078
  if (datasetAutoLogin !== void 0 && datasetAutoLogin !== null) {
58577
59079
  result2.autoLogin = ["true", "1", "yes", ""].includes(datasetAutoLogin.trim().toLowerCase());
58578
59080
  }
59081
+ const datasetDensity = parseDensity(element.getAttribute("data-callix-density") ?? element.dataset.callixDensity);
59082
+ if (datasetDensity) {
59083
+ result2.density = datasetDensity;
59084
+ }
59085
+ const datasetClickToCall = element.getAttribute("data-callix-click-to-call") ?? element.dataset.callixClickToCall;
59086
+ if (datasetClickToCall !== void 0 && datasetClickToCall !== null) {
59087
+ result2.clickToCall = !["false", "0", "no", "off"].includes(datasetClickToCall.trim().toLowerCase());
59088
+ }
58579
59089
  return result2;
58580
59090
  }
59091
+ function normalizeClickToCallOptions(value) {
59092
+ if (value === false) {
59093
+ return { enabled: false };
59094
+ }
59095
+ if (value === true || value === void 0 || value === null) {
59096
+ return { enabled: true };
59097
+ }
59098
+ return { ...value, enabled: value.enabled !== false };
59099
+ }
58581
59100
  class CallixDialerWidget {
58582
59101
  constructor(options) {
58583
59102
  this.root = null;
@@ -58608,9 +59127,11 @@ class CallixDialerWidget {
58608
59127
  if (this.options.autoLogin === void 0 && derived.autoLogin !== void 0) {
58609
59128
  this.options.autoLogin = derived.autoLogin;
58610
59129
  }
58611
- if (this.options.uiVariant === "mobile" && !this.container.style.getPropertyValue("container-type")) {
58612
- this.container.style.setProperty("container-type", "inline-size");
58613
- this.container.style.setProperty("container-name", "dialercontainer");
59130
+ if (this.options.density === void 0 && derived.density !== void 0) {
59131
+ this.options.density = derived.density;
59132
+ }
59133
+ if (this.options.clickToCall === void 0 && derived.clickToCall !== void 0) {
59134
+ this.options.clickToCall = derived.clickToCall;
58614
59135
  }
58615
59136
  this.root = clientExports.createRoot(this.container);
58616
59137
  this.root.render(
@@ -58954,6 +59475,7 @@ function DialerWrapper({ options, containerEl }) {
58954
59475
  }, []);
58955
59476
  const DialerComponent = options.mode === "inline" || uiVariant === "mobile" ? InlineDialer : FloatingDialer;
58956
59477
  const dialerMode = options.mode;
59478
+ const clickToCall = normalizeClickToCallOptions(options.clickToCall);
58957
59479
  if (isDisconnecting) {
58958
59480
  return /* @__PURE__ */ jsxRuntimeExports.jsx(DialerConfigContext.Provider, { value: { skipManualQualification }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
58959
59481
  DialerComponent,
@@ -58967,6 +59489,7 @@ function DialerWrapper({ options, containerEl }) {
58967
59489
  sessionStatus: "Encerrando sessão anterior...",
58968
59490
  variant: uiVariant,
58969
59491
  portalTarget,
59492
+ density: options.density,
58970
59493
  mode: dialerMode
58971
59494
  }
58972
59495
  ) });
@@ -58986,6 +59509,7 @@ function DialerWrapper({ options, containerEl }) {
58986
59509
  onCancelLoginConfirmation: handleCancelLoginConfirmation,
58987
59510
  variant: uiVariant,
58988
59511
  portalTarget,
59512
+ density: options.density,
58989
59513
  mode: dialerMode
58990
59514
  }
58991
59515
  ) });
@@ -58995,21 +59519,33 @@ function DialerWrapper({ options, containerEl }) {
58995
59519
  {
58996
59520
  domain: callixDomain,
58997
59521
  userSessionToken: sessionToken,
58998
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(SdkCleanupManager, { ref: cleanupRef, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
58999
- DialerComponent,
59000
- {
59001
- isAuthenticated,
59002
- username,
59003
- onLogin: handleLogin,
59004
- onLogout: handleLogout,
59005
- isLoadingSession: isLoading,
59006
- sessionError: error2,
59007
- sessionStatus: loginStatus,
59008
- variant: uiVariant,
59009
- portalTarget,
59010
- mode: dialerMode
59011
- }
59012
- ) })
59522
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs(SdkCleanupManager, { ref: cleanupRef, children: [
59523
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
59524
+ DialerComponent,
59525
+ {
59526
+ isAuthenticated,
59527
+ username,
59528
+ onLogin: handleLogin,
59529
+ onLogout: handleLogout,
59530
+ isLoadingSession: isLoading,
59531
+ sessionError: error2,
59532
+ sessionStatus: loginStatus,
59533
+ variant: uiVariant,
59534
+ portalTarget,
59535
+ density: options.density,
59536
+ mode: dialerMode
59537
+ }
59538
+ ),
59539
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
59540
+ ClickToCallAgent,
59541
+ {
59542
+ enabled: clickToCall.enabled,
59543
+ pollIntervalMs: clickToCall.pollIntervalMs,
59544
+ sessionToken,
59545
+ onTask: clickToCall.onTask
59546
+ }
59547
+ )
59548
+ ] })
59013
59549
  },
59014
59550
  providerKey
59015
59551
  ) });