Say what the level means on both screens that decide it

The invitation form gains the choice, and the option labels say what actually
ships rather than the word "write": a delegate can add readings, results,
observations, a medication and a marked dose, and can edit or delete nothing,
including what they entered a moment ago. That gap between what "write access"
sounds like and what it does is where a support case lives, so it is on the
card rather than in a reply.

The delegate reads the same thing before they accept. A pending write
invitation states its level and one sentence about what accepting means, on the
screen where they accept it, because acceptance is where the two consents meet
and it happens once.

Ending an access says what stays: the entries remain and the owner can remove
any of them, and the activity view keeps saying who entered what for as long as
the instance's audit window reaches. That number comes from the server, so an
instance running ninety days says ninety, and a browser that has not been told
says nothing rather than a made-up year.

The browser journey now invites at the wider level and reads the posted body
for it. That is the only check in the tree that can: the component convention
is server-rendered only, so no test under src/ runs the form's submit path, and
every one of them stays green with the control wired to a constant.
This commit is contained in:
Marc-André Bombeck committed 2026-08-03 10:12:22 +02:00
1 parent 533400ce60
commit d82e29737b
13 files changed
+451 -7

No files matched your search

+7
View File
@@ -120,6 +120,13 @@ export interface InviteInput {
identifier: string;
/** ISO-8601 with offset, or null for a grant that runs until somebody ends it. */
expiresAt: string | null;
/**
* What the invitation offers. Required rather than optional even though the
* server defaults an omitted field to READ: a caller that does not name the
* level has not decided it, and this is the one moment the level can be
* decided at all.
*/
access: "READ" | "WRITE";
}
export function useInviteGrant() {