Skip to content

fix(webui): keep saved filters intact when editing a filter preset#98

Merged
SoulKyu merged 4 commits into
mainfrom
looper/96-fix-webui-editing-a-filter-d9d28da4844a561b
Jul 26, 2026
Merged

fix(webui): keep saved filters intact when editing a filter preset#98
SoulKyu merged 4 commits into
mainfrom
looper/96-fix-webui-editing-a-filter-d9d28da4844a561b

Conversation

@SoulKyu

@SoulKyu SoulKyu commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Problem

The Edit button in the Saved Filters modal only copied a preset's name, description, share flag and hidden alerts/rules into the form — never its filter definition. saveNewPreset() then built the PUT payload from captureCurrentFilterState(), so clicking Update Filter persisted whatever filters were live on the dashboard.

Fixing a typo in a shared preset's description while browsing unfiltered replaced its filter_data with the unfiltered state (and wiped column_configs when the column checkbox was off), silently changing the dashboard for every teammate using it.

Changes

  • buildPresetFilterData() (new): returns the preset's stored filter_data when editing, the live dashboard state when creating a new preset or when the user opts in. Used by saveNewPreset().
  • New recaptureFilters state (default off, reset on edit/cancel/save) backing an explicit "Replace saved filters with my current dashboard filters" checkbox, shown only in edit mode with a note that leaving it unchecked keeps the saved criteria untouched.
  • The "Current filters to be saved" summary now renders what will actually be persisted, and relabels itself to "Saved filters for this preset (kept as-is)" when the opt-in is off.
  • "Include current column configuration" is hidden when it would have no effect (editing without re-capture).
  • editPreset() copies hidden_alerts / hidden_rules instead of aliasing them, so form edits no longer mutate the cached preset object.
  • Hidden alerts/rules remain editable from the edit form and are merged over the stored filter data.

Behaviour is unchanged for creating a new preset and for updateActiveFilterPreset(), which still deliberately re-captures the live state.

Validation

  • make webui-templates (only .templ sources edited; *_templ.go regenerated)
  • go build ./...
  • Branch logic exercised with a node assert check: new preset → live state; edit with opt-in off → stored filter_data unchanged; edit with opt-in on → live state.

Closes #96

🔁 Powered by Looper · runner=worker · agent=claude-code · An autonomous AI dev team for your GitHub repos.

@SoulKyu SoulKyu left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SoulKyu The core fix is right and well targeted: routing saveNewPreset() through buildPresetFilterData() means editing a preset's name or description no longer rewrites its filter_data from whatever happens to be on the dashboard, and the summary panel now describes what will actually be persisted instead of what is on screen. The defensive copies in editPreset() are a good catch too. I verified go build ./... passes and that both *_templ.go files are regenerated in sync with their .templ sources.

Two non-blocking follow-ups, both about the boundary of the new opt-in rather than the branch itself: one path still lets dashboard-side state into the "keep saved filters" update, and the checkbox copy does not match the set of fields the opt-in actually replaces. Details inline.

🔁 Powered by Looper · runner=reviewer · agent=claude-code · An autonomous AI dev team for your GitHub repos.

Comment thread internal/webui/templates/scripts/dashboard_filter_presets.templ
Comment thread internal/webui/templates/components/filter_preset_modal.templ
@SoulKyu

SoulKyu commented Jul 25, 2026

Copy link
Copy Markdown
Owner Author

Looper fixer round completef288c7f

  • ✅ Review comment on internal/webui/templates/scripts/dashboard_filter_presets.templ:327 (@SoulKyu) — thread
    • Closed the aliasing at the source: applyFilterPreset() now does this.filterHiddenAlerts = [...(data.hidden_alerts || [])] and the same for hidden_rules (internal/webui/templates/scripts/dashboard_filter_presets.templ:200-201), so dashboard hides from addFilterHiddenAlert() no longer mutate the cached preset's filter_data and can't be written back on a description-only edit.
  • ✅ Review comment on internal/webui/templates/components/filter_preset_modal.templ:139 (@SoulKyu) — thread
    • Reworded both strings to the suggested scope-accurate text in internal/webui/templates/components/filter_preset_modal.templ:135-137, and factored the duplicated condition into willReplaceSavedFilters() on the mixin (dashboard_filter_presets.templ:307-311), now used by buildPresetFilterData() and by the modal's x-show and x-text bindings at lines 142 and 153.

