Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: jinja rendering for print designer #99

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion print_designer/patches.txt
@@ -1,2 +1,3 @@
print_designer.patches.update_white_space_property
print_designer.patches.introduce_barcode
print_designer.patches.introduce_barcode
print_designer.patches.introduce_jinja
14 changes: 14 additions & 0 deletions print_designer/patches/introduce_jinja.py
@@ -0,0 +1,14 @@
import frappe
from print_designer.patches.patch_utils import patch_formats
def execute():
""" Add parseJinja property in DynamicFields (Static) and staticText """
def element_callback(el):
if el.get("type") == "text" and not el.get("isDynamic"):
el["parseJinja"] = False
def dynamic_content_callback(el):
if el.get("is_static", False):
el["parseJinja"] = False
patch_formats({
"element": element_callback,
"dynamic_content": dynamic_content_callback
}, types=["text", "table", "barcode"])
18 changes: 15 additions & 3 deletions print_designer/print_designer/page/print_designer/jinja/main.html
Expand Up @@ -3,7 +3,11 @@
{{ element.classes | join(' ') }}">
<p style="{% if element.isFixedSize %}width:{{ element.width }}px;height:{{ element.height }}px;{% else %}width:fit-content; height:fit-content; max-width: {{ settings.page.width - settings.page.marginLeft - settings.page.marginRight - element.startX }}px; {%endif%} {{convert_css(element.style)}}"
class="staticText {{ element.classes | join(' ') }}">
{{_(element.content)}}
{% if element.parseJinja %}
{{frappe.render_template(element.content, {'doc': doc})}}
{% else %}
{{_(element.content)}}
{% endif %}
</p>
</div>
{%- endmacro %}
Expand Down Expand Up @@ -50,7 +54,11 @@
{% macro render_barcode(element) -%}
{%- set field = element.dynamicContent[0] -%}
{%- if field.is_static -%}
{%- set value = field.value -%}
{% if field.parseJinja %}
{%- set value = frappe.render_template(field.value, {'doc': doc}) -%}
{% else %}
{%- set value = _(field.value) -%}
{% endif %}
{%- elif field.doctype -%}
{%- set value = frappe.db.get_value(field.doctype, doc[field.parentField], field.fieldname) -%}
{%- else -%}
Expand Down Expand Up @@ -112,7 +120,11 @@
{%- endmacro %}
{%- macro render_spanvalue(field, element, row) -%}
{%- if field.is_static -%}
{{ _(field.value) }}
{% if field.parseJinja %}
{{frappe.render_template(field.value, {'doc': doc, 'row': row})}}
{% else %}
{{ _(field.value) }}
{% endif %}
{%- elif field.doctype -%}
{%- set value = _(frappe.db.get_value(field.doctype, doc[field.parentField], field.fieldname)) -%}
{{ frappe.format(value, {'fieldtype': field.fieldtype, 'options': field.options}) }}
Expand Down
Expand Up @@ -91,6 +91,16 @@ def get_barcode(barcode_format, barcode_value, options={}, width=None, height=No
import re
barcode_value = re.search(r'data-barcode-value="(.*?)">', barcode_value).group(1)

if barcode_value == '':
fallback_html_string = '''
<div class="fallback-barcode">
<div class="content">
<span>No Value was Provided to Barcode</span>
</div>
</div>
'''
return { "type": "svg", "value": fallback_html_string }

if barcode_format == "qrcode": return get_qrcode(barcode_value, options, png_base64)

import barcode
Expand Down
46 changes: 46 additions & 0 deletions print_designer/public/js/print_designer/PropertiesPanelState.js
Expand Up @@ -519,6 +519,52 @@ export const createPropertiesPanel = () => {
[colorStyleFrappeControl("Background", "rectangleBackgroundColor", "backgroundColor")],
],
});
MainStore.propertiesPanel.push({
title: "Enable Jinja Parsing",
sectionCondtional: () =>
MainStore.getCurrentElementsId.length === 1 &&
(MainStore.getCurrentElementsValues[0].type === "text" &&
!MainStore.getCurrentElementsValues[0].isDynamic),
fields: [
[
{
label: "Render Jinja",
name: "parseJinja",
labelDirection: "column",
condtional: () =>
MainStore.getCurrentElementsId.length === 1 &&
(MainStore.getCurrentElementsValues[0].type === "text" &&
!MainStore.getCurrentElementsValues[0].isDynamic),
frappeControl: (ref, name) => {
const MainStore = useMainStore();
makeFeild({
name: name,
ref: ref,
fieldtype: "Select",
requiredData: [MainStore.getCurrentElementsValues[0]],
options: () => [
{ label: "Yes", value: "Yes" },
{ label: "No", value: "No" },
],
formatValue: (object, property, isStyle) => {
if (!object) return;
return object[property] ? "Yes" : "No";
},
onChangeCallback: (value = null) => {
if (value && MainStore.getCurrentElementsValues[0]) {
MainStore.getCurrentElementsValues[0]["parseJinja"] =
value === "Yes";
MainStore.frappeControls[name].$input.blur();
}
},
reactiveObject: () => MainStore.getCurrentElementsValues[0],
propertyName: "parseJinja",
});
},
},
],
],
});
MainStore.propertiesPanel.push({
title: "Text Tool",
sectionCondtional: () => MainStore.activeControl === "text",
Expand Down
Expand Up @@ -68,7 +68,7 @@ watch(()=> dynamicContent.value, () => {
}, { deep:true, immediate: true });

watch(() => [value.value, barcodeFormat.value, barcodeColor.value, barcodeBackgroundColor.value], async () => {
if (!value.value || !barcodeFormat.value) return;
if (!barcodeFormat.value) return;
try {
const options = {
background : barcodeBackgroundColor.value || "#ffffff",
Expand All @@ -79,11 +79,26 @@ watch(() => [value.value, barcodeFormat.value, barcodeColor.value, barcodeBackgr
} else {
options["foreground"] = barcodeColor.value || "#000000";
}
let finalValue = value.value;
if (finalValue != '') {
try {
finalValue = frappe.render(finalValue, {doc: MainStore.docData})
} catch (error) {
console.error("Error in Jinja Template\n", { value_string: finalValue, error });
frappe.show_alert(
{
message: "Unable Render Jinja Template. Please Check Console",
indicator: "red",
},
5
);
}
}
let barcode = await frappe.call(
"print_designer.print_designer.page.print_designer.print_designer.get_barcode",
{
barcode_format: barcodeFormat.value,
barcode_value: value.value,
barcode_value: finalValue,
options,
}
);
Expand Down Expand Up @@ -159,12 +174,13 @@ const handleDblClick = (e, element) => {
};
</script>

<style lang="scss" scoped>
.fallback-image {
<style lang="scss" deep>
.fallback-barcode {
width: 100%;
user-select: none;
height: 100%;
display: flex;
overflow: hidden;
align-items: center;
justify-content: center;
background-color: var(--subtle-fg);
Expand Down
Expand Up @@ -81,10 +81,31 @@ const props = defineProps({
default: null,
},
});

const parseJinja = (value, context) => {
if (value == '') return '';
try {
return frappe.render(value, context)
} catch (error) {
console.error("Error in Jinja Template\n", { value_string: value, error });
frappe.show_alert(
{
message: "Unable Render Jinja Template. Please Check Console",
indicator: "red",
},
5
);
return value
}
}

const getHTML = (field, index) => {
if (props.table) {
if (field.is_static) {
return field.value;
if (field.parseJinja) {
return parseJinja(field.value, {doc: MainStore.docData, row: MainStore.docData[props.table.fieldname]?.[index - 1]})
}
return field.value
} else {
if (typeof MainStore.docData[props.table.fieldname]?.[index - 1][field.fieldname] != "undefined"){
return frappe.format(
Expand All @@ -97,10 +118,13 @@ const getHTML = (field, index) => {
return ["Image, Attach Image"].indexOf(field.fieldtype) != -1 ? null : `{{ ${ field.fieldname } }}`;
}
} else {
return (
field.value ||
`{{ ${field.parentField ? field.parentField + "." : ""}${field.fieldname} }}`
);
if (field.is_static) {
if (field.parseJinja) {
return parseJinja(field.value, {doc: MainStore.docData})
}
return field.value
}
return (field.value || `{{ ${field.parentField ? field.parentField + "." : ""}${field.fieldname} }}`);
}
};

Expand Down
Expand Up @@ -34,7 +34,7 @@
:class="['staticText', classes]"
v-if="type == 'text'"
data-placeholder=""
v-html="content"
v-html="getHTML(contenteditable, content, parseJinja )"
></p>
<BaseResizeHandles
v-if="!contenteditable && MainStore.getCurrentElementsId.includes(id)"
Expand Down Expand Up @@ -83,6 +83,7 @@ const {
height,
style,
classes,
parseJinja,
} = toRefs(props.object);

const { setElements } = useElement({
Expand All @@ -97,16 +98,24 @@ const toggleDragResize = (toggle) => {
isResizable.value = toggle;
props.object.contenteditable = !toggle;
};
watch(
() => MainStore.activeControl,
() => {
if (MainStore.activeControl == "text") {
toggleDragResize(false);
} else {
toggleDragResize(true);
const getHTML = (contenteditable, content, parseJinja) => {
if (!contenteditable && content != '' && parseJinja) {
try {
return frappe.render(content, {doc: MainStore.docData})
} catch (error) {
console.error("Error in Jinja Template\n", { value_string: content, error });
frappe.show_alert(
{
message: "Unable Render Jinja Template. Please Check Console",
indicator: "red",
},
5
);
return content
}
}
);
return content
}

const handleMouseDown = (e, element) => {
lockAxis(element, e.shiftKey);
Expand Down Expand Up @@ -169,6 +178,7 @@ const handleBlur = (e) => {
content.value = DOMRef.value.firstElementChild.innerHTML;
MainStore.getCurrentElementsId.includes(id.value) &&
DOMRef.value.classList.add("active-elements");
contenteditable.value = false;
};

const handleKeyDown = (e) => {
Expand Down
Expand Up @@ -73,12 +73,19 @@
</div>
</div>
<div class="footer">
<div class="icons" v-if="!fieldnames[0]?.is_static">
<div @click="addStaticText">
<div class="icons">
<div @click="addStaticText" v-if="!fieldnames[0]?.is_static">
<em style="font-weight: 900">T</em>
<sub style="font-weight: 600; font-size: 1em bottom:-0.15em">+</sub>
<span style="font-size: 12px; padding: 0px 5px">Static Text</span>
</div>
<div v-if="fieldnames[0] && fieldnames[0].is_static" @click="fieldnames[0].parseJinja = !fieldnames[0].parseJinja">
<span
class="jinja-toggle fa fa-code"
:style="[fieldnames[0].parseJinja && 'color:var(--primary)']"
></span>
<span :style="['font-size: 12px; padding: 0px 5px', fieldnames[0].parseJinja && 'color:var(--primary)']">{{fieldnames[0].parseJinja ? "Disable Jinja" : "Render Jinja"}}</span>
</div>
</div>
</div>
</template>
Expand Down Expand Up @@ -156,11 +163,26 @@ const setBarcode = async () => {
} else {
options["foreground"] = barcodeColor.value || "#000000";
}
let value = props.fieldnames[0].value;
if (props.fieldnames[0].parseJinja && value != "") {
try {
value = frappe.render(value, {doc: MainStore.docData})
} catch (error) {
console.error("Error in Jinja Template\n", { value_string: value, error });
frappe.show_alert(
{
message: "Unable Render Jinja Template. Please Check Console",
indicator: "red",
},
5
);
}
}
let barcode = await frappe.call(
"print_designer.print_designer.page.print_designer.print_designer.get_barcode",
{
barcode_format: barcodeFormat.value,
barcode_value: props.fieldnames[0].value,
barcode_value: value,
options,
height: 80.0,
}
Expand Down Expand Up @@ -362,5 +384,25 @@ const addStaticText = (event) => {
max-width: 120px;
max-height: 120px;
}
.fallback-barcode {
width: 100%;
user-select: none;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background-color: var(--subtle-fg);
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;

span {
font-size: smaller;
text-align: center;
}
}
}
}
</style>