From 2e5d53ef6f41113a5ad84dbd37c6670e141c44b4 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 18 Jan 2023 14:17:05 +0530 Subject: [PATCH 1/6] fix: if section has one column input field's width should be half --- .../js/form_builder/components/FormBuilder.vue | 12 ++++++++++++ frappe/public/js/form_builder/components/Section.vue | 8 +++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/form_builder/components/FormBuilder.vue b/frappe/public/js/form_builder/components/FormBuilder.vue index c641643414..1ab8c715ea 100644 --- a/frappe/public/js/form_builder/components/FormBuilder.vue +++ b/frappe/public/js/form_builder/components/FormBuilder.vue @@ -109,6 +109,12 @@ onMounted(() => { box-shadow: var(--card-shadow); background-color: var(--card-bg); + :deep(.section-columns.has-one-column .field) { + input.form-control, .signature-field { + width: calc(50% - 19px); + } + } + :deep(.column-container .field.sortable-chosen) { background-color: var(--bg-light-gray); border-radius: var(--border-radius-sm); @@ -221,6 +227,12 @@ onMounted(() => { .section-columns { margin-top: 8px; + &.has-one-column .field { + input.form-control, .signature-field { + width: calc(50% - 15px); + } + } + .section-columns-container { .column { padding-left: 15px; diff --git a/frappe/public/js/form_builder/components/Section.vue b/frappe/public/js/form_builder/components/Section.vue index d23a599fa6..f1419b7c89 100644 --- a/frappe/public/js/form_builder/components/Section.vue +++ b/frappe/public/js/form_builder/components/Section.vue @@ -130,7 +130,13 @@ function move_sections_to_tab() {
{{ section.df.description }}
-
+
Date: Wed, 18 Jan 2023 20:51:11 +0530 Subject: [PATCH 3/6] fix: on duplicate of standard field create a custom field in customize form --- frappe/public/js/form_builder/components/Field.vue | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frappe/public/js/form_builder/components/Field.vue b/frappe/public/js/form_builder/components/Field.vue index 5a7ce5626f..58c2d85b3b 100644 --- a/frappe/public/js/form_builder/components/Field.vue +++ b/frappe/public/js/form_builder/components/Field.vue @@ -32,6 +32,10 @@ function move_fields_to_column() { function duplicate_field() { let duplicate_field = clone_field(props.field); + if (store.is_customize_form) { + duplicate_field.df.is_custom_field = 1; + } + if (duplicate_field.df.label) { duplicate_field.df.label = duplicate_field.df.label + " Copy"; } From 986cc8d634d519bcf021194009f17cbd47c57912 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 18 Jan 2023 21:30:17 +0530 Subject: [PATCH 4/6] fix: make duplicated field as unsaved field reset creation, modified, modified_by, owner --- frappe/public/js/form_builder/components/Field.vue | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/frappe/public/js/form_builder/components/Field.vue b/frappe/public/js/form_builder/components/Field.vue index 58c2d85b3b..cf3e21c310 100644 --- a/frappe/public/js/form_builder/components/Field.vue +++ b/frappe/public/js/form_builder/components/Field.vue @@ -40,6 +40,13 @@ function duplicate_field() { duplicate_field.df.label = duplicate_field.df.label + " Copy"; } duplicate_field.df.fieldname = ""; + duplicate_field.df.__islocal = 1; + duplicate_field.df.__unsaved = 1; + duplicate_field.df.owner = frappe.session.user; + + delete duplicate_field.df.creation; + delete duplicate_field.df.modified; + delete duplicate_field.df.modified_by; // push duplicate_field after props.field in the same column let index = props.column.fields.indexOf(props.field); From b3f9e69a6e5749bf15af190e951a200f28777b52 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 18 Jan 2023 22:49:05 +0530 Subject: [PATCH 5/6] fix: use frappe.desk.form.save.savedocs instead of frappe.client.save for saving --- frappe/public/js/form_builder/store.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/form_builder/store.js b/frappe/public/js/form_builder/store.js index 314e8f5ed7..246956dc94 100644 --- a/frappe/public/js/form_builder/store.js +++ b/frappe/public/js/form_builder/store.js @@ -182,8 +182,10 @@ export const useStore = defineStore("form-builder-store", { } else { this.doc.fields = this.get_updated_fields(); this.validate_fields(this.doc.fields, this.doc.istable); - await frappe.call("frappe.client.save", { doc: this.doc }); - frappe.toast("Fields Table Updated"); + await frappe.call({ + method: "frappe.desk.form.save.savedocs", + args: { doc: this.doc, action: "Save" }, + }); } this.fetch(); } catch (e) { From 3b2cfcad2c9b7073e969302056c804498f6e7804 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 18 Jan 2023 23:51:52 +0530 Subject: [PATCH 6/6] fix: ask if need to delete tab/section/column with or without children --- .../js/form_builder/components/Column.vue | 67 +++++++++++++------ .../js/form_builder/components/Section.vue | 45 +++++++++---- .../js/form_builder/components/Tabs.vue | 63 +++++++++-------- frappe/public/js/form_builder/utils.js | 25 +++++++ 4 files changed, 139 insertions(+), 61 deletions(-) diff --git a/frappe/public/js/form_builder/components/Column.vue b/frappe/public/js/form_builder/components/Column.vue index a8f1f84118..a9c4bf0ea1 100644 --- a/frappe/public/js/form_builder/components/Column.vue +++ b/frappe/public/js/form_builder/components/Column.vue @@ -4,7 +4,7 @@ import Field from "./Field.vue"; import EditableInput from "./EditableInput.vue"; import { ref } from "vue"; import { useStore } from "../store"; -import { move_children_to_parent } from "../utils"; +import { move_children_to_parent, confirm_dialog } from "../utils"; let props = defineProps(["section", "column"]); let store = useStore(); @@ -24,32 +24,61 @@ function remove_column() { if (store.is_customize_form && props.column.df.is_custom_field == 0) { frappe.msgprint(__("Cannot delete standard field. You can hide it if you want")); throw "cannot delete standard field"; + } else if (props.column.fields.length == 0 || store.has_standard_field(props.column)) { + delete_column(); + } else { + confirm_dialog( + __("Delete Column", null, "Title of confirmation dialog"), + __("Are you sure you want to delete the column? All the fields in the column will be moved to the previous column.", null, "Confirmation dialog message"), + () => delete_column(), + __("Delete column", null, "Button text"), + () => delete_column(true), + __("Delete entire column with fields", null, "Button text") + ); } +} +function delete_column(with_children) { // move all fields to previous column let columns = props.section.columns; let index = columns.indexOf(props.column); - if (index > 0) { - let prev_column = columns[index - 1]; - prev_column.fields = [...prev_column.fields, ...props.column.fields]; - } else { - if (props.column.fields.length != 0) { - // create a new column if current column has fields and push fields to it - columns.unshift({ - df: store.get_df("Column Break"), - fields: props.column.fields, - is_first: true, - }); - index++; + if (with_children && index == 0 && columns.length == 1) { + if (props.column.fields.length == 0) { + frappe.msgprint(__("Section must have at least one column")); + throw "section must have at least one column"; + } + + columns.unshift({ + df: store.get_df("Column Break"), + fields: [], + is_first: true, + }); + index++; + } + + if (!with_children) { + if (index > 0) { + let prev_column = columns[index - 1]; + prev_column.fields = [...prev_column.fields, ...props.column.fields]; } else { - // set next column as first column - let next_column = columns[index + 1]; - if (next_column) { - next_column.is_first = true; + if (props.column.fields.length == 0) { + // set next column as first column + let next_column = columns[index + 1]; + if (next_column) { + next_column.is_first = true; + } else { + frappe.msgprint(__("Section must have at least one column")); + throw "section must have at least one column"; + } } else { - frappe.msgprint(__("Section must have at least one column")); - throw "section must have at least one column"; + // create a new column if current column has fields and push fields to it + columns.unshift({ + df: store.get_df("Column Break"), + fields: props.column.fields, + is_first: true, + }); + index++; } } } diff --git a/frappe/public/js/form_builder/components/Section.vue b/frappe/public/js/form_builder/components/Section.vue index f1419b7c89..4624c72a38 100644 --- a/frappe/public/js/form_builder/components/Section.vue +++ b/frappe/public/js/form_builder/components/Section.vue @@ -4,7 +4,7 @@ import Column from "./Column.vue"; import EditableInput from "./EditableInput.vue"; import { ref } from "vue"; import { useStore } from "../store"; -import { section_boilerplate, move_children_to_parent } from "../utils"; +import { section_boilerplate, move_children_to_parent, confirm_dialog } from "../utils"; let props = defineProps(["tab", "section"]); let store = useStore(); @@ -27,25 +27,42 @@ function remove_section() { if (store.is_customize_form && props.section.df.is_custom_field == 0) { frappe.msgprint(__("Cannot delete standard field. You can hide it if you want")); throw "cannot delete standard field"; + } else if (store.has_standard_field(props.section)) { + delete_section(); + } else if (is_section_empty()) { + delete_section(true); + } else { + confirm_dialog( + __("Delete Section", null, "Title of confirmation dialog"), + __("Are you sure you want to delete the section? All the columns along with fields in the section will be moved to the previous section.", null, "Confirmation dialog message"), + () => delete_section(), + __("Delete section", null, "Button text"), + () => delete_section(true), + __("Delete entire section with columns", null, "Button text") + ); } +} +function delete_section(with_children) { let sections = props.tab.sections; let index = sections.indexOf(props.section); - if (index > 0) { - let prev_section = sections[index - 1]; - if (!is_section_empty()) { - // move all columns from current section to previous section - prev_section.columns = [...prev_section.columns, ...props.section.columns]; + if (!with_children) { + if (index > 0) { + let prev_section = sections[index - 1]; + if (!is_section_empty()) { + // move all columns from current section to previous section + prev_section.columns = [...prev_section.columns, ...props.section.columns]; + } + } else if (index == 0 && !is_section_empty()) { + // create a new section and push columns to it + sections.unshift({ + df: store.get_df("Section Break"), + columns: props.section.columns, + is_first: true, + }); + index++; } - } else if (index == 0 && !is_section_empty()) { - // create a new section and push columns to it - sections.unshift({ - df: store.get_df("Section Break"), - columns: props.section.columns, - is_first: true, - }); - index++; } // remove section diff --git a/frappe/public/js/form_builder/components/Tabs.vue b/frappe/public/js/form_builder/components/Tabs.vue index d2ef939f80..625ca38745 100644 --- a/frappe/public/js/form_builder/components/Tabs.vue +++ b/frappe/public/js/form_builder/components/Tabs.vue @@ -3,7 +3,7 @@ import Section from "./Section.vue"; import EditableInput from "./EditableInput.vue"; import draggable from "vuedraggable"; import { useStore } from "../store"; -import { section_boilerplate } from "../utils"; +import { section_boilerplate, confirm_dialog } from "../utils"; import { ref, computed, nextTick } from "vue"; let store = useStore(); @@ -51,44 +51,51 @@ function add_new_section() { function is_current_tab_empty() { // check if sections have columns and it contains fields - return !store.current_tab.sections.some(section => { - // if section doesnt have fields remove the section - let has_fields = section.columns.some(column => column.fields.length); - - if (!has_fields) { - // remove section if empty - let index = store.current_tab.sections.indexOf(section); - store.current_tab.sections.splice(index, 1); - has_fields = true; - } - - return has_fields; - }); + return !store.current_tab.sections.some( + section => section.columns.some(column => column.fields.length) + ); } function remove_tab() { if (store.is_customize_form && store.current_tab.df.is_custom_field == 0) { frappe.msgprint(__("Cannot delete standard field. You can hide it if you want")); throw "cannot delete standard field"; + } else if (store.has_standard_field(store.current_tab)) { + delete_tab(); + } else if (is_current_tab_empty()) { + delete_tab(true); + } else { + confirm_dialog( + __("Delete Tab", null, "Title of confirmation dialog"), + __("Are you sure you want to delete the tab? All the sections along with fields in the tab will be moved to the previous tab.", null, "Confirmation dialog message"), + () => delete_tab(), + __("Delete tab", null, "Button text"), + () => delete_tab(true), + __("Delete entire tab with sections", null, "Button text") + ); } +} +function delete_tab(with_children) { let tabs = layout.value.tabs; let index = tabs.indexOf(store.current_tab); - if (index > 0) { - let prev_tab = tabs[index - 1]; - if (!is_current_tab_empty()) { - // move all sections from current tab to previous tab - prev_tab.sections = [...prev_tab.sections, ...store.current_tab.sections]; + if (!with_children) { + if (index > 0) { + let prev_tab = tabs[index - 1]; + if (!is_current_tab_empty()) { + // move all sections from current tab to previous tab + prev_tab.sections = [...prev_tab.sections, ...store.current_tab.sections]; + } + } else { + // create a new tab and push sections to it + tabs.unshift({ + df: store.get_df("Tab Break", "", __("Details")), + sections: store.current_tab.sections, + is_first: true, + }); + index++; } - } else { - // create a new tab and push sections to it - tabs.unshift({ - df: store.get_df("Tab Break", "", __("Details")), - sections: store.current_tab.sections, - is_first: true, - }); - index++; } // remove tab @@ -185,7 +192,7 @@ function remove_tab() {
-
{{ __("Drag & Drop a section here") }}
+
{{ __("Drag & Drop a section here from another tab") }}
{{ __("OR") }}