135 lines
5.7 KiB
Python
135 lines
5.7 KiB
Python
import json
|
|
from flask import url_for
|
|
|
|
def render_form(form_json, module_id, aufmass_id=None):
|
|
fields = json.loads(form_json) if isinstance(form_json, str) else form_json
|
|
parts = []
|
|
berechnen_url = url_for("custom_modules.berechnen", module_id=module_id)
|
|
if aufmass_id:
|
|
berechnen_url += f'?aufmass_id={aufmass_id}'
|
|
parts.append(f'<form hx-post="{berechnen_url}" hx-target="#modul-modal-body" hx-swap="innerHTML" class="box custom-module-form">')
|
|
|
|
in_group = False
|
|
for f in fields:
|
|
ftype = f.get('type', 'text')
|
|
name = f.get('name', f.get('id', ''))
|
|
label = f.get('label', '')
|
|
placeholder = f.get('placeholder', '')
|
|
default = f.get('default', '')
|
|
required = f.get('required', False)
|
|
cond = f.get('conditional_show')
|
|
col_size = f.get('columns', '12')
|
|
cond_attrs = ''
|
|
if cond and cond.get('field') and cond.get('value'):
|
|
cond_attrs = f' data-cond-field="{cond["field"]}" data-cond-value="{cond["value"]}" style="display:none"'
|
|
|
|
if ftype == 'group_start':
|
|
if in_group:
|
|
parts.append('</div>')
|
|
collapsible = f.get('collapsible', False)
|
|
title = f.get('title', 'Gruppe')
|
|
parts.append(f'<div class="box" style="padding:10px;background:#f8faff;margin-bottom:10px"{cond_attrs}>')
|
|
parts.append(f'<h5 class="title is-6 mb-2">{title}</h5>')
|
|
parts.append('<div class="columns is-multiline mb-0">')
|
|
in_group = True
|
|
continue
|
|
|
|
if ftype == 'group_end':
|
|
if in_group:
|
|
parts.append('</div>')
|
|
parts.append('</div>')
|
|
in_group = False
|
|
continue
|
|
|
|
if ftype == 'separator':
|
|
parts.append(f'<hr{cond_attrs}>')
|
|
continue
|
|
|
|
if ftype == 'label':
|
|
parts.append(f'<p{cond_attrs} style="margin-bottom:6px">{_esc(f.get("text", ""))}</p>')
|
|
continue
|
|
|
|
req_mark = ' <span class="has-text-danger">*</span>' if required else ''
|
|
field_html = ''
|
|
|
|
if ftype == 'text':
|
|
field_html = f'<input class="input" type="text" name="{_esc(name)}" placeholder="{_esc(placeholder)}" value="{_esc(default)}" style="font-size:0.9rem">'
|
|
|
|
elif ftype == 'number':
|
|
inputmode = f.get('inputmode', 'decimal')
|
|
min_attr = f' min="{f.get("min")}"' if f.get('min') != '' else ''
|
|
max_attr = f' max="{f.get("max")}"' if f.get('max') != '' else ''
|
|
step_attr = f' step="{f.get("step")}"' if f.get('step') and f.get('step') != 'any' else ''
|
|
field_html = f'<input class="input" type="text" inputmode="{inputmode}" name="{_esc(name)}" placeholder="{_esc(placeholder)}" value="{_esc(default)}"{min_attr}{max_attr}{step_attr} style="font-size:0.9rem">'
|
|
|
|
elif ftype == 'checkbox':
|
|
checked = 'checked' if default else ''
|
|
field_html = f'<label class="checkbox"><input type="checkbox" name="{_esc(name)}" value="an" {checked}> {_esc(label)}</label>'
|
|
|
|
elif ftype == 'dropdown':
|
|
opts = f.get('options', [])
|
|
options_html = ''
|
|
for o in opts:
|
|
val = o.get('value', '')
|
|
lbl = o.get('label', val)
|
|
sel = 'selected' if default == val else ''
|
|
options_html += f'<option value="{_esc(val)}" {sel}>{_esc(lbl)}</option>'
|
|
field_html = f'<div class="select is-fullwidth"><select name="{_esc(name)}">{options_html}</select></div>'
|
|
|
|
elif ftype == 'radio':
|
|
opts = f.get('options', [])
|
|
radio_html = ''
|
|
for o in opts:
|
|
val = o.get('value', '')
|
|
lbl = o.get('label', val)
|
|
checked = 'checked' if default == val else ''
|
|
radio_html += f'<label class="radio"><input type="radio" name="{_esc(name)}" value="{_esc(val)}" {checked}> {_esc(lbl)}</label>'
|
|
field_html = radio_html
|
|
|
|
if field_html:
|
|
if col_size != '12' and in_group:
|
|
parts.append(f'<div class="column is-{col_size}"{cond_attrs}><div class="field"><label class="label" style="font-size:0.85rem">{_esc(label)}{req_mark}</label><div class="control">{field_html}</div></div></div>')
|
|
else:
|
|
parts.append(f'<div class="field"{cond_attrs}><label class="label" style="font-size:0.85rem">{_esc(label)}{req_mark}</label><div class="control">{field_html}</div></div>')
|
|
|
|
if in_group:
|
|
parts.append('</div>')
|
|
|
|
parts.append(f'<button class="button is-primary mt-3" type="submit">Berechnen & ins Aufmaß übernehmen</button>')
|
|
parts.append('</form>')
|
|
|
|
cond_js = '''
|
|
<script>
|
|
document.querySelectorAll('.custom-module-form [data-cond-field]').forEach(function(el) {
|
|
var fieldName = el.dataset.condField;
|
|
var condValue = el.dataset.condValue;
|
|
var input = document.querySelector('[name="'+fieldName+'"]');
|
|
if (input) {
|
|
function toggle() {
|
|
var show = false;
|
|
if (input.type === 'checkbox') {
|
|
show = input.checked;
|
|
} else {
|
|
show = (input.value === condValue);
|
|
}
|
|
el.style.display = show ? '' : 'none';
|
|
}
|
|
toggle();
|
|
input.addEventListener('change', toggle);
|
|
if (input.type !== 'checkbox') input.addEventListener('input', toggle);
|
|
}
|
|
});
|
|
document.querySelectorAll('.custom-module-form input[inputmode="decimal"]').forEach(function(el) {
|
|
if (typeof sanitizeNum === 'function') {
|
|
el.addEventListener('input', function() { sanitizeNum(el); });
|
|
}
|
|
});
|
|
</script>'''
|
|
parts.append(cond_js)
|
|
|
|
return '\n'.join(parts)
|
|
|
|
|
|
def _esc(s):
|
|
return str(s).replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"')
|