Fix copy, colour and frame details across the record pages

- Achievements: '1 Tag', not '1 Tage'; point totals grouped; whole
  descriptions instead of a clamp that hid the target at the end.
- Illness: the episode note label is translated, day notes read in the
  content colour.
- Coach history: conversation titles read in the content colour.
- Anamnesis and mental wellbeing: drop a second page gutter the shell
  already provides, and the nested main landmark.
- Privacy: repair words broken by a stray hyphen and space.
- Cycle: the ring caption reads 'Tag 9 deines Zyklus' as one phrase.
- Practitioners: the add button says 'Hinzufügen' like every other page;
  a shorter search placeholder fits a phone.
- Empty state cards inset their content from their border, so a phone's
  full-width action no longer runs into the frame.
This commit is contained in:
Marc-André Bombeck committed 2026-10-08 16:34:54 +02:00
1 parent 9fd43623dd
commit 3e1f630fa1
17 files changed
+133 -82

No files matched your search

+8 -1
View File
@@ -271,7 +271,14 @@ export function CycleView() {
days: verdict.overdueDays,
})
: t("cycle.ring.periodOverdue")
: t("cycle.ring.caption")}
: // The ring shows the bare number; the caption under it
// reads as a whole phrase ("Tag 9 deines Zyklus"), not
// a fragment hanging off the ring.
verdict?.dayOfCycle != null
? t("cycle.insightsSummary.currentDay", {
day: verdict.dayOfCycle,
})
: t("cycle.ring.caption")}
</p>
) : null}
{/* Log CTA — first period when no cycle exists yet, next period
@@ -86,7 +86,8 @@ function DayLogRow({ log }: { log: IllnessDayLogDTO }) {
) : null}
{log.note ? (
<p className="text-muted-foreground text-sm whitespace-pre-wrap">
// User-written text reads in the content colour, never muted.
<p className="text-foreground text-sm whitespace-pre-wrap">
{log.note}
</p>
) : null}
@@ -98,7 +98,7 @@ export function PractitionerList({ enabled = true }: { enabled?: boolean }) {
onClick={() => openSheet("new")}
>
<Plus className="size-4" aria-hidden />
{t("practitioners.add")}
{t("common.add")}
</Button>
) : null;
+4 -2
View File
@@ -130,7 +130,9 @@ export function ProfileSummaryPage() {
});
return (
<main className="container mx-auto max-w-3xl space-y-6 px-4 py-6">
// The shell owns the page frame (gutter, width, top space) and the
// <main> landmark; this page only sets its rhythm.
<div className="space-y-6">
<PageHeader title={t("settings.sections.anamnesis.title")} />
{summary.isPending ? (
<p className="text-muted-foreground text-sm" aria-live="polite">
@@ -141,6 +143,6 @@ export function ProfileSummaryPage() {
<QueryErrorCard onRetry={() => void summary.refetch()} />
) : null}
{summary.data ? <ProfileSummary summary={summary.data} /> : null}
</main>
</div>
);
}
@@ -46,4 +46,13 @@ describe("<EmptyState>", () => {
expect(card).toContain("border-dashed");
expect(plain).not.toContain("border-dashed");
});
it("insets the card variant's content from its border, not the plain one", () => {
const card = renderToStaticMarkup(<EmptyState title="leer" />);
const plain = renderToStaticMarkup(
<EmptyState title="leer" variant="plain" />,
);
expect(card).toMatch(/class="[^"]*\bpx-4\b/);
expect(plain).not.toMatch(/\bpx-4\b/);
});
});
+3
View File
@@ -64,6 +64,9 @@ function EmptyState({
className={cn(
"flex flex-col items-center justify-center text-center",
size === "compact" ? "gap-2 py-4" : "gap-3 py-8",
// The card variant paints a border, so its content insets from it:
// a full-width phone CTA otherwise ran edge to edge into the frame.
variant === "card" && "px-4",
)}
>
{icon ? (