From b8fbed0f667af953c5ead931e444416d751bdaed Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Sun, 11 Jul 2021 17:19:34 +0530 Subject: [PATCH 01/40] feat: New Print Format Builder - Print Format Builder Beta page - Add margin fields in Print Format - Using vuedraggable for drag and drop --- .../doctype/print_format/print_format.json | 39 +++- .../doctype/print_format/print_format.py | 4 + frappe/printing/page/print/print.js | 6 + .../print_format_builder.js | 18 +- .../print_format_builder.py | 9 +- .../print_format_builder_beta/__init__.py | 0 .../print_format_builder_beta.css | 3 + .../print_format_builder_beta.js | 31 +++ .../print_format_builder_beta.json | 22 ++ .../js/print_format_builder/PrintFormat.vue | 73 +++++++ .../PrintFormatBuilder.vue | 110 ++++++++++ .../PrintFormatControls.vue | 163 ++++++++++++++ .../PrintFormatSection.vue | 203 ++++++++++++++++++ .../print_format_builder.bundle.js | 31 +++ .../public/js/print_format_builder/utils.js | 100 +++++++++ package.json | 3 +- yarn.lock | 12 ++ 17 files changed, 815 insertions(+), 12 deletions(-) create mode 100644 frappe/printing/page/print_format_builder_beta/__init__.py create mode 100644 frappe/printing/page/print_format_builder_beta/print_format_builder_beta.css create mode 100644 frappe/printing/page/print_format_builder_beta/print_format_builder_beta.js create mode 100644 frappe/printing/page/print_format_builder_beta/print_format_builder_beta.json create mode 100644 frappe/public/js/print_format_builder/PrintFormat.vue create mode 100644 frappe/public/js/print_format_builder/PrintFormatBuilder.vue create mode 100644 frappe/public/js/print_format_builder/PrintFormatControls.vue create mode 100644 frappe/public/js/print_format_builder/PrintFormatSection.vue create mode 100644 frappe/public/js/print_format_builder/print_format_builder.bundle.js create mode 100644 frappe/public/js/print_format_builder/utils.js diff --git a/frappe/printing/doctype/print_format/print_format.json b/frappe/printing/doctype/print_format/print_format.json index 4032cef209..52220ed67c 100644 --- a/frappe/printing/doctype/print_format/print_format.json +++ b/frappe/printing/doctype/print_format/print_format.json @@ -19,6 +19,10 @@ "html", "raw_commands", "section_break_9", + "margin_top", + "margin_bottom", + "margin_left", + "margin_right", "align_labels_right", "show_section_headings", "line_breaks", @@ -31,7 +35,8 @@ "section_break_13", "print_format_help", "format_data", - "print_format_builder" + "print_format_builder", + "print_format_builder_beta" ], "fields": [ { @@ -205,13 +210,43 @@ "fieldname": "absolute_value", "fieldtype": "Check", "label": "Show Absolute Values" + }, + { + "default": "0", + "fieldname": "print_format_builder_beta", + "fieldtype": "Check", + "label": "Print Format Builder Beta" + }, + { + "default": "15", + "fieldname": "margin_top", + "fieldtype": "Float", + "label": "Margin Top" + }, + { + "default": "15", + "fieldname": "margin_bottom", + "fieldtype": "Float", + "label": "Margin Bottom" + }, + { + "default": "15", + "fieldname": "margin_left", + "fieldtype": "Float", + "label": "Margin Left" + }, + { + "default": "15", + "fieldname": "margin_right", + "fieldtype": "Float", + "label": "Margin Right" } ], "icon": "fa fa-print", "idx": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2021-03-01 15:25:46.578863", + "modified": "2021-07-11 11:53:52.028982", "modified_by": "Administrator", "module": "Printing", "name": "Print Format", diff --git a/frappe/printing/doctype/print_format/print_format.py b/frappe/printing/doctype/print_format/print_format.py index 878a864b38..2049c4105c 100644 --- a/frappe/printing/doctype/print_format/print_format.py +++ b/frappe/printing/doctype/print_format/print_format.py @@ -38,6 +38,10 @@ class PrintFormat(Document): def extract_images(self): from frappe.core.doctype.file.file import extract_images_from_html + + if self.print_format_builder_beta: + return + if self.format_data: data = json.loads(self.format_data) for df in data: diff --git a/frappe/printing/page/print/print.js b/frappe/printing/page/print/print.js index ca2a340661..225c06980e 100644 --- a/frappe/printing/page/print/print.js +++ b/frappe/printing/page/print/print.js @@ -258,6 +258,11 @@ frappe.ui.form.PrintView = class { fieldtype: 'Read Only', default: print_format.name || 'Standard', }, + { + label: __('Use the new Print Format Builder Beta'), + fieldname: 'beta', + fieldtype: 'Check' + }, ], (data) => { frappe.route_options = { @@ -265,6 +270,7 @@ frappe.ui.form.PrintView = class { doctype: this.frm.doctype, name: data.print_format_name, based_on: data.based_on, + beta: data.beta }; frappe.set_route('print-format-builder'); this.print_sel.val(data.print_format_name); diff --git a/frappe/printing/page/print_format_builder/print_format_builder.js b/frappe/printing/page/print_format_builder/print_format_builder.js index ca2a8bc378..e2d00bd53d 100644 --- a/frappe/printing/page/print_format_builder/print_format_builder.js +++ b/frappe/printing/page/print_format_builder/print_format_builder.js @@ -12,9 +12,9 @@ frappe.pages['print-format-builder'].on_page_show = function(wrapper) { }); } else if(frappe.route_options) { if(frappe.route_options.make_new) { - let { doctype, name, based_on } = frappe.route_options; + let { doctype, name, based_on, beta } = frappe.route_options; frappe.route_options = null; - frappe.print_format_builder.setup_new_print_format(doctype, name, based_on); + frappe.print_format_builder.setup_new_print_format(doctype, name, based_on, beta); } else { frappe.print_format_builder.print_format = frappe.route_options.doc; frappe.route_options = null; @@ -126,18 +126,22 @@ frappe.PrintFormatBuilder = class PrintFormatBuilder { }); } - setup_new_print_format(doctype, name, based_on) { + setup_new_print_format(doctype, name, based_on, beta) { frappe.call({ method: 'frappe.printing.page.print_format_builder.print_format_builder.create_custom_format', args: { doctype: doctype, name: name, - based_on: based_on + based_on: based_on, + beta: Boolean(beta) }, callback: (r) => { - if(!r.exc) { - if(r.message) { - this.print_format = r.message; + if(r.message) { + let print_format = r.message; + if (print_format.print_format_builder_beta) { + frappe.set_route('print-format-builder-beta', print_format.name); + } else { + this.print_format = print_format; this.refresh(); } } diff --git a/frappe/printing/page/print_format_builder/print_format_builder.py b/frappe/printing/page/print_format_builder/print_format_builder.py index d9f57762b0..fae564d3c3 100644 --- a/frappe/printing/page/print_format_builder/print_format_builder.py +++ b/frappe/printing/page/print_format_builder/print_format_builder.py @@ -1,11 +1,16 @@ import frappe @frappe.whitelist() -def create_custom_format(doctype, name, based_on='Standard'): +def create_custom_format(doctype, name, based_on='Standard', beta=False): doc = frappe.new_doc('Print Format') doc.doc_type = doctype doc.name = name - doc.print_format_builder = 1 + beta = frappe.parse_json(beta) + + if beta: + doc.print_format_builder_beta = 1 + else: + doc.print_format_builder = 1 doc.format_data = frappe.db.get_value('Print Format', based_on, 'format_data') \ if based_on != 'Standard' else None doc.insert() diff --git a/frappe/printing/page/print_format_builder_beta/__init__.py b/frappe/printing/page/print_format_builder_beta/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.css b/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.css new file mode 100644 index 0000000000..0bd8d9c0f3 --- /dev/null +++ b/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.css @@ -0,0 +1,3 @@ +.layout-main-section-wrapper { + margin-bottom: 0; +} diff --git a/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.js b/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.js new file mode 100644 index 0000000000..9004dd8b98 --- /dev/null +++ b/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.js @@ -0,0 +1,31 @@ +frappe.pages["print-format-builder-beta"].on_page_load = function(wrapper) { + var page = frappe.ui.make_app_page({ + parent: wrapper, + title: __("Print Format Builder"), + single_column: true + }); + + function load_print_format_builder_beta() { + let route = frappe.get_route(); + let $parent = $(wrapper).find(".layout-main-section"); + $parent.empty(); + + if (route.length > 1) { + frappe.require("print_format_builder.bundle.js").then(() => { + frappe.print_format_builder = new frappe.ui.PrintFormatBuilder({ + wrapper: $parent, + page, + print_format: route[1] + }); + }); + } + } + + load_print_format_builder_beta(); + + // hot reload in development + if (frappe.boot.developer_mode) { + frappe.hot_update = frappe.hot_update || []; + frappe.hot_update.push(load_print_format_builder_beta); + } +}; diff --git a/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.json b/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.json new file mode 100644 index 0000000000..a5b1288bc0 --- /dev/null +++ b/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.json @@ -0,0 +1,22 @@ +{ + "content": null, + "creation": "2021-07-10 12:22:16.138485", + "docstatus": 0, + "doctype": "Page", + "idx": 0, + "modified": "2021-07-10 12:22:16.138485", + "modified_by": "Administrator", + "module": "Printing", + "name": "print-format-builder-beta", + "owner": "Administrator", + "page_name": "Print Format Builder Beta", + "roles": [ + { + "role": "System Manager" + } + ], + "script": null, + "standard": "Yes", + "style": null, + "system_page": 0 +} \ No newline at end of file diff --git a/frappe/public/js/print_format_builder/PrintFormat.vue b/frappe/public/js/print_format_builder/PrintFormat.vue new file mode 100644 index 0000000000..9e8e2033a4 --- /dev/null +++ b/frappe/public/js/print_format_builder/PrintFormat.vue @@ -0,0 +1,73 @@ + + + + + \ No newline at end of file diff --git a/frappe/public/js/print_format_builder/PrintFormatBuilder.vue b/frappe/public/js/print_format_builder/PrintFormatBuilder.vue new file mode 100644 index 0000000000..88b70f8b1b --- /dev/null +++ b/frappe/public/js/print_format_builder/PrintFormatBuilder.vue @@ -0,0 +1,110 @@ + + + + + \ No newline at end of file diff --git a/frappe/public/js/print_format_builder/PrintFormatControls.vue b/frappe/public/js/print_format_builder/PrintFormatControls.vue new file mode 100644 index 0000000000..b4f452751a --- /dev/null +++ b/frappe/public/js/print_format_builder/PrintFormatControls.vue @@ -0,0 +1,163 @@ + + + + + \ No newline at end of file diff --git a/frappe/public/js/print_format_builder/PrintFormatSection.vue b/frappe/public/js/print_format_builder/PrintFormatSection.vue new file mode 100644 index 0000000000..e204c2053f --- /dev/null +++ b/frappe/public/js/print_format_builder/PrintFormatSection.vue @@ -0,0 +1,203 @@ + + + + + \ No newline at end of file diff --git a/frappe/public/js/print_format_builder/print_format_builder.bundle.js b/frappe/public/js/print_format_builder/print_format_builder.bundle.js new file mode 100644 index 0000000000..e364bfa29e --- /dev/null +++ b/frappe/public/js/print_format_builder/print_format_builder.bundle.js @@ -0,0 +1,31 @@ +import PrintFormatBuilderComponent from "./PrintFormatBuilder.vue"; + +class PrintFormatBuilder { + constructor({ wrapper, page, print_format }) { + this.$wrapper = $(wrapper); + this.page = page; + this.print_format = print_format; + this.page.set_title(__("Editing {0}", [this.print_format])); + this.page.set_primary_action(__("Save changes"), () => { + this.$component.save_changes(); + }); + this.page.set_secondary_action(__("Reset changes"), () => { + this.$component.reset_changes(); + }); + + let $vm = new Vue({ + el: this.$wrapper.get(0), + render: h => + h(PrintFormatBuilderComponent, { + props: { + print_format_name: print_format + } + }) + }); + this.$component = $vm.$children[0]; + } +} + +frappe.provide("frappe.ui"); +frappe.ui.PrintFormatBuilder = PrintFormatBuilder; +export default PrintFormatBuilder; diff --git a/frappe/public/js/print_format_builder/utils.js b/frappe/public/js/print_format_builder/utils.js new file mode 100644 index 0000000000..9428ca61f9 --- /dev/null +++ b/frappe/public/js/print_format_builder/utils.js @@ -0,0 +1,100 @@ +export function create_default_layout(meta) { + let layout = { + sections: [] + }; + + let section = null, + column = null; + + function set_column(df) { + if (!section) { + set_section(); + } + column = get_new_column(df); + section.columns.push(column); + } + + function set_section(df) { + section = get_new_section(df); + column = null; + layout.sections.push(section); + } + + function get_new_section(df) { + if (!df) { + df = { label: "" }; + } + return { + label: df.label || "", + columns: [] + }; + } + + function get_new_column(df) { + if (!df) { + df = { label: "" }; + } + return { + label: df.label || "", + fields: [] + }; + } + + for (let df of meta.fields) { + if (df.fieldname) { + // make a copy to avoid mutation bugs + df = JSON.parse(JSON.stringify(df)); + } else { + continue; + } + + if (df.fieldtype === "Section Break") { + set_section(df); + } else if (df.fieldtype === "Column Break") { + set_column(df); + } else if (df.label) { + if (!column) set_column(); + + if (!df.print_hide) { + let field = { + label: df.label, + fieldname: df.fieldname, + options: df.options + }; + + if (df.fieldtype === "Table") { + field.table_columns = get_table_columns(df); + } + + column.fields.push(field); + section.has_fields = true; + } + } + } + + // remove empty sections + layout.sections = layout.sections.filter(section => section.has_fields); + + return layout; +} + +export function get_table_columns(df) { + let table_columns = []; + let table_fields = frappe.get_meta(df.options).fields; + + for (let tf of table_fields) { + if ( + !in_list(["Section Break", "Column Break"], tf.fieldtype) && + !tf.print_hide && + df.label + ) { + table_columns.push({ + label: tf.label, + fieldname: tf.fieldname, + options: tf.options, + width: tf.width || 0 + }); + } + } + return table_columns; +} diff --git a/package.json b/package.json index 2283a44533..82a335c2ba 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,8 @@ "superagent": "^3.8.2", "touch": "^3.1.0", "vue": "2.6.12", - "vue-router": "^2.0.0" + "vue-router": "^2.0.0", + "vuedraggable": "^2.24.3" }, "devDependencies": { "chalk": "^2.3.2", diff --git a/yarn.lock b/yarn.lock index ee530d747b..cdabdb6565 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6911,6 +6911,11 @@ socket.io@^2.4.0: socket.io-client "2.4.0" socket.io-parser "~3.4.0" +sortablejs@1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.10.2.tgz#6e40364d913f98b85a14f6678f92b5c1221f5290" + integrity sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A== + sortablejs@^1.7.0: version "1.8.3" resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.8.3.tgz#5ae908ef96300966e95440a143340f5dd565a0df" @@ -7790,6 +7795,13 @@ vue@2.6.12: resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.12.tgz#f5ebd4fa6bd2869403e29a896aed4904456c9123" integrity sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg== +vuedraggable@^2.24.3: + version "2.24.3" + resolved "https://registry.yarnpkg.com/vuedraggable/-/vuedraggable-2.24.3.tgz#43c93849b746a24ce503e123d5b259c701ba0d19" + integrity sha512-6/HDXi92GzB+Hcs9fC6PAAozK1RLt1ewPTLjK0anTYguXLAeySDmcnqE8IC0xa7shvSzRjQXq3/+dsZ7ETGF3g== + dependencies: + sortablejs "1.10.2" + wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" From f58254db7870d0d95f85636a3b81508c4ee37e09 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Sun, 11 Jul 2021 17:21:06 +0530 Subject: [PATCH 02/40] fix: poor man's hot reload --- esbuild/esbuild.js | 41 ++++++++++--------- .../build_events/build_events.bundle.js | 28 +++++++++++++ frappe/sessions.py | 4 ++ 3 files changed, 54 insertions(+), 19 deletions(-) diff --git a/esbuild/esbuild.js b/esbuild/esbuild.js index 9074beae06..442846c73f 100644 --- a/esbuild/esbuild.js +++ b/esbuild/esbuild.js @@ -280,10 +280,24 @@ function get_watch_config() { assets_json, prev_assets_json } = await write_assets_json(result.metafile); + + let changed_files; if (prev_assets_json) { - log_rebuilt_assets(prev_assets_json, assets_json); + changed_files = get_rebuilt_assets( + prev_assets_json, + assets_json + ); + + let timestamp = new Date().toLocaleTimeString(); + let message = `${timestamp}: Compiled ${changed_files.length} files...`; + log(chalk.yellow(message)); + for (let filepath of changed_files) { + let filename = path.basename(filepath); + log(" " + filename); + } + log(); } - notify_redis({ success: true }); + notify_redis({ success: true, changed_files }); } } }; @@ -453,7 +467,7 @@ function run_build_command_for_apps(apps) { process.chdir(cwd); } -async function notify_redis({ error, success }) { +async function notify_redis({ error, success, changed_files }) { // notify redis which in turns tells socketio to publish this to browser let subscriber = get_redis_subscriber("redis_socketio"); subscriber.on("error", _ => { @@ -475,7 +489,8 @@ async function notify_redis({ error, success }) { } if (success) { payload = { - success: true + success: true, + changed_files }; } @@ -505,7 +520,7 @@ function open_in_editor() { subscriber.subscribe("open_in_editor"); } -function log_rebuilt_assets(prev_assets, new_assets) { +function get_rebuilt_assets(prev_assets, new_assets) { let added_files = []; let old_files = Object.values(prev_assets); let new_files = Object.values(new_assets); @@ -515,17 +530,5 @@ function log_rebuilt_assets(prev_assets, new_assets) { added_files.push(filepath); } } - - log( - chalk.yellow( - `${new Date().toLocaleTimeString()}: Compiled ${ - added_files.length - } files...` - ) - ); - for (let filepath of added_files) { - let filename = path.basename(filepath); - log(" " + filename); - } - log(); -} \ No newline at end of file + return added_files; +} diff --git a/frappe/public/js/frappe/build_events/build_events.bundle.js b/frappe/public/js/frappe/build_events/build_events.bundle.js index 6c8986af3f..6180ccbd66 100644 --- a/frappe/public/js/frappe/build_events/build_events.bundle.js +++ b/frappe/public/js/frappe/build_events/build_events.bundle.js @@ -7,6 +7,34 @@ let error = null; frappe.realtime.on("build_event", data => { if (data.success) { + // remove executed cache for rebuilt files + let changed_files = data.changed_files; + if (Array.isArray(changed_files)) { + for (let file of changed_files) { + if (file.includes(".bundle.")) { + let parts = file.split(".bundle."); + if (parts.length === 2) { + let filename = parts[0].split("/").slice(-1)[0]; + + frappe.assets.executed_ = frappe.assets.executed_.filter( + asset => !asset.includes(`${filename}.bundle`) + ); + } + } + } + } + // update assets json + frappe.call("frappe.sessions.get_boot_assets_json").then(r => { + if (r.message) { + frappe.boot.assets_json = r.message; + + if (frappe.hot_update) { + frappe.hot_update.forEach(callback => { + callback(); + }); + } + } + }); show_build_success(data); } else if (data.error) { show_build_error(data); diff --git a/frappe/sessions.py b/frappe/sessions.py index ce104968ad..8a8355ec75 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -159,6 +159,10 @@ def get(): return bootinfo +@frappe.whitelist() +def get_boot_assets_json(): + return get_assets_json() + def get_csrf_token(): if not frappe.local.session.data.csrf_token: generate_csrf_token() From f536a1ff91db91397ff09d2e996014c09ff80aba Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Sun, 15 Aug 2021 16:10:04 +0530 Subject: [PATCH 03/40] fix: More features - Show selection dialog if Print Format not selected - Field component - Common store - Edit Field label inline - Configure table columns - Edit Custom HTML - Preview --- .../print_format_builder_beta.js | 20 + .../print_format_builder/ConfigureColumns.vue | 86 +++++ .../public/js/print_format_builder/Field.vue | 345 ++++++++++++++++++ .../js/print_format_builder/PrintFormat.vue | 21 +- .../PrintFormatBuilder.vue | 94 +---- .../PrintFormatControls.vue | 61 +++- .../PrintFormatSection.vue | 62 +--- .../print_format_builder.bundle.js | 55 ++- .../public/js/print_format_builder/store.js | 125 +++++++ .../public/js/print_format_builder/utils.js | 22 +- 10 files changed, 732 insertions(+), 159 deletions(-) create mode 100644 frappe/public/js/print_format_builder/ConfigureColumns.vue create mode 100644 frappe/public/js/print_format_builder/Field.vue create mode 100644 frappe/public/js/print_format_builder/store.js diff --git a/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.js b/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.js index 9004dd8b98..b40e8c6f97 100644 --- a/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.js +++ b/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.js @@ -18,6 +18,26 @@ frappe.pages["print-format-builder-beta"].on_page_load = function(wrapper) { print_format: route[1] }); }); + } else { + let d = new frappe.ui.Dialog({ + title: __("Select Print Format to edit"), + fields: [ + { + label: __("Print Format"), + fieldname: "print_format", + fieldtype: "Link", + options: "Print Format", + filters: { + print_format_builder_beta: 1 + } + } + ], + primary_action({ print_format }) { + if (!print_format) return; + frappe.set_route("print-format-builder-beta", print_format); + } + }); + d.show(); } } diff --git a/frappe/public/js/print_format_builder/ConfigureColumns.vue b/frappe/public/js/print_format_builder/ConfigureColumns.vue new file mode 100644 index 0000000000..8966595216 --- /dev/null +++ b/frappe/public/js/print_format_builder/ConfigureColumns.vue @@ -0,0 +1,86 @@ + + + diff --git a/frappe/public/js/print_format_builder/Field.vue b/frappe/public/js/print_format_builder/Field.vue new file mode 100644 index 0000000000..d82015f51c --- /dev/null +++ b/frappe/public/js/print_format_builder/Field.vue @@ -0,0 +1,345 @@ + + + diff --git a/frappe/public/js/print_format_builder/PrintFormat.vue b/frappe/public/js/print_format_builder/PrintFormat.vue index 9e8e2033a4..d10eb88a6f 100644 --- a/frappe/public/js/print_format_builder/PrintFormat.vue +++ b/frappe/public/js/print_format_builder/PrintFormat.vue @@ -19,13 +19,14 @@ @@ -70,4 +71,4 @@ export default { box-shadow: var(--shadow-lg); border-radius: var(--border-radius); } - \ No newline at end of file + diff --git a/frappe/public/js/print_format_builder/PrintFormatBuilder.vue b/frappe/public/js/print_format_builder/PrintFormatBuilder.vue index 88b70f8b1b..78205406e3 100644 --- a/frappe/public/js/print_format_builder/PrintFormatBuilder.vue +++ b/frappe/public/js/print_format_builder/PrintFormatBuilder.vue @@ -1,14 +1,10 @@ @@ -16,87 +12,33 @@ @@ -107,4 +49,4 @@ export default { padding-top: 0.5rem; padding-bottom: 4rem; } - \ No newline at end of file + diff --git a/frappe/public/js/print_format_builder/PrintFormatControls.vue b/frappe/public/js/print_format_builder/PrintFormatControls.vue index b4f452751a..dd54f8f55e 100644 --- a/frappe/public/js/print_format_builder/PrintFormatControls.vue +++ b/frappe/public/js/print_format_builder/PrintFormatControls.vue @@ -14,7 +14,7 @@ type="number" class="form-control form-control-sm" :value="print_format[df.fieldname]" - @change="(e) => update_margin(df.fieldname, e.target.value)" + @change="e => update_margin(df.fieldname, e.target.value)" /> @@ -24,7 +24,7 @@ @@ -74,19 +62,23 @@ @@ -166,38 +158,8 @@ export default { padding-right: 8px; } -.field { - display: flex; - justify-content: space-between; - align-items: center; - width: 100%; - background-color: var(--bg-light-gray); - border-radius: var(--border-radius); - border: 1px dashed var(--gray-400); - padding: 0.5rem 0.75rem; - font-size: var(--text-sm); -} - -.field:not(:first-child) { - margin-top: 0.5rem; -} - -.btn-remove-field { - opacity: 0; - padding: 2px; - box-shadow: none; -} - -.btn-remove-field:hover { - background-color: white; -} - -.field:hover .btn-remove-field { - opacity: 1; -} - .drag-container { height: 100%; min-height: 2rem; } - \ No newline at end of file + diff --git a/frappe/public/js/print_format_builder/print_format_builder.bundle.js b/frappe/public/js/print_format_builder/print_format_builder.bundle.js index e364bfa29e..63cc098380 100644 --- a/frappe/public/js/print_format_builder/print_format_builder.bundle.js +++ b/frappe/public/js/print_format_builder/print_format_builder.bundle.js @@ -5,13 +5,24 @@ class PrintFormatBuilder { this.$wrapper = $(wrapper); this.page = page; this.print_format = print_format; + + this.page.clear_actions() + this.page.clear_custom_actions() + this.page.set_title(__("Editing {0}", [this.print_format])); this.page.set_primary_action(__("Save changes"), () => { - this.$component.save_changes(); + this.$component.$store.save_changes(); }); this.page.set_secondary_action(__("Reset changes"), () => { - this.$component.reset_changes(); + this.$component.$store.reset_changes(); }); + this.page.add_button( + __("Preview"), + () => { + this.preview(); + }, + { icon: "small-file" } + ); let $vm = new Vue({ el: this.$wrapper.get(0), @@ -24,6 +35,46 @@ class PrintFormatBuilder { }); this.$component = $vm.$children[0]; } + + async preview() { + let doctype = this.$component.$store.print_format.doc_type; + let default_doc = await frappe.db.get_list(doctype, { + limit: 1 + }); + + let d = new frappe.ui.Dialog({ + title: __("Preview Print Format"), + fields: [ + { + label: __("Type"), + fieldname: "type", + fieldtype: "Select", + options: ["PDF", "HTML"], + default: "PDF" + }, + { + label: __("Select Document"), + fieldname: "docname", + fieldtype: "Link", + options: doctype, + reqd: 1, + default: default_doc.length > 0 ? default_doc[0].name : null + } + ], + primary_action: ({ docname, type }) => { + let params = new URLSearchParams(); + params.append("doctype", doctype); + params.append("name", docname); + params.append("print_format", this.print_format); + let url = + type == "PDF" + ? `/api/method/frappe.utils.weasyprint.download_pdf` + : "/printpreview"; + window.open(`${url}?${params.toString()}`, "_blank"); + } + }); + d.show(); + } } frappe.provide("frappe.ui"); diff --git a/frappe/public/js/print_format_builder/store.js b/frappe/public/js/print_format_builder/store.js new file mode 100644 index 0000000000..3af4a21dec --- /dev/null +++ b/frappe/public/js/print_format_builder/store.js @@ -0,0 +1,125 @@ +import { create_default_layout, pluck } from "./utils"; + +let stores = {}; + +export function getStore(print_format_name) { + if (stores[print_format_name]) { + return stores[print_format_name]; + } + + let options = { + data() { + return { + print_format_name, + print_format: null, + doctype: null, + meta: null, + layout: null + }; + }, + methods: { + fetch() { + frappe.model.clear_doc("Print Format", this.print_format_name); + frappe.model.with_doc( + "Print Format", + this.print_format_name, + () => { + this.print_format = frappe.get_doc( + "Print Format", + this.print_format_name + ); + frappe.model.with_doctype( + this.print_format.doc_type, + () => { + this.meta = frappe.get_meta( + this.print_format.doc_type + ); + this.layout = this.get_layout(); + } + ); + } + ); + }, + update({ fieldname, value }) { + this.$set(this.print_format, fieldname, value); + }, + save_changes() { + frappe.dom.freeze(__("Saving...")); + + this.layout.sections = this.layout.sections + .filter(section => !section.remove) + .map(section => { + section.columns = section.columns.map(column => { + column.fields = column.fields + .filter(df => !df.remove) + .map(df => { + if (df.table_columns) { + df.table_columns = df.table_columns.map( + tf => { + return pluck(tf, [ + "label", + "fieldname", + "fieldtype", + "options", + "width" + ]); + } + ); + } + return pluck(df, [ + "label", + "fieldname", + "fieldtype", + "options", + "table_columns" + ]); + }); + return column; + }); + return section; + }); + + this.print_format.format_data = JSON.stringify(this.layout); + + frappe + .call("frappe.client.save", { + doc: this.print_format + }) + .then(() => this.fetch()) + .always(() => frappe.dom.unfreeze()); + }, + reset_changes() { + this.fetch(); + }, + get_layout() { + if (this.print_format) { + if (!this.print_format.format_data) { + return create_default_layout(this.meta); + } + if (typeof this.print_format.format_data == "string") { + return JSON.parse(this.print_format.format_data); + } + return this.print_format.format_data; + } + return null; + } + } + }; + stores[print_format_name] = new Vue(options); + return stores[print_format_name]; +} + +export let storeMixin = { + inject: ["$store"], + computed: { + print_format() { + return this.$store.print_format; + }, + layout() { + return this.$store.layout; + }, + meta() { + return this.$store.meta; + } + } +}; diff --git a/frappe/public/js/print_format_builder/utils.js b/frappe/public/js/print_format_builder/utils.js index 9428ca61f9..7bdd78a04a 100644 --- a/frappe/public/js/print_format_builder/utils.js +++ b/frappe/public/js/print_format_builder/utils.js @@ -59,6 +59,7 @@ export function create_default_layout(meta) { let field = { label: df.label, fieldname: df.fieldname, + fieldtype: df.fieldtype, options: df.options }; @@ -81,20 +82,35 @@ export function create_default_layout(meta) { export function get_table_columns(df) { let table_columns = []; let table_fields = frappe.get_meta(df.options).fields; - + let total_columns = 0; for (let tf of table_fields) { if ( !in_list(["Section Break", "Column Break"], tf.fieldtype) && !tf.print_hide && - df.label + df.label && + total_columns < 12 ) { + let columns = + typeof tf.width == "number" && tf.width < 12 ? tf.width : 2; table_columns.push({ label: tf.label, fieldname: tf.fieldname, + fieldtype: tf.fieldtype, options: tf.options, - width: tf.width || 0 + width: columns }); + total_columns += columns; } } return table_columns; } + +export function pluck(object, keys) { + let out = {}; + for (let key of keys) { + if (key in object) { + out[key] = object[key]; + } + } + return out; +} From a25817b64ce9d3d2434f2057899ce68fffb26037 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Sat, 21 Aug 2021 18:03:36 +0530 Subject: [PATCH 04/40] fix: Set table column width in percentages --- .../print_format_builder/ConfigureColumns.vue | 45 ++++++++++++++----- .../public/js/print_format_builder/Field.vue | 20 +++++---- .../public/js/print_format_builder/utils.js | 16 ++++--- 3 files changed, 56 insertions(+), 25 deletions(-) diff --git a/frappe/public/js/print_format_builder/ConfigureColumns.vue b/frappe/public/js/print_format_builder/ConfigureColumns.vue index 8966595216..da10f99e40 100644 --- a/frappe/public/js/print_format_builder/ConfigureColumns.vue +++ b/frappe/public/js/print_format_builder/ConfigureColumns.vue @@ -1,6 +1,6 @@ + diff --git a/frappe/public/js/print_format_builder/LetterHeadEditor.vue b/frappe/public/js/print_format_builder/LetterHeadEditor.vue new file mode 100644 index 0000000000..d958a6e67a --- /dev/null +++ b/frappe/public/js/print_format_builder/LetterHeadEditor.vue @@ -0,0 +1,108 @@ + + + diff --git a/frappe/public/js/print_format_builder/MarginText.vue b/frappe/public/js/print_format_builder/MarginText.vue new file mode 100644 index 0000000000..85229d2d42 --- /dev/null +++ b/frappe/public/js/print_format_builder/MarginText.vue @@ -0,0 +1,114 @@ + + + diff --git a/frappe/public/js/print_format_builder/Preview.vue b/frappe/public/js/print_format_builder/Preview.vue index 1ce89a87aa..1d795f27d7 100644 --- a/frappe/public/js/print_format_builder/Preview.vue +++ b/frappe/public/js/print_format_builder/Preview.vue @@ -97,6 +97,9 @@ export default { params.append("doctype", this.doctype); params.append("name", this.docname); params.append("print_format", this.print_format.name); + if (this.$store.letterhead) { + params.append("letterhead", this.$store.letterhead.name); + } let url = this.type == "PDF" ? `/api/method/frappe.utils.weasyprint.download_pdf` diff --git a/frappe/public/js/print_format_builder/PrintFormat.vue b/frappe/public/js/print_format_builder/PrintFormat.vue index f0f7f713ea..74cfb84133 100644 --- a/frappe/public/js/print_format_builder/PrintFormat.vue +++ b/frappe/public/js/print_format_builder/PrintFormat.vue @@ -1,6 +1,20 @@ - diff --git a/frappe/public/js/print_format_builder/PrintFormat.vue b/frappe/public/js/print_format_builder/PrintFormat.vue index 74cfb84133..7163282067 100644 --- a/frappe/public/js/print_format_builder/PrintFormat.vue +++ b/frappe/public/js/print_format_builder/PrintFormat.vue @@ -32,7 +32,12 @@ @change="$set(layout, 'footer', $event)" :button-label="__('Edit Footer')" /> - + @@ -85,6 +90,10 @@ export default { sections.push(_section); } this.$set(this.layout, "sections", sections); + }, + update_letterhead_footer(val) { + this.letterhead.footer = val; + this.letterhead._dirty = true; } } }; diff --git a/frappe/public/js/print_format_builder/store.js b/frappe/public/js/print_format_builder/store.js index f72cf0c450..57c48632c8 100644 --- a/frappe/public/js/print_format_builder/store.js +++ b/frappe/public/js/print_format_builder/store.js @@ -115,9 +115,11 @@ export function getStore(print_format_name) { }) .then(() => { if (this.letterhead && this.letterhead._dirty) { - return frappe.call("frappe.client.save", { - doc: this.letterhead - }); + return frappe + .call("frappe.client.save", { + doc: this.letterhead + }) + .then(r => (this.letterhead = r.message)); } }) .then(() => this.fetch()) @@ -125,7 +127,6 @@ export function getStore(print_format_name) { }, reset_changes() { this.fetch(); - }, get_layout() { if (this.print_format) { @@ -143,7 +144,7 @@ export function getStore(print_format_name) { return create_default_layout(this.meta); }, change_letterhead(letterhead) { - frappe.db.get_doc("Letter Head", letterhead).then(doc => { + return frappe.db.get_doc("Letter Head", letterhead).then(doc => { this.letterhead = doc; }); } diff --git a/frappe/public/js/print_format_builder/utils.js b/frappe/public/js/print_format_builder/utils.js index 77d4e2a36d..6c52b2e4a4 100644 --- a/frappe/public/js/print_format_builder/utils.js +++ b/frappe/public/js/print_format_builder/utils.js @@ -94,8 +94,8 @@ export function get_table_columns(df) { typeof tf.width == "number" && tf.width < 100 ? tf.width : tf.width - ? 20 - : 10; + ? 20 + : 10; table_columns.push({ label: tf.label, fieldname: tf.fieldname, @@ -118,3 +118,13 @@ export function pluck(object, keys) { } return out; } + +export function get_image_dimensions(src) { + return new Promise(resolve => { + let img = new Image(); + img.onload = function() { + resolve({ width: this.width, height: this.height }); + }; + img.src = src; + }); +} diff --git a/frappe/templates/print_format/print_format.html b/frappe/templates/print_format/print_format.html index 7e710e8c3f..69fb50d959 100644 --- a/frappe/templates/print_format/print_format.html +++ b/frappe/templates/print_format/print_format.html @@ -13,7 +13,7 @@ - {{ header }} + {{ header or '' }} {% for section in layout.sections %}
{% if section.label %} @@ -31,6 +31,6 @@
{% endfor %} - {{ footer }} + {{ footer or '' }} diff --git a/frappe/templates/print_format/print_header.html b/frappe/templates/print_format/print_header.html index 69a5360d6f..9b1357e08c 100644 --- a/frappe/templates/print_format/print_header.html +++ b/frappe/templates/print_format/print_header.html @@ -6,7 +6,7 @@ position: fixed; top: 0; left: 0; - width: 100%; + width: {{ body_width | int }}mm; padding-top: {{ print_format.margin_top | int }}mm; padding-left: {{ print_format.margin_left | int }}mm; padding-right: {{ print_format.margin_right | int }}mm; From 67acd5d22e5865b40e76fd8b57d458301707dbeb Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Tue, 5 Oct 2021 14:21:12 +0530 Subject: [PATCH 15/40] fix: Set route_options from links --- frappe/public/js/frappe/router.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frappe/public/js/frappe/router.js b/frappe/public/js/frappe/router.js index 484f1ac911..7b26832522 100644 --- a/frappe/public/js/frappe/router.js +++ b/frappe/public/js/frappe/router.js @@ -58,6 +58,12 @@ $('body').on('click', 'a', function(e) { if (frappe.router.is_app_route(e.currentTarget.pathname)) { // target has "/app, this is a v2 style route. + + frappe.route_options = {}; + let params = new URLSearchParams(e.currentTarget.search); + for (const [key, value] of params) { + frappe.route_options[key] = value; + } return override(e.currentTarget.pathname + e.currentTarget.hash); } }); From d8457a3c71bdc2f0e73ba648f83537427faa4222 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Tue, 5 Oct 2021 14:21:57 +0530 Subject: [PATCH 16/40] fix: Show print preview on print page --- frappe/printing/page/print/print.js | 41 ++++++++- .../print_format_builder_beta.js | 83 +++++++++++++++++-- frappe/public/scss/desk/print_preview.scss | 5 ++ .../templates/print_format/print_format.css | 2 +- 4 files changed, 120 insertions(+), 11 deletions(-) diff --git a/frappe/printing/page/print/print.js b/frappe/printing/page/print/print.js index 27fc4be802..30d9fedeae 100644 --- a/frappe/printing/page/print/print.js +++ b/frappe/printing/page/print/print.js @@ -41,7 +41,11 @@ frappe.ui.form.PrintView = class {
- ` + +
+ +
+ ` ); this.print_settings = frappe.model.get_doc( @@ -134,7 +138,7 @@ frappe.ui.form.PrintView = class { add_sidebar_item(df, is_dynamic) { if (df.fieldtype == 'Select') { - df.input_class = 'btn btn-default btn-sm'; + df.input_class = 'btn btn-default btn-sm text-left'; } let field = frappe.ui.form.make_control({ @@ -187,6 +191,13 @@ frappe.ui.form.PrintView = class { this.set_breadcrumbs(); this.setup_customize_dialog(); + // print format builder beta + this.page.add_inner_message(` + + ${__('Try the new Print Format Builder')} + + `); + let tasks = [ this.refresh_print_options, this.set_default_print_language, @@ -383,6 +394,17 @@ frappe.ui.form.PrintView = class { } preview() { + let print_format = this.get_print_format(); + if (print_format.print_format_builder_beta) { + this.print_wrapper.find('.print-preview-wrapper').hide(); + this.print_wrapper.find('.preview-beta-wrapper').show(); + this.preview_beta(); + return; + } + + this.print_wrapper.find('.preview-beta-wrapper').hide(); + this.print_wrapper.find('.print-preview-wrapper').show(); + const $print_format = this.print_wrapper.find('iframe'); this.$print_format_body = $print_format.contents(); this.get_print_html((out) => { @@ -406,6 +428,21 @@ frappe.ui.form.PrintView = class { }); } + preview_beta() { + let print_format = this.get_print_format(); + const iframe = this.print_wrapper.find('.preview-beta-wrapper iframe'); + let params = new URLSearchParams({ + doctype: this.frm.doc.doctype, + name: this.frm.doc.name, + print_format: print_format.name + }); + let letterhead = this.get_letterhead(); + if (letterhead) { + params.append("letterhead", letterhead); + } + iframe.prop('src', `/printpreview?${params.toString()}`); + } + setup_print_format_dom(out, $print_format) { this.print_wrapper.find('.print-format-skeleton').remove(); let base_url = frappe.urllib.get_base_url(); diff --git a/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.js b/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.js index d53266d0d5..7185d17832 100644 --- a/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.js +++ b/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.js @@ -31,23 +31,90 @@ function load_print_format_builder_beta(wrapper) { }); } else { let d = new frappe.ui.Dialog({ - title: __("Select Print Format to edit"), + title: __("Create or Edit Print Format"), fields: [ { - label: __("Print Format"), + label: __("Action"), + fieldname: "action", + fieldtype: "Select", + options: [ + { label: __("Create New"), value: "Create" }, + { label: __("Edit Existing"), value: "Edit" } + ], + change() { + let action = d.get_value("action"); + d.get_primary_btn().text( + action === "Create" ? __("Create") : __("Edit") + ); + } + }, + { + label: __("Select Document Type"), + fieldname: "doctype", + fieldtype: "Link", + options: "DocType", + filters: { + istable: 0 + }, + reqd: 1, + default: frappe.route_options + ? frappe.route_options.doctype + : null + }, + { + label: __("Print Format Name"), + fieldname: "print_format_name", + fieldtype: "Data", + depends_on: doc => doc.action === "Create", + mandatory_depends_on: doc => doc.action === "Create" + }, + { + label: __("Select Print Format"), fieldname: "print_format", fieldtype: "Link", options: "Print Format", - filters: { - print_format_builder_beta: 1 - } + only_select: 1, + depends_on: doc => doc.action === "Edit", + get_query() { + return { + filters: { + doc_type: d.get_value("doctype"), + print_format_builder_beta: 1 + } + }; + }, + mandatory_depends_on: doc => doc.action === "Edit" } ], - primary_action({ print_format }) { - if (!print_format) return; - frappe.set_route("print-format-builder-beta", print_format); + primary_action_label: __("Edit"), + primary_action({ + action, + doctype, + print_format, + print_format_name + }) { + if (action === "Edit") { + frappe.set_route("print-format-builder-beta", print_format); + } else if (action === "Create") { + d.get_primary_btn().prop("disabled", true); + frappe.db + .insert({ + doctype: "Print Format", + name: print_format_name, + doc_type: doctype, + print_format_builder_beta: 1 + }) + .then(doc => { + d.get_primary_btn().prop("disabled", false); + frappe.set_route( + "print-format-builder-beta", + doc.name + ); + }); + } } }); + d.set_value("action", "Create"); d.show(); } } diff --git a/frappe/public/scss/desk/print_preview.scss b/frappe/public/scss/desk/print_preview.scss index 3c0acc68b8..468b37fe5a 100644 --- a/frappe/public/scss/desk/print_preview.scss +++ b/frappe/public/scss/desk/print_preview.scss @@ -14,6 +14,11 @@ } } +.preview-beta-wrapper { + border-radius: var(--border-radius); + overflow: hidden; +} + .print-toolbar { margin: 0px; padding: var(--padding-md) 0; diff --git a/frappe/templates/print_format/print_format.css b/frappe/templates/print_format/print_format.css index a52dcf49aa..fd0c0be1b6 100644 --- a/frappe/templates/print_format/print_format.css +++ b/frappe/templates/print_format/print_format.css @@ -34,7 +34,7 @@ body { @media screen { html { - background-color: var(--gray-300); + background-color: var(--gray-200); } body { background-color: white; From c31bf0aadc1fd1f3cb020cac292971a25552f58f Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Tue, 19 Oct 2021 15:45:40 +0530 Subject: [PATCH 17/40] fix: Page number - simpler configuration - remove arbitrary margin text configuration --- .../doctype/print_format/print_format.json | 10 +- .../js/print_format_builder/MarginText.vue | 114 ------------------ .../js/print_format_builder/PrintFormat.vue | 43 +++++-- .../PrintFormatControls.vue | 30 +++++ .../templates/print_format/print_format.css | 19 ++- 5 files changed, 81 insertions(+), 135 deletions(-) delete mode 100644 frappe/public/js/print_format_builder/MarginText.vue diff --git a/frappe/printing/doctype/print_format/print_format.json b/frappe/printing/doctype/print_format/print_format.json index ab13c28f3b..75ec0fa7fd 100644 --- a/frappe/printing/doctype/print_format/print_format.json +++ b/frappe/printing/doctype/print_format/print_format.json @@ -30,6 +30,7 @@ "column_break_11", "font_size", "font", + "page_number", "css_section", "css", "custom_html_help", @@ -245,13 +246,20 @@ "fieldname": "font_size", "fieldtype": "Int", "label": "Font Size" + }, + { + "default": "Hide", + "fieldname": "page_number", + "fieldtype": "Select", + "label": "Page Number", + "options": "Hide\nTop Left\nTop Center\nTop Right\nBottom Left\nBottom Center\nBottom Right" } ], "icon": "fa fa-print", "idx": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2021-09-07 09:47:57.119820", + "modified": "2021-10-12 17:52:41.167107", "modified_by": "Administrator", "module": "Printing", "name": "Print Format", diff --git a/frappe/public/js/print_format_builder/MarginText.vue b/frappe/public/js/print_format_builder/MarginText.vue deleted file mode 100644 index 85229d2d42..0000000000 --- a/frappe/public/js/print_format_builder/MarginText.vue +++ /dev/null @@ -1,114 +0,0 @@ - - - diff --git a/frappe/public/js/print_format_builder/PrintFormat.vue b/frappe/public/js/print_format_builder/PrintFormat.vue index 7163282067..5138a54a38 100644 --- a/frappe/public/js/print_format_builder/PrintFormat.vue +++ b/frappe/public/js/print_format_builder/PrintFormat.vue @@ -1,11 +1,6 @@