Add voxtelesys_integration/public/js/voxtelesys_call_popup.js

This commit is contained in:
Zac Gaetano 2026-05-11 08:08:58 -04:00
parent babc12e6c1
commit bad09d7981

View 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;
})();