Updated Participant search and delete. Filtered import.

This commit is contained in:
Pen Anderson 2026-03-04 14:50:05 -06:00
parent 219acb62d6
commit 64ce97c74d
3 changed files with 30 additions and 9 deletions

View file

@ -53,7 +53,7 @@
<strong style="color:var(--c-text)">Supported formats:</strong><br>
<strong>CrowdWork / ticketing platform:</strong> columns <code>Patron Name</code>, <code>Patron Email</code>, <code>Tier Name</code>, <code>Order Number</code><br>
<strong>Generic:</strong> columns <code>name</code>, <code>email</code>, <code>ticket_id</code>, <code>ticket_type</code>, <code>note</code><br>
Duplicate names are skipped.
Duplicate tickets (same source + external ID) are skipped. Participants are matched or created by email.
</div>
<button type="submit" class="btn btn-primary" disabled={!file || importing}>

View file

@ -53,7 +53,8 @@
.filter(p => {
if (!s) return true
return p.preferred_name?.toLowerCase().includes(s) ||
p.email?.toLowerCase().includes(s)
p.email?.toLowerCase().includes(s) ||
p.phone?.toLowerCase().includes(s)
})
.sort((a, b) => (a.preferred_name || a.email).localeCompare(b.preferred_name || b.email))
})
@ -177,6 +178,18 @@
}
}
async function deleteParticipant(id) {
if (!confirm('Permanently delete this participant and all their records?')) return
error = ''
try {
await api.participants.delete(id)
await db.participants.delete(id)
editId = null
} catch (err) {
error = err.message
}
}
async function addTicket(e, participantId) {
e.preventDefault()
addingTicket = true; error = ''
@ -327,6 +340,8 @@
<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>
<span class="spacer"></span>
<button type="button" class="btn btn-danger btn-sm" onclick={() => deleteParticipant(editId)}>Delete</button>
</div>
</form>
</td>

View file

@ -14,7 +14,6 @@
let adding = $state(false)
let newName = $state('')
let newEmail = $state('')
let newPhone = $state('')
let newDeptID = $state('')
let newIsLead = $state(false)
let newNote = $state('')
@ -25,6 +24,7 @@
const allVolunteers = liveQuery(() =>
db.volunteers.filter(v => !v.deleted_at).toArray()
)
const allParticipants = liveQuery(() => db.participants.toArray())
const allDepts = liveQuery(() =>
db.departments.filter(d => !d.deleted_at).toArray()
.then(arr => arr.sort((a, b) => a.name.localeCompare(b.name)))
@ -62,7 +62,6 @@
const data = {
name: newName,
email: newEmail,
phone: newPhone,
is_lead: newIsLead,
note: newNote,
}
@ -70,7 +69,7 @@
const v = await api.volunteers.create(data)
await db.volunteers.put(v)
showAdd = false
newName = newEmail = newPhone = newNote = ''
newName = newEmail = newNote = ''
newDeptID = ''
newIsLead = false
} catch (err) {
@ -93,6 +92,10 @@
function deptFor(id) {
return ($allDepts ?? []).find(d => d.id === id)
}
function participantFor(id) {
return ($allParticipants ?? []).find(p => p.id === id) ?? null
}
</script>
<div class="page">
@ -121,10 +124,6 @@
<label for="v-email">Email</label>
<input id="v-email" type="email" bind:value={newEmail} placeholder="email@example.com" />
</div>
<div class="form-group">
<label for="v-phone">Phone</label>
<input id="v-phone" bind:value={newPhone} placeholder="Optional" />
</div>
<div class="form-group">
<label for="v-dept">Department</label>
<select id="v-dept" bind:value={newDeptID}>
@ -195,12 +194,19 @@
<tbody>
{#each filtered as v (v.id)}
{@const dept = deptFor(v.department_id)}
{@const participant = participantFor(v.participant_id)}
<tr>
<td>
<strong>{v.name}</strong>
{#if v.is_lead}
<span class="badge badge-lead" style="margin-left:0.4rem">Lead</span>
{/if}
{#if !v.participant_id}
<span class="badge badge-unchecked" style="margin-left:0.4rem" title="Not linked to a participant — no ticket record">No ticket</span>
{/if}
{#if v.email}
<div class="text-muted" style="font-size:0.78rem">{v.email}</div>
{/if}
{#if v.note}
<div class="text-muted" style="font-size:0.78rem">{v.note}</div>
{/if}