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:
parent
d5abff0d82
commit
8dc26b8ae3
|
|
@ -8,6 +8,10 @@ const props = defineProps({
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
|
archived: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const toastMessage = ref('')
|
const toastMessage = ref('')
|
||||||
|
|
@ -16,6 +20,7 @@ const confirmDialog = ref(false)
|
||||||
const confirmWarnings = ref([])
|
const confirmWarnings = ref([])
|
||||||
const confirmServiceId = ref(null)
|
const confirmServiceId = ref(null)
|
||||||
const finalizing = ref(false)
|
const finalizing = ref(false)
|
||||||
|
const showArchived = ref(props.archived)
|
||||||
|
|
||||||
function formatDate(dateStr) {
|
function formatDate(dateStr) {
|
||||||
if (!dateStr) return '—'
|
if (!dateStr) return '—'
|
||||||
|
|
@ -172,9 +177,39 @@ function stateIconClass(isDone) {
|
||||||
|
|
||||||
<AuthenticatedLayout>
|
<AuthenticatedLayout>
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
<div class="flex flex-wrap items-center justify-between gap-4">
|
||||||
<h2 class="text-xl font-semibold leading-tight text-gray-800">Services</h2>
|
<div>
|
||||||
<p class="text-sm text-gray-500">Hier siehst du alle heutigen und kommenden Services.</p>
|
<h2 class="text-xl font-semibold leading-tight text-gray-800">Services</h2>
|
||||||
|
<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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -199,7 +234,7 @@ function stateIconClass(isDone) {
|
||||||
|
|
||||||
<div class="overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm">
|
<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">
|
<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>
|
||||||
|
|
||||||
<div v-else class="overflow-x-auto">
|
<div v-else class="overflow-x-auto">
|
||||||
|
|
|
||||||
|
|
@ -270,4 +270,79 @@ public function test_service_edit_erfordert_authentifizierung(): void
|
||||||
|
|
||||||
$response->assertRedirect(route('login'));
|
$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)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue