3 Commits
Author SHA1 Message Date
John O'Keefe 77cf0b827b refactor(db): GetCollectionItemsForDashboard embeds the media row; excluded filter moves into SQL; third converter dies
SELECT mi.*, ci.excluded forced every caller to filter tombstone
exclusions in Go over a 69-field row, then a 45-field hand converter
(getCollectionItemsRowToMediaItems) copied it field-by-field into
MediaItems for BuildSections. The query now filters
(ci.excluded IS NOT TRUE) itself and returns sqlc.embed(mi) — callers
consume .MediaItems directly, the Go filter loops die in both
collection-detail ladders (JSON + SSR), and the converter is deleted.

Pins, H1/H3 tests, unit suites green.
2026-10-17 16:51:30 -04:00
John O'Keefe 188ae076ab refactor(utils): one PgTextString for the four pgtype.Text unwrappers
handlers textToString, router textToString, router getText, and utils
normText were four copies of the same NULL-to-empty unwrap (99 call
sites). One exported utils.PgTextString replaces them all; the private
normText (comparison normalizer) stays but is now the odd one out.

Harness fix found on the way: setupTestServer built a full wiring
literal for route registration and then RETURNED a second, sparser
literal that never copied AnnotationService — setup.AnnotationService
was always nil on the struct every test receives. Nobody noticed until
cross_library_move_test became the first caller (it previously used
the now-retired plain CreateMediaHighlight query); it now exercises
the real SaveHighlight path through the wired service.
2026-10-17 16:36:58 -04:00
John O'Keefe 1ce1b79489 refactor(cards): H2 foundation — cards.go, the one BookInfo construction site
BookInfo, LibraryTypeMap, and MediaBadgeKind move verbatim out of
collections.go into handlers/cards.go (the card package surface, same
package: no import-graph or templ changes), joined by the H2 builder:

- buildBookCard (unexported): the ONLY place BookInfo is constructed,
  fed by cardSource; cover URL via ResolveMediaURL, badge via
  MediaBadgeKind — both inside the builder, not per call site.
- Four exported adapters normalize the four real feed shapes:
  database.MediaItems (sections/smart/series/tags/preview),
  GetCollectionItemsForDashboardRow (collection dashboard branch),
  GetCollectionItemsRow (collection-items joins — MediaItemID field),
  SearchMediaItemsUnifiedRow (badge rides its LibraryTypeName column,
  so no LibraryTypeMap query at all).
- Adapters return non-nil empty slices, which retires the per-site
  []BookInfo{} empty-seed assignments when call sites migrate.

No call site changes in this commit — the 20 literals and 5 seeds are
migrated one surface per commit next, each with the H0 pin suite.

Verified: build, vet (only the two pre-existing flagged saveCoverImage
warnings), and 14/14 contract pins — the key freeze is byte-identical.
2026-10-17 14:00:34 -04:00