style: apply prettier across the tree

One-shot prettier pass so the new format:check gate starts from a clean
baseline. Formatting only — no behaviour change. Generator-owned and
vendored artifacts stay untouched via .prettierignore.
This commit is contained in:
Marc-André Bombeck committed 2026-06-19 22:20:31 +02:00
1 parent b3fea2ef59
commit 1f4ba5939d
1000 files changed
+12130 -8385

No files matched your search

@@ -11,10 +11,7 @@ function render(node: React.ReactNode) {
);
}
function day(
date: string,
overrides: Partial<CalendarDay> = {},
): CalendarDay {
function day(date: string, overrides: Partial<CalendarDay> = {}): CalendarDay {
return {
date,
phase: "FOLLICULAR",
@@ -29,7 +29,10 @@ describe("<FieldInfo>", () => {
// Radix renders the tooltip content lazily (portal, on open); the trigger
// therefore reports a closed state until interacted with.
const html = render(
<FieldInfo label="Cervical mucus" detail="Discharge changes across the cycle." />,
<FieldInfo
label="Cervical mucus"
detail="Discharge changes across the cycle."
/>,
);
expect(html).toContain('data-state="closed"');
});
@@ -107,7 +107,11 @@ describe("buildDayLogInput (new row → omit empties)", () => {
);
expect(input.basalBodyTempC).toBe(36.5);
expect(input.temperatureExcluded).toBe(true);
const patch = buildDayLogPatch({ ...blank(), bbt: "36.5", bbtDisturbed: true });
const patch = buildDayLogPatch({
...blank(),
bbt: "36.5",
bbtDisturbed: true,
});
expect(patch.temperatureExcluded).toBe(true);
});
@@ -145,7 +149,13 @@ describe("buildDayLogInput (new row → omit empties)", () => {
describe("section summary-badge counts (v1.17.0)", () => {
it("counts each logged symptom", () => {
expect(symptomsCount(blank())).toBe(0);
const s = { ...blank(), symptoms: new Map([["cramps", 2], ["headache", null]]) };
const s = {
...blank(),
symptoms: new Map([
["cramps", 2],
["headache", null],
]),
};
expect(symptomsCount(s)).toBe(2);
});
+8 -12
View File
@@ -131,9 +131,7 @@ export function CycleHistoryChart({
{t("cycle.history.chartTitle")}
</CardTitle>
{hasData ? (
<CardDescription>
{t("cycle.history.chartCaption")}
</CardDescription>
<CardDescription>{t("cycle.history.chartCaption")}</CardDescription>
) : null}
</CardHeader>
<CardContent className="space-y-4">
@@ -144,7 +142,10 @@ export function CycleHistoryChart({
) : (
<>
{/* Stat chips — avg / variability / regularity classification. */}
<ul className="flex flex-wrap gap-2" data-slot="cycle-history-stats">
<ul
className="flex flex-wrap gap-2"
data-slot="cycle-history-stats"
>
<StatChip
hue={REST_HUE}
label={t("cycle.history.avgLength")}
@@ -187,9 +188,7 @@ export function CycleHistoryChart({
})}
periodLabel={t("cycle.history.legendPeriodSegment")}
ovulationLabel={t("cycle.calendar.legendOvulationConfirmed")}
barTitle={(b) =>
t("cycle.history.days", { count: b.lengthDays })
}
barTitle={(b) => t("cycle.history.days", { count: b.lengthDays })}
/>
{/* Inline legend mirroring the bar segments. */}
@@ -279,8 +278,7 @@ function BarField({
const x = cx - barW / 2;
const totalH = scale(b.lengthDays);
const topY = baseY - totalH;
const periodH =
b.periodDays != null ? scale(b.periodDays) : 0;
const periodH = b.periodDays != null ? scale(b.periodDays) : 0;
const periodTopY = baseY - periodH;
const restH = totalH - periodH;
const ovuY =
@@ -353,9 +351,7 @@ function StatChip({
} & React.HTMLAttributes<HTMLLIElement>) {
return (
<li
className={cn(
"bg-muted/40 flex items-center gap-2 rounded-md px-3 py-2",
)}
className={cn("bg-muted/40 flex items-center gap-2 rounded-md px-3 py-2")}
{...rest}
>
<span
+22 -22
View File
@@ -114,28 +114,28 @@ export function CycleRingTile({ className }: { className?: string }) {
className,
)}
>
<div className="text-foreground flex items-center gap-2">
<Droplets className="h-5 w-5 shrink-0" aria-hidden="true" />
<span className="truncate text-base leading-none font-semibold">
{t("cycle.wellnessRing.label")}
</span>
</div>
<div className="flex flex-col items-center gap-1.5">
<CycleRing
dayOfCycle={wheel.dayOfCycle}
cycleLength={wheel.cycleLength}
phase={wheel.phase}
spans={wheel.spans}
animate={play}
size={120}
/>
<span
data-slot="wellness-cycle-band-word"
className="text-muted-foreground text-center text-xs"
>
{t(`cycle.phase.${wheel.phase}`)}
</span>
</div>
<div className="text-foreground flex items-center gap-2">
<Droplets className="h-5 w-5 shrink-0" aria-hidden="true" />
<span className="truncate text-base leading-none font-semibold">
{t("cycle.wellnessRing.label")}
</span>
</div>
<div className="flex flex-col items-center gap-1.5">
<CycleRing
dayOfCycle={wheel.dayOfCycle}
cycleLength={wheel.cycleLength}
phase={wheel.phase}
spans={wheel.spans}
animate={play}
size={120}
/>
<span
data-slot="wellness-cycle-band-word"
className="text-muted-foreground text-center text-xs"
>
{t(`cycle.phase.${wheel.phase}`)}
</span>
</div>
</Link>
</div>
);
+1 -5
View File
@@ -17,11 +17,7 @@ import { Switch } from "@/components/ui/switch";
import { NativeSelect } from "@/components/ui/native-select";
import { Separator } from "@/components/ui/separator";
import { useTranslations } from "@/lib/i18n/context";
import type {
CycleGoal,
CycleProfileDTO,
SecondarySymptom,
} from "./types";
import type { CycleGoal, CycleProfileDTO, SecondarySymptom } from "./types";
import { useUpdateCyclePrefs } from "./use-cycle";
/**
+39 -42
View File
@@ -171,10 +171,7 @@ export function CycleView() {
node never matched, leaving the tile stuck at the rise rule's
`opacity: 0` (invisible but still boxed). The `contents` wrapper
carries the flag without adding a layout box. */}
<div
data-revealed={play ? "true" : undefined}
className="contents"
>
<div data-revealed={play ? "true" : undefined} className="contents">
<div
data-slot="cycle-wheel-tile"
style={
@@ -187,49 +184,49 @@ export function CycleView() {
play && "wellness-tile-rise",
)}
>
{loading ? (
<div className="flex h-[220px] items-center justify-center">
<Loader2 className="text-primary h-8 w-8 animate-spin motion-reduce:animate-none" />
</div>
) : calendarError ? (
<div className="flex h-[220px] flex-col items-center justify-center gap-3 text-center">
<p className="text-muted-foreground text-sm">
{t("cycle.loadError")}
{loading ? (
<div className="flex h-[220px] items-center justify-center">
<Loader2 className="text-primary h-8 w-8 animate-spin motion-reduce:animate-none" />
</div>
) : calendarError ? (
<div className="flex h-[220px] flex-col items-center justify-center gap-3 text-center">
<p className="text-muted-foreground text-sm">
{t("cycle.loadError")}
</p>
<Button
variant="outline"
size="sm"
className="gap-1.5"
onClick={() => void calendar.refetch()}
>
<RefreshCw className="h-3.5 w-3.5" aria-hidden="true" />
{t("common.retry")}
</Button>
</div>
) : (
<CycleRing
dayOfCycle={wheel.dayOfCycle}
cycleLength={wheel.cycleLength}
phase={wheel.phase}
spans={wheel.spans}
animate={play}
/>
)}
{!calendarError ? (
<p className="text-muted-foreground text-xs">
{t("cycle.ring.caption")}
</p>
) : null}
{/* First-period CTA — only when no cycle is active yet. */}
{!loading && !calendarError && wheel.dayOfCycle == null ? (
<Button
variant="outline"
size="sm"
className="gap-1.5"
onClick={() => void calendar.refetch()}
className="mt-1 w-full"
onClick={() => openSheet(today)}
>
<RefreshCw className="h-3.5 w-3.5" aria-hidden="true" />
{t("common.retry")}
{t("cycle.ring.firstPeriodCta")}
</Button>
</div>
) : (
<CycleRing
dayOfCycle={wheel.dayOfCycle}
cycleLength={wheel.cycleLength}
phase={wheel.phase}
spans={wheel.spans}
animate={play}
/>
)}
{!calendarError ? (
<p className="text-muted-foreground text-xs">
{t("cycle.ring.caption")}
</p>
) : null}
{/* First-period CTA — only when no cycle is active yet. */}
{!loading && !calendarError && wheel.dayOfCycle == null ? (
<Button
variant="outline"
className="mt-1 w-full"
onClick={() => openSheet(today)}
>
{t("cycle.ring.firstPeriodCta")}
</Button>
) : null}
) : null}
</div>
</div>
+134 -135
View File
@@ -42,10 +42,7 @@ import {
PopoverTrigger,
} from "@/components/ui/popover";
import { ResponsiveSheet } from "@/components/ui/responsive-sheet";
import {
SheetSection,
SheetSectionCount,
} from "@/components/ui/sheet-section";
import { SheetSection, SheetSectionCount } from "@/components/ui/sheet-section";
import { useTranslations } from "@/lib/i18n/context";
import { CUSTOM_SYMPTOM_ICON_ALLOWLIST } from "@/lib/cycle/custom-symptoms-shared";
import { FieldInfo } from "./field-info";
@@ -509,7 +506,9 @@ export function LogDaySheet({
const [cervixFirmness, setCervixFirmness] = useState<CervixFirmness | null>(
null,
);
const [cervixOpening, setCervixOpening] = useState<CervixOpening | null>(null);
const [cervixOpening, setCervixOpening] = useState<CervixOpening | null>(
null,
);
const [intercourse, setIntercourse] = useState(false);
const [protectedSex, setProtectedSex] = useState(false);
const [pregnancyTest, setPregnancyTest] = useState<HomeTestResult | null>(
@@ -884,121 +883,121 @@ export function LogDaySheet({
/>
}
>
<Input
type="number"
inputMode="decimal"
step="0.01"
min={30}
max={45}
value={bbt}
onChange={(e) => setBbt(e.target.value)}
placeholder={t("cycle.sheet.temperaturePlaceholder")}
className="w-32"
aria-label={t("cycle.sheet.temperature")}
/>
<p className="text-muted-foreground text-xs">
{t("cycle.sheet.bbtHint")}
</p>
{resolveBbt(bbt) != null && (
<div className="mt-3 flex items-start justify-between gap-3">
<div className="flex flex-col">
<span className="text-sm">
{t("cycle.sheet.temperatureDisturbed")}
</span>
<span className="text-muted-foreground text-xs">
{t("cycle.sheet.temperatureDisturbedHint")}
</span>
</div>
<Switch
checked={bbtDisturbed}
onCheckedChange={setBbtDisturbed}
aria-label={t("cycle.sheet.temperatureDisturbed")}
/>
</div>
)}
</Field>
{/* Ovulation test */}
<Field
label={t("cycle.sheet.ovulationTest")}
info={
<FieldInfo
label={t("cycle.fieldInfo.ovulationTestLabel")}
detail={t("cycle.fieldInfo.ovulationTest")}
<Input
type="number"
inputMode="decimal"
step="0.01"
min={30}
max={45}
value={bbt}
onChange={(e) => setBbt(e.target.value)}
placeholder={t("cycle.sheet.temperaturePlaceholder")}
className="w-32"
aria-label={t("cycle.sheet.temperature")}
/>
}
>
<div className="flex flex-wrap gap-2">
{OPK_VALUES.map((v) => (
<Chip
key={v}
active={opk === v}
onClick={() => setOpk((cur) => (cur === v ? null : v))}
>
{t(`cycle.ovulationTest.${v}`)}
</Chip>
))}
</div>
</Field>
{/* Symptothermal secondary symptom: cervical mucus (default) OR the
cervix observation, per the user's advanced-settings choice. Only one
of the two is offered so the log sheet never overwhelms. */}
{showCervix ? (
<Field label={t("cycle.sheet.cervix")}>
<div className="space-y-3">
<CervixRow
label={t("cycle.sheet.cervixPosition")}
values={CERVIX_POSITION_VALUES}
current={cervixPosition}
labelFor={(v) => t(`cycle.cervixPosition.${v}`)}
onToggle={(v) =>
setCervixPosition((cur) => (cur === v ? null : v))
}
/>
<CervixRow
label={t("cycle.sheet.cervixFirmness")}
values={CERVIX_FIRMNESS_VALUES}
current={cervixFirmness}
labelFor={(v) => t(`cycle.cervixFirmness.${v}`)}
onToggle={(v) =>
setCervixFirmness((cur) => (cur === v ? null : v))
}
/>
<CervixRow
label={t("cycle.sheet.cervixOpening")}
values={CERVIX_OPENING_VALUES}
current={cervixOpening}
labelFor={(v) => t(`cycle.cervixOpening.${v}`)}
onToggle={(v) =>
setCervixOpening((cur) => (cur === v ? null : v))
}
/>
</div>
<p className="text-muted-foreground text-xs">
{t("cycle.sheet.bbtHint")}
</p>
{resolveBbt(bbt) != null && (
<div className="mt-3 flex items-start justify-between gap-3">
<div className="flex flex-col">
<span className="text-sm">
{t("cycle.sheet.temperatureDisturbed")}
</span>
<span className="text-muted-foreground text-xs">
{t("cycle.sheet.temperatureDisturbedHint")}
</span>
</div>
<Switch
checked={bbtDisturbed}
onCheckedChange={setBbtDisturbed}
aria-label={t("cycle.sheet.temperatureDisturbed")}
/>
</div>
)}
</Field>
) : (
{/* Ovulation test */}
<Field
label={t("cycle.sheet.mucus")}
label={t("cycle.sheet.ovulationTest")}
info={
<FieldInfo
label={t("cycle.fieldInfo.mucusLabel")}
detail={t("cycle.fieldInfo.mucus")}
label={t("cycle.fieldInfo.ovulationTestLabel")}
detail={t("cycle.fieldInfo.ovulationTest")}
/>
}
>
<div className="flex flex-wrap gap-2">
{MUCUS_VALUES.map((v) => (
{OPK_VALUES.map((v) => (
<Chip
key={v}
active={mucus === v}
onClick={() => setMucus((cur) => (cur === v ? null : v))}
active={opk === v}
onClick={() => setOpk((cur) => (cur === v ? null : v))}
>
{t(`cycle.mucus.${v}`)}
{t(`cycle.ovulationTest.${v}`)}
</Chip>
))}
</div>
</Field>
)}
{/* Symptothermal secondary symptom: cervical mucus (default) OR the
cervix observation, per the user's advanced-settings choice. Only one
of the two is offered so the log sheet never overwhelms. */}
{showCervix ? (
<Field label={t("cycle.sheet.cervix")}>
<div className="space-y-3">
<CervixRow
label={t("cycle.sheet.cervixPosition")}
values={CERVIX_POSITION_VALUES}
current={cervixPosition}
labelFor={(v) => t(`cycle.cervixPosition.${v}`)}
onToggle={(v) =>
setCervixPosition((cur) => (cur === v ? null : v))
}
/>
<CervixRow
label={t("cycle.sheet.cervixFirmness")}
values={CERVIX_FIRMNESS_VALUES}
current={cervixFirmness}
labelFor={(v) => t(`cycle.cervixFirmness.${v}`)}
onToggle={(v) =>
setCervixFirmness((cur) => (cur === v ? null : v))
}
/>
<CervixRow
label={t("cycle.sheet.cervixOpening")}
values={CERVIX_OPENING_VALUES}
current={cervixOpening}
labelFor={(v) => t(`cycle.cervixOpening.${v}`)}
onToggle={(v) =>
setCervixOpening((cur) => (cur === v ? null : v))
}
/>
</div>
</Field>
) : (
<Field
label={t("cycle.sheet.mucus")}
info={
<FieldInfo
label={t("cycle.fieldInfo.mucusLabel")}
detail={t("cycle.fieldInfo.mucus")}
/>
}
>
<div className="flex flex-wrap gap-2">
{MUCUS_VALUES.map((v) => (
<Chip
key={v}
active={mucus === v}
onClick={() => setMucus((cur) => (cur === v ? null : v))}
>
{t(`cycle.mucus.${v}`)}
</Chip>
))}
</div>
</Field>
)}
</SheetSection>
{/* Intimacy & contraception */}
@@ -1008,48 +1007,48 @@ export function LogDaySheet({
>
{/* Intercourse + protection */}
<Field label={t("cycle.sheet.intercourse")}>
<div className="space-y-2">
<div className="flex items-center justify-between gap-2">
<div className="flex items-center gap-1.5">
<Label
htmlFor="cycle-intercourse"
className="text-sm font-normal"
>
{t("cycle.sheet.intercourse")}
</Label>
<FieldInfo
label={t("cycle.fieldInfo.intercourseLabel")}
detail={t("cycle.fieldInfo.intercourse")}
/>
</div>
<Switch
id="cycle-intercourse"
checked={intercourse}
onCheckedChange={setIntercourse}
/>
</div>
{intercourse ? (
<div className="space-y-2">
<div className="flex items-center justify-between gap-2">
<div className="flex items-center gap-1.5">
<Label
htmlFor="cycle-protected"
className="text-muted-foreground text-sm font-normal"
htmlFor="cycle-intercourse"
className="text-sm font-normal"
>
{t("cycle.sheet.protected")}
{t("cycle.sheet.intercourse")}
</Label>
<FieldInfo
label={t("cycle.fieldInfo.protectedLabel")}
detail={t("cycle.fieldInfo.protected")}
label={t("cycle.fieldInfo.intercourseLabel")}
detail={t("cycle.fieldInfo.intercourse")}
/>
</div>
<Switch
id="cycle-protected"
checked={protectedSex}
onCheckedChange={setProtectedSex}
id="cycle-intercourse"
checked={intercourse}
onCheckedChange={setIntercourse}
/>
</div>
) : null}
</div>
{intercourse ? (
<div className="flex items-center justify-between gap-2">
<div className="flex items-center gap-1.5">
<Label
htmlFor="cycle-protected"
className="text-muted-foreground text-sm font-normal"
>
{t("cycle.sheet.protected")}
</Label>
<FieldInfo
label={t("cycle.fieldInfo.protectedLabel")}
detail={t("cycle.fieldInfo.protected")}
/>
</div>
<Switch
id="cycle-protected"
checked={protectedSex}
onCheckedChange={setProtectedSex}
/>
</div>
) : null}
</div>
</Field>
{/* Contraceptive */}
+70 -70
View File
@@ -124,83 +124,83 @@ export function PhaseEducationCard({
className,
)}
>
{/* Zone 1 — eyebrow + phase name with a hue dot (never colour-only). */}
<div className="flex items-center gap-2">
<Sparkles
className="h-4 w-4 shrink-0"
style={{ color: hue }}
aria-hidden="true"
/>
<span className="text-muted-foreground text-xs font-medium tracking-wide uppercase">
{t("cycle.phaseEducation.title")}
</span>
</div>
{/* Zone 1 — eyebrow + phase name with a hue dot (never colour-only). */}
<div className="flex items-center gap-2">
<Sparkles
className="h-4 w-4 shrink-0"
style={{ color: hue }}
aria-hidden="true"
/>
<span className="text-muted-foreground text-xs font-medium tracking-wide uppercase">
{t("cycle.phaseEducation.title")}
</span>
</div>
{stillLearning ? (
<p
data-slot="cycle-phase-education-learning"
className="text-muted-foreground mt-2 text-sm"
>
{t("cycle.phaseEducation.stillLearning")}
</p>
) : (
<>
<h3 className="text-foreground mt-2 flex items-center gap-2 text-base font-semibold">
<span
aria-hidden="true"
className="inline-block h-2.5 w-2.5 shrink-0 rounded-full"
style={{ backgroundColor: hue }}
/>
{phaseName}
</h3>
{stillLearning ? (
<p
data-slot="cycle-phase-education-learning"
className="text-muted-foreground mt-2 text-sm"
>
{t("cycle.phaseEducation.stillLearning")}
</p>
) : (
<>
<h3 className="text-foreground mt-2 flex items-center gap-2 text-base font-semibold">
<span
aria-hidden="true"
className="inline-block h-2.5 w-2.5 shrink-0 rounded-full"
style={{ backgroundColor: hue }}
/>
{phaseName}
</h3>
{/* Zone 2 — the curated, descriptive-only "what's happening" line,
{/* Zone 2 — the curated, descriptive-only "what's happening" line,
followed by a deeper "context" line (what someone might notice +
gentle framing of normal variation). Both stay descriptive, never
clinical advice / a medical claim. */}
<p className="text-muted-foreground mt-1.5 text-sm leading-relaxed">
{t(PHASE_WHATS_HAPPENING_KEY[phase as CyclePhase])}
</p>
<p className="text-muted-foreground mt-2 text-sm leading-relaxed">
{t(PHASE_CONTEXT_KEY[phase as CyclePhase])}
</p>
<p className="text-muted-foreground mt-1.5 text-sm leading-relaxed">
{t(PHASE_WHATS_HAPPENING_KEY[phase as CyclePhase])}
</p>
<p className="text-muted-foreground mt-2 text-sm leading-relaxed">
{t(PHASE_CONTEXT_KEY[phase as CyclePhase])}
</p>
{/* Zone 3 — the user's OWN clustered symptoms for this phase
{/* Zone 3 — the user's OWN clustered symptoms for this phase
("you often notice this here"). */}
{chips.length > 0 ? (
<div className="mt-4">
<p className="text-muted-foreground text-xs font-medium">
{t("cycle.phaseEducation.commonHere")}
</p>
<ul
data-slot="cycle-phase-education-chips"
className="mt-2 flex flex-wrap gap-1.5"
>
{chips.map((row) => {
const meta = byKey.get(row.symptomKey);
const Icon = meta?.icon;
const name = meta ? t(meta.labelKey) : row.symptomKey;
return (
<li
key={row.symptomKey}
className="border-foreground/15 bg-background/55 text-foreground inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs font-medium"
>
{Icon ? (
<Icon
className="h-3 w-3 shrink-0"
style={{ color: hue }}
aria-hidden="true"
/>
) : null}
{name}
</li>
);
})}
</ul>
</div>
) : null}
</>
)}
{chips.length > 0 ? (
<div className="mt-4">
<p className="text-muted-foreground text-xs font-medium">
{t("cycle.phaseEducation.commonHere")}
</p>
<ul
data-slot="cycle-phase-education-chips"
className="mt-2 flex flex-wrap gap-1.5"
>
{chips.map((row) => {
const meta = byKey.get(row.symptomKey);
const Icon = meta?.icon;
const name = meta ? t(meta.labelKey) : row.symptomKey;
return (
<li
key={row.symptomKey}
className="border-foreground/15 bg-background/55 text-foreground inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs font-medium"
>
{Icon ? (
<Icon
className="h-3 w-3 shrink-0"
style={{ color: hue }}
aria-hidden="true"
/>
) : null}
{name}
</li>
);
})}
</ul>
</div>
) : null}
</>
)}
</section>
</div>
);
+2 -4
View File
@@ -193,8 +193,7 @@ export function useDeleteCustomSymptom() {
export function useCycleProfile() {
return useQuery({
queryKey: queryKeys.cycleProfile(),
queryFn: () =>
apiGet<CycleProfileDTO>("/api/cycle/profile"),
queryFn: () => apiGet<CycleProfileDTO>("/api/cycle/profile"),
staleTime: 5 * 60_000,
});
}
@@ -207,8 +206,7 @@ export function useCycleProfile() {
export function useCycleInsights() {
return useQuery({
queryKey: queryKeys.cycleInsights(),
queryFn: () =>
apiGet<CycleInsightsResponse>("/api/cycle/insights"),
queryFn: () => apiGet<CycleInsightsResponse>("/api/cycle/insights"),
staleTime: 5 * 60_000,
});
}