Skip to content

Commit eaadc34

Browse files
committed
Added: bootstrap theme form HTML (#118)
1 parent d12f611 commit eaadc34

File tree

2 files changed

+71
-50
lines changed

2 files changed

+71
-50
lines changed

libreforms_fastapi/app/templates/create_form.html.jinja

+11-8
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,24 @@
66

77
{% block content %}
88
<h1>Create Form: {{form_name}}</h1>
9-
<form id="registrationForm">
109

11-
{% for field in form_html %}
10+
<div class="container">
1211

13-
{{field | safe}}
12+
<form id="registrationForm">
1413

15-
{% endfor %}
14+
{% for field in form_html %}
1615

17-
<fieldset style="padding-top: 10px;" class="form-check">
18-
<button type="submit" class="btn btn-primary" id="createButton">Create</button>
19-
</fieldset>
16+
{{field | safe}}
2017

18+
{% endfor %}
2119

20+
<fieldset style="padding-top: 10px;" class="form-check">
21+
<button type="submit" class="btn btn-primary" id="createButton">Create</button>
22+
</fieldset>
2223

23-
</form>
24+
</form>
25+
26+
</div>
2427
{% endblock %}
2528

2629

libreforms_fastapi/utils/pydantic_models.py

+60-42
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,6 @@ def username_pattern(cls, value):
4949
raise ValueError(config.USERNAME_HELPER_TEXT)
5050
return value.lower()
5151

52-
# @validator('password', 'verify_password', pre=True)
53-
# def coerce_to_secret_str(cls, value):
54-
# # This is somewhat redundant, as Pydantic handles coercion to SecretStr,
55-
# if not isinstance(value, SecretStr):
56-
# return SecretStr(value)
57-
# return value
58-
5952
@validator('password', 'verify_password', pre=True, each_item=False)
6053
def password_pattern(cls, value):
6154
# Since value is now of type SecretStr, we need to get its actual value
@@ -80,6 +73,11 @@ def passwords_match(cls, v, values, **kwargs):
8073
"field_name": "text_input",
8174
"default": "Default Text",
8275
"validators": [],
76+
# "validators": {
77+
# "_min_length": None,
78+
# "_max_length": None,
79+
# "_regex": None,
80+
# },
8381
"required": False,
8482
"options": None,
8583
"description": "This is a text field",
@@ -257,70 +255,90 @@ class Config:
257255
return model
258256

259257

260-
def get_form_html(
261-
form_name:str,
262-
config_path:str=config.FORM_CONFIG_PATH,
263-
current_document:dict=None
264-
) -> List[str]:
265-
pass
258+
def get_form_html(form_name:str, config_path:str=config.FORM_CONFIG_PATH, current_document:dict=None) -> List[str]:
266259
"""
267-
Generates a list of HTML form fields based on the input config and form name, supporting default values.
260+
Generates a list of Bootstrap 5 styled HTML form fields based on the input config and form name,
261+
supporting default values.
268262
269263
Params:
270264
current_document (dict): optional document containing the form's existing data. If passed, it will override
271265
the default content of the form config.
272266
273-
Returns: List[str] of HTML elements for front-end
267+
Returns: List[str] of HTML elements for the front-end
274268
"""
275-
form_config = load_form_config(config_path=config.FORM_CONFIG_PATH)
269+
form_config = load_form_config(config_path=config_path)
276270

277271
if form_name not in form_config:
278-
raise Exception(f"Form '{form_name}' not found in")
279-
272+
raise Exception(f"Form '{form_name}' not found in config")
280273

281274
form_html = []
282275

283276
for field_name, field_info in form_config[form_name].items():
284-
285-
if current_document:
286-
default = ""
287-
if field_name in current_document['data'].keys():
288-
default = current_document['data'][field_name]
289-
else:
290-
default = field_info.get("default")
277+
default = current_document['data'][field_name] if current_document and field_name in current_document['data'] else field_info.get("default")
278+
field_html = ""
291279

280+
description_id = f"{field_name}HelpInline"
292281

293282
if field_info['input_type'] in ['text', 'number', 'email', 'date']:
294-
field_html = f'<label for="{field_name}">{field_name.capitalize()}:</label>' \
295-
f'<input type="{field_info["input_type"]}" id="{field_name}" name="{field_name}" value="{default or ""}"><br><br>'
283+
field_html += f'''
284+
<fieldset class="form-check" style="padding-top: 10px;">
285+
<label aria-labelledby="{description_id}" for="{field_name}" class="form-check-label">{field_name.replace("_", " ").capitalize()}</label>
286+
<span id="{description_id}" class="form-text">| {field_info["description"]}</span>
287+
<input type="{field_info["input_type"]}" class="form-control" id="{field_name}" name="{field_name}" value="{default or ''}">
288+
<div class="valid-feedback"></div>
289+
<div class="invalid-feedback"></div>
290+
</fieldset>'''
291+
296292
elif field_info['input_type'] == 'textarea':
297-
field_html = f'<label for="{field_name}">{field_name.capitalize()}:</label><br>' \
298-
f'<textarea id="{field_name}" name="{field_name}" rows="4" cols="50">{default or ""}</textarea><br><br>'
299-
293+
field_html += f'''
294+
<fieldset class="form-check" style="padding-top: 10px;">
295+
<label aria-labelledby="{description_id}" for="{field_name}" class="form-check-label">{field_name.replace("_", " ").capitalize()}</label>
296+
<span id="{description_id}" class="form-text">| {field_info["description"]}</span>
297+
<textarea class="form-control" id="{field_name}" name="{field_name}" rows="4">{default or ''}</textarea>
298+
<div class="valid-feedback"></div>
299+
<div class="invalid-feedback"></div>
300+
</fieldset>'''
301+
300302
elif field_info['input_type'] in ['checkbox', 'radio']:
301-
field_html = f'<label>{field_name.capitalize()}:</label><br>'
303+
field_html += f'''
304+
<fieldset class="form-check" style="padding-top: 10px;">
305+
<label aria-labelledby="{description_id}" for="{field_name}" class="form-check-label">{field_name.replace("_", " ").capitalize()}</label>
306+
<span id="{description_id}" class="form-text">| {field_info["description"]}</span>
307+
'''
302308
for option in field_info['options']:
303309
checked = "checked" if default and (option == default or option in default) else ""
304-
field_html += f'<input type="{field_info["input_type"]}" id="{option}" name="{field_name}" value="{option}" {checked}>' \
305-
f'<label for="{option}">{option}</label><br>'
306-
field_html += '<br>'
310+
field_html += f'''
311+
<div class="form-check {field_info["input_type"]}-form-check">
312+
<input class="form-check-input" type="{field_info["input_type"]}" id="{option}" name="{field_name}" value="{option}" {checked}>
313+
<label class="form-check-label" for="{option}">{option}</label>
314+
</div>
315+
'''
316+
field_html += f'''
317+
</fieldset>
318+
'''
307319

308320
elif field_info['input_type'] == 'select':
309-
field_html = f'<label for="{field_name}">{field_name.capitalize()}:</label>' \
310-
f'<select id="{field_name}" name="{field_name}">'
321+
field_html += f'''
322+
<fieldset class="form-check" style="padding-top: 10px;">
323+
<label aria-labelledby="{description_id}" for="{field_name}" class="form-check-label">{field_name.replace("_", " ").capitalize()}</label>
324+
<span id="{description_id}" class="form-text">| {field_info["description"]}</span>
325+
<select class="form-control" id="{field_name}" name="{field_name}">'''
311326
for option in field_info['options']:
312327
selected = "selected" if default and (option == default or option in default) else ""
313328
field_html += f'<option value="{option}" {selected}>{option}</option>'
314-
field_html += '</select><br><br>'
315-
else:
316-
continue # Skip if the input type is not recognized
317-
318-
form_html.append(field_html)
329+
field_html += '''
330+
</select>
331+
</fieldset>'''
332+
333+
# Skipping file input for now becase it usually doesn't have a default value and handling
334+
# might be different based on requirements
335+
336+
if field_html:
337+
form_html.append(field_html)
319338

320339
return form_html
321340

322341

323-
324342
class HelpRequest(BaseModel):
325343
"""A quick and dirty pydantic model for help request data"""
326344
subject: str

0 commit comments

Comments
 (0)