diff --git a/src/routes/admin/+page.svelte b/src/routes/admin/+page.svelte index 4720e73..ebfe9f8 100644 --- a/src/routes/admin/+page.svelte +++ b/src/routes/admin/+page.svelte @@ -5,8 +5,9 @@ let unitsData = $state({ units: [], groups: [] }); let selectedItem = $state<{ type: 'unit' | 'group'; id: string } | null>(null); let checkedUnitIds = $state>(new Set()); - let bulkAction = $state(''); // '' | 'move' + let bulkAction = $state(''); // '' | 'move' | 'delete' let bulkMoveTargetGroupId = $state(''); + let bulkDeleteConfirming = $state(false); let expandedGroups = $state>(new Set()); let deleteGroupTarget = $state(null); let deleteGroupAction = $state<'reassign' | 'orphan'>('reassign'); @@ -425,6 +426,22 @@ } } + // ── Bulk delete ────────────────────────────────────────────────────────────── + async function executeBulkDelete() { + if (checkedUnitIds.size === 0) return; + formError = null; + const ids = [...checkedUnitIds]; + const results = await Promise.all( + ids.map((unitId) => fetch(`/admin/api/units/${unitId}`, { method: 'DELETE' })) + ); + const failed = results.filter((r) => !r.ok); + if (failed.length) formError = `${failed.length} unit(s) failed to delete`; + checkedUnitIds = new Set(); + bulkAction = ''; + bulkDeleteConfirming = false; + await loadData(); + } + // ── Unit save/delete ───────────────────────────────────────────────────────── async function saveUnit() { formError = null; @@ -589,19 +606,23 @@ bind:value={bulkAction} disabled={checkedUnitIds.size === 0} aria-label="Bulk action" + onchange={() => { bulkDeleteConfirming = false; }} > + - + {#if bulkAction !== 'delete'} + + {/if} @@ -617,6 +638,22 @@ {/if} + + {#if bulkAction === 'delete'} + {#if !bulkDeleteConfirming} +
+ +
+ {:else} +
+ Delete {checkedUnitIds.size} unit{checkedUnitIds.size === 1 ? '' : 's'}? + + +
+ {/if} + {/if} @@ -1034,6 +1071,34 @@ white-space: nowrap; min-width: 4.5rem; } + .bulk-confirm-row { + flex-wrap: wrap; + gap: 0.4rem; + } + .bulk-confirm-label { + font-size: 0.8rem; + color: var(--pico-color); + white-space: nowrap; + } + .bulk-go--danger { + --pico-background-color: var(--pico-del-color, #e74c3c); + --pico-border-color: var(--pico-del-color, #e74c3c); + background: var(--pico-del-color, #e74c3c); + border-color: var(--pico-del-color, #e74c3c); + } + .bulk-cancel { + font-size: 0.8rem; + padding: 0.15rem 0.4rem; + color: var(--pico-muted-color); + } + .bulk-delete-confirm-btn { + background: transparent; + border: 1px solid var(--pico-del-color, #e74c3c); + color: var(--pico-del-color, #e74c3c); + --pico-background-color: transparent; + --pico-border-color: var(--pico-del-color, #e74c3c); + --pico-color: var(--pico-del-color, #e74c3c); + } /* Tree groups */ .tree-group { @@ -1191,8 +1256,7 @@ .reassign-options { margin: 0.5rem 0 0.5rem 1.5rem; display: flex; flex-direction: column; gap: 0.5rem; } .radio-stack { display: flex; flex-direction: column; gap: 0.25rem; } - /* ── Drag-to-reorder ───────────────────────────────────────────────────────── */ - .tree-group-header { + /* ── Drag-to-reorder ───────────────────────────────────────────────────────── */ .tree-group-header { cursor: grab; } .tree-group-header.dragging {