This commit is contained in:
Ross
2025-12-13 21:33:39 +00:00
parent 18a45ff87e
commit bd5ca8f64c
@@ -33,8 +33,33 @@ function _close_shift_modal_targets(){
// Constraint type schemas: name -> fields metadata
const SCHEMAS = {
"night": {label: "Night constraint", fields: [ {name: "options", label: "Options (free JSON)", type: "json"} ]},
"pre": {label: "Pre-shift", fields: [ {name: "days", label: "Days (comma-separated)", type: "list"}, {name: "ignore_shifts", label: "Ignore shifts (comma-separated)", type: "list"} ]},
"post": {label: "Post-shift", fields: [ {name: "days", label: "Days (comma-separated)", type: "list"}, {name: "ignore_shifts", label: "Ignore shifts (comma-separated)", type: "list"} ]},
// PreShiftConstraint fields from rota_generator/shifts.py:
// weeks: Optional[List[int]]
// start_date: Optional[date]
// end_date: Optional[date]
// days: Optional[int]
// ignore_shifts: List[str]
// exclude_days: List[str]
// allow_self: bool
"pre": {label: "Pre-shift", fields: [
{name: "weeks", label: "Weeks (comma-separated integers)", type: "list_int"},
{name: "start_date", label: "Start date", type: "date"},
{name: "end_date", label: "End date", type: "date"},
{name: "days", label: "Days (integer)", type: "int"},
{name: "ignore_shifts", label: "Ignore shifts (comma-separated)", type: "list"},
{name: "exclude_days", label: "Exclude days (comma-separated)", type: "list"},
{name: "allow_self", label: "Allow self", type: "bool"}
]},
// PostShiftConstraint same fields
"post": {label: "Post-shift", fields: [
{name: "weeks", label: "Weeks (comma-separated integers)", type: "list_int"},
{name: "start_date", label: "Start date", type: "date"},
{name: "end_date", label: "End date", type: "date"},
{name: "days", label: "Days (integer)", type: "int"},
{name: "ignore_shifts", label: "Ignore shifts (comma-separated)", type: "list"},
{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"} ]},
@@ -105,16 +130,32 @@ function _close_shift_modal_targets(){
fieldWrap.appendChild(label);
const control = document.createElement('div'); control.className='control';
let input;
if(f.type==='text' || f.type==='int' || f.type==='float' || f.type==='list'){
if(f.type==='text' || f.type==='int' || f.type==='float' || f.type==='list' || f.type==='list_int'){
input = document.createElement('input'); input.className='input'; input.type='text';
const cur = (c.options && c.options[f.name]!==undefined) ? c.options[f.name] : (c[f.name]!==undefined?c[f.name]:'');
if(Array.isArray(cur) && f.type==='list') input.value = cur.join(','); else input.value = cur===null? '': String(cur);
if(Array.isArray(cur) && (f.type==='list' || f.type==='list_int')) input.value = cur.join(','); else input.value = cur===null? '': String(cur);
input.oninput = syncToData;
} else if(f.type==='json'){
input = document.createElement('textarea'); input.className='textarea'; input.rows=3;
const cur = (c.options && c.options[f.name]!==undefined) ? c.options[f.name] : (c[f.name]!==undefined?c[f.name]:'');
try{ input.value = JSON.stringify(cur); }catch(e){ input.value = String(cur); }
input.oninput = syncToData;
} else if(f.type==='bool'){
// checkbox for boolean
const cb = document.createElement('input'); cb.type='checkbox'; cb.className='checkbox';
const curb = (c.options && c.options[f.name]!==undefined) ? c.options[f.name] : (c[f.name]!==undefined?c[f.name]:false);
cb.checked = !!curb;
cb.setAttribute('data-field-name', f.name);
cb.onchange = syncToData;
control.appendChild(cb);
fieldWrap.appendChild(control);
fieldsContainer.appendChild(fieldWrap);
return; // already appended
} else if(f.type==='date'){
input = document.createElement('input'); input.className='input'; input.type='date';
const curd = (c.options && c.options[f.name]!==undefined) ? c.options[f.name] : (c[f.name]!==undefined?c[f.name]:'');
input.value = curd ? String(curd) : '';
input.oninput = syncToData;
} else {
input = document.createElement('input'); input.className='input'; input.type='text'; input.oninput = syncToData;
}
@@ -141,6 +182,12 @@ function _close_shift_modal_targets(){
v = v===''?null:parseFloat(v);
} else if(f.type==='list'){
v = v===''?[]:v.split(',').map(x=>x.trim()).filter(Boolean);
} else if(f.type==='list_int'){
v = v===''?[]:v.split(',').map(x=>parseInt(x.trim(),10)).filter(n=>!Number.isNaN(n));
} else if(f.type==='bool'){
const cb = fieldsContainer.querySelector('[data-field-name="'+f.name+'"]'); if(cb) v = !!cb.checked; else v = !!v;
} else if(f.type==='date'){
v = v===''?null:v; // keep as ISO date string
} else if(f.type==='json'){
try{ v = v===''?{}:JSON.parse(v); }catch(e){ v = input.value; }
}