@@ -45,7 +45,7 @@ context("Web Form", () => { | |||
cy.login(); | |||
cy.visit("/app/web-form/note"); | |||
cy.findByRole("tab", { name: "Form Settings" }).click(); | |||
cy.findByRole("tab", { name: "Settings" }).click(); | |||
cy.get('input[data-fieldname="login_required"]').check({ force: true }); | |||
cy.save(); | |||
@@ -65,7 +65,8 @@ context("Web Form", () => { | |||
cy.login(); | |||
cy.visit("/app/web-form/note"); | |||
cy.findByRole("tab", { name: "List Settings" }).click(); | |||
cy.findByRole("tab", { name: "Settings" }).click(); | |||
cy.get(".section-head").contains("List Settings").click(); | |||
cy.get('input[data-fieldname="show_list"]').check(); | |||
cy.save(); | |||
@@ -78,7 +79,7 @@ context("Web Form", () => { | |||
it("Show Custom List Title", () => { | |||
cy.visit("/app/web-form/note"); | |||
cy.findByRole("tab", { name: "List Settings" }).click(); | |||
cy.findByRole("tab", { name: "Settings" }).click(); | |||
cy.fill_field("list_title", "Note List"); | |||
cy.save(); | |||
@@ -97,7 +98,7 @@ context("Web Form", () => { | |||
cy.visit("/app/web-form/note"); | |||
cy.findByRole("tab", { name: "List Settings" }).click(); | |||
cy.findByRole("tab", { name: "Settings" }).click(); | |||
cy.get('[data-fieldname="list_columns"] .grid-footer button') | |||
.contains("Add Row") | |||
@@ -108,19 +109,19 @@ context("Web Form", () => { | |||
cy.get("@grid-rows").find('.grid-row:first [data-fieldname="fieldname"]').click(); | |||
cy.get("@grid-rows") | |||
.find('.grid-row:first select[data-fieldname="fieldname"]') | |||
.select("Title (Data)"); | |||
.select("Title"); | |||
cy.get("@add-row").click(); | |||
cy.get("@grid-rows").find('.grid-row[data-idx="2"] [data-fieldname="fieldname"]').click(); | |||
cy.get("@grid-rows") | |||
.find('.grid-row[data-idx="2"] select[data-fieldname="fieldname"]') | |||
.select("Public (Check)"); | |||
.select("Public"); | |||
cy.get("@add-row").click(); | |||
cy.get("@grid-rows").find('.grid-row:last [data-fieldname="fieldname"]').click(); | |||
cy.get("@grid-rows") | |||
.find('.grid-row:last select[data-fieldname="fieldname"]') | |||
.select("Content (Text Editor)"); | |||
.select("Content"); | |||
cy.save(); | |||
@@ -171,7 +172,7 @@ context("Web Form", () => { | |||
it("Edit Mode", () => { | |||
cy.visit("/app/web-form/note"); | |||
cy.findByRole("tab", { name: "Form Settings" }).click(); | |||
cy.findByRole("tab", { name: "Settings" }).click(); | |||
cy.get('input[data-fieldname="allow_edit"]').check(); | |||
cy.save(); | |||
@@ -179,7 +180,7 @@ context("Web Form", () => { | |||
cy.visit("/note/Note 1"); | |||
cy.url().should("include", "/note/Note%201"); | |||
cy.get(".web-form-actions a").contains("Edit").click(); | |||
cy.get(".web-form-actions a").contains("Edit Response").click(); | |||
cy.url().should("include", "/note/Note%201/edit"); | |||
// Editable Field | |||
@@ -194,7 +195,7 @@ context("Web Form", () => { | |||
it("Allow Multiple Response", () => { | |||
cy.visit("/app/web-form/note"); | |||
cy.findByRole("tab", { name: "Form Settings" }).click(); | |||
cy.findByRole("tab", { name: "Settings" }).click(); | |||
cy.get('input[data-fieldname="allow_multiple"]').check(); | |||
cy.save(); | |||
@@ -212,7 +213,7 @@ context("Web Form", () => { | |||
it("Allow Delete", () => { | |||
cy.visit("/app/web-form/note"); | |||
cy.findByRole("tab", { name: "Form Settings" }).click(); | |||
cy.findByRole("tab", { name: "Settings" }).click(); | |||
cy.get('input[data-fieldname="allow_delete"]').check(); | |||
cy.save(); | |||
@@ -235,7 +236,7 @@ context("Web Form", () => { | |||
it("Navigate and Submit a WebForm", () => { | |||
cy.visit("/update-profile"); | |||
cy.get(".web-form-actions a").contains("Edit").click(); | |||
cy.get(".web-form-actions a").contains("Edit Response").click(); | |||
cy.fill_field("middle_name", "_Test User"); | |||
@@ -247,7 +248,7 @@ context("Web Form", () => { | |||
cy.call("frappe.tests.ui_test_helpers.update_webform_to_multistep").then(() => { | |||
cy.visit("/update-profile-duplicate"); | |||
cy.get(".web-form-actions a").contains("Edit").click(); | |||
cy.get(".web-form-actions a").contains("Edit Response").click(); | |||
cy.fill_field("middle_name", "_Test User"); | |||
@@ -32,10 +32,7 @@ frappe.get_modal = function (title, content) { | |||
${content} | |||
</div> | |||
<div class="modal-footer hidden"> | |||
<button type="button" class="btn btn-default btn-sm btn-modal-close" data-dismiss="modal"> | |||
<i class="octicon octicon-x visible-xs" style="padding: 1px 0px;"></i> | |||
<span class="hidden-xs">${__("Close")}</span> | |||
</button> | |||
<button type="button" class="btn btn-sm btn-secondary hidden"></button> | |||
<button type="button" class="btn btn-sm btn-primary hidden"></button> | |||
</div> | |||
</div> | |||
@@ -49,11 +46,19 @@ frappe.ui.Dialog = class Dialog extends frappe.ui.Dialog { | |||
return this.$wrapper.find(".modal-footer .btn-primary"); | |||
} | |||
get_secondary_btn() { | |||
return this.$wrapper.find(".modal-footer .btn-secondary"); | |||
} | |||
set_primary_action(label, click) { | |||
this.$wrapper.find(".modal-footer").removeClass("hidden"); | |||
return super.set_primary_action(label, click).removeClass("hidden"); | |||
} | |||
set_secondary_action(click) { | |||
return super.set_secondary_action(click).removeClass("hidden"); | |||
} | |||
make() { | |||
super.make(); | |||
if (this.fields) { | |||
@@ -82,7 +82,7 @@ frappe.ui.form.Control = class BaseControl { | |||
is_null(value) && | |||
!in_list(["HTML", "Image", "Button"], this.df.fieldtype) | |||
) | |||
status = "None"; | |||
status = "Read"; | |||
return status; | |||
} | |||
@@ -93,11 +93,11 @@ frappe.ui.form.ControlColor = class ControlColor extends frappe.ui.form.ControlD | |||
set_formatted_input(value) { | |||
super.set_formatted_input(value); | |||
this.$input.val(value); | |||
this.selected_color.css({ | |||
this.$input?.val(value); | |||
this.selected_color?.css({ | |||
"background-color": value || "transparent", | |||
}); | |||
this.selected_color.toggleClass("no-value", !value); | |||
this.selected_color?.toggleClass("no-value", !value); | |||
} | |||
get_color() { | |||
@@ -294,7 +294,10 @@ frappe.form.formatters = { | |||
let formatted_value = frappe.form.formatters.Text(value); | |||
// to use ql-editor styles | |||
try { | |||
if (!$(formatted_value).find(".ql-editor").length) { | |||
if ( | |||
!$(formatted_value).find(".ql-editor").length && | |||
!$(formatted_value).hasClass("ql-editor") | |||
) { | |||
formatted_value = `<div class="ql-editor read-mode">${formatted_value}</div>`; | |||
} | |||
} catch (e) { | |||
@@ -168,7 +168,7 @@ frappe.ui.Dialog = class Dialog extends frappe.ui.FieldGroup { | |||
set_secondary_action(click) { | |||
this.footer.removeClass("hide"); | |||
this.get_secondary_btn().removeClass("hide").off("click").on("click", click); | |||
return this.get_secondary_btn().removeClass("hide").off("click").on("click", click); | |||
} | |||
set_secondary_action_label(label) { | |||
@@ -72,7 +72,6 @@ frappe.warn = function (title, message_html, proceed_action, primary_label, is_m | |||
d.$body.append(`<div class="frappe-confirm-message">${message_html}</div>`); | |||
d.standard_actions.find(".btn-primary").removeClass("btn-primary").addClass("btn-danger"); | |||
d.standard_actions.find(".btn-primary").removeClass("btn-primary").addClass("btn-danger"); | |||
d.show(); | |||
return d; | |||
@@ -24,10 +24,10 @@ export default class WebForm extends frappe.ui.FieldGroup { | |||
super.make(); | |||
this.set_page_breaks(); | |||
this.set_field_values(); | |||
this.setup_listeners(); | |||
if (this.is_new || this.is_form_editable) { | |||
if (this.is_new || this.in_edit_mode) { | |||
this.setup_primary_action(); | |||
this.setup_discard_action(); | |||
} | |||
this.setup_previous_next_button(); | |||
@@ -35,6 +35,7 @@ export default class WebForm extends frappe.ui.FieldGroup { | |||
// webform client script | |||
frappe.init_client_script && frappe.init_client_script(); | |||
this.setup_listeners(); | |||
frappe.web_form.events.trigger("after_load"); | |||
this.after_load && this.after_load(); | |||
} | |||
@@ -43,34 +44,39 @@ export default class WebForm extends frappe.ui.FieldGroup { | |||
let field = this.fields_dict[fieldname]; | |||
field.df.change = () => { | |||
handler(field, field.value); | |||
this.make_form_dirty(); | |||
}; | |||
} | |||
setup_listeners() { | |||
// Event listener for triggering Save/Next button for Multi Step Forms | |||
// Do not use `on` event here since that can be used by user which will render this function useless | |||
// setTimeout has 200ms delay so that all the base_control triggers for the fields have been run | |||
let me = this; | |||
if (!me.is_multi_step_form) { | |||
return; | |||
} | |||
// setup change event for all fields if not already set through client script | |||
this.fields.forEach((field) => { | |||
if (!field.change) { | |||
field.change = () => { | |||
this.make_form_dirty(); | |||
}; | |||
} | |||
}); | |||
} | |||
for (let field of $(".input-with-feedback")) { | |||
$(field).change((e) => { | |||
setTimeout(() => { | |||
e.stopPropagation(); | |||
me.toggle_buttons(); | |||
}, 200); | |||
}); | |||
} | |||
make_form_dirty() { | |||
frappe.form_dirty = true; | |||
$(".indicator-pill.orange").removeClass("hide"); | |||
} | |||
set_page_breaks() { | |||
if (this.page_breaks.length) return; | |||
this.page_breaks = $(".page-break"); | |||
if (this.page_breaks.length) { | |||
this.page_breaks.each((i, page_break) => { | |||
if (!$(page_break).find("form").length) { | |||
$(page_break).remove(); | |||
} | |||
}); | |||
} | |||
this.page_breaks = $(`.page-break`); | |||
this.is_multi_step_form = true; | |||
this.page_breaks = $(".page-break"); | |||
this.is_multi_step_form = !!this.page_breaks.length; | |||
} | |||
setup_previous_next_button() { | |||
@@ -80,15 +86,19 @@ export default class WebForm extends frappe.ui.FieldGroup { | |||
return; | |||
} | |||
$(".web-form-footer .web-form-actions .left-area").prepend(` | |||
<button class="btn btn-default btn-previous btn-md mr-2">${__("Previous")}</button> | |||
`); | |||
this.$next_button = $(`<button class="btn btn-default btn-next btn-sm ml-2"> | |||
${__("Next")} | |||
</button>`); | |||
this.$previous_button = $(`<button class="btn btn-default btn-previous btn-sm"> | |||
${__("Previous")} | |||
</button>`); | |||
$(".web-form-footer .web-form-actions .right-area").prepend(` | |||
<button class="btn btn-default btn-next btn-md">${__("Next")}</button> | |||
`); | |||
this.$next_button.insertAfter(".web-form-footer .right-area .discard-btn"); | |||
this.in_view_mode && $(".web-form-footer .right-area").append(this.$next_button); | |||
$(".web-form-footer .left-area").prepend(this.$previous_button); | |||
$(".btn-previous").on("click", function () { | |||
this.$previous_button.on("click", () => { | |||
let is_validated = me.validate_section(); | |||
if (!is_validated) return false; | |||
@@ -115,7 +125,7 @@ export default class WebForm extends frappe.ui.FieldGroup { | |||
return false; | |||
}); | |||
$(".btn-next").on("click", function () { | |||
this.$next_button.on("click", () => { | |||
let is_validated = me.validate_section(); | |||
if (!is_validated) return false; | |||
@@ -155,7 +165,29 @@ export default class WebForm extends frappe.ui.FieldGroup { | |||
} | |||
setup_primary_action() { | |||
$(".web-form-container").on("submit", () => this.save()); | |||
$(".web-form").on("submit", () => this.save()); | |||
} | |||
setup_discard_action() { | |||
$(".web-form-footer .discard-btn").on("click", () => this.discard_form()); | |||
} | |||
discard_form() { | |||
let path = window.location.href; | |||
// remove new or edit after last / from url | |||
path = path.substring(0, path.lastIndexOf("/")); | |||
if (frappe.form_dirty) { | |||
frappe.warn( | |||
__("Discard?"), | |||
__("Are you sure you want to discard the changes?"), | |||
() => (window.location.href = path), | |||
__("Discard") | |||
); | |||
} else { | |||
window.location.href = path; | |||
} | |||
return false; | |||
} | |||
validate_section() { | |||
@@ -223,8 +255,18 @@ export default class WebForm extends frappe.ui.FieldGroup { | |||
} | |||
render_progress_dots() { | |||
if (!this.is_multi_step_form) return; | |||
$(".center-area.paging").empty(); | |||
if (this.in_view_mode) { | |||
let paging_text = __("Page {0} of {1}", [ | |||
this.current_section + 1, | |||
this.page_breaks.length + 1, | |||
]); | |||
$(".center-area.paging").append(`<div>${paging_text}</div>`); | |||
return; | |||
} | |||
this.$slide_progress = $(`<div class="slides-progress"></div>`).appendTo( | |||
$(".center-area.paging") | |||
); | |||
@@ -246,12 +288,6 @@ export default class WebForm extends frappe.ui.FieldGroup { | |||
} | |||
this.$slide_progress.append($dot); | |||
} | |||
let paging_text = __("Page {0} of {1}", [ | |||
this.current_section + 1, | |||
this.page_breaks.length + 1, | |||
]); | |||
$(".center-area.paging").append(`<div>${paging_text}</div>`); | |||
} | |||
toggle_buttons() { | |||
@@ -290,7 +326,7 @@ export default class WebForm extends frappe.ui.FieldGroup { | |||
show_next_and_hide_save_button() { | |||
$(".btn-next").show(); | |||
$(".submit-btn").hide(); | |||
!this.allow_incomplete && $(".submit-btn").hide(); | |||
} | |||
toggle_previous_button() { | |||
@@ -398,16 +434,16 @@ export default class WebForm extends frappe.ui.FieldGroup { | |||
render_success_page(data) { | |||
if (this.allow_edit && data.name) { | |||
$(".success-page").append(` | |||
<a href="/${this.route}/${data.name}/edit" class="edit-button btn btn-light btn-md ml-2"> | |||
$(".success-footer").append(` | |||
<a href="/${this.route}/${data.name}/edit" class="edit-button btn btn-default btn-md"> | |||
${__("Edit your response", null, "Button in web form")} | |||
</a> | |||
`); | |||
} | |||
if (this.login_required && !this.allow_multiple && !this.show_list && data.name) { | |||
$(".success-page").append(` | |||
<a href="/${this.route}/${data.name}" class="view-button btn btn-light btn-md ml-2"> | |||
$(".success-footer").append(` | |||
<a href="/${this.route}/${data.name}" class="view-button btn btn-default btn-md"> | |||
${__("View your response", null, "Button in web form")} | |||
</a> | |||
`); | |||
@@ -381,7 +381,11 @@ frappe.ui.WebFormListRow = class WebFormListRow { | |||
formatter(this.doc[field.fieldname], field, { only_value: 1 }, this.doc) | |||
)) || | |||
""; | |||
let cell = $(`<td>${value}</td>`); | |||
let cell = $(`<td><p class="ellipsis">${value}</p></td>`); | |||
if (field.fieldtype === "Text Editor") { | |||
value = $(value).addClass("ellipsis"); | |||
cell = $("<td></td>").append(value); | |||
} | |||
cell.appendTo(this.row); | |||
}); | |||
@@ -36,9 +36,6 @@ frappe.ready(function () { | |||
function show_form() { | |||
let web_form = new WebForm({ | |||
parent: $(".web-form-wrapper"), | |||
is_new: web_form_doc.is_new, | |||
is_form_editable: web_form_doc.is_form_editable, | |||
web_form_name: web_form_doc.name, | |||
}); | |||
let doc = reference_doc || {}; | |||
setup_fields(web_form_doc, doc); | |||
@@ -58,7 +55,7 @@ frappe.ready(function () { | |||
function setup_fields(web_form_doc, doc_data) { | |||
web_form_doc.web_form_fields.forEach((df) => { | |||
df.is_web_form = true; | |||
df.read_only = !web_form_doc.is_new && !web_form_doc.is_form_editable; | |||
df.read_only = !web_form_doc.is_new && !web_form_doc.in_edit_mode; | |||
if (df.fieldtype === "Table") { | |||
df.get_data = () => { | |||
let data = []; | |||
@@ -1,361 +1,494 @@ | |||
@import "../common/form"; | |||
[data-doctype="Web Form"] { | |||
.page_content { | |||
max-width: 800px; | |||
margin: auto; | |||
h1 { | |||
font-size: 2.25rem; | |||
margin-top: 0; | |||
margin-bottom: 0; | |||
} | |||
.web-form-banner-image { | |||
margin: -4rem -14rem 5rem; | |||
padding-top: 3rem; | |||
position: relative; | |||
img { | |||
position: absolute; | |||
object-fit: cover; | |||
.page-content-wrapper { | |||
.container { | |||
.page-header { | |||
width: 100%; | |||
height: 250px; | |||
z-index: -1; | |||
} | |||
} | |||
.web-form-header { | |||
border: 1px solid var(--dark-border-color); | |||
border-bottom: none; | |||
border-top-left-radius: var(--border-radius-md); | |||
border-top-right-radius: var(--border-radius-md); | |||
background-color: var(--fg-color); | |||
padding: 2rem 2rem 0; | |||
.breadcrumb-container { | |||
padding: 0px; | |||
margin: 0 0 2rem; | |||
ol.breadcrumb { | |||
padding: 0px; | |||
img { | |||
margin: -1rem 0rem -10.5rem; | |||
object-fit: cover; | |||
width: 100%; | |||
height: 250px; | |||
z-index: -1; | |||
} | |||
} | |||
.web-form-head { | |||
border-bottom: 1px solid var(--dark-border-color); | |||
padding-bottom: 1.25rem; | |||
.page_content { | |||
max-width: 800px; | |||
margin: auto; | |||
.title { | |||
display: flex; | |||
justify-content: space-between; | |||
h1 { | |||
font-size: 2.25rem; | |||
margin-top: 0; | |||
margin-bottom: 0; | |||
padding-bottom: 2px; | |||
} | |||
.web-form-introduction { | |||
color: var(--text-muted); | |||
margin-top: 1.25rem; | |||
.web-form-header { | |||
border: 1px solid var(--dark-border-color); | |||
border-bottom: none; | |||
border-top-left-radius: var(--border-radius-md); | |||
border-top-right-radius: var(--border-radius-md); | |||
background-color: var(--fg-color); | |||
padding: 2rem 2rem 0; | |||
p { | |||
color: var(--text-muted); | |||
.breadcrumb-container { | |||
padding: 0px; | |||
margin: 0 0 2rem; | |||
ol.breadcrumb { | |||
padding: 0px; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
.web-form { | |||
background-color: var(--fg-color); | |||
padding: 1.25rem 2rem 2rem; | |||
border: 1px solid var(--dark-border-color); | |||
border-top: none; | |||
border-bottom-left-radius: var(--border-radius); | |||
border-bottom-right-radius: var(--border-radius); | |||
.web-form-wrapper { | |||
.form-control { | |||
color: var(--text-color); | |||
background-color: var(--control-bg); | |||
} | |||
.web-form-head { | |||
border-bottom: 1px solid var(--dark-border-color); | |||
padding-bottom: 1.25rem; | |||
.form-section { | |||
.section-head { | |||
font-weight: bold; | |||
font-size: var(--text-xl); | |||
padding: var(--padding-md) 0; | |||
} | |||
} | |||
.title { | |||
display: flex; | |||
justify-content: space-between; | |||
flex-wrap: wrap; | |||
gap: 1rem; | |||
.form-column { | |||
padding: 0 var(--padding-sm); | |||
.web-form-title p { | |||
margin-bottom: 0; | |||
} | |||
&:first-child { | |||
padding-left: 0; | |||
} | |||
.indicator-pill { | |||
margin-top: 7px; | |||
} | |||
&:last-child { | |||
padding-right: 0; | |||
} | |||
.web-form-actions { | |||
display: flex; | |||
align-items: center; | |||
justify-content: flex-end; | |||
flex: 1; | |||
.btn { | |||
font-size: var(--text-base); | |||
} | |||
} | |||
} | |||
@include media-breakpoint-down(sm) { | |||
padding: 0; | |||
.web-form-introduction { | |||
color: var(--text-muted); | |||
margin-top: 1.25rem; | |||
p { | |||
color: var(--text-muted); | |||
} | |||
} | |||
} | |||
} | |||
.web-form-skeleton { | |||
.box-group { | |||
display: flex; | |||
gap: 20px; | |||
margin-bottom: 15px; | |||
.web-form { | |||
background-color: var(--fg-color); | |||
padding: 1.25rem 2rem 2rem; | |||
border: 1px solid var(--dark-border-color); | |||
border-top: none; | |||
border-bottom-left-radius: var(--border-radius); | |||
border-bottom-right-radius: var(--border-radius); | |||
.web-form-wrapper { | |||
.form-control { | |||
color: var(--text-color); | |||
background-color: var(--control-bg); | |||
} | |||
.box-container { | |||
width: 100%; | |||
.form-section { | |||
.section-head { | |||
font-weight: bold; | |||
font-size: var(--text-xl); | |||
padding: var(--padding-md) 0; | |||
} | |||
} | |||
.box { | |||
background-color: var(--control-bg); | |||
border-radius: var(--border-radius); | |||
.form-column { | |||
padding: 0 var(--padding-sm); | |||
.frappe-control[data-fieldtype="Rating"] { | |||
.like-disabled-input { | |||
background-color: unset; | |||
padding-left: 0px; | |||
.rating { | |||
cursor: default; | |||
} | |||
} | |||
} | |||
.box-label { | |||
height: 20px; | |||
width: 100px; | |||
margin-bottom: 0.5rem; | |||
&:first-child { | |||
padding-left: 0; | |||
} | |||
.box-area { | |||
height: 34px; | |||
width: 100%; | |||
&:last-child { | |||
padding-right: 0; | |||
} | |||
@include media-breakpoint-down(xs) { | |||
padding: 0; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
.web-form-footer { | |||
margin-top: 1rem; | |||
.web-form-skeleton { | |||
.box-group { | |||
display: flex; | |||
flex-wrap: wrap; | |||
.web-form-actions { | |||
display: flex; | |||
justify-content: space-between; | |||
.box-container { | |||
width: 100%; | |||
padding: 0 var(--padding-sm); | |||
margin-bottom: 15px; | |||
.btn { | |||
font-size: var(--font-size-base); | |||
&:first-child { | |||
padding-left: 0; | |||
} | |||
&:last-child { | |||
padding-right: 0; | |||
} | |||
@include media-breakpoint-down(xs) { | |||
padding: 0; | |||
} | |||
.box { | |||
background-color: var(--control-bg); | |||
border-radius: var(--border-radius); | |||
} | |||
.box-label { | |||
height: 20px; | |||
width: 100px; | |||
margin-bottom: 0.5rem; | |||
} | |||
.box-area { | |||
height: 34px; | |||
width: 100%; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
.center-area { | |||
padding: 0.5rem; | |||
display: flex; | |||
align-items: center; | |||
.web-form-footer { | |||
margin-top: 1rem; | |||
.slides-progress { | |||
.web-form-actions { | |||
display: flex; | |||
margin-right: .5rem; | |||
.slide-step { | |||
@include flex(flex, center, center, null); | |||
height: 18px; | |||
width: 18px; | |||
border-radius: var(--border-radius-full); | |||
border: 1px solid var(--gray-300); | |||
margin: 0 var(--margin-xs); | |||
background-color: var(--card-bg); | |||
.slide-step-indicator { | |||
height: 6px; | |||
width: 6px; | |||
background-color: var(--gray-300); | |||
border-radius: var(--border-radius-full); | |||
} | |||
justify-content: space-between; | |||
flex-wrap: wrap; | |||
.slide-step-complete { | |||
display: none; | |||
.btn { | |||
font-size: var(--text-base); | |||
} | |||
.icon-xs { | |||
height: 10px; | |||
width: 10px; | |||
} | |||
.btn-link { | |||
padding-left: 0px; | |||
color: var(--text-color); | |||
&:hover { | |||
color: var(--text-on-light-blue); | |||
} | |||
} | |||
&.active { | |||
border: 1px solid var(--primary); | |||
.left-area { | |||
display: flex; | |||
flex: 1; | |||
.slide-step-indicator { | |||
display: block; | |||
background-color: var(--primary); | |||
} | |||
@include media-breakpoint-down(sm) { | |||
order: 1 | |||
} | |||
} | |||
&.step-success:not(.active) { | |||
background-color: var(--primary); | |||
border: 1px solid var(--primary); | |||
.center-area { | |||
display: flex; | |||
align-items: center; | |||
font-size: var(--text-base); | |||
.slides-progress { | |||
display: flex; | |||
.slide-step { | |||
@include flex(flex, center, center, null); | |||
height: 18px; | |||
width: 18px; | |||
border-radius: var(--border-radius-full); | |||
border: 1px solid var(--gray-300); | |||
margin: 0 var(--margin-xs); | |||
background-color: var(--card-bg); | |||
.slide-step-indicator { | |||
height: 6px; | |||
width: 6px; | |||
background-color: var(--gray-300); | |||
border-radius: var(--border-radius-full); | |||
} | |||
.slide-step-indicator { | |||
display: none; | |||
} | |||
.slide-step-complete { | |||
display: none; | |||
.icon-xs { | |||
height: 10px; | |||
width: 10px; | |||
} | |||
} | |||
&.active { | |||
border: 1px solid var(--primary); | |||
.slide-step-indicator { | |||
display: block; | |||
background-color: var(--primary); | |||
} | |||
} | |||
&.step-success:not(.active) { | |||
background-color: var(--primary); | |||
border: 1px solid var(--primary); | |||
.slide-step-complete { | |||
display: flex; | |||
.slide-step-indicator { | |||
display: none; | |||
} | |||
.icon use { | |||
stroke-width: 2; | |||
stroke: var(--white); | |||
.slide-step-complete { | |||
display: flex; | |||
.icon use { | |||
stroke-width: 2; | |||
stroke: var(--white); | |||
} | |||
} | |||
} | |||
@include media-breakpoint-down(xs) { | |||
width: 16px; | |||
height: 16px; | |||
} | |||
} | |||
} | |||
@include media-breakpoint-down(sm) { | |||
order: 0; | |||
width: 100%; | |||
justify-content: center; | |||
margin-bottom: 1.5rem; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
.attachments { | |||
margin-top: 2rem; | |||
padding: 2rem; | |||
border-radius: var(--border-radius); | |||
border: 1px solid var(--dark-border-color); | |||
.attachment { | |||
display: flex; | |||
justify-content: space-between; | |||
gap: 6px; | |||
color: var(--text-muted); | |||
font-size: var(--text-md); | |||
&:hover { | |||
text-decoration: none; | |||
.file-name span { | |||
text-decoration: underline; | |||
.right-area { | |||
display: flex; | |||
justify-content: flex-end; | |||
flex: 1; | |||
@include media-breakpoint-down(sm) { | |||
order: 2 | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
.success-page { | |||
background-color: var(--fg-color); | |||
padding: 2rem; | |||
border: 1px solid var(--dark-border-color); | |||
border-radius: var(--border-radius); | |||
text-align: center; | |||
svg.icon { | |||
width: 5rem; | |||
height: 5rem; | |||
margin: 1rem; | |||
} | |||
.attachments { | |||
margin-top: 2rem; | |||
padding: 2rem; | |||
border-radius: var(--border-radius); | |||
border: 1px solid var(--dark-border-color); | |||
h2 { | |||
margin-top: 0; | |||
margin-bottom: 0; | |||
} | |||
.attachment { | |||
display: flex; | |||
justify-content: space-between; | |||
gap: 6px; | |||
color: var(--text-muted); | |||
font-size: var(--text-md); | |||
.success-message { | |||
margin-bottom: 1.6rem; | |||
} | |||
} | |||
&:hover { | |||
text-decoration: none; | |||
.file-name span { | |||
text-decoration: underline; | |||
} | |||
} | |||
} | |||
} | |||
.web-list-container { | |||
min-height: 470px; | |||
border: 1px solid var(--dark-border-color); | |||
border-radius: var(--border-radius-md); | |||
padding: 2rem; | |||
.success-page { | |||
background-color: var(--fg-color); | |||
padding: 5rem 2rem; | |||
margin-top: 3rem; | |||
border: 1px solid var(--dark-border-color); | |||
border-radius: var(--border-radius); | |||
text-align: center; | |||
.web-list-header { | |||
display: flex; | |||
justify-content: space-between; | |||
border-bottom: 1px solid var(--dark-border-color); | |||
padding-bottom: 1.25rem; | |||
.success-header { | |||
display: flex; | |||
justify-content: center; | |||
align-items: center; | |||
gap: 0.5rem; | |||
margin-top: 1rem; | |||
.web-list-actions { | |||
align-self: center; | |||
} | |||
} | |||
.success-icon { | |||
width: 3rem; | |||
height: 3rem; | |||
margin: 0; | |||
.web-list-filters { | |||
display: flex; | |||
flex-wrap: wrap; | |||
margin: 1.25rem 0; | |||
gap: 10px; | |||
.form-group.frappe-control { | |||
min-width: 145px; | |||
padding: 0px; | |||
margin: 0px; | |||
align-self: center; | |||
.checkbox { | |||
.input-xs { | |||
height: var(--checkbox-size); | |||
@include media-breakpoint-down(sm) { | |||
width: 2rem; | |||
height: 2rem; | |||
} | |||
} | |||
.help-box { | |||
display: none; | |||
.success-title { | |||
margin-top: 0; | |||
margin-bottom: 0; | |||
} | |||
} | |||
.input-xs { | |||
height: 28px; | |||
line-height: 1.2; | |||
.success-body .success-message { | |||
margin: 1rem 0rem 1.5rem; | |||
} | |||
.success-footer a { | |||
margin: 0rem 0.3rem 1rem; | |||
} | |||
} | |||
} | |||
.web-list-table { | |||
overflow: auto; | |||
.web-list-container { | |||
min-height: 470px; | |||
border: 1px solid var(--dark-border-color); | |||
border-radius: var(--border-radius-md); | |||
padding: 2rem; | |||
.table { | |||
border-bottom: 1px solid var(--border-color); | |||
border-top: 1px solid var(--border-color); | |||
.web-list-header { | |||
display: flex; | |||
justify-content: space-between; | |||
flex-wrap: wrap; | |||
gap: 1rem; | |||
border-bottom: 1px solid var(--dark-border-color); | |||
padding-bottom: 1.25rem; | |||
thead tr { | |||
th { | |||
border: 0; | |||
font-size: 13px; | |||
font-weight: normal; | |||
color: var(--text-muted); | |||
.web-list-actions { | |||
display: flex; | |||
align-items: center; | |||
justify-content: flex-end; | |||
flex: 1; | |||
} | |||
} | |||
input[type="checkbox"] { | |||
margin-bottom: -2px; | |||
.web-list-filters { | |||
display: flex; | |||
flex-wrap: wrap; | |||
margin: 1.25rem 0; | |||
gap: 10px; | |||
.form-group.frappe-control { | |||
min-width: 145px; | |||
padding: 0px; | |||
margin: 0px; | |||
align-self: center; | |||
.checkbox { | |||
.input-xs { | |||
height: var(--checkbox-size); | |||
} | |||
.help-box { | |||
display: none; | |||
} | |||
} | |||
.input-xs { | |||
height: 28px; | |||
line-height: 1.2; | |||
} | |||
} | |||
} | |||
tbody tr { | |||
color: var(--text-color); | |||
cursor: pointer; | |||
.web-list-table { | |||
overflow: auto; | |||
td { | |||
font-size: 13px; | |||
.table { | |||
border-bottom: 1px solid var(--border-color); | |||
border-top: 1px solid var(--border-color); | |||
thead tr { | |||
th { | |||
border: 0; | |||
font-size: 13px; | |||
font-weight: normal; | |||
color: var(--text-muted); | |||
input[type="checkbox"] { | |||
margin-bottom: -2px; | |||
} | |||
} | |||
} | |||
tbody tr { | |||
color: var(--text-color); | |||
cursor: pointer; | |||
td { | |||
font-size: 13px; | |||
border-top: 1px solid var(--border-color); | |||
max-width: 160px; | |||
.ql-editor, p { | |||
width: max-content; | |||
max-width: 150px; | |||
margin-bottom: 0; | |||
&.read-mode { | |||
display: inline-flex; | |||
gap: 5px; | |||
} | |||
} | |||
} | |||
} | |||
input[type="checkbox"] { | |||
margin-top: 2px; | |||
} | |||
.list-col-checkbox { | |||
width: 1rem; | |||
} | |||
.list-col-serial { | |||
width: 1.5rem; | |||
} | |||
} | |||
} | |||
input[type="checkbox"] { | |||
margin-top: 2px; | |||
.no-result { | |||
min-height: 330px; | |||
border-top: 1px solid var(--border-color); | |||
} | |||
} | |||
.list-col-checkbox { | |||
width: 1rem; | |||
.web-list-footer { | |||
text-align: right; | |||
} | |||
} | |||
.list-col-serial { | |||
width: 1.5rem; | |||
.breadcrumb-container.container { | |||
@include media-breakpoint-up(sm) { | |||
padding-left: 0; | |||
} | |||
} | |||
.no-result { | |||
min-height: 330px; | |||
border-top: 1px solid var(--border-color); | |||
@include media-breakpoint-down(lg) { | |||
padding-left: 1.5rem; | |||
padding-right: 1.5rem; | |||
} | |||
} | |||
.web-list-footer { | |||
text-align: right; | |||
} | |||
} | |||
.breadcrumb-container.container { | |||
@include media-breakpoint-up(sm) { | |||
@include media-breakpoint-down(lg) { | |||
padding-left: 0; | |||
padding-right: 0; | |||
} | |||
} | |||
} | |||
@@ -2,49 +2,44 @@ | |||
{% block breadcrumbs %}{% endblock %} | |||
{% block header %} | |||
{% if banner_image %} | |||
<!-- banner image --> | |||
<img class="web-form-banner-image" src="{{ banner_image }}" alt="Banner Image"> | |||
{% endif %} | |||
{% endblock %} | |||
{% macro header_buttons() %} | |||
{% if allow_print and not is_new %} | |||
{% if allow_edit and in_view_mode %} | |||
<!-- edit button --> | |||
<a href="/{{ route }}/{{ doc_name }}/edit" class="edit-button btn btn-default btn-sm">{{ _("Edit Response", null, "Button in web form") }}</a> | |||
{% endif %} | |||
{% if allow_print and in_view_mode %} | |||
{% set print_format_url = "/printview?doctype=" + doc_type + "&name=" + doc_name + "&format=" + print_format %} | |||
<!-- print button --> | |||
<a href="{{ print_format_url }}" target="_blank" class="print-btn btn btn-light btn-sm ml-2"> | |||
<a href="{{ print_format_url }}" target="_blank" class="print-btn btn btn-default btn-sm ml-2"> | |||
<svg class="icon icon-sm"><use href="#icon-printer"></use></svg> | |||
</a> | |||
{% endif %} | |||
{% if allow_edit and doc_name and not is_form_editable %} | |||
<!-- edit button --> | |||
<a href="/{{ route }}/{{ doc_name }}/edit" class="edit-button btn btn-primary btn-sm ml-2">{{ _("Edit", null, "Button in web form") }}</a> | |||
{% endif %} | |||
{% endmacro %} | |||
{% macro action_buttons() %} | |||
{% if is_new or is_form_editable %} | |||
<div class="left-area"> | |||
<!-- clear button --> | |||
<a href="/{{ path }}" class="clear-btn btn btn-light btn-md"> | |||
{% if is_form_editable %} | |||
{{ _("Reset Form", null, "Button in web form") }} | |||
{% else %} | |||
{{ _("Clear Form", null, "Button in web form") }} | |||
{% endif %} | |||
</a> | |||
</div> | |||
<div class="center-area paging"></div> | |||
<div class="right-area"> | |||
<div class="left-area"></div> | |||
<div class="center-area paging"></div> | |||
<div class="right-area"> | |||
{% if not in_view_mode %} | |||
<!-- discard button --> | |||
<button class="discard-btn btn btn-default btn-sm"> | |||
{{ _("Discard", null, "Button in web form") }} | |||
</button> | |||
<!-- submit button --> | |||
<button type="submit" class="submit-btn btn btn-primary btn-md ml-2">{{ button_label or _("Submit", null, "Button in web form") }}</button> | |||
</div> | |||
{% endif %} | |||
<button type="submit" class="submit-btn btn btn-primary btn-sm ml-2">{{ button_label or _("Submit", null, "Button in web form") }}</button> | |||
{% endif %} | |||
</div> | |||
{% endmacro %} | |||
{% block page_content %} | |||
<!-- banner image --> | |||
{% if banner_image %} | |||
<div class="web-form-banner-image"> | |||
<img src="{{ banner_image }}" alt="Banner Image"> | |||
</div> | |||
{% endif %} | |||
<!-- web form container --> | |||
<div class="web-form-container"> | |||
<!-- breadcrumb --> | |||
@@ -61,12 +56,20 @@ | |||
{% endif %} | |||
<div class="web-form-head"> | |||
<div class="title"> | |||
<h1>{{ _(title) }}</h1> | |||
<div class="web-form-title ellipsis"> | |||
{% if show_list and not is_new %} | |||
<h1 class="ellipsis">{{ _(web_form_title) }}</h1> | |||
<p class="ellipsis">{{ _(title) }}</p> | |||
{% else %} | |||
<h1 class="ellipsis">{{ _(title) }}</h1> | |||
{% endif %} | |||
</div> | |||
<span class="indicator-pill orange hide">Not Saved</span> | |||
<div class="web-form-actions"> | |||
{{ header_buttons() }} | |||
</div> | |||
</div> | |||
{% if is_new and introduction_text %} | |||
{% if introduction_text and (is_new or in_edit_mode) %} | |||
<div class="web-form-introduction">{{ introduction_text }}</div> | |||
{% endif %} | |||
</div> | |||
@@ -115,30 +118,37 @@ | |||
<!-- success page --> | |||
<div class="success-page hide"> | |||
<svg class="icon"> | |||
<use href="#icon-solid-success"></use> | |||
</svg> | |||
<h2 class="success-title">{{ _(success_title) or _("Submitted") }}</h2> | |||
<p class="success-message">{{ _(success_message) or _("Thank you for spending your valuable time to fill this form") }}</p> | |||
{% if success_url %} | |||
<div class="success_url_message"> | |||
<p> | |||
<span>Click on this </span> | |||
<a href="{{ success_url }}">{{_("URL")}}</a> | |||
<span> if you are not redirected within </span> | |||
<span class="time">5</span> | |||
<span> seconds.</span> | |||
</p> | |||
</div> | |||
{% else %} | |||
{% if show_list %} | |||
<a href="/{{ route }}/list" class="show-list-button btn btn-light btn-md mr-2">{{ _("See previous responses", null, "Button in web form") }}</a> | |||
{% endif %} | |||
{% if not login_required or allow_multiple %} | |||
<a href="/{{ route }}/new" class="new-btn btn btn-light btn-md">{{ _("Submit another response", null, "Button in web form") }}</a> | |||
<div class="success-header"> | |||
<svg class="success-icon icon"> | |||
<use href="#icon-solid-success"></use> | |||
</svg> | |||
<h2 class="success-title">{{ _(success_title) or _("Submitted") }}</h2> | |||
</div> | |||
<div class="success-body"> | |||
<p class="success-message">{{ _(success_message) or _("Thank you for spending your valuable time to fill this form") }}</p> | |||
</div> | |||
<div class="success-footer"> | |||
{% if success_url %} | |||
<div class="success_url_message"> | |||
<p> | |||
<span>Click on this </span> | |||
<a href="{{ success_url }}">{{_("URL")}}</a> | |||
<span> if you are not redirected within </span> | |||
<span class="time">5</span> | |||
<span> seconds.</span> | |||
</p> | |||
</div> | |||
{% else %} | |||
{% if show_list %} | |||
<a href="/{{ route }}/list" class="show-list-button btn btn-default btn-md">{{ _("See previous responses", null, "Button in web form") }}</a> | |||
{% endif %} | |||
{% if not login_required or allow_multiple %} | |||
<a href="/{{ route }}/new" class="new-btn btn btn-default btn-md">{{ _("Submit another response", null, "Button in web form") }}</a> | |||
{% endif %} | |||
{% endif %} | |||
{% endif %} | |||
</div> | |||
</div> | |||
{% endblock page_content %} | |||
@@ -1,10 +1,10 @@ | |||
<div class="web-form-skeleton"> | |||
<div class="box-group"> | |||
<div class="box-container"> | |||
<div class="box-container col-sm-6"> | |||
<div class="box box-label"></div> | |||
<div class="box box-area"></div> | |||
</div> | |||
<div class="box-container"> | |||
<div class="box-container col-sm-6"> | |||
<div class="box box-label"></div> | |||
<div class="box box-area"></div> | |||
</div> | |||
@@ -16,21 +16,21 @@ | |||
</div> | |||
</div> | |||
<div class="box-group"> | |||
<div class="box-container"> | |||
<div class="box-container col-sm-6"> | |||
<div class="box box-label"></div> | |||
<div class="box box-area"></div> | |||
</div> | |||
<div class="box-container"> | |||
<div class="box-container col-sm-6"> | |||
<div class="box box-label"></div> | |||
<div class="box box-area"></div> | |||
</div> | |||
</div> | |||
<div class="box-group"> | |||
<div class="box-container"> | |||
<div class="box-container col-sm-6"> | |||
<div class="box box-label"></div> | |||
<div class="box box-area"></div> | |||
</div> | |||
<div class="box-container"> | |||
<div class="box-container col-sm-6"> | |||
<div class="box box-label"></div> | |||
<div class="box box-area"></div> | |||
</div> | |||
@@ -7,7 +7,9 @@ | |||
<div class="web-list-container"> | |||
<!-- list --> | |||
<div class="web-list-header"> | |||
<h1>{{ _(list_title or title) }}</h1> | |||
<div class="web-list-title ellipsis"> | |||
<h1 class="ellipsis">{{ _(list_title or title) }}</h1> | |||
</div> | |||
<div class="web-list-actions"> | |||
{%- if allow_multiple -%} | |||
<a class="btn btn-primary btn-sm button-new" href="/{{ route }}/new">New</a> | |||
@@ -71,7 +71,7 @@ class TestWebForm(unittest.TestCase): | |||
def test_webform_render(self): | |||
set_request(method="GET", path="manage-events/new") | |||
content = get_response_content("manage-events/new") | |||
self.assertIn("<h1>New Manage Events</h1>", content) | |||
self.assertIn('<h1 class="ellipsis">New Manage Events</h1>', content) | |||
self.assertIn('data-doctype="Web Form"', content) | |||
self.assertIn('data-path="manage-events/new"', content) | |||
self.assertIn('source-type="Generator"', content) |
@@ -1,4 +1,28 @@ | |||
frappe.ui.form.on("Web Form", { | |||
setup: function () { | |||
frappe.meta.docfield_map["Web Form Field"].fieldtype.formatter = (value) => { | |||
const prefix = { | |||
"Page Break": "--red-600", | |||
"Section Break": "--blue-600", | |||
"Column Break": "--yellow-600", | |||
}; | |||
if (prefix[value]) { | |||
value = `<span class="bold" style="color: var(${prefix[value]})">${value}</span>`; | |||
} | |||
return value; | |||
}; | |||
frappe.meta.docfield_map["Web Form Field"].fieldname.formatter = (value) => { | |||
if (!value) return; | |||
return frappe.unscrub(value); | |||
}; | |||
frappe.meta.docfield_map["Web Form List Column"].fieldname.formatter = (value) => { | |||
if (!value) return; | |||
return frappe.unscrub(value); | |||
}; | |||
}, | |||
refresh: function (frm) { | |||
// show is-standard only if developer mode | |||
frm.get_field("is_standard").toggle(frappe.boot.developer_mode); | |||
@@ -32,6 +56,14 @@ frappe.ui.form.on("Web Form", { | |||
frm.scroll_to_field("web_form_fields"); | |||
frappe.throw(__("Atleast one field is required in Web Form Fields Table")); | |||
} | |||
let page_break_count = frm.doc.web_form_fields.filter( | |||
(f) => f.fieldtype == "Page Break" | |||
).length; | |||
if (page_break_count >= 10) { | |||
frappe.throw(__("There can be only 9 Page Break fields in a Web Form")); | |||
} | |||
}, | |||
add_publish_button(frm) { | |||
@@ -97,7 +129,7 @@ frappe.ui.form.on("Web Form", { | |||
get_fields_for_doctype(doc.doc_type).then((fields) => { | |||
let as_select_option = (df) => ({ | |||
label: df.label + " (" + df.fieldtype + ")", | |||
label: df.label, | |||
value: df.fieldname, | |||
}); | |||
update_options(fields.map(as_select_option)); | |||
@@ -147,9 +179,19 @@ frappe.ui.form.on("Web Form List Column", { | |||
frappe.ui.form.on("Web Form Field", { | |||
fieldtype: function (frm, doctype, name) { | |||
var doc = frappe.get_doc(doctype, name); | |||
let doc = frappe.get_doc(doctype, name); | |||
if (doc.fieldtype == "Page Break") { | |||
let page_break_count = frm.doc.web_form_fields.filter( | |||
(f) => f.fieldtype == "Page Break" | |||
).length; | |||
page_break_count >= 10 && | |||
frappe.throw(__("There can be only 9 Page Break fields in a Web Form")); | |||
} | |||
if (["Section Break", "Column Break", "Page Break"].includes(doc.fieldtype)) { | |||
doc.fieldname = ""; | |||
doc.label = ""; | |||
doc.options = ""; | |||
frm.refresh_field("web_form_fields"); | |||
} | |||
@@ -188,23 +230,18 @@ function get_fields_for_doctype(doctype) { | |||
function render_list_settings_message(frm) { | |||
// render list setting message | |||
if (frm.fields_dict["list_setting_message"] && !frm.doc.login_required) { | |||
const switch_to_form_settings_tab = ` | |||
<span class="bold pointer" title="${__("Switch to Form Settings Tab")}"> | |||
${__("Form Settings Tab")} | |||
</span> | |||
const go_to_login_required_field = ` | |||
<code class="pointer" title="${__("Go to Login Required field")}"> | |||
${__("login_required")} | |||
</code> | |||
`; | |||
let message = __( | |||
"Login is required to see web form list view. Enable {0} to see list settings", | |||
[go_to_login_required_field] | |||
); | |||
$(frm.fields_dict["list_setting_message"].wrapper) | |||
.html( | |||
$( | |||
`<div class="form-message blue"> | |||
${__( | |||
"Login is required to see web form list view. Enable <code>login_required</code> from {0} to see list settings", | |||
[switch_to_form_settings_tab] | |||
)} | |||
</div>` | |||
) | |||
) | |||
.find("span") | |||
.html($(`<div class="form-message blue">${message}</div>`)) | |||
.find("code") | |||
.click(() => frm.scroll_to_field("login_required")); | |||
} else { | |||
$(frm.fields_dict["list_setting_message"].wrapper).empty(); | |||
@@ -5,50 +5,50 @@ | |||
"document_type": "Document", | |||
"engine": "InnoDB", | |||
"field_order": [ | |||
"title_and_route_tab", | |||
"form_tab", | |||
"title", | |||
"route", | |||
"published", | |||
"column_break_4", | |||
"column_break_1", | |||
"doc_type", | |||
"module", | |||
"is_standard", | |||
"introduction", | |||
"section_break_1", | |||
"introduction_text", | |||
"form_settings_tab", | |||
"web_form_fields", | |||
"settings_tab", | |||
"login_required", | |||
"allow_multiple", | |||
"allow_edit", | |||
"allow_delete", | |||
"column_break_18", | |||
"column_break_2", | |||
"apply_document_permissions", | |||
"allow_print", | |||
"print_format", | |||
"allow_comments", | |||
"show_attachments", | |||
"allow_incomplete", | |||
"form_fields", | |||
"web_form_fields", | |||
"section_break_2", | |||
"max_attachment_size", | |||
"list_settings_tab", | |||
"section_break_3", | |||
"list_setting_message", | |||
"show_list", | |||
"list_title", | |||
"list_columns", | |||
"sidebar_settings_tab", | |||
"section_break_4", | |||
"show_sidebar", | |||
"website_sidebar", | |||
"customization_tab", | |||
"button_label", | |||
"banner_image", | |||
"column_break_37", | |||
"column_break_3", | |||
"breadcrumbs", | |||
"section_break_43", | |||
"section_break_5", | |||
"success_title", | |||
"success_url", | |||
"column_break_41", | |||
"column_break_4", | |||
"success_message", | |||
"scripting_style_tab", | |||
"section_break_6", | |||
"client_script", | |||
"custom_css" | |||
], | |||
@@ -81,10 +81,6 @@ | |||
"label": "Module", | |||
"options": "Module Def" | |||
}, | |||
{ | |||
"fieldname": "column_break_4", | |||
"fieldtype": "Column Break" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "is_standard", | |||
@@ -158,12 +154,6 @@ | |||
"fieldtype": "Check", | |||
"label": "Allow Incomplete Forms" | |||
}, | |||
{ | |||
"collapsible": 1, | |||
"fieldname": "introduction", | |||
"fieldtype": "Section Break", | |||
"label": "Introduction" | |||
}, | |||
{ | |||
"fieldname": "introduction_text", | |||
"fieldtype": "Text Editor", | |||
@@ -250,21 +240,6 @@ | |||
"label": "List Columns", | |||
"options": "Web Form List Column" | |||
}, | |||
{ | |||
"fieldname": "title_and_route_tab", | |||
"fieldtype": "Tab Break", | |||
"label": "Title & Route" | |||
}, | |||
{ | |||
"collapsible": 1, | |||
"fieldname": "form_fields", | |||
"fieldtype": "Section Break", | |||
"label": "Form Fields" | |||
}, | |||
{ | |||
"fieldname": "column_break_18", | |||
"fieldtype": "Column Break" | |||
}, | |||
{ | |||
"fieldname": "website_sidebar", | |||
"fieldtype": "Link", | |||
@@ -277,62 +252,89 @@ | |||
"label": "List Setting Message" | |||
}, | |||
{ | |||
"fieldname": "form_settings_tab", | |||
"fieldname": "customization_tab", | |||
"fieldtype": "Tab Break", | |||
"label": "Form Settings" | |||
"label": "Customization" | |||
}, | |||
{ | |||
"collapsible": 1, | |||
"collapsible_depends_on": "show_list", | |||
"fieldname": "list_settings_tab", | |||
"fieldtype": "Tab Break", | |||
"label": "List Settings" | |||
"fieldname": "success_title", | |||
"fieldtype": "Data", | |||
"label": "Success Title" | |||
}, | |||
{ | |||
"collapsible": 1, | |||
"fieldname": "sidebar_settings_tab", | |||
"fieldtype": "Tab Break", | |||
"label": "Sidebar Settings" | |||
"fieldname": "banner_image", | |||
"fieldtype": "Attach Image", | |||
"label": "Banner Image" | |||
}, | |||
{ | |||
"fieldname": "scripting_style_tab", | |||
"fieldname": "form_tab", | |||
"fieldtype": "Tab Break", | |||
"label": "Scripting / Style" | |||
"label": "Form" | |||
}, | |||
{ | |||
"fieldname": "customization_tab", | |||
"fieldname": "column_break_1", | |||
"fieldtype": "Column Break" | |||
}, | |||
{ | |||
"fieldname": "section_break_1", | |||
"fieldtype": "Section Break" | |||
}, | |||
{ | |||
"fieldname": "settings_tab", | |||
"fieldtype": "Tab Break", | |||
"label": "Customization" | |||
"label": "Settings" | |||
}, | |||
{ | |||
"fieldname": "success_title", | |||
"fieldtype": "Data", | |||
"label": "Success Title" | |||
"fieldname": "column_break_2", | |||
"fieldtype": "Column Break" | |||
}, | |||
{ | |||
"fieldname": "banner_image", | |||
"fieldtype": "Attach Image", | |||
"label": "Banner Image" | |||
"collapsible": 1, | |||
"fieldname": "section_break_2", | |||
"fieldtype": "Section Break" | |||
}, | |||
{ | |||
"fieldname": "column_break_41", | |||
"collapsible": 1, | |||
"collapsible_depends_on": "show_list", | |||
"fieldname": "section_break_3", | |||
"fieldtype": "Section Break", | |||
"label": "List Settings" | |||
}, | |||
{ | |||
"collapsible": 1, | |||
"collapsible_depends_on": "show_sidebar", | |||
"fieldname": "section_break_4", | |||
"fieldtype": "Section Break", | |||
"label": "Sidebar Settings" | |||
}, | |||
{ | |||
"fieldname": "column_break_3", | |||
"fieldtype": "Column Break" | |||
}, | |||
{ | |||
"fieldname": "section_break_43", | |||
"collapsible": 1, | |||
"collapsible_depends_on": "eval: doc.success_title || doc.success_message || doc.success_url", | |||
"fieldname": "section_break_5", | |||
"fieldtype": "Section Break", | |||
"label": "After Submission" | |||
}, | |||
{ | |||
"fieldname": "column_break_37", | |||
"fieldname": "column_break_4", | |||
"fieldtype": "Column Break" | |||
}, | |||
{ | |||
"collapsible": 1, | |||
"collapsible_depends_on": "eval: doc.client_script || doc.custom_css", | |||
"fieldname": "section_break_6", | |||
"fieldtype": "Section Break", | |||
"label": "Scripting / Style" | |||
} | |||
], | |||
"has_web_view": 1, | |||
"icon": "icon-edit", | |||
"is_published_field": "published", | |||
"links": [], | |||
"modified": "2022-08-11 16:27:25.914627", | |||
"modified": "2022-08-17 18:58:49.451658", | |||
"modified_by": "Administrator", | |||
"module": "Website", | |||
"name": "Web Form", | |||
@@ -124,7 +124,8 @@ def get_context(context): | |||
def get_context(self, context): | |||
"""Build context to render the `web_form.html` template""" | |||
context.is_form_editable = False | |||
context.in_edit_mode = False | |||
context.in_view_mode = False | |||
self.set_web_form_module() | |||
if frappe.form_dict.is_list: | |||
@@ -156,10 +157,14 @@ def get_context(context): | |||
frappe.redirect(f"/{self.route}/new") | |||
if frappe.form_dict.is_edit and not self.allow_edit: | |||
context.in_view_mode = True | |||
frappe.redirect(f"/{self.route}/{frappe.form_dict.name}") | |||
if frappe.form_dict.is_edit: | |||
context.is_form_editable = True | |||
context.in_edit_mode = True | |||
if frappe.form_dict.is_read: | |||
context.in_view_mode = True | |||
if ( | |||
not frappe.form_dict.is_edit | |||
@@ -167,7 +172,7 @@ def get_context(context): | |||
and self.allow_edit | |||
and frappe.form_dict.name | |||
): | |||
context.is_form_editable = True | |||
context.in_edit_mode = True | |||
frappe.redirect(f"/{frappe.local.path}/edit") | |||
if ( | |||
@@ -179,6 +184,7 @@ def get_context(context): | |||
): | |||
name = frappe.db.get_value(self.doc_type, {"owner": frappe.session.user}, "name") | |||
if name: | |||
context.in_view_mode = True | |||
frappe.redirect(f"/{self.route}/{name}") | |||
# Show new form when | |||
@@ -203,7 +209,9 @@ def get_context(context): | |||
# load web form doc | |||
context.web_form_doc = self.as_dict(no_nulls=True) | |||
context.web_form_doc.update(dict_with_keys(context, ["is_list", "is_new", "is_form_editable"])) | |||
context.web_form_doc.update( | |||
dict_with_keys(context, ["is_list", "is_new", "in_edit_mode", "in_view_mode"]) | |||
) | |||
if self.show_sidebar and self.website_sidebar: | |||
context.sidebar_items = get_sidebar_items(self.website_sidebar) | |||
@@ -278,17 +286,11 @@ def get_context(context): | |||
if frappe.form_dict.name: | |||
context.doc_name = frappe.form_dict.name | |||
context.reference_doc = frappe.get_doc(self.doc_type, context.doc_name) | |||
context.title = strip_html( | |||
context.reference_doc.get(context.reference_doc.meta.get_title_field()) | |||
context.web_form_title = context.title | |||
context.title = ( | |||
strip_html(context.reference_doc.get(context.reference_doc.meta.get_title_field())) | |||
or context.doc_name | |||
) | |||
if context.is_form_editable and context.parents: | |||
context.parents.append( | |||
{ | |||
"label": _(context.title), | |||
"route": f"{self.route}/{context.doc_name}", | |||
} | |||
) | |||
context.title = _("Editing {0}").format(context.title) | |||
context.reference_doc.add_seen() | |||
context.reference_doctype = context.reference_doc.doctype | |||
context.reference_name = context.reference_doc.name | |||
@@ -309,7 +311,7 @@ def get_context(context): | |||
context.reference_doc.doctype, context.reference_doc.name | |||
) | |||
context.reference_doc = json.loads(context.reference_doc.as_json()) | |||
context.reference_doc = context.reference_doc.as_dict(no_nulls=True) | |||
def add_custom_context_and_script(self, context): | |||
"""Update context from module if standard and append script""" | |||
@@ -481,7 +483,7 @@ def accept(web_form, data, docname=None): | |||
for field in web_form.web_form_fields: | |||
fieldname = field.fieldname | |||
df = meta.get_field(fieldname) | |||
value = data.get(fieldname, None) | |||
value = data.get(fieldname, "") | |||
if df and df.fieldtype in ("Attach", "Attach Image"): | |||
if value and "data:" and "base64" in value: | |||
@@ -32,20 +32,20 @@ | |||
"fieldname": "fieldname", | |||
"fieldtype": "Select", | |||
"in_list_view": 1, | |||
"label": "Fieldname" | |||
"label": "Field" | |||
}, | |||
{ | |||
"fieldname": "fieldtype", | |||
"fieldtype": "Select", | |||
"in_list_view": 1, | |||
"label": "Fieldtype", | |||
"options": "Attach\nAttach Image\nCheck\nCurrency\nData\nDate\nDatetime\nDuration\nFloat\nHTML\nInt\nLink\nPassword\nRating\nSelect\nSignature\nSmall Text\nText\nText Editor\nTable\nTime\nSection Break\nColumn Break\nPage Break" | |||
"options": "Attach\nAttach Image\nCheck\nCurrency\nColor\nData\nDate\nDatetime\nDuration\nFloat\nHTML\nInt\nLink\nPassword\nRating\nSelect\nSignature\nSmall Text\nText\nText Editor\nTable\nTime\nSection Break\nColumn Break\nPage Break" | |||
}, | |||
{ | |||
"fieldname": "label", | |||
"fieldtype": "Data", | |||
"in_list_view": 1, | |||
"label": "Label" | |||
"label": "Custom Label" | |||
}, | |||
{ | |||
"default": "0", | |||
@@ -58,6 +58,7 @@ | |||
"default": "0", | |||
"fieldname": "reqd", | |||
"fieldtype": "Check", | |||
"in_list_view": 1, | |||
"label": "Mandatory" | |||
}, | |||
{ | |||
@@ -146,7 +147,7 @@ | |||
], | |||
"istable": 1, | |||
"links": [], | |||
"modified": "2022-08-10 12:59:51.170546", | |||
"modified": "2022-08-22 17:22:39.026893", | |||
"modified_by": "Administrator", | |||
"module": "Website", | |||
"name": "Web Form Field", | |||
@@ -15,14 +15,14 @@ | |||
"fieldname": "fieldname", | |||
"fieldtype": "Select", | |||
"in_list_view": 1, | |||
"label": "Fieldname", | |||
"label": "Field", | |||
"reqd": 1 | |||
}, | |||
{ | |||
"fieldname": "label", | |||
"fieldtype": "Data", | |||
"in_list_view": 1, | |||
"label": "Label" | |||
"label": "Custom Label" | |||
}, | |||
{ | |||
"fieldname": "fieldtype", | |||
@@ -35,7 +35,7 @@ | |||
"index_web_pages_for_search": 1, | |||
"istable": 1, | |||
"links": [], | |||
"modified": "2022-06-21 17:22:14.978947", | |||
"modified": "2022-08-17 19:09:01.417841", | |||
"modified_by": "Administrator", | |||
"module": "Website", | |||
"name": "Web Form List Column", | |||