Add visual indicators for required fields and ARIA attributes in shift forms
This commit is contained in:
@@ -8,3 +8,34 @@
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
// Mark required fields with a visual asterisk and set ARIA attributes
|
||||
(function(){
|
||||
try{
|
||||
const form = document.currentScript.previousElementSibling;
|
||||
if(!form) return;
|
||||
const requiredEls = form.querySelectorAll('input[required], select[required], textarea[required]');
|
||||
requiredEls.forEach(el=>{
|
||||
el.setAttribute('aria-required','true');
|
||||
// try to find a label: either <label for="id_x"> or a sibling .label
|
||||
const id = el.id;
|
||||
let lbl = id ? form.querySelector('label[for="'+id+'"]') : null;
|
||||
if(!lbl){
|
||||
// look for nearest label element in the same .field
|
||||
const field = el.closest('.field');
|
||||
if(field) lbl = field.querySelector('label');
|
||||
}
|
||||
if(lbl){
|
||||
const already = lbl.querySelector('.required-indicator') || lbl.querySelector('.has-text-danger') || /\*\s*$/.test(lbl.textContent);
|
||||
if(!already){
|
||||
const span = document.createElement('span');
|
||||
span.className = 'required-indicator has-text-danger';
|
||||
span.textContent = ' *';
|
||||
lbl.appendChild(span);
|
||||
}
|
||||
}
|
||||
});
|
||||
}catch(e){ /* ignore */ }
|
||||
})();
|
||||
</script>
|
||||
|
||||
@@ -60,12 +60,12 @@ function _close_shift_modal_targets(){
|
||||
{name: "exclude_days", label: "Exclude days (comma-separated)", type: "list"},
|
||||
{name: "allow_self", label: "Allow self", type: "bool"}
|
||||
]},
|
||||
"min_summed_grade_by_shifts_per_day": {label: "Min summed grade by shifts per day", fields: [ {name: "shifts", label: "Shifts (comma-separated)", type: "list"}, {name: "min_grade_sum", label: "Minimum grade sum", type: "int"}, {name: "days", label: "Days (comma-separated)", type: "list"} ]},
|
||||
"limit_grade_number": {label: "Limit grade number", fields: [ {name: "grade", label: "Grade", type: "int"}, {name: "max_number", label: "Max number", type: "int"} ]},
|
||||
"minimum_grade_number": {label: "Minimum grade number", fields: [ {name: "grade", label: "Grade", type: "int"}, {name: "min_number", label: "Min number", type: "int"} ]},
|
||||
"require_remote_site_presence": {label: "Require remote site presence", fields: [ {name: "site", label: "Site", type: "text"}, {name: "required_number", label: "Required number", type: "int"} ]},
|
||||
"max_shifts_per_week": {label: "Max shifts per week", fields: [ {name: "max_shifts", label: "Max shifts", type: "int"} ]},
|
||||
"max_shifts_per_week_block": {label: "Max shifts per week block", fields: [ {name: "week_block", label: "Week block", type: "int"}, {name: "max_shifts", label: "Max shifts", type: "int"} ]}
|
||||
"min_summed_grade_by_shifts_per_day": {label: "Min summed grade by shifts per day", fields: [ {name: "shifts", label: "Shifts (comma-separated)", type: "list", required: true}, {name: "min_grade_sum", label: "Minimum grade sum", type: "int", required: true}, {name: "days", label: "Days (comma-separated)", type: "list", required: true} ]},
|
||||
"limit_grade_number": {label: "Limit grade number", fields: [ {name: "grade", label: "Grade", type: "int", required: true}, {name: "max_number", label: "Max number", type: "int", required: true} ]},
|
||||
"minimum_grade_number": {label: "Minimum grade number", fields: [ {name: "grade", label: "Grade", type: "int", required: true}, {name: "min_number", label: "Min number", type: "int", required: true} ]},
|
||||
"require_remote_site_presence": {label: "Require remote site presence", fields: [ {name: "site", label: "Site", type: "text", required: true}, {name: "required_number", label: "Required number", type: "int", required: true} ]},
|
||||
"max_shifts_per_week": {label: "Max shifts per week", fields: [ {name: "max_shifts", label: "Max shifts", type: "int", required: true} ]},
|
||||
"max_shifts_per_week_block": {label: "Max shifts per week block", fields: [ {name: "week_block", label: "Week block", type: "int", required: true}, {name: "max_shifts", label: "Max shifts", type: "int", required: true} ]}
|
||||
};
|
||||
|
||||
const textarea = document.getElementById('id_constraints');
|
||||
@@ -148,6 +148,13 @@ function _close_shift_modal_targets(){
|
||||
SCHEMAS[t].fields.forEach(f=>{
|
||||
const fieldWrap = document.createElement('div'); fieldWrap.className='field';
|
||||
const label = document.createElement('label'); label.className='label'; label.textContent = f.label;
|
||||
// append required indicator if schema marks this field required
|
||||
if(f.required){
|
||||
if(!label.querySelector('.required-indicator')){
|
||||
const span = document.createElement('span'); span.className='required-indicator has-text-danger'; span.textContent=' *';
|
||||
label.appendChild(span);
|
||||
}
|
||||
}
|
||||
fieldWrap.appendChild(label);
|
||||
const control = document.createElement('div'); control.className='control';
|
||||
let input;
|
||||
@@ -168,6 +175,7 @@ function _close_shift_modal_targets(){
|
||||
cb.checked = !!curb;
|
||||
cb.setAttribute('data-field-name', f.name);
|
||||
cb.onchange = syncToData;
|
||||
if(f.required){ cb.required = true; cb.setAttribute('aria-required','true'); }
|
||||
control.appendChild(cb);
|
||||
fieldWrap.appendChild(control);
|
||||
fieldsContainer.appendChild(fieldWrap);
|
||||
@@ -181,6 +189,7 @@ function _close_shift_modal_targets(){
|
||||
input = document.createElement('input'); input.className='input'; input.type='text'; input.oninput = syncToData;
|
||||
}
|
||||
input.setAttribute('data-field-name', f.name);
|
||||
if(f.required){ input.required = true; input.setAttribute('aria-required','true'); }
|
||||
control.appendChild(input);
|
||||
fieldWrap.appendChild(control);
|
||||
fieldsContainer.appendChild(fieldWrap);
|
||||
@@ -248,6 +257,34 @@ function _close_shift_modal_targets(){
|
||||
|
||||
})();
|
||||
</script>
|
||||
<script>
|
||||
// Mark required fields in the modal shift form with a visible asterisk and add ARIA attributes
|
||||
(function(){
|
||||
try{
|
||||
const modalForm = document.querySelector('#shift-modal form');
|
||||
if(!modalForm) return;
|
||||
const requiredEls = modalForm.querySelectorAll('input[required], select[required], textarea[required]');
|
||||
requiredEls.forEach(el=>{
|
||||
el.setAttribute('aria-required','true');
|
||||
const id = el.id;
|
||||
let lbl = id ? modalForm.querySelector('label[for="'+id+'"]') : null;
|
||||
if(!lbl){
|
||||
const field = el.closest('.field');
|
||||
if(field) lbl = field.querySelector('label');
|
||||
}
|
||||
if(lbl){
|
||||
const already = lbl.querySelector('.required-indicator') || lbl.querySelector('.has-text-danger') || /\*\s*$/.test(lbl.textContent);
|
||||
if(!already){
|
||||
const span = document.createElement('span');
|
||||
span.className = 'required-indicator has-text-danger';
|
||||
span.textContent = ' *';
|
||||
lbl.appendChild(span);
|
||||
}
|
||||
}
|
||||
});
|
||||
}catch(e){ /* ignore */ }
|
||||
})();
|
||||
</script>
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<button class="button is-primary" type="submit">Save</button>
|
||||
|
||||
Reference in New Issue
Block a user