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:
1 parent
cb4e892b10
commit
4a68fe7cab
3 files changed
+195
-89
No files matched your search
@@ -12,11 +12,84 @@ async function refreshConflicts(): Promise<void> {
|
||||
});
|
||||
const list = document.getElementById("conflicts-list");
|
||||
if (list) Alpine.initTree(list);
|
||||
filterConflicts();
|
||||
updateBulkActions();
|
||||
} catch (error) {
|
||||
console.error("Failed to refresh conflicts:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Client-side filter and bulk selection -------------------------------
|
||||
// Pure functions over the server-rendered conflict cards (rendering
|
||||
// ownership: JS decides what shows; templ rendered what they look like).
|
||||
|
||||
function filterConflicts(): void {
|
||||
const select = document.getElementById(
|
||||
"status-filter",
|
||||
) as HTMLSelectElement | null;
|
||||
const filter = select?.value ?? "unresolved";
|
||||
let total = 0;
|
||||
let visible = 0;
|
||||
document.querySelectorAll<HTMLElement>(".conflict-item").forEach((item) => {
|
||||
total++;
|
||||
const status = item.dataset.status ?? "unresolved";
|
||||
const show = filter === "all" || status === filter;
|
||||
item.style.display = show ? "" : "none";
|
||||
if (show) visible++;
|
||||
});
|
||||
const empty = document.getElementById("filter-empty");
|
||||
if (empty) empty.classList.toggle("hidden", visible > 0 || total === 0);
|
||||
// Cards hidden by the filter can't stay selected.
|
||||
if (visible !== total) updateBulkActions();
|
||||
}
|
||||
|
||||
function toggleAllConflicts(): void {
|
||||
const selectAll = document.getElementById(
|
||||
"select-all-conflicts",
|
||||
) as HTMLInputElement | null;
|
||||
if (!selectAll) return;
|
||||
document.querySelectorAll<HTMLElement>(".conflict-item").forEach((item) => {
|
||||
if (item.style.display === "none") return;
|
||||
const box = item.querySelector<HTMLInputElement>(".conflict-checkbox");
|
||||
if (box) box.checked = selectAll.checked;
|
||||
});
|
||||
updateBulkActions();
|
||||
}
|
||||
|
||||
function getSelectedConflictIds(): string[] {
|
||||
return Array.from(
|
||||
document.querySelectorAll<HTMLInputElement>(".conflict-checkbox:checked"),
|
||||
)
|
||||
.map((box) => box.dataset.conflictId || "")
|
||||
.filter((id) => id !== "");
|
||||
}
|
||||
|
||||
function updateBulkActions(): void {
|
||||
const selected = getSelectedConflictIds();
|
||||
const bar = document.getElementById("bulk-actions");
|
||||
if (bar) bar.classList.toggle("hidden", selected.length === 0);
|
||||
const count = document.getElementById("selected-count");
|
||||
if (count) count.textContent = String(selected.length);
|
||||
}
|
||||
|
||||
function resolveSelected(): void {
|
||||
const ids = getSelectedConflictIds();
|
||||
if (ids.length === 0) return;
|
||||
const strategyEl = document.getElementById(
|
||||
"bulk-strategy",
|
||||
) as HTMLSelectElement | null;
|
||||
const strategy = (strategyEl?.value ?? "most_recent") as
|
||||
| "most_recent"
|
||||
| "highest_progress";
|
||||
void bulkResolve(strategy, ids);
|
||||
}
|
||||
|
||||
function dismissSelected(): void {
|
||||
const ids = getSelectedConflictIds();
|
||||
if (ids.length === 0) return;
|
||||
void bulkDismiss(ids);
|
||||
}
|
||||
|
||||
async function resolveConflict(
|
||||
conflictId: string,
|
||||
winner: string,
|
||||
@@ -168,9 +241,17 @@ export {
|
||||
refreshConflicts,
|
||||
resolveConflict,
|
||||
showResolveModal,
|
||||
filterConflicts,
|
||||
toggleAllConflicts,
|
||||
updateBulkActions,
|
||||
resolveSelected,
|
||||
dismissSelected,
|
||||
};
|
||||
|
||||
Alpine.data("conflicts", () => ({
|
||||
init() {
|
||||
filterConflicts();
|
||||
},
|
||||
bulkDismiss,
|
||||
bulkResolve,
|
||||
dismissAllResolved,
|
||||
@@ -179,4 +260,9 @@ Alpine.data("conflicts", () => ({
|
||||
refreshConflicts,
|
||||
resolveConflict,
|
||||
showResolveModal,
|
||||
filterConflicts,
|
||||
toggleAllConflicts,
|
||||
updateBulkActions,
|
||||
resolveSelected,
|
||||
dismissSelected,
|
||||
}));
|
||||
Reference in new issue
Block a user