fix(conflicts): the status filter, select-all, and bulk actions actually work

They never did: filterConflicts/toggleAllConflicts/updateBulkActions
were referenced in the templ but defined nowhere, and the bulk buttons
called bulkResolve()/bulkDismiss() with no arguments against functions
expecting (strategy, conflictIds).

- conflict cards carry data-status; the status dropdown filters them
  client-side (pure client state — allowed to be JS), applied on page
  init and re-applied after every partial refresh, with a
  'no conflicts match this filter' note when everything is filtered out
- select-all toggles only the visible cards; the bulk bar shows the
  selected count and resolve/dismiss now gather the checked ids and the
  chosen strategy (the bar's own buttons use resolveSelected()/
  dismissSelected() wrappers)
- dead onchange= globals became Alpine @change handlers on the server-
  rendered controls; partial swaps re-bind via Alpine.initTree (already
  in refreshConflicts) and re-apply filter + bulk state

Visible change: the dropdown's stated default (Unresolved Only) now
actually applies on load, so resolved conflicts are hidden until the
filter is changed.
This commit is contained in:
John O'Keefe committed 2026-10-18 12:42:50 -04:00
1 parent cb4e892b10
commit 4a68fe7cab
3 files changed
+195 -89

No files matched your search

+9 -6
View File
@@ -8,7 +8,7 @@ import "bookhoard/internal/handlers"
templ ConflictsListContent(user User, conflicts []handlers.ConflictDetailResponse) {
<div id="select-all-container" class="mb-4 hidden">
<label class="flex items-center space-x-2 cursor-pointer">
<input type="checkbox" id="select-all-conflicts" class="w-5 h-5 rounded" onchange="toggleAllConflicts()"/>
<input type="checkbox" id="select-all-conflicts" class="w-5 h-5 rounded" @change="toggleAllConflicts()"/>
<span class="text-sm font-semibold" style="color: var(--text-primary);">Select All</span>
</label>
</div>
@@ -22,14 +22,14 @@ templ ConflictsListContent(user User, conflicts []handlers.ConflictDetailRespons
</div>
}
for _, conflict := range conflicts {
<div class="conflict-item card p-6" data-conflict-id={ conflict.ID } style="border-left-width: 4px; border-left-color: var(--status-warning);">
<div class="conflict-item card p-6" data-conflict-id={ conflict.ID } data-status={ conflict.ResolutionStatus } style="border-left-width: 4px; border-left-color: var(--status-warning);">
<div class="flex justify-between items-start mb-4 gap-4">
<div class="flex items-start space-x-4">
<input
type="checkbox"
class="conflict-checkbox w-5 h-5 rounded mt-1"
data-conflict-id={ conflict.ID }
onchange="updateBulkActions()"
@change="updateBulkActions()"
/>
<div class="flex items-start gap-3">
<span style="color: var(--status-warning);">
@@ -51,6 +51,9 @@ templ ConflictsListContent(user User, conflicts []handlers.ConflictDetailRespons
</div>
</div>
}
<p id="filter-empty" class="hidden text-center p-4" style="color: var(--text-secondary)">
No conflicts match this filter.
</p>
}
// ConflictStatsBar renders the total/unresolved/resolved stat cards.
@@ -110,7 +113,7 @@ templ Conflicts(user User, conflicts []handlers.ConflictDetailResponse, total in
<div class="flex space-x-3">
<select
id="status-filter"
onchange="filterConflicts()"
@change="filterConflicts()"
class="input"
>
<option value="unresolved">Unresolved Only</option>
@@ -138,11 +141,11 @@ templ Conflicts(user User, conflicts []handlers.ConflictDetailResponse, total in
</select>
</div>
<div class="flex space-x-2">
<button @click="bulkResolve()" class="btn btn-primary text-sm">
<button @click="resolveSelected()" class="btn btn-primary text-sm">
@Icon("check", "h-4 w-4")
Resolve Selected
</button>
<button @click="bulkDismiss()" class="btn btn-danger text-sm">
<button @click="dismissSelected()" class="btn btn-danger text-sm">
Dismiss Selected
</button>
</div>