mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 06:40:50 +00:00
chore(ui): apply M7 + L1 + L3 + L4 polish across medication form, motion-reduce, details aria, therapy timeline
W10 design-review carry-over items. M7 lifts the medication-form day-of-week buttons, the more-horizontal trigger, and the schedule label-input gaps to the 44 px / space-y-2 standard used by every other settings surface. L1 pairs every animate-spin/pulse/bounce with motion-reduce:animate-none across the component tree so users with prefers-reduced-motion get a still surface. L3 adds the aria-controls + panel-id pairing to the inventory + coach-evidence <details> blocks for parity with the Health Score provenance accordion. L4 inserts a sr-only h4 per therapy-timeline entry so screen-reader users can jump between medication blocks.
This commit is contained in:
1 parent
19b5129306
commit
32a023dfff
71 files changed
+165
-136
No files matched your search
@@ -265,7 +265,7 @@ export function AboutSection() {
|
||||
{/* Updates — proper heading + last-check timestamp + manual button
|
||||
that proxies through `/api/version/check-updates`. The v1.4.2
|
||||
version called `api.github.com` directly from the browser, which
|
||||
the production CSP blocked silently — that's why "nothing
|
||||
the production CSP blocked silently — that motion-reduce:animate-none's why "nothing
|
||||
happened" when the user clicked the button. */}
|
||||
{version && (
|
||||
<div className="bg-card border-border rounded-xl border p-6">
|
||||
@@ -284,7 +284,7 @@ export function AboutSection() {
|
||||
disabled={checking}
|
||||
>
|
||||
{checking ? (
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin motion-reduce:animate-none" />
|
||||
) : (
|
||||
<RefreshCw className="mr-2 h-4 w-4" />
|
||||
)}
|
||||
|
||||
@@ -407,7 +407,7 @@ export function AccountSection() {
|
||||
wide on sm+). Date of birth is the bottom of the
|
||||
biological-profile block; language is the only UI
|
||||
preference on this card. They sit together to close the
|
||||
"single-cell row" gap that broke the form's grid. */}
|
||||
"single-cell row" gap that broke the form motion-reduce:animate-none's grid. */}
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="dob">{t("settings.dateOfBirth")}</Label>
|
||||
@@ -459,7 +459,7 @@ export function AccountSection() {
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit" disabled={saving}>
|
||||
{saving ? (
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin motion-reduce:animate-none" />
|
||||
) : (
|
||||
<Save className="mr-2 h-4 w-4" />
|
||||
)}
|
||||
@@ -483,7 +483,7 @@ export function AccountSection() {
|
||||
disabled={passkeyLoading}
|
||||
>
|
||||
{passkeyLoading ? (
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin motion-reduce:animate-none" />
|
||||
) : (
|
||||
<KeyRound className="mr-2 h-4 w-4" />
|
||||
)}
|
||||
@@ -728,7 +728,7 @@ function PasskeyListSection({ isAuthenticated }: { isAuthenticated: boolean }) {
|
||||
which on a 393px viewport hid the right-most column —
|
||||
including the destructive delete action. Render the desktop
|
||||
table only at `≥ md`, and at `< md` paint a card-list where
|
||||
every passkey's name, device type, backup status, created
|
||||
every passkey motion-reduce:animate-none's name, device type, backup status, created
|
||||
date, and delete action are all visible without scrolling. */}
|
||||
<div className="border-border mt-3 hidden overflow-x-auto rounded-lg border md:block">
|
||||
<table className="w-full text-sm">
|
||||
|
||||
@@ -281,7 +281,7 @@ function DataResetCard() {
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button variant="destructive" size="sm" disabled={deleting}>
|
||||
{deleting ? (
|
||||
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin" />
|
||||
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin motion-reduce:animate-none" />
|
||||
) : (
|
||||
<Trash2 className="mr-1 h-3.5 w-3.5" />
|
||||
)}
|
||||
|
||||
@@ -716,7 +716,7 @@ function CodexProviderForm({
|
||||
/* ────────────────────────────────────────────────────────────────
|
||||
* OpenAI form — API key + model select + collapsed Base URL override.
|
||||
* Save mutation flips both `aiProvider` (for the legacy single-result
|
||||
* resolver) and `aiOpenaiKeyEncrypted` (the user's key) so an OPENAI
|
||||
* resolver) and `aiOpenaiKeyEncrypted` (the user motion-reduce:animate-none's key) so an OPENAI
|
||||
* pick is visible to every code path that reads the row.
|
||||
* ──────────────────────────────────────────────────────────────── */
|
||||
|
||||
@@ -889,7 +889,7 @@ function OpenAIProviderForm({
|
||||
disabled={saveMutation.isPending}
|
||||
>
|
||||
{saveMutation.isPending ? (
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin motion-reduce:animate-none" />
|
||||
) : (
|
||||
<Save className="mr-2 h-4 w-4" />
|
||||
)}
|
||||
@@ -1045,7 +1045,7 @@ function AnthropicProviderForm({
|
||||
disabled={saveMutation.isPending}
|
||||
>
|
||||
{saveMutation.isPending ? (
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin motion-reduce:animate-none" />
|
||||
) : (
|
||||
<Save className="mr-2 h-4 w-4" />
|
||||
)}
|
||||
@@ -1202,7 +1202,7 @@ function LocalProviderForm({
|
||||
disabled={saveMutation.isPending}
|
||||
>
|
||||
{saveMutation.isPending ? (
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin motion-reduce:animate-none" />
|
||||
) : (
|
||||
<Save className="mr-2 h-4 w-4" />
|
||||
)}
|
||||
@@ -1454,7 +1454,7 @@ function FallbackChainCard({
|
||||
data-testid="ai-fallback-chain-save"
|
||||
>
|
||||
{saveMutation.isPending ? (
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin motion-reduce:animate-none" />
|
||||
) : (
|
||||
<Save className="mr-2 h-4 w-4" />
|
||||
)}
|
||||
@@ -1659,7 +1659,7 @@ function RuntimeActionsRow({
|
||||
data-testid="ai-test-active-provider"
|
||||
>
|
||||
{testing ? (
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin motion-reduce:animate-none" />
|
||||
) : (
|
||||
<Sparkles className="mr-2 h-4 w-4" />
|
||||
)}
|
||||
@@ -1673,7 +1673,7 @@ function RuntimeActionsRow({
|
||||
disabled={regen}
|
||||
>
|
||||
{regen ? (
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin motion-reduce:animate-none" />
|
||||
) : (
|
||||
<RefreshCw className="mr-2 h-4 w-4" />
|
||||
)}
|
||||
|
||||
@@ -210,7 +210,7 @@ function ApiTokensCard() {
|
||||
size="sm"
|
||||
disabled={creating || !newName.trim()}
|
||||
>
|
||||
{creating && <Loader2 className="mr-1 h-3.5 w-3.5 animate-spin" />}
|
||||
{creating && <Loader2 className="mr-1 h-3.5 w-3.5 animate-spin motion-reduce:animate-none" />}
|
||||
{t("common.create")}
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
@@ -241,7 +241,7 @@ export function DashboardLayoutSection({ id }: { id: string }) {
|
||||
|
||||
{isLoading || !layout ? (
|
||||
<div className="text-muted-foreground flex items-center gap-2 text-sm">
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
<Loader2 className="h-4 w-4 animate-spin motion-reduce:animate-none" />
|
||||
{t("common.loading")}
|
||||
</div>
|
||||
) : (
|
||||
@@ -342,7 +342,7 @@ export function DashboardLayoutSection({ id }: { id: string }) {
|
||||
disabled={saveMutation.isPending}
|
||||
>
|
||||
{saveMutation.isPending && (
|
||||
<Loader2 className="mr-2 h-3.5 w-3.5 animate-spin" />
|
||||
<Loader2 className="mr-2 h-3.5 w-3.5 animate-spin motion-reduce:animate-none" />
|
||||
)}
|
||||
{t("common.save")}
|
||||
</Button>
|
||||
|
||||
@@ -224,7 +224,7 @@ function DoctorReportCard() {
|
||||
onClick={() => setOpen(true)}
|
||||
disabled={busy}
|
||||
>
|
||||
{busy && <Loader2 className="mr-1 h-3.5 w-3.5 animate-spin" />}
|
||||
{busy && <Loader2 className="mr-1 h-3.5 w-3.5 animate-spin motion-reduce:animate-none" />}
|
||||
{t("settings.sections.export.cards.doctorReport.action")}
|
||||
</Button>
|
||||
}
|
||||
@@ -358,7 +358,7 @@ function CsvCard({
|
||||
disabled={busy}
|
||||
>
|
||||
{busy ? (
|
||||
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin" />
|
||||
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin motion-reduce:animate-none" />
|
||||
) : (
|
||||
<Download className="mr-1 h-3.5 w-3.5" />
|
||||
)}
|
||||
@@ -445,7 +445,7 @@ function MedicationsCsvCard() {
|
||||
disabled={busy}
|
||||
>
|
||||
{busy ? (
|
||||
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin" />
|
||||
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin motion-reduce:animate-none" />
|
||||
) : (
|
||||
<Download className="mr-1 h-3.5 w-3.5" />
|
||||
)}
|
||||
@@ -534,7 +534,7 @@ function FullBackupCard() {
|
||||
disabled={busy}
|
||||
>
|
||||
{busy ? (
|
||||
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin" />
|
||||
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin motion-reduce:animate-none" />
|
||||
) : (
|
||||
<FileText className="mr-1 h-3.5 w-3.5" />
|
||||
)}
|
||||
|
||||
@@ -434,7 +434,7 @@ function WithingsCard({
|
||||
}
|
||||
>
|
||||
{credsSaving ? (
|
||||
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin" />
|
||||
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin motion-reduce:animate-none" />
|
||||
) : (
|
||||
<Save className="mr-1 h-3.5 w-3.5" />
|
||||
)}
|
||||
@@ -463,7 +463,7 @@ function WithingsCard({
|
||||
disabled={syncing}
|
||||
>
|
||||
{syncing ? (
|
||||
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin" />
|
||||
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin motion-reduce:animate-none" />
|
||||
) : (
|
||||
<RefreshCw className="mr-1 h-3.5 w-3.5" />
|
||||
)}
|
||||
@@ -473,7 +473,7 @@ function WithingsCard({
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button variant="outline" size="sm" disabled={syncing}>
|
||||
{syncing ? (
|
||||
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin" />
|
||||
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin motion-reduce:animate-none" />
|
||||
) : (
|
||||
<RefreshCw className="mr-1 h-3.5 w-3.5" />
|
||||
)}
|
||||
@@ -726,7 +726,7 @@ function MoodLogCard({
|
||||
disabled={saving || (!url.trim() && !apiKey.trim())}
|
||||
size="sm"
|
||||
>
|
||||
{saving && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
||||
{saving && <Loader2 className="mr-2 h-4 w-4 animate-spin motion-reduce:animate-none" />}
|
||||
<Save className="mr-2 h-4 w-4" />
|
||||
{t("common.save")}
|
||||
</Button>
|
||||
@@ -792,7 +792,7 @@ function MoodLogCard({
|
||||
disabled={syncing}
|
||||
onClick={() => handleSync(false)}
|
||||
>
|
||||
{syncing && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
||||
{syncing && <Loader2 className="mr-2 h-4 w-4 animate-spin motion-reduce:animate-none" />}
|
||||
<RefreshCw className="mr-2 h-4 w-4" />
|
||||
{t("settings.moodLogSync")}
|
||||
</Button>
|
||||
|
||||
@@ -112,7 +112,7 @@ export function NotificationStatusCard() {
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="bg-card border-border rounded-xl border p-6">
|
||||
<Loader2 className="text-muted-foreground h-4 w-4 animate-spin" />
|
||||
<Loader2 className="text-muted-foreground h-4 w-4 animate-spin motion-reduce:animate-none" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -279,7 +279,7 @@ function ChannelRow({
|
||||
className="min-h-11"
|
||||
>
|
||||
{reEnablePending ? (
|
||||
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin" />
|
||||
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin motion-reduce:animate-none" />
|
||||
) : (
|
||||
<RotateCw className="mr-1 h-3.5 w-3.5" />
|
||||
)}
|
||||
@@ -296,7 +296,7 @@ function ChannelRow({
|
||||
className="min-h-11"
|
||||
>
|
||||
{testPending ? (
|
||||
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin" />
|
||||
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin motion-reduce:animate-none" />
|
||||
) : (
|
||||
<Send className="mr-1 h-3.5 w-3.5" />
|
||||
)}
|
||||
|
||||
@@ -195,7 +195,7 @@ export function NtfyCard({ isAuthenticated }: { isAuthenticated: boolean }) {
|
||||
onClick={() => test.mutate()}
|
||||
>
|
||||
{test.isPending ? (
|
||||
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin" />
|
||||
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin motion-reduce:animate-none" />
|
||||
) : (
|
||||
<Send className="mr-1 h-3.5 w-3.5" />
|
||||
)}
|
||||
@@ -203,7 +203,7 @@ export function NtfyCard({ isAuthenticated }: { isAuthenticated: boolean }) {
|
||||
</Button>
|
||||
<Button type="submit" disabled={save.isPending}>
|
||||
{save.isPending && (
|
||||
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin" />
|
||||
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin motion-reduce:animate-none" />
|
||||
)}
|
||||
{t("common.save")}
|
||||
</Button>
|
||||
|
||||
@@ -200,7 +200,7 @@ export function TelegramCard({
|
||||
onClick={handleTest}
|
||||
>
|
||||
{testing ? (
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin motion-reduce:animate-none" />
|
||||
) : (
|
||||
<Send className="mr-2 h-4 w-4" />
|
||||
)}
|
||||
@@ -208,7 +208,7 @@ export function TelegramCard({
|
||||
</Button>
|
||||
<Button type="submit" disabled={saving}>
|
||||
{saving ? (
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin motion-reduce:animate-none" />
|
||||
) : (
|
||||
<Save className="mr-2 h-4 w-4" />
|
||||
)}
|
||||
|
||||
@@ -87,7 +87,7 @@ export function TestConnectionButton({
|
||||
disabled={disabled || testing}
|
||||
>
|
||||
{testing ? (
|
||||
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin" />
|
||||
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin motion-reduce:animate-none" />
|
||||
) : (
|
||||
<Plug className="mr-1 h-3.5 w-3.5" />
|
||||
)}
|
||||
|
||||
@@ -182,7 +182,7 @@ export function WebPushCard() {
|
||||
<div className="mt-4 space-y-3">
|
||||
{loading ? (
|
||||
<div className="flex items-center gap-2">
|
||||
<Loader2 className="text-muted-foreground h-4 w-4 animate-spin" />
|
||||
<Loader2 className="text-muted-foreground h-4 w-4 animate-spin motion-reduce:animate-none" />
|
||||
</div>
|
||||
) : !isSupported ? (
|
||||
<p className="text-muted-foreground text-sm">
|
||||
@@ -202,7 +202,7 @@ export function WebPushCard() {
|
||||
disabled={actionLoading}
|
||||
>
|
||||
{actionLoading && (
|
||||
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin" />
|
||||
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin motion-reduce:animate-none" />
|
||||
)}
|
||||
{t("settings.webPushUnsubscribe")}
|
||||
</Button>
|
||||
@@ -214,7 +214,7 @@ export function WebPushCard() {
|
||||
disabled={actionLoading}
|
||||
>
|
||||
{actionLoading ? (
|
||||
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin" />
|
||||
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin motion-reduce:animate-none" />
|
||||
) : (
|
||||
<BellRing className="mr-1 h-3.5 w-3.5" />
|
||||
)}
|
||||
|
||||
Reference in new issue
Block a user