feat(matching): match results as a scoped templ partial + enriched matches
- BookMatch now carries title/author/cover (the fields the old client renderer always assumed but the API never sent — cards would have shown 'undefined'); filled during the family-lens pass in QueryBooks - new partial route GET /unlinked-books/matches renders the scoped match cascade server-side; searchMatches swaps it via htmx.ajax instead of rebuilding cards from JSON - NOTE: the unlinked-books surface is currently dark end-to-end — the templ page has no route, the live linking.ts renderer targets a container no template has, and its Link button calls a window.showMatchModal that does not exist. This commit ships the scoped backend + partial; wiring the UI is a product decision.
This commit is contained in:
1 parent
a31da89df5
commit
327fc2c0f9
5 files changed
+383
-160
No files matched your search
@@ -742,6 +742,45 @@ func registerFrontendRoutes(cfg *Config) {
|
||||
// itself here (htmx) whenever the library switcher dispatches
|
||||
// week-refresh, carrying the current #library-select value. Same
|
||||
// resolution rules as the dashboard page (family-lens safe).
|
||||
// Unlinked-book match results partial: the match cascade for one device
|
||||
// file, rendered server-side and scoped to the requester's visible
|
||||
// libraries (absent-from-results) — rendering-ownership: templ renders,
|
||||
// JS only swaps. NOTE: nothing routes to the unlinked-books page yet;
|
||||
// this endpoint is ready for whichever UI wires it up.
|
||||
frontendProtected.GET("/unlinked-books/matches", func(c *echo.Context) error {
|
||||
user, ok := c.Get("user").(database.Users)
|
||||
if !ok {
|
||||
return c.HTML(http.StatusUnauthorized, "<div>Unauthorized</div>")
|
||||
}
|
||||
userUUID, uuidErr := uuid.Parse(user.ID.String())
|
||||
if uuidErr != nil {
|
||||
return c.HTML(http.StatusUnauthorized, "<div>Unauthorized</div>")
|
||||
}
|
||||
set, _, setErr := cfg.LibraryService.VisibleLibrarySet(c.Request().Context(), pgtype.UUID{Bytes: userUUID, Valid: true})
|
||||
if setErr != nil {
|
||||
return c.HTML(http.StatusInternalServerError, "<div>Failed to resolve visible libraries</div>")
|
||||
}
|
||||
visibleIDs := make([]string, 0, len(set))
|
||||
for id := range set {
|
||||
visibleIDs = append(visibleIDs, id)
|
||||
}
|
||||
req := &services.BookQueryRequest{
|
||||
SHA256: c.QueryParam("sha256"),
|
||||
Title: c.QueryParam("title"),
|
||||
VisibleLibraryIDs: visibleIDs,
|
||||
}
|
||||
resp, matchErr := services.NewBookMatchingService(cfg.Queries).QueryBooks(c.Request().Context(), req)
|
||||
if matchErr != nil {
|
||||
return c.HTML(http.StatusInternalServerError, "<div>Failed to search</div>")
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if renderErr := templates.UnlinkedMatchCards(c.QueryParam("progress_id"), resp.Matches).Render(c.Request().Context(), &buf); renderErr != nil {
|
||||
return renderErr
|
||||
}
|
||||
return c.HTML(http.StatusOK, buf.String())
|
||||
})
|
||||
|
||||
frontendProtected.GET("/dashboard/week-widget", func(c *echo.Context) error {
|
||||
user, err := getTemplateUserWithTheme(c, cfg)
|
||||
if err != nil {
|
||||
|
||||
Reference in new issue
Block a user