feat(adjustments): date picker single/range toggle + preset quick-buttons
Single by default (one <input> + '...' toggle reveals the second). In single mode the JS mirrors From into the hidden To on every change, so form submit sends adj_date_from=adj_date_to=X for an exact-day filter on the backend (contract unchanged). Four presets: Today (single), This week (Mon-Sun range), This month (1st to last, range), Clear. Presets auto-switch mode so users see what was populated. On page load, range mode is inferred from the URL: if both dates present AND differ -> range mode; else single mode. That way a bookmarked range URL still shows both pickers. No backend changes, no new tests — the 8 existing adjustments tests already cover the from/to contract shape.
This commit is contained in:
parent
6905703492
commit
c851b49dea
@ -716,16 +716,32 @@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{# --- Date range --- #}
|
||||
<div style="min-width: 130px;">
|
||||
<label for="adjDateFrom" class="form-label small mb-1">From</label>
|
||||
<input type="date" id="adjDateFrom" name="adj_date_from" class="form-control form-control-sm"
|
||||
{# --- Date picker: single by default, "..." toggles range mode --- #}
|
||||
{# - Single mode (default): pick ONE date; JS mirrors it into the hidden #}
|
||||
{# "To" field on submit so the backend's adj_date_from/adj_date_to #}
|
||||
{# contract stays the same. #}
|
||||
{# - Range mode: both pickers visible, JS leaves the "To" alone. #}
|
||||
{# - Presets below: Today / This week / This month / Clear. #}
|
||||
<div id="adjDateWrap" style="min-width: 180px;">
|
||||
<label class="form-label small mb-1 d-flex align-items-center gap-2">
|
||||
<span id="adjDateLabel">Date</span>
|
||||
<button type="button" id="adjDateRangeToggle" class="btn btn-link btn-sm p-0 ms-auto"
|
||||
title="Toggle range mode" aria-label="Toggle date range mode">
|
||||
<i class="fas fa-ellipsis-h"></i>
|
||||
</button>
|
||||
</label>
|
||||
<input type="date" name="adj_date_from" id="adjDateFrom"
|
||||
class="form-control form-control-sm"
|
||||
value="{{ adj_filter_values.adj_date_from }}">
|
||||
</div>
|
||||
<div style="min-width: 130px;">
|
||||
<label for="adjDateTo" class="form-label small mb-1">To</label>
|
||||
<input type="date" id="adjDateTo" name="adj_date_to" class="form-control form-control-sm"
|
||||
value="{{ adj_filter_values.adj_date_to }}">
|
||||
<input type="date" name="adj_date_to" id="adjDateTo"
|
||||
class="form-control form-control-sm mt-1"
|
||||
value="{{ adj_filter_values.adj_date_to }}" hidden>
|
||||
<div class="d-flex gap-2 mt-1 small" id="adjDatePresets">
|
||||
<button type="button" class="btn btn-link btn-sm p-0 text-muted" data-preset="today">Today</button>
|
||||
<button type="button" class="btn btn-link btn-sm p-0 text-muted" data-preset="week">This week</button>
|
||||
<button type="button" class="btn btn-link btn-sm p-0 text-muted" data-preset="month">This month</button>
|
||||
<button type="button" class="btn btn-link btn-sm p-0 text-muted" data-preset="clear">Clear</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# --- Sort state (column-header clicks will set these via JS in Task 9) --- #}
|
||||
@ -3668,6 +3684,74 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
applyWorkerCrossFilter();
|
||||
}
|
||||
|
||||
// === ADJUSTMENTS TAB — Date picker single/range toggle + presets ===
|
||||
// Single mode (default): one <input> visible. On form submit, JS mirrors
|
||||
// the From value into the hidden To so backend gets from==to (exact-day
|
||||
// filter). Range mode: both inputs visible, user picks independently.
|
||||
var dateWrap = document.getElementById('adjDateWrap');
|
||||
if (dateWrap) {
|
||||
var dateFrom = document.getElementById('adjDateFrom');
|
||||
var dateTo = document.getElementById('adjDateTo');
|
||||
var toggleBtn = document.getElementById('adjDateRangeToggle');
|
||||
var dateLabel = document.getElementById('adjDateLabel');
|
||||
|
||||
function applyMode(rangeMode) {
|
||||
dateTo.hidden = !rangeMode;
|
||||
dateLabel.textContent = rangeMode ? 'Date range' : 'Date';
|
||||
// In single mode mirror dateFrom into dateTo so form submit
|
||||
// sends equal values for an exact-day match.
|
||||
if (!rangeMode) dateTo.value = dateFrom.value;
|
||||
}
|
||||
// Start in range mode if URL already has different From and To
|
||||
var urlFrom = dateFrom.value || '';
|
||||
var urlTo = dateTo.value || '';
|
||||
var initialRange = !!urlFrom && !!urlTo && urlFrom !== urlTo;
|
||||
applyMode(initialRange);
|
||||
|
||||
toggleBtn.addEventListener('click', function() {
|
||||
// Toggle: if hidden was the 'To' then we're switching to range
|
||||
applyMode(dateTo.hidden);
|
||||
});
|
||||
// Keep the mirror in sync while in single mode
|
||||
dateFrom.addEventListener('change', function() {
|
||||
if (dateTo.hidden) dateTo.value = dateFrom.value;
|
||||
});
|
||||
|
||||
// Preset quick-buttons
|
||||
function iso(d) { return d.toISOString().slice(0, 10); }
|
||||
document.querySelectorAll('#adjDatePresets [data-preset]').forEach(function(btn) {
|
||||
btn.addEventListener('click', function() {
|
||||
var preset = btn.getAttribute('data-preset');
|
||||
var today = new Date();
|
||||
if (preset === 'today') {
|
||||
dateFrom.value = iso(today);
|
||||
dateTo.value = iso(today);
|
||||
applyMode(false);
|
||||
} else if (preset === 'week') {
|
||||
// ISO-week: Mon -> Sun
|
||||
var dayOffset = (today.getDay() + 6) % 7; // 0 = Monday
|
||||
var weekStart = new Date(today);
|
||||
weekStart.setDate(today.getDate() - dayOffset);
|
||||
var weekEnd = new Date(weekStart);
|
||||
weekEnd.setDate(weekStart.getDate() + 6);
|
||||
dateFrom.value = iso(weekStart);
|
||||
dateTo.value = iso(weekEnd);
|
||||
applyMode(true);
|
||||
} else if (preset === 'month') {
|
||||
var monthStart = new Date(today.getFullYear(), today.getMonth(), 1);
|
||||
var monthEnd = new Date(today.getFullYear(), today.getMonth() + 1, 0);
|
||||
dateFrom.value = iso(monthStart);
|
||||
dateTo.value = iso(monthEnd);
|
||||
applyMode(true);
|
||||
} else if (preset === 'clear') {
|
||||
dateFrom.value = '';
|
||||
dateTo.value = '';
|
||||
applyMode(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// --- Direct delete buttons on each unpaid row ---
|
||||
// Short-circuits the edit modal's usual 2-step delete flow by opening
|
||||
// #deleteConfirmModal directly with the correct form action + labels.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user