snapshot: preserve dashboard filters and realtime search
This commit is contained in:
@@ -425,18 +425,37 @@
|
||||
|
||||
.search-form {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.filter-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.filter-field {
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.filter-field label {
|
||||
font-size: 11px;
|
||||
font-weight: 800;
|
||||
color: #294465;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.search-box input {
|
||||
.search-box input,
|
||||
.filter-field select,
|
||||
.filter-field input[type="date"] {
|
||||
width: 100%;
|
||||
border: 1px solid var(--line-strong);
|
||||
border-radius: 16px;
|
||||
padding: 14px 16px;
|
||||
border-radius: 14px;
|
||||
padding: 11px 13px;
|
||||
font: inherit;
|
||||
font-weight: 600;
|
||||
color: var(--ink);
|
||||
@@ -444,7 +463,9 @@
|
||||
box-shadow: inset 0 1px 0 rgba(255,255,255,0.98);
|
||||
}
|
||||
|
||||
.search-box input:focus {
|
||||
.search-box input:focus,
|
||||
.filter-field select:focus,
|
||||
.filter-field input[type="date"]:focus {
|
||||
outline: none;
|
||||
border-color: rgba(0, 0, 120, 0.3);
|
||||
box-shadow: 0 0 0 4px rgba(0, 0, 120, 0.08);
|
||||
@@ -488,7 +509,7 @@
|
||||
}
|
||||
|
||||
.table-controls {
|
||||
padding: 14px 18px 14px;
|
||||
padding: 12px 18px 12px;
|
||||
border-bottom: 1px solid var(--line);
|
||||
background: linear-gradient(180deg, rgba(255,255,255,0.98), rgba(244,248,255,0.96));
|
||||
}
|
||||
@@ -496,16 +517,16 @@
|
||||
.table-controls-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
gap: 14px;
|
||||
gap: 10px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.control-stack {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
padding: 14px;
|
||||
gap: 10px;
|
||||
padding: 12px;
|
||||
border: 1px solid rgba(217, 227, 238, 0.95);
|
||||
border-radius: 18px;
|
||||
border-radius: 16px;
|
||||
background: linear-gradient(180deg, rgba(255,255,255,0.98), rgba(249,251,255,0.95));
|
||||
box-shadow: inset 0 1px 0 rgba(255,255,255,0.9);
|
||||
}
|
||||
@@ -848,6 +869,10 @@
|
||||
.table-controls-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.filter-grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
@@ -860,4 +885,5 @@
|
||||
.topbar { flex-direction: column; }
|
||||
.quick-actions { justify-content: flex-start; }
|
||||
.table-wrap { padding-left: 12px; padding-right: 12px; }
|
||||
.filter-grid { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
@@ -1,4 +1,34 @@
|
||||
(function () {
|
||||
const filterForm = document.querySelector('.search-form');
|
||||
if (filterForm) {
|
||||
const searchInput = filterForm.querySelector('input[name="q"]');
|
||||
const immediateFields = Array.from(
|
||||
filterForm.querySelectorAll('select[name], input[type="date"][name]')
|
||||
);
|
||||
let debounceTimer = null;
|
||||
|
||||
const submitFilters = function () {
|
||||
if (debounceTimer) {
|
||||
window.clearTimeout(debounceTimer);
|
||||
debounceTimer = null;
|
||||
}
|
||||
filterForm.requestSubmit();
|
||||
};
|
||||
|
||||
if (searchInput) {
|
||||
searchInput.addEventListener('input', function () {
|
||||
if (debounceTimer) {
|
||||
window.clearTimeout(debounceTimer);
|
||||
}
|
||||
debounceTimer = window.setTimeout(submitFilters, 280);
|
||||
});
|
||||
}
|
||||
|
||||
immediateFields.forEach(function (field) {
|
||||
field.addEventListener('change', submitFilters);
|
||||
});
|
||||
}
|
||||
|
||||
const selectAll = document.getElementById('select-all');
|
||||
const rowChecks = Array.from(document.querySelectorAll('.row-select'));
|
||||
const selectedCount = document.getElementById('selected-count');
|
||||
|
||||
@@ -112,7 +112,7 @@
|
||||
{% endif %}
|
||||
|
||||
<section class="content-grid">
|
||||
<section class="table-card">
|
||||
<section class="table-card" id="vorgaenge">
|
||||
<div class="table-head">
|
||||
<div>
|
||||
<h2>{% trans "Vorgänge" %}</h2>
|
||||
@@ -123,14 +123,50 @@
|
||||
<div class="table-controls">
|
||||
<div class="table-controls-grid">
|
||||
<div class="control-stack">
|
||||
<form method="get" action="/requests/" class="search-form">
|
||||
<form method="get" action="/requests/#vorgaenge" class="search-form">
|
||||
<div class="search-box">
|
||||
<input type="search" name="q" value="{{ search_query }}" placeholder="{% trans "Nach Name oder E-Mail suchen" %}" aria-label="{% trans "Nach Name oder E-Mail suchen" %}" />
|
||||
</div>
|
||||
<div class="filter-grid">
|
||||
<div class="filter-field">
|
||||
<label for="type-filter">{% trans "Typ" %}</label>
|
||||
<select id="type-filter" name="type">
|
||||
<option value="">{% trans "Alle" %}</option>
|
||||
<option value="onboarding" {% if selected_type == 'onboarding' %}selected{% endif %}>{% trans "Onboarding" %}</option>
|
||||
<option value="offboarding" {% if selected_type == 'offboarding' %}selected{% endif %}>{% trans "Offboarding" %}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-field">
|
||||
<label for="status-filter">{% trans "Status" %}</label>
|
||||
<select id="status-filter" name="status">
|
||||
<option value="">{% trans "Alle" %}</option>
|
||||
{% for choice in status_choices %}
|
||||
<option value="{{ choice.value }}" {% if selected_status == choice.value %}selected{% endif %}>{{ choice.label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-field">
|
||||
<label for="department-filter">{% trans "Abteilung" %}</label>
|
||||
<select id="department-filter" name="department">
|
||||
<option value="">{% trans "Alle" %}</option>
|
||||
{% for department in departments %}
|
||||
<option value="{{ department }}" {% if selected_department == department %}selected{% endif %}>{{ department }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-field">
|
||||
<label for="date-from-filter">{% trans "Von" %}</label>
|
||||
<input id="date-from-filter" type="date" name="date_from" value="{{ date_from }}" />
|
||||
</div>
|
||||
<div class="filter-field">
|
||||
<label for="date-to-filter">{% trans "Bis" %}</label>
|
||||
<input id="date-to-filter" type="date" name="date_to" value="{{ date_to }}" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="intro-actions">
|
||||
<button class="btn btn-primary" type="submit">{% trans "Suchen" %}</button>
|
||||
{% if search_query %}
|
||||
<a class="btn btn-secondary" href="/requests/">{% trans "Zurücksetzen" %}</a>
|
||||
{% if has_filters %}
|
||||
<a class="btn btn-secondary" href="/requests/#vorgaenge">{% trans "Zurücksetzen" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -557,11 +557,37 @@ def requests_dashboard(request):
|
||||
return redirect('requests_dashboard')
|
||||
|
||||
search_query = request.GET.get('q', '').strip()
|
||||
type_filter = (request.GET.get('type') or '').strip().lower()
|
||||
status_filter = (request.GET.get('status') or '').strip().lower()
|
||||
department_filter = (request.GET.get('department') or '').strip()
|
||||
date_from = (request.GET.get('date_from') or '').strip()
|
||||
date_to = (request.GET.get('date_to') or '').strip()
|
||||
|
||||
onboarding_qs = OnboardingRequest.objects.order_by('-created_at')
|
||||
offboarding_qs = OffboardingRequest.objects.order_by('-created_at')
|
||||
all_onboarding = OnboardingRequest.objects.all()
|
||||
all_offboarding = OffboardingRequest.objects.all()
|
||||
|
||||
if search_query:
|
||||
onboarding_qs = onboarding_qs.filter(Q(full_name__icontains=search_query) | Q(work_email__icontains=search_query))
|
||||
offboarding_qs = offboarding_qs.filter(Q(full_name__icontains=search_query) | Q(work_email__icontains=search_query))
|
||||
if status_filter in {'submitted', 'processing', 'completed', 'failed'}:
|
||||
onboarding_qs = onboarding_qs.filter(processing_status=status_filter)
|
||||
offboarding_qs = offboarding_qs.filter(processing_status=status_filter)
|
||||
if department_filter:
|
||||
onboarding_qs = onboarding_qs.filter(department=department_filter)
|
||||
offboarding_qs = offboarding_qs.filter(department=department_filter)
|
||||
if date_from:
|
||||
onboarding_qs = onboarding_qs.filter(created_at__date__gte=date_from)
|
||||
offboarding_qs = offboarding_qs.filter(created_at__date__gte=date_from)
|
||||
if date_to:
|
||||
onboarding_qs = onboarding_qs.filter(created_at__date__lte=date_to)
|
||||
offboarding_qs = offboarding_qs.filter(created_at__date__lte=date_to)
|
||||
|
||||
if type_filter == 'onboarding':
|
||||
offboarding_qs = offboarding_qs.none()
|
||||
elif type_filter == 'offboarding':
|
||||
onboarding_qs = onboarding_qs.none()
|
||||
|
||||
onboarding_items = onboarding_qs[:50]
|
||||
offboarding_items = offboarding_qs[:50]
|
||||
@@ -649,12 +675,36 @@ def requests_dashboard(request):
|
||||
|
||||
onboarding_total = onboarding_qs.count()
|
||||
offboarding_total = offboarding_qs.count()
|
||||
departments = sorted(
|
||||
{
|
||||
value.strip()
|
||||
for value in list(all_onboarding.exclude(department='').values_list('department', flat=True))
|
||||
+ list(all_offboarding.exclude(department='').values_list('department', flat=True))
|
||||
if value and value.strip()
|
||||
},
|
||||
key=str.lower,
|
||||
)
|
||||
status_choices = [
|
||||
{'value': 'submitted', 'label': _request_status_label('submitted', language_code)},
|
||||
{'value': 'processing', 'label': _request_status_label('processing', language_code)},
|
||||
{'value': 'completed', 'label': _request_status_label('completed', language_code)},
|
||||
{'value': 'failed', 'label': _request_status_label('failed', language_code)},
|
||||
]
|
||||
has_filters = any([search_query, type_filter, status_filter, department_filter, date_from, date_to])
|
||||
return render(
|
||||
request,
|
||||
'workflows/requests_dashboard.html',
|
||||
{
|
||||
'rows': rows[:60],
|
||||
'search_query': search_query,
|
||||
'selected_type': type_filter,
|
||||
'selected_status': status_filter,
|
||||
'selected_department': department_filter,
|
||||
'date_from': date_from,
|
||||
'date_to': date_to,
|
||||
'departments': departments,
|
||||
'status_choices': status_choices,
|
||||
'has_filters': has_filters,
|
||||
'onboarding_total': onboarding_total,
|
||||
'offboarding_total': offboarding_total,
|
||||
'combined_total': onboarding_total + offboarding_total,
|
||||
|
||||
Reference in New Issue
Block a user