🔁 Powered by Looper · runner=fixer · agent=claude-code · An autonomous AI dev team for your GitHub repos.

@SoulKyu

SoulKyu commented Jul 25, 2026

Copy link
Copy Markdown
Owner Author

🔀 Rebased on main (clean, build green). — rebaser

@SoulKyu
SoulKyu force-pushed the looper/96-fix-webui-editing-a-filter-d9d28da4844a561b branch from f288c7f to 99c8575 Compare July 25, 2026 18:05

@SoulKyu SoulKyu left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SoulKyu The core of this fix reads correctly now. Routing saveNewPreset() through buildPresetFilterData() means a description-only edit no longer rewrites filter_data from whatever happens to be on the dashboard, the copy-on-read in applyFilterPreset() and editPreset() closes the aliasing that let dashboard-side hides leak into the cached preset, and folding the branch into willReplaceSavedFilters() keeps the modal labels and the persistence decision from drifting apart. The reworded checkbox copy also matches the scope that is actually replaced. I verified the generated *_templ.go files are in sync with the .templ sources for the new bindings.

One remaining gap on the opt-in path, left inline: column_configs can still be cleared rather than preserved. It is narrower than the original report and needs a deliberate user action, so I am not treating it as a merge blocker.

🔁 Powered by Looper · runner=reviewer · agent=claude-code · An autonomous AI dev team for your GitHub repos.

Comment thread internal/webui/templates/scripts/dashboard_filter_presets.templ Outdated
@SoulKyu

SoulKyu commented Jul 25, 2026

Copy link
Copy Markdown
Owner Author

🧪 QA Report

Env: make docker-build-all + docker compose up -d (webui :8081, backend, postgres, fake alertmanager). Flows driven in a real Chromium via Playwright, logged in through the actual login form. Fixture reproduces the issue scenario: shared preset "Prod Critical" (search=payments, severities=[critical], statuses=[firing], teams=[payments], alert_names=[HighErrorRate], display=compact, view=group, group_by=team, sort=severity/desc, items_per_page=25, 12 column_configs, 1 hidden_alert, 1 hidden_rule), with the live dashboard deliberately set to a different state.

Criterion Result Evidence
Edit → change description → Update leaves filter_data (filters, display/view mode, sort, items per page, column configs, hidden alerts/rules) byte-for-byte unchanged Live dashboard forced to search=LIVE-NOISE, severities=[warning], items_per_page=100, plus a live-only hidden alert LIVE-FP. Clicked Edit → typed a new description → Update Filter ("Filter preset updated!"). GET /api/v1/dashboard/filter-presets before vs after: JSON.stringify(filter_data) identical; hidden_alerts still ["SAVED-FP-1"] (live LIVE-FP did not leak), hidden_rules=1, column_configs=12, items_per_page=25. Only description changed.
Re-capturing the current dashboard state requires an explicit, clearly-labeled opt-in Edit form shows an amber-boxed unchecked checkbox #recapture-filters: "Replace this preset's saved view (filters, display and sort options, and column layout) with my current dashboard — Leave unchecked to keep the saved view as-is…". It is not rendered when creating a new preset. Summary heading reads "Saved view for this preset (kept as-is):" and lists the preset's stored filters (payments/critical/firing), not the live ones; "Include current column configuration" is hidden. Ticking the box flips the heading to "Current view to be saved:" with the live values and reveals the column checkbox.
Opt-in off/on behavior works whether or not the edited preset is currently applied Not applied — off: filter_data unchanged (row 1); on: persisted search="LIVE-UNFILTERED", severities=["warning"], items_per_page=100, view_mode=list, sort_by=name. Applied (clicked Load first, activePresetName="Prod Critical", 12 columns merged) — off: filter_data still byte-identical after a description edit; on (after tweaking the live dashboard): persisted search="TWEAKED-WHILE-APPLIED", severities=["info"], items_per_page=50, column_configs=12.
No edits to *_templ.go; regenerated via make webui-templates Regenerated both files inside the Docker builder stage (make webui-full-rebuild, which deletes and re-runs templ generate) and diffed against the committed ones: the only differing line is the // templ: version: banner (v0.3.977 in the image vs v0.3.906 used by the author). Generated content is otherwise byte-identical — the files are genuine templ output.

Exploratory: adjacent flows intact — creating a new preset still captures the live state (search=LIVE-NOISE, severities=[warning], items_per_page=100) and correctly hides the re-capture checkbox; updateActiveFilterPreset() still deliberately re-captures (search="ACTIVE-UPDATE-RECAPTURE", items_per_page=10); ticking the opt-in then hitting Cancel and re-opening Edit resets it to unchecked (no leaked flag that would silently clobber on the next save); editing a second preset right after shows that preset's own saved view and leaves its filter_data unchanged. No page errors in the console during any run. Out-of-scope note: a preset whose column_configs carry ids outside the app's col_* set makes mergeSystemColumns() produce duplicate order values, so any re-capture save is rejected with Duplicate column order: 0 — pre-existing behavior on the unchanged captureCurrentFilterState() path (also hits "Save Current Filters" on main), not introduced here.

Verdict: PASS

@SoulKyu SoulKyu added the qa:passed QA agent: all acceptance criteria verified label Jul 25, 2026

@SoulKyu SoulKyu left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SoulKyu The column-layout fix in this head lands in the right place. buildPresetFilterData() now copies the preset's stored column_configs through when the user opts into re-capture with the columns box off, so the opt-in can only ever replace a saved layout, never clear one, and the guard is at the persistence boundary rather than relying on the form seeding a page-lifetime flag. The willReplaceSavedFilters() predicate from the previous round is holding up well: the modal labels, the include-columns visibility and the persistence branch all read from one place now, which is what keeps this class of bug from coming back.

Verification on my side: I re-ran templ generate in the review worktree and the working tree came back clean, so both generated files match their .templ sources exactly, and go build ./... is green.

One non-blocking follow-up, left inline on the modal: the column-layout fix made the re-capture checkbox's own copy stale, and the nested columns checkbox now means two different things depending on create-vs-edit. Nothing is destroyed in either state anymore, so this is not a merge blocker.

🔁 Powered by Looper · runner=reviewer · agent=claude-code · An autonomous AI dev team for your GitHub repos.

Comment thread internal/webui/templates/components/filter_preset_modal.templ Outdated
@SoulKyu

SoulKyu commented Jul 25, 2026

Copy link
Copy Markdown
Owner Author

Looper fixer round completef11bf12

  • ⏸️ Review comment on internal/webui/templates/scripts/dashboard_filter_presets.templ (@SoulKyu) — thread
    • Already implemented on this branch: commit 34bc81e added exactly the suggested guard in buildPresetFilterData() (internal/webui/templates/scripts/dashboard_filter_presets.templ:318-324), so when editing with the columns box off the captured payload reuses editingPreset.filter_data.column_configs instead of []. No further code change was needed.
  • ✅ Review comment on internal/webui/templates/components/filter_preset_modal.templ (@SoulKyu) — thread
    • Applied the suggested wording in internal/webui/templates/components/filter_preset_modal.templ: line 135 now reads "Replace this preset's saved view (filters, display and sort options) with my current dashboard", and the include-columns label is x-text bound to "editingPreset ? 'Also replace the saved column layout with my current one' : 'Include current column configuration'". Templates regenerated with make webui-templates.

🔁 Powered by Looper · runner=fixer · agent=claude-code · An autonomous AI dev team for your GitHub repos.

@SoulKyu

SoulKyu commented Jul 25, 2026

Copy link
Copy Markdown
Owner Author

🧪 QA Report

Re-test after the rebase (head 34bc81e, which now also carries "keep a preset's saved column layout when re-capturing filters").

Env: make docker-build-webui + docker compose up -d (webui :8081, backend, postgres, fake alertmanager, fresh DB volume). Flows driven in a real Chromium via Playwright, logged in through the actual login form. Fixture: shared preset "Prod Critical"search=payments, severities=[critical], statuses=[firing], teams=[payments], alert_names=[HighErrorRate], display=compact, view=group, group_by=team, sort=severity/desc, items_per_page=25, 12 column_configs in a non-default order with col_summary/col_duration hidden, 1 hidden_alert, 1 hidden_rule — with the live dashboard deliberately set to a different state each time.

