Allows for editing Hunt details
This commit is contained in:
@@ -35,6 +35,9 @@ export const apiGetAllHunts = (status?: 'UNSTARTED' | 'ONGOING' | 'CLOSED') =>
|
||||
export const apiCreateHunt = (title: string, startDateTime: string, endDateTime: string) =>
|
||||
client.post<HuntResponse>('/hunt', { title, startDateTime, endDateTime })
|
||||
|
||||
export const apiUpdateHunt = (huntId: string, title: string, startDateTime: string, endDateTime: string, isTerminated: boolean) =>
|
||||
client.patch<HuntResponse>(`/hunt/${huntId}`, { title, startDateTime, endDateTime, isTerminated })
|
||||
|
||||
// ── Hunter ────────────────────────────────────────────────────────────────────
|
||||
|
||||
export const apiGetOngoingHunts = () =>
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
let {
|
||||
value = $bindable(''),
|
||||
defaultTimeIndex = 36,
|
||||
initialValue = '',
|
||||
}: {
|
||||
value?: string
|
||||
defaultTimeIndex?: number
|
||||
initialValue?: string
|
||||
} = $props()
|
||||
|
||||
const months = [
|
||||
@@ -25,11 +27,19 @@
|
||||
return { value: i, label: `${displayHour}:${minute.toString().padStart(2, '0')} ${ampm}` }
|
||||
})
|
||||
|
||||
const today = new Date()
|
||||
let month = $state(today.getMonth())
|
||||
let day = $state(today.getDate())
|
||||
let year = $state(today.getFullYear())
|
||||
let timeIndex = $state(untrack(() => defaultTimeIndex))
|
||||
const seed = untrack(() => {
|
||||
if (initialValue) {
|
||||
const d = new Date(initialValue)
|
||||
return { month: d.getMonth(), day: d.getDate(), year: d.getFullYear(), timeIndex: d.getHours() * 4 + Math.round(d.getMinutes() / 15) }
|
||||
}
|
||||
const today = new Date()
|
||||
return { month: today.getMonth(), day: today.getDate(), year: today.getFullYear(), timeIndex: defaultTimeIndex }
|
||||
})
|
||||
|
||||
let month = $state(seed.month)
|
||||
let day = $state(seed.day)
|
||||
let year = $state(seed.year)
|
||||
let timeIndex = $state(seed.timeIndex)
|
||||
|
||||
const daysInMonth = $derived(new Date(year, month + 1, 0).getDate())
|
||||
|
||||
|
||||
@@ -8,11 +8,13 @@
|
||||
apiGetHunt,
|
||||
apiGetItems,
|
||||
apiListTeams,
|
||||
apiUpdateHunt,
|
||||
apiUpdateItem
|
||||
} from '../../lib/api/index'
|
||||
import type {HuntResponse, ItemResponse, TeamResponse} from '../../lib/api/types'
|
||||
import StatusBadge from '../../lib/components/StatusBadge.svelte'
|
||||
import LoadingSpinner from '../../lib/components/LoadingSpinner.svelte'
|
||||
import DateTimePicker from '../../lib/components/DateTimePicker.svelte'
|
||||
|
||||
let { params }: { params: { huntId: string } } = $props()
|
||||
|
||||
@@ -34,6 +36,40 @@
|
||||
let newTeamName = $state('')
|
||||
let addingTeam = $state(false)
|
||||
|
||||
let editingHunt = $state(false)
|
||||
let editTitle = $state('')
|
||||
let editStart = $state('')
|
||||
let editEnd = $state('')
|
||||
let editTerminated = $state(false)
|
||||
let savingHunt = $state(false)
|
||||
|
||||
function startEditHunt() {
|
||||
if (!hunt) return
|
||||
editTitle = hunt.title
|
||||
editStart = hunt.startDateTime
|
||||
editEnd = hunt.endDateTime
|
||||
editTerminated = hunt.isTerminated
|
||||
editingHunt = true
|
||||
}
|
||||
|
||||
function cancelEditHunt() {
|
||||
editingHunt = false
|
||||
}
|
||||
|
||||
async function saveHunt() {
|
||||
if (!hunt || !editTitle.trim() || !editStart || !editEnd) return
|
||||
savingHunt = true
|
||||
error = ''
|
||||
try {
|
||||
hunt = await apiUpdateHunt(params.huntId, editTitle.trim(), editStart, editEnd, editTerminated)
|
||||
editingHunt = false
|
||||
} catch (e: unknown) {
|
||||
error = e instanceof Error ? e.message : 'Failed to update hunt'
|
||||
} finally {
|
||||
savingHunt = false
|
||||
}
|
||||
}
|
||||
|
||||
let editingItemId = $state<string | null>(null)
|
||||
let editName = $state('')
|
||||
let editPoints = $state(0)
|
||||
@@ -127,18 +163,67 @@
|
||||
<div class="p-4 pb-6">
|
||||
<div class="flex items-center gap-3 mb-4">
|
||||
<button class="btn btn-ghost btn-sm btn-circle" onclick={() => push('/admin')}>←</button>
|
||||
<div>
|
||||
<div class="flex-1 min-w-0">
|
||||
{#if hunt}
|
||||
<h1 class="text-lg font-bold leading-tight">{hunt.title}</h1>
|
||||
<h1 class="text-lg font-bold leading-tight truncate">{hunt.title}</h1>
|
||||
<StatusBadge status={huntStatus(hunt)} />
|
||||
{/if}
|
||||
</div>
|
||||
{#if hunt && !editingHunt}
|
||||
<button class="btn btn-outline btn-sm shrink-0" onclick={startEditHunt}>Edit Hunt</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if editingHunt && hunt}
|
||||
<div class="bg-base-200 rounded-2xl p-4 mb-4 flex flex-col gap-4">
|
||||
<h2 class="font-bold text-sm uppercase tracking-wide text-base-content/50">Edit Hunt</h2>
|
||||
{#if error}
|
||||
<div class="alert alert-error text-sm">{error}</div>
|
||||
{/if}
|
||||
<label class="form-control">
|
||||
<span class="label-text text-xs font-medium mb-1">Title</span>
|
||||
<input type="text" class="input input-bordered input-sm" bind:value={editTitle} disabled={savingHunt} required />
|
||||
</label>
|
||||
<fieldset class="form-control">
|
||||
<legend class="label-text text-xs font-medium mb-1">Start</legend>
|
||||
<DateTimePicker bind:value={editStart} initialValue={editStart} />
|
||||
</fieldset>
|
||||
<fieldset class="form-control">
|
||||
<legend class="label-text text-xs font-medium mb-1">End</legend>
|
||||
<DateTimePicker bind:value={editEnd} initialValue={editEnd} />
|
||||
</fieldset>
|
||||
<button
|
||||
type="button"
|
||||
class="flex items-center justify-between p-4 rounded-xl border-2 transition-all {editTerminated ? 'border-error bg-error/10' : 'border-base-300 bg-base-100'}"
|
||||
onclick={() => editTerminated = !editTerminated}
|
||||
disabled={savingHunt}
|
||||
>
|
||||
<div class="text-left">
|
||||
<p class="font-semibold {editTerminated ? 'text-error' : 'text-base-content'}">Terminated</p>
|
||||
<p class="text-xs text-base-content/50">Permanently closes the hunt for all hunters</p>
|
||||
</div>
|
||||
<div class="w-6 h-6 rounded-full border-2 flex items-center justify-center shrink-0 {editTerminated ? 'border-error bg-error' : 'border-base-300'}">
|
||||
{#if editTerminated}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-error-content" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 00-1.414 0L8 12.586 4.707 9.293a1 1 0 00-1.414 1.414l4 4a1 1 0 001.414 0l8-8a1 1 0 000-1.414z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
{/if}
|
||||
</div>
|
||||
</button>
|
||||
<div class="flex gap-2">
|
||||
<button class="btn btn-primary btn-sm flex-1" onclick={saveHunt} disabled={savingHunt}>
|
||||
{#if savingHunt}<span class="loading loading-spinner loading-xs"></span>{/if}
|
||||
Save
|
||||
</button>
|
||||
<button class="btn btn-ghost btn-sm" onclick={cancelEditHunt} disabled={savingHunt}>Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if loading}
|
||||
<LoadingSpinner />
|
||||
{:else}
|
||||
{#if error}
|
||||
{#if error && !editingHunt}
|
||||
<div class="alert alert-error mb-4 text-sm">{error}</div>
|
||||
{/if}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user