Replace teal-brand→primary, teal-dark→primary-dark, teal-light→primary-light, gray-soft→gray-200, gray-brand→gray-900 across 53 files. Fixes theming not applying — colors now use CSS custom properties from org_config. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
90 lines
3.4 KiB
Svelte
90 lines
3.4 KiB
Svelte
<script lang="ts">
|
|
import { goto } from '$app/navigation';
|
|
import { page } from '$app/state';
|
|
import StatusBadge from '$lib/components/ui/StatusBadge.svelte';
|
|
import Pagination from '$lib/components/ui/Pagination.svelte';
|
|
|
|
let { data } = $props();
|
|
|
|
const statusOptions = [
|
|
{ value: '', label: 'All Statuses' },
|
|
{ value: 'pending', label: 'Pending' },
|
|
{ value: 'reviewing', label: 'Reviewing' },
|
|
{ value: 'approved', label: 'Approved' },
|
|
{ value: 'denied', label: 'Denied' }
|
|
];
|
|
|
|
function handleFilterChange(value: string) {
|
|
const url = new URL(page.url);
|
|
if (value) { url.searchParams.set('status', value); } else { url.searchParams.delete('status'); }
|
|
url.searchParams.delete('page');
|
|
goto(url.pathname + url.search);
|
|
}
|
|
|
|
function buildBaseUrl(): string {
|
|
const url = new URL(page.url);
|
|
return url.pathname + '?' + url.searchParams.toString();
|
|
}
|
|
|
|
function formatDate(d: string | Date): string {
|
|
return new Date(d).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
|
|
}
|
|
</script>
|
|
|
|
<svelte:head><title>Volunteer Applications — Admin</title></svelte:head>
|
|
|
|
<div>
|
|
<div class="flex items-center justify-between mb-6">
|
|
<h1 class="text-2xl font-bold text-gray-900">Volunteer Applications</h1>
|
|
<span class="text-sm text-gray-500">{data.total} total</span>
|
|
</div>
|
|
|
|
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 mb-6">
|
|
<select
|
|
value={data.status}
|
|
onchange={(e) => handleFilterChange(e.currentTarget.value)}
|
|
class="px-3 py-2.5 rounded-lg border border-gray-200 bg-white text-sm focus:outline-none focus:ring-2 focus:ring-primary"
|
|
>
|
|
{#each statusOptions as opt}
|
|
<option value={opt.value}>{opt.label}</option>
|
|
{/each}
|
|
</select>
|
|
</div>
|
|
|
|
{#if data.volunteers.length === 0}
|
|
<div class="text-center py-12 text-gray-400">
|
|
<p class="text-sm font-medium">No volunteer applications found.</p>
|
|
</div>
|
|
{:else}
|
|
<div class="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
|
|
<table class="w-full text-sm text-left">
|
|
<thead>
|
|
<tr class="border-b border-gray-200 bg-gray-50">
|
|
<th class="px-6 py-3 font-semibold text-gray-600">Name</th>
|
|
<th class="px-6 py-3 font-semibold text-gray-600">Email</th>
|
|
<th class="px-6 py-3 font-semibold text-gray-600">Phone</th>
|
|
<th class="px-6 py-3 font-semibold text-gray-600">Status</th>
|
|
<th class="px-6 py-3 font-semibold text-gray-600">Date</th>
|
|
<th class="px-6 py-3 font-semibold text-gray-600 text-right">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{#each data.volunteers as vol}
|
|
<tr class="border-b border-gray-200 last:border-0 hover:bg-gray-50 transition-colors">
|
|
<td class="px-6 py-4 font-medium text-gray-900">{vol.applicantName}</td>
|
|
<td class="px-6 py-4 text-gray-600"><a href="mailto:{vol.email}" class="hover:text-primary">{vol.email}</a></td>
|
|
<td class="px-6 py-4 text-gray-600">{vol.phone ?? '—'}</td>
|
|
<td class="px-6 py-4"><StatusBadge status={vol.status} /></td>
|
|
<td class="px-6 py-4 text-gray-500 text-xs">{formatDate(vol.createdAt)}</td>
|
|
<td class="px-6 py-4 text-right">
|
|
<a href="/admin/volunteers/{vol.id}" class="text-sm font-medium text-primary hover:text-primary-dark">View</a>
|
|
</td>
|
|
</tr>
|
|
{/each}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<Pagination page={data.page} totalPages={data.totalPages} baseUrl={buildBaseUrl()} />
|
|
{/if}
|
|
</div>
|