Restructure route organization
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import type { PageProps } from './$types';
|
||||
|
||||
let { data }: PageProps = $props();
|
||||
|
||||
function displayCategoryName(category: { name: string; displayName?: string }) {
|
||||
return category.displayName ?? category.name;
|
||||
}
|
||||
|
||||
let search = $state('');
|
||||
let categoryId: number | null = $state(null);
|
||||
let initialized = $state(false);
|
||||
|
||||
$effect(() => {
|
||||
if (!initialized) {
|
||||
search = data.search;
|
||||
categoryId = data.categoryId;
|
||||
initialized = true;
|
||||
}
|
||||
});
|
||||
|
||||
function applyFilters() {
|
||||
const params = new URLSearchParams();
|
||||
if (search) params.set('search', search);
|
||||
if (categoryId) params.set('category', String(categoryId));
|
||||
goto(`/proclamations?${params.toString()}`, { replaceState: true });
|
||||
}
|
||||
|
||||
function clearFilters() {
|
||||
search = '';
|
||||
categoryId = null;
|
||||
goto('/proclamations', { replaceState: true });
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex h-full min-h-0 flex-col gap-6 p-6">
|
||||
<h1 class="text-2xl font-semibold text-gray-900">Proclamations</h1>
|
||||
|
||||
<!-- Filters -->
|
||||
<div class="flex flex-wrap items-end gap-4">
|
||||
<div>
|
||||
<label for="search" class="block text-sm font-medium text-gray-700">Search</label>
|
||||
<input
|
||||
type="text"
|
||||
id="search"
|
||||
bind:value={search}
|
||||
placeholder="Search by name..."
|
||||
class="mt-1 rounded-lg border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500"
|
||||
onkeydown={(e) => e.key === 'Enter' && applyFilters()}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="category" class="block text-sm font-medium text-gray-700">Category</label>
|
||||
<select
|
||||
id="category"
|
||||
bind:value={categoryId}
|
||||
onchange={applyFilters}
|
||||
class="mt-1 rounded-lg border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500"
|
||||
>
|
||||
<option value={null}>All Categories</option>
|
||||
{#each data.categories as cat}
|
||||
<option value={cat.id}>{displayCategoryName(cat)}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="rounded-lg bg-blue-600 px-4 py-2 font-medium text-white transition-colors hover:bg-blue-700"
|
||||
onclick={applyFilters}
|
||||
>
|
||||
Search
|
||||
</button>
|
||||
{#if data.search || data.categoryId}
|
||||
<button
|
||||
type="button"
|
||||
class="rounded-lg border border-gray-300 px-4 py-2 text-sm font-medium text-gray-700 transition-colors hover:bg-gray-50"
|
||||
onclick={clearFilters}
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Results -->
|
||||
{#if data.proclamations.length > 0}
|
||||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{#each data.proclamations as proclamation}
|
||||
<a
|
||||
href="/proclamations/{proclamation.id}"
|
||||
class="flex flex-col rounded-lg border border-gray-200 bg-white p-5 shadow-sm transition-shadow hover:shadow-md"
|
||||
>
|
||||
<h2 class="text-lg font-medium text-gray-900">{proclamation.name}</h2>
|
||||
<p class="mt-1 text-sm text-gray-500">{displayCategoryName(proclamation.category)}</p>
|
||||
<div class="mt-auto flex items-center justify-between pt-4">
|
||||
<span class="text-sm text-purple-600">
|
||||
{proclamation.solfeggioFrequency.value} Hz —
|
||||
{proclamation.solfeggioFrequency.name}
|
||||
</span>
|
||||
<span class="font-semibold text-gray-900">
|
||||
${proclamation.price.toFixed(2)}
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex flex-1 items-center justify-center">
|
||||
<p class="text-lg text-gray-500">No proclamations found.</p>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
Reference in New Issue
Block a user