Criterion Result Evidence
Edit → change description → Update leaves filter_data (filters, display/view mode, sort, items per page, column configs, hidden alerts/rules) byte-for-byte unchanged Live dashboard driven through the real controls to search=LIVE-NOISE, severities=[warning], items_per_page=100, plus a live-only filter-hidden alert LIVE-FP. Edit → typed a new description → Update Filter ("Filter preset updated!"). GET /api/v1/dashboard/filter-presets before vs after: JSON.stringify(filter_data) identical; hidden_alerts still ["SAVED-FP-1"] (LIVE-FP did not leak), 12 column_configs with the saved order and hidden flags intact. Only description changed.
Re-capturing the current dashboard state requires an explicit, clearly-labeled opt-in Edit form shows an amber-boxed unchecked #recapture-filters: "Replace this preset's saved view (filters, display and sort options, and column layout) with my current dashboard — Leave unchecked to keep the saved view as-is: only the name, description, sharing and the hidden alerts/rules below are updated." Not rendered when creating a new preset. Summary heading reads "Saved view for this preset (kept as-is):" and lists the stored values (payments / critical / firing / compact / group / severity (desc) / 25), not the live ones; "Include current column configuration" is hidden. Ticking the box flips the heading to "Current view to be saved:" with the live values (LIVE-UNFILTERED / info / expanded / list / name (asc) / 50) and reveals the column checkbox.
The opt-in off/on behavior works whether or not the edited preset is currently applied Not applied — off: filter_data unchanged (row 1); on: persisted search=LIVE-UNFILTERED, severities=[info], display=expanded, view=list, sort=name/asc, items_per_page=50. Applied (clicked Load first → activePresetName="Prod Critical", saved 12-column layout merged with col_summary/col_duration hidden) — off: filter_data still byte-identical after a description edit; on (after tweaking the live dashboard): persisted search=TWEAKED-WHILE-APPLIED, severities=[critical,info], items_per_page=50.
No edits to *_templ.go; regenerated via make webui-templates Built --target builder (which runs make webui-full-rebuild: deletes and re-runs templ generate) and diffed the container's output against the committed files. The only differing line is the // templ: version: banner (v0.3.977 in the image vs v0.3.906 used by the author); generated content is otherwise byte-identical. Diff touches only the 2 .templ sources and their 2 generated counterparts.

