Initial commit – AufmaßCreater v2.35

This commit is contained in:
2026-06-10 11:03:43 +02:00
commit 84c933ea9c
2823 changed files with 490495 additions and 0 deletions
@@ -0,0 +1,134 @@
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 &amp; 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('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;')