From 7b34c5eb66509851e993df19335a5ebedd0f3055 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 1 May 2025 17:52:11 +0530 Subject: [PATCH] fix: load script and setup class instances --- crm/api/script.py | 17 ------ frontend/src/data/script.js | 110 ++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 17 deletions(-) delete mode 100644 crm/api/script.py create mode 100644 frontend/src/data/script.js diff --git a/crm/api/script.py b/crm/api/script.py deleted file mode 100644 index bfbfa405..00000000 --- a/crm/api/script.py +++ /dev/null @@ -1,17 +0,0 @@ -import frappe -from pypika import Criterion - - -@frappe.whitelist() -def get_scripts(doctype, view="Form"): - Script = frappe.qb.DocType("CRM Form Script") - query = ( - frappe.qb.from_(Script) - .select("*") - .where(Script.dt == doctype) - .where(Script.view == view) - .where(Script.enabled == 1) - ) - - scripts = query.run(as_dict=True) - return scripts diff --git a/frontend/src/data/script.js b/frontend/src/data/script.js new file mode 100644 index 00000000..3464ecfe --- /dev/null +++ b/frontend/src/data/script.js @@ -0,0 +1,110 @@ +import { globalStore } from '@/stores/global' +import { createToast } from '@/utils' +import { call, createListResource } from 'frappe-ui' +import { reactive } from 'vue' +import router from '@/router' + +const doctypeScripts = reactive({}) + +export function getScript(doctype, view = 'Form') { + const scripts = createListResource({ + doctype: 'CRM Form Script', + cache: ['Form Scripts', doctype, view], + fields: ['name', 'dt', 'view', 'script'], + filters: { view, dt: doctype, enabled: 1 }, + onSuccess: (_scripts) => { + for (let script of _scripts) { + if (!doctypeScripts[doctype]) { + doctypeScripts[doctype] = {} + } + doctypeScripts[doctype][script.name] = script || {} + } + }, + }) + + if (!doctypeScripts[doctype] && !scripts.loading) { + scripts.fetch() + } + + function setupScript(document, helpers = {}) { + let scripts = doctypeScripts[doctype] + if (!scripts) return null + + const { $dialog, $socket, makeCall } = globalStore() + + helpers.createDialog = $dialog + helpers.createToast = createToast + helpers.socket = $socket + helpers.router = router + helpers.call = call + + helpers.crm = { + makePhoneCall: makeCall, + } + + return setupMultipleFormControllers(scripts, document, helpers) + } + + function setupMultipleFormControllers(scriptStrings, document, helpers) { + const controllers = {} + + for (let scriptName in scriptStrings) { + let script = scriptStrings[scriptName]?.script + if (!script) continue + try { + const className = getClassName(script) + if (!className) throw new Error('No class found') + + const FormClass = evaluateFormClass(script, className, helpers) + controllers[className] = setupFormController(FormClass, document) + } catch (err) { + console.error('Failed to load form controller:', err) + } + } + + return controllers + } + + function setupFormController(FormClass, document) { + const controller = new FormClass() + + for (const key in document) { + if (document.hasOwnProperty(key)) { + controller[key] = document[key] + } + } + + controller.actions = (controller.actions || []).filter( + (action) => typeof action.condition !== 'function' || action.condition(), + ) + + return controller + } + + // utility function to setup a form controller + function getClassName(script) { + const match = script.match(/class\s+([A-Za-z0-9_]+)/) + return match ? match[1] : null + } + + function evaluateFormClass(script, className, helpers = {}) { + const helperKeys = Object.keys(helpers) + const helperValues = Object.values(helpers) + + const wrappedScript = ` + ${script} + return ${className}; + ` + + const FormClass = new Function(...helperKeys, wrappedScript)( + ...helperValues, + ) + return FormClass + } + + return { + scripts, + setupScript, + setupFormController, + } +}