callix-dialer-widget 2.3.0 → 2.4.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, { useState, useMemo, useRef, useEffect, useCallback, forwardRef, useLayoutEffect, createContext, useImperativeHandle, StrictMode, useContext } from "react";
9
+ import React, { useState, useMemo, useRef, useEffect, useCallback, forwardRef, useLayoutEffect, useId, 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) {
@@ -48510,6 +48510,15 @@ function isFormResponse(value) {
48510
48510
  const form = value;
48511
48511
  return typeof form.id === "number" && !!form.values && typeof form.values === "object" && Array.isArray(form.fields);
48512
48512
  }
48513
+ const NETWORK_ERROR_MESSAGE = "Não foi possível conectar ao servidor do discador. Verifique a conexão ou se o back-end está atualizado.";
48514
+ const ROUTE_MISSING_MESSAGE = "Rota do formulário não encontrada no servidor do discador. Atualize o back-end (deploy) para esta versão do widget.";
48515
+ function describeHttpError(status, body) {
48516
+ if (status === 401) return "Sessão expirada. Entre novamente no discador.";
48517
+ const fromBody = body.message || body.error;
48518
+ if (fromBody) return fromBody;
48519
+ if (status === 404) return ROUTE_MISSING_MESSAGE;
48520
+ return `Erro ${status}`;
48521
+ }
48513
48522
  async function requestOnce(url, headers) {
48514
48523
  const controller = new AbortController();
48515
48524
  const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS$1);
@@ -48517,8 +48526,7 @@ async function requestOnce(url, headers) {
48517
48526
  const response = await fetch(url, { method: "GET", headers, credentials: "omit", signal: controller.signal });
48518
48527
  if (!response.ok) {
48519
48528
  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);
48529
+ const error2 = new Error(describeHttpError(response.status, body));
48522
48530
  error2.status = response.status;
48523
48531
  throw error2;
48524
48532
  }
@@ -48531,6 +48539,11 @@ async function requestOnce(url, headers) {
48531
48539
  if (error2 instanceof Error && error2.name === "AbortError") {
48532
48540
  throw new Error("Tempo esgotado ao buscar o formulário");
48533
48541
  }
48542
+ if (error2 instanceof TypeError) {
48543
+ const wrapped = new Error(NETWORK_ERROR_MESSAGE);
48544
+ wrapped.cause = error2;
48545
+ throw wrapped;
48546
+ }
48534
48547
  throw error2;
48535
48548
  } finally {
48536
48549
  clearTimeout(timer);
@@ -56488,9 +56501,55 @@ function DebugPanel({ onClose }) {
56488
56501
  ] });
56489
56502
  }
56490
56503
  const VIEWPORT_MARGIN$1 = 16;
56504
+ const BASE_WIDTH = 520;
56505
+ const FALLBACK_HEIGHT = 600;
56506
+ const FORM_SCALE_STORAGE_KEY = "callix_dialer_form_scale";
56507
+ const FORM_SCALE_MIN = 70;
56508
+ const FORM_SCALE_MAX = 180;
56509
+ const FORM_SCALE_STEP = 10;
56510
+ const FORM_SCALE_DEFAULT = 100;
56511
+ const CONTROL_MIN_PX = 28;
56512
+ const CONTROL_BUTTON_CLASS = "flex flex-shrink-0 items-center justify-center rounded-md transition hover:bg-black hover:bg-opacity-10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white";
56513
+ const useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
56491
56514
  function clamp$1(value, min2, max2) {
56492
56515
  return Math.min(Math.max(value, min2), max2);
56493
56516
  }
