One record answers another, from a list
Test / test (pull_request) Successful in 56s

Three small things that together make a board work: a link on every
listed record to a page about it (ResourceSpec.link, {id} and {field}
in the address); a state mail whose `to` is a field of the record this
one belongs to ({notice.email}), with that record's fields in the
text, and a lint that the form carries the record field it reaches
through; and value: "{user.email}" filled from the session and hidden,
so someone the site knows is not asked who they are.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GT37Z1Xtfd9pUuQtMTg6Yt
This commit is contained in:
Bendik Aagaard Lynghaug
2026-09-30 06:47:36 +02:00
co-authored by Claude Fable 5.1
parent 470338b969
commit fc998c8592
9 changed files with 239 additions and 11 deletions
+18
View File
@@ -136,6 +136,10 @@ is a feature wasted: each one says something true about the choice.
value: "{key}" # a preset; a dynamic page's segment fills it
```
`value: "{user.email}"` or `"{user.name}"` is the signed-in person's
own, and a field it fills is carried rather than asked: someone the
site knows is not asked who they are. A visitor gets the field, empty.
`type` is one of:
| type | what it is |
@@ -192,6 +196,19 @@ The other half is a listing that asks for the children:
where: { field: case, equals: "{key}" }
```
And the way there from a list: `link` puts a link on every listed
record to a page about it, with `{id}` for the record's id and
`{field}` for any of its fields. A notice on a public board links to
`/oppslag/{id}`, the dynamic page `/oppslag/[key]` whose form carries
the notice's id back in with `value: "{key}"`:
```yaml
resource:
source: { kind: kv, bucket: oppslag }
public: true
link: { to: "/oppslag/{id}", label: Jeg kan låne ut eller låne denne }
```
## A resource
Live data, declared by content and read by the server. What a resource
@@ -206,6 +223,7 @@ reads is never a client-supplied parameter.
where: { field: case, equals: "{key}" }
jq: ".[] | {name, url: .html_url}"
empty: Nothing decided yet.
link: { to: "/oppslag/{id}", label: Answer this } # a page per record
transitions: # the desk's buttons
- { from: complete, to: on_hearing, label: Send it out }
```
+7
View File
@@ -62,6 +62,13 @@ as text with an HTML alternative. `to: submitter` means the record's
`email` field, and the lint then requires every form filling that
bucket to collect a required `email`.
A record that belongs to another (a `type: record` field, say
`notice`) can write to that record's people: `to: "{notice.email}"`
goes to whoever posted the notice, and `{notice.hva}` in the body is
what they posted. The lint requires every form filling the bucket to
carry that field. This is how an answer to a notice on a board reaches
the neighbour who put it there, without anyone in between.
Portal renders it and publishes on `portal.mail.send`. gdo delivers
it, and a reply comes back on `portal.mail.received` as a note beside
the record. A note never moves a case.