From f536a1ff91db91397ff09d2e996014c09ff80aba Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Sun, 15 Aug 2021 16:10:04 +0530 Subject: [PATCH] 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; +}