feat: add archived services toggle to services list

- Backend: Accept archived query param to filter past vs future services
- Frontend: Add pill-style toggle with Kommende/Vergangene labels
- URL updates with ?archived=1 param when viewing past services
- Empty state text changes based on archived state
- Tests: Add coverage for archived filter functionality
This commit is contained in:
Thorsten Bus 2026-03-02 10:44:40 +01:00
parent d5abff0d82
commit 8dc26b8ae3
2 changed files with 114 additions and 4 deletions

View file

@ -8,6 +8,10 @@ const props = defineProps({
type: Array,
default: () => [],
},
archived: {
type: Boolean,
default: false,
},
})
const toastMessage = ref('')
@ -16,6 +20,7 @@ const confirmDialog = ref(false)
const confirmWarnings = ref([])
const confirmServiceId = ref(null)
const finalizing = ref(false)
const showArchived = ref(props.archived)
function formatDate(dateStr) {
if (!dateStr) return '—'
@ -172,9 +177,39 @@ function stateIconClass(isDone) {
<AuthenticatedLayout>
<template #header>
<div class="flex flex-wrap items-center justify-between gap-3">
<div class="flex flex-wrap items-center justify-between gap-4">
<div>
<h2 class="text-xl font-semibold leading-tight text-gray-800">Services</h2>
<p class="text-sm text-gray-500">Hier siehst du alle heutigen und kommenden Services.</p>
<p class="mt-1 text-sm text-gray-500">
{{ showArchived ? 'Hier siehst du alle vergangenen Services.' : 'Hier siehst du alle heutigen und kommenden Services.' }}
</p>
</div>
<div class="inline-flex rounded-lg border border-gray-300 bg-white p-1 shadow-sm">
<button
type="button"
:class="[
'rounded-md px-4 py-2 text-sm font-medium transition-all',
!showArchived
? 'bg-blue-600 text-white shadow-sm'
: 'text-gray-700 hover:bg-gray-100',
]"
@click="router.get(route('services.index'), { archived: 0 }, { preserveState: true, preserveScroll: true })"
>
Kommende
</button>
<button
type="button"
:class="[
'rounded-md px-4 py-2 text-sm font-medium transition-all',
showArchived
? 'bg-blue-600 text-white shadow-sm'
: 'text-gray-700 hover:bg-gray-100',
]"
@click="router.get(route('services.index'), { archived: 1 }, { preserveState: true, preserveScroll: true })"
>
Vergangene
</button>
</div>
</div>
</template>
@ -199,7 +234,7 @@ function stateIconClass(isDone) {
<div class="overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm">
<div v-if="services.length === 0" data-testid="service-list-empty" class="p-8 text-center text-sm text-gray-500">
Aktuell gibt es keine heutigen oder kommenden Services.
{{ showArchived ? 'Keine vergangenen Services vorhanden.' : 'Aktuell gibt es keine heutigen oder kommenden Services.' }}
</div>
<div v-else class="overflow-x-auto">

View file

@ -270,4 +270,79 @@ public function test_service_edit_erfordert_authentifizierung(): void
$response->assertRedirect(route('login'));
}
public function test_services_index_zeigt_nur_zukuenftige_services_standardmaessig(): void
{
Carbon::setTestNow('2026-03-01 10:00:00');
$this->withoutVite();
$user = User::factory()->create();
Service::factory()->create([
'date' => Carbon::today()->subDays(5),
'title' => 'Vergangener Service',
]);
$todayService = Service::factory()->create([
'date' => Carbon::today(),
'title' => 'Heutiger Service',
]);
$futureService = Service::factory()->create([
'date' => Carbon::today()->addDays(3),
'title' => 'Zukünftiger Service',
]);
$response = $this->actingAs($user)->get(route('services.index'));
$response->assertOk();
$response->assertInertia(
fn ($page) => $page
->component('Services/Index')
->has('services', 2)
->where('services.0.title', 'Heutiger Service')
->where('services.1.title', 'Zukünftiger Service')
->where('archived', false)
);
}
public function test_services_index_zeigt_vergangene_services_mit_archived_parameter(): void
{
Carbon::setTestNow('2026-03-01 10:00:00');
$this->withoutVite();
$user = User::factory()->create();
$pastService1 = Service::factory()->create([
'date' => Carbon::today()->subDays(5),
'title' => 'Vergangener Service 1',
]);
$pastService2 = Service::factory()->create([
'date' => Carbon::today()->subDays(2),
'title' => 'Vergangener Service 2',
]);
Service::factory()->create([
'date' => Carbon::today(),
'title' => 'Heutiger Service',
]);
Service::factory()->create([
'date' => Carbon::today()->addDays(3),
'title' => 'Zukünftiger Service',
]);
$response = $this->actingAs($user)->get(route('services.index', ['archived' => 1]));
$response->assertOk();
$response->assertInertia(
fn ($page) => $page
->component('Services/Index')
->has('services', 2)
->where('services.0.title', 'Vergangener Service 2')
->where('services.1.title', 'Vergangener Service 1')
->where('archived', true)
);
}
}