fix(settings): F13 username readability, F14 mobile bottom-nav padding, Mood Log overflow

Three closely-related polish fixes from the v1.4.33 audit:

* `/settings/account` username field flips `disabled` to `readOnly`.
  The disabled state inherits `opacity-50` from the `<Input>` primitive,
  which on Marc's mobile pass read identically to placeholder text
  and made the field look empty. `readOnly` keeps the full-contrast
  value rendering while still preventing edits.
* `<SettingsShell>` main column reserves a 96 px bottom gutter on
  `<md` so the last form field on `/settings/account` (Gender) and
  `/settings/ai` (Active provider) is not eaten by the floating
  mobile bottom-nav. Desktop keeps the parent's own padding because
  the bottom-nav doesn't render on `md+`.
* Mood Log webhook-secret row stacks vertically on `<sm` with
  `break-all` on the input value, killing the body-level horizontal
  scroll triggered by the long hex secret on Pixel 5. The
  Sync / Voll-Sync / Trennen triplet picks up the Withings
  `flex-wrap` + `min-w-[10rem]` pattern, and the in-form save row
  right-aligns its primary action so it matches every other
  integration form.
* New locale key `settings.moodLogFullSyncConfirm`
  ("Vollständig synchronisieren" / "Synchronize fully") is now
  wired into the Voll-Sync confirmation dialog so the trigger and
  confirm actions read as distinct copy.

Locale parity across de, en, es, fr, it, pl.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Marc-André BombeckandClaude Opus 4.7 committed 2026-05-16 17:15:26 +02:00
1 parent 7b78a4bf39
commit c98d07effd
9 files changed
+52 -15

No files matched your search

