fix: address agent review issues for theme color settings (#174)

Code Quality Improvements:
- Export COLOR_THEMES from themeStore.ts for reuse (DRY)
- Add coral preset to color list (fixes default inconsistency)
- Sync local customThemeColor state reactively via Zustand subscribe
- Add hex color validation with visual feedback (red border + disabled button)
- Remove dead /themes route from Header.tsx (page doesn't exist)
- Add CSS color-mix() fallback for older browsers
- Add themeCoral i18n key to all 30 locale files
This commit is contained in:
diegosouzapw
2026-03-02 12:12:52 -03:00
parent 6e2816f08b
commit c4f1990aff
34 changed files with 56 additions and 13 deletions

View File

@@ -3,7 +3,7 @@
import { useState, useEffect } from "react";
import { Button, Card, Toggle } from "@/shared/components";
import { useTheme } from "@/shared/hooks/useTheme";
import useThemeStore from "@/store/themeStore";
import useThemeStore, { COLOR_THEMES } from "@/store/themeStore";
import { cn } from "@/shared/utils/cn";
import { useTranslations } from "next-intl";
@@ -14,6 +14,17 @@ export default function AppearanceTab() {
const [settings, setSettings] = useState<Record<string, any>>({});
const [loading, setLoading] = useState(true);
const [customThemeColor, setCustomThemeColor] = useState(customColor || "#3b82f6");
const isValidHex = /^#([0-9a-fA-F]{6})$/.test(customThemeColor.startsWith("#") ? customThemeColor : `#${customThemeColor}`);
// Subscribe to store changes (e.g. from another tab) via Zustand external subscription
useEffect(() => {
const unsubscribe = useThemeStore.subscribe((state) => {
if (state.customColor && state.customColor !== customThemeColor) {
setCustomThemeColor(state.customColor);
}
});
return unsubscribe;
}, []); // eslint-disable-line react-hooks/exhaustive-deps
const themeOptionLabels: Record<string, string> = {
light: t("themeLight"),
dark: t("themeDark"),
@@ -51,12 +62,13 @@ export default function AppearanceTab() {
};
const presetThemes = [
{ id: "blue", color: "#3b82f6", label: t("themeBlue") },
{ id: "red", color: "#ef4444", label: t("themeRed") },
{ id: "green", color: "#22c55e", label: t("themeGreen") },
{ id: "violet", color: "#8b5cf6", label: t("themeViolet") },
{ id: "orange", color: "#f97316", label: t("themeOrange") },
{ id: "cyan", color: "#06b6d4", label: t("themeCyan") },
{ id: "coral", color: COLOR_THEMES.coral, label: t("themeCoral") },
{ id: "blue", color: COLOR_THEMES.blue, label: t("themeBlue") },
{ id: "red", color: COLOR_THEMES.red, label: t("themeRed") },
{ id: "green", color: COLOR_THEMES.green, label: t("themeGreen") },
{ id: "violet", color: COLOR_THEMES.violet, label: t("themeViolet") },
{ id: "orange", color: COLOR_THEMES.orange, label: t("themeOrange") },
{ id: "cyan", color: COLOR_THEMES.cyan, label: t("themeCyan") },
];
return (
@@ -149,9 +161,10 @@ export default function AppearanceTab() {
value={customThemeColor}
onChange={(e) => setCustomThemeColor(e.target.value)}
placeholder="#3b82f6"
className="flex-1 h-10 px-3 rounded-lg bg-surface border border-border text-sm text-text-main focus:outline-none focus:border-primary"
maxLength={7}
className={`flex-1 h-10 px-3 rounded-lg bg-surface border text-sm text-text-main focus:outline-none ${isValidHex ? "border-border focus:border-primary" : "border-red-400 focus:border-red-500"}`}
/>
<Button onClick={() => setCustomColorTheme(customThemeColor)}>{t("themeCreate")}</Button>
<Button onClick={() => setCustomColorTheme(customThemeColor)} disabled={!isValidHex}>{t("themeCreate")}</Button>
</div>
</div>

View File

@@ -141,8 +141,9 @@ body {
-moz-osx-font-smoothing: grayscale;
}
/* Selection */
/* Selection — with fallback for browsers that don't support color-mix() */
::selection {
background-color: rgba(229, 77, 94, 0.22);
background-color: color-mix(in srgb, var(--color-primary) 22%, transparent);
color: var(--color-primary);
}

View File

@@ -1100,6 +1100,7 @@
"themeAccentDesc": "اختر لونًا جاهزًا أو أنشئ سمتك الخاصة بلون واحد",
"themeCreate": "إنشاء سمة",
"themeCustom": "سمة مخصصة",
"themeCoral": "مرجاني",
"themeBlue": "أزرق",
"themeRed": "أحمر",
"themeGreen": "أخضر",

View File

@@ -1100,6 +1100,7 @@
"themeAccentDesc": "Изберете готов цвят или създайте своя тема с един цвят",
"themeCreate": "Създай тема",
"themeCustom": "Персонална тема",
"themeCoral": "Коралов",
"themeBlue": "Син",
"themeRed": "Червен",
"themeGreen": "Зелен",

View File

@@ -1100,6 +1100,7 @@
"themeAccentDesc": "Vælg en forudindstillet farve eller opret dit eget tema med én farve",
"themeCreate": "Opret tema",
"themeCustom": "Brugerdefineret tema",
"themeCoral": "Koral",
"themeBlue": "Blå",
"themeRed": "Rød",
"themeGreen": "Grøn",

View File

@@ -1100,6 +1100,7 @@
"themeAccentDesc": "Wähle eine voreingestellte Farbe oder erstelle dein eigenes Thema mit einer Farbe",
"themeCreate": "Thema erstellen",
"themeCustom": "Benutzerdefiniertes Thema",
"themeCoral": "Koralle",
"themeBlue": "Blau",
"themeRed": "Rot",
"themeGreen": "Grün",

View File

@@ -94,6 +94,7 @@
"presetColors": "Popular colors",
"createTheme": "Create theme",
"chooseColor": "Pick one color",
"themeCoral": "Coral",
"themeBlue": "Blue",
"themeRed": "Red",
"themeGreen": "Green",

View File

@@ -1100,6 +1100,7 @@
"themeAccentDesc": "Elige un color predefinido o crea tu propio tema con un solo color",
"themeCreate": "Crear tema",
"themeCustom": "Tema personalizado",
"themeCoral": "Coral",
"themeBlue": "Azul",
"themeRed": "Rojo",
"themeGreen": "Verde",

View File

@@ -1100,6 +1100,7 @@
"themeAccentDesc": "Valitse valmis väri tai luo oma teemasi yhdellä värillä",
"themeCreate": "Luo teema",
"themeCustom": "Mukautettu teema",
"themeCoral": "Koralli",
"themeBlue": "Sininen",
"themeRed": "Punainen",
"themeGreen": "Vihreä",

View File

@@ -1100,6 +1100,7 @@
"themeAccentDesc": "Choisissez une couleur prédéfinie ou créez votre propre thème avec une seule couleur",
"themeCreate": "Créer un thème",
"themeCustom": "Thème personnalisé",
"themeCoral": "Corail",
"themeBlue": "Bleu",
"themeRed": "Rouge",
"themeGreen": "Vert",

View File

@@ -1100,6 +1100,7 @@
"themeAccentDesc": "בחר צבע מוכן מראש או צור ערכת נושא משלך עם צבע אחד",
"themeCreate": "צור ערכת נושא",
"themeCustom": "ערכת נושא מותאמת אישית",
"themeCoral": "אלמוג",
"themeBlue": "כחול",
"themeRed": "אדום",
"themeGreen": "ירוק",

View File

@@ -1100,6 +1100,7 @@
"themeAccentDesc": "Válassz előre beállított színt, vagy készíts saját témát egy színből",
"themeCreate": "Téma létrehozása",
"themeCustom": "Egyéni téma",
"themeCoral": "Korall",
"themeBlue": "Kék",
"themeRed": "Piros",
"themeGreen": "Zöld",

View File

@@ -1100,6 +1100,7 @@
"themeAccentDesc": "Pilih warna preset atau buat tema Anda sendiri dengan satu warna",
"themeCreate": "Buat tema",
"themeCustom": "Tema kustom",
"themeCoral": "Koral",
"themeBlue": "Biru",
"themeRed": "Merah",
"themeGreen": "Hijau",

View File

@@ -1100,6 +1100,7 @@
"themeAccentDesc": "एक प्रीसेट रंग चुनें या एक ही रंग से अपनी थीम बनाएं",
"themeCreate": "थीम बनाएं",
"themeCustom": "कस्टम थीम",
"themeCoral": "Koral",
"themeBlue": "नीला",
"themeRed": "लाल",
"themeGreen": "हरा",

View File

@@ -1100,6 +1100,7 @@
"themeAccentDesc": "Scegli un colore predefinito o crea il tuo tema con un solo colore",
"themeCreate": "Crea tema",
"themeCustom": "Tema personalizzato",
"themeCoral": "Corallo",
"themeBlue": "Blu",
"themeRed": "Rosso",
"themeGreen": "Verde",

View File

@@ -1100,6 +1100,7 @@
"themeAccentDesc": "プリセットカラーを選ぶか、1色で独自のテーマを作成します",
"themeCreate": "テーマを作成",
"themeCustom": "カスタムテーマ",
"themeCoral": "コーラル",
"themeBlue": "青",
"themeRed": "赤",
"themeGreen": "緑",

View File

@@ -1100,6 +1100,7 @@
"themeAccentDesc": "미리 설정된 색상을 선택하거나 단일 색상으로 나만의 테마를 만드세요",
"themeCreate": "테마 만들기",
"themeCustom": "사용자 지정 테마",
"themeCoral": "코랄",
"themeBlue": "파랑",
"themeRed": "빨강",
"themeGreen": "초록",

View File

@@ -1100,6 +1100,7 @@
"themeAccentDesc": "Pilih warna pratetap atau cipta tema anda sendiri dengan satu warna",
"themeCreate": "Cipta tema",
"themeCustom": "Tema tersuai",
"themeCoral": "Koral",
"themeBlue": "Biru",
"themeRed": "Merah",
"themeGreen": "Hijau",

View File

@@ -1100,6 +1100,7 @@
"themeAccentDesc": "Kies een vooraf ingestelde kleur of maak je eigen thema met één kleur",
"themeCreate": "Thema maken",
"themeCustom": "Aangepast thema",
"themeCoral": "Koraal",
"themeBlue": "Blauw",
"themeRed": "Rood",
"themeGreen": "Groen",

View File

@@ -1100,6 +1100,7 @@
"themeAccentDesc": "Velg en forhåndsinnstilt farge eller lag ditt eget tema med én farge",
"themeCreate": "Opprett tema",
"themeCustom": "Egendefinert tema",
"themeCoral": "Korall",
"themeBlue": "Blå",
"themeRed": "Rød",
"themeGreen": "Grønn",

View File

@@ -1100,6 +1100,7 @@
"themeAccentDesc": "Pumili ng preset na kulay o gumawa ng sarili mong tema gamit ang isang kulay",
"themeCreate": "Gumawa ng tema",
"themeCustom": "Custom na tema",
"themeCoral": "Koral",
"themeBlue": "Asul",
"themeRed": "Pula",
"themeGreen": "Berde",

View File

@@ -1100,6 +1100,7 @@
"themeAccentDesc": "Wybierz gotowy kolor lub utwórz własny motyw z jednego koloru",
"themeCreate": "Utwórz motyw",
"themeCustom": "Motyw niestandardowy",
"themeCoral": "Koralowy",
"themeBlue": "Niebieski",
"themeRed": "Czerwony",
"themeGreen": "Zielony",

View File

@@ -1117,6 +1117,7 @@
"themeAccentDesc": "Escolha uma cor predefinida ou crie seu próprio tema com uma cor",
"themeCreate": "Criar tema",
"themeCustom": "Tema personalizado",
"themeCoral": "Coral",
"themeBlue": "Azul",
"themeRed": "Vermelho",
"themeGreen": "Verde",

View File

@@ -1100,6 +1100,7 @@
"themeAccentDesc": "Escolha uma cor predefinida ou crie seu próprio tema com uma cor",
"themeCreate": "Criar tema",
"themeCustom": "Tema personalizado",
"themeCoral": "Coral",
"themeBlue": "Azul",
"themeRed": "Vermelho",
"themeGreen": "Verde",

View File

@@ -1100,6 +1100,7 @@
"themeAccentDesc": "Alege o culoare presetată sau creează-ți propria temă cu o singură culoare",
"themeCreate": "Creează temă",
"themeCustom": "Temă personalizată",
"themeCoral": "Coral",
"themeBlue": "Albastru",
"themeRed": "Roșu",
"themeGreen": "Verde",

View File

@@ -93,6 +93,7 @@
"presetColors": "Популярные цвета",
"createTheme": "Создать тему",
"chooseColor": "Выберите один цвет",
"themeCoral": "Коралловый",
"themeBlue": "Синий",
"themeRed": "Красный",
"themeGreen": "Зелёный",

View File

@@ -1100,6 +1100,7 @@
"themeAccentDesc": "Vyberte prednastavenú farbu alebo si vytvorte vlastnú tému jednou farbou",
"themeCreate": "Vytvoriť tému",
"themeCustom": "Vlastná téma",
"themeCoral": "Koralový",
"themeBlue": "Modrá",
"themeRed": "Červená",
"themeGreen": "Zelená",

View File

@@ -1100,6 +1100,7 @@
"themeAccentDesc": "Välj en förinställd färg eller skapa ditt eget tema med en färg",
"themeCreate": "Skapa tema",
"themeCustom": "Anpassat tema",
"themeCoral": "Korall",
"themeBlue": "Blå",
"themeRed": "Röd",
"themeGreen": "Grön",

View File

@@ -1100,6 +1100,7 @@
"themeAccentDesc": "เลือกสีสำเร็จรูปหรือสร้างธีมของคุณเองด้วยสีเดียว",
"themeCreate": "สร้างธีม",
"themeCustom": "ธีมกำหนดเอง",
"themeCoral": "คอรัล",
"themeBlue": "น้ำเงิน",
"themeRed": "แดง",
"themeGreen": "เขียว",

View File

@@ -1100,6 +1100,7 @@
"themeAccentDesc": "Оберіть готовий колір або створіть власну тему з одного кольору",
"themeCreate": "Створити тему",
"themeCustom": "Користувацька тема",
"themeCoral": "Кораловий",
"themeBlue": "Синій",
"themeRed": "Червоний",
"themeGreen": "Зелений",

View File

@@ -1100,6 +1100,7 @@
"themeAccentDesc": "Chọn màu đặt sẵn hoặc tạo chủ đề của riêng bạn bằng một màu",
"themeCreate": "Tạo chủ đề",
"themeCustom": "Chủ đề tùy chỉnh",
"themeCoral": "San hô",
"themeBlue": "Xanh dương",
"themeRed": "Đỏ",
"themeGreen": "Xanh lá",

View File

@@ -1100,6 +1100,7 @@
"themeAccentDesc": "选择预设颜色,或使用单一颜色创建你自己的主题",
"themeCreate": "创建主题",
"themeCustom": "自定义主题",
"themeCoral": "珊瑚色",
"themeBlue": "蓝色",
"themeRed": "红色",
"themeGreen": "绿色",

View File

@@ -90,8 +90,7 @@ function usePageInfo(pathname: string | null) {
return { title: t("endpoint"), description: t("endpointDescription"), breadcrumbs: [] };
if (pathname.includes("/profile"))
return { title: t("settings"), description: t("settingsDescription"), breadcrumbs: [] };
if (pathname.includes("/themes"))
return { title: t("themes"), description: t("themesDescription"), breadcrumbs: [] };
// Note: /themes page removed theme settings live in /settings → AppearanceTab
return { title: "", description: "", breadcrumbs: [] };
}

View File

@@ -57,7 +57,7 @@ const useThemeStore = create<ThemeState>()(
)
);
const COLOR_THEMES: Record<string, string> = {
export const COLOR_THEMES: Record<string, string> = {
coral: "#e54d5e",
blue: "#3b82f6",
red: "#ef4444",