feat(02-03): add CompletedToggle to show/hide completed tasks

- Create CompletedToggle component with checkbox UI
- Add showCompleted URL param to +page.server.ts load function
- Update +page.svelte to sync preferences with URL
- Toggle state persists in localStorage via preferences store
- Clicking toggle updates URL and invalidates data for server filter
This commit is contained in:
Thomas Richter
2026-01-29 11:12:51 +01:00
parent 7d66a8f6fc
commit fc7c1f6c98
3 changed files with 73 additions and 4 deletions

View File

@@ -0,0 +1,42 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { page } from '$app/state';
import { preferences } from '$lib/stores/preferences.svelte';
import { get } from 'svelte/store';
let showCompleted = $state(false);
// Initialize and sync with URL on mount
$effect(() => {
if (typeof window !== 'undefined') {
showCompleted = get(preferences).showCompleted;
}
});
function handleChange(e: Event) {
const target = e.target as HTMLInputElement;
showCompleted = target.checked;
// Update preference store
preferences.update((p) => ({ ...p, showCompleted }));
// Update URL to refresh data from server
const url = new URL(page.url);
if (showCompleted) {
url.searchParams.set('showCompleted', 'true');
} else {
url.searchParams.delete('showCompleted');
}
goto(url.toString(), { invalidateAll: true });
}
</script>
<label class="flex items-center gap-2 text-sm text-gray-600 cursor-pointer select-none">
<input
type="checkbox"
checked={showCompleted}
onchange={handleChange}
class="w-4 h-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500"
/>
Show completed
</label>

View File

@@ -2,11 +2,13 @@ import type { PageServerLoad, Actions } from './$types';
import { fail } from '@sveltejs/kit';
import { entryRepository } from '$lib/server/db/repository';
export const load: PageServerLoad = async () => {
const entries = entryRepository.getOrdered({ showCompleted: false });
export const load: PageServerLoad = async ({ url }) => {
const showCompleted = url.searchParams.get('showCompleted') === 'true';
const entries = entryRepository.getOrdered({ showCompleted });
return {
entries
entries,
showCompleted
};
};

View File

@@ -1,8 +1,32 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { page } from '$app/state';
import EntryList from '$lib/components/EntryList.svelte';
import QuickCapture from '$lib/components/QuickCapture.svelte';
import CompletedToggle from '$lib/components/CompletedToggle.svelte';
import { preferences } from '$lib/stores/preferences.svelte';
import { get } from 'svelte/store';
let { data } = $props();
// Sync URL with preference on mount (client-side only)
$effect(() => {
if (typeof window !== 'undefined') {
const urlShowCompleted = page.url.searchParams.get('showCompleted') === 'true';
const prefShowCompleted = get(preferences).showCompleted;
// Only sync if different and not already matching
if (urlShowCompleted !== prefShowCompleted) {
const url = new URL(page.url);
if (prefShowCompleted) {
url.searchParams.set('showCompleted', 'true');
} else {
url.searchParams.delete('showCompleted');
}
goto(url.toString(), { replaceState: true, invalidateAll: true });
}
}
});
</script>
<svelte:head>
@@ -11,8 +35,9 @@
<main class="min-h-screen bg-gray-50 pb-40">
<header class="bg-white border-b border-gray-200 sticky top-0 z-10">
<div class="max-w-2xl mx-auto px-4 py-4">
<div class="max-w-2xl mx-auto px-4 py-4 flex items-center justify-between">
<h1 class="text-xl font-bold text-gray-900">TaskPlaner</h1>
<CompletedToggle />
</div>
</header>