56517
+ function clampFormScale(percent) {
56518
+ if (!Number.isFinite(percent)) return FORM_SCALE_DEFAULT;
56519
+ const rounded = Math.round(percent / FORM_SCALE_STEP) * FORM_SCALE_STEP;
56520
+ return clamp$1(rounded, FORM_SCALE_MIN, FORM_SCALE_MAX);
56521
+ }
56522
+ function readStoredFormScale() {
56523
+ if (typeof window === "undefined") return FORM_SCALE_DEFAULT;
56524
+ try {
56525
+ const raw = window.localStorage.getItem(FORM_SCALE_STORAGE_KEY);
56526
+ if (raw === null || raw.trim() === "") return FORM_SCALE_DEFAULT;
56527
+ return clampFormScale(Number(raw));
56528
+ } catch {
56529
+ return FORM_SCALE_DEFAULT;
56530
+ }
56531
+ }
56532
+ function storeFormScale(percent) {
56533
+ try {
56534
+ window.localStorage.setItem(FORM_SCALE_STORAGE_KEY, String(percent));
56535
+ } catch {
56536
+ }
56537
+ }
56538
+ function detectZoomSupport() {
56539
+ if (typeof CSS === "undefined" || typeof CSS.supports !== "function") return true;
56540
+ try {
56541
+ if (!CSS.supports("zoom", "1.5")) return false;
56542
+ } catch {
56543
+ return true;
56544
+ }
56545
+ const firefox = typeof navigator !== "undefined" ? /\bFirefox\/(\d+)/.exec(navigator.userAgent) : null;
56546
+ if (firefox && Number(firefox[1]) < 131) return false;
56547
+ return true;
56548
+ }
56549
+ function readViewport() {
56550
+ if (typeof window === "undefined") return { width: 1024, height: 768 };
56551
+ return { width: window.innerWidth, height: window.innerHeight };
56552
+ }
56494
56553
  function formatDuration(seconds) {
56495
56554
  const hrs = Math.floor(seconds / 3600);
56496
56555
  const mins = Math.floor(seconds % 3600 / 60);
@@ -56513,13 +56572,24 @@ function DraggableClientInfoModal({
56513
56572
  campaignContactPhoneId,
56514
56573
  callDuration,
56515
56574
  isCallActive,
56516
- onClose
56575
+ onClose,
56576
+ portalTarget
56517
56577
  }) {
56518
56578
  const containerRef = useRef(null);
56519
56579
  const pointerRef = useRef({ pointerId: null, offsetX: 0, offsetY: 0 });
56520
56580
  const dragMovedRef = useRef(false);
56581
+ const focusWithinRef = useRef(false);
56582
+ const previouslyFocusedRef = useRef(null);
56583
+ const titleId = useId();
56521
56584
  const [position2, setPosition] = useState({ x: 100, y: 100 });
56522
56585
  const [isDragging, setIsDragging] = useState(false);
56586
+ const [viewport, setViewport] = useState(readViewport);
56587
+ const zoomSupported = useMemo(detectZoomSupport, []);
56588
+ const [scalePercent, setScalePercent] = useState(readStoredFormScale);
56589
+ const [announcement, setAnnouncement] = useState("");
56590
+ const scale = zoomSupported ? scalePercent / 100 : 1;
56591
+ const scaleRef = useRef(scale);
56592
+ scaleRef.current = scale;
56523
56593
  const [form, setForm] = useState(null);
56524
56594
  const [isLoading, setIsLoading] = useState(false);
56525
56595
  const [error2, setError] = useState(null);
@@ -56533,39 +56603,47 @@ function DraggableClientInfoModal({
56533
56603
  const requestRef = useRef(request);
56534
56604
  requestRef.current = request;
56535
56605
  const partialRefreshKeyRef = useRef(null);
56536
- const clampPositionToViewport = useCallback((proposed) => {
56537
- if (typeof window === "undefined") {
56538
- return proposed;
56539
- }
56540
- const viewportWidth = window.innerWidth;
56541
- const viewportHeight = window.innerHeight;
56542
- const element = containerRef.current;
56543
- const width = (element == null ? void 0 : element.offsetWidth) ?? 500;
56544
- const height = (element == null ? void 0 : element.offsetHeight) ?? 600;
56545
- const minX = VIEWPORT_MARGIN$1;
56546
- const minY = VIEWPORT_MARGIN$1;
56547
- const maxX = Math.max(minX, viewportWidth - width - VIEWPORT_MARGIN$1);
56548
- const maxY = Math.max(minY, viewportHeight - height - VIEWPORT_MARGIN$1);
56606
+ const measure = useCallback(() => {
56607
+ var _a2;
56608
+ const rect = (_a2 = containerRef.current) == null ? void 0 : _a2.getBoundingClientRect();
56609
+ const currentScale = scaleRef.current;
56549
56610
  return {
56550
- x: clamp$1(proposed.x, minX, maxX),
56551
- y: clamp$1(proposed.y, minY, maxY)
56611
+ width: rect && rect.width > 0 ? rect.width : BASE_WIDTH * currentScale,
56612
+ height: rect && rect.height > 0 ? rect.height : FALLBACK_HEIGHT * currentScale
56552
56613
  };
56553
56614
  }, []);
56554
- useEffect(() => {
56615
+ const clampPositionToViewport = useCallback(
56616
+ (proposed) => {
56617
+ if (typeof window === "undefined") {
56618
+ return proposed;
56619
+ }
56620
+ const { width, height } = measure();
56621
+ const minX = VIEWPORT_MARGIN$1;
56622
+ const minY = VIEWPORT_MARGIN$1;
56623
+ const maxX = Math.max(minX, window.innerWidth - width - VIEWPORT_MARGIN$1);
56624
+ const maxY = Math.max(minY, window.innerHeight - height - VIEWPORT_MARGIN$1);
56625
+ return {
56626
+ x: clamp$1(proposed.x, minX, maxX),
56627
+ y: clamp$1(proposed.y, minY, maxY)
56628
+ };
56629
+ },
56630
+ [measure]
56631
+ );
56632
+ useIsomorphicLayoutEffect(() => {
56555
56633
  if (isOpen && typeof window !== "undefined") {
56556
- const element = containerRef.current;
56557
- const width = (element == null ? void 0 : element.offsetWidth) ?? 500;
56558
- const height = (element == null ? void 0 : element.offsetHeight) ?? 600;
56634
+ setViewport(readViewport());
56635
+ const { width, height } = measure();
56559
56636
  const centerX = (window.innerWidth - width) / 2;
56560
56637
  const centerY = (window.innerHeight - height) / 2;
56561
56638
  setPosition(clampPositionToViewport({ x: centerX, y: centerY }));
56562
56639
  }
56563
- }, [isOpen, clampPositionToViewport]);
56640
+ }, [isOpen, measure, clampPositionToViewport]);
56564
56641
  useEffect(() => {
56565
56642
  if (!isOpen || typeof window === "undefined") {
56566
56643
  return;
56567
56644
  }
56568
56645
  const handleViewportChange = () => {
56646
+ setViewport(readViewport());
56569
56647
  setPosition((prev) => clampPositionToViewport(prev));
56570
56648
  };
56571
56649
  window.addEventListener("resize", handleViewportChange);
@@ -56575,6 +56653,27 @@ function DraggableClientInfoModal({
56575
56653
  window.removeEventListener("orientationchange", handleViewportChange);
56576
56654
  };
56577
56655
  }, [isOpen, clampPositionToViewport]);
56656
+ const keepInsideViewport = useCallback(() => {
56657
+ setPosition((prev) => {
56658
+ const next = clampPositionToViewport(prev);
56659
+ return next.x === prev.x && next.y === prev.y ? prev : next;
56660
+ });
56661
+ }, [clampPositionToViewport]);
56662
+ useEffect(() => {
56663
+ if (!isOpen) {
56664
+ return;
56665
+ }
56666
+ keepInsideViewport();
56667
+ }, [isOpen, scalePercent, keepInsideViewport]);
56668
+ useEffect(() => {
56669
+ const element = containerRef.current;
56670
+ if (!isOpen || !element || typeof ResizeObserver === "undefined") {
56671
+ return;
56672
+ }
56673
+ const observer = new ResizeObserver(() => keepInsideViewport());
56674
+ observer.observe(element);
56675
+ return () => observer.disconnect();
56676
+ }, [isOpen, keepInsideViewport]);
56578
56677
  useEffect(() => {
56579
56678
  const current = requestRef.current;
56580
56679
  if (!current || !requestKey) {
@@ -56615,13 +56714,11 @@ function DraggableClientInfoModal({
56615
56714
  return;
56616
56715
  }
56617
56716
  const handlePointerMove = (event) => {
56618
- var _a2, _b;
56619
56717
  if (pointerRef.current.pointerId !== event.pointerId) {
56620
56718
  return;
56621
56719
  }
56622
56720
  dragMovedRef.current = true;
56623
- const modalWidth = ((_a2 = containerRef.current) == null ? void 0 : _a2.offsetWidth) ?? 0;
56624
- const modalHeight = ((_b = containerRef.current) == null ? void 0 : _b.offsetHeight) ?? 0;
56721
+ const { width: modalWidth, height: modalHeight } = measure();
56625
56722
  const nextX = event.clientX - pointerRef.current.offsetX;
56626
56723
  const nextY = event.clientY - pointerRef.current.offsetY;
56627
56724
  const viewportWidth = typeof window !== "undefined" ? window.innerWidth : 0;
@@ -56644,19 +56741,32 @@ function DraggableClientInfoModal({
56644
56741
  };
56645
56742
  window.addEventListener("pointermove", handlePointerMove);
56646
56743
  window.addEventListener("pointerup", handlePointerUp);
56744
+ window.addEventListener("pointercancel", handlePointerUp);
56647
56745
  return () => {
56648
56746
  window.removeEventListener("pointermove", handlePointerMove);
56649
56747
  window.removeEventListener("pointerup", handlePointerUp);
56748
+ window.removeEventListener("pointercancel", handlePointerUp);
56650
56749
  };
56651
- }, [isDragging]);
56750
+ }, [isDragging, measure]);
56652
56751
  const beginDrag = useCallback((event) => {
56653
56752
  if (event.button !== 0 && event.pointerType !== "touch") {
56654
56753
  return;
56655
56754
  }
56755
+ if (event.target instanceof Element && event.target.closest("button")) {
56756
+ return;
56757
+ }
56656
56758
  const element = containerRef.current;
56657
56759
  if (!element) {
56658
56760
  return;
56659
56761
  }
56762
+ event.preventDefault();
56763
+ const handle = event.currentTarget;
56764
+ if (typeof handle.setPointerCapture === "function") {
56765
+ try {
56766
+ handle.setPointerCapture(event.pointerId);
56767
+ } catch {
56768
+ }
56769
+ }
56660
56770
  const rect = element.getBoundingClientRect();
56661
56771
  pointerRef.current = {
56662
56772
  pointerId: event.pointerId,
@@ -56674,6 +56784,59 @@ function DraggableClientInfoModal({
56674
56784
  const handleRetry = useCallback(() => {
56675
56785
  setRetry((prev) => ({ key: requestKey, n: prev.n + 1 }));
56676
56786
  }, [requestKey]);
56787
+ const applyScale = useCallback((next) => {
56788
+ const value = clampFormScale(next);
56789
+ setScalePercent(value);
56790
+ storeFormScale(value);
56791
+ return value;
56792
+ }, []);
56793
+ const canDecrease = scalePercent > FORM_SCALE_MIN;
56794
+ const canIncrease = scalePercent < FORM_SCALE_MAX;
56795
+ const handleDecrease = useCallback(() => {
56796
+ if (canDecrease) setAnnouncement(`Tamanho do formulário: ${applyScale(scalePercent - FORM_SCALE_STEP)}%`);
56797
+ }, [applyScale, canDecrease, scalePercent]);
56798
+ const handleIncrease = useCallback(() => {
56799
+ if (canIncrease) setAnnouncement(`Tamanho do formulário: ${applyScale(scalePercent + FORM_SCALE_STEP)}%`);
56800
+ }, [applyScale, canIncrease, scalePercent]);
56801
+ const handleResetScale = useCallback(() => {
56802
+ applyScale(FORM_SCALE_DEFAULT);
56803
+ setAnnouncement("");
56804
+ }, [applyScale]);
56805
+ useEffect(() => {
56806
+ if (!isOpen || typeof document === "undefined") {
56807
+ return;
56808
+ }
56809
+ const container = containerRef.current;
56810
+ const active = document.activeElement;
56811
+ previouslyFocusedRef.current = active && active !== document.body ? active : null;
56812
+ if (container && (!active || active === document.body)) {
56813
+ container.focus({ preventScroll: true });
56814
+ }
56815
+ return () => {
56816
+ const previous = previouslyFocusedRef.current;
56817
+ if (focusWithinRef.current && previous && previous.isConnected) {
56818
+ previous.focus({ preventScroll: true });
56819
+ }
56820
+ focusWithinRef.current = false;
56821
+ };
56822
+ }, [isOpen]);
56823
+ const handleFocusWithin = useCallback(() => {
56824
+ focusWithinRef.current = true;
56825
+ }, []);
56826
+ const handleBlurWithin = useCallback((event) => {
56827
+ if (!event.currentTarget.contains(event.relatedTarget)) {
56828
+ focusWithinRef.current = false;
56829
+ }
56830
+ }, []);
56831
+ const handleKeyDown = useCallback(
56832
+ (event) => {
56833
+ if (event.key === "Escape" && !isCallActive) {
56834
+ event.stopPropagation();
56835
+ handleCloseClick();
56836
+ }
56837
+ },
56838
+ [handleCloseClick, isCallActive]
56839
+ );
56677
56840
  if (!isOpen) {
56678
56841
  return null;
56679
56842
  }
@@ -56681,101 +56844,180 @@ function DraggableClientInfoModal({
56681
56844
  const hasRows = rows.length > 0;
56682
56845
  const hasFields = Boolean(form && form.fields.length > 0);
56683
56846
  const hasData = hasRows || hasFields;
56684
- const modalContent = /* @__PURE__ */ jsxRuntimeExports.jsxs(
56847
+ const scaledStyle = {
56848
+ zoom: scale,
56849
+ width: `${BASE_WIDTH}px`,
56850
+ maxWidth: `${Math.max(160, Math.floor((viewport.width - 2 * VIEWPORT_MARGIN$1) / scale))}px`,
56851
+ maxHeight: `${Math.max(120, Math.floor((viewport.height - 2 * VIEWPORT_MARGIN$1) / scale))}px`
56852
+ };
56853
+ const controlPx = scale < 1 ? Math.ceil(CONTROL_MIN_PX / scale) : CONTROL_MIN_PX;
56854
+ const controlStyle = { width: `${controlPx}px`, height: `${controlPx}px` };
56855
+ const percentStyle = { height: `${controlPx}px`, minWidth: `${Math.ceil(48 / Math.min(scale, 1))}px` };
56856
+ const modalContent = /* @__PURE__ */ jsxRuntimeExports.jsx(
56685
56857
  "div",
56686
56858
  {
56687
56859
  ref: containerRef,
56688
- className: tw`fixed z-[9999] flex flex-col overflow-hidden rounded-xl border border-slate-300 bg-white shadow-2xl`,
56860
+ role: "dialog",
56861
+ "aria-labelledby": titleId,
56862
+ tabIndex: -1,
56863
+ onKeyDown: handleKeyDown,
56864
+ onFocus: handleFocusWithin,
56865
+ onBlur: handleBlurWithin,
56866
+ className: tw`fixed z-[9999] overflow-hidden rounded-xl border border-gray-300 bg-white text-gray-900 shadow-2xl focus:outline-none`,
56689
56867
  style: {
56690
56868
  left: `${position2.x}px`,
56691
56869
  top: `${position2.y}px`,
56692
- width: "520px",
56693
- maxWidth: "calc(100vw - 32px)",
56694
- maxHeight: "calc(100vh - 32px)"
56870
+ maxWidth: `calc(100vw - ${2 * VIEWPORT_MARGIN$1}px)`,
56871
+ maxHeight: `calc(100vh - ${2 * VIEWPORT_MARGIN$1}px)`
56695
56872
  },
56696
- children: [
56697
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
56698
- "div",
56699
- {
56700
- onPointerDown: beginDrag,
56701
- className: tw`flex cursor-move items-center justify-between border-b border-slate-200 bg-gradient-to-r from-blue-500 to-blue-600 px-4 py-3 text-white`,
56702
- children: [
56703
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: tw`flex items-center gap-2`, children: [
56704
- /* @__PURE__ */ jsxRuntimeExports.jsx(FiUser, { className: tw`h-5 w-5` }),
56705
- /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: tw`text-sm font-bold`, children: "Informações da Campanha" })
56706
- ] }),
56707
- /* @__PURE__ */ jsxRuntimeExports.jsx(
56708
- "button",
56709
- {
56710
- type: "button",
56711
- onClick: handleCloseClick,
56712
- disabled: isCallActive,
56713
- className: tw`rounded-full p-1 transition hover:bg-white/20 disabled:cursor-not-allowed disabled:opacity-40`,
56714
- title: isCallActive ? "Aguarde o fim da chamada para fechar" : "Fechar",
56715
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(FiX, { className: tw`h-5 w-5` })
56716
- }
56717
- )
56718
- ]
56719
- }
56720
- ),
56721
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: tw`flex-1 space-y-3 overflow-y-auto p-4`, children: [
56722
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: tw`grid grid-cols-2 gap-3`, children: [
56723
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: tw`flex items-center gap-2 rounded-lg border border-slate-200 bg-slate-50 px-3 py-2`, children: [
56724
- /* @__PURE__ */ jsxRuntimeExports.jsx(FiClock, { className: tw`h-4 w-4 text-slate-500` }),
56725
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
56726
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`text-[10px] text-slate-500 uppercase tracking-wide font-medium`, children: "Duração" }),
56727
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`text-sm font-bold text-slate-800`, children: formatDuration(callDuration) })
56728
- ] })
56729
- ] }),
56730
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: tw`flex items-center gap-2 rounded-lg border border-slate-200 bg-slate-50 px-3 py-2`, children: [
56731
- /* @__PURE__ */ jsxRuntimeExports.jsx(FiUser, { className: tw`h-4 w-4 text-slate-500` }),
56732
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: tw`min-w-0 flex-1`, children: [
56733
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`text-[10px] text-slate-500 uppercase tracking-wide font-medium`, children: "Cliente" }),
56734
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`truncate text-sm font-bold text-slate-800`, children: (campaignContact == null ? void 0 : campaignContact.label) || "N/A" })
56735
- ] })
56736
- ] })
56737
- ] }),
56738
- isLoading && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: tw`rounded-lg border border-blue-200 bg-blue-50 p-6 text-center`, role: "status", children: [
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` }),
56740
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`text-xs font-medium text-blue-700`, children: "Carregando dados do contato..." })
56741
- ] }),
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: [
56743
- /* @__PURE__ */ jsxRuntimeExports.jsx(FiAlertCircle, { className: tw`h-4 w-4 flex-shrink-0 text-red-600` }),
56744
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: tw`min-w-0 flex-1`, children: [
56745
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`text-xs font-semibold text-red-700`, children: "Erro ao carregar dados" }),
56746
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`text-[10px] text-red-600`, children: error2 })
56747
- ] }),
56748
- /* @__PURE__ */ jsxRuntimeExports.jsx(
56749
- "button",
56873
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
56874
+ "div",
56875
+ {
56876
+ "data-testid": "campaign-form-scaled",
56877
+ "data-scale": zoomSupported ? scalePercent : FORM_SCALE_DEFAULT,
56878
+ className: tw`flex flex-col`,
56879
+ style: scaledStyle,
56880
+ children: [
56881
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
56882
+ "div",
56750
56883
  {
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"
56884
+ onPointerDown: beginDrag,
56885
+ className: tw`flex flex-wrap cursor-move select-none items-center gap-2 border-b border-gray-200 bg-gradient-to-r from-blue-600 to-blue-700 px-3 py-2.5 text-white`,
56886
+ style: { touchAction: "none" },
56887
+ children: [
56888
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: tw`flex min-w-[7rem] flex-1 items-center gap-2`, children: [
56889
+ /* @__PURE__ */ jsxRuntimeExports.jsx(FiUser, { "aria-hidden": true, className: tw`h-5 w-5 flex-shrink-0` }),
56890
+ /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { id: titleId, className: tw`truncate text-sm font-bold`, children: "Informações da Campanha" })
56891
+ ] }),
56892
+ zoomSupported && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { role: "group", "aria-label": "Tamanho do formulário", className: tw`ml-auto flex flex-shrink-0 items-center gap-0.5`, children: [
56893
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
56894
+ "button",
56895
+ {
56896
+ type: "button",
56897
+ onClick: handleDecrease,
56898
+ "aria-disabled": !canDecrease,
56899
+ "aria-label": "Diminuir o tamanho do formulário",
56900
+ title: "Diminuir o formulário e o texto",
56901
+ className: tw`${CONTROL_BUTTON_CLASS} ${canDecrease ? "" : "cursor-not-allowed"}`,
56902
+ style: controlStyle,
56903
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(FiMinus, { "aria-hidden": true, className: tw`h-4 w-4 ${canDecrease ? "" : "opacity-40"}` })
56904
+ }
56905
+ ),
56906
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
56907
+ "button",
56908
+ {
56909
+ type: "button",
56910
+ onClick: handleResetScale,
56911
+ "aria-label": `Tamanho atual ${scalePercent}%. Restaurar o tamanho padrão`,
56912
+ title: "Restaurar o tamanho padrão (100%)",
56913
+ className: tw`flex-shrink-0 rounded-md px-1 text-center text-xs font-semibold tabular-nums transition hover:bg-black hover:bg-opacity-10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white`,
56914
+ style: percentStyle,
56915
+ children: [
56916
+ scalePercent,
56917
+ "%"
56918
+ ]
56919
+ }
56920
+ ),
56921
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
56922
+ "button",
56923
+ {
56924
+ type: "button",
56925
+ onClick: handleIncrease,
56926
+ "aria-disabled": !canIncrease,
56927
+ "aria-label": "Aumentar o tamanho do formulário",
56928
+ title: "Aumentar o formulário e o texto",
56929
+ className: tw`${CONTROL_BUTTON_CLASS} ${canIncrease ? "" : "cursor-not-allowed"}`,
56930
+ style: controlStyle,
56931
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(FiPlus, { "aria-hidden": true, className: tw`h-4 w-4 ${canIncrease ? "" : "opacity-40"}` })
56932
+ }
56933
+ ),
56934
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { role: "status", className: tw`sr-only`, children: announcement })
56935
+ ] }),
56936
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
56937
+ "button",
56938
+ {
56939
+ type: "button",
56940
+ onClick: handleCloseClick,
56941
+ disabled: isCallActive,
56942
+ "aria-label": isCallActive ? "Aguarde o fim da chamada para fechar" : "Fechar",
56943
+ className: tw`${CONTROL_BUTTON_CLASS} rounded-full disabled:cursor-not-allowed disabled:opacity-40`,
56944
+ style: controlStyle,
56945
+ title: isCallActive ? "Aguarde o fim da chamada para fechar" : "Fechar",
56946
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(FiX, { "aria-hidden": true, className: tw`h-5 w-5` })
56947
+ }
56948
+ )
56949
+ ]
56755
56950
  }
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)) }),
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: [
56769
- /* @__PURE__ */ jsxRuntimeExports.jsx(FiUser, { className: tw`mb-2 h-8 w-8 text-slate-400` }),
56770
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`text-sm text-slate-500`, children: "Nenhum dado disponível" })
56771
- ] })
56772
- ] }),
56773
- isCallActive && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: tw`border-t border-slate-200 bg-slate-50 px-4 py-2`, children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`text-center text-xs text-slate-500`, children: "Aguarde o fim da chamada para fechar" }) })
56774
- ]
56951
+ ),
56952
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
56953
+ "div",
56954
+ {
56955
+ tabIndex: 0,
56956
+ role: "region",
56957
+ "aria-label": "Dados do contato",
56958
+ className: tw`min-h-0 flex-1 space-y-3 overflow-y-auto p-4 focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-blue-500`,
56959
+ children: [
56960
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: tw`grid grid-cols-2 gap-3`, children: [
56961
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: tw`flex items-center gap-2 rounded-lg border border-gray-200 bg-gray-50 px-3 py-2`, children: [
56962
+ /* @__PURE__ */ jsxRuntimeExports.jsx(FiClock, { "aria-hidden": true, className: tw`h-4 w-4 text-gray-500` }),
56963
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
56964
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`text-[10px] text-gray-500 uppercase tracking-wide font-medium`, children: "Duração" }),
56965
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`text-sm font-bold text-gray-800`, children: formatDuration(callDuration) })
56966
+ ] })
56967
+ ] }),
56968
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: tw`flex items-center gap-2 rounded-lg border border-gray-200 bg-gray-50 px-3 py-2`, children: [
56969
+ /* @__PURE__ */ jsxRuntimeExports.jsx(FiUser, { "aria-hidden": true, className: tw`h-4 w-4 text-gray-500` }),
56970
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: tw`min-w-0 flex-1`, children: [
56971
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`text-[10px] text-gray-500 uppercase tracking-wide font-medium`, children: "Cliente" }),
56972
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`truncate text-sm font-bold text-gray-800`, children: (campaignContact == null ? void 0 : campaignContact.label) || "N/A" })
56973
+ ] })
56974
+ ] })
56975
+ ] }),
56976
+ isLoading && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: tw`rounded-lg border border-blue-200 bg-blue-50 p-6 text-center`, role: "status", children: [
56977
+ /* @__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` }),
56978
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`text-xs font-medium text-blue-700`, children: "Carregando dados do contato..." })
56979
+ ] }),
56980
+ 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: [
56981
+ /* @__PURE__ */ jsxRuntimeExports.jsx(FiAlertCircle, { "aria-hidden": true, className: tw`h-4 w-4 flex-shrink-0 text-red-600` }),
56982
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: tw`min-w-0 flex-1`, children: [
56983
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`text-xs font-semibold text-red-700`, children: "Erro ao carregar dados" }),
56984
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`text-[10px] text-red-600`, children: error2 })
56985
+ ] }),
56986
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
56987
+ "button",
56988
+ {
56989
+ type: "button",
56990
+ onClick: handleRetry,
56991
+ 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`,
56992
+ children: "Tentar novamente"
56993
+ }
56994
+ )
56995
+ ] }) }),
56996
+ !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(
56997
+ Field,
56998
+ {
56999
+ label: field.label,
57000
+ value: field.value || "-",
57001
+ fullWidth: field.width === "100%"
57002
+ },
57003
+ `${rowIndex}-${fieldIndex}-${field.key}`
57004
+ )) }, rowIndex)) }),
57005
+ !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)) }),
57006
+ !isLoading && !error2 && !hasData && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: tw`flex flex-col items-center justify-center rounded-lg border border-gray-200 bg-gray-50 px-4 py-8 text-center`, children: [
57007
+ /* @__PURE__ */ jsxRuntimeExports.jsx(FiUser, { "aria-hidden": true, className: tw`mb-2 h-8 w-8 text-gray-400` }),
57008
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`text-sm text-gray-500`, children: "Nenhum dado disponível" })
57009
+ ] })
57010
+ ]
57011
+ }
57012
+ ),
57013
+ isCallActive && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: tw`border-t border-gray-200 bg-gray-50 px-4 py-2`, children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`text-center text-xs text-gray-500`, children: "Aguarde o fim da chamada para fechar" }) })
57014
+ ]
57015
+ }
57016
+ )
56775
57017
  }
56776
57018
  );
56777
57019
  if (typeof document !== "undefined") {
56778
- return createPortal(modalContent, document.body);
57020
+ return createPortal(modalContent, portalTarget ?? document.body);
56779
57021
  }
56780
57022
  return null;
56781
57023
  }
@@ -56785,9 +57027,16 @@ function Field({
56785
57027
  fullWidth
56786
57028
  }) {
56787
57029
  const widthClass = fullWidth ? "col-span-full" : "";
56788
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: tw`rounded-lg border border-slate-200 bg-white p-2 ${widthClass}`, children: [
56789
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`mb-0.5 truncate text-[9px] font-semibold uppercase tracking-wide text-slate-500`, children: label }),
56790
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`break-words text-xs font-semibold text-slate-800 line-clamp-2`, children: value })
57030
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: tw`rounded-lg border border-gray-200 bg-white p-2 ${widthClass}`, children: [
57031
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`mb-0.5 truncate text-[9px] font-semibold uppercase tracking-wide text-gray-500`, children: label }),
57032
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
57033
+ "p",
57034
+ {
57035
+ className: tw`overflow-hidden break-words text-xs font-semibold text-gray-800`,
57036
+ style: { display: "-webkit-box", WebkitLineClamp: 2, WebkitBoxOrient: "vertical" },
57037
+ children: value
57038
+ }
57039
+ )
56791
57040
  ] });
56792
57041
  }
56793
57042
  if (typeof window !== "undefined" && !window.__fetchIntercepted__) {
@@ -58364,14 +58613,15 @@ function InlineDialer({ variant = "default", portalTarget, isAuthenticated, mode
58364
58613
  if (!isAuthenticated || !isMiniMode) {
58365
58614
  return /* @__PURE__ */ jsxRuntimeExports.jsx(FloatingDialer, { variant, portalTarget, isAuthenticated, ...props });
58366
58615
  }
58367
- return /* @__PURE__ */ jsxRuntimeExports.jsx(AuthenticatedMiniDialer, { ...props });
58616
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(AuthenticatedMiniDialer, { portalTarget, ...props });
58368
58617
  }
58369
58618
  function AuthenticatedMiniDialer({
58370
58619
  username,
58371
58620
  onLogin,
58372
58621
  onLogout,
58373
58622
  isLoadingSession,
58374
- sessionError
58623
+ sessionError,
58624
+ portalTarget
58375
58625
  }) {
58376
58626
  var _a2;
58377
58627
  const { skipManualQualification } = useDialerConfig();
@@ -58636,7 +58886,8 @@ function AuthenticatedMiniDialer({
58636
58886
  campaignContactPhoneId: ((_a2 = currentCall == null ? void 0 : currentCall.data) == null ? void 0 : _a2.campaignContactPhoneId) ?? null,
58637
58887
  callDuration: callDurationSeconds,
58638
58888
  isCallActive: isInCall || isConnecting,
58639
- onClose: () => setIsClientInfoModalOpen(false)
58889
+ onClose: () => setIsClientInfoModalOpen(false),
58890
+ portalTarget
58640
58891
  }
58641
58892
  ),
58642
58893
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -58878,7 +59129,7 @@ function ClickToCallAgent({ enabled = true, pollIntervalMs, sessionToken, onTask
58878
59129
  }, [enabled, pollIntervalMs, sessionToken]);
58879
59130
  return null;
58880
59131
  }
58881
- const WIDGET_VERSION = "2.3.0";
59132
+ const WIDGET_VERSION = "2.4.0";
58882
59133
  let currentSipWebSocket = null;
58883
59134
  function sendSipUnregisterIfPossible(ws) {
58884
59135
  if (ws.readyState !== WebSocket.OPEN) return;