callix-dialer-widget 2.4.0 → 2.5.1
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.
|
@@ -48425,6 +48425,125 @@ if (!globalTwind.__twindInitialized) {
|
|
|
48425
48425
|
setup(config);
|
|
48426
48426
|
globalTwind.__twindInitialized = true;
|
|
48427
48427
|
}
|
|
48428
|
+
const AUDIO_ISSUES = {
|
|
48429
|
+
unsupported: { level: "error", title: "Navegador sem acesso ao microfone", detail: "Este navegador não permite usar o microfone pela página.", action: "Use Chrome, Edge ou Firefox atualizados." },
|
|
48430
|
+
"insecure-context": { level: "error", title: "Página sem HTTPS", detail: "O navegador só libera o microfone em páginas HTTPS (ou localhost).", action: "Abra o discador pelo endereço com https://." },
|
|
48431
|
+
"no-microphone": { level: "error", title: "Sem microfone", detail: "Nenhum microfone foi encontrado neste computador, por isso o discador não consegue conectar nem ligar.", action: "Conecte um fone ou headset e confira se o microfone está habilitado nas configurações de som do Windows; o discador reconhece sozinho quando aparecer." },
|
|
48432
|
+
"mic-permission-denied": { level: "error", title: "Microfone bloqueado pelo navegador", detail: "O navegador não permitiu que o discador use o microfone.", action: "Clique no cadeado ao lado do endereço, permita o microfone e recarregue a página." },
|
|
48433
|
+
"mic-unavailable": { level: "error", title: "Microfone em uso ou com falha", detail: "Outro programa pode estar usando o microfone, ou o dispositivo falhou ao iniciar.", action: "Feche outros aplicativos de áudio (Teams, Meet, gravadores) e tente de novo." },
|
|
48434
|
+
"no-speaker": { level: "warning", title: "Sem saída de som", detail: "Nenhum alto-falante ou fone foi encontrado: você não vai ouvir o cliente.", action: "Conecte um fone e confira o dispositivo de saída nas configurações de som." },
|
|
48435
|
+
"enumerate-failed": { level: "warning", title: "Não foi possível verificar os dispositivos de áudio", detail: "O navegador não listou os dispositivos de áudio.", action: "Confira as permissões da página e recarregue." }
|
|
48436
|
+
};
|
|
48437
|
+
function audioIssue(kind, detailSuffix) {
|
|
48438
|
+
const base = AUDIO_ISSUES[kind];
|
|
48439
|
+
return { ...base, detail: detailSuffix ? `${base.detail} (${detailSuffix})` : base.detail, kind, source: "device", code: `device:${kind}` };
|
|
48440
|
+
}
|
|
48441
|
+
const NO_DEVICE_ERRORS = /* @__PURE__ */ new Set(["NotFoundError", "DevicesNotFoundError", "OverconstrainedError"]);
|
|
48442
|
+
const DENIED_ERRORS = /* @__PURE__ */ new Set(["NotAllowedError", "PermissionDeniedError", "PermissionDismissedError", "SecurityError"]);
|
|
48443
|
+
const UNAVAILABLE_ERRORS = /* @__PURE__ */ new Set(["NotReadableError", "TrackStartError", "AbortError", "SourceUnavailableError"]);
|
|
48444
|
+
function classifyGetUserMediaError(error2) {
|
|
48445
|
+
const name = (error2 == null ? void 0 : error2.name) ?? "";
|
|
48446
|
+
if (NO_DEVICE_ERRORS.has(name)) return "no-microphone";
|
|
48447
|
+
if (DENIED_ERRORS.has(name)) return "mic-permission-denied";
|
|
48448
|
+
if (UNAVAILABLE_ERRORS.has(name)) return "mic-unavailable";
|
|
48449
|
+
return "mic-unavailable";
|
|
48450
|
+
}
|
|
48451
|
+
async function checkAudioDevices(options = {}) {
|
|
48452
|
+
const mediaDevices = options.mediaDevices === void 0 ? typeof navigator !== "undefined" ? navigator.mediaDevices : null : options.mediaDevices;
|
|
48453
|
+
const secure = options.isSecureContext ?? (typeof window !== "undefined" ? window.isSecureContext !== false : true);
|
|
48454
|
+
if (!mediaDevices || typeof mediaDevices.getUserMedia !== "function") return audioIssue(secure ? "unsupported" : "insecure-context");
|
|
48455
|
+
try {
|
|
48456
|
+
const stream = await mediaDevices.getUserMedia({ audio: true });
|
|
48457
|
+
for (const track of stream.getTracks()) track.stop();
|
|
48458
|
+
} catch (error2) {
|
|
48459
|
+
const kind = classifyGetUserMediaError(error2);
|
|
48460
|
+
const name = error2 == null ? void 0 : error2.name;
|
|
48461
|
+
return audioIssue(kind, kind === "mic-unavailable" && name ? name : void 0);
|
|
48462
|
+
}
|
|
48463
|
+
if (typeof mediaDevices.enumerateDevices !== "function") return null;
|
|
48464
|
+
let devices;
|
|
48465
|
+
try {
|
|
48466
|
+
devices = await mediaDevices.enumerateDevices();
|
|
48467
|
+
} catch {
|
|
48468
|
+
return audioIssue("enumerate-failed");
|
|
48469
|
+
}
|
|
48470
|
+
if (!devices.some((device) => device.kind === "audioinput")) return audioIssue("no-microphone");
|
|
48471
|
+
const canListOutputs = options.canListOutputs ?? (typeof HTMLMediaElement !== "undefined" && "setSinkId" in HTMLMediaElement.prototype);
|
|
48472
|
+
if (canListOutputs && !devices.some((device) => device.kind === "audiooutput")) return audioIssue("no-speaker");
|
|
48473
|
+
return null;
|
|
48474
|
+
}
|
|
48475
|
+
const STARTUP_NOTICES = {
|
|
48476
|
+
missingAudioPermissions: { level: "error", title: "Discador sem acesso ao microfone", detail: "O Callix não conseguiu usar o microfone para iniciar o atendimento.", action: "Conecte ou habilite um microfone, permita o uso no navegador e recarregue a página." },
|
|
48477
|
+
maxUsersReached: { level: "error", title: "Limite de usuários do Callix atingido", detail: "O serviço já está com o número máximo de agentes conectados.", action: "Aguarde um colega sair ou fale com o supervisor." },
|
|
48478
|
+
userIsAlreadyConnectedOnAnotherDevice: { level: "error", title: "Usuário já conectado em outro lugar", detail: "Este login está ativo em outro computador, aba ou no Callix web.", action: "Saia de lá (ou feche a outra aba) e entre de novo aqui." },
|
|
48479
|
+
unknownError: { level: "error", title: "O discador não conseguiu iniciar", detail: "O Callix não respondeu como esperado ao iniciar o atendimento.", action: "Recarregue a página; se continuar, confira a internet e avise o suporte." }
|
|
48480
|
+
};
|
|
48481
|
+
function describeStartupError(error2) {
|
|
48482
|
+
const kind = error2 && error2 in STARTUP_NOTICES ? error2 : "unknownError";
|
|
48483
|
+
return { ...STARTUP_NOTICES[kind], source: "startup", code: `startup:${kind}` };
|
|
48484
|
+
}
|
|
48485
|
+
const SESSION_DROP_NOTICES = {
|
|
48486
|
+
exclusiveLogin: { level: "error", title: "Você entrou em outro lugar", detail: "Este login foi aberto em outro computador, aba ou no Callix web, e a sessão daqui foi encerrada.", action: "Feche o outro acesso e entre de novo aqui." },
|
|
48487
|
+
supervisor: { level: "error", title: "Sessão encerrada pelo supervisor", detail: "Um supervisor desconectou você do atendimento.", action: "Fale com o supervisor antes de entrar de novo." },
|
|
48488
|
+
userBlocked: { level: "error", title: "Usuário bloqueado", detail: "Seu usuário foi bloqueado no Callix.", action: "Fale com o supervisor ou o administrador." },
|
|
48489
|
+
breakTimeLimit: { level: "warning", title: "Tempo de pausa excedido", detail: "A pausa passou do limite e o Callix encerrou a sessão.", action: "Entre de novo para voltar ao atendimento." },
|
|
48490
|
+
manualCallTimeLimit: { level: "warning", title: "Tempo de chamada manual excedido", detail: "A chamada manual passou do limite configurado e a sessão foi encerrada.", action: "Entre de novo para continuar." },
|
|
48491
|
+
idle: { level: "warning", title: "Sessão encerrada por inatividade", detail: "Ficou muito tempo sem atividade e o Callix desconectou.", action: "Entre de novo para continuar." },
|
|
48492
|
+
user: { level: "warning", title: "Você saiu do atendimento", detail: "A sessão foi encerrada a seu pedido.", action: "Entre de novo quando quiser voltar." }
|
|
48493
|
+
};
|
|
48494
|
+
function describeSessionDrop(originator) {
|
|
48495
|
+
const kind = originator && originator in SESSION_DROP_NOTICES ? originator : null;
|
|
48496
|
+
if (!kind) return { level: "error", source: "session", code: "session:unknown", title: "Sessão encerrada", detail: `A sessão no Callix foi encerrada${originator ? ` (${originator})` : ""}.`, action: "Entre de novo para continuar." };
|
|
48497
|
+
return { ...SESSION_DROP_NOTICES[kind], source: "session", code: `session:${kind}` };
|
|
48498
|
+
}
|
|
48499
|
+
function describeOffline() {
|
|
48500
|
+
return { level: "error", source: "offline", code: "offline", title: "Discador desconectado do Callix", detail: "O atendimento está offline: sem internet, sem microfone ou o Callix derrubou a conexão.", action: "Confira a internet e o microfone; o discador tenta reconectar sozinho. Se não voltar, recarregue a página." };
|
|
48501
|
+
}
|
|
48502
|
+
const REJECT_REASONS = {
|
|
48503
|
+
1: { title: "Número inválido", detail: "O Callix recusou o número discado.", action: "Confira o DDD e o número." },
|
|
48504
|
+
2: { title: "Usuário não encontrado", detail: "O Callix não reconheceu o usuário para esta chamada.", action: "Saia e entre de novo no discador." },
|
|
48505
|
+
3: { title: "Equipe bloqueada para ligar", detail: "Sua equipe está sem permissão de chamada manual neste momento.", action: "Fale com o supervisor." },
|
|
48506
|
+
4: { title: "Número na black list", detail: "Este número está na lista de bloqueio e não pode ser discado." },
|
|
48507
|
+
5: { title: "Sem rota para este número", detail: "A telefonia não tem rota de saída para este destino.", action: "Confira o número ou fale com o suporte." },
|
|
48508
|
+
6: { title: "Chamada recusada pelo Callix", detail: "O Callix não autorizou esta chamada." },
|
|
48509
|
+
7: { title: "Erro interno do Callix", detail: "O Callix falhou ao iniciar a chamada.", action: "Tente de novo em instantes." },
|
|
48510
|
+
8: { title: "Ligação internacional não autorizada", detail: "Chamadas internacionais estão desligadas para o seu usuário.", action: "Fale com o supervisor." },
|
|
48511
|
+
9: { title: "Saldo de telefonia insuficiente", detail: "A conta de telefonia do Callix está sem saldo.", action: "Avise o administrador." },
|
|
48512
|
+
10: { title: "Caixa postal ou silêncio", detail: "A chamada caiu em caixa postal ou ninguém falou do outro lado." }
|
|
48513
|
+
};
|
|
48514
|
+
const CAUSES = {
|
|
48515
|
+
Busy: { title: "Número ocupado", detail: "O destino está em outra ligação.", action: "Tente de novo em alguns minutos." },
|
|
48516
|
+
Unavailable: { title: "Número indisponível", detail: "O destino não atendeu ou está fora de área." },
|
|
48517
|
+
"Not Found": { title: "Número não existe", detail: "A operadora respondeu que este número não existe.", action: "Confira o DDD e o número." },
|
|
48518
|
+
"Address Incomplete": { title: "Número incompleto", detail: "Faltam dígitos no número discado.", action: "Confira o DDD e o número." },
|
|
48519
|
+
"Blocked By Carrier": { title: "Bloqueado pela operadora", detail: "A operadora bloqueou a chamada para este destino." },
|
|
48520
|
+
Rejected: { title: "Chamada recusada", detail: "O destino ou a operadora recusou a chamada." },
|
|
48521
|
+
"SIP Failure Code": { title: "Falha na telefonia", detail: "A operadora devolveu um erro ao completar a chamada.", action: "Tente de novo; se repetir, avise o suporte." },
|
|
48522
|
+
"Bad Media Description": { title: "Falha de áudio na chamada", detail: "A negociação de áudio com a operadora falhou.", action: "Recarregue a página e tente de novo." },
|
|
48523
|
+
"Silence or Machine Detected": { title: "Caixa postal ou silêncio", detail: "A chamada caiu em caixa postal ou ninguém falou do outro lado." }
|
|
48524
|
+
};
|
|
48525
|
+
function describeCallFailure(failure) {
|
|
48526
|
+
const reject = (failure == null ? void 0 : failure.rejectReason) != null ? REJECT_REASONS[failure.rejectReason] : void 0;
|
|
48527
|
+
const cause = (failure == null ? void 0 : failure.cause) ? CAUSES[failure.cause] : void 0;
|
|
48528
|
+
const chosen = reject ?? cause;
|
|
48529
|
+
if (chosen) return { level: "warning", source: "call", code: `call:${(failure == null ? void 0 : failure.rejectReason) != null ? `reject-${failure.rejectReason}` : failure == null ? void 0 : failure.cause}`, ...chosen };
|
|
48530
|
+
const raw = (failure == null ? void 0 : failure.message) || (failure == null ? void 0 : failure.cause);
|
|
48531
|
+
return { level: "warning", source: "call", code: "call:unknown", title: "Chamada não concluída", detail: raw ? `O Callix informou: ${raw}.` : "O Callix não informou o motivo.", action: "Tente de novo; se repetir, avise o suporte." };
|
|
48532
|
+
}
|
|
48533
|
+
function pickNotice(inputs) {
|
|
48534
|
+
if (inputs.sessionDrop) return describeSessionDrop(inputs.sessionDrop);
|
|
48535
|
+
if (inputs.operatorState === "startupError") {
|
|
48536
|
+
if (inputs.startupError === "missingAudioPermissions" && inputs.deviceIssue) {
|
|
48537
|
+
const action = inputs.deviceIssue.action ?? "";
|
|
48538
|
+
return { ...inputs.deviceIssue, action: /recarregue a página/i.test(action) ? action : `${action} Depois, recarregue a página para o discador iniciar.`.trim() };
|
|
48539
|
+
}
|
|
48540
|
+
return describeStartupError(inputs.startupError);
|
|
48541
|
+
}
|
|
48542
|
+
if (inputs.deviceIssue) return inputs.deviceIssue;
|
|
48543
|
+
if (inputs.operatorState === "manualCallFailed" || inputs.callFailure) return describeCallFailure(inputs.callFailure);
|
|
48544
|
+
if (inputs.operatorState === "offline") return describeOffline();
|
|
48545
|
+
return null;
|
|
48546
|
+
}
|
|
48428
48547
|
const DEFAULT_API_BASE_URL = "https://callix-zeta.vercel.app";
|
|
48429
48548
|
function resolveApiBaseUrl() {
|
|
48430
48549
|
if (typeof window === "undefined") {
|
|
@@ -52115,6 +52234,10 @@ function AuthenticatedDialer({
|
|
|
52115
52234
|
const visibleInputRef = useRef(null);
|
|
52116
52235
|
const [callError, setCallError] = useState(null);
|
|
52117
52236
|
const [availabilityError, setAvailabilityError] = useState(null);
|
|
52237
|
+
clientSdkReactExports.useCallOperatorEventListener("userSessionDropped", (data) => {
|
|
52238
|
+
const notice = describeSessionDrop(data == null ? void 0 : data.originator);
|
|
52239
|
+
setAvailabilityError(`${notice.title}: ${notice.detail}${notice.action ? ` ${notice.action}` : ""}`);
|
|
52240
|
+
});
|
|
52118
52241
|
const [afterCallError, setAfterCallError] = useState(null);
|
|
52119
52242
|
const [isSubmittingAfterCall, setIsSubmittingAfterCall] = useState(false);
|
|
52120
52243
|
const [afterCallResult, setAfterCallResult] = useState("success");
|
|
@@ -52306,7 +52429,11 @@ function AuthenticatedDialer({
|
|
|
52306
52429
|
const inputs = devices.filter((device) => device.kind === "audioinput");
|
|
52307
52430
|
const outputs = devices.filter((device) => device.kind === "audiooutput");
|
|
52308
52431
|
setAudioDevices({ inputs, outputs });
|
|
52309
|
-
|
|
52432
|
+
const issue = await checkAudioDevices();
|
|
52433
|
+
if (cancelled) {
|
|
52434
|
+
return;
|
|
52435
|
+
}
|
|
52436
|
+
setAudioDeviceError(issue ? `${issue.title}: ${issue.detail}${issue.action ? ` ${issue.action}` : ""}` : null);
|
|
52310
52437
|
setSelectedInputId((prev) => {
|
|
52311
52438
|
var _a4;
|
|
52312
52439
|
return prev ?? ((_a4 = inputs[0]) == null ? void 0 : _a4.deviceId) ?? null;
|
|
@@ -52521,9 +52648,8 @@ function AuthenticatedDialer({
|
|
|
52521
52648
|
return;
|
|
52522
52649
|
}
|
|
52523
52650
|
const handleFailed = (event) => {
|
|
52524
|
-
|
|
52525
|
-
|
|
52526
|
-
setCallError(cause ? `Chamada nao concluida: ${cause}` : "Chamada nao concluida.");
|
|
52651
|
+
const notice = describeCallFailure(event == null ? void 0 : event.failure);
|
|
52652
|
+
setCallError(`${notice.title}: ${notice.detail}${notice.action ? ` ${notice.action}` : ""}`);
|
|
52527
52653
|
};
|
|
52528
52654
|
currentCall.on("failed", handleFailed);
|
|
52529
52655
|
return () => {
|
|
@@ -54430,6 +54556,179 @@ function filterHistoryItems(items, query) {
|
|
|
54430
54556
|
}
|
|
54431
54557
|
return items.filter((item) => itemMatchesHistoryQuery(item, parsed));
|
|
54432
54558
|
}
|
|
54559
|
+
const EXPORT_STATUS = { pending: 1, inProgress: 2, completed: 3 };
|
|
54560
|
+
const EXPORT_KIND_LABELS = { manual: "Chamadas manuais", campaign: "Chamadas de campanha" };
|
|
54561
|
+
const EXPORT_CONTENT_LABELS = { audio: "Áudios (zip)", data: "Dados (planilha)" };
|
|
54562
|
+
const dayInBrasilia = (date) => new Intl.DateTimeFormat("en-CA", { timeZone: "America/Sao_Paulo", year: "numeric", month: "2-digit", day: "2-digit" }).format(date);
|
|
54563
|
+
function defaultExportRange(now = /* @__PURE__ */ new Date()) {
|
|
54564
|
+
return { from: dayInBrasilia(new Date(now.getTime() - 30 * 24 * 36e5)), to: dayInBrasilia(now) };
|
|
54565
|
+
}
|
|
54566
|
+
function formatFileSize(bytes) {
|
|
54567
|
+
if (!bytes || bytes <= 0) return "";
|
|
54568
|
+
if (bytes < 1024 * 1024) return `${Math.max(1, Math.round(bytes / 1024))} KB`;
|
|
54569
|
+
return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
54570
|
+
}
|
|
54571
|
+
function describeExport(summary) {
|
|
54572
|
+
const what = `${summary.kind ? EXPORT_KIND_LABELS[summary.kind] : "Exportação"} · ${summary.content ? EXPORT_CONTENT_LABELS[summary.content].toLowerCase() : ""}`.replace(/ · $/, "");
|
|
54573
|
+
if (summary.status === EXPORT_STATUS.completed) return `${what}: pronta${summary.fileSize ? ` (${formatFileSize(summary.fileSize)})` : ""}`;
|
|
54574
|
+
if (summary.status === EXPORT_STATUS.inProgress) return `${what}: processando${summary.progress ? ` ${summary.progress}%` : ""}`;
|
|
54575
|
+
if (summary.status === EXPORT_STATUS.pending) return `${what}: na fila do Callix`;
|
|
54576
|
+
return `${what}: ${summary.statusLabel}`;
|
|
54577
|
+
}
|
|
54578
|
+
async function readError(response, fallback) {
|
|
54579
|
+
const body = await response.json().catch(() => null);
|
|
54580
|
+
return typeof (body == null ? void 0 : body.error) === "string" && body.error ? body.error : fallback;
|
|
54581
|
+
}
|
|
54582
|
+
async function createCallExport(apiBaseUrl, headers, request) {
|
|
54583
|
+
const response = await fetch(`${apiBaseUrl}/api/call-exports`, { method: "POST", headers: { "Content-Type": "application/json", ...headers }, credentials: "omit", body: JSON.stringify(request) });
|
|
54584
|
+
if (!response.ok) throw new Error(await readError(response, `Não foi possível pedir a exportação (${response.status})`));
|
|
54585
|
+
const body = await response.json();
|
|
54586
|
+
return body.export;
|
|
54587
|
+
}
|
|
54588
|
+
async function listCallExports(apiBaseUrl, headers) {
|
|
54589
|
+
const response = await fetch(`${apiBaseUrl}/api/call-exports`, { method: "GET", headers, credentials: "omit" });
|
|
54590
|
+
if (!response.ok) throw new Error(await readError(response, `Não foi possível listar as exportações (${response.status})`));
|
|
54591
|
+
const body = await response.json();
|
|
54592
|
+
return body.items ?? [];
|
|
54593
|
+
}
|
|
54594
|
+
async function fetchCallExportDownload(apiBaseUrl, headers, id) {
|
|
54595
|
+
const response = await fetch(`${apiBaseUrl}/api/call-exports/${encodeURIComponent(id)}/download`, { method: "GET", headers, credentials: "omit" });
|
|
54596
|
+
const body = await response.json().catch(() => null);
|
|
54597
|
+
if (response.ok && (body == null ? void 0 : body.url) && body.export) return { state: "ready", url: body.url, filename: body.filename ?? null, fileSize: body.fileSize ?? null, export: body.export };
|
|
54598
|
+
if (response.status === 409 && (body == null ? void 0 : body.export)) {
|
|
54599
|
+
return body.export.status <= EXPORT_STATUS.completed ? { state: "pending", export: body.export } : { state: "failed", message: body.error ?? `Exportação ${body.export.statusLabel}`, export: body.export };
|
|
54600
|
+
}
|
|
54601
|
+
return { state: "failed", message: (body == null ? void 0 : body.error) ?? `Não foi possível consultar a exportação (${response.status})`, export: body == null ? void 0 : body.export };
|
|
54602
|
+
}
|
|
54603
|
+
const RECENT_LIMIT = 3;
|
|
54604
|
+
function CallExportPanel({ apiBaseUrl, getAuthHeaders: getAuthHeaders2, onClose, pollIntervalMs = 5e3 }) {
|
|
54605
|
+
const [range] = useState(() => defaultExportRange());
|
|
54606
|
+
const [kind, setKind] = useState("manual");
|
|
54607
|
+
const [content, setContent] = useState("audio");
|
|
54608
|
+
const [from, setFrom] = useState(range.from);
|
|
54609
|
+
const [to, setTo] = useState(range.to);
|
|
54610
|
+
const [submitting, setSubmitting] = useState(false);
|
|
54611
|
+
const [error2, setError] = useState(null);
|
|
54612
|
+
const [job, setJob] = useState(null);
|
|
54613
|
+
const [download, setDownload] = useState(null);
|
|
54614
|
+
const [recent, setRecent] = useState([]);
|
|
54615
|
+
const [opening, setOpening] = useState(null);
|
|
54616
|
+
const mounted = useRef(true);
|
|
54617
|
+
useEffect(() => {
|
|
54618
|
+
mounted.current = true;
|
|
54619
|
+
return () => {
|
|
54620
|
+
mounted.current = false;
|
|
54621
|
+
};
|
|
54622
|
+
}, []);
|
|
54623
|
+
useEffect(() => {
|
|
54624
|
+
let active = true;
|
|
54625
|
+
listCallExports(apiBaseUrl, getAuthHeaders2()).then((items) => {
|
|
54626
|
+
if (active) setRecent(items.slice(0, RECENT_LIMIT));
|
|
54627
|
+
}).catch(() => void 0);
|
|
54628
|
+
return () => {
|
|
54629
|
+
active = false;
|
|
54630
|
+
};
|
|
54631
|
+
}, [apiBaseUrl, getAuthHeaders2]);
|
|
54632
|
+
useEffect(() => {
|
|
54633
|
+
if (!job || (download == null ? void 0 : download.state) === "ready" || (download == null ? void 0 : download.state) === "failed") return;
|
|
54634
|
+
let timer = null;
|
|
54635
|
+
let active = true;
|
|
54636
|
+
const check = async () => {
|
|
54637
|
+
const result2 = await fetchCallExportDownload(apiBaseUrl, getAuthHeaders2(), job.id);
|
|
54638
|
+
if (!active) return;
|
|
54639
|
+
setDownload(result2);
|
|
54640
|
+
if (result2.state === "pending") {
|
|
54641
|
+
setJob(result2.export);
|
|
54642
|
+
timer = setTimeout(check, pollIntervalMs);
|
|
54643
|
+
} else if (result2.state === "ready") {
|
|
54644
|
+
setJob(result2.export);
|
|
54645
|
+
setRecent((items) => [result2.export, ...items.filter((i2) => i2.id !== result2.export.id)].slice(0, RECENT_LIMIT));
|
|
54646
|
+
}
|
|
54647
|
+
};
|
|
54648
|
+
void check();
|
|
54649
|
+
return () => {
|
|
54650
|
+
active = false;
|
|
54651
|
+
if (timer) clearTimeout(timer);
|
|
54652
|
+
};
|
|
54653
|
+
}, [job == null ? void 0 : job.id, apiBaseUrl, getAuthHeaders2, pollIntervalMs]);
|
|
54654
|
+
const submit = useCallback(async () => {
|
|
54655
|
+
setError(null);
|
|
54656
|
+
if (!from || !to) {
|
|
54657
|
+
setError("Informe o período.");
|
|
54658
|
+
return;
|
|
54659
|
+
}
|
|
54660
|
+
if (to < from) {
|
|
54661
|
+
setError("O fim do período vem antes do início.");
|
|
54662
|
+
return;
|
|
54663
|
+
}
|
|
54664
|
+
setSubmitting(true);
|
|
54665
|
+
setDownload(null);
|
|
54666
|
+
try {
|
|
54667
|
+
const created = await createCallExport(apiBaseUrl, getAuthHeaders2(), { kind, content, from, to });
|
|
54668
|
+
if (!mounted.current) return;
|
|
54669
|
+
setJob(created);
|
|
54670
|
+
setRecent((items) => [created, ...items.filter((i2) => i2.id !== created.id)].slice(0, RECENT_LIMIT));
|
|
54671
|
+
} catch (err) {
|
|
54672
|
+
if (mounted.current) setError(err instanceof Error ? err.message : "Não foi possível pedir a exportação.");
|
|
54673
|
+
} finally {
|
|
54674
|
+
if (mounted.current) setSubmitting(false);
|
|
54675
|
+
}
|
|
54676
|
+
}, [apiBaseUrl, getAuthHeaders2, kind, content, from, to]);
|
|
54677
|
+
const openRecent = useCallback(
|
|
54678
|
+
async (item) => {
|
|
54679
|
+
setOpening(item.id);
|
|
54680
|
+
setError(null);
|
|
54681
|
+
try {
|
|
54682
|
+
const result2 = await fetchCallExportDownload(apiBaseUrl, getAuthHeaders2(), item.id);
|
|
54683
|
+
if (!mounted.current) return;
|
|
54684
|
+
if (result2.state === "ready") window.open(result2.url, "_blank", "noopener,noreferrer");
|
|
54685
|
+
else setError(result2.state === "pending" ? describeExport(result2.export) : result2.message);
|
|
54686
|
+
} finally {
|
|
54687
|
+
if (mounted.current) setOpening(null);
|
|
54688
|
+
}
|
|
54689
|
+
},
|
|
54690
|
+
[apiBaseUrl, getAuthHeaders2]
|
|
54691
|
+
);
|
|
54692
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "export-panel", role: "region", "aria-label": "Exportar histórico", children: [
|
|
54693
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "export-panel-header", children: [
|
|
54694
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "export-panel-title", children: "Exportar histórico" }),
|
|
54695
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "export-panel-close", onClick: onClose, "aria-label": "Fechar exportação", children: /* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fas fa-xmark", "aria-hidden": "true" }) })
|
|
54696
|
+
] }),
|
|
54697
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "export-panel-form", children: [
|
|
54698
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "export-panel-field", children: [
|
|
54699
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Tipo" }),
|
|
54700
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("select", { value: kind, onChange: (event) => setKind(event.target.value), "aria-label": "Tipo de chamada", children: Object.keys(EXPORT_KIND_LABELS).map((value) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value, children: EXPORT_KIND_LABELS[value] }, value)) })
|
|
54701
|
+
] }),
|
|
54702
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "export-panel-field", children: [
|
|
54703
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Conteúdo" }),
|
|
54704
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("select", { value: content, onChange: (event) => setContent(event.target.value), "aria-label": "Conteúdo", children: Object.keys(EXPORT_CONTENT_LABELS).map((value) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value, children: EXPORT_CONTENT_LABELS[value] }, value)) })
|
|
54705
|
+
] }),
|
|
54706
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "export-panel-field", children: [
|
|
54707
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "De" }),
|
|
54708
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("input", { type: "date", value: from, max: to, onChange: (event) => setFrom(event.target.value), "aria-label": "Início do período" })
|
|
54709
|
+
] }),
|
|
54710
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "export-panel-field", children: [
|
|
54711
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Até" }),
|
|
54712
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("input", { type: "date", value: to, min: from, onChange: (event) => setTo(event.target.value), "aria-label": "Fim do período" })
|
|
54713
|
+
] }),
|
|
54714
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "retry-btn export-panel-submit", onClick: () => void submit(), disabled: submitting, children: submitting ? "Pedindo..." : "Exportar" })
|
|
54715
|
+
] }),
|
|
54716
|
+
error2 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "export-panel-error", role: "alert", children: error2 }),
|
|
54717
|
+
job && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "export-panel-job", role: "status", children: [
|
|
54718
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: (download == null ? void 0 : download.state) === "failed" ? download.message : describeExport(job) }),
|
|
54719
|
+
(download == null ? void 0 : download.state) === "ready" && /* @__PURE__ */ jsxRuntimeExports.jsxs("a", { className: "export-panel-link", href: download.url, target: "_blank", rel: "noopener noreferrer", children: [
|
|
54720
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fas fa-download", "aria-hidden": "true" }),
|
|
54721
|
+
" Baixar ",
|
|
54722
|
+
download.filename ?? "arquivo"
|
|
54723
|
+
] })
|
|
54724
|
+
] }),
|
|
54725
|
+
recent.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("ul", { className: "export-panel-recent", "aria-label": "Exportações recentes", children: recent.map((item) => /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { children: [
|
|
54726
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: describeExport(item) }),
|
|
54727
|
+
item.status === EXPORT_STATUS.completed && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "export-panel-recent-btn", onClick: () => void openRecent(item), disabled: opening === item.id, children: opening === item.id ? "..." : "Baixar" })
|
|
54728
|
+
] }, item.id)) }),
|
|
54729
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "export-panel-hint", children: "O Callix prepara o arquivo em alguns minutos; o link de download vale por cerca de uma hora." })
|
|
54730
|
+
] });
|
|
54731
|
+
}
|
|
54433
54732
|
const DEFAULT_QUALIFICATION = "chamada sem qualificação";
|
|
54434
54733
|
const SEARCH_DEBOUNCE_MS = 350;
|
|
54435
54734
|
const DETAILS_TITLE = "Ver detalhes da chamada";
|
|
@@ -54903,6 +55202,7 @@ function CallHistoryList({
|
|
|
54903
55202
|
}) {
|
|
54904
55203
|
const [openPlayerId, setOpenPlayerId] = useState(null);
|
|
54905
55204
|
const [selectedId, setSelectedId] = useState(null);
|
|
55205
|
+
const [exportOpen, setExportOpen] = useState(false);
|
|
54906
55206
|
const recordings = useRecordings(apiBaseUrl, getAuthHeaders2);
|
|
54907
55207
|
const { retain } = recordings;
|
|
54908
55208
|
const [draft, setDraft] = useState(query ?? "");
|
|
@@ -54988,8 +55288,12 @@ function CallHistoryList({
|
|
|
54988
55288
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
54989
55289
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "history-header", children: [
|
|
54990
55290
|
/* @__PURE__ */ jsxRuntimeExports.jsx("h1", { className: "header-title", children: "Recentes" }),
|
|
54991
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
55291
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "history-header-actions", children: [
|
|
55292
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: `history-refresh-btn history-export-btn ${exportOpen ? "active" : ""}`, onClick: () => setExportOpen((open) => !open), "aria-label": "Exportar histórico", "aria-pressed": exportOpen, title: "Exportar áudios ou dados do período", children: /* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fas fa-file-export", "aria-hidden": "true" }) }),
|
|
55293
|
+
/* @__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" }) })
|
|
55294
|
+
] })
|
|
54992
55295
|
] }),
|
|
55296
|
+
exportOpen && /* @__PURE__ */ jsxRuntimeExports.jsx(CallExportPanel, { apiBaseUrl, getAuthHeaders: getAuthHeaders2, onClose: () => setExportOpen(false) }),
|
|
54993
55297
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "history-search-bar", children: [
|
|
54994
55298
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "history-search", children: [
|
|
54995
55299
|
/* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fas fa-magnifying-glass history-search-icon", "aria-hidden": "true" }),
|
|
@@ -56500,6 +56804,122 @@ function DebugPanel({ onClose }) {
|
|
|
56500
56804
|
showCopySuccess && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: styles.copySuccess, children: "✅ Logs copiados para a área de transferência!" })
|
|
56501
56805
|
] });
|
|
56502
56806
|
}
|
|
56807
|
+
function DialerNoticeBanner({ notice, onDismiss, onRecheck, onHeightChange }) {
|
|
56808
|
+
const ref = useRef(null);
|
|
56809
|
+
const visible = notice !== null;
|
|
56810
|
+
useLayoutEffect(() => {
|
|
56811
|
+
const el = ref.current;
|
|
56812
|
+
if (!onHeightChange) return;
|
|
56813
|
+
if (!el) {
|
|
56814
|
+
onHeightChange(0);
|
|
56815
|
+
return;
|
|
56816
|
+
}
|
|
56817
|
+
const report = () => onHeightChange(el.offsetHeight);
|
|
56818
|
+
report();
|
|
56819
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
56820
|
+
const observer = new ResizeObserver(report);
|
|
56821
|
+
observer.observe(el);
|
|
56822
|
+
return () => {
|
|
56823
|
+
observer.disconnect();
|
|
56824
|
+
onHeightChange(0);
|
|
56825
|
+
};
|
|
56826
|
+
}, [visible, onHeightChange]);
|
|
56827
|
+
if (!notice) return null;
|
|
56828
|
+
const dismissible = notice.source === "call" || notice.source === "session";
|
|
56829
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { ref, className: `dialer-notice ${notice.level}`, role: notice.level === "error" ? "alert" : "status", "data-code": notice.code, children: [
|
|
56830
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: `fas ${notice.level === "error" ? "fa-circle-exclamation" : "fa-triangle-exclamation"} dialer-notice-icon`, "aria-hidden": "true" }),
|
|
56831
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "dialer-notice-body", children: [
|
|
56832
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "dialer-notice-title", children: notice.title }),
|
|
56833
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "dialer-notice-detail", children: notice.detail }),
|
|
56834
|
+
notice.action && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "dialer-notice-action", children: notice.action }),
|
|
56835
|
+
notice.source === "device" && onRecheck && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "dialer-notice-btn", onClick: onRecheck, children: "Verificar de novo" })
|
|
56836
|
+
] }),
|
|
56837
|
+
dismissible && onDismiss && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "dialer-notice-close", onClick: onDismiss, "aria-label": "Fechar aviso", children: /* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fas fa-xmark", "aria-hidden": "true" }) })
|
|
56838
|
+
] });
|
|
56839
|
+
}
|
|
56840
|
+
function useDialerNotices(options = {}) {
|
|
56841
|
+
const operatorState = clientSdkReactExports.useCallOperatorState();
|
|
56842
|
+
const currentCall = clientSdkReactExports.useCallOperatorCurrentCall();
|
|
56843
|
+
const [deviceIssue, setDeviceIssue] = useState(null);
|
|
56844
|
+
const [sessionDrop, setSessionDrop] = useState(null);
|
|
56845
|
+
const [callFailure, setCallFailure] = useState(null);
|
|
56846
|
+
const [dismissedCode, setDismissedCode] = useState(null);
|
|
56847
|
+
const failureTimer = useRef(null);
|
|
56848
|
+
const ttl = options.callFailureTtlMs ?? 2e4;
|
|
56849
|
+
const recheckDevices = useCallback(async () => {
|
|
56850
|
+
if (options.checkDevices === false) return;
|
|
56851
|
+
const issue = await checkAudioDevices();
|
|
56852
|
+
setDeviceIssue(issue);
|
|
56853
|
+
}, [options.checkDevices]);
|
|
56854
|
+
useEffect(() => {
|
|
56855
|
+
var _a2;
|
|
56856
|
+
if (options.checkDevices === false) return;
|
|
56857
|
+
let cancelled = false;
|
|
56858
|
+
const run = async () => {
|
|
56859
|
+
const issue = await checkAudioDevices();
|
|
56860
|
+
if (!cancelled) setDeviceIssue(issue);
|
|
56861
|
+
};
|
|
56862
|
+
void run();
|
|
56863
|
+
const mediaDevices = typeof navigator !== "undefined" ? navigator.mediaDevices : void 0;
|
|
56864
|
+
const handler = () => void run();
|
|
56865
|
+
(_a2 = mediaDevices == null ? void 0 : mediaDevices.addEventListener) == null ? void 0 : _a2.call(mediaDevices, "devicechange", handler);
|
|
56866
|
+
return () => {
|
|
56867
|
+
var _a3;
|
|
56868
|
+
cancelled = true;
|
|
56869
|
+
(_a3 = mediaDevices == null ? void 0 : mediaDevices.removeEventListener) == null ? void 0 : _a3.call(mediaDevices, "devicechange", handler);
|
|
56870
|
+
};
|
|
56871
|
+
}, [options.checkDevices]);
|
|
56872
|
+
clientSdkReactExports.useCallOperatorEventListener("userSessionDropped", (data) => {
|
|
56873
|
+
setSessionDrop((data == null ? void 0 : data.originator) ?? "unknown");
|
|
56874
|
+
});
|
|
56875
|
+
useEffect(() => {
|
|
56876
|
+
if (!(currentCall == null ? void 0 : currentCall.on)) return;
|
|
56877
|
+
const handleFailed = (event) => {
|
|
56878
|
+
var _a2;
|
|
56879
|
+
const failure = (event == null ? void 0 : event.failure) ?? ((_a2 = currentCall.toCallData) == null ? void 0 : _a2.call(currentCall).failure) ?? null;
|
|
56880
|
+
setCallFailure(failure ?? { cause: null });
|
|
56881
|
+
setDismissedCode(null);
|
|
56882
|
+
if (failureTimer.current) clearTimeout(failureTimer.current);
|
|
56883
|
+
failureTimer.current = setTimeout(() => setCallFailure(null), ttl);
|
|
56884
|
+
};
|
|
56885
|
+
currentCall.on("failed", handleFailed);
|
|
56886
|
+
return () => {
|
|
56887
|
+
var _a2;
|
|
56888
|
+
(_a2 = currentCall.off) == null ? void 0 : _a2.call(currentCall, "failed", handleFailed);
|
|
56889
|
+
};
|
|
56890
|
+
}, [currentCall, ttl]);
|
|
56891
|
+
useEffect(() => {
|
|
56892
|
+
var _a2, _b;
|
|
56893
|
+
if ((operatorState == null ? void 0 : operatorState.state) !== "manualCallFailed") return;
|
|
56894
|
+
const failure = ((_b = (_a2 = operatorState.call) == null ? void 0 : _a2.toCallData) == null ? void 0 : _b.call(_a2).failure) ?? null;
|
|
56895
|
+
setCallFailure((current) => current ?? failure ?? { cause: null });
|
|
56896
|
+
setDismissedCode(null);
|
|
56897
|
+
}, [operatorState]);
|
|
56898
|
+
useEffect(() => {
|
|
56899
|
+
if ((operatorState == null ? void 0 : operatorState.state) === "callInProgress" || (operatorState == null ? void 0 : operatorState.state) === "manualCallRinging" || (operatorState == null ? void 0 : operatorState.state) === "callRinging") {
|
|
56900
|
+
setCallFailure(null);
|
|
56901
|
+
}
|
|
56902
|
+
if ((operatorState == null ? void 0 : operatorState.state) === "idle" || (operatorState == null ? void 0 : operatorState.state) === "onBreak") setSessionDrop(null);
|
|
56903
|
+
}, [operatorState == null ? void 0 : operatorState.state]);
|
|
56904
|
+
useEffect(() => () => {
|
|
56905
|
+
if (failureTimer.current) clearTimeout(failureTimer.current);
|
|
56906
|
+
}, []);
|
|
56907
|
+
const notice = pickNotice({
|
|
56908
|
+
deviceIssue,
|
|
56909
|
+
operatorState: (operatorState == null ? void 0 : operatorState.state) ?? null,
|
|
56910
|
+
startupError: (operatorState == null ? void 0 : operatorState.state) === "startupError" ? operatorState.error ?? null : null,
|
|
56911
|
+
sessionDrop,
|
|
56912
|
+
callFailure
|
|
56913
|
+
});
|
|
56914
|
+
const visible = notice && notice.code !== dismissedCode ? notice : null;
|
|
56915
|
+
const dismiss = useCallback(() => {
|
|
56916
|
+
if (!notice) return;
|
|
56917
|
+
if (notice.source === "call") setCallFailure(null);
|
|
56918
|
+
else if (notice.source === "session") setSessionDrop(null);
|
|
56919
|
+
else setDismissedCode(notice.code);
|
|
56920
|
+
}, [notice]);
|
|
56921
|
+
return { notice: visible, deviceIssue, dismiss, recheckDevices };
|
|
56922
|
+
}
|
|
56503
56923
|
const VIEWPORT_MARGIN$1 = 16;
|
|
56504
56924
|
const BASE_WIDTH = 520;
|
|
56505
56925
|
const FALLBACK_HEIGHT = 600;
|
|
@@ -57562,7 +57982,9 @@ function AuthenticatedMobileDialer({
|
|
|
57562
57982
|
};
|
|
57563
57983
|
}, [campaignFormKey]);
|
|
57564
57984
|
const callStateStr = callState;
|
|
57565
|
-
const
|
|
57985
|
+
const { notice: dialerNotice, dismiss: dismissDialerNotice, recheckDevices } = useDialerNotices();
|
|
57986
|
+
const [noticeHeight, setNoticeHeight] = useState(0);
|
|
57987
|
+
const isError = callStateStr === "startupError" || callStateStr === "offline" || (dialerNotice == null ? void 0 : dialerNotice.source) === "device" || (dialerNotice == null ? void 0 : dialerNotice.source) === "session";
|
|
57566
57988
|
const isIdle = !callStateStr || callStateStr === "starting" || callStateStr === "idle" || callStateStr === "manualCallSetup" || callStateStr === "onBreak";
|
|
57567
57989
|
const isConnecting = callState === "manualCallSetup" || callState === "manualCallRinging" || callState === "callRinging";
|
|
57568
57990
|
const isInCall = callState === "callInProgress";
|
|
@@ -58008,20 +58430,21 @@ function AuthenticatedMobileDialer({
|
|
|
58008
58430
|
if (isConnecting) return "Ligando...";
|
|
58009
58431
|
if (isInCall) return "Em chamada";
|
|
58010
58432
|
if (isAfterCall) return "Encerrando...";
|
|
58011
|
-
if (
|
|
58433
|
+
if (callStateStr === "manualCallFailed") return "Chamada não concluída";
|
|
58434
|
+
if (isError) return (dialerNotice == null ? void 0 : dialerNotice.title) ?? "Erro de conexão";
|
|
58012
58435
|
if (isOnBreak) return "Em pausa";
|
|
58013
58436
|
return "Disponível";
|
|
58014
|
-
}, [isAfterCall, isConnecting, isError, isInCall, isOnBreak]);
|
|
58437
|
+
}, [callStateStr, dialerNotice, isAfterCall, isConnecting, isError, isInCall, isOnBreak]);
|
|
58015
58438
|
const statusBadgeClass = useMemo(() => {
|
|
58016
58439
|
if (isError) return "error";
|
|
58017
58440
|
if (isOnBreak) return "paused";
|
|
58018
58441
|
return "available";
|
|
58019
58442
|
}, [isError, isOnBreak]);
|
|
58020
58443
|
const statusBadgeText = useMemo(() => {
|
|
58021
|
-
if (isError) return "Erro";
|
|
58444
|
+
if (isError) return (dialerNotice == null ? void 0 : dialerNotice.source) === "device" ? dialerNotice.title : "Erro";
|
|
58022
58445
|
if (isOnBreak) return currentBreakName || "Em pausa";
|
|
58023
58446
|
return "Disponível";
|
|
58024
|
-
}, [isError, isOnBreak, currentBreakName]);
|
|
58447
|
+
}, [dialerNotice, isError, isOnBreak, currentBreakName]);
|
|
58025
58448
|
const pauseDurationLabel = useMemo(() => {
|
|
58026
58449
|
const hours = Math.floor(pauseDurationSeconds / 3600);
|
|
58027
58450
|
const minutes = Math.floor(pauseDurationSeconds % 3600 / 60).toString().padStart(2, "0");
|
|
@@ -58057,7 +58480,7 @@ function AuthenticatedMobileDialer({
|
|
|
58057
58480
|
addLog("info", "Debug", "🔒 Painel de debug fechado");
|
|
58058
58481
|
} });
|
|
58059
58482
|
}
|
|
58060
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "phone-container", "data-density": density === "compact" ? "compact" : "comfortable", children: [
|
|
58483
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "phone-container", "data-density": density === "compact" ? "compact" : "comfortable", style: { "--notice-h": `${noticeHeight}px` }, children: [
|
|
58061
58484
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `screen dialer-screen ${activeScreen === "dialer" ? "active" : ""}`, id: "dialerScreen", children: [
|
|
58062
58485
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "dialer-header", children: [
|
|
58063
58486
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "status-container", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: `status-badge ${statusBadgeClass}`, id: "statusBadge", children: [
|
|
@@ -58075,6 +58498,7 @@ function AuthenticatedMobileDialer({
|
|
|
58075
58498
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Pausa" })
|
|
58076
58499
|
] }) })
|
|
58077
58500
|
] }),
|
|
58501
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(DialerNoticeBanner, { notice: dialerNotice, onDismiss: dismissDialerNotice, onRecheck: () => void recheckDevices(), onHeightChange: setNoticeHeight }),
|
|
58078
58502
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "phone-number-display", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
58079
58503
|
PhoneNumberInput,
|
|
58080
58504
|
{
|
|
@@ -59129,7 +59553,7 @@ function ClickToCallAgent({ enabled = true, pollIntervalMs, sessionToken, onTask
|
|
|
59129
59553
|
}, [enabled, pollIntervalMs, sessionToken]);
|
|
59130
59554
|
return null;
|
|
59131
59555
|
}
|
|
59132
|
-
const WIDGET_VERSION = "2.
|
|
59556
|
+
const WIDGET_VERSION = "2.5.1";
|
|
59133
59557
|
let currentSipWebSocket = null;
|
|
59134
59558
|
function sendSipUnregisterIfPossible(ws) {
|
|
59135
59559
|
if (ws.readyState !== WebSocket.OPEN) return;
|