diff --git a/.gitignore b/.gitignore index ba04025..3aafa7b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,7 @@ *.swp tags node_modules -__pycache__ \ No newline at end of file +__pycache__ + +*dist/ +.vscode/ \ No newline at end of file diff --git a/README.md b/README.md index c27c9d8..1ee878d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -### Custom Ui +### Custom UI Custom UI diff --git a/custom_ui/commands.py b/custom_ui/commands.py new file mode 100644 index 0000000..79e2273 --- /dev/null +++ b/custom_ui/commands.py @@ -0,0 +1,40 @@ +import click +import os +import subprocess +import frappe +from custom_ui.utils import create_module + +@click.command("build-frontend") +def build_frontend(): + app_package_path = frappe.get_app_path("custom_ui") + app_root = os.path.dirname(app_package_path) + + candidates = [ + os.path.join(app_root, 'frontend'), + os.path.join(app_package_path, 'frontend') + ] + + frontend_path = None + for p in candidates: + if os.path.exists(p): + frontend_path = p + break + + if frontend_path: + click.echo("\n📦 Building frontend for custom_ui...\n") + try: + subprocess.check_call(["npm", "install"], cwd=frontend_path) + subprocess.check_call(["npm", "run", "build"], cwd=frontend_path) + click.echo("\n✅ Frontend build completed successfully.\n") + except subprocess.CalledProcessError as e: + click.echo(f"\n❌ Frontend build failed: {e}\n") + else: + frappe.log_error(message="No frontend directory found for custom_ui", title="Frontend Build Skipped") + click.echo(f"\n⚠️ Frontend directory does not exist. Skipping build. Path was {frontend_path}\n") + +@click.command("create-module") +def create_module_command(): + create_module() + click.echo("✅ Custom UI module created or already exists.") + +commands = [build_frontend, create_module_command] \ No newline at end of file diff --git a/custom_ui/config/desktop.py b/custom_ui/config/desktop.py new file mode 100644 index 0000000..9c04d09 --- /dev/null +++ b/custom_ui/config/desktop.py @@ -0,0 +1,8 @@ +from frappe import _ + +def get_data(): + return [{ + "module_name": "Custom UI", + "type": "module", + "label": _("Custom UI") + }] \ No newline at end of file diff --git a/custom_ui/custom_ui/page/__init__.py b/custom_ui/custom_ui/page/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/custom_ui/custom_ui/page/custom_ui/__init__.py b/custom_ui/custom_ui/page/custom_ui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/custom_ui/custom_ui/page/custom_ui/custom_ui.html b/custom_ui/custom_ui/page/custom_ui/custom_ui.html new file mode 100644 index 0000000..7b3a836 --- /dev/null +++ b/custom_ui/custom_ui/page/custom_ui/custom_ui.html @@ -0,0 +1,6 @@ +
+{% if bundle_path %} + +{% else %} +Bundle path is not available.
+{% endif %} diff --git a/custom_ui/custom_ui/page/custom_ui/custom_ui.js b/custom_ui/custom_ui/page/custom_ui/custom_ui.js new file mode 100644 index 0000000..d4ed85e --- /dev/null +++ b/custom_ui/custom_ui/page/custom_ui/custom_ui.js @@ -0,0 +1,11 @@ +frappe.pages["custom_ui"].on_page_load = function (wrapper) { + $(wrapper).html(''); + console.log("App root div created"); + + const script = document.createElement("script"); + + script.src = "/assets/custom_ui/dist/assets/custom_ui.bundle.js"; + script.type = "module"; + document.body.appendChild(script); + console.log("Custom UI script loaded:", script.src); +}; diff --git a/custom_ui/custom_ui/page/custom_ui/custom_ui.json b/custom_ui/custom_ui/page/custom_ui/custom_ui.json new file mode 100644 index 0000000..f88a6f6 --- /dev/null +++ b/custom_ui/custom_ui/page/custom_ui/custom_ui.json @@ -0,0 +1,10 @@ +{ + "name": "custom_ui", + "title": "Custom UI", + "doctype": "Page", + "module": "Custom UI", + "standard": "Yes", + "type": "Single", + "content": "", + "icon": "octicon octicon-screen-full" +} diff --git a/custom_ui/custom_ui/page/custom_ui/custom_ui.py b/custom_ui/custom_ui/page/custom_ui/custom_ui.py new file mode 100644 index 0000000..f385174 --- /dev/null +++ b/custom_ui/custom_ui/page/custom_ui/custom_ui.py @@ -0,0 +1,17 @@ +import frappe +from custom_ui.utils import get_manifest + +def get_context(context): + frappe.throw("Loading custom_ui context...") + print("Loading custom_ui context with manifest...") + manifest = get_manifest() + + context.template = "custom_ui/custom_ui/page/custom_ui/custom_ui.html" + print("Manifest:", manifest) + if manifest: + js_file = manifest["src/main.js"]["file"] + context.bundle_path = f"/assets/custom_ui/dist/{js_file}" + else: + print("ERROR: Manifest is missing or invalid.") + frappe.log_error(message="Manifest file is missing or invalid for custom_ui", title="Context Load Failed") + context.bundle_path = "" \ No newline at end of file diff --git a/custom_ui/hooks.py b/custom_ui/hooks.py index 6173390..9285381 100644 --- a/custom_ui/hooks.py +++ b/custom_ui/hooks.py @@ -1,10 +1,31 @@ +from . import commands + app_name = "custom_ui" -app_title = "Custom Ui" +app_title = "Custom UI" app_publisher = "Shiloh Code LLC" app_description = "Custom UI" app_email = "casey@shilohcode.com" app_license = "mit" +after_install = "custom_ui.install.after_install" +after_migrate = "custom_ui.install.after_migrate" +# on_session_creation = "custom_ui.utils.on_login_redirect" +# on_login = "custom_ui.utils.on_login_redirect" +# page_js = { +# "custom_ui": "custom_ui/custom_ui/page/custom_ui/custom_ui.js" +# } + +# app_include_js = ["/assets/custom_ui/dist/assets/main.js"] + +add_to_apps_screen = [ + { + "name": "custom_ui", + "logo": "/assets/custom_ui/logo.png", + "title": "Custom UI", + "route": "/app/custom_ui", + # "has_permission": "custom_ui.api.permission.has_app_permission" + } +] # Apps # ------------------ diff --git a/custom_ui/install.py b/custom_ui/install.py new file mode 100644 index 0000000..57b0568 --- /dev/null +++ b/custom_ui/install.py @@ -0,0 +1,46 @@ +import os +import subprocess +import frappe +from .utils import create_module + +def after_install(): + create_module() + build_frontend() + +def after_migrate(): + build_frontend() + +def build_frontend(): + app_package_path = frappe.get_app_path("custom_ui") + app_root = os.path.dirname(app_package_path) + candidates = [ + os.path.join(app_root, "frontend"), + os.path.join(app_package_path, "frontend") + ] + + frontend_path = None + for p in candidates: + if os.path.exists(p): + frontend_path = p + break + + if not frontend_path: + frappe.log_error(message="No frontend directory found for custom_ui", title="Frontend Build Skipped") + print(f"⚠️ Frontend directory does not exist. Skipping build. Path was {frontend_path}") + return + + dist_path = os.path.join(app_root, "custom_ui", "public", "dist") + should_build = True + if os.path.exists(dist_path) and os.listdir(dist_path): + print("ℹ️ Frontend already built. Skipping rebuild.") + should_build = False + + if should_build: + print("\n📦 Building frontend for custom_ui...\n") + try: + subprocess.check_call(["npm", "install"], cwd=frontend_path) + subprocess.check_call(["npm", "run", "build"], cwd=frontend_path) + print("\n✅ Frontend build completed successfully.\n") + except subprocess.CalledProcessError as e: + frappe.log_error(message=str(e), title="Frontend Build Failed") + print(f"\n❌ Frontend build failed: {e}\n") \ No newline at end of file diff --git a/custom_ui/modules.txt b/custom_ui/modules.txt index 725d690..093d28c 100644 --- a/custom_ui/modules.txt +++ b/custom_ui/modules.txt @@ -1 +1 @@ -Custom Ui \ No newline at end of file +Custom UI \ No newline at end of file diff --git a/custom_ui/public/js/custom_ui_loader.js b/custom_ui/public/js/custom_ui_loader.js new file mode 100644 index 0000000..1a6b7f9 --- /dev/null +++ b/custom_ui/public/js/custom_ui_loader.js @@ -0,0 +1,20 @@ +frappe.pages["custom-ui"].on_page_load = async (wrapper) => { + const page = frappe.ui.make_app_page({ + parent: wrapper, + title: "Custom UI", + single_column: true, + }); + + const $main = $(wrapper).find(".layout-main-section"); + $main.empty(); + + const manifestUrl = "/assets/custom_ui/dist/.vite/manifest.json"; + const manifest = await (await fetch(manifestUrl)).json(); + + const mainJs = `/assets/custom_ui/dist/${manifest["src/main.js"]["file"]}`; + + const script = document.createElement("script"); + script.type = "module"; + script.src = mainJs; + document.body.appendChild(script); +}; diff --git a/custom_ui/utils.py b/custom_ui/utils.py new file mode 100644 index 0000000..959cf34 --- /dev/null +++ b/custom_ui/utils.py @@ -0,0 +1,37 @@ +import frappe, os, json + +def get_manifest(): + app_path = frappe.get_app_path("custom_ui") + manifest_path = os.path.join(app_path, "custom_ui","public", "dist", ".vite", "manifest.json") + if not os.path.exists(manifest_path): + frappe.log_error(message="Manifest file not found for custom_ui", title="Manifest Load Failed") + return {} + with open(manifest_path) as f: + return json.load(f) + +def create_module(): + print("Creating Custom UI module if it does not exist...") + + mod_name = "Custom UI" + if frappe.db.exists("Module Def", mod_name): + mod = frappe.get_doc("Module Def", mod_name) + mod.show_in_sidebar = 1 # make sure it’s visible + mod.save(ignore_permissions=True) + print(f"Module '{mod_name}' updated with show_in_sidebar=1") + else: + frappe.get_doc({ + "doctype": "Module Def", + "module_name": mod_name, + "app_name": "custom_ui", + "color": "#1976d2", + "icon": "octicon octicon-code", + "show_in_sidebar": 1 + }).insert(ignore_permissions=True) + print(f"Module '{mod_name}' created") + + frappe.db.commit() + print("Custom UI module creation process completed.") + +def on_login_redirect(login_manager): + frappe.local.response["type"] = "redirect" + frappe.local.response["location"] = "/app/custom-ui" \ No newline at end of file diff --git a/custom_ui/www/__init__.py b/custom_ui/www/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..1511959 --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,5 @@ +# Vue 3 + Vite + +This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 ` +