3.3 KiB
Concise Default View — design spec
Problem
The index page shows every book grouped by status. As the library grows, this becomes too much information at a glance. The user needs a focused default showing only what's active right now, with easy paths to browse the rest.
Design
The index view takes an optional filter query parameter. With no filter, it shows a
concise default: only books in-flight (READING) and the 5 most recently added WANT
books. All other books are accessible via filter links. With a filter (all, read,
want), it shows the full matching subset.
Behaviour
Default / no filter (concise view)
- READING group: all READING books (unchanged from current).
- WANT group: sorted by
:ADDED:date descending, limited to 5. - READ / ABANDONED: replaced by summary links — e.g. "Read · 3" as a clickable
link to
/index?filter=read. - Filter nav bar shown at the top: links to
All books (N),Want list,Read (N).
?filter=all
Full index, all groups, no truncation — identical to today's default.
?filter=read
Only the READ group. Single group in the listing.
?filter=want
Only the WANT group. Single group in the listing.
Error handling
- An unrecognised/empty filter value is treated as
nil(concise default). - A WANT book with no
:ADDED:property sorts before dated entries (treated as oldest). - Fewer than 5 WANT books → all are shown, no padding or placeholder rows.
Implementation
Model: spine-index-model
Signature changes from:
spine-index-model (books &optional selected-id)
to:
spine-index-model (books &optional filter selected-id)
When filter is nil, after building the group alist:
- Sort the
:wantgroup entries byaddeddescending (ISO date string; nils sort first = oldest). - Truncate to 5 entries.
- Replace
:readand:abandonedgroups with a:summary-groupslist. Each entry is a plist matching the existing model convention:The template renders these as clickable(:label "read" :count 3 :href "/index?filter=read").rowlinks. Whenfilteris non-nil,:summary-groupsis absent. Whenfilteris non-nil, return only the matching group (or all groups for"all").
Handler: defservlet index
Read the filter key from the request query alist. Pass it to spine-index-model.
Pass current_filter in the template context for conditional rendering.
Template: index.mustache
- Filter bar (above groups): shown when
current_filteris nil (links to expanded views) or truthy (link back to concise view). - Summary groups: new
{{#summary_groups}}block renders a simple.rowlink per suppressed group (READ, ABANDONED). - Existing group rendering unchanged — still handles full listing when
filter=allor single-group views.
Sorting
Within the WANT group for the concise view: sort by added field descending. The
added field is already part of each book's model entry. Nil dates sort before dated
entries (oldest).
Out of scope
- Pagination for large WANT lists (5 is a design choice; if the user wants more, the "All books" link is there).
- Remembering the user's last selected filter across requests (stateless is simpler).
- Keyboard-driven filter switching (plain links for now).