82 lines
2.8 KiB
Svelte
82 lines
2.8 KiB
Svelte
<script lang="ts">
|
||
import {push} from 'svelte-spa-router'
|
||
import {auth} from '../../lib/stores/auth.svelte'
|
||
import {apiGetAllHunts} from '../../lib/api/index'
|
||
import type {HuntResponse} from '../../lib/api/types'
|
||
import StatusBadge from '../../lib/components/StatusBadge.svelte'
|
||
import LoadingSpinner from '../../lib/components/LoadingSpinner.svelte'
|
||
|
||
$effect(() => {
|
||
if (!auth.isLoggedIn) push('/login')
|
||
if (!auth.isAdmin) push('/')
|
||
})
|
||
|
||
let hunts = $state<HuntResponse[]>([])
|
||
let loading = $state(true)
|
||
let error = $state('')
|
||
|
||
$effect(() => {
|
||
apiGetAllHunts()
|
||
.then(h => { hunts = h })
|
||
.catch(e => { error = e instanceof Error ? e.message : 'Failed to load' })
|
||
.finally(() => { loading = false })
|
||
})
|
||
|
||
function huntStatus(h: HuntResponse): 'ONGOING' | 'UNSTARTED' | 'CLOSED' {
|
||
if (h.isTerminated) return 'CLOSED'
|
||
const now = Date.now()
|
||
if (now < new Date(h.startDateTime).getTime()) return 'UNSTARTED'
|
||
if (now > new Date(h.endDateTime).getTime()) return 'CLOSED'
|
||
return 'ONGOING'
|
||
}
|
||
|
||
function formatDate(dt: string) {
|
||
return new Date(dt).toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' })
|
||
}
|
||
</script>
|
||
|
||
<div class="p-4 pb-6">
|
||
<div class="flex items-center justify-between mb-4">
|
||
<h1 class="text-xl font-bold">Hunts</h1>
|
||
<button class="btn btn-primary btn-sm" onclick={() => push('/admin/hunt/create')}>
|
||
+ New Hunt
|
||
</button>
|
||
</div>
|
||
|
||
{#if loading}
|
||
<LoadingSpinner />
|
||
{:else if error}
|
||
<div class="alert alert-error">{error}</div>
|
||
{:else if hunts.length === 0}
|
||
<div class="text-center py-16 text-base-content/50">
|
||
<p class="text-4xl mb-3">📋</p>
|
||
<p class="font-medium">No hunts yet</p>
|
||
<button class="btn btn-primary mt-4" onclick={() => push('/admin/hunt/create')}>Create First Hunt</button>
|
||
</div>
|
||
{:else}
|
||
<div class="flex flex-col gap-3">
|
||
{#each hunts as hunt}
|
||
<div class="card bg-base-100 shadow-sm border border-base-200">
|
||
<div class="card-body p-4 gap-2">
|
||
<div class="flex items-start justify-between gap-2">
|
||
<h2 class="font-semibold">{hunt.title}</h2>
|
||
<StatusBadge status={huntStatus(hunt)} />
|
||
</div>
|
||
<p class="text-xs text-base-content/50">
|
||
{formatDate(hunt.startDateTime)} – {formatDate(hunt.endDateTime)}
|
||
</p>
|
||
<div class="card-actions justify-end gap-2 mt-1">
|
||
<button class="btn btn-outline btn-xs" onclick={() => push(`/admin/hunt/${hunt.id}/review`)}>
|
||
Review Photos
|
||
</button>
|
||
<button class="btn btn-primary btn-xs" onclick={() => push(`/admin/hunt/${hunt.id}`)}>
|
||
Manage
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{/each}
|
||
</div>
|
||
{/if}
|
||
</div>
|