mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 21:10:59 +00:00
fix(handover): show the link holder display names only
The claim preview is anonymous: whoever holds the link has proved nothing yet. It listed each guardian's grant id and fell back to their login name. It now carries the display name alone, null when there is none, and the page says "a guardian" for that. The notifications to the guardians fell back to the new owner's login name when the record had no display name. They now use sentences about the person the guardian looked after instead.
This commit is contained in:
1 parent
f71becef29
commit
055c48a3fe
16 files changed
+221
-47
No files matched your search
@@ -41806,14 +41806,15 @@ components:
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
grantId:
|
||||
type: string
|
||||
displayName:
|
||||
type: string
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: "null"
|
||||
description: "The guardian's display name; null when they set none. Never a login name or an id: the holder of the link
|
||||
has proved nothing yet."
|
||||
proposal:
|
||||
$ref: "#/components/schemas/ProfileClaimGuardianAccess"
|
||||
required:
|
||||
- grantId
|
||||
- displayName
|
||||
- proposal
|
||||
additionalProperties: false
|
||||
|
||||
+12
-1
@@ -531,6 +531,7 @@
|
||||
"signOut": "Abmelden und weiter",
|
||||
"guardiansTitle": "Wer vorerst Zugriff behält",
|
||||
"guardiansHint": "Vorgeschlagen von den Personen, die deine Akte geführt haben. Du entscheidest direkt nach der Anmeldung.",
|
||||
"guardianUnnamed": "Eine betreuende Person",
|
||||
"usernameHint": "3 bis 30 Buchstaben, Ziffern, _ oder -.",
|
||||
"usernameReserved": "Benutzernamen, die mit managed- beginnen, sind reserviert.",
|
||||
"emailHint": "Pflicht, damit du wieder hineinkommst, wenn du dein Passwort vergisst.",
|
||||
@@ -8445,7 +8446,17 @@
|
||||
"changedTitle": "{name} hat deinen Zugriff geändert",
|
||||
"changedEnd": "Dein Zugriff auf die Gesundheitsakte von {name} ist beendet.",
|
||||
"changedRead": "Du kannst die Gesundheitsakte von {name} jetzt ansehen, aber nicht ändern.",
|
||||
"changedManage": "Du kannst die Gesundheitsakte von {name} verwalten."
|
||||
"changedManage": "Du kannst die Gesundheitsakte von {name} verwalten.",
|
||||
"unnamed": {
|
||||
"claimedTitle": "Die Person, die du betreut hast, hat jetzt ein eigenes Konto",
|
||||
"claimedEnd": "Die Person, die du betreut hast, hat die eigene Gesundheitsakte übernommen. Dein Zugriff ist beendet.",
|
||||
"claimedRead": "Die Person, die du betreut hast, hat die eigene Gesundheitsakte übernommen. Du kannst sie weiter ansehen, aber nicht mehr ändern.",
|
||||
"claimedManage": "Die Person, die du betreut hast, hat die eigene Gesundheitsakte übernommen. Du kannst sie weiter verwalten, bis sie anders entscheidet.",
|
||||
"changedTitle": "Dein Zugriff auf eine Gesundheitsakte hat sich geändert",
|
||||
"changedEnd": "Dein Zugriff auf die Gesundheitsakte der Person, die du betreut hast, ist beendet.",
|
||||
"changedRead": "Du kannst die Gesundheitsakte der Person, die du betreut hast, jetzt ansehen, aber nicht ändern.",
|
||||
"changedManage": "Du kannst die Gesundheitsakte der Person, die du betreut hast, verwalten."
|
||||
}
|
||||
}
|
||||
},
|
||||
"i18n": {
|
||||
|
||||
+12
-1
@@ -531,6 +531,7 @@
|
||||
"signOut": "Sign out and continue",
|
||||
"guardiansTitle": "Who keeps access for now",
|
||||
"guardiansHint": "Proposed by the people who looked after your record. You decide right after signing in.",
|
||||
"guardianUnnamed": "A guardian",
|
||||
"usernameHint": "3 to 30 letters, numbers, _ or -.",
|
||||
"usernameReserved": "Usernames starting with managed- are reserved.",
|
||||
"emailHint": "Required, so you can get back in if you forget your password.",
|
||||
@@ -8445,7 +8446,17 @@
|
||||
"changedTitle": "{name} changed your access",
|
||||
"changedEnd": "Your access to the health record of {name} has ended.",
|
||||
"changedRead": "You can now view the health record of {name}, but not change it.",
|
||||
"changedManage": "You can manage the health record of {name}."
|
||||
"changedManage": "You can manage the health record of {name}.",
|
||||
"unnamed": {
|
||||
"claimedTitle": "The person you looked after now has their own account",
|
||||
"claimedEnd": "The person you looked after took over their health record. Your access has ended.",
|
||||
"claimedRead": "The person you looked after took over their health record. You can still view it, but no longer change it.",
|
||||
"claimedManage": "The person you looked after took over their health record. You can still manage it until they decide otherwise.",
|
||||
"changedTitle": "Your access to a health record changed",
|
||||
"changedEnd": "Your access to the health record of the person you looked after has ended.",
|
||||
"changedRead": "You can now view the health record of the person you looked after, but not change it.",
|
||||
"changedManage": "You can manage the health record of the person you looked after."
|
||||
}
|
||||
}
|
||||
},
|
||||
"i18n": {
|
||||
|
||||
+12
-1
@@ -531,6 +531,7 @@
|
||||
"signOut": "Cerrar sesión y continuar",
|
||||
"guardiansTitle": "Quién conserva el acceso por ahora",
|
||||
"guardiansHint": "Lo proponen las personas que llevaron tu historial. Tú decides justo después de iniciar sesión.",
|
||||
"guardianUnnamed": "Una persona tutora",
|
||||
"usernameHint": "De 3 a 30 letras, números, _ o -.",
|
||||
"usernameReserved": "Los nombres de usuario que empiezan por managed- están reservados.",
|
||||
"emailHint": "Obligatorio, para que puedas volver a entrar si olvidas tu contraseña.",
|
||||
@@ -8445,7 +8446,17 @@
|
||||
"changedTitle": "{name} ha cambiado tu acceso",
|
||||
"changedEnd": "Tu acceso al historial de salud de {name} ha terminado.",
|
||||
"changedRead": "Ahora puedes ver el historial de salud de {name}, pero no cambiarlo.",
|
||||
"changedManage": "Puedes gestionar el historial de salud de {name}."
|
||||
"changedManage": "Puedes gestionar el historial de salud de {name}.",
|
||||
"unnamed": {
|
||||
"claimedTitle": "La persona a la que cuidabas ya tiene su propia cuenta",
|
||||
"claimedEnd": "La persona a la que cuidabas ha asumido su historial de salud. Tu acceso ha terminado.",
|
||||
"claimedRead": "La persona a la que cuidabas ha asumido su historial de salud. Puedes seguir viéndolo, pero ya no modificarlo.",
|
||||
"claimedManage": "La persona a la que cuidabas ha asumido su historial de salud. Puedes seguir gestionándolo hasta que decida otra cosa.",
|
||||
"changedTitle": "Tu acceso a un historial de salud ha cambiado",
|
||||
"changedEnd": "Tu acceso al historial de salud de la persona a la que cuidabas ha terminado.",
|
||||
"changedRead": "Ahora puedes ver el historial de salud de la persona a la que cuidabas, pero no modificarlo.",
|
||||
"changedManage": "Puedes gestionar el historial de salud de la persona a la que cuidabas."
|
||||
}
|
||||
}
|
||||
},
|
||||
"i18n": {
|
||||
|
||||
+12
-1
@@ -531,6 +531,7 @@
|
||||
"signOut": "Se déconnecter et continuer",
|
||||
"guardiansTitle": "Qui garde l'accès pour l'instant",
|
||||
"guardiansHint": "Proposé par les personnes qui ont tenu votre dossier. Vous décidez juste après la connexion.",
|
||||
"guardianUnnamed": "Un tuteur",
|
||||
"usernameHint": "3 à 30 lettres, chiffres, _ ou -.",
|
||||
"usernameReserved": "Les noms d'utilisateur commençant par managed- sont réservés.",
|
||||
"emailHint": "Obligatoire, pour pouvoir revenir si vous oubliez votre mot de passe.",
|
||||
@@ -8445,7 +8446,17 @@
|
||||
"changedTitle": "{name} a modifié votre accès",
|
||||
"changedEnd": "Votre accès au dossier de santé de {name} a pris fin.",
|
||||
"changedRead": "Vous pouvez désormais consulter le dossier de santé de {name}, sans le modifier.",
|
||||
"changedManage": "Vous pouvez gérer le dossier de santé de {name}."
|
||||
"changedManage": "Vous pouvez gérer le dossier de santé de {name}.",
|
||||
"unnamed": {
|
||||
"claimedTitle": "La personne dont vous vous occupiez a maintenant son propre compte",
|
||||
"claimedEnd": "La personne dont vous vous occupiez a repris son dossier de santé. Votre accès a pris fin.",
|
||||
"claimedRead": "La personne dont vous vous occupiez a repris son dossier de santé. Vous pouvez toujours le consulter, mais plus le modifier.",
|
||||
"claimedManage": "La personne dont vous vous occupiez a repris son dossier de santé. Vous pouvez toujours le gérer jusqu'à ce qu'elle en décide autrement.",
|
||||
"changedTitle": "Votre accès à un dossier de santé a changé",
|
||||
"changedEnd": "Votre accès au dossier de santé de la personne dont vous vous occupiez a pris fin.",
|
||||
"changedRead": "Vous pouvez désormais consulter le dossier de santé de la personne dont vous vous occupiez, mais pas le modifier.",
|
||||
"changedManage": "Vous pouvez gérer le dossier de santé de la personne dont vous vous occupiez."
|
||||
}
|
||||
}
|
||||
},
|
||||
"i18n": {
|
||||
|
||||
+12
-1
@@ -531,6 +531,7 @@
|
||||
"signOut": "Esci e continua",
|
||||
"guardiansTitle": "Chi mantiene l'accesso per ora",
|
||||
"guardiansHint": "Proposto dalle persone che hanno tenuto la tua cartella. Decidi subito dopo l'accesso.",
|
||||
"guardianUnnamed": "Un tutore",
|
||||
"usernameHint": "Da 3 a 30 lettere, numeri, _ o -.",
|
||||
"usernameReserved": "I nomi utente che iniziano con managed- sono riservati.",
|
||||
"emailHint": "Obbligatorio, così puoi rientrare se dimentichi la password.",
|
||||
@@ -8445,7 +8446,17 @@
|
||||
"changedTitle": "{name} ha cambiato il tuo accesso",
|
||||
"changedEnd": "Il tuo accesso alla cartella sanitaria di {name} è terminato.",
|
||||
"changedRead": "Ora puoi vedere la cartella sanitaria di {name}, ma non modificarla.",
|
||||
"changedManage": "Puoi gestire la cartella sanitaria di {name}."
|
||||
"changedManage": "Puoi gestire la cartella sanitaria di {name}.",
|
||||
"unnamed": {
|
||||
"claimedTitle": "La persona di cui ti occupavi ora ha un proprio account",
|
||||
"claimedEnd": "La persona di cui ti occupavi ha preso in carico la propria cartella sanitaria. Il tuo accesso è terminato.",
|
||||
"claimedRead": "La persona di cui ti occupavi ha preso in carico la propria cartella sanitaria. Puoi ancora consultarla, ma non più modificarla.",
|
||||
"claimedManage": "La persona di cui ti occupavi ha preso in carico la propria cartella sanitaria. Puoi ancora gestirla finché non deciderà diversamente.",
|
||||
"changedTitle": "Il tuo accesso a una cartella sanitaria è cambiato",
|
||||
"changedEnd": "Il tuo accesso alla cartella sanitaria della persona di cui ti occupavi è terminato.",
|
||||
"changedRead": "Ora puoi consultare la cartella sanitaria della persona di cui ti occupavi, ma non modificarla.",
|
||||
"changedManage": "Puoi gestire la cartella sanitaria della persona di cui ti occupavi."
|
||||
}
|
||||
}
|
||||
},
|
||||
"i18n": {
|
||||
|
||||
+12
-1
@@ -531,6 +531,7 @@
|
||||
"signOut": "로그아웃하고 계속",
|
||||
"guardiansTitle": "당분간 접근 권한을 유지하는 사람",
|
||||
"guardiansHint": "기록을 관리해 온 사람들이 제안한 내용입니다. 로그인한 직후에 직접 결정합니다.",
|
||||
"guardianUnnamed": "보호자",
|
||||
"usernameHint": "영문자, 숫자, _ 또는 - 로 3~30자.",
|
||||
"usernameReserved": "managed- 로 시작하는 사용자 이름은 예약되어 있습니다.",
|
||||
"emailHint": "비밀번호를 잊었을 때 다시 들어올 수 있도록 필요합니다.",
|
||||
@@ -8445,7 +8446,17 @@
|
||||
"changedTitle": "{name} 님이 내 접근 권한을 바꿨습니다",
|
||||
"changedEnd": "{name} 님의 건강 기록에 대한 내 접근 권한이 종료되었습니다.",
|
||||
"changedRead": "이제 {name} 님의 건강 기록을 볼 수는 있지만 변경할 수는 없습니다.",
|
||||
"changedManage": "{name} 님의 건강 기록을 관리할 수 있습니다."
|
||||
"changedManage": "{name} 님의 건강 기록을 관리할 수 있습니다.",
|
||||
"unnamed": {
|
||||
"claimedTitle": "돌보던 분이 이제 자신의 계정을 갖게 되었어요",
|
||||
"claimedEnd": "돌보던 분이 자신의 건강 기록을 넘겨받았어요. 내 접근 권한은 종료되었어요.",
|
||||
"claimedRead": "돌보던 분이 자신의 건강 기록을 넘겨받았어요. 계속 볼 수는 있지만 더 이상 변경할 수 없어요.",
|
||||
"claimedManage": "돌보던 분이 자신의 건강 기록을 넘겨받았어요. 그분이 다르게 결정할 때까지 계속 관리할 수 있어요.",
|
||||
"changedTitle": "건강 기록에 대한 접근 권한이 변경되었어요",
|
||||
"changedEnd": "돌보던 분의 건강 기록에 대한 접근 권한이 종료되었어요.",
|
||||
"changedRead": "이제 돌보던 분의 건강 기록을 볼 수 있지만 변경할 수는 없어요.",
|
||||
"changedManage": "돌보던 분의 건강 기록을 관리할 수 있어요."
|
||||
}
|
||||
}
|
||||
},
|
||||
"i18n": {
|
||||
|
||||
+12
-1
@@ -531,6 +531,7 @@
|
||||
"signOut": "Wyloguj i kontynuuj",
|
||||
"guardiansTitle": "Kto na razie zachowuje dostęp",
|
||||
"guardiansHint": "Zaproponowane przez osoby, które prowadziły Twoją kartę. Zdecydujesz zaraz po zalogowaniu.",
|
||||
"guardianUnnamed": "Opiekun",
|
||||
"usernameHint": "Od 3 do 30 liter, cyfr, _ lub -.",
|
||||
"usernameReserved": "Nazwy użytkownika zaczynające się od managed- są zarezerwowane.",
|
||||
"emailHint": "Wymagany, żebyś mógł wrócić, jeśli zapomnisz hasła.",
|
||||
@@ -8445,7 +8446,17 @@
|
||||
"changedTitle": "{name} zmienia Twój dostęp",
|
||||
"changedEnd": "Twój dostęp do karty zdrowia osoby {name} się zakończył.",
|
||||
"changedRead": "Możesz teraz oglądać kartę zdrowia osoby {name}, ale nie zmieniać jej.",
|
||||
"changedManage": "Możesz zarządzać kartą zdrowia osoby {name}."
|
||||
"changedManage": "Możesz zarządzać kartą zdrowia osoby {name}.",
|
||||
"unnamed": {
|
||||
"claimedTitle": "Osoba pod Twoją opieką ma teraz własne konto",
|
||||
"claimedEnd": "Osoba pod Twoją opieką przejęła swoją dokumentację zdrowotną. Twój dostęp wygasł.",
|
||||
"claimedRead": "Osoba pod Twoją opieką przejęła swoją dokumentację zdrowotną. Nadal możesz ją przeglądać, ale nie możesz już jej zmieniać.",
|
||||
"claimedManage": "Osoba pod Twoją opieką przejęła swoją dokumentację zdrowotną. Nadal możesz nią zarządzać, dopóki nie zdecyduje inaczej.",
|
||||
"changedTitle": "Twój dostęp do dokumentacji zdrowotnej się zmienił",
|
||||
"changedEnd": "Twój dostęp do dokumentacji zdrowotnej osoby pod Twoją opieką wygasł.",
|
||||
"changedRead": "Możesz teraz przeglądać dokumentację zdrowotną osoby pod Twoją opieką, ale nie możesz jej zmieniać.",
|
||||
"changedManage": "Możesz zarządzać dokumentacją zdrowotną osoby pod Twoją opieką."
|
||||
}
|
||||
}
|
||||
},
|
||||
"i18n": {
|
||||
|
||||
@@ -92,11 +92,11 @@ export const POST = apiHandler(async (request: NextRequest) => {
|
||||
if (result.changed.length > 0) {
|
||||
const self = await prisma.user.findUnique({
|
||||
where: { id: user.id },
|
||||
select: { displayName: true, username: true },
|
||||
select: { displayName: true },
|
||||
});
|
||||
notifyGuardiansOfHandover(
|
||||
"changed",
|
||||
self?.displayName?.trim() || self?.username || user.username,
|
||||
self?.displayName ?? null,
|
||||
result.changed,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -138,11 +138,7 @@ export const POST = apiHandler(async (request: NextRequest) => {
|
||||
details: { method: "password" },
|
||||
});
|
||||
|
||||
notifyGuardiansOfHandover(
|
||||
"claimed",
|
||||
claimed.displayName ?? claimed.username,
|
||||
claimed.guardians,
|
||||
);
|
||||
notifyGuardiansOfHandover("claimed", claimed.displayName, claimed.guardians);
|
||||
|
||||
annotate({
|
||||
action: { name: "profile_claim.complete" },
|
||||
|
||||
@@ -219,15 +219,15 @@ export function ClaimProfile({ token }: { token: string | null }) {
|
||||
</h2>
|
||||
{data.guardians.length > 0 ? (
|
||||
<ul className="divide-y rounded-lg border">
|
||||
{data.guardians.map((guardian) => (
|
||||
{data.guardians.map((guardian, index) => (
|
||||
<li
|
||||
key={guardian.grantId}
|
||||
key={index}
|
||||
data-slot="claim-guardian"
|
||||
data-proposal={guardian.proposal}
|
||||
className="flex items-center justify-between gap-3 px-3 py-2"
|
||||
>
|
||||
<span className="min-w-0 truncate text-sm">
|
||||
{guardian.displayName}
|
||||
{guardian.displayName ?? t("auth.claim.guardianUnnamed")}
|
||||
</span>
|
||||
<span className="text-muted-foreground shrink-0 text-xs">
|
||||
{accessLabel(t, guardian.proposal)}
|
||||
|
||||
@@ -18,41 +18,69 @@ import { dispatchLocalisedNotification } from "@/lib/notifications/dispatch-loca
|
||||
function accessMessageKey(
|
||||
kind: "claimed" | "changed",
|
||||
access: HandoverAccess,
|
||||
named: boolean,
|
||||
): string {
|
||||
if (kind === "claimed") {
|
||||
switch (access) {
|
||||
case "end":
|
||||
return "notifications.handover.claimedEnd";
|
||||
return named
|
||||
? "notifications.handover.claimedEnd"
|
||||
: "notifications.handover.unnamed.claimedEnd";
|
||||
case "read":
|
||||
return "notifications.handover.claimedRead";
|
||||
return named
|
||||
? "notifications.handover.claimedRead"
|
||||
: "notifications.handover.unnamed.claimedRead";
|
||||
case "manage":
|
||||
return "notifications.handover.claimedManage";
|
||||
return named
|
||||
? "notifications.handover.claimedManage"
|
||||
: "notifications.handover.unnamed.claimedManage";
|
||||
}
|
||||
}
|
||||
switch (access) {
|
||||
case "end":
|
||||
return "notifications.handover.changedEnd";
|
||||
return named
|
||||
? "notifications.handover.changedEnd"
|
||||
: "notifications.handover.unnamed.changedEnd";
|
||||
case "read":
|
||||
return "notifications.handover.changedRead";
|
||||
return named
|
||||
? "notifications.handover.changedRead"
|
||||
: "notifications.handover.unnamed.changedRead";
|
||||
case "manage":
|
||||
return "notifications.handover.changedManage";
|
||||
return named
|
||||
? "notifications.handover.changedManage"
|
||||
: "notifications.handover.unnamed.changedManage";
|
||||
}
|
||||
}
|
||||
|
||||
function titleKey(kind: "claimed" | "changed", named: boolean): string {
|
||||
if (kind === "claimed") {
|
||||
return named
|
||||
? "notifications.handover.claimedTitle"
|
||||
: "notifications.handover.unnamed.claimedTitle";
|
||||
}
|
||||
return named
|
||||
? "notifications.handover.changedTitle"
|
||||
: "notifications.handover.unnamed.changedTitle";
|
||||
}
|
||||
|
||||
/**
|
||||
* `recordName` is the display name the Guardians knew the record by, or null.
|
||||
* Never the login name: the new owner chose it to sign in with, and a
|
||||
* notification to somebody else is no place for it. Without a display name
|
||||
* the sentences speak of "the person you looked after".
|
||||
*/
|
||||
export function notifyGuardiansOfHandover(
|
||||
kind: "claimed" | "changed",
|
||||
recordName: string,
|
||||
recordName: string | null,
|
||||
guardians: { guardianId: string; access: HandoverAccess }[],
|
||||
): void {
|
||||
const name = recordName?.trim() || null;
|
||||
for (const guardian of guardians) {
|
||||
void dispatchLocalisedNotification({
|
||||
userId: guardian.guardianId,
|
||||
titleKey:
|
||||
kind === "claimed"
|
||||
? "notifications.handover.claimedTitle"
|
||||
: "notifications.handover.changedTitle",
|
||||
messageKey: accessMessageKey(kind, guardian.access),
|
||||
params: { name: recordName },
|
||||
titleKey: titleKey(kind, name !== null),
|
||||
messageKey: accessMessageKey(kind, guardian.access, name !== null),
|
||||
...(name ? { params: { name } } : {}),
|
||||
}).catch((err: unknown) => {
|
||||
getEvent()?.addWarning(
|
||||
`handover notification failed: ${err instanceof Error ? err.message : String(err)}`,
|
||||
|
||||
@@ -432,9 +432,13 @@ function isOpen(
|
||||
export interface HandoverPreview {
|
||||
displayName: string | null;
|
||||
expiresAt: Date;
|
||||
/**
|
||||
* Display names only: the holder of the link has proved nothing beyond
|
||||
* holding it, so no grant id and no login name (a Guardian without a
|
||||
* display name is null, and the page says "a guardian").
|
||||
*/
|
||||
guardians: {
|
||||
grantId: string;
|
||||
displayName: string;
|
||||
displayName: string | null;
|
||||
proposal: HandoverAccess;
|
||||
}[];
|
||||
}
|
||||
@@ -443,8 +447,8 @@ export interface HandoverPreview {
|
||||
* What a link would hand over, or null for every kind of unusable link.
|
||||
*
|
||||
* Deliberately minimal: the profile's name, the expiry, and who keeps which
|
||||
* access. No health data, no date of birth, no usernames — the person holding
|
||||
* the link has not proved anything yet beyond holding it.
|
||||
* access. No health data, no date of birth, no usernames and no grant ids —
|
||||
* the person holding the link has not proved anything yet beyond holding it.
|
||||
*/
|
||||
export async function previewHandover(
|
||||
rawToken: string,
|
||||
@@ -468,7 +472,7 @@ export async function previewHandover(
|
||||
where: { grantorId: row.profileId, ...activeGuardianWhere(now) },
|
||||
select: {
|
||||
id: true,
|
||||
grantee: { select: { displayName: true, username: true } },
|
||||
grantee: { select: { displayName: true } },
|
||||
},
|
||||
orderBy: { createdAt: "asc" },
|
||||
});
|
||||
@@ -476,8 +480,7 @@ export async function previewHandover(
|
||||
displayName: profile.displayName,
|
||||
expiresAt: row.expiresAt,
|
||||
guardians: guardians.map((g) => ({
|
||||
grantId: g.id,
|
||||
displayName: nameOf(g.grantee),
|
||||
displayName: g.grantee.displayName?.trim() || null,
|
||||
proposal: proposalFor(state, g.id),
|
||||
})),
|
||||
};
|
||||
|
||||
@@ -718,8 +718,12 @@ const claimPreview = z
|
||||
guardians: z
|
||||
.array(
|
||||
z.object({
|
||||
grantId: z.string(),
|
||||
displayName: z.string(),
|
||||
displayName: z
|
||||
.string()
|
||||
.nullable()
|
||||
.describe(
|
||||
"The guardian's display name; null when they set none. Never a login name or an id: the holder of the link has proved nothing yet.",
|
||||
),
|
||||
proposal: claimAccess,
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -18,8 +18,7 @@ export interface ProfileClaimPreview {
|
||||
displayName: string | null;
|
||||
expiresAt: string;
|
||||
guardians: {
|
||||
grantId: string;
|
||||
displayName: string;
|
||||
displayName: string | null;
|
||||
proposal: HandoverAccess;
|
||||
}[];
|
||||
}
|
||||
|
||||
@@ -88,14 +88,25 @@ vi.mock("@/lib/password-breach-check", () => ({
|
||||
|
||||
// The outbox. Guardians are told on their own channels; what is sent, to
|
||||
// whom, is read back from here instead of from a mail server.
|
||||
const sent: { userId: string; titleKey: string; messageKey: string }[] = [];
|
||||
const sent: {
|
||||
userId: string;
|
||||
titleKey: string;
|
||||
messageKey: string;
|
||||
params?: Record<string, unknown>;
|
||||
}[] = [];
|
||||
vi.mock("@/lib/notifications/dispatch-localised", () => ({
|
||||
dispatchLocalisedNotification: vi.fn(
|
||||
async (opts: { userId: string; titleKey: string; messageKey: string }) => {
|
||||
async (opts: {
|
||||
userId: string;
|
||||
titleKey: string;
|
||||
messageKey: string;
|
||||
params?: Record<string, unknown>;
|
||||
}) => {
|
||||
sent.push({
|
||||
userId: opts.userId,
|
||||
titleKey: opts.titleKey,
|
||||
messageKey: opts.messageKey,
|
||||
...(opts.params ? { params: opts.params } : {}),
|
||||
});
|
||||
},
|
||||
),
|
||||
@@ -410,6 +421,58 @@ describe("the handover link", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("what the link shows before anyone signs in", () => {
|
||||
it("names each guardian by display name only, never a login name or a grant id", async () => {
|
||||
const { creator, second, profile, creatorGrant, secondGrant } =
|
||||
await household();
|
||||
await getPrismaClient().user.update({
|
||||
where: { id: creator.id },
|
||||
data: { displayName: "Alex Example" },
|
||||
});
|
||||
signIn(creator);
|
||||
const token = await mintToken(profile.id, [
|
||||
{ grantId: creatorGrant.id, proposal: "manage" },
|
||||
{ grantId: secondGrant.id, proposal: "read" },
|
||||
]);
|
||||
signOut();
|
||||
|
||||
const response = await preview(token);
|
||||
expect(response.status).toBe(200);
|
||||
const body = await response.json();
|
||||
const text = JSON.stringify(body);
|
||||
expect(text).not.toContain(creatorGrant.id);
|
||||
expect(text).not.toContain(secondGrant.id);
|
||||
expect(text).not.toContain(creator.username);
|
||||
expect(text).not.toContain(second.username);
|
||||
expect(body.data.guardians).toEqual([
|
||||
{ displayName: "Alex Example", proposal: "manage" },
|
||||
{ displayName: null, proposal: "read" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("tells the guardians without the new login name when the profile has no display name", async () => {
|
||||
const { creator, profile, creatorGrant } = await household();
|
||||
signIn(creator);
|
||||
const token = await mintToken(profile.id, [
|
||||
{ grantId: creatorGrant.id, proposal: "end" },
|
||||
]);
|
||||
signOut();
|
||||
await getPrismaClient().user.update({
|
||||
where: { id: profile.id },
|
||||
data: { displayName: null },
|
||||
});
|
||||
const response = await claim(token, { username: "robinlogin" });
|
||||
expect(response.status).toBe(201);
|
||||
await vi.waitFor(() => expect(sent.length).toBeGreaterThan(0));
|
||||
for (const message of sent) {
|
||||
expect(message.titleKey).toBe(
|
||||
"notifications.handover.unnamed.claimedTitle",
|
||||
);
|
||||
expect(message.params).toBeUndefined();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("minting the link", () => {
|
||||
it("needs a fresh second factor", async () => {
|
||||
const creator = await person("stale", false);
|
||||
@@ -618,11 +681,14 @@ describe("claiming the profile", () => {
|
||||
userId: creator.id,
|
||||
titleKey: "notifications.handover.claimedTitle",
|
||||
messageKey: "notifications.handover.claimedManage",
|
||||
// The name the Guardians gave the profile, never the new login.
|
||||
params: { name: "Robin" },
|
||||
},
|
||||
{
|
||||
userId: second.id,
|
||||
titleKey: "notifications.handover.claimedTitle",
|
||||
messageKey: "notifications.handover.claimedEnd",
|
||||
params: { name: "Robin" },
|
||||
},
|
||||
]),
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user