Skip to content

Commit

Permalink
feat: custom vue page first cut!
Browse files Browse the repository at this point in the history
  • Loading branch information
NagariaHussain committed Sep 23, 2023
1 parent 94d4035 commit 7e4d7a8
Show file tree
Hide file tree
Showing 2 changed files with 190 additions and 2 deletions.
110 changes: 109 additions & 1 deletion doppio/commands/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
import os
import click
import frappe
import subprocess
from .spa_generator import SPAGenerator
from frappe.commands import get_site, pass_context
from frappe import get_module_path, scrub
from .utils import add_build_command_to_package_json, add_routing_rule_to_hooks
from .boilerplates import (
CUSTOM_PAGE_APP_COMPONENT_BOILERPLATE,
CUSTOM_PAGE_JS_TEMPLATE,
CUSTOM_PAGE_JS_BUNDLE_TEMPLATE,
)
from pathlib import Path


@click.command("add-spa")
Expand Down Expand Up @@ -62,4 +72,102 @@ def add_frappe_ui_starter(name, app):
add_routing_rule_to_hooks(app, name)


commands = [generate_spa, add_frappe_ui]
@click.command("add-custom-page")
@click.option("--page-name", prompt="Custom Page Name")
@click.option("--app", prompt="App Name")
@click.option(
"--starter",
type=click.Choice(["vue", "simple"]),
default="vue",
prompt="Which framework do you want to use?",
help="Setup a custom page with the framework of your choice",
)
@pass_context
def add_custom_page(context, app, page_name, starter):
site = get_site(context)
frappe.init(site=site)

try:
frappe.connect()
setup_custom_page(site, app, page_name, starter)
finally:
frappe.destroy()


def setup_custom_page(site, app_name, page_name, starter):
if not frappe.conf.developer_mode:
click.echo("Please enable developer mode to add custom page")
return

module_name = frappe.get_all(
"Module Def",
filters={"app_name": app_name},
limit=1,
pluck="name",
order_by="creation",
)[0]

# create page doc
page = frappe.new_doc("Page")
page.module = module_name
page.standard = "Yes"
page.page_name = page_name
page.title = page_name
page.insert()
frappe.db.commit()

if starter == "vue":
setup_vue_custom_page_starter(page, app_name)

print("Opening", page.title, "in browser...")
page_url = f"{frappe.utils.get_site_url(site)}/app/{page.name}"
click.launch(page_url)


def setup_vue_custom_page_starter(page_doc, app_name):
context = {
"pascal_cased_name": page_doc.name.replace("-", " ").title().replace(" ", ""),
"scrubbed_name": page_doc.name.replace("-", "_"),
"page_title": page_doc.title,
"page_name": page_doc.name,
}

custom_page_js_file_content = frappe.render_template(CUSTOM_PAGE_JS_TEMPLATE, context)
custom_page_js_bundle_file_content = frappe.render_template(
CUSTOM_PAGE_JS_BUNDLE_TEMPLATE, context
)

js_file_path = os.path.join(
frappe.get_module_path(app_name),
scrub(page_doc.doctype),
scrub(page_doc.name),
scrub(page_doc.name) + ".js",
)
js_bundle_file_path = os.path.join(
frappe.get_app_path(app_name),
"public",
"js",
scrub(page_doc.name),
scrub(page_doc.name) + ".bundle.js",
)

with Path(js_file_path).open("w") as f:
f.write(custom_page_js_file_content)

# create dir if not exists
Path(js_bundle_file_path).parent.mkdir(parents=True, exist_ok=True)
with Path(js_bundle_file_path).open("w") as f:
f.write(custom_page_js_bundle_file_content)

app_component_path = os.path.join(
frappe.get_app_path(app_name), "public", "js", scrub(page_doc.name), "App.vue"
)

with Path(app_component_path).open("w") as f:
f.write(CUSTOM_PAGE_APP_COMPONENT_BOILERPLATE)

from frappe.build import bundle
bundle("development", apps=app_name)


commands = [generate_spa, add_frappe_ui, add_custom_page]
82 changes: 81 additions & 1 deletion doppio/commands/boilerplates.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,4 +271,84 @@
}
export default App
"""
"""

CUSTOM_PAGE_JS_TEMPLATE = """frappe.pages["{{ page_name }}"].on_page_load = function (wrapper) {
frappe.ui.make_app_page({
parent: wrapper,
title: __("{{ page_title }}"),
single_column: true,
});
// hot reload in development
if (frappe.boot.developer_mode) {
frappe.hot_update = frappe.hot_update || [];
frappe.hot_update.push(() => load_custom_page(wrapper));
}
};
frappe.pages["{{ page_name }}"].on_page_show = function (wrapper) {
load_custom_page(wrapper);
};
function load_custom_page(wrapper) {
let $parent = $(wrapper).find(".layout-main-section");
$parent.empty();
frappe.require("{{ scrubbed_name }}.bundle.js").then(() => {
frappe.{{ scrubbed_name }} = new frappe.ui.{{ pascal_cased_name }}({
wrapper: $parent,
page: wrapper.page,
});
});
}
"""

CUSTOM_PAGE_JS_BUNDLE_TEMPLATE = """import { createApp } from "vue";
import App from "./App.vue";
class {{ pascal_cased_name }} {
constructor({ page, wrapper }) {
this.$wrapper = $(wrapper);
this.page = page;
this.init();
}
init() {
this.setup_page_actions();
this.setup_app();
}
setup_page_actions() {
// setup page actions
this.primary_btn = this.page.set_primary_action(__("Print Message"), () =>
frappe.msgprint("Hello Custom Page!")
);
}
setup_app() {
// create a vue instance
let app = createApp(App);
// mount the app
this.${{ scrubbed_name }} = app.mount(this.$wrapper.get(0));
}
}
frappe.provide("frappe.ui");
frappe.ui.{{ pascal_cased_name }} = {{ pascal_cased_name }};
export default {{ pascal_cased_name }};
"""

CUSTOM_PAGE_APP_COMPONENT_BOILERPLATE = """<script setup>
import { ref } from "vue";
const dynamicMessage = ref("Hello from App.vue");
</script>
<template>
<div>
<h1>{{ dynamicMessage }}</h1>
</div>
</template>
"""

0 comments on commit 7e4d7a8

Please sign in to comment.