Enhance worker list display with site, grade, and FTE information; add non-working days summary rendering

This commit is contained in:
Ross
2025-12-15 21:41:10 +00:00
parent c5850eb1b2
commit 9fc2ba3210
@@ -4,9 +4,23 @@
<ul>
{% for w in rota.workers.all %}
<li>
<a href="{% url 'rota:worker_detail' w.id %}">{{ w.name }}</a>
<button class="button is-small is-light ml-2" hx-get="{% url 'rota:worker_edit' rota.id w.id %}" hx-target="#modal" hx-swap="innerHTML">Edit</button>
<button class="button is-small is-danger ml-2" hx-get="{% url 'rota:worker_delete' rota.id w.id %}" hx-target="#modal" hx-swap="innerHTML">Delete</button>
<div style="display:flex; align-items:center; justify-content:space-between; gap:0.75rem;">
<div style="display:flex; align-items:center; gap:0.5rem;">
<a href="{% url 'rota:worker_detail' w.id %}">{{ w.name }}</a>
<small style="color:#666">&mdash; {{ w.site }}{% if w.grade %} · grade {{ w.grade }}{% endif %} · FTE {{ w.fte }}</small>
{# show non-working days stored in options.nwds if present; render JSON safely and let JS format it #}
{% if w.options.nwds %}
<span id="nwds-summary-{{ w.id }}" class="nwds-summary" data-nwds-id="nwds-data-{{ w.id }}" style="margin-left:0.5rem;color:#666"></span>
{% with script_id='nwds-data-'|add:w.id %}
{{ w.options.nwds|default:"[]"|json_script:script_id }}
{% endwith %}
{% endif %}
</div>
<div style="flex:0 0 auto;">
<button class="button is-small is-light ml-2" hx-get="{% url 'rota:worker_edit' rota.id w.id %}" hx-target="#modal" hx-swap="innerHTML">Edit</button>
<button class="button is-small is-danger ml-2" hx-get="{% url 'rota:worker_delete' rota.id w.id %}" hx-target="#modal" hx-swap="innerHTML">Delete</button>
</div>
</div>
</li>
{% empty %}
<li>No workers assigned</li>
@@ -14,3 +28,69 @@
</ul>
</div>
</div>
<script>
// Render NWDS summaries: find script tags with id starting 'nwds-data-' and populate the
// corresponding summary span. Clicking a day label toggles display of date details.
(function(){
const DAYS = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'];
function dayLabelFromItem(item){
if(item === null || item === undefined) return '(any)';
if(typeof item === 'number') return DAYS[item] || String(item);
if(typeof item === 'string'){
const n = parseInt(item,10);
if(!Number.isNaN(n)) return DAYS[n] || item;
// normalise to first 3 letters
return item.trim().slice(0,3);
}
if(typeof item === 'object'){
if(item.day !== undefined){
if(typeof item.day === 'number') return DAYS[item.day] || String(item.day);
return String(item.day).slice(0,3);
}
}
return String(item);
}
document.querySelectorAll('.nwds-summary').forEach(function(target){
try{
let arr = [];
const dataAttr = target.getAttribute('data-nwds');
if(dataAttr){
try{ arr = JSON.parse(dataAttr); }catch(e){ arr = []; }
} else {
const scriptId = target.getAttribute('data-nwds-id');
if(scriptId){
let script = document.getElementById(scriptId);
// Some template/tooling may emit the JSON <script> without an id (observed in output).
// Fallback: look for the next sibling <script type="application/json"> after the span.
if(!script){
let sib = target.nextElementSibling;
while(sib){
if(sib.tagName === 'SCRIPT' && (sib.type === 'application/json' || sib.getAttribute('type') === 'application/json')){ script = sib; break; }
sib = sib.nextElementSibling;
}
}
if(script){
try{ arr = JSON.parse(script.textContent || script.innerText || '[]'); }catch(e){ arr = []; }
}
}
}
if(!Array.isArray(arr) || arr.length===0){ target.textContent = ''; return; }
arr.forEach(function(item, idx){
const a = document.createElement('a');
a.href = '#'; a.className = 'nwds-day'; a.style.marginLeft = (idx? '0.4rem':'0'); a.style.color = '#666'; a.textContent = dayLabelFromItem(item);
const details = document.createElement('span'); details.className='nwds-dates'; details.style.display='none'; details.style.marginLeft='0.25rem'; details.style.color='#444';
if(item && typeof item === 'object'){
const sd = item.start_date || item.from || '';
const ed = item.end_date || item.to || '';
if(sd || ed){ details.textContent = (sd||'') + (sd && ed ? ' → ' : '') + (ed||''); }
}
a.addEventListener('click', function(ev){ ev.preventDefault(); if(details.textContent){ details.style.display = details.style.display === 'none' ? 'inline' : 'none'; } });
target.appendChild(a);
target.appendChild(details);
});
}catch(e){ console.error('nwds render failed', e); }
});
})();
</script>