UI changes for collapsing and header bcg
This commit is contained in:
parent
71aeb691df
commit
996acdef92
4 changed files with 106 additions and 48 deletions
18
api.php
18
api.php
|
|
@ -117,11 +117,17 @@ function migrate(PDO $db): void
|
|||
source TEXT NOT NULL DEFAULT 'auto',
|
||||
status TEXT NOT NULL DEFAULT 'planned',
|
||||
allow_unpaid INTEGER NOT NULL DEFAULT 0,
|
||||
non_working_days REAL NOT NULL DEFAULT 0,
|
||||
note TEXT NOT NULL DEFAULT '',
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
)");
|
||||
|
||||
$columns = array_column($db->query('PRAGMA table_info(entries)')->fetchAll(), 'name');
|
||||
if (!in_array('non_working_days', $columns, true)) {
|
||||
$db->exec('ALTER TABLE entries ADD COLUMN non_working_days REAL NOT NULL DEFAULT 0');
|
||||
}
|
||||
|
||||
$db->exec("CREATE TABLE IF NOT EXISTS monthly_corrections (
|
||||
year INTEGER NOT NULL,
|
||||
month INTEGER NOT NULL,
|
||||
|
|
@ -293,7 +299,7 @@ function save_flex_override(PDO $db, int $year, int $month, mixed $value): void
|
|||
|
||||
function get_entries(PDO $db): array
|
||||
{
|
||||
return $db->query('SELECT id, title, start_date, end_date, source, status, allow_unpaid, note
|
||||
return $db->query('SELECT id, title, start_date, end_date, source, status, allow_unpaid, non_working_days, note
|
||||
FROM entries ORDER BY start_date, end_date, id')->fetchAll();
|
||||
}
|
||||
|
||||
|
|
@ -305,6 +311,7 @@ function save_entry(PDO $db, array $entry, ?int $id = null): int
|
|||
$source = (string)($entry['source'] ?? 'auto');
|
||||
$status = (string)($entry['status'] ?? 'planned');
|
||||
$allowUnpaid = !empty($entry['allow_unpaid']) ? 1 : 0;
|
||||
$nonWorkingDays = (float)($entry['non_working_days'] ?? 0);
|
||||
$note = trim((string)($entry['note'] ?? ''));
|
||||
|
||||
if ($title === '') {
|
||||
|
|
@ -319,6 +326,9 @@ function save_entry(PDO $db, array $entry, ?int $id = null): int
|
|||
if (!in_array($source, ['auto', 'vacation', 'feriefridag', 'flex', 'unpaid'], true)) {
|
||||
fail('Invalid source', 400);
|
||||
}
|
||||
if ($nonWorkingDays < 0 || fmod($nonWorkingDays * 2, 1.0) !== 0.0) {
|
||||
fail('Additional non-working days must be in steps of 0.5', 400);
|
||||
}
|
||||
if ($status === 'confirmed') {
|
||||
$status = 'approved';
|
||||
}
|
||||
|
|
@ -333,13 +343,14 @@ function save_entry(PDO $db, array $entry, ?int $id = null): int
|
|||
':source' => $source,
|
||||
':status' => $status,
|
||||
':allow_unpaid' => $allowUnpaid,
|
||||
':non_working_days' => $nonWorkingDays,
|
||||
':note' => $note,
|
||||
];
|
||||
|
||||
if ($id === null) {
|
||||
$stmt = $db->prepare('INSERT INTO entries
|
||||
(title, start_date, end_date, source, status, allow_unpaid, note)
|
||||
VALUES (:title, :start_date, :end_date, :source, :status, :allow_unpaid, :note)');
|
||||
(title, start_date, end_date, source, status, allow_unpaid, non_working_days, note)
|
||||
VALUES (:title, :start_date, :end_date, :source, :status, :allow_unpaid, :non_working_days, :note)');
|
||||
$stmt->execute($params);
|
||||
return (int)$db->lastInsertId();
|
||||
}
|
||||
|
|
@ -352,6 +363,7 @@ function save_entry(PDO $db, array $entry, ?int $id = null): int
|
|||
source = :source,
|
||||
status = :status,
|
||||
allow_unpaid = :allow_unpaid,
|
||||
non_working_days = :non_working_days,
|
||||
note = :note,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = :id');
|
||||
|
|
|
|||
114
app.js
114
app.js
|
|
@ -35,13 +35,14 @@ async function init() {
|
|||
function bindEvents() {
|
||||
$('addEntryBtn').addEventListener('click', () => openEntryDialog());
|
||||
$('settingsBtn').addEventListener('click', openSettingsDialog);
|
||||
$('toggleEntriesBtn').addEventListener('click', toggleHeaderEntries);
|
||||
$('entriesTable').querySelector('thead').addEventListener('click', toggleHeaderEntries);
|
||||
$('closeEntryDialogBtn').addEventListener('click', closeEntryDialog);
|
||||
$('closeSettingsDialogBtn').addEventListener('click', closeSettingsDialog);
|
||||
$('entryDialog').addEventListener('click', closeDialogOnBackdrop);
|
||||
$('settingsDialog').addEventListener('click', closeDialogOnBackdrop);
|
||||
$('entryForm').addEventListener('submit', saveEntry);
|
||||
$('settingsForm').addEventListener('submit', saveSettings);
|
||||
['startDate', 'endDate', 'nonWorkingDays'].forEach((id) => $(id).addEventListener('input', updateWorkdayPreview));
|
||||
$('cancelEditBtn').addEventListener('click', resetEntryForm);
|
||||
}
|
||||
|
||||
|
|
@ -79,11 +80,9 @@ function toggleHeaderEntries() {
|
|||
|
||||
function setHeaderEntriesCollapsed(collapsed) {
|
||||
const entries = document.querySelector('.header-entries');
|
||||
const button = $('toggleEntriesBtn');
|
||||
if (!entries || !button) return;
|
||||
if (!entries) return;
|
||||
entries.classList.toggle('is-collapsed', collapsed);
|
||||
button.setAttribute('aria-expanded', collapsed ? 'false' : 'true');
|
||||
button.textContent = collapsed ? 'Show entries' : 'Hide entries';
|
||||
$('entriesTable').querySelector('thead').setAttribute('aria-expanded', collapsed ? 'false' : 'true');
|
||||
}
|
||||
|
||||
function renderAll() {
|
||||
|
|
@ -153,6 +152,7 @@ async function saveEntry(event) {
|
|||
source: $('source').value,
|
||||
status: $('status').value,
|
||||
allow_unpaid: $('allowUnpaid').checked ? 1 : 0,
|
||||
non_working_days: Number($('nonWorkingDays').value || 0),
|
||||
note: $('note').value.trim(),
|
||||
};
|
||||
|
||||
|
|
@ -229,18 +229,40 @@ function editEntry(id) {
|
|||
$('source').value = entry.source;
|
||||
$('status').value = entry.status;
|
||||
$('allowUnpaid').checked = Boolean(Number(entry.allow_unpaid));
|
||||
$('nonWorkingDays').value = Number(entry.non_working_days || 0);
|
||||
$('note').value = entry.note || '';
|
||||
updateWorkdayPreview();
|
||||
$('entryFormTitle').textContent = 'Edit date-range entry';
|
||||
$('saveEntryBtn').textContent = 'Update entry';
|
||||
$('cancelEditBtn').classList.remove('hidden');
|
||||
openEntryDialog(id);
|
||||
}
|
||||
|
||||
function updateWorkdayPreview() {
|
||||
const start = $('startDate').value;
|
||||
const end = $('endDate').value;
|
||||
const extra = Number($('nonWorkingDays').value || 0);
|
||||
const preview = $('workdayPreview');
|
||||
if (!start || !end) {
|
||||
preview.textContent = 'Set dates to see calculated working days.';
|
||||
return;
|
||||
}
|
||||
if (start > end) {
|
||||
preview.textContent = 'Start date must be before end date.';
|
||||
return;
|
||||
}
|
||||
const gross = workdaysBetween(start, end).length;
|
||||
const effective = Math.max(0, gross - extra);
|
||||
preview.textContent = `${days(effective)} working days will be used (${days(gross)} weekdays minus ${days(extra)} additional non-working days).`;
|
||||
}
|
||||
|
||||
function resetEntryForm() {
|
||||
$('entryForm').reset();
|
||||
$('entryId').value = '';
|
||||
$('source').value = 'auto';
|
||||
$('status').value = 'planned';
|
||||
$('nonWorkingDays').value = 0;
|
||||
updateWorkdayPreview();
|
||||
$('entryFormTitle').textContent = 'Add date-range entry';
|
||||
$('saveEntryBtn').textContent = 'Save entry';
|
||||
$('cancelEditBtn').classList.add('hidden');
|
||||
|
|
@ -295,6 +317,7 @@ function renderWarnings() {
|
|||
}
|
||||
|
||||
function renderEntries() {
|
||||
$('entriesHeaderTitle').textContent = `VACATIONS (${state.entries.length})`;
|
||||
const tbody = $('entriesTable').querySelector('tbody');
|
||||
if (!state.entries.length) {
|
||||
tbody.innerHTML = '<tr><td colspan="8" class="muted">No entries yet.</td></tr>';
|
||||
|
|
@ -302,7 +325,7 @@ function renderEntries() {
|
|||
}
|
||||
|
||||
tbody.innerHTML = state.entries.map((entry) => {
|
||||
const summary = state.plan.entrySummaries[entry.id] || { workdays: 0, usedVacation: 0, usedFeriefridage: 0, usedFlex: 0, usedFlexDays: 0 };
|
||||
const summary = state.plan.entrySummaries[entry.id] || { workdays: 0, grossWorkdays: 0, nonWorkingDays: 0, usedVacation: 0, usedFeriefridage: 0, usedFlex: 0, usedFlexDays: 0 };
|
||||
const source = sourceLabels[entry.source] || entry.source;
|
||||
return `
|
||||
<tr>
|
||||
|
|
@ -403,11 +426,12 @@ function calculatePlan(settings, entries, corrections = {}, feriefridageOverride
|
|||
|
||||
const allWorkdays = [];
|
||||
for (const entry of entries) {
|
||||
const dates = workdaysBetween(entry.start_date, entry.end_date);
|
||||
entrySummaries[entry.id] = { workdays: dates.length, usedVacation: 0, usedFeriefridage: 0, usedFlex: 0, usedFlexDays: 0, usedUnpaid: 0 };
|
||||
for (const date of dates) {
|
||||
if (date < startDate || date > endDate) continue;
|
||||
allWorkdays.push({ date, entryId: entry.id });
|
||||
const grossDates = workdaysBetween(entry.start_date, entry.end_date);
|
||||
const units = workdayUnitsForEntry(entry);
|
||||
entrySummaries[entry.id] = { workdays: sumFractions(units), grossWorkdays: grossDates.length, nonWorkingDays: Number(entry.non_working_days || 0), usedVacation: 0, usedFeriefridage: 0, usedFlex: 0, usedFlexDays: 0, usedUnpaid: 0 };
|
||||
for (const unit of units) {
|
||||
if (unit.date < startDate || unit.date > endDate) continue;
|
||||
allWorkdays.push({ date: unit.date, fraction: unit.fraction, entryId: entry.id });
|
||||
}
|
||||
}
|
||||
allWorkdays.sort((a, b) => a.date - b.date || a.entryId - b.entryId);
|
||||
|
|
@ -468,7 +492,7 @@ function calculatePlan(settings, entries, corrections = {}, feriefridageOverride
|
|||
vacationBalance,
|
||||
feriefridageBalance,
|
||||
flexBalance,
|
||||
}, settings);
|
||||
}, settings, item.fraction);
|
||||
|
||||
vacationBalance = allocation.balances.vacationBalance;
|
||||
feriefridageBalance = allocation.balances.feriefridageBalance;
|
||||
|
|
@ -502,7 +526,7 @@ function calculatePlan(settings, entries, corrections = {}, feriefridageOverride
|
|||
};
|
||||
current.entries.push(monthEntry);
|
||||
}
|
||||
monthEntry.workdays += 1;
|
||||
monthEntry.workdays += item.fraction;
|
||||
monthEntry.usedVacation += allocation.usedVacation;
|
||||
monthEntry.usedFeriefridage += allocation.usedFeriefridage;
|
||||
monthEntry.usedFlex += allocation.usedFlex;
|
||||
|
|
@ -586,8 +610,8 @@ function calculateCurrentSnapshot(settings, entries, corrections = {}, feriefrid
|
|||
|
||||
const workdays = [];
|
||||
for (const entry of entries) {
|
||||
for (const date of workdaysBetween(entry.start_date, entry.end_date)) {
|
||||
if (date >= startDate && date <= today) workdays.push({ date, entry });
|
||||
for (const unit of workdayUnitsForEntry(entry)) {
|
||||
if (unit.date >= startDate && unit.date <= today) workdays.push({ date: unit.date, fraction: unit.fraction, entry });
|
||||
}
|
||||
}
|
||||
workdays.sort((a, b) => a.date - b.date || a.entry.id - b.entry.id);
|
||||
|
|
@ -610,7 +634,7 @@ function calculateCurrentSnapshot(settings, entries, corrections = {}, feriefrid
|
|||
const limit = today < monthEnd ? today : monthEnd;
|
||||
while (workdayIndex < workdays.length && workdays[workdayIndex].date >= monthStart && workdays[workdayIndex].date <= limit) {
|
||||
const item = workdays[workdayIndex++];
|
||||
const allocation = allocateDay(item.entry, item.date, { vacationBalance, feriefridageBalance, flexBalance }, settings);
|
||||
const allocation = allocateDay(item.entry, item.date, { vacationBalance, feriefridageBalance, flexBalance }, settings, item.fraction);
|
||||
vacationBalance = allocation.balances.vacationBalance;
|
||||
feriefridageBalance = allocation.balances.feriefridageBalance;
|
||||
flexBalance = allocation.balances.flexBalance;
|
||||
|
|
@ -659,16 +683,16 @@ function feriefridageRemainingAfterApprovedPlans(settings, entries, today, balan
|
|||
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 });
|
||||
for (const unit of workdayUnitsForEntry(entry)) {
|
||||
if (unit.date > today && unit.date <= deadline) {
|
||||
futureApprovedWorkdays.push({ date: unit.date, fraction: unit.fraction, 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);
|
||||
const allocation = allocateDay(item.entry, item.date, { vacationBalance, feriefridageBalance, flexBalance }, settings, item.fraction);
|
||||
vacationBalance = allocation.balances.vacationBalance;
|
||||
feriefridageBalance = allocation.balances.feriefridageBalance;
|
||||
flexBalance = allocation.balances.flexBalance;
|
||||
|
|
@ -677,34 +701,34 @@ function feriefridageRemainingAfterApprovedPlans(settings, entries, today, balan
|
|||
return feriefridageBalance;
|
||||
}
|
||||
|
||||
function allocateDay(entry, date, balances, settings) {
|
||||
function allocateDay(entry, date, balances, settings, fraction = 1) {
|
||||
const source = entry.source;
|
||||
const allowUnpaid = Boolean(Number(entry.allow_unpaid));
|
||||
const dayHours = hoursForDate(date, settings);
|
||||
const dayHours = hoursForDate(date, settings) * fraction;
|
||||
let { vacationBalance, feriefridageBalance, flexBalance } = balances;
|
||||
const result = { usedVacation: 0, usedFeriefridage: 0, usedFlex: 0, usedFlexDays: 0, usedUnpaid: 0 };
|
||||
|
||||
const useVacation = () => {
|
||||
if (allowUnpaid && vacationBalance < 1) {
|
||||
if (allowUnpaid && vacationBalance < fraction) {
|
||||
const use = Math.max(0, vacationBalance);
|
||||
result.usedVacation += use;
|
||||
result.usedUnpaid += 1 - use;
|
||||
result.usedUnpaid += fraction - use;
|
||||
vacationBalance = 0;
|
||||
} else {
|
||||
result.usedVacation += 1;
|
||||
vacationBalance -= 1;
|
||||
result.usedVacation += fraction;
|
||||
vacationBalance -= fraction;
|
||||
}
|
||||
};
|
||||
|
||||
const useFeriefridag = () => {
|
||||
if (allowUnpaid && feriefridageBalance < 1) {
|
||||
if (allowUnpaid && feriefridageBalance < fraction) {
|
||||
const use = Math.max(0, feriefridageBalance);
|
||||
result.usedFeriefridage += use;
|
||||
result.usedUnpaid += 1 - use;
|
||||
result.usedUnpaid += fraction - use;
|
||||
feriefridageBalance = 0;
|
||||
} else {
|
||||
result.usedFeriefridage += 1;
|
||||
feriefridageBalance -= 1;
|
||||
result.usedFeriefridage += fraction;
|
||||
feriefridageBalance -= fraction;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -712,18 +736,18 @@ function allocateDay(entry, date, balances, settings) {
|
|||
if (allowUnpaid && flexBalance < dayHours) {
|
||||
const useHours = Math.max(0, flexBalance);
|
||||
result.usedFlex += useHours;
|
||||
result.usedFlexDays += useHours / dayHours;
|
||||
result.usedUnpaid += 1 - (useHours / dayHours);
|
||||
result.usedFlexDays += useHours / hoursForDate(date, settings);
|
||||
result.usedUnpaid += fraction - (useHours / hoursForDate(date, settings));
|
||||
flexBalance = 0;
|
||||
} else {
|
||||
result.usedFlex += dayHours;
|
||||
result.usedFlexDays += 1;
|
||||
result.usedFlexDays += fraction;
|
||||
flexBalance -= dayHours;
|
||||
}
|
||||
};
|
||||
|
||||
if (source === 'unpaid') {
|
||||
result.usedUnpaid = 1;
|
||||
result.usedUnpaid = fraction;
|
||||
} else if (source === 'vacation') {
|
||||
useVacation();
|
||||
} else if (source === 'feriefridag') {
|
||||
|
|
@ -731,16 +755,16 @@ function allocateDay(entry, date, balances, settings) {
|
|||
} else if (source === 'flex') {
|
||||
useFlex();
|
||||
} else {
|
||||
if (feriefridageBalance >= 1 && isBeforeFeriefridageExpiry(date, settings)) {
|
||||
if (feriefridageBalance >= fraction && isBeforeFeriefridageExpiry(date, settings)) {
|
||||
useFeriefridag();
|
||||
} else if (vacationBalance >= 1) {
|
||||
} else if (vacationBalance >= fraction) {
|
||||
useVacation();
|
||||
} else if (flexBalance >= dayHours) {
|
||||
useFlex();
|
||||
} else if (allowUnpaid) {
|
||||
const use = Math.max(0, vacationBalance);
|
||||
result.usedVacation += use;
|
||||
result.usedUnpaid += 1 - use;
|
||||
result.usedUnpaid += fraction - use;
|
||||
vacationBalance = 0;
|
||||
} else {
|
||||
useVacation();
|
||||
|
|
@ -766,6 +790,21 @@ function workdaysBetween(start, end) {
|
|||
return days;
|
||||
}
|
||||
|
||||
function workdayUnitsForEntry(entry) {
|
||||
const units = workdaysBetween(entry.start_date, entry.end_date).map((date) => ({ date, fraction: 1 }));
|
||||
let remaining = Math.max(0, Number(entry.non_working_days || 0));
|
||||
for (let i = units.length - 1; i >= 0 && remaining > 0; i--) {
|
||||
const reduction = Math.min(units[i].fraction, remaining);
|
||||
units[i].fraction -= reduction;
|
||||
remaining -= reduction;
|
||||
}
|
||||
return units.filter((unit) => unit.fraction > 0.0001);
|
||||
}
|
||||
|
||||
function sumFractions(units) {
|
||||
return units.reduce((sum, unit) => sum + unit.fraction, 0);
|
||||
}
|
||||
|
||||
function hoursForDate(date, settings) {
|
||||
return date.getDay() === 5 ? Number(settings.friday_hours || 7) : Number(settings.normal_day_hours || 7.5);
|
||||
}
|
||||
|
|
@ -800,6 +839,7 @@ function normalizeEntry(entry) {
|
|||
...entry,
|
||||
id: Number(entry.id),
|
||||
allow_unpaid: Number(entry.allow_unpaid),
|
||||
non_working_days: Number(entry.non_working_days || 0),
|
||||
status: entry.status === 'confirmed' ? 'approved' : entry.status,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
<table id="entriesTable" class="compact-entries-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Entry</th>
|
||||
<th id="entriesHeaderTitle">VACATIONS</th>
|
||||
<th class="num">Days</th>
|
||||
<th class="num">Vac. days</th>
|
||||
<th class="num">FF</th>
|
||||
|
|
@ -30,7 +30,6 @@
|
|||
<div class="header-warning-row" aria-live="polite">
|
||||
<span id="headerWarnings" class="header-warning-list"></span>
|
||||
<span class="header-action-list">
|
||||
<button id="toggleEntriesBtn" class="header-action-button" type="button" aria-expanded="true">Entries</button>
|
||||
<button id="addEntryBtn" class="header-action-button" type="button">Add entry</button>
|
||||
<button id="settingsBtn" class="header-action-button" type="button">Settings</button>
|
||||
</span>
|
||||
|
|
@ -93,6 +92,11 @@
|
|||
<option value="approved">Approved</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Additional non-working days
|
||||
<input id="nonWorkingDays" type="number" step="0.5" min="0" value="0">
|
||||
</label>
|
||||
<div id="workdayPreview" class="workday-preview full">Set dates to see calculated working days.</div>
|
||||
<label class="checkbox-row">
|
||||
<input id="allowUnpaid" type="checkbox">
|
||||
<span>If this entry goes negative, convert missing days in this date range to unpaid</span>
|
||||
|
|
|
|||
12
style.css
12
style.css
|
|
@ -28,7 +28,7 @@ body {
|
|||
top: 0;
|
||||
z-index: 50;
|
||||
padding: .35rem clamp(.45rem, 2vw, 1.25rem);
|
||||
background: linear-gradient(180deg, rgba(36,26,22,.96), rgba(36,26,22,.82));
|
||||
background: linear-gradient(180deg, rgba(55,42,34,.88), rgba(43,32,26,.74));
|
||||
border-bottom: 1px solid rgba(163,138,99,.32);
|
||||
box-shadow: 0 2px 16px #0008;
|
||||
backdrop-filter: blur(12px);
|
||||
|
|
@ -78,7 +78,8 @@ h2 { margin-bottom: 0; font-size: 1.1rem; }
|
|||
border-radius: 10px;
|
||||
background: rgba(24,18,15,.72);
|
||||
}
|
||||
.header-entries.is-collapsed { display: none; }
|
||||
.header-entries.is-collapsed tbody { display: none; }
|
||||
.header-entries thead { cursor: pointer; user-select: none; }
|
||||
.header-entries .table-wrap { overflow-x: auto; overflow-y: visible; }
|
||||
.compact-entries-table { font-size: .74rem; }
|
||||
.compact-entries-table th,
|
||||
|
|
@ -389,8 +390,8 @@ tbody tr:hover { background: rgba(255,255,255,.04); }
|
|||
.header-entries .table-wrap { overflow-x: visible; overflow-y: visible; }
|
||||
.compact-entries-table,
|
||||
.compact-entries-table tbody { display: block; width: 100%; }
|
||||
.compact-entries-table thead { display: none; }
|
||||
.compact-entries-table tr {
|
||||
.compact-entries-table thead { display: table-header-group; cursor: pointer; }
|
||||
.compact-entries-table tbody tr {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) repeat(4, auto);
|
||||
gap: .12rem .32rem;
|
||||
|
|
@ -398,7 +399,7 @@ tbody tr:hover { background: rgba(255,255,255,.04); }
|
|||
padding: .18rem 0;
|
||||
border-bottom: 1px solid rgba(163,138,99,.22);
|
||||
}
|
||||
.compact-entries-table tr:last-child { border-bottom: 0; }
|
||||
.compact-entries-table tbody tr:last-child { border-bottom: 0; }
|
||||
.compact-entries-table td {
|
||||
display: block;
|
||||
border-bottom: 0;
|
||||
|
|
@ -406,6 +407,7 @@ tbody tr:hover { background: rgba(255,255,255,.04); }
|
|||
white-space: normal;
|
||||
line-height: 1.05;
|
||||
}
|
||||
.compact-entries-table th { padding: .12rem .2rem; font-size: .62rem; }
|
||||
.compact-entries-table td:nth-child(1) { grid-column: 1 / -1; min-width: 0; }
|
||||
.compact-entries-table td:nth-child(2)::before { content: 'wd '; color: var(--muted); font-weight: 700; }
|
||||
.compact-entries-table td:nth-child(3)::before { content: 'vd '; color: var(--muted); font-weight: 700; }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue