docs: add edit book actions design spec
This commit is contained in:
@@ -0,0 +1,127 @@
|
|||||||
|
# Edit Book Actions — design spec
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
The index page shows books grouped by status but provides no way to change a book's
|
||||||
|
status, add reading notes, or record ratings. Users must edit the Org file manually.
|
||||||
|
We need lightweight, context-sensitive edit actions accessible from the agenda view.
|
||||||
|
|
||||||
|
## Interaction model
|
||||||
|
|
||||||
|
Click a book row on the index page → the footer (minibuffer) shows action links
|
||||||
|
tailored to that book's current status. Clicking an action either performs it
|
||||||
|
immediately (status transitions) or shows a lightweight prompt page (add note,
|
||||||
|
mark read with date).
|
||||||
|
|
||||||
|
No JavaScript. All actions are plain `<a href>` links or simple HTML forms.
|
||||||
|
|
||||||
|
## Actions per status
|
||||||
|
|
||||||
|
### WANT
|
||||||
|
- **Mark reading** — immediate: sets TODO to READING
|
||||||
|
- **Add note** — prompt page: date + textarea, appends a note
|
||||||
|
|
||||||
|
### READING
|
||||||
|
- **Mark read** — prompt page: date picker (default today)
|
||||||
|
Rating is deferred (see out of scope).
|
||||||
|
- **Abandon** — immediate: sets TODO to ABANDONED
|
||||||
|
- **Add note** — prompt page: date + textarea
|
||||||
|
|
||||||
|
### READ
|
||||||
|
- **Read again** — immediate: sets TODO to READING
|
||||||
|
- **Add note** — prompt page: date + textarea
|
||||||
|
### ABANDONED
|
||||||
|
- **Try again** — immediate: sets TODO to READING
|
||||||
|
|
||||||
|
## Implementation
|
||||||
|
|
||||||
|
### New Org write functions (`spine.el`)
|
||||||
|
|
||||||
|
**`spine-set-status (id status &optional date)`**
|
||||||
|
|
||||||
|
Finds the book by `:ID:` property, changes its TODO keyword to `status`, and records
|
||||||
|
the state transition in the LOGBOOK drawer (using `org-todo`). If `date` is provided
|
||||||
|
(for READ status), sets a `:READ_DATE:` property.
|
||||||
|
|
||||||
|
**`spine-add-note (id text &optional date)`**
|
||||||
|
|
||||||
|
Finds the book by `:ID:`, appends `- [YYYY-MM-DD] text` to the headline body. Date
|
||||||
|
defaults to today if not provided. Follows the same pattern as `spine-add-book`:
|
||||||
|
`find-file-noselect`, modify, save, `kill-buffer`.
|
||||||
|
|
||||||
|
**`spine-set-rating (id rating)`**
|
||||||
|
|
||||||
|
Sets the `:RATING:` property on the book.
|
||||||
|
|
||||||
|
### Handler: `httpd/edit`
|
||||||
|
|
||||||
|
Single handler dispatching on `action` query param:
|
||||||
|
|
||||||
|
- `action=status` — calls `spine-set-status` with `id` and `value` (the target TODO
|
||||||
|
state). Redirects to `/index?id=<id>`.
|
||||||
|
- `action=note` — GET shows a prompt page with date + textarea for the book. POST
|
||||||
|
processes: calls `spine-add-note` with `id`, `text`, and optional `date`. Redirects
|
||||||
|
to `/index?id=<id>`.
|
||||||
|
- `action=rating` — calls `spine-set-rating` with `id` and `value`. Redirects to
|
||||||
|
`/index?id=<id>`.
|
||||||
|
- Unknown action or missing `id` — redirect to `/index` silently.
|
||||||
|
|
||||||
|
The prompt page for notes is a minimal HTML page (no Mustache template needed — small
|
||||||
|
enough to inline): shows the book title as context, a date input (default today), a
|
||||||
|
textarea, and a submit button. POST data includes `id`, `action`, `date`, `text`.
|
||||||
|
|
||||||
|
### Model: `spine-index-model` minibuffer extension
|
||||||
|
|
||||||
|
When a book is selected (`selected-id` matches), the model gains a `minibuffer` field:
|
||||||
|
|
||||||
|
```elisp
|
||||||
|
("minibuffer" .
|
||||||
|
(ht ("actions"
|
||||||
|
(list
|
||||||
|
(ht ("label" "Mark reading") ("href" "/edit?id=X&action=status&value=READING"))
|
||||||
|
(ht ("label" "Add note") ("href" "/edit?id=X&action=note"))))))
|
||||||
|
```
|
||||||
|
|
||||||
|
Actions are computed from the selected book's `:status` property. The exact set
|
||||||
|
follows the table in "Actions per status" above.
|
||||||
|
|
||||||
|
### Template: `index.mustache` minibuffer section
|
||||||
|
|
||||||
|
The existing minibuffer footer placeholder is replaced with a dynamic version:
|
||||||
|
|
||||||
|
```mustache
|
||||||
|
{{#minibuffer}}
|
||||||
|
<footer class="minibuffer">
|
||||||
|
<span class="muted">M-x</span>
|
||||||
|
<span style="display:flex;gap:.5rem;flex-wrap:wrap">
|
||||||
|
{{#actions}}
|
||||||
|
<a href="{{href}}" class="pill {{command}}">{{label}}</a>
|
||||||
|
{{/actions}}
|
||||||
|
</span>
|
||||||
|
</footer>
|
||||||
|
{{/minibuffer}}
|
||||||
|
```
|
||||||
|
|
||||||
|
Styled as pill links consistent with the existing pill classes. `M-x` prefix keeps
|
||||||
|
the Emacs feel.
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
|
||||||
|
New test file `test/spine-edit-test.el`:
|
||||||
|
|
||||||
|
- `spine-set-status` changes TODO state correctly for each transition
|
||||||
|
- `spine-set-status` records LOGBOOK entry
|
||||||
|
- `spine-add-note` appends a note with the correct format
|
||||||
|
- `spine-add-note` defaults to today when no date provided
|
||||||
|
- `spine-set-rating` sets the rating property
|
||||||
|
- Model includes `minibuffer` field when book is selected
|
||||||
|
- Model has correct action set per status
|
||||||
|
|
||||||
|
## Out of scope
|
||||||
|
|
||||||
|
- Editing book properties (author, format, ISBN) — add-book covers creation; editing
|
||||||
|
those can come later if needed
|
||||||
|
- Deleting books — add-book is additive; no delete flow yet
|
||||||
|
- Rating prompt on the "Mark read" page — deferred; rating can be set separately
|
||||||
|
- Inline editing in the detail section — the minibuffer actions are the primary
|
||||||
|
surface for now
|
||||||
Reference in New Issue
Block a user