mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 14:16:12 +00:00
fix(documents): one cap line for both pickers, with a search-aware hint
The vault picker and the Coach's attachment picker each wrote their own "showing the newest 50" line, which the UI standards leave to `ListCapNotice`, and with a search running past fifty matches the line still said "Search to find an older document". Both pickers now share `DocumentPickerCapNotice` on `ListCapNotice`, which takes the sentence to show, and a running search asks for a narrower one.
This commit is contained in:
1 parent
92e1e24599
commit
671245c782
15 files changed
+83
-37
No files matched your search
+2
-1
@@ -12490,7 +12490,8 @@
|
||||
"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."
|
||||
"capped": "Hier stehen die neuesten {count}. Ältere Dokumente findest du über die Suche.",
|
||||
"cappedSearching": "Hier stehen die neuesten {count} Treffer. Ältere findest du mit einer genaueren Suche."
|
||||
}
|
||||
},
|
||||
"selfDescription": {
|
||||
|
||||
+2
-1
@@ -12490,7 +12490,8 @@
|
||||
"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."
|
||||
"capped": "Showing the newest {count}. Search to find an older document.",
|
||||
"cappedSearching": "Showing the newest {count} matches. Narrow the search to find an older one."
|
||||
}
|
||||
},
|
||||
"selfDescription": {
|
||||
|
||||
+2
-1
@@ -12490,7 +12490,8 @@
|
||||
"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."
|
||||
"capped": "Se muestran los {count} más recientes. Usa la búsqueda para encontrar un documento más antiguo.",
|
||||
"cappedSearching": "Se muestran las {count} coincidencias más recientes. Afina la búsqueda para encontrar una más antigua."
|
||||
}
|
||||
},
|
||||
"selfDescription": {
|
||||
|
||||
+2
-1
@@ -12490,7 +12490,8 @@
|
||||
"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."
|
||||
"capped": "Les {count} plus récents sont affichés. Utilisez la recherche pour trouver un document plus ancien.",
|
||||
"cappedSearching": "Les {count} résultats les plus récents sont affichés. Affinez la recherche pour en trouver un plus ancien."
|
||||
}
|
||||
},
|
||||
"selfDescription": {
|
||||
|
||||
+2
-1
@@ -12490,7 +12490,8 @@
|
||||
"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."
|
||||
"capped": "Sono mostrati i {count} più recenti. Usa la ricerca per trovare un documento più vecchio.",
|
||||
"cappedSearching": "Sono mostrati i {count} risultati più recenti. Restringi la ricerca per trovarne uno più vecchio."
|
||||
}
|
||||
},
|
||||
"selfDescription": {
|
||||
|
||||
+2
-1
@@ -12490,7 +12490,8 @@
|
||||
"purgeConfirmBody": "대기 기간이 끝나기 전에 지금 삭제되며 복구할 수 없습니다."
|
||||
},
|
||||
"picker": {
|
||||
"capped": "최신 {count}개를 보여 줘요. 이전 문서는 검색으로 찾아 주세요."
|
||||
"capped": "최신 {count}개를 보여 줘요. 이전 문서는 검색으로 찾아 주세요.",
|
||||
"cappedSearching": "최근 검색 결과 {count}개를 보여 줘요. 이전 문서는 검색어를 좁혀서 찾아 주세요."
|
||||
}
|
||||
},
|
||||
"selfDescription": {
|
||||
|
||||
+2
-1
@@ -12490,7 +12490,8 @@
|
||||
"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."
|
||||
"capped": "De nieuwste {count} staan hier. Zoek om een ouder document te vinden.",
|
||||
"cappedSearching": "De nieuwste {count} resultaten staan hier. Verfijn de zoekopdracht om een ouder document te vinden."
|
||||
}
|
||||
},
|
||||
"selfDescription": {
|
||||
|
||||
+2
-1
@@ -12490,7 +12490,8 @@
|
||||
"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."
|
||||
"capped": "Widać {count} najnowszych. Starszy dokument znajdziesz przez wyszukiwanie.",
|
||||
"cappedSearching": "Widać {count} najnowszych wyników. Zawęź wyszukiwanie, aby znaleźć starszy."
|
||||
}
|
||||
},
|
||||
"selfDescription": {
|
||||
|
||||
+2
-1
@@ -12490,7 +12490,8 @@
|
||||
"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."
|
||||
"capped": "Mostrando os {count} mais recentes. Use a busca para encontrar um documento mais antigo.",
|
||||
"cappedSearching": "Mostrando os {count} resultados mais recentes. Refine a busca para encontrar um mais antigo."
|
||||
}
|
||||
},
|
||||
"selfDescription": {
|
||||
|
||||
+2
-1
@@ -12490,7 +12490,8 @@
|
||||
"purgeConfirmBody": "Он будет удалён сейчас, а не в конце периода ожидания, и его нельзя будет восстановить."
|
||||
},
|
||||
"picker": {
|
||||
"capped": "Показаны {count} самых новых. Более старый документ найдите через поиск."
|
||||
"capped": "Показаны {count} самых новых. Более старый документ найдите через поиск.",
|
||||
"cappedSearching": "Показаны {count} самых новых совпадений. Уточните поиск, чтобы найти более старый документ."
|
||||
}
|
||||
},
|
||||
"selfDescription": {
|
||||
|
||||
@@ -37,7 +37,10 @@ vi.mock("@tanstack/react-query", () => ({
|
||||
}));
|
||||
|
||||
import { I18nProvider } from "@/lib/i18n/context";
|
||||
import { DocumentPickerSheet } from "../document-picker-sheet";
|
||||
import {
|
||||
DocumentPickerCapNotice,
|
||||
DocumentPickerSheet,
|
||||
} from "../document-picker-sheet";
|
||||
|
||||
function render() {
|
||||
return renderToStaticMarkup(
|
||||
@@ -60,12 +63,22 @@ 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('data-slot="list-cap-notice"');
|
||||
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"');
|
||||
expect(render()).not.toContain('data-slot="list-cap-notice"');
|
||||
});
|
||||
|
||||
it("asks for a narrower search, not a search, while one runs", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<I18nProvider initialLocale="en">
|
||||
<DocumentPickerCapNotice cut count={50} searching />
|
||||
</I18nProvider>,
|
||||
);
|
||||
expect(html).toContain("Narrow the search");
|
||||
expect(html).not.toContain("Search to find");
|
||||
});
|
||||
});
|
||||
@@ -18,6 +18,7 @@ import { Check, Search } from "lucide-react";
|
||||
import { useEffect, useRef, useState, type ReactNode } from "react";
|
||||
|
||||
import { useDocumentDate } from "@/components/documents/use-document-date";
|
||||
import { ListCapNotice } from "@/components/ui/list-cap-notice";
|
||||
import { DOCUMENT_KIND_ICONS } from "@/components/documents/document-kind-meta";
|
||||
import { EmptyState } from "@/components/ui/empty-state";
|
||||
import { Input } from "@/components/ui/input";
|
||||
@@ -191,17 +192,40 @@ 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}
|
||||
<DocumentPickerCapNotice
|
||||
cut={cut && !list.isError}
|
||||
count={documents.length}
|
||||
searching={query !== ""}
|
||||
/>
|
||||
</div>
|
||||
</ResponsiveSheet>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The line under a picker list cut at one page: the search reaches the rest,
|
||||
* and while one runs, a narrower one does. Shared by the vault picker and
|
||||
* the Coach's attachment picker.
|
||||
*/
|
||||
export function DocumentPickerCapNotice({
|
||||
cut,
|
||||
count,
|
||||
searching,
|
||||
}: {
|
||||
cut: boolean;
|
||||
count: number;
|
||||
searching: boolean;
|
||||
}) {
|
||||
const { t } = useTranslations();
|
||||
return (
|
||||
<ListCapNotice
|
||||
cap={{ truncated: cut, limit: count }}
|
||||
message={t(
|
||||
searching
|
||||
? "documents.picker.cappedSearching"
|
||||
: "documents.picker.capped",
|
||||
{ count: String(count) },
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -56,14 +56,14 @@ describe("AttachmentPicker", () => {
|
||||
it("says the list is cut, and how to reach the rest", () => {
|
||||
page.nextCursor = "d49";
|
||||
const html = render();
|
||||
expect(html).toContain('data-slot="coach-attachment-picker-capped"');
|
||||
expect(html).toContain('data-slot="list-cap-notice"');
|
||||
expect(html).toContain("Showing the newest 50");
|
||||
});
|
||||
|
||||
it("says nothing when every document is listed", () => {
|
||||
page.nextCursor = null;
|
||||
const html = render();
|
||||
expect(html).not.toContain('data-slot="coach-attachment-picker-capped"');
|
||||
expect(html).not.toContain('data-slot="list-cap-notice"');
|
||||
expect(
|
||||
html.match(/data-slot="coach-attachment-picker-item"/g),
|
||||
).toHaveLength(3);
|
||||
|
||||
@@ -15,6 +15,7 @@ import { cn } from "@/lib/utils";
|
||||
import type { InboundDocumentDto } from "@/lib/validations/inbound-documents";
|
||||
|
||||
import { DOCUMENT_KIND_ICONS } from "@/components/documents/document-kind-meta";
|
||||
import { DocumentPickerCapNotice } from "@/components/documents/document-picker-sheet";
|
||||
import { QueryErrorRow } from "@/components/ui/query-error-row";
|
||||
import { EmptyState } from "@/components/ui/empty-state";
|
||||
|
||||
@@ -266,16 +267,11 @@ export function AttachmentPicker({
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
{cut && !list.isError ? (
|
||||
<p
|
||||
data-slot="coach-attachment-picker-capped"
|
||||
className="text-muted-foreground text-xs"
|
||||
>
|
||||
{t("documents.picker.capped", {
|
||||
count: String(list.data?.documents.length ?? 0),
|
||||
})}
|
||||
</p>
|
||||
) : null}
|
||||
<DocumentPickerCapNotice
|
||||
cut={cut && !list.isError}
|
||||
count={list.data?.documents.length ?? 0}
|
||||
searching={query !== ""}
|
||||
/>
|
||||
</div>
|
||||
</ResponsiveSheet>
|
||||
);
|
||||
|
||||
@@ -8,14 +8,17 @@ import { cn } from "@/lib/utils";
|
||||
* The line under a list the server cut at its ceiling. Renders nothing for a
|
||||
* complete list, so a surface passes the cap it read (`apiGetList`) and
|
||||
* never decides on its own whether to show it. Meta tier: one muted sentence
|
||||
* at the `text-xs` floor.
|
||||
* at the `text-xs` floor. `message` replaces the generic sentence where the
|
||||
* surface can say how to reach the rest (a search).
|
||||
*/
|
||||
export function ListCapNotice({
|
||||
cap,
|
||||
className,
|
||||
message,
|
||||
}: {
|
||||
cap: ListCap | null | undefined;
|
||||
className?: string;
|
||||
message?: string;
|
||||
}) {
|
||||
const { t } = useTranslations();
|
||||
if (!cap?.truncated) return null;
|
||||
@@ -24,7 +27,7 @@ export function ListCapNotice({
|
||||
data-slot="list-cap-notice"
|
||||
className={cn("text-muted-foreground text-xs", className)}
|
||||
>
|
||||
{t("common.listCapped", { count: String(cap.limit) })}
|
||||
{message ?? t("common.listCapped", { count: String(cap.limit) })}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user