docs: add concise default view design spec
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
# 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 `:want` group entries by `added` descending (ISO date string; nils sort
|
||||
first = oldest).
|
||||
- Truncate to 5 entries.
|
||||
- Replace `:read` and `:abandoned` groups with a `:summary-groups` list. Each
|
||||
entry is a plist matching the existing model convention:
|
||||
```
|
||||
(:label "read" :count 3 :href "/index?filter=read")
|
||||
```
|
||||
The template renders these as clickable `.row` links.
|
||||
When `filter` is non-nil, `:summary-groups` is absent.
|
||||
When `filter` is 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_filter` is nil (links to expanded
|
||||
views) or truthy (link back to concise view).
|
||||
- **Summary groups**: new `{{#summary_groups}}` block renders a simple `.row` link per
|
||||
suppressed group (READ, ABANDONED).
|
||||
- Existing group rendering unchanged — still handles full listing when `filter=all`
|
||||
or 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).
|
||||
Reference in New Issue
Block a user