diff --git a/voxtelesys_integration/public/js/lead.js b/voxtelesys_integration/public/js/lead.js
new file mode 100644
index 0000000..2e47b5b
--- /dev/null
+++ b/voxtelesys_integration/public/js/lead.js
@@ -0,0 +1,71 @@
+/**
+ * Lead form JS — click-to-call + SMS controls.
+ *
+ * Frappe's Phone fieldtype already renders a phone icon that routes through
+ * frappe.phone_call.handler (which we override globally), so a separate
+ * button isn't strictly required — but we add an explicit "Call / SMS" group
+ * in the menu for discoverability, mirroring the HD Ticket UX.
+ */
+frappe.ui.form.on("Lead", {
+ refresh(frm) {
+ if (!frappe.boot.voxtelesys || !frappe.boot.voxtelesys.enabled) return;
+ if (frm.is_new()) return;
+
+ const phone = frm.doc.mobile_no || frm.doc.phone || "";
+ if (!phone) return;
+
+ frm.add_custom_button(
+ ` ${__("Call {0}", [phone])}`,
+ () => {
+ frappe.call({
+ method: "voxtelesys_integration.api.voxtelesys.make_outbound_call",
+ args: {
+ to: phone,
+ reference_doctype: frm.doctype,
+ reference_name: frm.docname,
+ },
+ freeze: true,
+ freeze_message: __("Dialing {0}…", [phone]),
+ });
+ },
+ __("Voxtelesys")
+ );
+
+ if (frappe.boot.voxtelesys.sms_enabled !== false) {
+ frm.add_custom_button(
+ ` ${__("Send SMS")}`,
+ () => {
+ const d = new frappe.ui.Dialog({
+ title: __("Send SMS"),
+ fields: [
+ { fieldname: "to", fieldtype: "Data", label: __("To"), default: phone, reqd: 1 },
+ { fieldname: "body", fieldtype: "Small Text", label: __("Message"), reqd: 1 },
+ ],
+ primary_action_label: __("Send"),
+ primary_action(values) {
+ frappe.call({
+ method: "voxtelesys_integration.api.sms.send_sms",
+ args: {
+ to: values.to,
+ body: values.body,
+ reference_doctype: frm.doctype,
+ reference_name: frm.docname,
+ },
+ freeze: true,
+ freeze_message: __("Sending SMS…"),
+ callback(r) {
+ if (r.message && r.message.ok) {
+ d.hide();
+ frappe.show_alert({ message: __("SMS sent"), indicator: "green" });
+ }
+ },
+ });
+ },
+ });
+ d.show();
+ },
+ __("Voxtelesys")
+ );
+ }
+ },
+});