First crack at the app. Still lots of bugs to squash.

This commit is contained in:
2026-05-17 23:30:08 -05:00
parent cfd936e5fa
commit 435481549c
35 changed files with 4029 additions and 0 deletions

View File

@@ -0,0 +1,81 @@
<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>