Makes the photo review process easier

This commit is contained in:
2026-05-18 12:05:31 -05:00
parent 2c614a2d46
commit 6191c6c804

View File

@@ -14,12 +14,13 @@
if (!auth.isAdmin) push('/')
})
type PhotoWithTeam = PhotoResponse & { teamName: string }
let hunt = $state<HuntResponse | null>(null)
let teams = $state<TeamResponse[]>([])
let items = $state<ItemResponse[]>([])
let selectedTeam = $state<TeamResponse | null>(null)
let selectedItem = $state<ItemResponse | null>(null)
let photos = $state<PhotoResponse[]>([])
let photos = $state<PhotoWithTeam[]>([])
let loading = $state(true)
let photosLoading = $state(false)
let error = $state('')
@@ -32,29 +33,28 @@
hunt = h
teams = t
items = i
// Pre-select team from query string if provided
const qs = new URLSearchParams(router.querystring ?? '')
const preTeam = qs.get('teamId')
if (preTeam) {
selectedTeam = t.find(x => x.id === preTeam) ?? null
}
})
.catch(e => { error = e instanceof Error ? e.message : 'Failed to load' })
.finally(() => { loading = false })
})
$effect(() => {
if (selectedTeam && selectedItem) {
if (selectedItem) {
photosLoading = true
photos = []
apiGetItemPhotos(params.huntId, selectedTeam.id, selectedItem.id)
.then(p => { photos = p })
Promise.all(
teams.map(team =>
apiGetItemPhotos(params.huntId, team.id, selectedItem!.id)
.then(ps => ps.map(p => ({ ...p, teamName: team.name })))
)
)
.then(results => { photos = results.flat() })
.catch(e => { error = e instanceof Error ? e.message : 'Failed to load photos' })
.finally(() => { photosLoading = false })
}
})
async function review(photo: PhotoResponse, status: 'APPROVED' | 'REJECTED') {
async function review(photo: PhotoWithTeam, status: 'APPROVED' | 'REJECTED') {
reviewing = photo.id
try {
await apiReviewPhoto(photo.id, status)
@@ -82,43 +82,24 @@
{#if loading}
<LoadingSpinner />
{:else}
<!-- Team selector -->
<!-- Item selector -->
<div class="mb-4">
<p class="text-sm font-medium text-base-content/60 mb-2">Select Team</p>
<p class="text-sm font-medium text-base-content/60 mb-2">Select Item</p>
<div class="flex flex-wrap gap-2">
{#each teams as team}
{#each items as item}
<button
class="btn btn-sm"
class:btn-primary={selectedTeam?.id === team.id}
class:btn-outline={selectedTeam?.id !== team.id}
onclick={() => { selectedTeam = team; selectedItem = null; photos = [] }}
class:btn-secondary={selectedItem?.id === item.id}
class:btn-outline={selectedItem?.id !== item.id}
onclick={() => { selectedItem = item }}
>
{team.name}
{item.name}
</button>
{/each}
</div>
</div>
{#if selectedTeam}
<!-- Item selector -->
<div class="mb-4">
<p class="text-sm font-medium text-base-content/60 mb-2">Select Item</p>
<div class="flex flex-wrap gap-2">
{#each items as item}
<button
class="btn btn-sm"
class:btn-secondary={selectedItem?.id === item.id}
class:btn-outline={selectedItem?.id !== item.id}
onclick={() => { selectedItem = item }}
>
{item.name}
</button>
{/each}
</div>
</div>
{/if}
{#if selectedTeam && selectedItem}
{#if selectedItem}
{#if photosLoading}
<LoadingSpinner />
{:else if activePhotos.length === 0}
@@ -133,7 +114,7 @@
<div class="p-3">
<div class="flex items-center justify-between mb-3">
<div>
<p class="font-semibold">{photo.hunterName}</p>
<p class="font-semibold">{photo.hunterName} <span class="text-base-content/50 font-normal">· {photo.teamName}</span></p>
<p class="text-xs text-base-content/50">
{new Date(photo.photoUploadDateTime).toLocaleString()}
</p>