mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 21:32:18 +00:00
chore(release): v1.12.11 — unify settings notice colours and callout style
Route every Settings callout, badge and status note through the semantic colour tokens (success / warning / info / primary) instead of raw palette classes, so the same meaning reads identically across cards and stays legible in light mode. Add a single SettingsInfoTile primitive — icon + tone + text on one fixed surface — and adopt it for the threshold-override caution note, replacing a bespoke box. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
94b2d6f910
commit
038be6ac57
10 files changed
+112
-16
No files matched your search
@@ -0,0 +1,90 @@
|
||||
"use client";
|
||||
|
||||
/**
|
||||
* `<SettingsInfoTile>` — the one callout shape for Settings.
|
||||
*
|
||||
* Before this primitive, every section hand-rolled its own notice box:
|
||||
* `border-l-2`/`border-l-4`, `rounded-md`/`rounded-lg`, mismatched
|
||||
* border/background tone intensities, and raw palette colours used as
|
||||
* text (which fail AA in light mode). The tile fixes the surface to a
|
||||
* single contract — icon left, short text right, a tone-coloured left
|
||||
* border — and routes every tone through the semantic tokens so the
|
||||
* same notice reads identically in light and dark.
|
||||
*
|
||||
* Tones map to the project's semantic colour tokens:
|
||||
* - `info` → `--info` (neutral guidance)
|
||||
* - `warning` → `--warning` (caution / override / reconnect)
|
||||
* - `success` → `--success` (confirmation)
|
||||
* - `primary` → `--primary` (brand-accented instruction)
|
||||
* - `neutral` → muted surface (low-emphasis aside)
|
||||
*/
|
||||
|
||||
import * as React from "react";
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export type SettingsInfoTileTone =
|
||||
| "info"
|
||||
| "warning"
|
||||
| "success"
|
||||
| "primary"
|
||||
| "neutral";
|
||||
|
||||
const TONE_SURFACE: Record<SettingsInfoTileTone, string> = {
|
||||
info: "border-info/40 bg-info/10",
|
||||
warning: "border-warning/40 bg-warning/10",
|
||||
success: "border-success/40 bg-success/10",
|
||||
primary: "border-primary/40 bg-primary/10",
|
||||
neutral: "border-border bg-muted/30",
|
||||
};
|
||||
|
||||
const TONE_ICON: Record<SettingsInfoTileTone, string> = {
|
||||
info: "text-info",
|
||||
warning: "text-warning",
|
||||
success: "text-success",
|
||||
primary: "text-primary",
|
||||
neutral: "text-muted-foreground",
|
||||
};
|
||||
|
||||
export interface SettingsInfoTileProps {
|
||||
/** Lucide icon component; rendered in the tone colour. */
|
||||
icon: LucideIcon;
|
||||
/** Visual tone — routes through the semantic colour tokens. */
|
||||
tone?: SettingsInfoTileTone;
|
||||
/** Optional emphasised first line. */
|
||||
title?: React.ReactNode;
|
||||
/** Body content — muted text. */
|
||||
children?: React.ReactNode;
|
||||
/** Optional extra classes on the outer wrapper. */
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function SettingsInfoTile({
|
||||
icon: Icon,
|
||||
tone = "info",
|
||||
title,
|
||||
children,
|
||||
className,
|
||||
}: SettingsInfoTileProps) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-start gap-2 rounded-md border-l-4 p-3 text-sm",
|
||||
TONE_SURFACE[tone],
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<Icon
|
||||
className={cn("mt-0.5 h-4 w-4 shrink-0", TONE_ICON[tone])}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<div className="min-w-0 space-y-1">
|
||||
{title ? <p className="text-foreground font-medium">{title}</p> : null}
|
||||
{children ? (
|
||||
<div className="text-muted-foreground">{children}</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -156,7 +156,7 @@ function ResearchModeCard() {
|
||||
data-slot="settings-research-mode-card"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<BookOpenCheck className="text-dracula-purple h-5 w-5" />
|
||||
<BookOpenCheck className="text-primary h-5 w-5" />
|
||||
<h2 className="text-lg font-semibold">
|
||||
{t("settings.researchMode.sectionTitle")}
|
||||
</h2>
|
||||
@@ -349,7 +349,7 @@ function DataResetCard() {
|
||||
{msg && (
|
||||
<p
|
||||
role="alert"
|
||||
className={`mt-3 text-sm ${msgType === "success" ? "text-dracula-green" : "text-destructive"}`}
|
||||
className={`mt-3 text-sm ${msgType === "success" ? "text-success" : "text-destructive"}`}
|
||||
>
|
||||
{msg}
|
||||
</p>
|
||||
@@ -493,7 +493,7 @@ function AccountDeleteCard() {
|
||||
{msg && (
|
||||
<p
|
||||
role="alert"
|
||||
className={`mt-3 text-sm ${msgType === "success" ? "text-dracula-green" : "text-destructive"}`}
|
||||
className={`mt-3 text-sm ${msgType === "success" ? "text-success" : "text-destructive"}`}
|
||||
>
|
||||
{msg}
|
||||
</p>
|
||||
|
||||
@@ -520,7 +520,7 @@ function ProviderStatusBadges({
|
||||
{settings?.codexStatus !== "connected" &&
|
||||
settings?.hasAdminKey &&
|
||||
activeProvider !== "codex" && (
|
||||
<Badge className="border-dracula-purple/30 bg-dracula-purple/15 text-dracula-purple">
|
||||
<Badge className="border-primary/30 bg-primary/15 text-primary">
|
||||
{t("settings.ai.adminAiActiveBadge")}
|
||||
</Badge>
|
||||
)}
|
||||
@@ -837,7 +837,7 @@ function CodexProviderForm({
|
||||
{t("settings.ai.codex.disconnectButton")}
|
||||
</Button>
|
||||
) : deviceCode ? (
|
||||
<div className="border-dracula-purple bg-dracula-purple/5 space-y-3 rounded-lg border-l-4 p-4">
|
||||
<div className="border-primary bg-primary/5 space-y-3 rounded-lg border-l-4 p-4">
|
||||
<p className="text-sm font-medium">
|
||||
{t("settings.ai.deviceCodeHeading")}
|
||||
</p>
|
||||
@@ -848,7 +848,7 @@ function CodexProviderForm({
|
||||
href={deviceCode.verificationUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-dracula-purple font-medium underline"
|
||||
className="text-primary font-medium underline"
|
||||
>
|
||||
{deviceCode.verificationUrl}
|
||||
</a>
|
||||
|
||||
@@ -201,7 +201,7 @@ export function HealthRecordExportPanel() {
|
||||
>
|
||||
<div className="mb-2 flex items-center gap-2">
|
||||
<FileText
|
||||
className="text-dracula-purple h-5 w-5 shrink-0"
|
||||
className="text-primary h-5 w-5 shrink-0"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<h2
|
||||
|
||||
@@ -324,7 +324,7 @@ function stateBadgeFor(
|
||||
label: t("settings.notificationStatus.stateActive"),
|
||||
variant: "default",
|
||||
className:
|
||||
"border-dracula-green/30 bg-dracula-green/15 text-dracula-green",
|
||||
"border-success/30 bg-success/15 text-success",
|
||||
icon: <CheckCircle2 className="h-3 w-3" aria-hidden />,
|
||||
};
|
||||
case "auto_disabled":
|
||||
@@ -338,7 +338,7 @@ function stateBadgeFor(
|
||||
return {
|
||||
label: t("settings.notificationStatus.stateSendingPaused"),
|
||||
variant: "outline",
|
||||
className: "border-dracula-orange/40 text-dracula-orange",
|
||||
className: "border-warning/40 text-warning",
|
||||
icon: <Clock className="h-3 w-3" aria-hidden />,
|
||||
};
|
||||
case "manually_disabled":
|
||||
|
||||
@@ -100,7 +100,7 @@ export function TestConnectionButton({
|
||||
{result?.kind === "ok" && (
|
||||
<p
|
||||
role="status"
|
||||
className="text-dracula-green flex items-center gap-1.5 text-xs"
|
||||
className="text-success flex items-center gap-1.5 text-xs"
|
||||
>
|
||||
<CheckCircle2 className="h-3.5 w-3.5" />
|
||||
{t("settings.testConnection.ok", { latency: result.latency })}
|
||||
|
||||
@@ -10,6 +10,7 @@ import { Label } from "@/components/ui/label";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { SettingsInfoTile } from "./_info-tile";
|
||||
import { useTranslations, useFormatters } from "@/lib/i18n/context";
|
||||
import { queryKeys } from "@/lib/query-keys";
|
||||
import {
|
||||
@@ -350,10 +351,9 @@ function MetricRow({
|
||||
defaultRange &&
|
||||
(override!.min < defaultRange.greenMin * 0.7 ||
|
||||
override!.max > defaultRange.greenMax * 1.3) && (
|
||||
<div className="border-dracula-orange/50 bg-dracula-orange/5 flex items-start gap-2 rounded-md border-l-2 p-2 text-xs">
|
||||
<AlertTriangle className="text-dracula-orange mt-0.5 h-3 w-3 shrink-0" />
|
||||
<span>{t("thresholds.overrideWarning")}</span>
|
||||
</div>
|
||||
<SettingsInfoTile tone="warning" icon={AlertTriangle}>
|
||||
{t("thresholds.overrideWarning")}
|
||||
</SettingsInfoTile>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
Reference in new issue
Block a user