Also verified for the new head's extra commit: with the opt-in on but "Include current column configuration" off, the preset's saved column layout is now preserved (column_configs byte-identical to the fixture's, col_summary/col_duration still hidden) while the filters are re-captured (search=RECAPTURE-NO-COLS, items_per_page=10) — it no longer wipes the layout to [].

Exploratory: adjacent flows intact — creating a new preset still captures the live state (NEW-PRESET-LIVE / warning / 100 / 12 columns) and correctly hides the re-capture checkbox; updateActiveFilterPreset() still deliberately re-captures (ACTIVE-UPDATE-RECAPTURE, items_per_page=10); ticking the opt-in then Cancel and re-opening Edit resets it to unchecked and a following save still leaves filter_data identical; hidden rules stay editable from the edit form with the opt-in off (added qa added rule → persisted alongside saved rule, everything else in filter_data untouched); editing a second preset shows that preset's own saved view. Dashboard search still filters (11 → 1 → 11 rows), / still focuses the search box and typing / inside it stays literal (Disk/Space), Load still applies filters and the saved column layout. No page errors and no ≥400 responses during any run.

Verdict: PASS

@SoulKyu

SoulKyu commented Jul 26, 2026

Copy link
Copy Markdown
Owner Author

🔀 Rebased on main (clean, build green). — rebaser

@SoulKyu
SoulKyu force-pushed the looper/96-fix-webui-editing-a-filter-d9d28da4844a561b branch from f11bf12 to caad6d8 Compare July 26, 2026 06:16

@SoulKyu SoulKyu left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SoulKyu Reviewed the final state of this PR (head caad6d8). Across the three rounds already on this PR, buildPresetFilterData() now correctly returns the preset's own stored filter_data on a description/sharing-only edit, willReplaceSavedFilters() is the single source of truth driving both the modal labels and the persistence branch, editPreset()/applyFilterPreset() copy hidden_alerts/hidden_rules instead of aliasing them, and the re-capture opt-in preserves the saved column layout unless the columns checkbox is explicitly checked. I re-read the current diff against main independently and didn't find anything new beyond what was already caught and fixed in the prior rounds — the checkbox wording now matches what it actually replaces in every state (create vs. edit vs. edit+recapture).

Verification I ran myself: templ generate produced zero diff against the committed *_templ.go files (generated code is in sync with the .templ sources), and go build ./... is green.

Nice work tightening this down over the review rounds — the fix is well targeted and the final wording cleanup makes the opt-in's scope unambiguous. Thanks for the thorough iteration!

🔁 Powered by Looper · runner=reviewer · agent=claude-code · An autonomous AI dev team for your GitHub repos.

@SoulKyu

SoulKyu commented Jul 26, 2026

Copy link
Copy Markdown
Owner Author

🔀 Rebased on main (clean, build green). — rebaser

@SoulKyu
SoulKyu force-pushed the looper/96-fix-webui-editing-a-filter-d9d28da4844a561b branch from caad6d8 to 48e6625 Compare July 26, 2026 06:46

@SoulKyu SoulKyu left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SoulKyu This head (48e6625) is byte-identical, in the reviewed files, to caad6d8 from the previous review round — I diffed them directly and the only change between the two commits is an unrelated factory-tui commit picked up by a rebase onto main, which is already merged upstream and outside this PR's diff.

I re-verified the substance independently rather than just trusting the prior rounds: buildPresetFilterData() correctly returns the preset's own stored filter_data on a description/sharing-only edit and only pulls in the live dashboard state when willReplaceSavedFilters() is true (creating a new preset, or the user explicitly checks the re-capture opt-in). That predicate is also the single source driving the modal labels and the include-columns checkbox visibility, so the UI and the persistence branch can't drift apart. editPreset() and applyFilterPreset() now copy hidden_alerts/hidden_rules with spread instead of aliasing, so editing the form or hiding an alert on the dashboard no longer mutates the cached preset object. And the column-layout opt-in only ever replaces column_configs when the columns checkbox is checked during re-capture — otherwise it falls back to the preset's stored layout, so it can't be silently cleared.

Verification I ran myself in this worktree: templ generate ./internal/webui/templates/... produced zero diff against the committed *_templ.go files, and go build ./... is green.

Nice, methodical fix for a subtle data-loss bug — thanks for iterating through the review rounds to close the column-layout and wording edge cases too.

🔁 Powered by Looper · runner=reviewer · agent=claude-code · An autonomous AI dev team for your GitHub repos.

@SoulKyu

SoulKyu commented Jul 26, 2026

Copy link
Copy Markdown
Owner Author

🔀 Rebased on main (clean, build green). — rebaser

@SoulKyu
SoulKyu force-pushed the looper/96-fix-webui-editing-a-filter-d9d28da4844a561b branch from 48e6625 to 9c2bd07 Compare July 26, 2026 08:48

@SoulKyu SoulKyu left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SoulKyu Reviewed the final head (9c2bd07) after the last review round. The only change since the previously reviewed head (48e6625) in the four files this PR touches is none at all in code — the diff between those two heads is entirely the unrelated 209bc0f (Sentry encryption key) commit picked up by a rebase onto main; I confirmed with git diff that the reviewed webui files are byte-identical between the two heads. The actual new commit on top, 9c2bd07, is a small, correct fix to the include-columns checkbox label: it now reads "Also replace the saved column layout with my current one" when editing (via x-text="editingPreset ? ... : 'Include current column configuration'" in filter_preset_modal.templ), which matches the column-preserving behaviour buildPresetFilterData() already implements — closing the last non-blocking wording gap flagged in the previous round.

I independently re-verified the substance rather than relying only on prior rounds: buildPresetFilterData() returns the preset's own stored filter_data on a description/sharing-only edit and only pulls in live dashboard state when willReplaceSavedFilters() is true (new preset, or the user explicitly checks the re-capture opt-in); that same predicate drives the modal labels and the include-columns visibility so they can't drift from the persistence branch; editPreset()/applyFilterPreset() copy hidden_alerts/hidden_rules with spread instead of aliasing, so dashboard-side hides or form edits no longer mutate the cached preset; and the re-capture opt-in only replaces column_configs when the columns checkbox is checked, otherwise it falls back to the preset's stored layout.

Verification I ran myself in this worktree: go build ./... is green, and make webui-templates (templ generate) produced zero diff against the committed *_templ.go files.

Nice, thorough iteration closing out the last wording nit — this is ready to merge. Thanks for the careful work across all the review rounds!

🔁 Powered by Looper · runner=reviewer · agent=claude-code · An autonomous AI dev team for your GitHub repos.

SoulKyu added 4 commits July 26, 2026 13:17
Editing a preset only copied its name, description and share flag into the
form, so "Update Filter" persisted whatever filters were live on the
dashboard, silently replacing the preset's saved definition (and wiping its
column configs when the column checkbox was off). Renaming a shared team
preset therefore corrupted it for every teammate using it.

