mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 14:16:12 +00:00
fix(documents): say when the document picker list is cut
The picker for linking documents to an illness and for choosing documents for a share link reads the newest fifty and dropped the route's nextCursor, so with a longer vault an older report seemed not to exist unless one already knew to search for it. A cut list now ends with a line that says how many are shown and that the search reaches the rest. Paging was left out: the coach attachment picker shares the picker's cache key with a single-page read.
This commit is contained in:
1 parent
d204b4344b
commit
53f4ecc2c5
13 files changed
+117
-3
No files matched your search
@@ -12491,6 +12491,9 @@
|
||||
"purgeFailed": "Das Dokument konnte nicht gelöscht werden.",
|
||||
"purgeConfirmTitle": "Dieses Dokument endgültig löschen?",
|
||||
"purgeConfirmBody": "Es wird jetzt entfernt statt am Ende der Wartezeit und lässt sich nicht wiederherstellen."
|
||||
},
|
||||
"picker": {
|
||||
"capped": "Hier stehen die neuesten {count}. Ältere Dokumente findest du über die Suche."
|
||||
}
|
||||
},
|
||||
"selfDescription": {
|
||||
|
||||
@@ -12491,6 +12491,9 @@
|
||||
"purgeFailed": "Couldn't delete the document.",
|
||||
"purgeConfirmTitle": "Delete this document for good?",
|
||||
"purgeConfirmBody": "It is removed now instead of at the end of the waiting period, and cannot be restored."
|
||||
},
|
||||
"picker": {
|
||||
"capped": "Showing the newest {count}. Search to find an older document."
|
||||
}
|
||||
},
|
||||
"selfDescription": {
|
||||
|
||||
@@ -12491,6 +12491,9 @@
|
||||
"purgeFailed": "No se pudo eliminar el documento.",
|
||||
"purgeConfirmTitle": "¿Eliminar este documento definitivamente?",
|
||||
"purgeConfirmBody": "Se elimina ahora en lugar de al final del periodo de espera y no se puede restaurar."
|
||||
},
|
||||
"picker": {
|
||||
"capped": "Se muestran los {count} más recientes. Usa la búsqueda para encontrar un documento más antiguo."
|
||||
}
|
||||
},
|
||||
"selfDescription": {
|
||||
|
||||
@@ -12491,6 +12491,9 @@
|
||||
"purgeFailed": "Impossible de supprimer le document.",
|
||||
"purgeConfirmTitle": "Supprimer ce document définitivement ?",
|
||||
"purgeConfirmBody": "Il est supprimé maintenant plutôt qu'à la fin du délai d'attente et ne peut pas être restauré."
|
||||
},
|
||||
"picker": {
|
||||
"capped": "Les {count} plus récents sont affichés. Utilisez la recherche pour trouver un document plus ancien."
|
||||
}
|
||||
},
|
||||
"selfDescription": {
|
||||
|
||||
@@ -12491,6 +12491,9 @@
|
||||
"purgeFailed": "Impossibile eliminare il documento.",
|
||||
"purgeConfirmTitle": "Eliminare definitivamente questo documento?",
|
||||
"purgeConfirmBody": "Viene rimosso ora anziché alla fine del periodo di attesa e non può essere ripristinato."
|
||||
},
|
||||
"picker": {
|
||||
"capped": "Sono mostrati i {count} più recenti. Usa la ricerca per trovare un documento più vecchio."
|
||||
}
|
||||
},
|
||||
"selfDescription": {
|
||||
|
||||
@@ -12491,6 +12491,9 @@
|
||||
"purgeFailed": "문서를 삭제하지 못했어요.",
|
||||
"purgeConfirmTitle": "이 문서를 완전히 삭제할까요?",
|
||||
"purgeConfirmBody": "대기 기간이 끝나기 전에 지금 삭제되며 복구할 수 없습니다."
|
||||
},
|
||||
"picker": {
|
||||
"capped": "최신 {count}개를 보여 줘요. 이전 문서는 검색으로 찾아 주세요."
|
||||
}
|
||||
},
|
||||
"selfDescription": {
|
||||
|
||||
@@ -12491,6 +12491,9 @@
|
||||
"purgeFailed": "Het document kon niet worden verwijderd.",
|
||||
"purgeConfirmTitle": "Dit document definitief verwijderen?",
|
||||
"purgeConfirmBody": "Het wordt nu verwijderd in plaats van aan het einde van de wachttijd en kan niet worden hersteld."
|
||||
},
|
||||
"picker": {
|
||||
"capped": "De nieuwste {count} staan hier. Zoek om een ouder document te vinden."
|
||||
}
|
||||
},
|
||||
"selfDescription": {
|
||||
|
||||
@@ -12491,6 +12491,9 @@
|
||||
"purgeFailed": "Nie udało się usunąć dokumentu.",
|
||||
"purgeConfirmTitle": "Usunąć ten dokument na stałe?",
|
||||
"purgeConfirmBody": "Zostanie usunięty teraz zamiast po okresie oczekiwania i nie będzie można go przywrócić."
|
||||
},
|
||||
"picker": {
|
||||
"capped": "Widać {count} najnowszych. Starszy dokument znajdziesz przez wyszukiwanie."
|
||||
}
|
||||
},
|
||||
"selfDescription": {
|
||||
|
||||
@@ -12491,6 +12491,9 @@
|
||||
"purgeFailed": "Não foi possível excluir o documento.",
|
||||
"purgeConfirmTitle": "Excluir este documento para sempre?",
|
||||
"purgeConfirmBody": "Ele é removido agora, e não no fim do período de espera, e não pode ser restaurado."
|
||||
},
|
||||
"picker": {
|
||||
"capped": "Mostrando os {count} mais recentes. Use a busca para encontrar um documento mais antigo."
|
||||
}
|
||||
},
|
||||
"selfDescription": {
|
||||
|
||||
@@ -12491,6 +12491,9 @@
|
||||
"purgeFailed": "Не удалось удалить документ.",
|
||||
"purgeConfirmTitle": "Удалить этот документ навсегда?",
|
||||
"purgeConfirmBody": "Он будет удалён сейчас, а не в конце периода ожидания, и его нельзя будет восстановить."
|
||||
},
|
||||
"picker": {
|
||||
"capped": "Показаны {count} самых новых. Более старый документ найдите через поиск."
|
||||
}
|
||||
},
|
||||
"selfDescription": {
|
||||
|
||||
@@ -83,7 +83,7 @@ const ALLOWLIST: readonly Allowed[] = [
|
||||
file: "components/documents/document-picker-sheet.tsx",
|
||||
limit: 50,
|
||||
reason:
|
||||
"Search-backed picker (episode links and share links): the query narrows the whole document vault server-side.",
|
||||
"Search-backed picker (episode links and share links): the query narrows the whole document vault server-side, and a cut list says so and points to the search.",
|
||||
},
|
||||
{
|
||||
file: "hooks/use-mood-day.ts",
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* The document picker reads one page of the vault, the newest fifty. With a
|
||||
* longer vault it listed those and said nothing, so an older report seemed
|
||||
* not to exist unless one already knew to search for it.
|
||||
*
|
||||
* ResponsiveSheet portals its body, which static markup does not render, so it
|
||||
* is mocked down to a wrapper that always shows its children.
|
||||
*/
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
|
||||
const page = vi.hoisted(() => ({ nextCursor: null as string | null }));
|
||||
|
||||
vi.mock("@/components/ui/responsive-sheet", () => ({
|
||||
ResponsiveSheet: ({ children }: { children: React.ReactNode }) => (
|
||||
<div data-slot="mock-sheet">{children}</div>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("@tanstack/react-query", () => ({
|
||||
useQuery: () => ({
|
||||
data: {
|
||||
// A cut page is a full one.
|
||||
documents: Array.from({ length: page.nextCursor ? 50 : 3 }, (_, i) => ({
|
||||
id: `d${i}`,
|
||||
kind: "LAB_RESULT",
|
||||
title: `Blood panel ${i}`,
|
||||
filename: null,
|
||||
documentDate: "2026-10-01",
|
||||
createdAt: "2026-10-01T08:00:00.000Z",
|
||||
})),
|
||||
nextCursor: page.nextCursor,
|
||||
},
|
||||
isPending: false,
|
||||
isError: false,
|
||||
}),
|
||||
}));
|
||||
|
||||
import { I18nProvider } from "@/lib/i18n/context";
|
||||
import { DocumentPickerSheet } from "../document-picker-sheet";
|
||||
|
||||
function render() {
|
||||
return renderToStaticMarkup(
|
||||
<I18nProvider initialLocale="en">
|
||||
<DocumentPickerSheet
|
||||
open
|
||||
onOpenChange={() => undefined}
|
||||
title="Link documents"
|
||||
description="Pick documents"
|
||||
searchPlaceholder="Search"
|
||||
emptyLabel="No documents"
|
||||
isPicked={() => false}
|
||||
onToggle={() => undefined}
|
||||
/>
|
||||
</I18nProvider>,
|
||||
);
|
||||
}
|
||||
|
||||
describe("DocumentPickerSheet", () => {
|
||||
it("says the list is cut, and how to reach the rest", () => {
|
||||
page.nextCursor = "d49";
|
||||
const html = render();
|
||||
expect(html).toContain('data-slot="document-picker-capped"');
|
||||
expect(html).toContain("Showing the newest 50");
|
||||
});
|
||||
|
||||
it("says nothing when every document is listed", () => {
|
||||
page.nextCursor = null;
|
||||
expect(render()).not.toContain('data-slot="document-picker-capped"');
|
||||
});
|
||||
});
|
||||
@@ -3,8 +3,9 @@
|
||||
/**
|
||||
* The vault document picker sheet: a `ResponsiveSheet` (bottom sheet on
|
||||
* phones) with a debounced search over the person's own vault — the same
|
||||
* recall path as the vault page, one page (50) of matches, the search input
|
||||
* as the way to an older document — and one toggle row per document.
|
||||
* recall path as the vault page, one page (50) of matches with a line that
|
||||
* says when more exist, the search input as the way to an older document —
|
||||
* and one toggle row per document.
|
||||
*
|
||||
* Two pickers used to carry this whole body as copies: linking documents to
|
||||
* an illness episode (each toggle writes at once) and choosing documents for
|
||||
@@ -97,6 +98,8 @@ export function DocumentPickerSheet({
|
||||
});
|
||||
|
||||
const documents = list.data?.documents ?? [];
|
||||
// More match than one page: say so, and that the search reaches the rest.
|
||||
const cut = Boolean(list.data?.nextCursor);
|
||||
|
||||
return (
|
||||
<ResponsiveSheet
|
||||
@@ -188,6 +191,16 @@ export function DocumentPickerSheet({
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
{cut && !list.isError ? (
|
||||
<p
|
||||
data-slot="document-picker-capped"
|
||||
className="text-muted-foreground text-xs"
|
||||
>
|
||||
{t("documents.picker.capped", {
|
||||
count: String(documents.length),
|
||||
})}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
</ResponsiveSheet>
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user