callix-dialer-widget 2.5.1 → 2.7.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.
- package/WIDGET-README.md +56 -10
- package/dist-widget/callix-dialer.css +1 -1
- package/dist-widget/callix-dialer.es.js +1711 -102
- package/dist-widget/callix-dialer.es.js.map +1 -1
- package/dist-widget/callix-dialer.umd.js +4 -4
- package/dist-widget/callix-dialer.umd.js.map +1 -1
- package/package.json +1 -1
|
@@ -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, {
|
|
9
|
+
import React, { createContext, useMemo, useRef, useEffect, useSyncExternalStore, useCallback, useContext, useState, forwardRef, useLayoutEffect, useId, useImperativeHandle, StrictMode } 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) {
|
|
@@ -43725,6 +43725,790 @@ function requireClientSdkReact() {
|
|
|
43725
43725
|
return clientSdkReact;
|
|
43726
43726
|
}
|
|
43727
43727
|
var clientSdkReactExports = requireClientSdkReact();
|
|
43728
|
+
class EngineSignal {
|
|
43729
|
+
constructor(value) {
|
|
43730
|
+
this.value = value;
|
|
43731
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
43732
|
+
}
|
|
43733
|
+
get() {
|
|
43734
|
+
return this.value;
|
|
43735
|
+
}
|
|
43736
|
+
set(value) {
|
|
43737
|
+
if (JSON.stringify(value) === JSON.stringify(this.value)) return;
|
|
43738
|
+
this.value = value;
|
|
43739
|
+
for (const listener of this.listeners) listener(value);
|
|
43740
|
+
}
|
|
43741
|
+
subscribe(fn) {
|
|
43742
|
+
this.listeners.add(fn);
|
|
43743
|
+
return () => this.listeners.delete(fn);
|
|
43744
|
+
}
|
|
43745
|
+
}
|
|
43746
|
+
const INITIAL_TRANSCRIPT = { segments: [], status: null, insight: null, interims: 0 };
|
|
43747
|
+
const MAX_SEGMENTS = 300;
|
|
43748
|
+
const RECONNECT_MIN_MS = 500;
|
|
43749
|
+
const RECONNECT_MAX_MS = 5e3;
|
|
43750
|
+
function engineWsUrl(baseUrl) {
|
|
43751
|
+
const url = new URL(baseUrl, typeof window !== "undefined" ? window.location.href : "http://localhost");
|
|
43752
|
+
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
43753
|
+
url.pathname = `${url.pathname.replace(/\/+$/, "")}/ws`;
|
|
43754
|
+
url.search = "";
|
|
43755
|
+
url.hash = "";
|
|
43756
|
+
return url.toString();
|
|
43757
|
+
}
|
|
43758
|
+
class EngineClient {
|
|
43759
|
+
constructor(options) {
|
|
43760
|
+
this.options = options;
|
|
43761
|
+
this.userSignal = new EngineSignal(void 0);
|
|
43762
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
43763
|
+
this.sessionLostListeners = /* @__PURE__ */ new Set();
|
|
43764
|
+
this.viewWaiters = [];
|
|
43765
|
+
this.ws = null;
|
|
43766
|
+
this.reconnectDelay = RECONNECT_MIN_MS;
|
|
43767
|
+
this.reconnectTimer = null;
|
|
43768
|
+
this.closed = false;
|
|
43769
|
+
this.iceServers = [{ urls: "stun:stun.l.google.com:19302" }];
|
|
43770
|
+
this.pc = null;
|
|
43771
|
+
this.connectionId = null;
|
|
43772
|
+
this.remoteSet = false;
|
|
43773
|
+
this.pendingCandidates = [];
|
|
43774
|
+
this.micStream = null;
|
|
43775
|
+
this.audioSeq = 0;
|
|
43776
|
+
this.unlockListener = null;
|
|
43777
|
+
this.agentId = options.agentId;
|
|
43778
|
+
this.session = options.session ?? null;
|
|
43779
|
+
this.log = options.log ?? (() => void 0);
|
|
43780
|
+
this.audio = options.audioElement !== void 0 ? options.audioElement : createAudioElement();
|
|
43781
|
+
this.state = {
|
|
43782
|
+
wsState: "idle",
|
|
43783
|
+
tabId: null,
|
|
43784
|
+
view: null,
|
|
43785
|
+
engaged: false,
|
|
43786
|
+
pcState: "idle",
|
|
43787
|
+
needsUnlock: false,
|
|
43788
|
+
micError: null,
|
|
43789
|
+
lastError: null,
|
|
43790
|
+
inputMuted: false,
|
|
43791
|
+
outputMuted: false,
|
|
43792
|
+
outputVolume: 100,
|
|
43793
|
+
transcript: INITIAL_TRANSCRIPT,
|
|
43794
|
+
sessionLost: null
|
|
43795
|
+
};
|
|
43796
|
+
}
|
|
43797
|
+
// ------------------------------------------------------------------ store
|
|
43798
|
+
getState() {
|
|
43799
|
+
return this.state;
|
|
43800
|
+
}
|
|
43801
|
+
subscribe(listener) {
|
|
43802
|
+
this.listeners.add(listener);
|
|
43803
|
+
return () => {
|
|
43804
|
+
this.listeners.delete(listener);
|
|
43805
|
+
};
|
|
43806
|
+
}
|
|
43807
|
+
setState(patch) {
|
|
43808
|
+
this.state = { ...this.state, ...patch };
|
|
43809
|
+
for (const listener of this.listeners) listener();
|
|
43810
|
+
}
|
|
43811
|
+
get currentCall() {
|
|
43812
|
+
var _a2, _b;
|
|
43813
|
+
return ((_b = (_a2 = this.state.view) == null ? void 0 : _a2.snapshot) == null ? void 0 : _b.leg.call) ?? null;
|
|
43814
|
+
}
|
|
43815
|
+
// ------------------------------------------------------------------ conexão
|
|
43816
|
+
connect() {
|
|
43817
|
+
if (this.closed) this.closed = false;
|
|
43818
|
+
if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) return;
|
|
43819
|
+
const Impl = this.options.WebSocketImpl ?? WebSocket;
|
|
43820
|
+
this.setState({ wsState: "connecting" });
|
|
43821
|
+
const ws = new Impl(engineWsUrl(this.options.url));
|
|
43822
|
+
this.ws = ws;
|
|
43823
|
+
ws.onopen = () => {
|
|
43824
|
+
if (this.ws !== ws) return;
|
|
43825
|
+
this.reconnectDelay = RECONNECT_MIN_MS;
|
|
43826
|
+
this.setState({ wsState: "open" });
|
|
43827
|
+
const hello = { type: "hello", role: "tab", agentId: this.agentId, token: this.options.token, label: this.options.label ?? "Discador", ...this.session ? { session: this.session } : {} };
|
|
43828
|
+
this.send(hello);
|
|
43829
|
+
this.log("conectado ao host do motor");
|
|
43830
|
+
};
|
|
43831
|
+
ws.onmessage = (event) => {
|
|
43832
|
+
if (this.ws !== ws) return;
|
|
43833
|
+
let message;
|
|
43834
|
+
try {
|
|
43835
|
+
message = JSON.parse(String(event.data));
|
|
43836
|
+
} catch {
|
|
43837
|
+
return;
|
|
43838
|
+
}
|
|
43839
|
+
this.handle(message);
|
|
43840
|
+
};
|
|
43841
|
+
ws.onclose = () => {
|
|
43842
|
+
if (this.ws !== ws) return;
|
|
43843
|
+
this.ws = null;
|
|
43844
|
+
const wasEngaged = this.state.engaged;
|
|
43845
|
+
this.setState({ wsState: "closed", tabId: null, engaged: false });
|
|
43846
|
+
if (wasEngaged) this.stopAudio();
|
|
43847
|
+
if (this.closed) return;
|
|
43848
|
+
this.log(`conexão com o motor caiu; nova tentativa em ${this.reconnectDelay} ms`);
|
|
43849
|
+
this.reconnectTimer = setTimeout(() => {
|
|
43850
|
+
this.reconnectTimer = null;
|
|
43851
|
+
this.connect();
|
|
43852
|
+
}, this.reconnectDelay);
|
|
43853
|
+
this.reconnectDelay = Math.min(this.reconnectDelay * 2, RECONNECT_MAX_MS);
|
|
43854
|
+
};
|
|
43855
|
+
ws.onerror = () => void 0;
|
|
43856
|
+
}
|
|
43857
|
+
/** fecha de vez (sem reconectar) e solta o áudio */
|
|
43858
|
+
close() {
|
|
43859
|
+
this.closed = true;
|
|
43860
|
+
if (this.reconnectTimer) {
|
|
43861
|
+
clearTimeout(this.reconnectTimer);
|
|
43862
|
+
this.reconnectTimer = null;
|
|
43863
|
+
}
|
|
43864
|
+
this.stopAudio();
|
|
43865
|
+
const ws = this.ws;
|
|
43866
|
+
this.ws = null;
|
|
43867
|
+
if (ws) {
|
|
43868
|
+
ws.onclose = null;
|
|
43869
|
+
ws.close();
|
|
43870
|
+
}
|
|
43871
|
+
for (const waiter of this.viewWaiters.splice(0)) clearTimeout(waiter.timer);
|
|
43872
|
+
this.setState({ wsState: "closed", tabId: null, engaged: false });
|
|
43873
|
+
}
|
|
43874
|
+
/** a view atual, ou a próxima que o host mandar (welcome/view) */
|
|
43875
|
+
waitForView(timeoutMs = 15e3) {
|
|
43876
|
+
if (this.state.view && this.state.tabId) return Promise.resolve(this.state.view);
|
|
43877
|
+
return new Promise((resolve, reject) => {
|
|
43878
|
+
const timer = setTimeout(() => {
|
|
43879
|
+
const index = this.viewWaiters.findIndex((w) => w.timer === timer);
|
|
43880
|
+
if (index >= 0) this.viewWaiters.splice(index, 1);
|
|
43881
|
+
reject(new Error("o motor não respondeu"));
|
|
43882
|
+
}, timeoutMs);
|
|
43883
|
+
this.viewWaiters.push({ resolve, timer });
|
|
43884
|
+
});
|
|
43885
|
+
}
|
|
43886
|
+
send(message) {
|
|
43887
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) this.ws.send(JSON.stringify(message));
|
|
43888
|
+
}
|
|
43889
|
+
handle(message) {
|
|
43890
|
+
switch (message.type) {
|
|
43891
|
+
case "welcome": {
|
|
43892
|
+
if (Array.isArray(message.iceServers) && message.iceServers.length > 0) this.iceServers = message.iceServers;
|
|
43893
|
+
this.setState({ tabId: message.tabId });
|
|
43894
|
+
this.applyView(message.view);
|
|
43895
|
+
this.log(`sou a aba ${message.tabId}`);
|
|
43896
|
+
if (this.options.autoEngage !== false && message.view.engagedTabId !== message.tabId) this.engage();
|
|
43897
|
+
break;
|
|
43898
|
+
}
|
|
43899
|
+
case "view":
|
|
43900
|
+
this.applyView(message.view);
|
|
43901
|
+
break;
|
|
43902
|
+
case "engage":
|
|
43903
|
+
this.setState({ engaged: message.engaged });
|
|
43904
|
+
if (message.engaged) void this.startAudio();
|
|
43905
|
+
else this.stopAudio();
|
|
43906
|
+
break;
|
|
43907
|
+
case "signal":
|
|
43908
|
+
void this.onSignal(message.payload);
|
|
43909
|
+
break;
|
|
43910
|
+
case "error":
|
|
43911
|
+
this.setState({ lastError: message.message });
|
|
43912
|
+
this.log(`erro do host: ${message.message}`);
|
|
43913
|
+
break;
|
|
43914
|
+
case "transcript":
|
|
43915
|
+
this.pushSegment(message.segment);
|
|
43916
|
+
break;
|
|
43917
|
+
case "transcript-sync":
|
|
43918
|
+
this.setState({ transcript: { segments: message.segments ?? [], status: message.status ?? null, insight: message.insight ?? null, interims: 0 } });
|
|
43919
|
+
break;
|
|
43920
|
+
case "insight": {
|
|
43921
|
+
const current = this.state.transcript.insight;
|
|
43922
|
+
if (!current || message.insight.revision >= current.revision || message.insight.callId !== current.callId) {
|
|
43923
|
+
this.setState({ transcript: { ...this.state.transcript, insight: message.insight } });
|
|
43924
|
+
}
|
|
43925
|
+
break;
|
|
43926
|
+
}
|
|
43927
|
+
case "transcript-status":
|
|
43928
|
+
this.onTranscriptStatus(message.status);
|
|
43929
|
+
break;
|
|
43930
|
+
}
|
|
43931
|
+
}
|
|
43932
|
+
applyView(view) {
|
|
43933
|
+
var _a2, _b;
|
|
43934
|
+
const previous = this.state.view;
|
|
43935
|
+
const lost = view.sessionLost ?? null;
|
|
43936
|
+
const newlyLost = Boolean(lost) && !(previous == null ? void 0 : previous.sessionLost);
|
|
43937
|
+
if (newlyLost) {
|
|
43938
|
+
this.session = null;
|
|
43939
|
+
this.log(`o Callix derrubou a sessão: ${(lost == null ? void 0 : lost.message) ?? ""}`);
|
|
43940
|
+
}
|
|
43941
|
+
const user = (_a2 = view.snapshot) == null ? void 0 : _a2.leg.user;
|
|
43942
|
+
this.userSignal.set(user ? { ...user, sessionId: 0 } : void 0);
|
|
43943
|
+
const call = (_b = view.snapshot) == null ? void 0 : _b.leg.call;
|
|
43944
|
+
const patch = { view, sessionLost: lost ? { originator: lost.originator, message: lost.message } : null };
|
|
43945
|
+
if (call && typeof call.muted === "boolean" && call.muted !== this.state.inputMuted && call.state !== "ended") patch.inputMuted = call.muted;
|
|
43946
|
+
this.setState(patch);
|
|
43947
|
+
for (const waiter of this.viewWaiters.splice(0)) {
|
|
43948
|
+
clearTimeout(waiter.timer);
|
|
43949
|
+
waiter.resolve(view);
|
|
43950
|
+
}
|
|
43951
|
+
if (newlyLost && lost) for (const listener of this.sessionLostListeners) listener(lost);
|
|
43952
|
+
}
|
|
43953
|
+
onSessionLost(listener) {
|
|
43954
|
+
this.sessionLostListeners.add(listener);
|
|
43955
|
+
return () => {
|
|
43956
|
+
this.sessionLostListeners.delete(listener);
|
|
43957
|
+
};
|
|
43958
|
+
}
|
|
43959
|
+
// ------------------------------------------------------------------ sessão e comandos
|
|
43960
|
+
/** modo sdk: o widget fez login no back-end e entrega a sessão ao host */
|
|
43961
|
+
setSession(session) {
|
|
43962
|
+
this.session = session;
|
|
43963
|
+
this.send({ type: "session", session });
|
|
43964
|
+
}
|
|
43965
|
+
engage() {
|
|
43966
|
+
this.send({ type: "engage" });
|
|
43967
|
+
}
|
|
43968
|
+
release() {
|
|
43969
|
+
this.send({ type: "release" });
|
|
43970
|
+
}
|
|
43971
|
+
command(command) {
|
|
43972
|
+
this.send({ type: "command", command });
|
|
43973
|
+
this.log(`comando: ${command.name}${command.phone ? ` ${command.phone}` : ""}`);
|
|
43974
|
+
}
|
|
43975
|
+
// ------------------------------------------------------------------ áudio
|
|
43976
|
+
setInputMuted(muted) {
|
|
43977
|
+
if (this.micStream) for (const track of this.micStream.getAudioTracks()) track.enabled = !muted;
|
|
43978
|
+
this.setState({ inputMuted: muted });
|
|
43979
|
+
if (this.currentCall) this.command({ name: muted ? "mute" : "unmute" });
|
|
43980
|
+
}
|
|
43981
|
+
setOutputMuted(muted) {
|
|
43982
|
+
if (this.audio) this.audio.muted = muted;
|
|
43983
|
+
this.setState({ outputMuted: muted });
|
|
43984
|
+
}
|
|
43985
|
+
setOutputVolume(volume) {
|
|
43986
|
+
const clamped = Math.max(0, Math.min(100, Math.round(volume)));
|
|
43987
|
+
if (this.audio) this.audio.volume = clamped / 100;
|
|
43988
|
+
this.setState({ outputVolume: clamped });
|
|
43989
|
+
}
|
|
43990
|
+
async setOutputDevice(deviceId) {
|
|
43991
|
+
const audio = this.audio;
|
|
43992
|
+
if (!(audio == null ? void 0 : audio.setSinkId)) throw new Error("este navegador não permite escolher a saída de som");
|
|
43993
|
+
await audio.setSinkId(deviceId);
|
|
43994
|
+
}
|
|
43995
|
+
/** depois de um toque do agente: tenta tocar o som do motor que o navegador segurou */
|
|
43996
|
+
async unlockAudio() {
|
|
43997
|
+
if (!this.audio || !this.audio.srcObject) return;
|
|
43998
|
+
try {
|
|
43999
|
+
await this.audio.play();
|
|
44000
|
+
this.setState({ needsUnlock: false });
|
|
44001
|
+
} catch {
|
|
44002
|
+
this.setState({ needsUnlock: true });
|
|
44003
|
+
}
|
|
44004
|
+
}
|
|
44005
|
+
async startAudio() {
|
|
44006
|
+
const seq = ++this.audioSeq;
|
|
44007
|
+
this.stopAudio();
|
|
44008
|
+
const mediaDevices = this.options.mediaDevices === void 0 ? typeof navigator !== "undefined" ? navigator.mediaDevices : null : this.options.mediaDevices;
|
|
44009
|
+
if (!(mediaDevices == null ? void 0 : mediaDevices.getUserMedia)) {
|
|
44010
|
+
this.setState({ micError: "Este navegador não permite usar o microfone pela página." });
|
|
44011
|
+
return;
|
|
44012
|
+
}
|
|
44013
|
+
let stream;
|
|
44014
|
+
try {
|
|
44015
|
+
stream = await mediaDevices.getUserMedia({ audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true } });
|
|
44016
|
+
} catch (error2) {
|
|
44017
|
+
const message = `Microfone indisponível: ${error2.message}`;
|
|
44018
|
+
this.setState({ micError: message });
|
|
44019
|
+
this.log(message);
|
|
44020
|
+
return;
|
|
44021
|
+
}
|
|
44022
|
+
if (seq !== this.audioSeq || !this.state.engaged) {
|
|
44023
|
+
for (const track of stream.getTracks()) track.stop();
|
|
44024
|
+
return;
|
|
44025
|
+
}
|
|
44026
|
+
this.micStream = stream;
|
|
44027
|
+
for (const track of stream.getAudioTracks()) track.enabled = !this.state.inputMuted;
|
|
44028
|
+
this.setState({ micError: null });
|
|
44029
|
+
const Impl = this.options.PeerConnectionImpl ?? RTCPeerConnection;
|
|
44030
|
+
const connection = new Impl({ iceServers: this.iceServers });
|
|
44031
|
+
this.pc = connection;
|
|
44032
|
+
const connectionId = Math.random().toString(36).slice(2, 10);
|
|
44033
|
+
this.connectionId = connectionId;
|
|
44034
|
+
this.remoteSet = false;
|
|
44035
|
+
this.pendingCandidates = [];
|
|
44036
|
+
for (const track of stream.getAudioTracks()) connection.addTrack(track, stream);
|
|
44037
|
+
connection.onicecandidate = (event) => {
|
|
44038
|
+
this.send({ type: "signal", payload: { connectionId, candidate: event.candidate ? event.candidate.toJSON() : null } });
|
|
44039
|
+
};
|
|
44040
|
+
connection.ontrack = (event) => {
|
|
44041
|
+
const remote = event.streams[0] ?? new MediaStream([event.track]);
|
|
44042
|
+
if (this.audio) {
|
|
44043
|
+
this.audio.srcObject = remote;
|
|
44044
|
+
this.audio.muted = this.state.outputMuted;
|
|
44045
|
+
this.audio.volume = this.state.outputVolume / 100;
|
|
44046
|
+
this.audio.play().then(() => this.setState({ needsUnlock: false })).catch(() => {
|
|
44047
|
+
this.setState({ needsUnlock: true });
|
|
44048
|
+
this.armUnlock();
|
|
44049
|
+
});
|
|
44050
|
+
}
|
|
44051
|
+
this.log("áudio do motor chegou");
|
|
44052
|
+
};
|
|
44053
|
+
connection.onconnectionstatechange = () => {
|
|
44054
|
+
if (this.pc !== connection) return;
|
|
44055
|
+
this.setState({ pcState: connection.connectionState });
|
|
44056
|
+
this.log(`WebRTC: ${connection.connectionState}`);
|
|
44057
|
+
if (connection.connectionState === "failed" && this.state.engaged) {
|
|
44058
|
+
this.log("conexão de áudio com o motor falhou; renegociando");
|
|
44059
|
+
void this.startAudio();
|
|
44060
|
+
}
|
|
44061
|
+
};
|
|
44062
|
+
const offer = await connection.createOffer();
|
|
44063
|
+
if (this.pc !== connection) return;
|
|
44064
|
+
await connection.setLocalDescription(offer);
|
|
44065
|
+
this.send({ type: "signal", payload: { connectionId, description: { type: offer.type, sdp: offer.sdp } } });
|
|
44066
|
+
this.setState({ pcState: connection.connectionState });
|
|
44067
|
+
}
|
|
44068
|
+
stopAudio() {
|
|
44069
|
+
if (this.pc) {
|
|
44070
|
+
this.pc.onconnectionstatechange = null;
|
|
44071
|
+
this.pc.onicecandidate = null;
|
|
44072
|
+
this.pc.ontrack = null;
|
|
44073
|
+
this.pc.close();
|
|
44074
|
+
this.pc = null;
|
|
44075
|
+
}
|
|
44076
|
+
this.connectionId = null;
|
|
44077
|
+
if (this.micStream) {
|
|
44078
|
+
for (const track of this.micStream.getTracks()) track.stop();
|
|
44079
|
+
this.micStream = null;
|
|
44080
|
+
}
|
|
44081
|
+
if (this.audio) this.audio.srcObject = null;
|
|
44082
|
+
if (this.state.pcState !== "idle") this.setState({ pcState: "idle" });
|
|
44083
|
+
}
|
|
44084
|
+
async onSignal(payload) {
|
|
44085
|
+
const connection = this.pc;
|
|
44086
|
+
if (!connection) return;
|
|
44087
|
+
if (payload.connectionId && payload.connectionId !== this.connectionId) return;
|
|
44088
|
+
try {
|
|
44089
|
+
if (payload.description) {
|
|
44090
|
+
await connection.setRemoteDescription(payload.description);
|
|
44091
|
+
this.remoteSet = true;
|
|
44092
|
+
for (const candidate of this.pendingCandidates.splice(0)) await connection.addIceCandidate(candidate).catch(() => void 0);
|
|
44093
|
+
}
|
|
44094
|
+
if (payload.candidate !== void 0) {
|
|
44095
|
+
const candidate = payload.candidate ?? void 0;
|
|
44096
|
+
if (!this.remoteSet && candidate) this.pendingCandidates.push(candidate);
|
|
44097
|
+
else await connection.addIceCandidate(candidate).catch(() => void 0);
|
|
44098
|
+
}
|
|
44099
|
+
} catch (error2) {
|
|
44100
|
+
this.log(`sinalização: ${error2.message}`);
|
|
44101
|
+
}
|
|
44102
|
+
}
|
|
44103
|
+
/** o próximo toque/tecla na página libera o som (política de autoplay) */
|
|
44104
|
+
armUnlock() {
|
|
44105
|
+
if (this.unlockListener || typeof document === "undefined") return;
|
|
44106
|
+
const listener = () => {
|
|
44107
|
+
document.removeEventListener("pointerdown", listener, true);
|
|
44108
|
+
document.removeEventListener("keydown", listener, true);
|
|
44109
|
+
this.unlockListener = null;
|
|
44110
|
+
void this.unlockAudio();
|
|
44111
|
+
};
|
|
44112
|
+
this.unlockListener = listener;
|
|
44113
|
+
document.addEventListener("pointerdown", listener, true);
|
|
44114
|
+
document.addEventListener("keydown", listener, true);
|
|
44115
|
+
}
|
|
44116
|
+
// ------------------------------------------------------------------ transcrição
|
|
44117
|
+
pushSegment(segment) {
|
|
44118
|
+
const current = this.state.transcript;
|
|
44119
|
+
let segments = current.segments;
|
|
44120
|
+
let insight = current.insight;
|
|
44121
|
+
let interims = current.interims;
|
|
44122
|
+
if (segments.length && segments[0].callId !== segment.callId) {
|
|
44123
|
+
segments = [];
|
|
44124
|
+
insight = null;
|
|
44125
|
+
interims = 0;
|
|
44126
|
+
}
|
|
44127
|
+
const index = segments.findIndex((s) => s.id === segment.id);
|
|
44128
|
+
const next = index >= 0 ? segments.map((s, i2) => i2 === index ? segment : s) : [...segments, segment];
|
|
44129
|
+
if (next.length > MAX_SEGMENTS) next.shift();
|
|
44130
|
+
if (segment.interim) interims += 1;
|
|
44131
|
+
this.setState({ transcript: { ...current, segments: next, insight, interims } });
|
|
44132
|
+
}
|
|
44133
|
+
onTranscriptStatus(status) {
|
|
44134
|
+
const current = this.state.transcript;
|
|
44135
|
+
if (!current.status || current.status.recordingId === status.recordingId || status.live === "running") {
|
|
44136
|
+
const fresh = current.status && current.status.recordingId !== status.recordingId;
|
|
44137
|
+
this.setState({ transcript: { ...current, status, ...fresh ? { segments: [], insight: null, interims: 0 } : {} } });
|
|
44138
|
+
}
|
|
44139
|
+
}
|
|
44140
|
+
}
|
|
44141
|
+
function createAudioElement() {
|
|
44142
|
+
if (typeof document === "undefined") return null;
|
|
44143
|
+
const audio = document.createElement("audio");
|
|
44144
|
+
audio.autoplay = true;
|
|
44145
|
+
audio.setAttribute("playsinline", "true");
|
|
44146
|
+
audio.style.display = "none";
|
|
44147
|
+
audio.dataset.callixEngineAudio = "true";
|
|
44148
|
+
document.body.appendChild(audio);
|
|
44149
|
+
return audio;
|
|
44150
|
+
}
|
|
44151
|
+
const EngineClientContext = createContext(null);
|
|
44152
|
+
function EngineClientProvider({ client: client2, children: children2 }) {
|
|
44153
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(EngineClientContext.Provider, { value: client2, children: children2 });
|
|
44154
|
+
}
|
|
44155
|
+
function useEngineClient() {
|
|
44156
|
+
return useContext(EngineClientContext);
|
|
44157
|
+
}
|
|
44158
|
+
function useEngineState(client2) {
|
|
44159
|
+
return useSyncExternalStore(client2.subscribe.bind(client2), client2.getState.bind(client2), client2.getState.bind(client2));
|
|
44160
|
+
}
|
|
44161
|
+
const ENGINE_BREAKS = [{ id: 0, name: "Pausa" }];
|
|
44162
|
+
function toCallQualification(q) {
|
|
44163
|
+
return { id: q.id, name: q.name, isSystemQualification: false, systemQualificationType: 0 };
|
|
44164
|
+
}
|
|
44165
|
+
function qualificationsFromView(view, kind) {
|
|
44166
|
+
var _a2;
|
|
44167
|
+
return (((_a2 = view == null ? void 0 : view.snapshot) == null ? void 0 : _a2.leg.qualifications) ?? []).filter((q) => q.kind === kind).map(toCallQualification);
|
|
44168
|
+
}
|
|
44169
|
+
class EngineCall {
|
|
44170
|
+
constructor(client2, snapshot) {
|
|
44171
|
+
this.client = client2;
|
|
44172
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
44173
|
+
this.snapshot = snapshot;
|
|
44174
|
+
}
|
|
44175
|
+
get id() {
|
|
44176
|
+
return this.snapshot.id;
|
|
44177
|
+
}
|
|
44178
|
+
get direction() {
|
|
44179
|
+
return this.snapshot.direction;
|
|
44180
|
+
}
|
|
44181
|
+
get data() {
|
|
44182
|
+
var _a2;
|
|
44183
|
+
return {
|
|
44184
|
+
destinationNumber: (_a2 = this.snapshot.contact) == null ? void 0 : _a2.phone,
|
|
44185
|
+
campaignContactPhoneId: this.snapshot.campaignContactPhoneId,
|
|
44186
|
+
answeredAt: this.snapshot.answeredAt ? new Date(this.snapshot.answeredAt) : void 0
|
|
44187
|
+
};
|
|
44188
|
+
}
|
|
44189
|
+
get callerNumber() {
|
|
44190
|
+
var _a2;
|
|
44191
|
+
return (_a2 = this.snapshot.contact) == null ? void 0 : _a2.phone;
|
|
44192
|
+
}
|
|
44193
|
+
get peerDisplayName() {
|
|
44194
|
+
var _a2;
|
|
44195
|
+
return (_a2 = this.snapshot.contact) == null ? void 0 : _a2.name;
|
|
44196
|
+
}
|
|
44197
|
+
get isEstablished() {
|
|
44198
|
+
return this.snapshot.state === "active";
|
|
44199
|
+
}
|
|
44200
|
+
get isEnded() {
|
|
44201
|
+
return this.snapshot.state === "ended";
|
|
44202
|
+
}
|
|
44203
|
+
/** segundos desde o atendimento (ou desde o início, enquanto toca) */
|
|
44204
|
+
get currentDurationSeconds() {
|
|
44205
|
+
const from = this.snapshot.answeredAt ?? this.snapshot.startedAt;
|
|
44206
|
+
return Math.max(0, Math.floor((Date.now() - from) / 1e3));
|
|
44207
|
+
}
|
|
44208
|
+
answer() {
|
|
44209
|
+
this.client.command({ name: "answer" });
|
|
44210
|
+
}
|
|
44211
|
+
reject() {
|
|
44212
|
+
this.client.command({ name: "reject" });
|
|
44213
|
+
}
|
|
44214
|
+
hangup() {
|
|
44215
|
+
this.client.command({ name: "hangup" });
|
|
44216
|
+
}
|
|
44217
|
+
isOnHold() {
|
|
44218
|
+
return { local: false, remote: false };
|
|
44219
|
+
}
|
|
44220
|
+
async hold() {
|
|
44221
|
+
}
|
|
44222
|
+
async unhold() {
|
|
44223
|
+
}
|
|
44224
|
+
isMuted() {
|
|
44225
|
+
return this.client.getState().inputMuted;
|
|
44226
|
+
}
|
|
44227
|
+
mute() {
|
|
44228
|
+
this.client.setInputMuted(true);
|
|
44229
|
+
}
|
|
44230
|
+
unmute() {
|
|
44231
|
+
this.client.setInputMuted(false);
|
|
44232
|
+
}
|
|
44233
|
+
sendDtmf(tones) {
|
|
44234
|
+
this.client.command({ name: "dtmf", tones });
|
|
44235
|
+
}
|
|
44236
|
+
on(event, listener) {
|
|
44237
|
+
let set = this.listeners.get(event);
|
|
44238
|
+
if (!set) {
|
|
44239
|
+
set = /* @__PURE__ */ new Set();
|
|
44240
|
+
this.listeners.set(event, set);
|
|
44241
|
+
}
|
|
44242
|
+
set.add(listener);
|
|
44243
|
+
return this;
|
|
44244
|
+
}
|
|
44245
|
+
once(event, listener) {
|
|
44246
|
+
const wrapped = (...args) => {
|
|
44247
|
+
this.off(event, wrapped);
|
|
44248
|
+
listener(...args);
|
|
44249
|
+
};
|
|
44250
|
+
return this.on(event, wrapped);
|
|
44251
|
+
}
|
|
44252
|
+
off(event, listener) {
|
|
44253
|
+
var _a2;
|
|
44254
|
+
(_a2 = this.listeners.get(event)) == null ? void 0 : _a2.delete(listener);
|
|
44255
|
+
return this;
|
|
44256
|
+
}
|
|
44257
|
+
toCallData() {
|
|
44258
|
+
var _a2;
|
|
44259
|
+
return {
|
|
44260
|
+
id: this.snapshot.id,
|
|
44261
|
+
direction: this.snapshot.direction,
|
|
44262
|
+
destinationNumber: ((_a2 = this.snapshot.contact) == null ? void 0 : _a2.phone) ?? "",
|
|
44263
|
+
answeredAt: this.snapshot.answeredAt ? new Date(this.snapshot.answeredAt).toISOString() : void 0,
|
|
44264
|
+
campaignContactPhoneId: this.snapshot.campaignContactPhoneId,
|
|
44265
|
+
isMuted: this.snapshot.muted
|
|
44266
|
+
};
|
|
44267
|
+
}
|
|
44268
|
+
}
|
|
44269
|
+
function operatorStateFromView(view, call) {
|
|
44270
|
+
var _a2, _b, _c;
|
|
44271
|
+
if (!view) return void 0;
|
|
44272
|
+
if (((_a2 = view.sessionLost) == null ? void 0 : _a2.originator) === "anotherDevice") return { state: "startupError", error: "userIsAlreadyConnectedOnAnotherDevice" };
|
|
44273
|
+
const leg = (_b = view.snapshot) == null ? void 0 : _b.leg;
|
|
44274
|
+
if (!leg) {
|
|
44275
|
+
if (view.engine === "starting") return { state: "starting" };
|
|
44276
|
+
if (view.engine === "running") return { state: "starting" };
|
|
44277
|
+
return { state: "offline" };
|
|
44278
|
+
}
|
|
44279
|
+
const asCall = call ?? void 0;
|
|
44280
|
+
switch (leg.status) {
|
|
44281
|
+
case "starting":
|
|
44282
|
+
return { state: "starting" };
|
|
44283
|
+
case "available":
|
|
44284
|
+
return { state: "idle" };
|
|
44285
|
+
case "ringing":
|
|
44286
|
+
return { state: "callRinging", call: asCall };
|
|
44287
|
+
case "dialing":
|
|
44288
|
+
return ((_c = leg.call) == null ? void 0 : _c.state) === "ringing" ? { state: "manualCallRinging", call: asCall } : { state: "manualCallSetup", call: asCall };
|
|
44289
|
+
case "inCall":
|
|
44290
|
+
return { state: "callInProgress", call: asCall };
|
|
44291
|
+
case "afterCall":
|
|
44292
|
+
return { state: "afterCall" };
|
|
44293
|
+
case "onBreak":
|
|
44294
|
+
return { state: "onBreak", break: ENGINE_BREAKS[0] };
|
|
44295
|
+
case "offline":
|
|
44296
|
+
return { state: "offline" };
|
|
44297
|
+
case "error":
|
|
44298
|
+
return { state: "startupError", error: "unknownError" };
|
|
44299
|
+
}
|
|
44300
|
+
}
|
|
44301
|
+
function callInfoFromView(view) {
|
|
44302
|
+
var _a2;
|
|
44303
|
+
const leg = (_a2 = view == null ? void 0 : view.snapshot) == null ? void 0 : _a2.leg;
|
|
44304
|
+
const call = leg == null ? void 0 : leg.call;
|
|
44305
|
+
if (!leg || !call) return void 0;
|
|
44306
|
+
const successQualifications = qualificationsFromView(view, "success");
|
|
44307
|
+
const discardQualifications = qualificationsFromView(view, "discard");
|
|
44308
|
+
const contact = call.contact;
|
|
44309
|
+
if (contact == null ? void 0 : contact.campaign) {
|
|
44310
|
+
const info = {
|
|
44311
|
+
campaign: {
|
|
44312
|
+
id: 0,
|
|
44313
|
+
name: contact.campaign,
|
|
44314
|
+
type: 0,
|
|
44315
|
+
requireCallComments: false,
|
|
44316
|
+
enableTransfer: false,
|
|
44317
|
+
enableScheduledCall: false,
|
|
44318
|
+
successQualifications,
|
|
44319
|
+
discardQualifications,
|
|
44320
|
+
previewDuration: "",
|
|
44321
|
+
showCampaignModelOnService: false,
|
|
44322
|
+
skipCallQualification: false
|
|
44323
|
+
},
|
|
44324
|
+
campaignList: { id: 0, name: "" },
|
|
44325
|
+
campaignContact: { id: call.campaignContactPhoneId ?? 0, label: contact.name, phoneNumber: contact.phone }
|
|
44326
|
+
};
|
|
44327
|
+
return { type: "campaign", info };
|
|
44328
|
+
}
|
|
44329
|
+
return { type: "manual", info: { successQualifications, discardQualifications } };
|
|
44330
|
+
}
|
|
44331
|
+
function useEngineCall(client2) {
|
|
44332
|
+
var _a2, _b;
|
|
44333
|
+
const state = useEngineState(client2);
|
|
44334
|
+
const snapshot = ((_b = (_a2 = state.view) == null ? void 0 : _a2.snapshot) == null ? void 0 : _b.leg.call) ?? null;
|
|
44335
|
+
const ref = useRef(null);
|
|
44336
|
+
if (!snapshot || snapshot.state === "ended") {
|
|
44337
|
+
ref.current = null;
|
|
44338
|
+
} else if (!ref.current || ref.current.id !== snapshot.id) {
|
|
44339
|
+
ref.current = new EngineCall(client2, snapshot);
|
|
44340
|
+
} else {
|
|
44341
|
+
ref.current.snapshot = snapshot;
|
|
44342
|
+
}
|
|
44343
|
+
return ref.current;
|
|
44344
|
+
}
|
|
44345
|
+
function useEngineOperatorState(client2) {
|
|
44346
|
+
const state = useEngineState(client2);
|
|
44347
|
+
const call = useEngineCall(client2);
|
|
44348
|
+
return useMemo(() => operatorStateFromView(state.view, call), [state.view, call]);
|
|
44349
|
+
}
|
|
44350
|
+
function useEngineCurrentCall(client2) {
|
|
44351
|
+
const call = useEngineCall(client2);
|
|
44352
|
+
return call ?? void 0;
|
|
44353
|
+
}
|
|
44354
|
+
function useEngineCurrentCallInfo(client2) {
|
|
44355
|
+
var _a2, _b, _c, _d, _e;
|
|
44356
|
+
const state = useEngineState(client2);
|
|
44357
|
+
const view = state.view;
|
|
44358
|
+
const key = JSON.stringify([(_b = (_a2 = view == null ? void 0 : view.snapshot) == null ? void 0 : _a2.leg.call) == null ? void 0 : _b.id, (_d = (_c = view == null ? void 0 : view.snapshot) == null ? void 0 : _c.leg.call) == null ? void 0 : _d.contact, (_e = view == null ? void 0 : view.snapshot) == null ? void 0 : _e.leg.qualifications]);
|
|
44359
|
+
return useMemo(() => callInfoFromView(view), [key]);
|
|
44360
|
+
}
|
|
44361
|
+
function useEngineQualifications(client2, kind) {
|
|
44362
|
+
var _a2, _b;
|
|
44363
|
+
const state = useEngineState(client2);
|
|
44364
|
+
const raw = ((_b = (_a2 = state.view) == null ? void 0 : _a2.snapshot) == null ? void 0 : _b.leg.qualifications) ?? [];
|
|
44365
|
+
const key = JSON.stringify(raw);
|
|
44366
|
+
return useMemo(() => qualificationsFromView(state.view, kind), [key, kind]);
|
|
44367
|
+
}
|
|
44368
|
+
function useEngineControls(client2) {
|
|
44369
|
+
return useMemo(
|
|
44370
|
+
() => ({
|
|
44371
|
+
becomeAvailable: () => {
|
|
44372
|
+
var _a2, _b;
|
|
44373
|
+
const status = (_b = (_a2 = client2.getState().view) == null ? void 0 : _a2.snapshot) == null ? void 0 : _b.leg.status;
|
|
44374
|
+
if (status === "onBreak") client2.command({ name: "resume" });
|
|
44375
|
+
},
|
|
44376
|
+
goOffline: () => void 0,
|
|
44377
|
+
enterOnBreak: () => client2.command({ name: "pause" }),
|
|
44378
|
+
makeManualCall: (phoneNumber) => client2.command({ name: "dial", phone: phoneNumber }),
|
|
44379
|
+
finishAfterCall: (data) => {
|
|
44380
|
+
if (typeof data.qualificationId === "number") client2.command({ name: "qualify", qualificationId: data.qualificationId, result: data.result === "failure" ? "discard" : "success" });
|
|
44381
|
+
else client2.command({ name: "finish" });
|
|
44382
|
+
}
|
|
44383
|
+
}),
|
|
44384
|
+
[client2]
|
|
44385
|
+
);
|
|
44386
|
+
}
|
|
44387
|
+
function useEngineCurrentCallControls(client2) {
|
|
44388
|
+
return useMemo(
|
|
44389
|
+
() => ({
|
|
44390
|
+
answer: () => client2.command({ name: "answer" }),
|
|
44391
|
+
reject: () => client2.command({ name: "reject" }),
|
|
44392
|
+
hangup: () => client2.command({ name: "hangup" }),
|
|
44393
|
+
isOnHold: () => ({ local: false, remote: false }),
|
|
44394
|
+
hold: async () => void 0,
|
|
44395
|
+
unhold: async () => void 0,
|
|
44396
|
+
isMuted: () => client2.getState().inputMuted,
|
|
44397
|
+
mute: () => client2.setInputMuted(true),
|
|
44398
|
+
unmute: () => client2.setInputMuted(false)
|
|
44399
|
+
}),
|
|
44400
|
+
[client2]
|
|
44401
|
+
);
|
|
44402
|
+
}
|
|
44403
|
+
function useEngineAudioInputMuted(client2) {
|
|
44404
|
+
const state = useEngineState(client2);
|
|
44405
|
+
const set = useCallback((muted) => client2.setInputMuted(muted), [client2]);
|
|
44406
|
+
return [state.inputMuted, set];
|
|
44407
|
+
}
|
|
44408
|
+
function useEngineAudioOutputMuted(client2) {
|
|
44409
|
+
const state = useEngineState(client2);
|
|
44410
|
+
const set = useCallback((muted) => client2.setOutputMuted(muted), [client2]);
|
|
44411
|
+
return [state.outputMuted, set];
|
|
44412
|
+
}
|
|
44413
|
+
function useEngineAudioOutputVolume(client2) {
|
|
44414
|
+
const state = useEngineState(client2);
|
|
44415
|
+
const set = useCallback((volume) => client2.setOutputVolume(volume), [client2]);
|
|
44416
|
+
return [state.outputVolume, set];
|
|
44417
|
+
}
|
|
44418
|
+
function useEngineEventListener(client2, event, listener) {
|
|
44419
|
+
const ref = useRef(listener);
|
|
44420
|
+
ref.current = listener;
|
|
44421
|
+
useEffect(() => {
|
|
44422
|
+
return client2.onSessionLost((lost) => {
|
|
44423
|
+
ref.current({ originator: lost.originator });
|
|
44424
|
+
});
|
|
44425
|
+
}, [client2, event]);
|
|
44426
|
+
}
|
|
44427
|
+
const operators = /* @__PURE__ */ new WeakMap();
|
|
44428
|
+
function engineOperator(client2) {
|
|
44429
|
+
let operator = operators.get(client2);
|
|
44430
|
+
if (!operator) {
|
|
44431
|
+
const breaks = new EngineSignal(ENGINE_BREAKS);
|
|
44432
|
+
operator = {
|
|
44433
|
+
breaks,
|
|
44434
|
+
user: client2.userSignal,
|
|
44435
|
+
audioOutput: {
|
|
44436
|
+
setAudioOutputDevice: (id) => void client2.setOutputDevice(id).catch(() => void 0),
|
|
44437
|
+
setVolume: (volume) => client2.setOutputVolume(volume),
|
|
44438
|
+
setMute: (muted) => client2.setOutputMuted(muted)
|
|
44439
|
+
},
|
|
44440
|
+
stop: () => void 0,
|
|
44441
|
+
goOffline: () => void 0,
|
|
44442
|
+
becomeAvailable: () => {
|
|
44443
|
+
var _a2, _b;
|
|
44444
|
+
if (((_b = (_a2 = client2.getState().view) == null ? void 0 : _a2.snapshot) == null ? void 0 : _b.leg.status) === "onBreak") client2.command({ name: "resume" });
|
|
44445
|
+
},
|
|
44446
|
+
enterOnBreak: () => client2.command({ name: "pause" }),
|
|
44447
|
+
makeManualCall: (phoneNumber) => client2.command({ name: "dial", phone: phoneNumber }),
|
|
44448
|
+
finishAfterCall: (data) => {
|
|
44449
|
+
if (typeof data.qualificationId === "number") client2.command({ name: "qualify", qualificationId: data.qualificationId, result: data.result === "failure" ? "discard" : "success" });
|
|
44450
|
+
else client2.command({ name: "finish" });
|
|
44451
|
+
}
|
|
44452
|
+
};
|
|
44453
|
+
operators.set(client2, operator);
|
|
44454
|
+
}
|
|
44455
|
+
return operator;
|
|
44456
|
+
}
|
|
44457
|
+
function useEngineOperator(client2) {
|
|
44458
|
+
return useMemo(() => engineOperator(client2), [client2]);
|
|
44459
|
+
}
|
|
44460
|
+
function useEngineTranscript(client2) {
|
|
44461
|
+
return useEngineState(client2).transcript;
|
|
44462
|
+
}
|
|
44463
|
+
function useCallOperator() {
|
|
44464
|
+
const engine = useContext(EngineClientContext);
|
|
44465
|
+
return engine ? useEngineOperator(engine) : clientSdkReactExports.useCallOperator();
|
|
44466
|
+
}
|
|
44467
|
+
function useCallOperatorState() {
|
|
44468
|
+
const engine = useContext(EngineClientContext);
|
|
44469
|
+
return engine ? useEngineOperatorState(engine) : clientSdkReactExports.useCallOperatorState();
|
|
44470
|
+
}
|
|
44471
|
+
function useCallOperatorControls() {
|
|
44472
|
+
const engine = useContext(EngineClientContext);
|
|
44473
|
+
return engine ? useEngineControls(engine) : clientSdkReactExports.useCallOperatorControls();
|
|
44474
|
+
}
|
|
44475
|
+
function useCallOperatorCurrentCall() {
|
|
44476
|
+
const engine = useContext(EngineClientContext);
|
|
44477
|
+
return engine ? useEngineCurrentCall(engine) : clientSdkReactExports.useCallOperatorCurrentCall();
|
|
44478
|
+
}
|
|
44479
|
+
function useCallOperatorCurrentCallInfo() {
|
|
44480
|
+
const engine = useContext(EngineClientContext);
|
|
44481
|
+
return engine ? useEngineCurrentCallInfo(engine) : clientSdkReactExports.useCallOperatorCurrentCallInfo();
|
|
44482
|
+
}
|
|
44483
|
+
function useCallOperatorSuccessQualifications() {
|
|
44484
|
+
const engine = useContext(EngineClientContext);
|
|
44485
|
+
return engine ? useEngineQualifications(engine, "success") : clientSdkReactExports.useCallOperatorSuccessQualifications();
|
|
44486
|
+
}
|
|
44487
|
+
function useCallOperatorDiscardQualifications() {
|
|
44488
|
+
const engine = useContext(EngineClientContext);
|
|
44489
|
+
return engine ? useEngineQualifications(engine, "discard") : clientSdkReactExports.useCallOperatorDiscardQualifications();
|
|
44490
|
+
}
|
|
44491
|
+
function useCallOperatorCurrentCallControls() {
|
|
44492
|
+
const engine = useContext(EngineClientContext);
|
|
44493
|
+
return engine ? useEngineCurrentCallControls(engine) : clientSdkReactExports.useCallOperatorCurrentCallControls();
|
|
44494
|
+
}
|
|
44495
|
+
function useCallOperatorAudioInputMuted() {
|
|
44496
|
+
const engine = useContext(EngineClientContext);
|
|
44497
|
+
return engine ? useEngineAudioInputMuted(engine) : clientSdkReactExports.useCallOperatorAudioInputMuted();
|
|
44498
|
+
}
|
|
44499
|
+
function useCallOperatorAudioOutputMuted() {
|
|
44500
|
+
const engine = useContext(EngineClientContext);
|
|
44501
|
+
return engine ? useEngineAudioOutputMuted(engine) : clientSdkReactExports.useCallOperatorAudioOutputMuted();
|
|
44502
|
+
}
|
|
44503
|
+
function useCallOperatorAudioOutputVolume() {
|
|
44504
|
+
const engine = useContext(EngineClientContext);
|
|
44505
|
+
return engine ? useEngineAudioOutputVolume(engine) : clientSdkReactExports.useCallOperatorAudioOutputVolume();
|
|
44506
|
+
}
|
|
44507
|
+
function useCallOperatorEventListener(event, listener) {
|
|
44508
|
+
const engine = useContext(EngineClientContext);
|
|
44509
|
+
if (engine) useEngineEventListener(engine, event, listener);
|
|
44510
|
+
else clientSdkReactExports.useCallOperatorEventListener(event, listener);
|
|
44511
|
+
}
|
|
43728
44512
|
var DefaultContext = {
|
|
43729
44513
|
color: void 0,
|
|
43730
44514
|
size: void 0,
|
|
@@ -48745,8 +49529,8 @@ function formatPhoneValue(raw) {
|
|
|
48745
49529
|
}
|
|
48746
49530
|
function CallInfoDisplay() {
|
|
48747
49531
|
var _a2;
|
|
48748
|
-
const callInfo =
|
|
48749
|
-
const currentCall =
|
|
49532
|
+
const callInfo = useCallOperatorCurrentCallInfo();
|
|
49533
|
+
const currentCall = useCallOperatorCurrentCall();
|
|
48750
49534
|
const campaignContactPhoneId = ((_a2 = currentCall == null ? void 0 : currentCall.data) == null ? void 0 : _a2.campaignContactPhoneId) ?? null;
|
|
48751
49535
|
if ((callInfo == null ? void 0 : callInfo.type) === "campaign") {
|
|
48752
49536
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(CampaignCallInfoDisplay, { campaignCallInfo: callInfo.info, campaignContactPhoneId });
|
|
@@ -48892,7 +49676,7 @@ function useSignal(signal) {
|
|
|
48892
49676
|
return value;
|
|
48893
49677
|
}
|
|
48894
49678
|
function useOperatorBreak() {
|
|
48895
|
-
const { enterOnBreak, becomeAvailable } =
|
|
49679
|
+
const { enterOnBreak, becomeAvailable } = useCallOperatorControls();
|
|
48896
49680
|
return {
|
|
48897
49681
|
// Use the SDK's native method
|
|
48898
49682
|
goOnBreak: enterOnBreak,
|
|
@@ -51592,6 +52376,8 @@ function FloatingDialer({
|
|
|
51592
52376
|
sessionStatus = null,
|
|
51593
52377
|
loginConfirmation = null,
|
|
51594
52378
|
onCancelLoginConfirmation,
|
|
52379
|
+
onTakeOverSession,
|
|
52380
|
+
onLogoutAllTabs,
|
|
51595
52381
|
variant = "default",
|
|
51596
52382
|
portalTarget = null
|
|
51597
52383
|
}) {
|
|
@@ -51944,7 +52730,9 @@ function FloatingDialer({
|
|
|
51944
52730
|
error: sessionError,
|
|
51945
52731
|
statusMessage: sessionStatus,
|
|
51946
52732
|
confirmation: loginConfirmation,
|
|
51947
|
-
onCancelConfirmation: onCancelLoginConfirmation
|
|
52733
|
+
onCancelConfirmation: onCancelLoginConfirmation,
|
|
52734
|
+
onTakeOver: onTakeOverSession,
|
|
52735
|
+
onLogoutAll: onLogoutAllTabs
|
|
51948
52736
|
}
|
|
51949
52737
|
);
|
|
51950
52738
|
if (isMiniMode) {
|
|
@@ -52080,7 +52868,7 @@ function FloatingDialer({
|
|
|
52080
52868
|
}
|
|
52081
52869
|
);
|
|
52082
52870
|
}
|
|
52083
|
-
function LoginForm({ defaultValue, onSubmit, isLoading, error: error2, statusMessage, confirmation, onCancelConfirmation }) {
|
|
52871
|
+
function LoginForm({ defaultValue, onSubmit, isLoading, error: error2, statusMessage, confirmation, onCancelConfirmation, onTakeOver, onLogoutAll }) {
|
|
52084
52872
|
const [value, setValue] = useState(defaultValue);
|
|
52085
52873
|
const [touched, setTouched] = useState(false);
|
|
52086
52874
|
useEffect(() => {
|
|
@@ -52111,8 +52899,41 @@ function LoginForm({ defaultValue, onSubmit, isLoading, error: error2, statusMes
|
|
|
52111
52899
|
}
|
|
52112
52900
|
);
|
|
52113
52901
|
}
|
|
52902
|
+
if (confirmation == null ? void 0 : confirmation.elsewhere) {
|
|
52903
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: tw`space-y-4`, "data-login-state": "elsewhere", children: [
|
|
52904
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: tw`space-y-2 text-center`, children: [
|
|
52905
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`text-[11px] font-semibold uppercase tracking-wide text-slate-500`, children: "Discador ativo em outra aba" }),
|
|
52906
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: tw`inline-flex items-center gap-2 rounded-full border border-slate-200 bg-slate-50 px-3 py-1 text-sm font-semibold text-slate-800`, children: [
|
|
52907
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(FiUser, { className: tw`text-primary` }),
|
|
52908
|
+
confirmation.username
|
|
52909
|
+
] })
|
|
52910
|
+
] }),
|
|
52911
|
+
error2 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`text-center text-xs text-red-500`, children: error2 }),
|
|
52912
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
52913
|
+
"button",
|
|
52914
|
+
{
|
|
52915
|
+
type: "button",
|
|
52916
|
+
id: "takeOverBtn",
|
|
52917
|
+
onClick: () => onTakeOver == null ? void 0 : onTakeOver(),
|
|
52918
|
+
className: tw`flex w-full items-center justify-center gap-2 rounded-xl bg-primary px-4 py-2 text-white shadow-lg transition hover:bg-primaryDark`,
|
|
52919
|
+
children: "Usar o discador nesta aba"
|
|
52920
|
+
}
|
|
52921
|
+
),
|
|
52922
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
52923
|
+
"button",
|
|
52924
|
+
{
|
|
52925
|
+
type: "button",
|
|
52926
|
+
id: "logoutAllBtn",
|
|
52927
|
+
onClick: () => onLogoutAll == null ? void 0 : onLogoutAll(),
|
|
52928
|
+
className: tw`w-full text-center text-xs font-semibold text-red-500 transition hover:text-red-600`,
|
|
52929
|
+
children: "Sair em todas as abas"
|
|
52930
|
+
}
|
|
52931
|
+
),
|
|
52932
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`text-center text-[11px] text-slate-400`, children: "O Callix aceita uma conexão por usuário: você já está conectado em outra aba deste navegador e pode continuar por lá." })
|
|
52933
|
+
] });
|
|
52934
|
+
}
|
|
52114
52935
|
if (confirmation) {
|
|
52115
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: tw`space-y-4`, children: [
|
|
52936
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: tw`space-y-4`, "data-login-state": "choose", children: [
|
|
52116
52937
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: tw`space-y-2 text-center`, children: [
|
|
52117
52938
|
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`text-[11px] font-semibold uppercase tracking-wide text-slate-500`, children: "Entrar como" }),
|
|
52118
52939
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: tw`inline-flex items-center gap-2 rounded-full border border-slate-200 bg-slate-50 px-3 py-1 text-sm font-semibold text-slate-800`, children: [
|
|
@@ -52121,10 +52942,12 @@ function LoginForm({ defaultValue, onSubmit, isLoading, error: error2, statusMes
|
|
|
52121
52942
|
] })
|
|
52122
52943
|
] }),
|
|
52123
52944
|
error2 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`text-center text-xs text-red-500`, children: error2 }),
|
|
52945
|
+
!error2 && confirmation.note && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`text-center text-xs text-slate-500`, children: confirmation.note }),
|
|
52124
52946
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
52125
52947
|
"button",
|
|
52126
52948
|
{
|
|
52127
52949
|
type: "button",
|
|
52950
|
+
id: "loginConfirmBtn",
|
|
52128
52951
|
onClick: () => void onSubmit(confirmation.username),
|
|
52129
52952
|
className: tw`flex w-full items-center justify-center gap-2 rounded-xl bg-primary px-4 py-2 text-white shadow-lg transition hover:bg-primaryDark`,
|
|
52130
52953
|
children: "Entrar"
|
|
@@ -52139,7 +52962,7 @@ function LoginForm({ defaultValue, onSubmit, isLoading, error: error2, statusMes
|
|
|
52139
52962
|
children: "Usar outro usuário"
|
|
52140
52963
|
}
|
|
52141
52964
|
),
|
|
52142
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`text-center text-[11px] text-slate-400`, children: "
|
|
52965
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: tw`text-center text-[11px] text-slate-400`, children: "Ao entrar, todas as abas abertas passam a usar o discador. Vai usar o Callix web? Então não entre aqui: nada é conectado sem a sua confirmação." })
|
|
52143
52966
|
] });
|
|
52144
52967
|
}
|
|
52145
52968
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("form", { className: tw`space-y-4`, onSubmit: handleSubmit, children: [
|
|
@@ -52189,19 +53012,19 @@ function AuthenticatedDialer({
|
|
|
52189
53012
|
const isMiniMode = variant === "min";
|
|
52190
53013
|
const { skipManualQualification } = useDialerConfig();
|
|
52191
53014
|
console.log("[FloatingDialer/AuthenticatedDialer] 🔧 skipManualQualification:", skipManualQualification);
|
|
52192
|
-
const operator =
|
|
52193
|
-
const operatorState =
|
|
52194
|
-
const currentCall =
|
|
52195
|
-
const currentCallInfo =
|
|
52196
|
-
const rawSuccessQualifications =
|
|
52197
|
-
const rawDiscardQualifications =
|
|
53015
|
+
const operator = useCallOperator();
|
|
53016
|
+
const operatorState = useCallOperatorState();
|
|
53017
|
+
const currentCall = useCallOperatorCurrentCall();
|
|
53018
|
+
const currentCallInfo = useCallOperatorCurrentCallInfo();
|
|
53019
|
+
const rawSuccessQualifications = useCallOperatorSuccessQualifications();
|
|
53020
|
+
const rawDiscardQualifications = useCallOperatorDiscardQualifications();
|
|
52198
53021
|
const successQualifications = useMemo(() => rawSuccessQualifications ?? [], [rawSuccessQualifications]);
|
|
52199
53022
|
const discardQualifications = useMemo(() => rawDiscardQualifications ?? [], [rawDiscardQualifications]);
|
|
52200
|
-
const { makeManualCall, becomeAvailable, finishAfterCall } =
|
|
52201
|
-
const { hangup, answer, reject } =
|
|
52202
|
-
const [inputMuted, setInputMuted] =
|
|
52203
|
-
const [outputMuted, setOutputMuted] =
|
|
52204
|
-
const [outputVolume, setOutputVolume] =
|
|
53023
|
+
const { makeManualCall, becomeAvailable, finishAfterCall } = useCallOperatorControls();
|
|
53024
|
+
const { hangup, answer, reject } = useCallOperatorCurrentCallControls();
|
|
53025
|
+
const [inputMuted, setInputMuted] = useCallOperatorAudioInputMuted();
|
|
53026
|
+
const [outputMuted, setOutputMuted] = useCallOperatorAudioOutputMuted();
|
|
53027
|
+
const [outputVolume, setOutputVolume] = useCallOperatorAudioOutputVolume();
|
|
52205
53028
|
useEffect(() => {
|
|
52206
53029
|
console.log("[FloatingDialer] Estado do operador mudou:", {
|
|
52207
53030
|
state: operatorState == null ? void 0 : operatorState.state,
|
|
@@ -52234,7 +53057,7 @@ function AuthenticatedDialer({
|
|
|
52234
53057
|
const visibleInputRef = useRef(null);
|
|
52235
53058
|
const [callError, setCallError] = useState(null);
|
|
52236
53059
|
const [availabilityError, setAvailabilityError] = useState(null);
|
|
52237
|
-
|
|
53060
|
+
useCallOperatorEventListener("userSessionDropped", (data) => {
|
|
52238
53061
|
const notice = describeSessionDrop(data == null ? void 0 : data.originator);
|
|
52239
53062
|
setAvailabilityError(`${notice.title}: ${notice.detail}${notice.action ? ` ${notice.action}` : ""}`);
|
|
52240
53063
|
});
|
|
@@ -56837,9 +57660,69 @@ function DialerNoticeBanner({ notice, onDismiss, onRecheck, onHeightChange }) {
|
|
|
56837
57660
|
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
57661
|
] });
|
|
56839
57662
|
}
|
|
57663
|
+
const EMOTION_LABEL = {
|
|
57664
|
+
raiva: "raiva",
|
|
57665
|
+
irritacao: "irritação",
|
|
57666
|
+
frustracao: "frustração",
|
|
57667
|
+
desconfianca: "desconfiança",
|
|
57668
|
+
impaciencia: "impaciência",
|
|
57669
|
+
confusao: "confusão",
|
|
57670
|
+
nervosismo: "nervosismo",
|
|
57671
|
+
surpresa: "surpresa",
|
|
57672
|
+
desanimo: "desânimo",
|
|
57673
|
+
cansaco: "cansaço",
|
|
57674
|
+
interesse: "interesse",
|
|
57675
|
+
entusiasmo: "entusiasmo",
|
|
57676
|
+
satisfacao: "satisfação",
|
|
57677
|
+
alivio: "alívio",
|
|
57678
|
+
neutro: "neutro"
|
|
57679
|
+
};
|
|
57680
|
+
const STANCE_LABEL = {
|
|
57681
|
+
aberto: "cliente aberto",
|
|
57682
|
+
neutro: "cliente neutro",
|
|
57683
|
+
resistente: "cliente resistente",
|
|
57684
|
+
hostil: "cliente hostil",
|
|
57685
|
+
indefinido: "lendo a conversa"
|
|
57686
|
+
};
|
|
57687
|
+
const SPEAKER_LABEL = { agent: "Você", customer: "Cliente", mixed: "" };
|
|
57688
|
+
function topEmotions(segment, max2 = 2) {
|
|
57689
|
+
return (segment.emotions ?? []).filter((e) => e.label !== "neutro" && e.intensity >= 0.3).slice(0, max2);
|
|
57690
|
+
}
|
|
57691
|
+
function LiveCaptionPanel({ maxLines = 4 }) {
|
|
57692
|
+
const client2 = useEngineClient();
|
|
57693
|
+
if (!client2) return null;
|
|
57694
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(LiveCaptionPanelInner, { client: client2, maxLines });
|
|
57695
|
+
}
|
|
57696
|
+
function LiveCaptionPanelInner({ client: client2, maxLines }) {
|
|
57697
|
+
const { segments, insight, status } = useEngineTranscript(client2);
|
|
57698
|
+
const listening = (status == null ? void 0 : status.live) === "running";
|
|
57699
|
+
if (segments.length === 0 && !insight && !listening) return null;
|
|
57700
|
+
const rows = segments.slice(-maxLines);
|
|
57701
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "live-captions", id: "liveCaptions", "aria-live": "polite", children: [
|
|
57702
|
+
insight && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `live-insight stance-${insight.customer.stance}`, id: "liveInsight", title: insight.summary, children: [
|
|
57703
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "live-insight-stance", children: STANCE_LABEL[insight.customer.stance] ?? insight.customer.stance }),
|
|
57704
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "live-insight-interest", children: [
|
|
57705
|
+
"interesse ",
|
|
57706
|
+
Math.round(insight.customer.interest),
|
|
57707
|
+
"%"
|
|
57708
|
+
] }),
|
|
57709
|
+
insight.nextMove && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "live-insight-next", children: insight.nextMove })
|
|
57710
|
+
] }),
|
|
57711
|
+
rows.length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("ul", { className: "live-caption-list", children: rows.map((segment) => /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { className: `live-caption ${segment.speaker}${segment.interim ? " interim" : ""}${segment.refined ? " refined" : ""}`, children: [
|
|
57712
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "live-caption-speaker", children: SPEAKER_LABEL[segment.speaker] }),
|
|
57713
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "live-caption-text", children: segment.text }),
|
|
57714
|
+
topEmotions(segment).map((emotion) => /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: `live-caption-emotion${emotion.source === "context" ? " ctx" : ""}`, title: emotion.evidence ?? "", children: [
|
|
57715
|
+
EMOTION_LABEL[emotion.label] ?? emotion.label,
|
|
57716
|
+
" ",
|
|
57717
|
+
Math.round(emotion.intensity * 100),
|
|
57718
|
+
"%"
|
|
57719
|
+
] }, emotion.label))
|
|
57720
|
+
] }, segment.id)) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "live-caption-waiting", children: "Ouvindo a conversa…" })
|
|
57721
|
+
] });
|
|
57722
|
+
}
|
|
56840
57723
|
function useDialerNotices(options = {}) {
|
|
56841
|
-
const operatorState =
|
|
56842
|
-
const currentCall =
|
|
57724
|
+
const operatorState = useCallOperatorState();
|
|
57725
|
+
const currentCall = useCallOperatorCurrentCall();
|
|
56843
57726
|
const [deviceIssue, setDeviceIssue] = useState(null);
|
|
56844
57727
|
const [sessionDrop, setSessionDrop] = useState(null);
|
|
56845
57728
|
const [callFailure, setCallFailure] = useState(null);
|
|
@@ -56869,7 +57752,7 @@ function useDialerNotices(options = {}) {
|
|
|
56869
57752
|
(_a3 = mediaDevices == null ? void 0 : mediaDevices.removeEventListener) == null ? void 0 : _a3.call(mediaDevices, "devicechange", handler);
|
|
56870
57753
|
};
|
|
56871
57754
|
}, [options.checkDevices]);
|
|
56872
|
-
|
|
57755
|
+
useCallOperatorEventListener("userSessionDropped", (data) => {
|
|
56873
57756
|
setSessionDrop((data == null ? void 0 : data.originator) ?? "unknown");
|
|
56874
57757
|
});
|
|
56875
57758
|
useEffect(() => {
|
|
@@ -57583,6 +58466,8 @@ function MobileDialerLogin({
|
|
|
57583
58466
|
sessionStatus,
|
|
57584
58467
|
loginConfirmation,
|
|
57585
58468
|
onCancelLoginConfirmation,
|
|
58469
|
+
onTakeOverSession,
|
|
58470
|
+
onLogoutAllTabs,
|
|
57586
58471
|
density
|
|
57587
58472
|
}) {
|
|
57588
58473
|
const [loginInput, setLoginInput] = useState(username ?? "");
|
|
@@ -57619,19 +58504,33 @@ function MobileDialerLogin({
|
|
|
57619
58504
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "login-spinner", "aria-hidden": "true" }),
|
|
57620
58505
|
loadingUser && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "login-loading-user", children: loadingUser }),
|
|
57621
58506
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "login-status", children: sessionStatus || "Conectando..." })
|
|
57622
|
-
] }) : showConfirmation && loginConfirmation ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "login-confirm", children: [
|
|
58507
|
+
] }) : showConfirmation && loginConfirmation ? loginConfirmation.elsewhere ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "login-confirm", "data-login-state": "elsewhere", children: [
|
|
58508
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "login-confirm-label", children: "Discador ativo em outra aba" }),
|
|
58509
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "login-user-chip", children: [
|
|
58510
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fas fa-user-check", "aria-hidden": "true" }),
|
|
58511
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: loginConfirmation.username })
|
|
58512
|
+
] }),
|
|
58513
|
+
sessionError && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "input-error", children: sessionError }),
|
|
58514
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("button", { type: "button", className: "login-confirm-btn login-confirm-btn-blue", id: "takeOverBtn", onClick: () => onTakeOverSession == null ? void 0 : onTakeOverSession(), children: [
|
|
58515
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fas fa-arrow-down", "aria-hidden": "true" }),
|
|
58516
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Usar o discador nesta aba" })
|
|
58517
|
+
] }),
|
|
58518
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "login-secondary-btn login-secondary-danger", id: "logoutAllBtn", onClick: () => onLogoutAllTabs == null ? void 0 : onLogoutAllTabs(), children: "Sair em todas as abas" }),
|
|
58519
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "login-hint", children: "O Callix aceita uma conexão por usuário: você já está conectado em outra aba deste navegador e pode continuar por lá." })
|
|
58520
|
+
] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "login-confirm", "data-login-state": "choose", children: [
|
|
57623
58521
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "login-confirm-label", children: "Entrar como" }),
|
|
57624
58522
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "login-user-chip", children: [
|
|
57625
58523
|
/* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fas fa-user", "aria-hidden": "true" }),
|
|
57626
58524
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: loginConfirmation.username })
|
|
57627
58525
|
] }),
|
|
57628
58526
|
sessionError && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "input-error", children: sessionError }),
|
|
57629
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
58527
|
+
!sessionError && loginConfirmation.note && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "login-note", children: loginConfirmation.note }),
|
|
58528
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("button", { type: "button", className: "login-confirm-btn", id: "loginConfirmBtn", onClick: () => void submitLogin(loginConfirmation.username), children: [
|
|
57630
58529
|
/* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fas fa-sign-in-alt", "aria-hidden": "true" }),
|
|
57631
58530
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Entrar" })
|
|
57632
58531
|
] }),
|
|
57633
58532
|
!loginConfirmation.locked && onCancelLoginConfirmation && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "login-secondary-btn", onClick: onCancelLoginConfirmation, children: "Usar outro usuário" }),
|
|
57634
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "login-hint", children: "
|
|
58533
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "login-hint", children: "Ao entrar, todas as abas abertas passam a usar o discador. Vai usar o Callix web? Então não entre aqui: nada é conectado sem a sua confirmação." })
|
|
57635
58534
|
] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
57636
58535
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "phone-number-display", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
57637
58536
|
"input",
|
|
@@ -57724,6 +58623,7 @@ Gostaríamos de fazer um *diagnóstico gratuito* para te apresentar os valores r
|
|
|
57724
58623
|
}
|
|
57725
58624
|
function AuthenticatedMobileDialer({
|
|
57726
58625
|
username,
|
|
58626
|
+
onLogout,
|
|
57727
58627
|
showClientInfoModal = false,
|
|
57728
58628
|
density
|
|
57729
58629
|
}) {
|
|
@@ -57762,21 +58662,21 @@ function AuthenticatedMobileDialer({
|
|
|
57762
58662
|
const [speakerDeviceId, setSpeakerDeviceId] = useState(null);
|
|
57763
58663
|
const [defaultDeviceId, setDefaultDeviceId] = useState("default");
|
|
57764
58664
|
const [speakerSupported, setSpeakerSupported] = useState(null);
|
|
57765
|
-
const operator =
|
|
57766
|
-
const operatorState =
|
|
57767
|
-
const currentCall =
|
|
57768
|
-
const { hangup, answer, reject } =
|
|
57769
|
-
const callInfo =
|
|
57770
|
-
const rawSuccessQualifications =
|
|
57771
|
-
const rawDiscardQualifications =
|
|
58665
|
+
const operator = useCallOperator();
|
|
58666
|
+
const operatorState = useCallOperatorState();
|
|
58667
|
+
const currentCall = useCallOperatorCurrentCall();
|
|
58668
|
+
const { hangup, answer, reject } = useCallOperatorCurrentCallControls();
|
|
58669
|
+
const callInfo = useCallOperatorCurrentCallInfo();
|
|
58670
|
+
const rawSuccessQualifications = useCallOperatorSuccessQualifications();
|
|
58671
|
+
const rawDiscardQualifications = useCallOperatorDiscardQualifications();
|
|
57772
58672
|
const successQualifications = useMemo(() => rawSuccessQualifications ?? [], [rawSuccessQualifications]);
|
|
57773
58673
|
const discardQualifications = useMemo(() => rawDiscardQualifications ?? [], [rawDiscardQualifications]);
|
|
57774
|
-
const { makeManualCall, finishAfterCall } =
|
|
58674
|
+
const { makeManualCall, finishAfterCall } = useCallOperatorControls();
|
|
57775
58675
|
const { goOnBreak, goOffBreak } = useOperatorBreak();
|
|
57776
58676
|
const breaks = useSignal(operator == null ? void 0 : operator.breaks) || [];
|
|
57777
58677
|
const userData = useSignal(operator == null ? void 0 : operator.user);
|
|
57778
58678
|
const isOnBreak = (operatorState == null ? void 0 : operatorState.state) === "onBreak";
|
|
57779
|
-
const [inputMuted, setInputMuted] =
|
|
58679
|
+
const [inputMuted, setInputMuted] = useCallOperatorAudioInputMuted();
|
|
57780
58680
|
const haptic = useHaptic();
|
|
57781
58681
|
const dtmf = useDTMF({ enabled: true });
|
|
57782
58682
|
const keySequenceRef = useRef("");
|
|
@@ -58490,13 +59390,30 @@ function AuthenticatedMobileDialer({
|
|
|
58490
59390
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "pause-timer", children: pauseDurationLabel })
|
|
58491
59391
|
] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "status-text", children: statusBadgeText })
|
|
58492
59392
|
] }) }),
|
|
58493
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
58494
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
58495
|
-
|
|
58496
|
-
|
|
58497
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
58498
|
-
|
|
58499
|
-
|
|
59393
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "status-actions", children: [
|
|
59394
|
+
isOnBreak ? /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: "resume-btn", id: "resumeBtn", onClick: handleGoOffBreak, children: [
|
|
59395
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fas fa-play" }),
|
|
59396
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Voltar" })
|
|
59397
|
+
] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: "pause-btn", id: "pauseBtn", onClick: () => setIsPauseModalOpen(true), children: [
|
|
59398
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fas fa-pause" }),
|
|
59399
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Pausa" })
|
|
59400
|
+
] }),
|
|
59401
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
59402
|
+
"button",
|
|
59403
|
+
{
|
|
59404
|
+
type: "button",
|
|
59405
|
+
className: "logout-btn",
|
|
59406
|
+
id: "logoutBtn",
|
|
59407
|
+
title: "Sair do discador (todas as abas)",
|
|
59408
|
+
"aria-label": "Sair do discador",
|
|
59409
|
+
onClick: () => {
|
|
59410
|
+
if (typeof window !== "undefined" && !window.confirm("Sair do discador em todas as abas?")) return;
|
|
59411
|
+
onLogout();
|
|
59412
|
+
},
|
|
59413
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fas fa-right-from-bracket" })
|
|
59414
|
+
}
|
|
59415
|
+
)
|
|
59416
|
+
] })
|
|
58500
59417
|
] }),
|
|
58501
59418
|
/* @__PURE__ */ jsxRuntimeExports.jsx(DialerNoticeBanner, { notice: dialerNotice, onDismiss: dismissDialerNotice, onRecheck: () => void recheckDevices(), onHeightChange: setNoticeHeight }),
|
|
58502
59419
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "phone-number-display", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
@@ -58595,6 +59512,7 @@ function AuthenticatedMobileDialer({
|
|
|
58595
59512
|
] })
|
|
58596
59513
|
] }) })
|
|
58597
59514
|
] }),
|
|
59515
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(LiveCaptionPanel, {}),
|
|
58598
59516
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "call-controls", children: [
|
|
58599
59517
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "control-row", children: [
|
|
58600
59518
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
@@ -59049,14 +59967,14 @@ function AuthenticatedMiniDialer({
|
|
|
59049
59967
|
}) {
|
|
59050
59968
|
var _a2;
|
|
59051
59969
|
const { skipManualQualification } = useDialerConfig();
|
|
59052
|
-
const operatorState =
|
|
59053
|
-
const currentCallInfo =
|
|
59054
|
-
const currentCall =
|
|
59055
|
-
const rawSuccessQualifications =
|
|
59056
|
-
const rawDiscardQualifications =
|
|
59970
|
+
const operatorState = useCallOperatorState();
|
|
59971
|
+
const currentCallInfo = useCallOperatorCurrentCallInfo();
|
|
59972
|
+
const currentCall = useCallOperatorCurrentCall();
|
|
59973
|
+
const rawSuccessQualifications = useCallOperatorSuccessQualifications();
|
|
59974
|
+
const rawDiscardQualifications = useCallOperatorDiscardQualifications();
|
|
59057
59975
|
const successQualifications = useMemo(() => rawSuccessQualifications ?? [], [rawSuccessQualifications]);
|
|
59058
59976
|
const discardQualifications = useMemo(() => rawDiscardQualifications ?? [], [rawDiscardQualifications]);
|
|
59059
|
-
const { finishAfterCall } =
|
|
59977
|
+
const { finishAfterCall } = useCallOperatorControls();
|
|
59060
59978
|
const [isClientInfoModalOpen, setIsClientInfoModalOpen] = useState(false);
|
|
59061
59979
|
const [isQualificationModalOpen, setIsQualificationModalOpen] = useState(false);
|
|
59062
59980
|
const [callDurationSeconds, setCallDurationSeconds] = useState(0);
|
|
@@ -59331,6 +60249,206 @@ function AuthenticatedMiniDialer({
|
|
|
59331
60249
|
)
|
|
59332
60250
|
] });
|
|
59333
60251
|
}
|
|
60252
|
+
const PROBE_TIMEOUT_MS = 600;
|
|
60253
|
+
const TAKEOVER_GRACE_MS = 3e3;
|
|
60254
|
+
const TAKEOVER_JITTER_MS = 600;
|
|
60255
|
+
const LEADER_HEARTBEAT_MS = 3e3;
|
|
60256
|
+
const LEADER_STALE_MS = 9e3;
|
|
60257
|
+
function channelName(username) {
|
|
60258
|
+
return `callix-dialer:${username.trim().toLowerCase()}`;
|
|
60259
|
+
}
|
|
60260
|
+
function leaderKey(username) {
|
|
60261
|
+
return `callix_dialer_${username}_leader`;
|
|
60262
|
+
}
|
|
60263
|
+
const TAB_ROLE_KEY = "callix_dialer_tab_role";
|
|
60264
|
+
const BUS_FALLBACK_SUFFIX = "_bus";
|
|
60265
|
+
function newTabId() {
|
|
60266
|
+
const cryptoApi = typeof globalThis.crypto !== "undefined" ? globalThis.crypto : void 0;
|
|
60267
|
+
if (cryptoApi && "randomUUID" in cryptoApi) return cryptoApi.randomUUID().slice(0, 8);
|
|
60268
|
+
return Math.random().toString(36).slice(2, 10);
|
|
60269
|
+
}
|
|
60270
|
+
function defaultChannelFactory(name) {
|
|
60271
|
+
if (typeof BroadcastChannel === "undefined") return null;
|
|
60272
|
+
try {
|
|
60273
|
+
return new BroadcastChannel(name);
|
|
60274
|
+
} catch {
|
|
60275
|
+
return null;
|
|
60276
|
+
}
|
|
60277
|
+
}
|
|
60278
|
+
function defaultStorage() {
|
|
60279
|
+
try {
|
|
60280
|
+
return typeof localStorage !== "undefined" ? localStorage : null;
|
|
60281
|
+
} catch {
|
|
60282
|
+
return null;
|
|
60283
|
+
}
|
|
60284
|
+
}
|
|
60285
|
+
class DialerTabBus {
|
|
60286
|
+
constructor(options) {
|
|
60287
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
60288
|
+
this.storageListener = null;
|
|
60289
|
+
this.closed = false;
|
|
60290
|
+
this.username = options.username;
|
|
60291
|
+
this.tabId = options.tabId ?? newTabId();
|
|
60292
|
+
this.status = options.status;
|
|
60293
|
+
this.storage = options.storage === void 0 ? defaultStorage() : options.storage;
|
|
60294
|
+
const factory = options.channelFactory ?? defaultChannelFactory;
|
|
60295
|
+
this.channel = factory(channelName(this.username));
|
|
60296
|
+
if (this.channel) {
|
|
60297
|
+
this.channel.onmessage = (event) => this.receive(event.data);
|
|
60298
|
+
} else if (typeof window !== "undefined" && this.storage) {
|
|
60299
|
+
const key = this.fallbackKey();
|
|
60300
|
+
this.storageListener = (event) => {
|
|
60301
|
+
if (event.key !== key || !event.newValue) return;
|
|
60302
|
+
try {
|
|
60303
|
+
const parsed = JSON.parse(event.newValue);
|
|
60304
|
+
this.receive(parsed.message);
|
|
60305
|
+
} catch {
|
|
60306
|
+
}
|
|
60307
|
+
};
|
|
60308
|
+
window.addEventListener("storage", this.storageListener);
|
|
60309
|
+
}
|
|
60310
|
+
}
|
|
60311
|
+
fallbackKey() {
|
|
60312
|
+
return `callix_dialer_${this.username}${BUS_FALLBACK_SUFFIX}`;
|
|
60313
|
+
}
|
|
60314
|
+
on(listener) {
|
|
60315
|
+
this.listeners.add(listener);
|
|
60316
|
+
return () => {
|
|
60317
|
+
this.listeners.delete(listener);
|
|
60318
|
+
};
|
|
60319
|
+
}
|
|
60320
|
+
post(message) {
|
|
60321
|
+
if (this.closed) return;
|
|
60322
|
+
const full = { ...message, from: this.tabId };
|
|
60323
|
+
if (this.channel) {
|
|
60324
|
+
try {
|
|
60325
|
+
this.channel.postMessage(full);
|
|
60326
|
+
} catch {
|
|
60327
|
+
}
|
|
60328
|
+
return;
|
|
60329
|
+
}
|
|
60330
|
+
if (this.storage) {
|
|
60331
|
+
try {
|
|
60332
|
+
this.storage.setItem(this.fallbackKey(), JSON.stringify({ nonce: Math.random(), at: Date.now(), message: full }));
|
|
60333
|
+
} catch {
|
|
60334
|
+
}
|
|
60335
|
+
}
|
|
60336
|
+
}
|
|
60337
|
+
receive(data) {
|
|
60338
|
+
if (this.closed || !data || typeof data !== "object") return;
|
|
60339
|
+
const message = data;
|
|
60340
|
+
if (typeof message.type !== "string" || typeof message.from !== "string" || message.from === this.tabId) return;
|
|
60341
|
+
if (message.type === "ping") {
|
|
60342
|
+
const status = this.status();
|
|
60343
|
+
this.post({ type: "pong", role: status.role, authenticated: status.authenticated });
|
|
60344
|
+
}
|
|
60345
|
+
for (const listener of this.listeners) listener(message);
|
|
60346
|
+
}
|
|
60347
|
+
/** pergunta quem está aí; devolve as abas que responderam dentro do prazo */
|
|
60348
|
+
probe(timeoutMs = PROBE_TIMEOUT_MS) {
|
|
60349
|
+
return new Promise((resolve) => {
|
|
60350
|
+
const found = /* @__PURE__ */ new Map();
|
|
60351
|
+
const off = this.on((message) => {
|
|
60352
|
+
if (message.type === "pong") found.set(message.from, { tabId: message.from, role: message.role, authenticated: message.authenticated });
|
|
60353
|
+
});
|
|
60354
|
+
this.post({ type: "ping" });
|
|
60355
|
+
setTimeout(() => {
|
|
60356
|
+
off();
|
|
60357
|
+
resolve([...found.values()]);
|
|
60358
|
+
}, timeoutMs);
|
|
60359
|
+
});
|
|
60360
|
+
}
|
|
60361
|
+
close() {
|
|
60362
|
+
this.closed = true;
|
|
60363
|
+
this.listeners.clear();
|
|
60364
|
+
if (this.channel) {
|
|
60365
|
+
this.channel.onmessage = null;
|
|
60366
|
+
try {
|
|
60367
|
+
this.channel.close();
|
|
60368
|
+
} catch {
|
|
60369
|
+
}
|
|
60370
|
+
}
|
|
60371
|
+
if (this.storageListener && typeof window !== "undefined") {
|
|
60372
|
+
window.removeEventListener("storage", this.storageListener);
|
|
60373
|
+
this.storageListener = null;
|
|
60374
|
+
}
|
|
60375
|
+
}
|
|
60376
|
+
}
|
|
60377
|
+
function findLeader(pongs) {
|
|
60378
|
+
return pongs.find((pong) => pong.role === "leader" && pong.authenticated) ?? null;
|
|
60379
|
+
}
|
|
60380
|
+
function anyDialerTab(pongs) {
|
|
60381
|
+
return pongs.some((pong) => pong.role !== null);
|
|
60382
|
+
}
|
|
60383
|
+
function readLeader(username, storage = defaultStorage(), now = Date.now()) {
|
|
60384
|
+
if (!storage) return null;
|
|
60385
|
+
try {
|
|
60386
|
+
const raw = storage.getItem(leaderKey(username));
|
|
60387
|
+
if (!raw) return null;
|
|
60388
|
+
const parsed = JSON.parse(raw);
|
|
60389
|
+
if (typeof parsed.tabId !== "string" || typeof parsed.at !== "number") return null;
|
|
60390
|
+
if (now - parsed.at > LEADER_STALE_MS) return null;
|
|
60391
|
+
return { tabId: parsed.tabId, at: parsed.at };
|
|
60392
|
+
} catch {
|
|
60393
|
+
return null;
|
|
60394
|
+
}
|
|
60395
|
+
}
|
|
60396
|
+
function claimLeader(username, tabId, storage = defaultStorage(), now = Date.now()) {
|
|
60397
|
+
if (!storage) return true;
|
|
60398
|
+
const current = readLeader(username, storage, now);
|
|
60399
|
+
if (current && current.tabId !== tabId) return false;
|
|
60400
|
+
try {
|
|
60401
|
+
storage.setItem(leaderKey(username), JSON.stringify({ tabId, at: now }));
|
|
60402
|
+
} catch {
|
|
60403
|
+
}
|
|
60404
|
+
return true;
|
|
60405
|
+
}
|
|
60406
|
+
function releaseLeader(username, tabId, storage = defaultStorage()) {
|
|
60407
|
+
if (!storage) return;
|
|
60408
|
+
try {
|
|
60409
|
+
const raw = storage.getItem(leaderKey(username));
|
|
60410
|
+
if (!raw) return;
|
|
60411
|
+
const parsed = JSON.parse(raw);
|
|
60412
|
+
if (parsed.tabId === tabId) storage.removeItem(leaderKey(username));
|
|
60413
|
+
} catch {
|
|
60414
|
+
}
|
|
60415
|
+
}
|
|
60416
|
+
function defaultSessionStorage() {
|
|
60417
|
+
try {
|
|
60418
|
+
return typeof sessionStorage !== "undefined" ? sessionStorage : null;
|
|
60419
|
+
} catch {
|
|
60420
|
+
return null;
|
|
60421
|
+
}
|
|
60422
|
+
}
|
|
60423
|
+
function saveTabRole(username, role, storage = defaultSessionStorage()) {
|
|
60424
|
+
if (!storage) return;
|
|
60425
|
+
try {
|
|
60426
|
+
storage.setItem(TAB_ROLE_KEY, JSON.stringify({ username, role, at: Date.now() }));
|
|
60427
|
+
} catch {
|
|
60428
|
+
}
|
|
60429
|
+
}
|
|
60430
|
+
function readTabRole(username, storage = defaultSessionStorage()) {
|
|
60431
|
+
if (!storage) return null;
|
|
60432
|
+
try {
|
|
60433
|
+
const raw = storage.getItem(TAB_ROLE_KEY);
|
|
60434
|
+
if (!raw) return null;
|
|
60435
|
+
const parsed = JSON.parse(raw);
|
|
60436
|
+
if (parsed.username !== username) return null;
|
|
60437
|
+
return parsed.role === "leader" || parsed.role === "member" ? parsed.role : null;
|
|
60438
|
+
} catch {
|
|
60439
|
+
return null;
|
|
60440
|
+
}
|
|
60441
|
+
}
|
|
60442
|
+
function clearTabRole(storage = defaultSessionStorage()) {
|
|
60443
|
+
if (!storage) return;
|
|
60444
|
+
try {
|
|
60445
|
+
storage.removeItem(TAB_ROLE_KEY);
|
|
60446
|
+
} catch {
|
|
60447
|
+
}
|
|
60448
|
+
}
|
|
60449
|
+
function takeoverDelayMs(random = Math.random()) {
|
|
60450
|
+
return TAKEOVER_GRACE_MS + Math.floor(random * TAKEOVER_JITTER_MS);
|
|
60451
|
+
}
|
|
59334
60452
|
const DEFAULT_CLICK_TO_CALL_INTERVAL_MS = 4e3;
|
|
59335
60453
|
const BUSY_INTERVAL_FACTOR = 3;
|
|
59336
60454
|
const DISPATCH_COOLDOWN_MS = 6e3;
|
|
@@ -59350,8 +60468,8 @@ function buildHeaders(sessionToken) {
|
|
|
59350
60468
|
}
|
|
59351
60469
|
const delay = (ms2) => new Promise((resolve) => setTimeout(resolve, ms2));
|
|
59352
60470
|
function ClickToCallAgent({ enabled = true, pollIntervalMs, sessionToken, onTask }) {
|
|
59353
|
-
const operatorState =
|
|
59354
|
-
const { makeManualCall } =
|
|
60471
|
+
const operatorState = useCallOperatorState();
|
|
60472
|
+
const { makeManualCall } = useCallOperatorControls();
|
|
59355
60473
|
const stateRef = useRef(operatorState == null ? void 0 : operatorState.state);
|
|
59356
60474
|
stateRef.current = operatorState == null ? void 0 : operatorState.state;
|
|
59357
60475
|
const makeManualCallRef = useRef(makeManualCall);
|
|
@@ -59553,7 +60671,7 @@ function ClickToCallAgent({ enabled = true, pollIntervalMs, sessionToken, onTask
|
|
|
59553
60671
|
}, [enabled, pollIntervalMs, sessionToken]);
|
|
59554
60672
|
return null;
|
|
59555
60673
|
}
|
|
59556
|
-
const WIDGET_VERSION = "2.
|
|
60674
|
+
const WIDGET_VERSION = "2.7.0";
|
|
59557
60675
|
let currentSipWebSocket = null;
|
|
59558
60676
|
function sendSipUnregisterIfPossible(ws) {
|
|
59559
60677
|
if (ws.readyState !== WebSocket.OPEN) return;
|
|
@@ -59679,9 +60797,23 @@ function useDialerConfig() {
|
|
|
59679
60797
|
return context;
|
|
59680
60798
|
}
|
|
59681
60799
|
const SdkCleanupManager = forwardRef(
|
|
59682
|
-
function SdkCleanupManager2({ children: children2 }, ref) {
|
|
59683
|
-
const operator =
|
|
59684
|
-
const controls =
|
|
60800
|
+
function SdkCleanupManager2({ children: children2, onSessionDropped }, ref) {
|
|
60801
|
+
const operator = useCallOperator();
|
|
60802
|
+
const controls = useCallOperatorControls();
|
|
60803
|
+
const operatorState = useCallOperatorState();
|
|
60804
|
+
const droppedRef = useRef(onSessionDropped);
|
|
60805
|
+
droppedRef.current = onSessionDropped;
|
|
60806
|
+
useCallOperatorEventListener("userSessionDropped", (data) => {
|
|
60807
|
+
var _a2;
|
|
60808
|
+
const originator = (data == null ? void 0 : data.originator) ?? null;
|
|
60809
|
+
(_a2 = droppedRef.current) == null ? void 0 : _a2.call(droppedRef, originator, describeSessionDrop(originator));
|
|
60810
|
+
});
|
|
60811
|
+
const startupError = (operatorState == null ? void 0 : operatorState.state) === "startupError" ? operatorState.error ?? null : null;
|
|
60812
|
+
useEffect(() => {
|
|
60813
|
+
var _a2;
|
|
60814
|
+
if (startupError !== "userIsAlreadyConnectedOnAnotherDevice") return;
|
|
60815
|
+
(_a2 = droppedRef.current) == null ? void 0 : _a2.call(droppedRef, "anotherDevice", describeStartupError(startupError));
|
|
60816
|
+
}, [startupError]);
|
|
59685
60817
|
useImperativeHandle(ref, () => ({
|
|
59686
60818
|
goOffline: () => {
|
|
59687
60819
|
console.log("[Callix Dialer] 📴 Chamando goOffline() para enviar SIP UNREGISTER...");
|
|
@@ -59702,21 +60834,27 @@ const SdkCleanupManager = forwardRef(
|
|
|
59702
60834
|
}
|
|
59703
60835
|
}
|
|
59704
60836
|
}), [operator, controls]);
|
|
60837
|
+
const controlsRef = useRef(controls);
|
|
60838
|
+
controlsRef.current = controls;
|
|
59705
60839
|
useEffect(() => {
|
|
59706
60840
|
return () => {
|
|
59707
60841
|
console.log("[Callix Dialer] 🔌 SdkCleanupManager desmontando, chamando goOffline()...");
|
|
59708
60842
|
try {
|
|
59709
|
-
|
|
60843
|
+
controlsRef.current.goOffline();
|
|
59710
60844
|
} catch (err) {
|
|
59711
60845
|
console.warn("[Callix Dialer] ⚠️ Erro no cleanup automático:", err);
|
|
59712
60846
|
}
|
|
59713
60847
|
};
|
|
59714
|
-
}, [
|
|
60848
|
+
}, []);
|
|
59715
60849
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: children2 });
|
|
59716
60850
|
}
|
|
59717
60851
|
);
|
|
59718
60852
|
function deriveOptionsFromElement(element) {
|
|
59719
60853
|
const result2 = {};
|
|
60854
|
+
const datasetEngineUrl = element.getAttribute("data-callix-engine-url") ?? element.dataset.callixEngineUrl;
|
|
60855
|
+
if (datasetEngineUrl && datasetEngineUrl.trim()) result2.engineUrl = datasetEngineUrl.trim();
|
|
60856
|
+
const datasetEngineToken = element.getAttribute("data-callix-engine-token") ?? element.dataset.callixEngineToken;
|
|
60857
|
+
if (datasetEngineToken && datasetEngineToken.trim()) result2.engineToken = datasetEngineToken.trim();
|
|
59720
60858
|
const datasetMode = element.getAttribute("data-callix-mode") || element.dataset.callixMode || element.dataset.mode;
|
|
59721
60859
|
const datasetUsername = element.getAttribute("data-callix-username") || element.dataset.callixUsername || element.dataset.username;
|
|
59722
60860
|
const classList = Array.from(element.classList);
|
|
@@ -59808,6 +60946,12 @@ class CallixDialerWidget {
|
|
|
59808
60946
|
if (this.options.clickToCall === void 0 && derived.clickToCall !== void 0) {
|
|
59809
60947
|
this.options.clickToCall = derived.clickToCall;
|
|
59810
60948
|
}
|
|
60949
|
+
if (!this.options.engineUrl && derived.engineUrl) {
|
|
60950
|
+
this.options.engineUrl = derived.engineUrl;
|
|
60951
|
+
}
|
|
60952
|
+
if (!this.options.engineToken && derived.engineToken) {
|
|
60953
|
+
this.options.engineToken = derived.engineToken;
|
|
60954
|
+
}
|
|
59811
60955
|
this.root = clientExports.createRoot(this.container);
|
|
59812
60956
|
this.root.render(
|
|
59813
60957
|
/* @__PURE__ */ jsxRuntimeExports.jsx(StrictMode, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(DialerWrapper, { options: this.options, containerEl: this.container }) })
|
|
@@ -59840,6 +60984,27 @@ const getStorageKeys = (username) => ({
|
|
|
59840
60984
|
SKIP_MANUAL_QUALIFICATION: `callix_dialer_${username}_skip_manual_qualification`
|
|
59841
60985
|
});
|
|
59842
60986
|
const LAST_USERNAME_KEY = "callix_dialer_last_username";
|
|
60987
|
+
function readStoredToken(user) {
|
|
60988
|
+
try {
|
|
60989
|
+
return localStorage.getItem(getStorageKeys(user).SESSION_TOKEN);
|
|
60990
|
+
} catch {
|
|
60991
|
+
return null;
|
|
60992
|
+
}
|
|
60993
|
+
}
|
|
60994
|
+
async function fetchSkipQualification(domain, token) {
|
|
60995
|
+
var _a2, _b;
|
|
60996
|
+
try {
|
|
60997
|
+
const response = await fetch(`https://${domain}/sdk/v1/client/user-service/config`, {
|
|
60998
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
60999
|
+
credentials: "omit"
|
|
61000
|
+
});
|
|
61001
|
+
if (!response.ok) return null;
|
|
61002
|
+
const data = await response.json();
|
|
61003
|
+
return ((_b = (_a2 = data == null ? void 0 : data.service) == null ? void 0 : _a2.outboundOptions) == null ? void 0 : _b.skipQualification) === true;
|
|
61004
|
+
} catch {
|
|
61005
|
+
return null;
|
|
61006
|
+
}
|
|
61007
|
+
}
|
|
59843
61008
|
function DialerWrapper({ options, containerEl }) {
|
|
59844
61009
|
const [username, setUsername] = useState(null);
|
|
59845
61010
|
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
|
@@ -59855,10 +61020,35 @@ function DialerWrapper({ options, containerEl }) {
|
|
|
59855
61020
|
const [isDisconnecting, setIsDisconnecting] = useState(false);
|
|
59856
61021
|
const presetUsername = options.presetUsername ? options.presetUsername.trim() : null;
|
|
59857
61022
|
const uiVariant = options.uiVariant ?? "default";
|
|
59858
|
-
const autoLogin = options.autoLogin === true;
|
|
59859
61023
|
const portalTarget = useMemo(() => options.portalTarget ?? containerEl ?? null, [options.portalTarget, containerEl]);
|
|
59860
|
-
const
|
|
59861
|
-
|
|
61024
|
+
const tabIdRef = useRef("");
|
|
61025
|
+
if (!tabIdRef.current) tabIdRef.current = newTabId();
|
|
61026
|
+
const [tabRole, setTabRole] = useState(null);
|
|
61027
|
+
const tabRoleRef = useRef(null);
|
|
61028
|
+
tabRoleRef.current = tabRole;
|
|
61029
|
+
const [tabDecided, setTabDecided] = useState(false);
|
|
61030
|
+
const [tabNote, setTabNote] = useState(null);
|
|
61031
|
+
const [engineWaiting, setEngineWaiting] = useState(false);
|
|
61032
|
+
const busRef = useRef(null);
|
|
61033
|
+
const takeoverTimerRef = useRef(null);
|
|
61034
|
+
const isAuthenticatedRef = useRef(false);
|
|
61035
|
+
isAuthenticatedRef.current = isAuthenticated;
|
|
61036
|
+
const isLoadingRef = useRef(false);
|
|
61037
|
+
isLoadingRef.current = isLoading;
|
|
61038
|
+
const logoffPendingRef = useRef(false);
|
|
61039
|
+
const handleLoginRef = useRef(async () => void 0);
|
|
61040
|
+
const handleLogoutRef = useRef(async () => void 0);
|
|
61041
|
+
const cancelTakeover = useCallback(() => {
|
|
61042
|
+
if (takeoverTimerRef.current) {
|
|
61043
|
+
clearTimeout(takeoverTimerRef.current);
|
|
61044
|
+
takeoverTimerRef.current = null;
|
|
61045
|
+
}
|
|
61046
|
+
}, []);
|
|
61047
|
+
useEffect(() => {
|
|
61048
|
+
if (options.autoLogin) {
|
|
61049
|
+
console.info("[Callix Dialer] autoLogin: desde a 2.7.0 o widget não cria sessão sem confirmação; ele entra sozinho só quando já existe uma conexão ativa deste usuário (outra aba ou o motor).");
|
|
61050
|
+
}
|
|
61051
|
+
}, [options.autoLogin]);
|
|
59862
61052
|
const currentSessionRef = useRef({
|
|
59863
61053
|
apiUrl: options.apiUrl || "/api/user-sdk-session",
|
|
59864
61054
|
token: null
|
|
@@ -59866,9 +61056,60 @@ function DialerWrapper({ options, containerEl }) {
|
|
|
59866
61056
|
useEffect(() => {
|
|
59867
61057
|
currentSessionRef.current.token = sessionToken;
|
|
59868
61058
|
}, [sessionToken]);
|
|
61059
|
+
const engineMode = Boolean(options.engineUrl);
|
|
61060
|
+
const engineModeRef = useRef(engineMode);
|
|
61061
|
+
engineModeRef.current = engineMode;
|
|
61062
|
+
const engineRef = useRef(null);
|
|
61063
|
+
const ensureEngine = useCallback(
|
|
61064
|
+
(user) => {
|
|
61065
|
+
const current = engineRef.current;
|
|
61066
|
+
if (current && current.agentId === user) {
|
|
61067
|
+
current.connect();
|
|
61068
|
+
return current;
|
|
61069
|
+
}
|
|
61070
|
+
current == null ? void 0 : current.close();
|
|
61071
|
+
const client2 = new EngineClient({
|
|
61072
|
+
url: options.engineUrl ?? "",
|
|
61073
|
+
token: options.engineToken ?? "",
|
|
61074
|
+
agentId: user,
|
|
61075
|
+
label: "Discador",
|
|
61076
|
+
log: (message) => console.log("[Callix Dialer/motor]", message)
|
|
61077
|
+
});
|
|
61078
|
+
client2.onSessionLost((lost) => {
|
|
61079
|
+
const notice = describeSessionDrop(lost.originator);
|
|
61080
|
+
setError(`${notice.title}: ${notice.detail}${notice.action ? ` ${notice.action}` : ""}`);
|
|
61081
|
+
setIsAuthenticated(false);
|
|
61082
|
+
setSessionToken(null);
|
|
61083
|
+
setTabNote(null);
|
|
61084
|
+
});
|
|
61085
|
+
client2.connect();
|
|
61086
|
+
engineRef.current = client2;
|
|
61087
|
+
return client2;
|
|
61088
|
+
},
|
|
61089
|
+
[options.engineUrl, options.engineToken]
|
|
61090
|
+
);
|
|
61091
|
+
useEffect(
|
|
61092
|
+
() => () => {
|
|
61093
|
+
var _a2;
|
|
61094
|
+
(_a2 = engineRef.current) == null ? void 0 : _a2.close();
|
|
61095
|
+
engineRef.current = null;
|
|
61096
|
+
},
|
|
61097
|
+
[]
|
|
61098
|
+
);
|
|
59869
61099
|
useEffect(() => {
|
|
61100
|
+
let unloadHandled = false;
|
|
59870
61101
|
const handleBeforeUnload = () => {
|
|
61102
|
+
var _a2;
|
|
61103
|
+
if (engineModeRef.current || unloadHandled) return;
|
|
61104
|
+
unloadHandled = true;
|
|
61105
|
+
setTimeout(() => {
|
|
61106
|
+
unloadHandled = false;
|
|
61107
|
+
}, 2e3);
|
|
59871
61108
|
const { apiUrl, token } = currentSessionRef.current;
|
|
61109
|
+
if (tabRoleRef.current === "leader") {
|
|
61110
|
+
(_a2 = busRef.current) == null ? void 0 : _a2.post({ type: "leaving" });
|
|
61111
|
+
if (busRef.current) releaseLeader(busRef.current.username, tabIdRef.current);
|
|
61112
|
+
}
|
|
59872
61113
|
if (token) {
|
|
59873
61114
|
const data = JSON.stringify({ logout: true });
|
|
59874
61115
|
const logoutUrl = `${apiUrl}?logout=true`;
|
|
@@ -59887,8 +61128,10 @@ function DialerWrapper({ options, containerEl }) {
|
|
|
59887
61128
|
}
|
|
59888
61129
|
};
|
|
59889
61130
|
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
61131
|
+
window.addEventListener("pagehide", handleBeforeUnload);
|
|
59890
61132
|
return () => {
|
|
59891
61133
|
window.removeEventListener("beforeunload", handleBeforeUnload);
|
|
61134
|
+
window.removeEventListener("pagehide", handleBeforeUnload);
|
|
59892
61135
|
};
|
|
59893
61136
|
}, []);
|
|
59894
61137
|
useEffect(() => {
|
|
@@ -59926,12 +61169,300 @@ function DialerWrapper({ options, containerEl }) {
|
|
|
59926
61169
|
setUsername(presetUsername);
|
|
59927
61170
|
}
|
|
59928
61171
|
}, [presetUsername, username]);
|
|
61172
|
+
const engineDomain = useCallback(
|
|
61173
|
+
() => options.domain || new URL(options.apiUrl || "/api/user-sdk-session", window.location.href).host,
|
|
61174
|
+
[options.domain, options.apiUrl]
|
|
61175
|
+
);
|
|
61176
|
+
const enterEngine = useCallback(async (user, domain, token) => {
|
|
61177
|
+
if (token) {
|
|
61178
|
+
const skip = await fetchSkipQualification(domain, token);
|
|
61179
|
+
if (skip !== null) {
|
|
61180
|
+
setSkipManualQualification(skip);
|
|
61181
|
+
try {
|
|
61182
|
+
localStorage.setItem(getStorageKeys(user).SKIP_MANUAL_QUALIFICATION, String(skip));
|
|
61183
|
+
} catch {
|
|
61184
|
+
}
|
|
61185
|
+
}
|
|
61186
|
+
}
|
|
61187
|
+
try {
|
|
61188
|
+
localStorage.setItem(LAST_USERNAME_KEY, user);
|
|
61189
|
+
if (token) {
|
|
61190
|
+
const STORAGE_KEYS = getStorageKeys(user);
|
|
61191
|
+
localStorage.setItem(STORAGE_KEYS.SESSION_TOKEN, token);
|
|
61192
|
+
localStorage.setItem(STORAGE_KEYS.DOMAIN, domain);
|
|
61193
|
+
localStorage.setItem(STORAGE_KEYS.TIMESTAMP, Date.now().toString());
|
|
61194
|
+
}
|
|
61195
|
+
} catch {
|
|
61196
|
+
}
|
|
61197
|
+
setSessionToken(token);
|
|
61198
|
+
setCallixDomain(domain);
|
|
61199
|
+
setUsername(user);
|
|
61200
|
+
setIsAuthenticated(true);
|
|
61201
|
+
setError(null);
|
|
61202
|
+
setTabNote(null);
|
|
61203
|
+
setProviderKey(Date.now());
|
|
61204
|
+
}, []);
|
|
61205
|
+
const joinEngine = useCallback(
|
|
61206
|
+
async (user) => {
|
|
61207
|
+
setIsLoading(true);
|
|
61208
|
+
setError(null);
|
|
61209
|
+
setLoginStatus("Entrando na sessão do motor...");
|
|
61210
|
+
try {
|
|
61211
|
+
await enterEngine(user, engineDomain(), readStoredToken(user));
|
|
61212
|
+
console.log("[Callix Dialer] Conectado ao motor como", user);
|
|
61213
|
+
} catch (err) {
|
|
61214
|
+
console.error("[Callix Dialer] Erro ao entrar na sessão do motor:", err);
|
|
61215
|
+
setError(err instanceof Error ? err.message : "Erro ao conectar ao motor");
|
|
61216
|
+
} finally {
|
|
61217
|
+
setIsLoading(false);
|
|
61218
|
+
setLoginStatus(null);
|
|
61219
|
+
}
|
|
61220
|
+
},
|
|
61221
|
+
[enterEngine, engineDomain]
|
|
61222
|
+
);
|
|
61223
|
+
useEffect(() => {
|
|
61224
|
+
if (!engineMode || !username || isRestoringSession) return;
|
|
61225
|
+
const user = username;
|
|
61226
|
+
const client2 = ensureEngine(user);
|
|
61227
|
+
setEngineWaiting(!client2.getState().view);
|
|
61228
|
+
const fallback = setTimeout(() => setEngineWaiting(false), 15e3);
|
|
61229
|
+
let joining = false;
|
|
61230
|
+
const sync = () => {
|
|
61231
|
+
const { view, sessionLost } = client2.getState();
|
|
61232
|
+
if (!view) return;
|
|
61233
|
+
setEngineWaiting(false);
|
|
61234
|
+
if (view.engine === "stopped" || view.engine === "crashed") logoffPendingRef.current = false;
|
|
61235
|
+
const hasSession = (view.engine === "running" || view.engine === "starting") && !sessionLost;
|
|
61236
|
+
if (hasSession && !logoffPendingRef.current && !isAuthenticatedRef.current && !isLoadingRef.current && !joining) {
|
|
61237
|
+
joining = true;
|
|
61238
|
+
console.log("[Callix Dialer] o motor já segura a sessão deste agente; entrando por aqui também");
|
|
61239
|
+
void joinEngine(user).finally(() => {
|
|
61240
|
+
joining = false;
|
|
61241
|
+
});
|
|
61242
|
+
} else if (view.engine === "stopped" && isAuthenticatedRef.current && !isLoadingRef.current) {
|
|
61243
|
+
console.log("[Callix Dialer] o motor desligou (Sair em outra aba ou servidor reiniciado); voltando à tela de login");
|
|
61244
|
+
setIsAuthenticated(false);
|
|
61245
|
+
setSessionToken(null);
|
|
61246
|
+
setCallixDomain(null);
|
|
61247
|
+
setTabNote("O discador foi encerrado em outra aba ou o motor foi reiniciado.");
|
|
61248
|
+
}
|
|
61249
|
+
};
|
|
61250
|
+
sync();
|
|
61251
|
+
const off = client2.subscribe(sync);
|
|
61252
|
+
return () => {
|
|
61253
|
+
off();
|
|
61254
|
+
clearTimeout(fallback);
|
|
61255
|
+
};
|
|
61256
|
+
}, [engineMode, username, isRestoringSession, ensureEngine, joinEngine]);
|
|
61257
|
+
const scheduleTakeoverRef = useRef(null);
|
|
61258
|
+
useEffect(() => {
|
|
61259
|
+
if (engineMode || !username) {
|
|
61260
|
+
setTabDecided(true);
|
|
61261
|
+
return;
|
|
61262
|
+
}
|
|
61263
|
+
setTabDecided(false);
|
|
61264
|
+
const user = username;
|
|
61265
|
+
const bus = new DialerTabBus({
|
|
61266
|
+
username: user,
|
|
61267
|
+
tabId: tabIdRef.current,
|
|
61268
|
+
status: () => ({ role: tabRoleRef.current, authenticated: isAuthenticatedRef.current })
|
|
61269
|
+
});
|
|
61270
|
+
busRef.current = bus;
|
|
61271
|
+
const becomeMember = () => {
|
|
61272
|
+
if (isAuthenticatedRef.current || tabRoleRef.current === "leader") return;
|
|
61273
|
+
setTabRole("member");
|
|
61274
|
+
saveTabRole(user, "member");
|
|
61275
|
+
setTabNote(null);
|
|
61276
|
+
setError(null);
|
|
61277
|
+
setTabDecided(true);
|
|
61278
|
+
};
|
|
61279
|
+
const scheduleTakeover = () => {
|
|
61280
|
+
cancelTakeover();
|
|
61281
|
+
takeoverTimerRef.current = setTimeout(() => {
|
|
61282
|
+
takeoverTimerRef.current = null;
|
|
61283
|
+
if (tabRoleRef.current !== "member" || isAuthenticatedRef.current || isLoadingRef.current) return;
|
|
61284
|
+
const current = readLeader(user);
|
|
61285
|
+
if (current && current.tabId !== tabIdRef.current) return;
|
|
61286
|
+
if (!claimLeader(user, tabIdRef.current)) return;
|
|
61287
|
+
bus.post({ type: "claim" });
|
|
61288
|
+
console.log("[Callix Dialer] a aba com o discador fechou; esta aba assume a conexão");
|
|
61289
|
+
void handleLoginRef.current(user);
|
|
61290
|
+
}, takeoverDelayMs());
|
|
61291
|
+
};
|
|
61292
|
+
scheduleTakeoverRef.current = scheduleTakeover;
|
|
61293
|
+
const off = bus.on((message) => {
|
|
61294
|
+
switch (message.type) {
|
|
61295
|
+
case "login":
|
|
61296
|
+
case "claim":
|
|
61297
|
+
cancelTakeover();
|
|
61298
|
+
becomeMember();
|
|
61299
|
+
break;
|
|
61300
|
+
case "leaving":
|
|
61301
|
+
if (tabRoleRef.current === "member" && !isAuthenticatedRef.current) scheduleTakeover();
|
|
61302
|
+
break;
|
|
61303
|
+
case "takeover":
|
|
61304
|
+
if (tabRoleRef.current === "leader") {
|
|
61305
|
+
console.log("[Callix Dialer] outra aba pediu o discador; soltando a conexão");
|
|
61306
|
+
void handleLogoutRef.current({ broadcast: false, keepDecision: true });
|
|
61307
|
+
}
|
|
61308
|
+
break;
|
|
61309
|
+
case "logout":
|
|
61310
|
+
cancelTakeover();
|
|
61311
|
+
void handleLogoutRef.current({ broadcast: false });
|
|
61312
|
+
setTabNote("Você saiu do discador em outra aba.");
|
|
61313
|
+
break;
|
|
61314
|
+
case "dropped": {
|
|
61315
|
+
cancelTakeover();
|
|
61316
|
+
releaseLeader(user, tabIdRef.current);
|
|
61317
|
+
setTabRole(null);
|
|
61318
|
+
clearTabRole();
|
|
61319
|
+
const notice = describeSessionDrop(message.originator);
|
|
61320
|
+
setError(`${notice.title}: ${notice.detail}${notice.action ? ` ${notice.action}` : ""}`);
|
|
61321
|
+
break;
|
|
61322
|
+
}
|
|
61323
|
+
}
|
|
61324
|
+
});
|
|
61325
|
+
return () => {
|
|
61326
|
+
off();
|
|
61327
|
+
bus.close();
|
|
61328
|
+
cancelTakeover();
|
|
61329
|
+
scheduleTakeoverRef.current = null;
|
|
61330
|
+
if (busRef.current === bus) busRef.current = null;
|
|
61331
|
+
setTabDecided(false);
|
|
61332
|
+
};
|
|
61333
|
+
}, [engineMode, username, cancelTakeover]);
|
|
61334
|
+
useEffect(() => {
|
|
61335
|
+
if (engineMode || !username || isAuthenticated || tabDecided || isRestoringSession || isLoading) return;
|
|
61336
|
+
const bus = busRef.current;
|
|
61337
|
+
if (!bus) return;
|
|
61338
|
+
const user = username;
|
|
61339
|
+
const stored = readTabRole(user);
|
|
61340
|
+
if (stored === "leader") {
|
|
61341
|
+
cancelTakeover();
|
|
61342
|
+
setTabDecided(true);
|
|
61343
|
+
if (claimLeader(user, tabIdRef.current)) {
|
|
61344
|
+
bus.post({ type: "claim" });
|
|
61345
|
+
console.log("[Callix Dialer] esta aba segurava o discador antes de recarregar; reconectando");
|
|
61346
|
+
void handleLoginRef.current(user);
|
|
61347
|
+
} else {
|
|
61348
|
+
setTabRole("member");
|
|
61349
|
+
saveTabRole(user, "member");
|
|
61350
|
+
}
|
|
61351
|
+
return;
|
|
61352
|
+
}
|
|
61353
|
+
let cancelled = false;
|
|
61354
|
+
void bus.probe().then((pongs) => {
|
|
61355
|
+
var _a2;
|
|
61356
|
+
if (cancelled) return;
|
|
61357
|
+
setTabDecided(true);
|
|
61358
|
+
const leaderLock = readLeader(user);
|
|
61359
|
+
if (findLeader(pongs) || leaderLock && leaderLock.tabId !== tabIdRef.current || stored === "member" && anyDialerTab(pongs)) {
|
|
61360
|
+
setTabRole("member");
|
|
61361
|
+
saveTabRole(user, "member");
|
|
61362
|
+
} else if (stored === "member") {
|
|
61363
|
+
setTabRole("member");
|
|
61364
|
+
(_a2 = scheduleTakeoverRef.current) == null ? void 0 : _a2.call(scheduleTakeoverRef);
|
|
61365
|
+
} else {
|
|
61366
|
+
setTabRole(null);
|
|
61367
|
+
clearTabRole();
|
|
61368
|
+
}
|
|
61369
|
+
});
|
|
61370
|
+
return () => {
|
|
61371
|
+
cancelled = true;
|
|
61372
|
+
};
|
|
61373
|
+
}, [engineMode, username, isAuthenticated, tabDecided, isRestoringSession, isLoading, cancelTakeover]);
|
|
61374
|
+
useEffect(() => {
|
|
61375
|
+
if (engineMode || tabRole !== "leader" || !username) return;
|
|
61376
|
+
const user = username;
|
|
61377
|
+
claimLeader(user, tabIdRef.current);
|
|
61378
|
+
const timer = setInterval(() => claimLeader(user, tabIdRef.current), LEADER_HEARTBEAT_MS);
|
|
61379
|
+
return () => clearInterval(timer);
|
|
61380
|
+
}, [engineMode, tabRole, username]);
|
|
61381
|
+
useEffect(() => {
|
|
61382
|
+
if (engineMode || tabRole !== "member" || !username) return;
|
|
61383
|
+
const user = username;
|
|
61384
|
+
const check = () => {
|
|
61385
|
+
if (document.visibilityState !== "visible" || takeoverTimerRef.current) return;
|
|
61386
|
+
const bus = busRef.current;
|
|
61387
|
+
if (!bus) return;
|
|
61388
|
+
void bus.probe().then((pongs) => {
|
|
61389
|
+
var _a2;
|
|
61390
|
+
if (tabRoleRef.current !== "member" || isAuthenticatedRef.current || takeoverTimerRef.current) return;
|
|
61391
|
+
if (findLeader(pongs) || readLeader(user)) return;
|
|
61392
|
+
console.log("[Callix Dialer] a aba com o discador não responde; esta aba vai assumir");
|
|
61393
|
+
(_a2 = scheduleTakeoverRef.current) == null ? void 0 : _a2.call(scheduleTakeoverRef);
|
|
61394
|
+
});
|
|
61395
|
+
};
|
|
61396
|
+
document.addEventListener("visibilitychange", check);
|
|
61397
|
+
window.addEventListener("focus", check);
|
|
61398
|
+
return () => {
|
|
61399
|
+
document.removeEventListener("visibilitychange", check);
|
|
61400
|
+
window.removeEventListener("focus", check);
|
|
61401
|
+
};
|
|
61402
|
+
}, [engineMode, tabRole, username]);
|
|
59929
61403
|
const handleLogin = useCallback(async (user) => {
|
|
59930
|
-
var _a2, _b;
|
|
61404
|
+
var _a2, _b, _c, _d;
|
|
59931
61405
|
setIsLoading(true);
|
|
59932
61406
|
setError(null);
|
|
59933
61407
|
setLoginStatus("Preparando login...");
|
|
59934
61408
|
console.log("[Callix Dialer] 🚀 handleLogin iniciado para usuário:", user);
|
|
61409
|
+
if (options.engineUrl) {
|
|
61410
|
+
const engineApiUrl = options.apiUrl || "/api/user-sdk-session";
|
|
61411
|
+
try {
|
|
61412
|
+
setLoginStatus("Conectando ao motor...");
|
|
61413
|
+
logoffPendingRef.current = false;
|
|
61414
|
+
const client2 = ensureEngine(user);
|
|
61415
|
+
const view = await client2.waitForView(15e3);
|
|
61416
|
+
const domain = engineDomain();
|
|
61417
|
+
const engineHasSession = (view.engine === "running" || view.engine === "starting") && !view.sessionLost;
|
|
61418
|
+
let token = null;
|
|
61419
|
+
if (engineHasSession) {
|
|
61420
|
+
token = readStoredToken(user);
|
|
61421
|
+
console.log("[Callix Dialer] motor já tem a sessão do agente; entrando sem novo login");
|
|
61422
|
+
} else {
|
|
61423
|
+
setLoginStatus("Autenticando no Callix...");
|
|
61424
|
+
const response = await fetch(engineApiUrl, {
|
|
61425
|
+
method: "POST",
|
|
61426
|
+
headers: { "Content-Type": "application/json" },
|
|
61427
|
+
body: JSON.stringify({ username: user }),
|
|
61428
|
+
credentials: "same-origin"
|
|
61429
|
+
});
|
|
61430
|
+
if (!response.ok) {
|
|
61431
|
+
const errorData = await response.json().catch(() => ({ error: "Erro desconhecido" }));
|
|
61432
|
+
throw new Error(errorData.error || `Falha na autenticação: ${response.status}`);
|
|
61433
|
+
}
|
|
61434
|
+
const data = await response.json();
|
|
61435
|
+
token = data.userSessionToken;
|
|
61436
|
+
client2.setSession({ domain, userSessionToken: token });
|
|
61437
|
+
setLoginStatus("Subindo o motor...");
|
|
61438
|
+
}
|
|
61439
|
+
await enterEngine(user, domain, token);
|
|
61440
|
+
console.log("[Callix Dialer] Conectado ao motor como", user);
|
|
61441
|
+
} catch (err) {
|
|
61442
|
+
console.error("[Callix Dialer] Erro ao entrar pelo motor:", err);
|
|
61443
|
+
setError(err instanceof Error ? err.message : "Erro ao conectar ao motor");
|
|
61444
|
+
setIsAuthenticated(false);
|
|
61445
|
+
setSessionToken(null);
|
|
61446
|
+
} finally {
|
|
61447
|
+
setIsLoading(false);
|
|
61448
|
+
setLoginStatus(null);
|
|
61449
|
+
}
|
|
61450
|
+
return;
|
|
61451
|
+
}
|
|
61452
|
+
if (!claimLeader(user, tabIdRef.current)) {
|
|
61453
|
+
console.log("[Callix Dialer] outra aba já segura a conexão deste usuário; esta aba vira membro");
|
|
61454
|
+
setTabRole("member");
|
|
61455
|
+
saveTabRole(user, "member");
|
|
61456
|
+
setTabDecided(true);
|
|
61457
|
+
setUsername(user);
|
|
61458
|
+
setIsLoading(false);
|
|
61459
|
+
setLoginStatus(null);
|
|
61460
|
+
return;
|
|
61461
|
+
}
|
|
61462
|
+
(_a2 = busRef.current) == null ? void 0 : _a2.post({ type: "claim" });
|
|
61463
|
+
cancelTakeover();
|
|
61464
|
+
setTabRole("leader");
|
|
61465
|
+
saveTabRole(user, "leader");
|
|
59935
61466
|
const apiUrl = options.apiUrl || "/api/user-sdk-session";
|
|
59936
61467
|
let previousToken;
|
|
59937
61468
|
if (typeof window !== "undefined") {
|
|
@@ -60026,6 +61557,9 @@ function DialerWrapper({ options, containerEl }) {
|
|
|
60026
61557
|
setError(null);
|
|
60027
61558
|
setProviderKey(Date.now());
|
|
60028
61559
|
console.log("[Callix Dialer] Nova providerKey gerada para forçar recriação do SDK");
|
|
61560
|
+
setTabNote(null);
|
|
61561
|
+
setTabDecided(true);
|
|
61562
|
+
(_b = busRef.current) == null ? void 0 : _b.post({ type: "login" });
|
|
60029
61563
|
try {
|
|
60030
61564
|
const configUrl = `https://${resolvedDomain}/sdk/v1/client/user-service/config`;
|
|
60031
61565
|
setLoginStatus("Carregando configurações...");
|
|
@@ -60040,7 +61574,7 @@ function DialerWrapper({ options, containerEl }) {
|
|
|
60040
61574
|
if (configResponse.ok) {
|
|
60041
61575
|
const configData = await configResponse.json();
|
|
60042
61576
|
console.log("[Callix Dialer] 📦 Dados da config recebidos:", JSON.stringify(configData, null, 2));
|
|
60043
|
-
const shouldSkipQualification = ((
|
|
61577
|
+
const shouldSkipQualification = ((_d = (_c = configData == null ? void 0 : configData.service) == null ? void 0 : _c.outboundOptions) == null ? void 0 : _d.skipQualification) === true;
|
|
60044
61578
|
console.log("[Callix Dialer] ✅ skipQualification configurado:", shouldSkipQualification);
|
|
60045
61579
|
setSkipManualQualification(shouldSkipQualification);
|
|
60046
61580
|
if (typeof window !== "undefined") {
|
|
@@ -60074,50 +61608,52 @@ function DialerWrapper({ options, containerEl }) {
|
|
|
60074
61608
|
setError(err instanceof Error ? err.message : "Erro ao autenticar");
|
|
60075
61609
|
setIsAuthenticated(false);
|
|
60076
61610
|
setSessionToken(null);
|
|
61611
|
+
releaseLeader(user, tabIdRef.current);
|
|
61612
|
+
setTabRole(null);
|
|
61613
|
+
clearTabRole();
|
|
60077
61614
|
} finally {
|
|
60078
61615
|
setIsLoading(false);
|
|
60079
61616
|
setLoginStatus(null);
|
|
60080
61617
|
}
|
|
60081
|
-
}, [options.apiUrl, options.domain, isAuthenticated, sessionToken]);
|
|
60082
|
-
useEffect(() => {
|
|
60083
|
-
if (lastPresetRef.current !== presetUsername) {
|
|
60084
|
-
lastPresetRef.current = presetUsername;
|
|
60085
|
-
autoLoginAttemptedRef.current = false;
|
|
60086
|
-
}
|
|
60087
|
-
if (!presetUsername || !autoLogin) {
|
|
60088
|
-
return;
|
|
60089
|
-
}
|
|
60090
|
-
if (isAuthenticated || isLoading || isRestoringSession || error2 || autoLoginAttemptedRef.current) {
|
|
60091
|
-
return;
|
|
60092
|
-
}
|
|
60093
|
-
autoLoginAttemptedRef.current = true;
|
|
60094
|
-
void handleLogin(presetUsername);
|
|
60095
|
-
}, [presetUsername, autoLogin, isAuthenticated, isLoading, isRestoringSession, error2, handleLogin]);
|
|
61618
|
+
}, [options.apiUrl, options.domain, options.engineUrl, isAuthenticated, sessionToken, ensureEngine, enterEngine, engineDomain, cancelTakeover]);
|
|
60096
61619
|
useEffect(() => {
|
|
60097
|
-
|
|
60098
|
-
|
|
60099
|
-
|
|
60100
|
-
|
|
60101
|
-
|
|
60102
|
-
void
|
|
60103
|
-
}, [autoLogin, presetUsername, username, isAuthenticated, isLoading, isRestoringSession, error2, handleLogin]);
|
|
60104
|
-
const handleLogout = useCallback(async () => {
|
|
61620
|
+
handleLoginRef.current = handleLogin;
|
|
61621
|
+
}, [handleLogin]);
|
|
61622
|
+
const handleLogout = useCallback(async (opts) => {
|
|
61623
|
+
var _a2, _b;
|
|
61624
|
+
const broadcast = (opts == null ? void 0 : opts.broadcast) !== false;
|
|
61625
|
+
const keepDecision = (opts == null ? void 0 : opts.keepDecision) === true;
|
|
60105
61626
|
const currentUsername = username;
|
|
60106
|
-
|
|
60107
|
-
|
|
60108
|
-
|
|
60109
|
-
|
|
60110
|
-
|
|
60111
|
-
|
|
60112
|
-
|
|
61627
|
+
cancelTakeover();
|
|
61628
|
+
if (options.engineUrl) {
|
|
61629
|
+
if (isAuthenticatedRef.current) {
|
|
61630
|
+
logoffPendingRef.current = true;
|
|
61631
|
+
(_a2 = engineRef.current) == null ? void 0 : _a2.command({ name: "logoff" });
|
|
61632
|
+
}
|
|
61633
|
+
} else {
|
|
61634
|
+
if (broadcast) (_b = busRef.current) == null ? void 0 : _b.post({ type: "logout" });
|
|
61635
|
+
if (cleanupRef.current) {
|
|
61636
|
+
console.log("[Callix Dialer] 📴 Logout: Chamando goOffline()...");
|
|
61637
|
+
cleanupRef.current.goOffline();
|
|
61638
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
61639
|
+
console.log("[Callix Dialer] 🛑 Logout: Chamando stop()...");
|
|
61640
|
+
cleanupRef.current.stop();
|
|
61641
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
61642
|
+
}
|
|
61643
|
+
if (currentUsername) releaseLeader(currentUsername, tabIdRef.current);
|
|
60113
61644
|
}
|
|
60114
|
-
setUsername(presetUsername ?? null);
|
|
60115
61645
|
setIsAuthenticated(false);
|
|
60116
61646
|
setSessionToken(null);
|
|
60117
61647
|
setCallixDomain(null);
|
|
60118
61648
|
setError(null);
|
|
60119
|
-
|
|
60120
|
-
|
|
61649
|
+
if (keepDecision) {
|
|
61650
|
+
setTabRole("member");
|
|
61651
|
+
if (currentUsername) saveTabRole(currentUsername, "member");
|
|
61652
|
+
return;
|
|
61653
|
+
}
|
|
61654
|
+
setTabRole(null);
|
|
61655
|
+
clearTabRole();
|
|
61656
|
+
setUsername(presetUsername ?? null);
|
|
60121
61657
|
if (typeof window !== "undefined" && currentUsername) {
|
|
60122
61658
|
try {
|
|
60123
61659
|
const STORAGE_KEYS = getStorageKeys(currentUsername);
|
|
@@ -60130,14 +61666,53 @@ function DialerWrapper({ options, containerEl }) {
|
|
|
60130
61666
|
console.warn("[Callix Dialer] Não foi possível limpar sessão:", err);
|
|
60131
61667
|
}
|
|
60132
61668
|
}
|
|
60133
|
-
}, [username, presetUsername]);
|
|
60134
|
-
|
|
61669
|
+
}, [username, presetUsername, options.engineUrl, cancelTakeover]);
|
|
61670
|
+
useEffect(() => {
|
|
61671
|
+
handleLogoutRef.current = handleLogout;
|
|
61672
|
+
}, [handleLogout]);
|
|
61673
|
+
const handleTakeOver = useCallback(async () => {
|
|
61674
|
+
const user = username;
|
|
61675
|
+
const bus = busRef.current;
|
|
61676
|
+
if (!user || !bus) return;
|
|
61677
|
+
setIsLoading(true);
|
|
61678
|
+
setError(null);
|
|
61679
|
+
setLoginStatus("Trazendo o discador para esta aba...");
|
|
61680
|
+
bus.post({ type: "takeover" });
|
|
61681
|
+
await new Promise((resolve) => setTimeout(resolve, 1500));
|
|
61682
|
+
const current = readLeader(user);
|
|
61683
|
+
if (current && current.tabId !== tabIdRef.current) {
|
|
61684
|
+
console.log("[Callix Dialer] a outra aba não soltou a conexão; continuando como membro");
|
|
61685
|
+
setIsLoading(false);
|
|
61686
|
+
setLoginStatus(null);
|
|
61687
|
+
return;
|
|
61688
|
+
}
|
|
61689
|
+
await handleLoginRef.current(user);
|
|
61690
|
+
}, [username]);
|
|
61691
|
+
const handleLogoutAll = useCallback(() => {
|
|
61692
|
+
void handleLogoutRef.current();
|
|
61693
|
+
}, []);
|
|
61694
|
+
const handleSdkSessionDropped = useCallback(
|
|
61695
|
+
(originator, notice) => {
|
|
61696
|
+
var _a2;
|
|
61697
|
+
const user = username;
|
|
61698
|
+
if (user) releaseLeader(user, tabIdRef.current);
|
|
61699
|
+
(_a2 = busRef.current) == null ? void 0 : _a2.post({ type: "dropped", originator });
|
|
61700
|
+
setTabRole(null);
|
|
61701
|
+
clearTabRole();
|
|
61702
|
+
setError(`${notice.title}: ${notice.detail}${notice.action ? ` ${notice.action}` : ""}`);
|
|
61703
|
+
setIsAuthenticated(false);
|
|
61704
|
+
setSessionToken(null);
|
|
61705
|
+
setCallixDomain(null);
|
|
61706
|
+
},
|
|
61707
|
+
[username]
|
|
61708
|
+
);
|
|
61709
|
+
const deciding = Boolean(username) && !isAuthenticated && (engineMode ? engineWaiting : !tabDecided);
|
|
60135
61710
|
const loginConfirmation = useMemo(() => {
|
|
60136
|
-
if (isAuthenticated || isLoading || isRestoringSession || isDisconnecting || !username ||
|
|
61711
|
+
if (isAuthenticated || isLoading || isRestoringSession || isDisconnecting || !username || deciding) {
|
|
60137
61712
|
return null;
|
|
60138
61713
|
}
|
|
60139
|
-
return { username, locked: Boolean(presetUsername) };
|
|
60140
|
-
}, [isAuthenticated, isLoading, isRestoringSession, isDisconnecting, username, presetUsername,
|
|
61714
|
+
return { username, locked: Boolean(presetUsername), elsewhere: !engineMode && tabRole === "member", note: tabNote };
|
|
61715
|
+
}, [isAuthenticated, isLoading, isRestoringSession, isDisconnecting, username, presetUsername, deciding, engineMode, tabRole, tabNote]);
|
|
60141
61716
|
const handleCancelLoginConfirmation = useCallback(() => {
|
|
60142
61717
|
setUsername(null);
|
|
60143
61718
|
setError(null);
|
|
@@ -60169,7 +61744,7 @@ function DialerWrapper({ options, containerEl }) {
|
|
|
60169
61744
|
}
|
|
60170
61745
|
) });
|
|
60171
61746
|
}
|
|
60172
|
-
if (!isAuthenticated || !sessionToken || !callixDomain) {
|
|
61747
|
+
if (!isAuthenticated || !engineMode && (!sessionToken || !callixDomain)) {
|
|
60173
61748
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(DialerConfigContext.Provider, { value: { skipManualQualification }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
60174
61749
|
DialerComponent,
|
|
60175
61750
|
{
|
|
@@ -60177,11 +61752,13 @@ function DialerWrapper({ options, containerEl }) {
|
|
|
60177
61752
|
username,
|
|
60178
61753
|
onLogin: handleLogin,
|
|
60179
61754
|
onLogout: handleLogout,
|
|
60180
|
-
isLoadingSession: isLoading || isRestoringSession ||
|
|
61755
|
+
isLoadingSession: isLoading || isRestoringSession || deciding,
|
|
60181
61756
|
sessionError: error2,
|
|
60182
61757
|
sessionStatus: loginStatus,
|
|
60183
61758
|
loginConfirmation,
|
|
60184
61759
|
onCancelLoginConfirmation: handleCancelLoginConfirmation,
|
|
61760
|
+
onTakeOverSession: handleTakeOver,
|
|
61761
|
+
onLogoutAllTabs: handleLogoutAll,
|
|
60185
61762
|
variant: uiVariant,
|
|
60186
61763
|
portalTarget,
|
|
60187
61764
|
density: options.density,
|
|
@@ -60189,12 +61766,44 @@ function DialerWrapper({ options, containerEl }) {
|
|
|
60189
61766
|
}
|
|
60190
61767
|
) });
|
|
60191
61768
|
}
|
|
61769
|
+
if (engineMode && engineRef.current) {
|
|
61770
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(DialerConfigContext.Provider, { value: { skipManualQualification }, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(EngineClientProvider, { client: engineRef.current, children: [
|
|
61771
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
61772
|
+
DialerComponent,
|
|
61773
|
+
{
|
|
61774
|
+
isAuthenticated,
|
|
61775
|
+
username,
|
|
61776
|
+
onLogin: handleLogin,
|
|
61777
|
+
onLogout: handleLogout,
|
|
61778
|
+
isLoadingSession: isLoading,
|
|
61779
|
+
sessionError: error2,
|
|
61780
|
+
sessionStatus: loginStatus,
|
|
61781
|
+
variant: uiVariant,
|
|
61782
|
+
portalTarget,
|
|
61783
|
+
density: options.density,
|
|
61784
|
+
mode: dialerMode
|
|
61785
|
+
}
|
|
61786
|
+
),
|
|
61787
|
+
sessionToken && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
61788
|
+
ClickToCallAgent,
|
|
61789
|
+
{
|
|
61790
|
+
enabled: clickToCall.enabled,
|
|
61791
|
+
pollIntervalMs: clickToCall.pollIntervalMs,
|
|
61792
|
+
sessionToken,
|
|
61793
|
+
onTask: clickToCall.onTask
|
|
61794
|
+
}
|
|
61795
|
+
)
|
|
61796
|
+
] }, providerKey) });
|
|
61797
|
+
}
|
|
61798
|
+
if (!sessionToken || !callixDomain) {
|
|
61799
|
+
return null;
|
|
61800
|
+
}
|
|
60192
61801
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(DialerConfigContext.Provider, { value: { skipManualQualification }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
60193
61802
|
clientSdkReactExports.CallixClientProvider,
|
|
60194
61803
|
{
|
|
60195
61804
|
domain: callixDomain,
|
|
60196
61805
|
userSessionToken: sessionToken,
|
|
60197
|
-
children: /* @__PURE__ */ jsxRuntimeExports.jsxs(SdkCleanupManager, { ref: cleanupRef, children: [
|
|
61806
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsxs(SdkCleanupManager, { ref: cleanupRef, onSessionDropped: handleSdkSessionDropped, children: [
|
|
60198
61807
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
60199
61808
|
DialerComponent,
|
|
60200
61809
|
{
|