The edit form now submits the preset's own stored filter_data, with an
explicit "Replace saved filters with my current dashboard filters" opt-in
(default off) for the re-capture flow. The filter summary and the column
config checkbox follow that opt-in so the form shows what will actually be
saved. Hidden alerts/rules stay editable from the form and are copied on
load so form edits no longer mutate the cached preset.

Generated-By: looper 0.11.0 (runner=worker, agent=claude-code)
applyFilterPreset() aliased the active preset's hidden_alerts/hidden_rules
arrays, so dashboard hides mutated the cached preset and were written back
on the next description-only edit. Copy on read, matching editPreset().

Reword the re-capture checkbox to name the real persisted scope (filters,
display and sort options, column layout) and expose the branch condition as
willReplaceSavedFilters() so the labels and the persistence branch cannot
drift apart.

Generated-By: looper 0.11.0 (runner=fixer, agent=claude-code)
…ters

Generated-By: looper 0.11.0 (runner=fixer, agent=claude-code)
The re-capture label claimed it replaced the column layout, but that only
holds when "Include current column configuration" is also ticked. Drop the
column clause from the parent label and let the nested checkbox describe
the branch it belongs to: when editing it now reads "Also replace the saved
column layout with my current one", while creating a preset keeps the
original wording.

Generated-By: looper 0.11.0 (runner=fixer, agent=claude-code)
@SoulKyu

SoulKyu commented Jul 26, 2026

Copy link
Copy Markdown
Owner Author

🔀 Rebased on main (clean, build green). — rebaser

@SoulKyu
SoulKyu force-pushed the looper/96-fix-webui-editing-a-filter-d9d28da4844a561b branch from 9c2bd07 to 2e84fd5 Compare July 26, 2026 11:17

@SoulKyu SoulKyu left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SoulKyu Reviewed the current head (2e84fd5). I diffed it against the previously reviewed and approved head (9c2bd07) and the four files this PR touches — filter_preset_modal.templ/_templ.go and dashboard_filter_presets.templ/_templ.go — are byte-identical between the two. The only difference is that this branch was rebased onto a newer main, picking up two unrelated, already-merged backend fixes (57647f7 re-firing-alert acknowledgement fix, 23693e7 RemoveAllResolvedAlerts admin restriction), neither of which touches this PR's diff.

So the substance is unchanged from the prior round: buildPresetFilterData() correctly returns the preset's own stored filter_data on a description/sharing-only edit and only pulls in live dashboard state when willReplaceSavedFilters() is true; that same predicate drives the modal labels and the include-columns checkbox visibility so they can't drift from the persistence branch; editPreset()/applyFilterPreset() copy hidden_alerts/hidden_rules with spread instead of aliasing; and the re-capture opt-in only replaces column_configs when the columns checkbox is checked, otherwise falling back to the preset's stored layout.

Verification I ran myself in this worktree: go build ./... is green, and make webui-templates (templ generate) produced zero diff against the committed *_templ.go files.

Nothing new here — ready to merge. Thanks for keeping the branch current with main.

🔁 Powered by Looper · runner=reviewer · agent=claude-code · An autonomous AI dev team for your GitHub repos.

@SoulKyu

SoulKyu commented Jul 26, 2026

Copy link
Copy Markdown
Owner Author

🧪 QA Report

Re-test after the clean rebase (head 2e84fd5, which adds "make the column checkbox label match its edit-mode behaviour" on top of the previously-tested 34bc81e).

Env: docker build -f Dockerfile.webui + docker compose up -d (webui :8081, backend, postgres, fake alertmanager, fresh DB volume). Flows driven in a real Chromium via Playwright, logged in through the actual login form. Fixture: shared preset "Prod Critical"search=payments, severities=[critical], statuses=[firing], teams=[database], alert_names=[DiskSpaceLow], display=compact, view=group, group_by=team, sort=severity/desc, items_per_page=25, 12 column_configs in a reversed order with col_summary/col_duration hidden, 1 hidden_alert, 1 hidden_rule — with the live dashboard deliberately driven to a different state through the real controls each time.

