From bad09d7981e2243292aaea55111a7f9158be7d97 Mon Sep 17 00:00:00 2001 From: Zac Gaetano Date: Mon, 11 May 2026 08:08:58 -0400 Subject: [PATCH] Add voxtelesys_integration/public/js/voxtelesys_call_popup.js --- .../public/js/voxtelesys_call_popup.js | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 voxtelesys_integration/public/js/voxtelesys_call_popup.js diff --git a/voxtelesys_integration/public/js/voxtelesys_call_popup.js b/voxtelesys_integration/public/js/voxtelesys_call_popup.js new file mode 100644 index 0000000..dfa6c57 --- /dev/null +++ b/voxtelesys_integration/public/js/voxtelesys_call_popup.js @@ -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: ` +
+
📞
+
${frappe.utils.escape_html(data.from || "Unknown")}
+ +
` }], + 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(``); + 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 = `
+ ${direction === "Outbound" ? "📤" : "📥"} + ${frappe.utils.escape_html(number)} + Connecting… + + +
`; + 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; +})(); \ No newline at end of file