Skip to content

Commit

Permalink
refactor: custom page => desk page
Browse files Browse the repository at this point in the history
  • Loading branch information
NagariaHussain committed Nov 24, 2023
1 parent ad17c05 commit dcb63aa
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 85 deletions.
6 changes: 6 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter"
},
"python.formatting.provider": "none"
}
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,12 @@ If you want to add a [frappe-ui](https://github.com/frappe/frappe-ui) starter pr
bench add-frappe-ui
```

## Creating Custom Pages
## Creating Desk Pages

If you want to setup Vue 3 or React powered custom desk pages, you can do that with just a single command:

```bash
bench --site <site-name> add-custom-page --app <app-name>
bench --site <site-name> add-desk-page --app <app-name>
```

Follow the prompt to select the framework of your choice and **everything will be setup for you auto-magically**! Once the setup is done, the page will be opened up in the browser.
Expand Down
102 changes: 52 additions & 50 deletions doppio/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,85 +4,87 @@
from .spa_generator import SPAGenerator
from frappe.commands import get_site, pass_context
from .utils import add_build_command_to_package_json, add_routing_rule_to_hooks
from .custom_page import setup_custom_page
from .desk_page import setup_desk_page


@click.command("add-spa")
@click.option("--name", default="dashboard", prompt="Dashboard Name")
@click.option("--app", prompt="App Name")
@click.option(
"--framework",
type=click.Choice(["vue", "react"]),
default="vue",
prompt="Which framework do you want to use?",
help="The framework to use for the SPA",
"--framework",
type=click.Choice(["vue", "react"]),
default="vue",
prompt="Which framework do you want to use?",
help="The framework to use for the SPA",
)
@click.option(
"--typescript",
default=False,
prompt="Configure TypeScript?",
is_flag=True,
help="Configure with TypeScript",
"--typescript",
default=False,
prompt="Configure TypeScript?",
is_flag=True,
help="Configure with TypeScript",
)
@click.option(
"--tailwindcss", default=False, is_flag=True, help="Configure tailwindCSS"
"--tailwindcss", default=False, is_flag=True, help="Configure tailwindCSS"
)
def generate_spa(framework, name, app, typescript, tailwindcss):
if not app:
click.echo("Please provide an app with --app")
return
generator = SPAGenerator(framework, name, app, tailwindcss, typescript)
generator.generate_spa()
if not app:
click.echo("Please provide an app with --app")
return
generator = SPAGenerator(framework, name, app, tailwindcss, typescript)
generator.generate_spa()


@click.command("add-frappe-ui")
@click.option("--name", default="frontend", prompt="Dashboard Name")
@click.option("--app", prompt="App Name")
def add_frappe_ui(name, app):
if not app:
click.echo("Please provide an app with --app")
return
if not app:
click.echo("Please provide an app with --app")
return

click.echo(f"Adding Frappe UI starter to {app}...")
add_frappe_ui_starter(name, app)
click.echo(f"Adding Frappe UI starter to {app}...")
add_frappe_ui_starter(name, app)

click.echo(
f"🖥️ You can start the dev server by running 'yarn dev' in apps/{app}/{name}"
)
click.echo("📄 Docs: https://frappeui.com")
click.echo(
f"🖥️ You can start the dev server by running 'yarn dev' in apps/{app}/{name}"
)
click.echo("📄 Docs: https://frappeui.com")


def add_frappe_ui_starter(name, app):
from pathlib import Path
from pathlib import Path

subprocess.run(
["npx", "degit", "NagariaHussain/doppio_frappeui_starter", name],
cwd=Path("../apps", app),
)
subprocess.run(["yarn"], cwd=Path("../apps", app, name))
subprocess.run(
["npx", "degit", "NagariaHussain/doppio_frappeui_starter", name],
cwd=Path("../apps", app),
)
subprocess.run(["yarn"], cwd=Path("../apps", app, name))

add_build_command_to_package_json(app, name)
add_routing_rule_to_hooks(app, name)
add_build_command_to_package_json(app, name)
add_routing_rule_to_hooks(app, name)


@click.command("add-custom-page")
@click.option("--page-name", prompt="Custom Page Name")
@click.command("add-desk-page")
@click.option("--page-name", prompt="Page Name")
@click.option("--app", prompt="App Name")
@click.option(
"--starter",
type=click.Choice(["vue", "react"]),
default="vue",
prompt="Which framework do you want to use?",
help="Setup a custom page with the framework of your choice",
"--starter",
type=click.Choice(["vue", "react"]),
default="vue",
prompt="Which framework do you want to use?",
help="Setup a desk 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)
def add_desk_page(context, app, page_name, starter):
site = get_site(context)
frappe.init(site=site)

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

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

commands = [generate_spa, add_frappe_ui, add_custom_page]
commands = [generate_spa, add_frappe_ui, add_desk_page]
18 changes: 9 additions & 9 deletions doppio/commands/boilerplates.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@
export default App
"""

CUSTOM_PAGE_JS_TEMPLATE = """frappe.pages["{{ page_name }}"].on_page_load = function (wrapper) {
DESK_PAGE_JS_TEMPLATE = """frappe.pages["{{ page_name }}"].on_page_load = function (wrapper) {
frappe.ui.make_app_page({
parent: wrapper,
title: __("{{ page_title }}"),
Expand All @@ -282,10 +282,10 @@
};
frappe.pages["{{ page_name }}"].on_page_show = function (wrapper) {
load_custom_page(wrapper);
load_desk_page(wrapper);
};
function load_custom_page(wrapper) {
function load_desk_page(wrapper) {
let $parent = $(wrapper).find(".layout-main-section");
$parent.empty();
Expand All @@ -298,7 +298,7 @@
}
"""

CUSTOM_PAGE_JS_BUNDLE_TEMPLATE_VUE = """import { createApp } from "vue";
DESK_PAGE_JS_BUNDLE_TEMPLATE_VUE = """import { createApp } from "vue";
import App from "./App.vue";
Expand All @@ -318,7 +318,7 @@ class {{ pascal_cased_name }} {
setup_page_actions() {
// setup page actions
this.primary_btn = this.page.set_primary_action(__("Print Message"), () =>
frappe.msgprint("Hello Custom Page!")
frappe.msgprint("Hello My Page!")
);
}
Expand All @@ -335,7 +335,7 @@ class {{ pascal_cased_name }} {
export default {{ pascal_cased_name }};
"""

CUSTOM_PAGE_VUE_APP_COMPONENT_BOILERPLATE = """<script setup>
DESK_PAGE_VUE_APP_COMPONENT_BOILERPLATE = """<script setup>
import { ref } from "vue";
const dynamicMessage = ref("Hello from App.vue");
Expand All @@ -347,7 +347,7 @@ class {{ pascal_cased_name }} {
</div>
</template>"""

CUSTOM_PAGE_REACT_APP_COMPONENT_BOILERPLATE = """import * as React from "react";
DESK_PAGE_REACT_APP_COMPONENT_BOILERPLATE = """import * as React from "react";
export function App() {
const dynamicMessage = React.useState("Hello from App.jsx");
Expand All @@ -359,7 +359,7 @@ class {{ pascal_cased_name }} {
);
}"""

CUSTOM_PAGE_JSX_BUNDLE_TEMPLATE_REACT = """import * as React from "react";
DESK_PAGE_JSX_BUNDLE_TEMPLATE_REACT = """import * as React from "react";
import { App } from "./App";
import { createRoot } from "react-dom/client";
Expand All @@ -380,7 +380,7 @@ class {{ pascal_cased_name }} {
setup_page_actions() {
// setup page actions
this.primary_btn = this.page.set_primary_action(__("Print Message"), () =>
frappe.msgprint("Hello Custom Page!")
frappe.msgprint("Hello My Page!")
);
}
Expand Down
48 changes: 24 additions & 24 deletions doppio/commands/custom_page.py → doppio/commands/desk_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,37 @@
from pathlib import Path

from .boilerplates import (
CUSTOM_PAGE_JS_BUNDLE_TEMPLATE_VUE,
CUSTOM_PAGE_JS_TEMPLATE,
CUSTOM_PAGE_VUE_APP_COMPONENT_BOILERPLATE,
CUSTOM_PAGE_JSX_BUNDLE_TEMPLATE_REACT,
CUSTOM_PAGE_REACT_APP_COMPONENT_BOILERPLATE,
DESK_PAGE_JS_BUNDLE_TEMPLATE_VUE,
DESK_PAGE_JS_TEMPLATE,
DESK_PAGE_VUE_APP_COMPONENT_BOILERPLATE,
DESK_PAGE_JSX_BUNDLE_TEMPLATE_REACT,
DESK_PAGE_REACT_APP_COMPONENT_BOILERPLATE,
)


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

page = create_page_doc(page_name, app_name, site)

if starter == "vue":
setup_vue_custom_page_starter(page, app_name)
setup_vue_desk_page_starter(page, app_name)
elif starter == "react":
setup_react_custom_page_starter(page, app_name)
setup_react_desk_page_starter(page, app_name)
else:
click.echo("Please provide a valid starter")
return

launch_custom_page_in_browser(page, site)
launch_desk_page_in_browser(page, site)


def setup_vue_custom_page_starter(page_doc, app_name):
setup_custom_page_for_framework("vue", page_doc, app_name)
def setup_vue_desk_page_starter(page_doc, app_name):
setup_desk_page_for_framework("vue", page_doc, app_name)


def setup_react_custom_page_starter(page_doc, app_name):
def setup_react_desk_page_starter(page_doc, app_name):
# check if package.json exists in app directory
# if not, create package.json using npm init --yes
package_json_path = Path(frappe.get_app_path(app_name)) / "package.json"
Expand All @@ -50,10 +50,10 @@ def setup_react_custom_page_starter(page_doc, app_name):
["yarn", "add", "react", "react-dom"], cwd=frappe.get_app_path(app_name)
)

setup_custom_page_for_framework("react", page_doc, app_name)
setup_desk_page_for_framework("react", page_doc, app_name)


def setup_custom_page_for_framework(framework, page_doc, app_name):
def setup_desk_page_for_framework(framework, page_doc, app_name):
bundle_type = "js" if framework == "vue" else "jsx"
context = {
"pascal_cased_name": page_doc.name.replace("-", " ").title().replace(" ", ""),
Expand All @@ -63,13 +63,13 @@ def setup_custom_page_for_framework(framework, page_doc, app_name):
"bundle_type": bundle_type,
}

custom_page_js_file_content = frappe.render_template(
CUSTOM_PAGE_JS_TEMPLATE, context
desk_page_js_file_content = frappe.render_template(
DESK_PAGE_JS_TEMPLATE, context
)
custom_page_js_bundle_file_content = frappe.render_template(
CUSTOM_PAGE_JS_BUNDLE_TEMPLATE_VUE
desk_page_js_bundle_file_content = frappe.render_template(
DESK_PAGE_JS_BUNDLE_TEMPLATE_VUE
if framework == "vue"
else CUSTOM_PAGE_JSX_BUNDLE_TEMPLATE_REACT,
else DESK_PAGE_JSX_BUNDLE_TEMPLATE_REACT,
context,
)

Expand All @@ -90,12 +90,12 @@ def setup_custom_page_for_framework(framework, page_doc, app_name):
)

with Path(js_file_path).open("w") as f:
f.write(custom_page_js_file_content)
f.write(desk_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)
f.write(desk_page_js_bundle_file_content)

app_component_file_name = "App.vue" if framework == "vue" else "App.jsx"
app_component_path = os.path.join(
Expand All @@ -112,9 +112,9 @@ def setup_custom_page_for_framework(framework, page_doc, app_name):

app_component_template = None
if framework == "vue":
app_component_template = CUSTOM_PAGE_VUE_APP_COMPONENT_BOILERPLATE
app_component_template = DESK_PAGE_VUE_APP_COMPONENT_BOILERPLATE
else:
app_component_template = CUSTOM_PAGE_REACT_APP_COMPONENT_BOILERPLATE
app_component_template = DESK_PAGE_REACT_APP_COMPONENT_BOILERPLATE

with Path(app_component_path).open("w") as f:
app_component_template = frappe.render_template(app_component_template, {
Expand Down Expand Up @@ -157,7 +157,7 @@ def create_page_doc(page_name, app_name, site):
return page


def launch_custom_page_in_browser(page, site):
def launch_desk_page_in_browser(page, site):
click.echo(f"Opening {page.title} in browser...")
page_url = f"{frappe.utils.get_site_url(site)}/app/{page.name}"
click.launch(page_url)
Expand Down

0 comments on commit dcb63aa

Please sign in to comment.