Values editable in the top row

This commit is contained in:
marijo 2026-06-25 11:47:35 +00:00
parent eb14ee71b1
commit e0d2ab12d3
2 changed files with 61 additions and 15 deletions

64
app.js
View file

@ -356,24 +356,39 @@ function resetEntryForm() {
function renderDashboard() { function renderDashboard() {
const current = state.plan.current; const current = state.plan.current;
const { year, month } = currentMonthParts();
const cards = [ const cards = [
['Feriefridage', days(current.feriefridage), 'Use before 31 August', current.feriefridage > 0.001 ? 'warn' : ''], {
['Vacation days', days(current.vacation), 'Available today', current.vacation < -0.001 ? 'bad' : 'good'], kind: 'feriefridage',
label: 'Feriefridage',
value: current.feriefridage,
hint: 'Use before 31 August',
tone: current.feriefridage > 0.001 ? 'warn' : '',
step: '0.01',
overridden: hasCorrection(state.feriefridageOverrides, year, month),
},
{
kind: 'vacation',
label: 'Vacation days',
value: current.vacation,
hint: 'Available today',
tone: current.vacation < -0.001 ? 'bad' : 'good',
step: '0.01',
overridden: hasCorrection(state.corrections, year, month),
},
{
kind: 'flex',
label: 'FLEX hours',
value: current.flex,
hint: 'Current balance',
tone: current.flex < -0.001 ? 'bad' : 'good',
step: '0.25',
overridden: hasCorrection(state.flexOverrides, year, month),
},
]; ];
$('dashboard').innerHTML = ` $('dashboard').innerHTML = `
${cards.map(([label, value, hint, tone]) => ` ${cards.map((card) => renderDashboardBalanceCard(card, year, month)).join('')}
<article class="card ${tone}">
<div class="label">${escapeHtml(label)}</div>
<div class="value">${value}</div>
<div class="hint">${escapeHtml(hint)}</div>
</article>
`).join('')}
<article class="card ${current.flex < -0.001 ? 'bad' : 'good'}">
<div class="label">FLEX hours</div>
<div class="value">${hours(current.flex)}</div>
<div class="hint">Current balance</div>
</article>
<article class="card ${current.totalDays < -0.001 ? 'bad' : 'good'}"> <article class="card ${current.totalDays < -0.001 ? 'bad' : 'good'}">
<div class="label">Total days</div> <div class="label">Total days</div>
<div class="value">${days(current.totalDays)}</div> <div class="value">${days(current.totalDays)}</div>
@ -382,6 +397,21 @@ function renderDashboard() {
`; `;
} }
function renderDashboardBalanceCard(card, year, month) {
return `
<article class="card ${card.tone}">
<div class="label">${escapeHtml(card.label)}</div>
<input class="value balance-override-input dashboard-balance-input ${card.overridden ? 'is-overridden' : ''}" type="number" step="${card.step}" value="${Number(Number(card.value || 0).toFixed(2))}" data-kind="${card.kind}" data-year="${year}" data-month="${month}" aria-label="${escapeHtml(card.label)} for current month">
<div class="hint">${escapeHtml(card.hint)}</div>
</article>
`;
}
function currentMonthParts() {
const today = new Date();
return { year: today.getFullYear(), month: today.getMonth() + 1 };
}
function renderHeaderWarnings() { function renderHeaderWarnings() {
const warnings = state.plan.current.warnings; const warnings = state.plan.current.warnings;
$('headerWarnings').innerHTML = warnings.length $('headerWarnings').innerHTML = warnings.length
@ -444,10 +474,14 @@ function renderPlanner() {
</table> </table>
</div> </div>
`; `;
bindBalanceOverrideInputs();
syncPlannerScroll();
}
function bindBalanceOverrideInputs() {
document.querySelectorAll('.balance-override-input').forEach((input) => { document.querySelectorAll('.balance-override-input').forEach((input) => {
input.addEventListener('change', (event) => saveBalanceOverride(event).catch((error) => toast(error.message || String(error)))); input.addEventListener('change', (event) => saveBalanceOverride(event).catch((error) => toast(error.message || String(error))));
}); });
syncPlannerScroll();
} }
function syncPlannerScroll() { function syncPlannerScroll() {

View file

@ -175,6 +175,17 @@ main {
.header-cards .card .label { font-size: .88rem; font-weight: 950; white-space: nowrap; } .header-cards .card .label { font-size: .88rem; font-weight: 950; white-space: nowrap; }
.header-cards .card .value { font-size: 1.55rem; font-weight: 950; margin: 0; } .header-cards .card .value { font-size: 1.55rem; font-weight: 950; margin: 0; }
.header-cards .card .hint { display: none; } .header-cards .card .hint { display: none; }
.header-cards .card .dashboard-balance-input {
width: 5.25rem;
min-width: 0;
padding: .05rem .18rem;
border-radius: 6px;
text-align: right;
background: rgba(16,16,16,.55);
font-size: 1.45rem;
font-weight: 950;
line-height: 1;
}
.card.bad .value { color: var(--bad); } .card.bad .value { color: var(--bad); }
.card.warn .value { color: var(--warn); } .card.warn .value { color: var(--warn); }
.card.good .value { color: var(--good); } .card.good .value { color: var(--good); }
@ -397,6 +408,7 @@ tbody tr:hover { background: rgba(255,255,255,.04); }
.header-cards .card { padding: .14rem .2rem; gap: .18rem; } .header-cards .card { padding: .14rem .2rem; gap: .18rem; }
.header-cards .card .label { font-size: .72rem; font-weight: 950; } .header-cards .card .label { font-size: .72rem; font-weight: 950; }
.header-cards .card .value { font-size: 1.45rem; font-weight: 950; } .header-cards .card .value { font-size: 1.45rem; font-weight: 950; }
.header-cards .card .dashboard-balance-input { width: 3.9rem; padding: .04rem .12rem; font-size: 1rem; }
.header-entries { padding: .16rem .22rem; margin: .16rem 0; } .header-entries { padding: .16rem .22rem; margin: .16rem 0; }
.header-entries .table-wrap { overflow-x: visible; overflow-y: visible; } .header-entries .table-wrap { overflow-x: visible; overflow-y: visible; }