diff --git a/clientside/api.py b/clientside/api.py index 475236b..2e41ec8 100644 --- a/clientside/api.py +++ b/clientside/api.py @@ -1,19 +1,60 @@ import frappe + def update_workspaces(): onehash_workspaces = frappe.get_all( - "Workspace", - filters={"name": ("in", ["OneHash Integrations", "OneHash Settings"])}, - fields=["name", "title", "icon", "indicator_color", "parent_page as parent", "public"], - ) + "Workspace", + filters={"name": ("in", ["OneHash Integrations", "OneHash Settings"])}, + fields=[ + "name", + "title", + "icon", + "indicator_color", + "parent_page as parent", + "public", + ], + ) erpnext_workspaces = frappe.get_all( - "Workspace", - filters={"name": ("in", ["ERPNext Integrations", "ERPNext Settings"])}, - fields=["name", "title", "icon", "indicator_color", "parent_page as parent", "public"], - ) + "Workspace", + filters={"name": ("in", ["ERPNext Integrations", "ERPNext Settings"])}, + fields=[ + "name", + "title", + "icon", + "indicator_color", + "parent_page as parent", + "public", + ], + ) if onehash_workspaces and erpnext_workspaces: for workspace in erpnext_workspaces: frappe.delete_doc("Workspace", workspace["name"], force=True) - frappe.db.commit() \ No newline at end of file + frappe.db.commit() + + update_workspace_shortcuts() + + +def update_workspace_shortcuts(): + workspace_shortcuts = frappe.get_all( + "Workspace Shortcut", fields=["name", "label", "url"] + ) + + workspace_shortcuts_to_remove = [] + for shortcut in workspace_shortcuts: + if shortcut["url"] and ( + shortcut["url"].startswith("https://frappe") + or shortcut["url"].startswith("https://erpnext") + ): + workspace_shortcuts_to_remove.append(shortcut) + elif shortcut["label"] == "Browse Apps": + workspace_shortcuts_to_remove.append(shortcut) + + if workspace_shortcuts_to_remove: + for shortcut in workspace_shortcuts_to_remove: + frappe.delete_doc("Workspace Shortcut", shortcut["name"]) + frappe.db.commit() + else: + print("No workspace shortcut to update.") + diff --git a/clientside/hooks.py b/clientside/hooks.py index 20c1fa0..3a9bfce 100644 --- a/clientside/hooks.py +++ b/clientside/hooks.py @@ -14,6 +14,7 @@ # include js, css files in header of desk.html # app_include_css = "" app_include_js = [ + "clientside.bundle.js", "assets/clientside/js/check_subscription.js", "/assets/clientside/js/support_widget.js", ] @@ -50,7 +51,7 @@ # "Role": "home_page" # } -after_migrate = ['clientside.api.update_workspaces'] +after_migrate = ["clientside.api.update_workspaces"] # Generators # ---------- diff --git a/clientside/install.py b/clientside/install.py index ce4d5d1..e89944a 100644 --- a/clientside/install.py +++ b/clientside/install.py @@ -8,7 +8,6 @@ def after_install(): create_role("OneHash Manager") update_workspace_title() hide_integrations() - update_workspace_shortcut() update_navbar_settings() add_custom_fields_to_communication() add_custom_fields_to_email_campaign() @@ -73,20 +72,6 @@ def hide_integrations(): print("Workspace 'Integrations' not found.") -def update_workspace_shortcut(): - workspace_shortcut_to_update = frappe.get_all( - "Workspace Shortcut", - filters={"label": "Browse Apps"}, - fields=["name", "label", "url"], - ) - if workspace_shortcut_to_update: - for shortcut in workspace_shortcut_to_update: - frappe.delete_doc("Workspace Shortcut", shortcut["name"]) - frappe.db.commit() - else: - print("No workspace shortcut with label 'Browse Apps' found.") - - def update_navbar_settings(): navbar_settings = frappe.get_single("Navbar Settings") diff --git a/clientside/public/js/clientside.bundle.js b/clientside/public/js/clientside.bundle.js new file mode 100644 index 0000000..7d6c766 --- /dev/null +++ b/clientside/public/js/clientside.bundle.js @@ -0,0 +1,3 @@ +import "./check_subscription"; +import "./support_widget"; +import "./whitelabel"; diff --git a/clientside/public/js/whitelabel.js b/clientside/public/js/whitelabel.js new file mode 100644 index 0000000..b8c0357 --- /dev/null +++ b/clientside/public/js/whitelabel.js @@ -0,0 +1,107 @@ +const LINK_MAP = { + "https://docs.erpnext.com//docs/user/manual/en/setting-up/articles/delete-submitted-document": + "https://chat.onehash.ai/hc/onehash-help-center/articles/1701752414-delete-submitted-document", + "https://docs.erpnext.com/docs/v14/user/manual/en/regional/india/generating_e_invoice#what-if-we-generate-e-waybill-before-the-e-invoice": + "https://chat.onehash.ai/hc/onehash-help-center/articles/1701664426-e_invoicing-under-gst#generating-irn", + "https://docs.erpnext.com/docs/user/manual/en/regional/india/gst-setup": + "https://chat.onehash.ai/hc/onehash-help-center/articles/1701753695-gst-setup", +}; + +const DOCS_FALLBACK = "https://chat.onehash.ai/hc/onehash-help-center/en/"; +const HOME_FALLBACK = "https://onehash.ai"; +const GITHUB_FALLBACK = "https://github.com/onehashai"; + +function whitelabelText(str) { + if (typeof str !== "string") return str; + return str + .replace(/\bERPNext\b/gi, "OneHash") + .replace(/\bFrappe\b/gi, "OneHash"); +} + +function whitelabelLink(url) { + if (!url) return url; + + if (LINK_MAP[url]) return LINK_MAP[url]; + + if (/https?:\/\/docs\.(erpnext|frappe)\./i.test(url)) return DOCS_FALLBACK; + + if (/https?:\/\/github\.com\/(erpnext|frappe)\//i.test(url)) + return GITHUB_FALLBACK; + + if (/https?:\/\/[^/]*(erpnext|frappe)[^/]*/i.test(url)) return HOME_FALLBACK; + + return url; +} + +function whitelabelHTML(html) { + if (typeof html !== "string") return html; + + html = html.replace( + /(href|src)=(["'])([^"']+)\2/gi, + (match, attr, quote, url) => { + return `${attr}=${quote}${whitelabelLink(url)}${quote}`; + }, + ); + + html = html.replace(/(?<=>|^)([^<]+)/g, (match) => whitelabelText(match)); + + return html; +} + +function whitelabelMessage(msg) { + if (typeof msg === "string") { + return whitelabelHTML(msg); + } + if (Array.isArray(msg)) { + return msg.map((item) => { + if (typeof item === "string") { + try { + const parsed = JSON.parse(item); + return JSON.stringify(whitelabelData(parsed)); + } catch { + return whitelabelHTML(item); + } + } + return typeof item === "object" ? whitelabelData(item) : item; + }); + } + return msg; +} + +function whitelabelData(data) { + if (!data) return data; + + if (data.title) { + data.title = whitelabelText(data.title); + } + + if (data.message !== undefined) { + data.message = whitelabelMessage(data.message); + } + + return data; +} + +const _msgprint = frappe.msgprint; + +frappe.msgprint = function (msg, title, is_minimizable) { + if (!msg) return; + + let data; + + if ($.isPlainObject(msg)) { + data = msg; + } else if (typeof msg === "string" && msg.trimStart().startsWith("{")) { + try { + data = JSON.parse(msg); + } catch { + data = { message: msg, title: title }; + } + } else { + data = { message: msg, title: title }; + } + + data = whitelabelData(data); + + _msgprint(data, data.title, is_minimizable); +};