-3
View File
@@ -1617,9 +1617,6 @@
"about": {
"title": "Über",
"description": "Version, Lizenz, Links."
},
"placeholder": {
"coming_soon": "Wird mit dem nächsten Release-Update hierher umziehen."
}
},
"profile": "Profil",
+1
View File
@@ -259,6 +259,7 @@
"emptyTitle": "Your dashboard is empty",
"emptyDescription": "Log your first measurement or mood entry. Tiles and charts appear automatically as data lands.",
"emptyAddMeasurement": "Add your first measurement",
"staleHint": "Last reading {count} d ago",
"recentWorkouts": {
"title": "Recent workouts",
"viewAll": "View all",
+1
View File
@@ -1898,6 +1898,7 @@
"moodLogWebhookSecretHelp": "Enter this secret in moodLog as webhook secret",
"moodLogSync": "Start sync",
"moodLogFullSync": "Full sync",
"moodLogFullSyncConfirm": "Synchronize fully",
"moodLogFullSyncTitle": "Full sync?",
"moodLogFullSyncDescription": "All available mood data will be fully synchronized.",
"moodLogDisconnect": "Disconnect",
+1
View File
@@ -1898,6 +1898,7 @@
"moodLogWebhookSecretHelp": "Enter this secret in moodLog as webhook secret",
"moodLogSync": "Start sync",
"moodLogFullSync": "Full sync",
"moodLogFullSyncConfirm": "Synchronize fully",
"moodLogFullSyncTitle": "Full sync?",
"moodLogFullSyncDescription": "All available mood data will be fully synchronized.",
"moodLogDisconnect": "Disconnect",
+1
View File
@@ -1898,6 +1898,7 @@
"moodLogWebhookSecretHelp": "Enter this secret in moodLog as webhook secret",
"moodLogSync": "Start sync",
"moodLogFullSync": "Full sync",
"moodLogFullSyncConfirm": "Synchronize fully",
"moodLogFullSyncTitle": "Full sync?",
"moodLogFullSyncDescription": "All available mood data will be fully synchronized.",
"moodLogDisconnect": "Disconnect",
+1
View File
@@ -1898,6 +1898,7 @@
"moodLogWebhookSecretHelp": "Enter this secret in moodLog as webhook secret",
"moodLogSync": "Start sync",
"moodLogFullSync": "Full sync",
"moodLogFullSyncConfirm": "Synchronize fully",
"moodLogFullSyncTitle": "Full sync?",
"moodLogFullSyncDescription": "All available mood data will be fully synchronized.",
"moodLogDisconnect": "Disconnect",
+8 -1
View File
@@ -351,10 +351,17 @@ export function AccountSection() {
<div className="grid gap-4 sm:grid-cols-2">
<div className="space-y-2">
<Label htmlFor="username">{t("settings.username")}</Label>
{/* v1.4.33 F13 — `disabled` adds `opacity-50` to the input
primitive, which the maintainer's mobile pass read as
"empty placeholder text" because the username then
matches the muted-foreground colour exactly. Username
changes still aren't allowed, but `readOnly` keeps the
text crisp (full contrast) so it reads as a value, not
a hint. */}
<Input
id="username"
value={user.username}
disabled
readOnly
autoComplete="username"
/>
</div>
@@ -753,7 +753,16 @@ function MoodLogCard({
enterKeyHint="done"
/>
</div>
<div className="flex flex-wrap items-start gap-2">
{/* v1.4.33 — save-button placement contract:
right-aligned primary `Speichern` with the secondary
`Verbindung testen` immediately to its left. Mood Log
used to left-align its row, which was the only
integration form that did. */}
<div className="flex flex-wrap items-center justify-end gap-2">
<TestConnectionButton
endpoint="/api/integrations/moodlog/test"
disabled={!status?.configured}
/>
<Button
type="submit"
disabled={saving || (!url.trim() && !apiKey.trim())}
@@ -763,10 +772,6 @@ function MoodLogCard({
<Save className="mr-2 h-4 w-4" />
{t("common.save")}
</Button>
<TestConnectionButton
endpoint="/api/integrations/moodlog/test"
disabled={!status?.configured}
/>
</div>
</form>
@@ -777,12 +782,18 @@ function MoodLogCard({
<Label htmlFor="moodlog-webhook-secret">
{t("settings.moodLogWebhookSecret")}
</Label>
<div className="flex gap-2">
{/* v1.4.33 — the webhook secret is a long hex string;
on a 393 CSS px viewport the input + Copy button
used to push past the card edge and trigger a body
horizontal scroll. Stack vertically on `<sm` and
break the input value so it wraps inside its own
box. */}
<div className="flex flex-col gap-2 sm:flex-row sm:items-center">
<Input
id="moodlog-webhook-secret"
value={status.webhookSecret}
readOnly
className="font-mono text-xs"
className="font-mono text-xs break-all"
/>
<Button
variant="outline"
@@ -792,6 +803,7 @@ function MoodLogCard({
setMsg(t("common.copied"));
setMsgType("success");
}}
className="w-full sm:w-auto"
>
{/* v1.4.22 D / D-DSGN-M-04 — use the proper
common.copy key instead of stripping the
@@ -818,7 +830,14 @@ function MoodLogCard({
</span>
</div>
<div className="flex gap-2">
{/* v1.4.33 — the Sync / Voll-Sync / Trennen triplet
overflowed the card edge on a 393 CSS px viewport
(~376 px of buttons + gaps inside 345 px of inner
width). Match the Withings card pattern: `flex-wrap`
+ `min-w-[10rem]` on `<sm` so the triggers stack two
per row and fill their column cleanly; tablet+ keeps
the inline row. */}
<div className="flex flex-wrap items-start gap-2 [&>*]:min-w-[10rem] sm:[&>*]:min-w-0">
<Button
variant="outline"
size="sm"
@@ -849,7 +868,7 @@ function MoodLogCard({
<AlertDialogFooter>
<AlertDialogCancel>{t("common.cancel")}</AlertDialogCancel>
<AlertDialogAction onClick={() => handleSync(true)}>
{t("settings.moodLogFullSync")}
{t("settings.moodLogFullSyncConfirm")}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
+11 -2
View File
@@ -218,8 +218,17 @@ export function SettingsShell({ active, children }: SettingsShellProps) {
`min-h-[calc(100dvh-12rem)]` reserve keeps the column tall
enough that swapping a short loading state for a long
section list (Thresholds, Sources) does not jump the page
height under the sticky sidebar. */}
<main className="min-h-[calc(100dvh-12rem)] min-w-0">{children}</main>
height under the sticky sidebar.
v1.4.33 F14 — `pb-24 md:pb-0` reserves a 96 px bottom gutter
on `<md` so the last form field on
`/settings/account` (Geschlecht / Gender) and
`/settings/ai` (Aktiver Provider) is not eaten by the
floating mobile bottom-nav. Desktop reverts to the parent's
own padding because the bottom-nav doesn't render on `md+`. */}
<main className="min-h-[calc(100dvh-12rem)] min-w-0 pb-24 md:pb-0">
{children}
</main>
</div>
</div>
);