From 3a9de12f264189937a322eb01423651d143a381d Mon Sep 17 00:00:00 2001 From: sepehr Date: Sun, 31 May 2026 13:46:13 +0200 Subject: [PATCH] fix: glossary cards UX - boutons clairs, badge compatible/incompatible, alerte cible dans sidebar --- .../src/app/dashboard/glossaries/page.tsx | 124 ++++++++++++------ .../dashboard/translate/GlossarySelector.tsx | 10 +- 2 files changed, 88 insertions(+), 46 deletions(-) diff --git a/frontend/src/app/dashboard/glossaries/page.tsx b/frontend/src/app/dashboard/glossaries/page.tsx index 26e38cb..c9c883e 100644 --- a/frontend/src/app/dashboard/glossaries/page.tsx +++ b/frontend/src/app/dashboard/glossaries/page.tsx @@ -10,6 +10,7 @@ import { Info, ExternalLink, } from 'lucide-react'; import Link from 'next/link'; +import { cn } from '@/lib/utils'; import { useUser } from '@/app/dashboard/useUser'; import { useI18n } from '@/lib/i18n'; import { useGlossaries, useGlossary } from './useGlossaries'; @@ -70,6 +71,10 @@ export default function GlossariesPage() { const isPro = user?.tier === 'pro'; const isLoading = isLoadingUser || isLoadingGlossaries; + // Current translation target from store + const currentTargetLang = settings.defaultTargetLanguage; + const currentTargetInfo = SUPPORTED_LANGUAGES.find(l => l.code === currentTargetLang); + // Track whether prompt has unsaved changes const promptHasUnsavedChanges = systemPrompt !== settings.systemPrompt; const promptIsActive = !!settings.systemPrompt?.trim(); @@ -451,19 +456,27 @@ export default function GlossariesPage() {

{glossaries.length > 0 - ? `${glossaries.length} glossaire${glossaries.length > 1 ? 's' : ''} — sélectionnez-en un dans la page Traduire pour l'activer` + ? `${glossaries.length} glossaire${glossaries.length > 1 ? 's' : ''} — cliquez sur une carte pour la modifier` : 'Créez votre premier glossaire ou importez un preset ci-dessus'}

- {glossaries.length > 0 && ( - - - Aller à Traduire pour activer - - )} +
+ {currentTargetInfo && ( + + Traduction active : + {currentTargetInfo.flag} {currentTargetInfo.label} + + )} + {glossaries.length > 0 && ( + + + Aller à Traduire pour activer + + )} +
{glossaries.length === 0 ? ( @@ -475,40 +488,57 @@ export default function GlossariesPage() {

{t('glossaries.emptyDesc')}

) : ( -
+
{glossaries.map((glossary: GlossaryListItem) => { const termCount = glossary.terms_count ?? 0; + const srcInfo = SUPPORTED_LANGUAGES.find(l => l.code === glossary.source_language); + const tgtInfo = SUPPORTED_LANGUAGES.find(l => l.code === glossary.target_language); + // Does this glossary match the current translation target? + const matchesTarget = currentTargetLang && glossary.target_language === currentTargetLang; + const mismatch = currentTargetLang && glossary.target_language && glossary.target_language !== currentTargetLang; return (
handleEditClick(glossary.id)} + className={cn( + 'editorial-card p-6 bg-white dark:bg-[#141414] border rounded-2xl shadow-sm group transition-all relative', + matchesTarget + ? 'border-brand-accent/40 ring-1 ring-brand-accent/20 hover:border-brand-accent/60' + : mismatch + ? 'border-amber-300/40 dark:border-amber-500/20 hover:border-amber-400/60 opacity-75 hover:opacity-100' + : 'border-black/5 dark:border-white/5 hover:border-brand-accent/30' + )} > -
-
+ {/* Match / mismatch badge */} + {matchesTarget && ( +
+ Compatible +
+ )} + {mismatch && ( +
+ Autre cible +
+ )} + +
+
- +
+

+ {glossary.name} +

+

+ {srcInfo?.flag ?? '🌐'} + {srcInfo?.label ?? glossary.source_language} + + {tgtInfo?.flag ?? '🌐'} + {tgtInfo?.label ?? glossary.target_language} +

+
-

- {glossary.name} -

-

- {SUPPORTED_LANGUAGES.find(l => l.code === glossary.source_language)?.flag ?? '🌐'} - {SUPPORTED_LANGUAGES.find(l => l.code === glossary.source_language)?.label ?? glossary.source_language} - - {SUPPORTED_LANGUAGES.find(l => l.code === glossary.target_language)?.flag ?? '🌐'} - {SUPPORTED_LANGUAGES.find(l => l.code === glossary.target_language)?.label ?? glossary.target_language} -

-
+ +
{termCount} {t('glossaries.defineTerms')} @@ -518,12 +548,24 @@ export default function GlossariesPage() { {new Date(glossary.created_at).toLocaleDateString()}
- {/* "How to activate" hint on hover */} -
-
- -

Sélectionnez-le dans la page Traduire pour l'activer

-
+ + {/* Action buttons — always visible, unambiguous */} +
+ +
); diff --git a/frontend/src/app/dashboard/translate/GlossarySelector.tsx b/frontend/src/app/dashboard/translate/GlossarySelector.tsx index 4b2ef05..31c282d 100644 --- a/frontend/src/app/dashboard/translate/GlossarySelector.tsx +++ b/frontend/src/app/dashboard/translate/GlossarySelector.tsx @@ -315,7 +315,7 @@ export function GlossarySelector({ sourceLang, targetLang, isPro, mode, glossary Le glossaire force la traduction de termes précis. Choisissez un glossaire dont la langue source correspond à la langue d'origine de votre document.

- {/* Mismatch Warning */} + {/* Mismatch Warning — source language */} {selected && sourceLang !== 'auto' && selected.source_language !== sourceLang && (
⚠️ @@ -325,12 +325,12 @@ export function GlossarySelector({ sourceLang, targetLang, isPro, mode, glossary
)} - {/* Incompatibility Warning */} - {selected && selected.source_language === targetLang && ( + {/* Mismatch Warning — target language */} + {selected && selected.target_language && selected.target_language !== targetLang && (
- ⚠️ + 🎯 - Incompatibilité : La langue source du glossaire est identique à la langue cible de traduction ({getFlag(targetLang)}). Les termes ne seront pas appliqués correctement. + Incompatibilité de cible : Ce glossaire est prévu pour traduire vers {getFlag(selected.target_language)} {selected.target_language.toUpperCase()}, mais votre document cible {targetFlag} {targetLang.toUpperCase()}. Les termes risquent de ne pas être pertinents.
)}