Add voxtelesys_integration/public/js/voxtelesys_call_popup.js
This commit is contained in:
parent
babc12e6c1
commit
bad09d7981
1 changed files with 117 additions and 0 deletions
117
voxtelesys_integration/public/js/voxtelesys_call_popup.js
Normal file
117
voxtelesys_integration/public/js/voxtelesys_call_popup.js
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
/**
|
||||||
|
* voxtelesys_call_popup.js
|
||||||
|
* Overrides frappe.phone_call.handler for click-to-call via Voxtelesys.
|
||||||
|
* Subscribes to realtime events for incoming call popup + active call bar.
|
||||||
|
*/
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
frappe.after_ajax(function () {
|
||||||
|
if (!frappe.boot.voxtelesys || !frappe.boot.voxtelesys.enabled) return;
|
||||||
|
VoxtelesysPhone.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
const VoxtelesysPhone = {
|
||||||
|
activeCallLog: null, popup: null, _popupTimeout: null,
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this._overridePhoneCallHandler();
|
||||||
|
this._subscribeRealtime();
|
||||||
|
console.log("[Voxtelesys] Telephony provider initialised.");
|
||||||
|
},
|
||||||
|
|
||||||
|
_overridePhoneCallHandler() {
|
||||||
|
frappe.phone_call = {
|
||||||
|
handler: (phoneNumbers, frm) => {
|
||||||
|
const numbers = Array.isArray(phoneNumbers) ? phoneNumbers : [phoneNumbers];
|
||||||
|
if (numbers.length === 1) { VoxtelesysPhone.dialNumber(numbers[0], frm); }
|
||||||
|
else { VoxtelesysPhone._showNumberPicker(numbers, frm); }
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
dialNumber(number, frm) {
|
||||||
|
const args = { to: number, from_: frappe.boot.voxtelesys.caller_id || "" };
|
||||||
|
if (frm && frm.doc) { args.reference_doctype = frm.doctype; args.reference_name = frm.docname; }
|
||||||
|
frappe.call({
|
||||||
|
method: "voxtelesys_integration.api.voxtelesys.make_outbound_call",
|
||||||
|
args,
|
||||||
|
callback: (r) => {
|
||||||
|
if (r.message && r.message.ok) {
|
||||||
|
VoxtelesysPhone._showCallBar({ direction: "Outbound", number, callLog: r.message.call_log, callSid: r.message.call_sid });
|
||||||
|
} else {
|
||||||
|
frappe.msgprint({ title: __("Call Failed"), message: __("Could not place call. Check Voxtelesys settings."), indicator: "red" });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_subscribeRealtime() {
|
||||||
|
frappe.realtime.on("voxtelesys_call_initiated", (data) => {
|
||||||
|
if (data.direction === "Inbound") VoxtelesysPhone._showIncomingCallPopup(data);
|
||||||
|
});
|
||||||
|
frappe.realtime.on("voxtelesys_call_disconnected", (data) => {
|
||||||
|
VoxtelesysPhone._handleCallDisconnected(data);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_showIncomingCallPopup(data) {
|
||||||
|
if (this.popup && this.popup.call_sid === data.call_sid) return;
|
||||||
|
this.popup = new frappe.ui.Dialog({
|
||||||
|
title: __("Incoming Call"),
|
||||||
|
fields: [{ fieldtype: "HTML", fieldname: "call_info", options: `
|
||||||
|
<div class="vtx-incoming-call">
|
||||||
|
<div class="vtx-phone-icon">📞</div>
|
||||||
|
<div class="vtx-caller">${frappe.utils.escape_html(data.from || "Unknown")}</div>
|
||||||
|
<div class="vtx-call-time vtx-blink">Ringing…</div>
|
||||||
|
</div>` }],
|
||||||
|
primary_action_label: __("Open Call Log"),
|
||||||
|
primary_action: () => { frappe.set_route("Form", "Voxtelesys Call Log", data.call_log); this.popup.hide(); },
|
||||||
|
});
|
||||||
|
this.popup.call_sid = data.call_sid;
|
||||||
|
this.popup.$wrapper.find(".modal-footer").prepend(`<button class="btn btn-default vtx-dismiss-btn">${__("Dismiss")}</button>`);
|
||||||
|
this.popup.$wrapper.find(".vtx-dismiss-btn").on("click", () => this.popup.hide());
|
||||||
|
this.popup.show();
|
||||||
|
this._popupTimeout = setTimeout(() => { if (this.popup) this.popup.hide(); }, 60000);
|
||||||
|
},
|
||||||
|
|
||||||
|
_showCallBar({ direction, number, callLog }) {
|
||||||
|
this._removeCallBar();
|
||||||
|
this.activeCallLog = callLog;
|
||||||
|
const bar = document.createElement("div");
|
||||||
|
bar.id = "vtx-call-bar";
|
||||||
|
bar.innerHTML = `<div class="vtx-call-bar-inner">
|
||||||
|
<span class="vtx-call-direction">${direction === "Outbound" ? "📤" : "📥"}</span>
|
||||||
|
<span class="vtx-call-number">${frappe.utils.escape_html(number)}</span>
|
||||||
|
<span class="vtx-call-status" id="vtx-status-label">Connecting…</span>
|
||||||
|
<button class="btn btn-xs btn-default vtx-open-log-btn">Open Log</button>
|
||||||
|
<button class="btn btn-xs btn-danger vtx-hangup-btn">End Call</button>
|
||||||
|
</div>`;
|
||||||
|
bar.querySelector(".vtx-open-log-btn").addEventListener("click", () => frappe.set_route("Form", "Voxtelesys Call Log", callLog));
|
||||||
|
bar.querySelector(".vtx-hangup-btn").addEventListener("click", () => VoxtelesysPhone._removeCallBar());
|
||||||
|
document.body.appendChild(bar);
|
||||||
|
},
|
||||||
|
|
||||||
|
_removeCallBar() { const el = document.getElementById("vtx-call-bar"); if (el) el.remove(); this.activeCallLog = null; },
|
||||||
|
|
||||||
|
_handleCallDisconnected(data) {
|
||||||
|
const lbl = document.getElementById("vtx-status-label");
|
||||||
|
if (lbl && this.activeCallLog === data.call_log) {
|
||||||
|
lbl.textContent = `${data.status || "Ended"} · ${data.duration ? data.duration + "s" : ""}`;
|
||||||
|
setTimeout(() => VoxtelesysPhone._removeCallBar(), 4000);
|
||||||
|
}
|
||||||
|
if (this.popup && this.popup.call_sid === data.call_sid) { clearTimeout(this._popupTimeout); this.popup.hide(); }
|
||||||
|
},
|
||||||
|
|
||||||
|
_showNumberPicker(numbers, frm) {
|
||||||
|
const d = new frappe.ui.Dialog({
|
||||||
|
title: __("Select Number to Call"),
|
||||||
|
fields: numbers.map((num, idx) => ({ fieldtype:"Button", fieldname:`num_${idx}`, label:num,
|
||||||
|
click: () => { d.hide(); VoxtelesysPhone.dialNumber(num, frm); } })),
|
||||||
|
});
|
||||||
|
d.show();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
window.VoxtelesysPhone = VoxtelesysPhone;
|
||||||
|
})();
|
||||||
Loading…
Reference in a new issue