feat: Add month range filter to adults and juniors dashboards
Add from/to combobox selectors and Apply/All buttons to filter which month columns are displayed. Defaults to last 5 months on load. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -259,6 +259,24 @@
|
||||
border-color: #00ff00;
|
||||
}
|
||||
|
||||
.filter-select {
|
||||
background-color: #1a1a1a;
|
||||
border: 1px solid #333;
|
||||
color: #00ff00;
|
||||
font-family: inherit;
|
||||
font-size: 11px;
|
||||
padding: 4px 8px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.filter-select:focus {
|
||||
border-color: #00ff00;
|
||||
}
|
||||
|
||||
.month-hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.filter-label {
|
||||
color: #888;
|
||||
text-transform: lowercase;
|
||||
@@ -478,6 +496,20 @@
|
||||
<div class="filter-container">
|
||||
<span class="filter-label">search member:</span>
|
||||
<input type="text" id="nameFilter" class="filter-input" placeholder="..." autocomplete="off">
|
||||
<span class="filter-label" style="margin-left: 16px;">from:</span>
|
||||
<select id="fromMonth" class="filter-select">
|
||||
{% for m in months %}
|
||||
<option value="{{ loop.index0 }}">{{ m }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<span class="filter-label" style="margin-left: 8px;">to:</span>
|
||||
<select id="toMonth" class="filter-select">
|
||||
{% for m in months %}
|
||||
<option value="{{ loop.index0 }}">{{ m }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<button type="button" onclick="applyMonthFilter()" class="filter-select" style="cursor: pointer;">Apply</button>
|
||||
<button type="button" onclick="resetMonthFilter()" class="filter-select" style="cursor: pointer;">All</button>
|
||||
</div>
|
||||
|
||||
<div class="table-container">
|
||||
@@ -486,7 +518,7 @@
|
||||
<tr>
|
||||
<th>Member</th>
|
||||
{% for m in months %}
|
||||
<th>{{ m }}</th>
|
||||
<th data-month-idx="{{ loop.index0 }}">{{ m }}</th>
|
||||
{% endfor %}
|
||||
<th>Balance</th>
|
||||
</tr>
|
||||
@@ -499,7 +531,7 @@
|
||||
<span class="info-icon" onclick="showMemberDetails('{{ row.name|e }}')">[i]</span>
|
||||
</td>
|
||||
{% for cell in row.months %}
|
||||
<td title="{{ cell.tooltip }}"
|
||||
<td data-month-idx="{{ loop.index0 }}" title="{{ cell.tooltip }}"
|
||||
class="{% if cell.status == 'empty' %}cell-empty{% elif cell.status == 'unpaid' or cell.status == 'partial' %}cell-unpaid{% elif cell.status == 'ok' %}cell-ok{% endif %}{% if cell.overridden %} cell-overridden{% endif %}">
|
||||
{{ cell.text }}
|
||||
{% if cell.status == 'unpaid' or cell.status == 'partial' %}
|
||||
@@ -522,7 +554,7 @@
|
||||
TOTAL
|
||||
</td>
|
||||
{% for t in totals %}
|
||||
<td class="{% if t.status == 'ok' %}cell-ok{% elif t.status == 'unpaid' %}cell-unpaid{% elif t.status == 'surplus' %}cell-overridden{% endif %}" style="padding-top: 4px; padding-bottom: 4px;">
|
||||
<td data-month-idx="{{ loop.index0 }}" class="{% if t.status == 'ok' %}cell-ok{% elif t.status == 'unpaid' %}cell-unpaid{% elif t.status == 'surplus' %}cell-overridden{% endif %}" style="padding-top: 4px; padding-bottom: 4px;">
|
||||
<span style="font-size: 0.6em; font-weight: normal; color: #666; text-transform: lowercase; display: block; margin-bottom: 2px;">received / expected</span>
|
||||
{{ t.text }}
|
||||
</td>
|
||||
@@ -889,6 +921,38 @@
|
||||
event.target.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// Month range filter
|
||||
function applyMonthFilter() {
|
||||
var fromIdx = parseInt(document.getElementById('fromMonth').value);
|
||||
var toIdx = parseInt(document.getElementById('toMonth').value);
|
||||
document.querySelectorAll('[data-month-idx]').forEach(function(el) {
|
||||
var idx = parseInt(el.getAttribute('data-month-idx'));
|
||||
if (idx >= fromIdx && idx <= toIdx) {
|
||||
el.classList.remove('month-hidden');
|
||||
} else {
|
||||
el.classList.add('month-hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function resetMonthFilter() {
|
||||
var fromSelect = document.getElementById('fromMonth');
|
||||
var toSelect = document.getElementById('toMonth');
|
||||
fromSelect.value = 0;
|
||||
toSelect.value = fromSelect.options.length - 1;
|
||||
applyMonthFilter();
|
||||
}
|
||||
|
||||
// Set defaults (last 5 months) and apply on load
|
||||
(function() {
|
||||
var fromSelect = document.getElementById('fromMonth');
|
||||
var toSelect = document.getElementById('toMonth');
|
||||
var defaultFrom = Math.max(0, fromSelect.options.length - 5);
|
||||
fromSelect.value = defaultFrom;
|
||||
toSelect.value = fromSelect.options.length - 1;
|
||||
applyMonthFilter();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
@@ -259,6 +259,24 @@
|
||||
border-color: #00ff00;
|
||||
}
|
||||
|
||||
.filter-select {
|
||||
background-color: #1a1a1a;
|
||||
border: 1px solid #333;
|
||||
color: #00ff00;
|
||||
font-family: inherit;
|
||||
font-size: 11px;
|
||||
padding: 4px 8px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.filter-select:focus {
|
||||
border-color: #00ff00;
|
||||
}
|
||||
|
||||
.month-hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.filter-label {
|
||||
color: #888;
|
||||
text-transform: lowercase;
|
||||
@@ -478,6 +496,20 @@
|
||||
<div class="filter-container">
|
||||
<span class="filter-label">search member:</span>
|
||||
<input type="text" id="nameFilter" class="filter-input" placeholder="..." autocomplete="off">
|
||||
<span class="filter-label" style="margin-left: 16px;">from:</span>
|
||||
<select id="fromMonth" class="filter-select">
|
||||
{% for m in months %}
|
||||
<option value="{{ loop.index0 }}">{{ m }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<span class="filter-label" style="margin-left: 8px;">to:</span>
|
||||
<select id="toMonth" class="filter-select">
|
||||
{% for m in months %}
|
||||
<option value="{{ loop.index0 }}">{{ m }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<button type="button" onclick="applyMonthFilter()" class="filter-select" style="cursor: pointer;">Apply</button>
|
||||
<button type="button" onclick="resetMonthFilter()" class="filter-select" style="cursor: pointer;">All</button>
|
||||
</div>
|
||||
|
||||
<div class="table-container">
|
||||
@@ -486,7 +518,7 @@
|
||||
<tr>
|
||||
<th>Member</th>
|
||||
{% for m in months %}
|
||||
<th>{{ m }}</th>
|
||||
<th data-month-idx="{{ loop.index0 }}">{{ m }}</th>
|
||||
{% endfor %}
|
||||
<th>Balance</th>
|
||||
</tr>
|
||||
@@ -499,7 +531,7 @@
|
||||
<span class="info-icon" onclick="showMemberDetails('{{ row.name|e }}')">[i]</span>
|
||||
</td>
|
||||
{% for cell in row.months %}
|
||||
<td title="{{ cell.tooltip }}"
|
||||
<td data-month-idx="{{ loop.index0 }}" title="{{ cell.tooltip }}"
|
||||
class="{% if cell.status == 'empty' %}cell-empty{% elif cell.status == 'unpaid' or cell.status == 'partial' %}cell-unpaid{% elif cell.status == 'ok' %}cell-ok{% endif %}{% if cell.overridden %} cell-overridden{% endif %}">
|
||||
{{ cell.text }}
|
||||
{% if cell.status == 'unpaid' or cell.status == 'partial' %}
|
||||
@@ -522,7 +554,7 @@
|
||||
TOTAL
|
||||
</td>
|
||||
{% for t in totals %}
|
||||
<td class="{% if t.status == 'ok' %}cell-ok{% elif t.status == 'unpaid' %}cell-unpaid{% elif t.status == 'surplus' %}cell-overridden{% endif %}" style="padding-top: 4px; padding-bottom: 4px;">
|
||||
<td data-month-idx="{{ loop.index0 }}" class="{% if t.status == 'ok' %}cell-ok{% elif t.status == 'unpaid' %}cell-unpaid{% elif t.status == 'surplus' %}cell-overridden{% endif %}" style="padding-top: 4px; padding-bottom: 4px;">
|
||||
<span style="font-size: 0.6em; font-weight: normal; color: #666; text-transform: lowercase; display: block; margin-bottom: 2px;">received / expected</span>
|
||||
{{ t.text }}
|
||||
</td>
|
||||
@@ -870,6 +902,38 @@
|
||||
event.target.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// Month range filter
|
||||
function applyMonthFilter() {
|
||||
var fromIdx = parseInt(document.getElementById('fromMonth').value);
|
||||
var toIdx = parseInt(document.getElementById('toMonth').value);
|
||||
document.querySelectorAll('[data-month-idx]').forEach(function(el) {
|
||||
var idx = parseInt(el.getAttribute('data-month-idx'));
|
||||
if (idx >= fromIdx && idx <= toIdx) {
|
||||
el.classList.remove('month-hidden');
|
||||
} else {
|
||||
el.classList.add('month-hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function resetMonthFilter() {
|
||||
var fromSelect = document.getElementById('fromMonth');
|
||||
var toSelect = document.getElementById('toMonth');
|
||||
fromSelect.value = 0;
|
||||
toSelect.value = fromSelect.options.length - 1;
|
||||
applyMonthFilter();
|
||||
}
|
||||
|
||||
// Set defaults (last 5 months) and apply on load
|
||||
(function() {
|
||||
var fromSelect = document.getElementById('fromMonth');
|
||||
var toSelect = document.getElementById('toMonth');
|
||||
var defaultFrom = Math.max(0, fromSelect.options.length - 5);
|
||||
fromSelect.value = defaultFrom;
|
||||
toSelect.value = fromSelect.options.length - 1;
|
||||
applyMonthFilter();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user