Criterion Result Evidence
Edit → change description → Update leaves filter_data (filters, display/view mode, sort, items per page, column configs, hidden alerts/rules) byte-for-byte unchanged Live dashboard driven through the real controls to search=LIVE-NOISE, severities=[warning] (Severity dropdown), items_per_page=100 (page-size select), plus a live-only filter-hidden alert LIVE-FP. Saved Filters → Edit → typed a new description → Update Filter. GET /api/v1/dashboard/filter-presets before vs after: JSON.stringify(filter_data) identical (unchanged: true); hidden_alerts still ["SAVED-FP-1"] (LIVE-FP did not leak), 12 column_configs with the saved order and hidden flags intact. Only description changed.
Re-capturing the current dashboard state requires an explicit, clearly-labeled opt-in Edit form shows an amber-boxed unchecked #recapture-filters: "Replace this preset's saved view (filters, display and sort options) with my current dashboard — Leave unchecked to keep the saved view as-is: only the name, description, sharing and the hidden alerts/rules below are updated." Not rendered when creating a new preset (recaptureVisible: false). Summary heading reads "Saved view for this preset (kept as-is):" and lists the stored values (payments / critical / firing / database / DiskSpaceLow / compact / group / severity (desc) / 25), not the live ones; the column checkbox is hidden. Ticking the box flips the heading to "Current view to be saved:" with the live values (LIVE-NOISE / warning / classic / list / duration (asc) / 100) and reveals the column checkbox.
The opt-in off/on behavior works whether or not the edited preset is currently applied Not applied — off: filter_data byte-identical (row 1); on: persisted search=LIVE-NOISE, severities=[warning], display=classic, view=list, sort=duration/asc, items_per_page=100. Applied (clicked Load first → activePresetName="Prod Critical", live view became payments/critical/25/group and the saved 12-column layout was merged with col_summary/col_duration hidden) — off: filter_data still byte-identical after a description edit; on (after tweaking the live dashboard and unticking the column checkbox): persisted search=TWEAKED-WHILE-APPLIED, items_per_page=10, while column_configs stayed byte-identical to the saved layout (no wipe to []).
No edits to *_templ.go; regenerated via make webui-templates Built --target builder (which runs make webui-full-rebuild: deletes and re-runs templ generate) and diffed the container's output against the committed files — both filter_preset_modal_templ.go and dashboard_filter_presets_templ.go are byte-identical, banner included. The whole diff touches only the 2 .templ sources and their 2 generated counterparts.

New in this head: with the opt-in on, the column checkbox now relabels itself per branch — Also replace the saved column layout with my current one in edit mode vs Include current column configuration when creating — and the parent opt-in label no longer claims to replace the column layout. Verified in the DOM in all three states (edit/off → checkbox hidden, edit/on → new label, create → original label) and in the screenshot of the open form.

Exploratory: adjacent flows intact — creating a new preset still captures the live state (search=NEW-PRESET-LIVE, 12 columns) and correctly hides the re-capture checkbox; updateActiveFilterPreset() still deliberately re-captures (persisted ACTIVE-UPDATE-RECAPTURE after loading the preset and retyping the search); ticking the opt-in then Cancel and re-opening Edit resets it to unchecked; hidden rules stay editable from the edit form with the opt-in off (added qa added rule through the real rule form → persisted alongside saved rule, every other filter_data key untouched); dashboard search still filters (15 → 5 rows); / still focuses the search box and typing / inside it stays literal (Disk/Space); Load still applies both filters and the saved column layout. No page errors and no ≥400 responses during any run.

Verdict: PASS

@SoulKyu SoulKyu added the ready-to-merge All gates green — safe to merge label Jul 26, 2026
@SoulKyu
SoulKyu merged commit 488e806 into main Jul 26, 2026
@SoulKyu
SoulKyu deleted the looper/96-fix-webui-editing-a-filter-d9d28da4844a561b branch July 26, 2026 12:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

looper:review Looper: PR awaiting agent review qa:passed QA agent: all acceptance criteria verified ready-to-merge All gates green — safe to merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(webui): editing a filter preset silently overwrites its saved filters with the live dashboard state

1 participant