fix(filter-builder): id labels survive Apply — stored ids resolve to names

The library cell reverted to the raw uuid after Apply: the apply flow
reloads the page, the SSR-preloaded tree re-ingests, and idLabels —
populated only by suggestion PICKS — starts empty, so valueCellText
falls back to the id. Same on any fresh load of a stored id-rule.

Two halves:

- Server: the id-source suggest query's typed filter (library/
  collection/added_by/feed) now also exact-matches a comma-separated
  ID list alongside the name ILIKE — so a suggest call with the stored
  ids returns their labeled rows. No new endpoint; the menu and the
  resolver share one surface.

- Client: fetchSuggestions gains a QUIET mode (fills idLabels, touches
  no menu state), id rows fall back to their stored ids as the typed
  lookup when no draft is present, and init() calls resolveIdLabels()
  after tree ingest — one quiet fetch per id-kind rule resolves every
  stored id to its name from the first paint after reload.

Pins: the Go side (suggest with a library's UUID returns the row with
the library NAME as display), the client side (resolveIdLabels fills
labels, the cell renders the name, no menu opens). 35 filter-builder
vitest cases green; container filter slice green.
This commit is contained in:
John O'Keefe committed 2026-11-01 00:11:57 -04:00
1 parent ed42111df1
commit 09f8f0875f
4 files changed
+87 -8

No files matched your search

+8 -1
View File
@@ -1112,9 +1112,16 @@ func (s *SearchService) composedIDSourceValues(ctx context.Context, b *filterBui
fromTables, valueExpr, displayExpr, joinMedia, limitBind, libBind string) ([]FieldValue, error) {
// The typed bind happens HERE and only when non-blank — an unused
// parameter is a 42P18 error (the blank-browse case binds nothing).
// Typing filters the NAME (ILIKE) — and also exact-matches a
// comma-separated ID list: the builder resolves a freshly ingested
// tree's stored ids to labels with the same call (2026-11-01: the
// library cell reverted to raw ids after Apply — labels only ever
// came from suggestion picks).
nameFilter := ""
if p.Typed != "" {
nameFilter = " AND " + displayExpr + " ILIKE '%' || " + b.bind(p.Typed) + " || '%'"
tb := b.bind(p.Typed)
nameFilter = " AND (" + displayExpr + " ILIKE '%' || " + tb + " || '%'" +
" OR " + valueExpr + "::text = ANY(string_to_array(" + tb + ", ',')))"
}
sqlStr := `SELECT ` + valueExpr + ` AS value, ` + displayExpr + ` AS display, COUNT(mi.id) AS count
FROM ` + fromTables + `