Tweaked Participants and Settings pages
This commit is contained in:
parent
3906b73c61
commit
219acb62d6
3 changed files with 75 additions and 20 deletions
|
|
@ -131,6 +131,7 @@ tr:hover td { background: rgba(255,255,255,0.02); }
|
||||||
}
|
}
|
||||||
.badge-checked { background: rgba(34,197,94,0.15); color: var(--c-success); }
|
.badge-checked { background: rgba(34,197,94,0.15); color: var(--c-success); }
|
||||||
.badge-unchecked { background: rgba(122,127,150,0.15); color: var(--c-muted); }
|
.badge-unchecked { background: rgba(122,127,150,0.15); color: var(--c-muted); }
|
||||||
|
.badge-partial { background: rgba(245,158,11,0.15); color: var(--c-warn); }
|
||||||
.badge-role { background: rgba(99,102,241,0.15); color: var(--c-accent-h); }
|
.badge-role { background: rgba(99,102,241,0.15); color: var(--c-accent-h); }
|
||||||
.badge-lead { background: rgba(245,158,11,0.15); color: var(--c-warn); }
|
.badge-lead { background: rgba(245,158,11,0.15); color: var(--c-warn); }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,15 @@
|
||||||
let newPronouns = $state('')
|
let newPronouns = $state('')
|
||||||
let newNote = $state('')
|
let newNote = $state('')
|
||||||
|
|
||||||
|
// Edit participant
|
||||||
|
let editId = $state(null)
|
||||||
|
let editName = $state('')
|
||||||
|
let editEmail = $state('')
|
||||||
|
let editPhone = $state('')
|
||||||
|
let editPronouns = $state('')
|
||||||
|
let editNote = $state('')
|
||||||
|
let saving = $state(false)
|
||||||
|
|
||||||
// Add ticket form (per participant)
|
// Add ticket form (per participant)
|
||||||
let addTicketFor = $state(null) // participant id
|
let addTicketFor = $state(null) // participant id
|
||||||
let addingTicket = $state(false)
|
let addingTicket = $state(false)
|
||||||
|
|
@ -57,14 +66,8 @@
|
||||||
return ticketsFor(participantId).filter(t => t.checked_in_at).length
|
return ticketsFor(participantId).filter(t => t.checked_in_at).length
|
||||||
}
|
}
|
||||||
|
|
||||||
async function toggleExpand(id) {
|
function toggleExpand(id) {
|
||||||
if (expandedId === id) {
|
expandedId = expandedId === id ? null : id
|
||||||
expandedId = null
|
|
||||||
expandedTickets = []
|
|
||||||
return
|
|
||||||
}
|
|
||||||
expandedId = id
|
|
||||||
expandedTickets = ticketsFor(id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function generateCodes() {
|
async function generateCodes() {
|
||||||
|
|
@ -148,6 +151,32 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function startEdit(p) {
|
||||||
|
editId = p.id
|
||||||
|
editName = p.preferred_name
|
||||||
|
editEmail = p.email
|
||||||
|
editPhone = p.phone
|
||||||
|
editPronouns = p.pronouns
|
||||||
|
editNote = p.note
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveEdit(e) {
|
||||||
|
e.preventDefault()
|
||||||
|
saving = true; error = ''
|
||||||
|
try {
|
||||||
|
const p = await api.participants.update(editId, {
|
||||||
|
preferred_name: editName, email: editEmail, phone: editPhone,
|
||||||
|
pronouns: editPronouns, note: editNote,
|
||||||
|
})
|
||||||
|
await db.participants.put(p)
|
||||||
|
editId = null
|
||||||
|
} catch (err) {
|
||||||
|
error = err.message
|
||||||
|
} finally {
|
||||||
|
saving = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function addTicket(e, participantId) {
|
async function addTicket(e, participantId) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
addingTicket = true; error = ''
|
addingTicket = true; error = ''
|
||||||
|
|
@ -283,6 +312,26 @@
|
||||||
{@const ci = checkedInCount(p.id)}
|
{@const ci = checkedInCount(p.id)}
|
||||||
{@const isExpanded = expandedId === p.id}
|
{@const isExpanded = expandedId === p.id}
|
||||||
{@const isMergeTarget = mergeMode && mergeSource?.id !== p.id}
|
{@const isMergeTarget = mergeMode && mergeSource?.id !== p.id}
|
||||||
|
{@const isEditing = editId === p.id}
|
||||||
|
{#if isEditing}
|
||||||
|
<tr class="edit-row">
|
||||||
|
<td colspan={canManage ? 5 : 4}>
|
||||||
|
<form class="participant-edit-form" onsubmit={saveEdit}>
|
||||||
|
<div class="edit-fields">
|
||||||
|
<input bind:value={editName} placeholder="Preferred name" />
|
||||||
|
<input type="email" bind:value={editEmail} placeholder="Email" />
|
||||||
|
<input bind:value={editPhone} placeholder="Phone" />
|
||||||
|
<input bind:value={editPronouns} placeholder="Pronouns" />
|
||||||
|
<input bind:value={editNote} placeholder="Note" style="flex:2" />
|
||||||
|
</div>
|
||||||
|
<div class="actions" style="margin-top:0.5rem">
|
||||||
|
<button type="submit" class="btn btn-primary btn-sm" disabled={saving}>{saving ? 'Saving…' : 'Save'}</button>
|
||||||
|
<button type="button" class="btn btn-ghost btn-sm" onclick={() => editId = null}>Cancel</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{:else}
|
||||||
<tr
|
<tr
|
||||||
class:merge-target={isMergeTarget}
|
class:merge-target={isMergeTarget}
|
||||||
onclick={mergeMode && mergeSource?.id !== p.id ? () => { mergeTarget = p } : null}
|
onclick={mergeMode && mergeSource?.id !== p.id ? () => { mergeTarget = p } : null}
|
||||||
|
|
@ -297,7 +346,12 @@
|
||||||
<div class="text-muted" style="font-size:0.78rem">{p.note}</div>
|
<div class="text-muted" style="font-size:0.78rem">{p.note}</div>
|
||||||
{/if}
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-muted">{p.email || '—'}</td>
|
<td class="text-muted">
|
||||||
|
{p.email || '—'}
|
||||||
|
{#if p.phone}
|
||||||
|
<div style="font-size:0.78rem">{p.phone}</div>
|
||||||
|
{/if}
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{#if pts.length > 0}
|
{#if pts.length > 0}
|
||||||
<button class="btn btn-ghost btn-sm" onclick={(e) => { e.stopPropagation(); toggleExpand(p.id) }}>
|
<button class="btn btn-ghost btn-sm" onclick={(e) => { e.stopPropagation(); toggleExpand(p.id) }}>
|
||||||
|
|
@ -320,13 +374,16 @@
|
||||||
{#if canManage}
|
{#if canManage}
|
||||||
<td>
|
<td>
|
||||||
{#if !mergeMode}
|
{#if !mergeMode}
|
||||||
|
<button class="btn btn-ghost btn-sm" onclick={(e) => { e.stopPropagation(); startEdit(p) }}
|
||||||
|
title="Edit participant">✎</button>
|
||||||
<button class="btn btn-ghost btn-sm" onclick={(e) => { e.stopPropagation(); startMerge(p) }}
|
<button class="btn btn-ghost btn-sm" onclick={(e) => { e.stopPropagation(); startMerge(p) }}
|
||||||
title="Merge this participant into another">⇄</button>
|
title="Merge this participant into another">⇄</button>
|
||||||
{/if}
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
{/if}
|
{/if}
|
||||||
</tr>
|
</tr>
|
||||||
{#if isExpanded}
|
{/if}
|
||||||
|
{#if isExpanded && !isEditing}
|
||||||
<tr class="ticket-rows">
|
<tr class="ticket-rows">
|
||||||
<td colspan="5">
|
<td colspan="5">
|
||||||
<div class="ticket-list">
|
<div class="ticket-list">
|
||||||
|
|
@ -417,12 +474,9 @@
|
||||||
font-size: 0.825rem;
|
font-size: 0.825rem;
|
||||||
padding: 0.3rem 0.5rem;
|
padding: 0.3rem 0.5rem;
|
||||||
}
|
}
|
||||||
.badge-partial {
|
.edit-row td { padding: 0.5rem 1rem; background: var(--c-bg); }
|
||||||
background: rgba(245,158,11,0.15);
|
.participant-edit-form { display: flex; flex-direction: column; gap: 0.25rem; }
|
||||||
color: var(--c-warn);
|
.edit-fields { display: flex; gap: 0.4rem; flex-wrap: wrap; }
|
||||||
padding: 0.15rem 0.5rem;
|
.edit-fields input { flex: 1; min-width: 120px; font-size: 0.825rem; padding: 0.3rem 0.5rem; width: auto; }
|
||||||
border-radius: 99px;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -232,8 +232,8 @@
|
||||||
Permanently delete all records of a given type. This cannot be undone.
|
Permanently delete all records of a given type. This cannot be undone.
|
||||||
</p>
|
</p>
|
||||||
<div style="display:flex;flex-wrap:wrap;gap:0.5rem">
|
<div style="display:flex;flex-wrap:wrap;gap:0.5rem">
|
||||||
<button class="btn btn-danger" onclick={() => resetModel('attendees', api.settings.resetAttendees)}>
|
<button class="btn btn-danger" onclick={() => resetModel('tickets', api.settings.resetTickets)}>
|
||||||
Delete All Attendees
|
Delete All Tickets
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-danger" onclick={() => resetModel('volunteers', api.settings.resetVolunteers)}>
|
<button class="btn btn-danger" onclick={() => resetModel('volunteers', api.settings.resetVolunteers)}>
|
||||||
Delete All Volunteers
|
Delete All Volunteers
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue