Add tooltip for shift constraints and enhance weekday checkbox handling in worker form
This commit is contained in:
@@ -5,6 +5,16 @@
|
||||
{% for s in rota.shifts %}
|
||||
<li>
|
||||
<strong>{{ s.name }}</strong> — sites: {{ s.sites|join:", " }} — days: {{ s.days|join:", " }} — workers: {{ s.workers_required }}
|
||||
{% if s.constraints %}
|
||||
<div style="margin-top:0.25rem;">
|
||||
<span class="tags">
|
||||
{% for c in s.constraints %}
|
||||
{# Build a simple tooltip showing key: value pairs for the constraint on hover #}
|
||||
<span class="tag is-light" title="{% for k,v in c.items %}{{ k }}: {{ v }}{% if not forloop.last %}; {% endif %}{% endfor %}">{{ c.name }}</span>
|
||||
{% endfor %}
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if s.balance_offset is defined and s.balance_offset %}
|
||||
— balance offset: {{ s.balance_offset }}
|
||||
{% endif %}
|
||||
|
||||
@@ -268,6 +268,7 @@
|
||||
if(textarea) textarea.style.display='none';
|
||||
const listEl = document.getElementById(cfg.container);
|
||||
const addBtn = document.getElementById(cfg.addButton);
|
||||
const WEEKDAYS = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'];
|
||||
|
||||
function parse(){
|
||||
let v = textarea ? textarea.value : ''; if(!v) return [];
|
||||
@@ -284,16 +285,48 @@
|
||||
const field = document.createElement('div'); field.className='field';
|
||||
const label = document.createElement('label'); label.className='label'; label.textContent=f.label; field.appendChild(label);
|
||||
const control = document.createElement('div'); control.className='control';
|
||||
let input;
|
||||
if(f.type==='date'){
|
||||
input = document.createElement('input'); input.type='date'; input.className='datepicker input'; input.setAttribute('autocomplete','off'); input.value = item[f.name] || '';
|
||||
} else if(f.type==='int'){
|
||||
input = document.createElement('input'); input.type='number'; input.className='input'; input.value = item[f.name] || '';
|
||||
} else {
|
||||
input = document.createElement('input'); input.type='text'; input.className='input'; input.value = item[f.name] || '';
|
||||
|
||||
// Special-case: render weekday checkboxes when the field name is 'days'
|
||||
if(f.name === 'days'){
|
||||
const box = document.createElement('div'); box.className='buttons'; box.setAttribute('data-field', f.name);
|
||||
WEEKDAYS.forEach(d => {
|
||||
const btn = document.createElement('label'); btn.className='button is-small is-light'; btn.style.marginRight='0.25rem';
|
||||
const chk = document.createElement('input'); chk.type='checkbox'; chk.value = d; chk.style.marginRight='0.35rem';
|
||||
// mark checked if item[f.name] contains this day (accept set/list/string)
|
||||
try{
|
||||
const cur = item[f.name];
|
||||
if(cur){
|
||||
if(Array.isArray(cur)){
|
||||
if(cur.indexOf(d)!==-1) chk.checked = true;
|
||||
} else if(typeof cur === 'string'){
|
||||
const parts = cur.split(',').map(s=>s.trim()); if(parts.indexOf(d)!==-1) chk.checked = true;
|
||||
} else if(typeof cur === 'object'){
|
||||
// set-like
|
||||
if(cur.hasOwnProperty(d) || (Array.from(Object.values(cur)).indexOf(d)!==-1)) chk.checked = true;
|
||||
}
|
||||
}
|
||||
}catch(e){}
|
||||
chk.onchange = ()=>{ sync(idx); };
|
||||
btn.appendChild(chk);
|
||||
const span = document.createElement('span'); span.textContent = d; btn.appendChild(span);
|
||||
box.appendChild(btn);
|
||||
});
|
||||
control.appendChild(box);
|
||||
field.appendChild(control); col.appendChild(field); row.appendChild(col);
|
||||
} else {
|
||||
let input;
|
||||
if(f.type==='date'){
|
||||
input = document.createElement('input'); input.type='date'; input.className='datepicker input'; input.setAttribute('autocomplete','off'); input.value = item[f.name] || '';
|
||||
} else if(f.type==='int'){
|
||||
input = document.createElement('input'); input.type='number'; input.className='input'; input.value = item[f.name] || '';
|
||||
} else {
|
||||
input = document.createElement('input'); input.type='text'; input.className='input'; input.value = item[f.name] || '';
|
||||
}
|
||||
// tag this control with the field name so sync() can find it reliably
|
||||
input.setAttribute('data-field', f.name);
|
||||
input.onchange = ()=>{ sync(idx); };
|
||||
control.appendChild(input); field.appendChild(control); col.appendChild(field); row.appendChild(col);
|
||||
}
|
||||
input.onchange = ()=>{ sync(idx); };
|
||||
control.appendChild(input); field.appendChild(control); col.appendChild(field); row.appendChild(col);
|
||||
});
|
||||
// remove
|
||||
const colRm = document.createElement('div'); colRm.className='column is-narrow'; const fr = document.createElement('div'); fr.className='field'; const ctr = document.createElement('div'); ctr.className='control'; const rm = document.createElement('button'); rm.type='button'; rm.className='button is-danger is-light is-small'; rm.textContent='Remove'; rm.onclick = ()=>{ removeAt(idx); }; ctr.appendChild(rm); fr.appendChild(ctr); colRm.appendChild(fr); row.appendChild(colRm);
|
||||
@@ -302,7 +335,38 @@
|
||||
return wrap;
|
||||
}
|
||||
|
||||
function sync(i){ const data = parse(); const el = listEl.children[i]; if(!el) return; const inputs = el.querySelectorAll('input'); const obj = {}; cfg.fields.forEach((f,j)=>{ const val = inputs[j].value; if(val!=='') obj[f.name]= f.type==='int'?parseInt(val,10):val; }); data[i]=obj; if(textarea) textarea.value = JSON.stringify(data); }
|
||||
function sync(i){
|
||||
const data = parse();
|
||||
const el = listEl.children[i]; if(!el) return;
|
||||
const obj = {};
|
||||
cfg.fields.forEach((f)=>{
|
||||
const fld = el.querySelector('[data-field="'+f.name+'"]');
|
||||
if(!fld){
|
||||
// fallback: try to find an input by label text
|
||||
const inp = el.querySelector('input');
|
||||
if(inp && inp.value!=='') obj[f.name] = f.type==='int'?parseInt(inp.value,10):inp.value;
|
||||
return;
|
||||
}
|
||||
// if this is a checkbox group (for days), fld will be a container
|
||||
if(f.name === 'days'){
|
||||
const checks = fld.querySelectorAll('input[type=checkbox]');
|
||||
const vals = Array.from(checks).filter(c=>c.checked).map(c=>c.value);
|
||||
if(vals.length) obj[f.name] = vals;
|
||||
} else {
|
||||
// single input control
|
||||
if(fld.tagName === 'INPUT'){
|
||||
const v = fld.value;
|
||||
if(v !== '') obj[f.name] = f.type==='int'?parseInt(v,10):v;
|
||||
} else {
|
||||
// container wrapping an input
|
||||
const inp = fld.querySelector('input');
|
||||
if(inp && inp.value !== '') obj[f.name] = f.type==='int'?parseInt(inp.value,10):inp.value;
|
||||
}
|
||||
}
|
||||
});
|
||||
data[i]=obj;
|
||||
if(textarea) textarea.value = JSON.stringify(data);
|
||||
}
|
||||
|
||||
function removeAt(i){ const data = parse(); data.splice(i,1); if(textarea) textarea.value = JSON.stringify(data); render(); }
|
||||
|
||||
@@ -321,8 +385,8 @@
|
||||
makeSimpleListEditor({textarea:'id_allowed_multi_shift_sets', container:'ams-list', addButton:'add-ams', fields:[{name:'shifts',label:'Shifts (comma-separated)',type:'text'}]});
|
||||
|
||||
// Hard day deps/exclusions need days/weeks/start/end
|
||||
makeSimpleListEditor({textarea:'id_hard_day_dependencies', container:'hdd-list', addButton:'add-hdd', fields:[{name:'days',label:'Days (comma-separated)',type:'text'},{name:'weeks',label:'Weeks (comma-separated ints)',type:'text'},{name:'start_date',label:'Start date',type:'date'},{name:'end_date',label:'End date',type:'date'}]});
|
||||
makeSimpleListEditor({textarea:'id_hard_day_exclusions', container:'hde-list', addButton:'add-hde', fields:[{name:'days',label:'Days (comma-separated)',type:'text'},{name:'weeks',label:'Weeks (comma-separated ints)',type:'text'},{name:'start_date',label:'Start date',type:'date'},{name:'end_date',label:'End date',type:'date'}]});
|
||||
makeSimpleListEditor({textarea:'id_hard_day_dependencies', container:'hdd-list', addButton:'add-hdd', fields:[{name:'days',label:'Days (weekdays)',type:'text'},{name:'weeks',label:'Weeks (comma-separated ints)',type:'text'},{name:'start_date',label:'Start date',type:'date'},{name:'end_date',label:'End date',type:'date'}]});
|
||||
makeSimpleListEditor({textarea:'id_hard_day_exclusions', container:'hde-list', addButton:'add-hde', fields:[{name:'days',label:'Days (weekdays)',type:'text'},{name:'weeks',label:'Weeks (comma-separated ints)',type:'text'},{name:'start_date',label:'Start date',type:'date'},{name:'end_date',label:'End date',type:'date'}]});
|
||||
|
||||
})();
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user