Feriefridage logic improved, mobile styling
This commit is contained in:
parent
c48c26290e
commit
71aeb691df
2 changed files with 89 additions and 12 deletions
35
app.js
35
app.js
|
|
@ -637,13 +637,46 @@ function calculateCurrentSnapshot(settings, entries, corrections = {}, feriefrid
|
||||||
if (feriefridageBalance < -0.001) warnings.push({ level: 'bad', text: `Feriefridage balance is negative today: ${days(feriefridageBalance)} days.` });
|
if (feriefridageBalance < -0.001) warnings.push({ level: 'bad', text: `Feriefridage balance is negative today: ${days(feriefridageBalance)} days.` });
|
||||||
if (flexBalance < -0.001) warnings.push({ level: 'bad', text: `FLEX balance is negative today: ${hours(flexBalance)}.` });
|
if (flexBalance < -0.001) warnings.push({ level: 'bad', text: `FLEX balance is negative today: ${hours(flexBalance)}.` });
|
||||||
if (today.getMonth() + 1 <= Number(settings.feriefridage_expiry_month || 8) && feriefridageBalance > 0.001) {
|
if (today.getMonth() + 1 <= Number(settings.feriefridage_expiry_month || 8) && feriefridageBalance > 0.001) {
|
||||||
warnings.push({ level: 'warn', text: `${days(feriefridageBalance)} feriefridage must be used before 31 August.` });
|
const remainingAfterApprovedPlans = feriefridageRemainingAfterApprovedPlans(settings, entries, today, {
|
||||||
|
vacationBalance,
|
||||||
|
feriefridageBalance,
|
||||||
|
flexBalance,
|
||||||
|
});
|
||||||
|
if (remainingAfterApprovedPlans > 0.001) {
|
||||||
|
warnings.push({ level: 'warn', text: `${days(remainingAfterApprovedPlans)} feriefridage need approved plan before 31 August.` });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const totalDays = vacationBalance + feriefridageBalance + (flexBalance / avgDayHours);
|
const totalDays = vacationBalance + feriefridageBalance + (flexBalance / avgDayHours);
|
||||||
return { vacation: vacationBalance, feriefridage: feriefridageBalance, flex: flexBalance, totalDays, warnings: dedupeWarnings(warnings) };
|
return { vacation: vacationBalance, feriefridage: feriefridageBalance, flex: flexBalance, totalDays, warnings: dedupeWarnings(warnings) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function feriefridageRemainingAfterApprovedPlans(settings, entries, today, balances) {
|
||||||
|
const expiryMonth = Number(settings.feriefridage_expiry_month || 8);
|
||||||
|
const deadline = dateFromParts(today.getFullYear(), expiryMonth + 1, 0);
|
||||||
|
let { vacationBalance, feriefridageBalance, flexBalance } = balances;
|
||||||
|
|
||||||
|
const futureApprovedWorkdays = [];
|
||||||
|
for (const entry of entries) {
|
||||||
|
if (entry.status !== 'approved') continue;
|
||||||
|
for (const date of workdaysBetween(entry.start_date, entry.end_date)) {
|
||||||
|
if (date > today && date <= deadline) {
|
||||||
|
futureApprovedWorkdays.push({ date, entry });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
futureApprovedWorkdays.sort((a, b) => a.date - b.date || a.entry.id - b.entry.id);
|
||||||
|
for (const item of futureApprovedWorkdays) {
|
||||||
|
const allocation = allocateDay(item.entry, item.date, { vacationBalance, feriefridageBalance, flexBalance }, settings);
|
||||||
|
vacationBalance = allocation.balances.vacationBalance;
|
||||||
|
feriefridageBalance = allocation.balances.feriefridageBalance;
|
||||||
|
flexBalance = allocation.balances.flexBalance;
|
||||||
|
}
|
||||||
|
|
||||||
|
return feriefridageBalance;
|
||||||
|
}
|
||||||
|
|
||||||
function allocateDay(entry, date, balances, settings) {
|
function allocateDay(entry, date, balances, settings) {
|
||||||
const source = entry.source;
|
const source = entry.source;
|
||||||
const allowUnpaid = Boolean(Number(entry.allow_unpaid));
|
const allowUnpaid = Boolean(Number(entry.allow_unpaid));
|
||||||
|
|
|
||||||
66
style.css
66
style.css
|
|
@ -367,6 +367,7 @@ tbody tr:hover { background: rgba(255,255,255,.04); }
|
||||||
|
|
||||||
@media (max-width: 1050px) {
|
@media (max-width: 1050px) {
|
||||||
.cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
.cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||||
|
.header-cards { grid-template-columns: repeat(4, minmax(0, 1fr)); }
|
||||||
.two-col { grid-template-columns: 1fr; }
|
.two-col { grid-template-columns: 1fr; }
|
||||||
.form-grid.compact { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
.form-grid.compact { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||||
}
|
}
|
||||||
|
|
@ -374,10 +375,11 @@ tbody tr:hover { background: rgba(255,255,255,.04); }
|
||||||
@media (max-width: 680px) {
|
@media (max-width: 680px) {
|
||||||
body { font-size: 14px; }
|
body { font-size: 14px; }
|
||||||
.floating-header { padding: .25rem .3rem; }
|
.floating-header { padding: .25rem .3rem; }
|
||||||
.cards { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: .22rem; }
|
.cards, .header-cards { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: .18rem; }
|
||||||
.card { padding: .28rem .32rem; min-width: 0; }
|
.card { padding: .28rem .32rem; min-width: 0; }
|
||||||
.card .label { font-size: .52rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
.header-cards .card { flex-direction: column; align-items: center; gap: .02rem; padding: .18rem .16rem; }
|
||||||
.card .value { font-size: .82rem; line-height: 1.05; }
|
.card .label { font-size: .48rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
|
||||||
|
.card .value { font-size: .76rem; line-height: 1.05; }
|
||||||
.card .hint { display: none; }
|
.card .hint { display: none; }
|
||||||
.header-cards .card { padding: .14rem .2rem; gap: .18rem; }
|
.header-cards .card { padding: .14rem .2rem; gap: .18rem; }
|
||||||
.header-cards .card .label { font-size: .48rem; }
|
.header-cards .card .label { font-size: .48rem; }
|
||||||
|
|
@ -422,20 +424,62 @@ tbody tr:hover { background: rgba(255,255,255,.04); }
|
||||||
.header-warning, .header-action-button { min-height: 1.2rem; font-size: .62rem; padding: .14rem .3rem; }
|
.header-warning, .header-action-button { min-height: 1.2rem; font-size: .62rem; padding: .14rem .3rem; }
|
||||||
|
|
||||||
main { padding: 0 .3rem 3rem; }
|
main { padding: 0 .3rem 3rem; }
|
||||||
.planner-column-header { margin-top: .1rem; padding-top: .08rem; }
|
.planner-column-header { display: none; }
|
||||||
.planner-header-table th { padding: .14rem .24rem; font-size: .54rem; letter-spacing: .01em; line-height: 1; }
|
.planner-panel .table-wrap { overflow: visible; }
|
||||||
.planner-body-table td { padding: .32rem .24rem; }
|
.month-table,
|
||||||
.month-table { min-width: 600px; }
|
.planner-body-table,
|
||||||
.month-table th:nth-child(1), .month-table td:nth-child(1) { width: 6.8rem; }
|
.planner-body-table tbody { display: block; width: 100%; min-width: 0; }
|
||||||
|
.planner-body-table tr {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
|
gap: .18rem .28rem;
|
||||||
|
padding: .42rem 0;
|
||||||
|
border-bottom: 1px solid var(--line);
|
||||||
|
}
|
||||||
|
.planner-body-table td {
|
||||||
|
display: block;
|
||||||
|
border-bottom: 0;
|
||||||
|
padding: 0;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.planner-body-table td:nth-child(1) {
|
||||||
|
grid-column: 1 / 2;
|
||||||
|
font-weight: 900;
|
||||||
|
font-size: .78rem;
|
||||||
|
line-height: 1.1;
|
||||||
|
}
|
||||||
|
.planner-body-table td:nth-child(2) {
|
||||||
|
grid-column: 2 / -1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.planner-body-table td:nth-child(n+3) {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.planner-body-table td:nth-child(3)::before { content: 'VD'; }
|
||||||
|
.planner-body-table td:nth-child(4)::before { content: 'FF'; }
|
||||||
|
.planner-body-table td:nth-child(5)::before { content: 'FLEX'; }
|
||||||
|
.planner-body-table td:nth-child(6)::before { content: 'Total'; }
|
||||||
|
.planner-body-table td:nth-child(n+3)::before {
|
||||||
|
display: block;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: .52rem;
|
||||||
|
font-weight: 900;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: .04em;
|
||||||
|
margin-bottom: .12rem;
|
||||||
|
}
|
||||||
|
.month-table th:nth-child(1), .month-table td:nth-child(1),
|
||||||
|
.month-table th:nth-child(2), .month-table td:nth-child(2),
|
||||||
.month-table th:nth-child(3), .month-table td:nth-child(3),
|
.month-table th:nth-child(3), .month-table td:nth-child(3),
|
||||||
.month-table th:nth-child(4), .month-table td:nth-child(4),
|
.month-table th:nth-child(4), .month-table td:nth-child(4),
|
||||||
.month-table th:nth-child(5), .month-table td:nth-child(5),
|
.month-table th:nth-child(5), .month-table td:nth-child(5),
|
||||||
.month-table th:nth-child(6), .month-table td:nth-child(6) { width: 5.2rem; }
|
.month-table th:nth-child(6), .month-table td:nth-child(6) { width: auto; }
|
||||||
.month-table td.entries { min-width: 10rem; }
|
.month-table td.entries { min-width: 0; }
|
||||||
.month-entry { margin: .05rem 0 .18rem; }
|
.month-entry { margin: .05rem 0 .18rem; }
|
||||||
.planner-entry-meta { display: block; margin-left: 0; font-size: .64rem; }
|
.planner-entry-meta { display: block; margin-left: 0; font-size: .64rem; }
|
||||||
.planner-entry-dates { font-size: .6rem; }
|
.planner-entry-dates { font-size: .6rem; }
|
||||||
.balance-override-input { width: 4.15rem; padding: .22rem .25rem; font-size: .75rem; }
|
.balance-override-input { width: 100%; max-width: 4.8rem; padding: .22rem .25rem; font-size: .75rem; }
|
||||||
|
.important-col { border-radius: 8px; padding: .2rem .25rem !important; }
|
||||||
|
|
||||||
.modal { width: calc(100vw - .8rem); max-height: calc(100dvh - .8rem); padding: .75rem; overflow: auto; }
|
.modal { width: calc(100vw - .8rem); max-height: calc(100dvh - .8rem); padding: .75rem; overflow: auto; }
|
||||||
.modal-heading { margin-bottom: .7rem; }
|
.modal-heading { margin-bottom: .7rem; }
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue