fix(ui): thème, textes et lisibilité restants de la revue
All checks were successful
CI / Lint, Unit Tests & Build (push) Successful in 6m57s
CI / Deploy production (on server) (push) Successful in 24s

Les boutons suivent la couleur d’apparence, les libellés trop petits ou trop techniques sont clarifiés, et le catalogue des fournisseurs se met à jour tout seul.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Antigravity
2026-08-30 21:13:07 +00:00
parent ebf6f16fde
commit afbb0dfc2d
77 changed files with 2842 additions and 1546 deletions

View File

@@ -55,6 +55,16 @@ const typeConfig: Record<string, { icon: typeof Globe }> = {
'task-extractor': { icon: ListChecks },
}
function kebabToCamel(value: string) {
return value.replace(/-([a-z])/g, (_, letter: string) => letter.toUpperCase())
}
function resolveStoredLabel(t: (key: string) => string, value: string) {
if (!value.startsWith('agents.')) return value
const translated = t(value)
return translated !== value ? translated : value
}
const frequencyKeys: Record<string, string> = {
manual: 'agents.frequencies.manual',
hourly: 'agents.frequencies.hourly',
@@ -209,9 +219,9 @@ export function AgentCard({ agent, onEdit, onRefresh, onToggle }: AgentCardProps
<Icon className="w-5 h-5" />
</div>
<div className="space-y-1">
<h4 className="text-[13px] font-bold text-foreground">{agent.name}</h4>
<h4 className="text-[13px] font-bold text-foreground">{resolveStoredLabel(t, agent.name)}</h4>
<p className="text-[10px] font-bold uppercase tracking-widest text-muted-foreground opacity-60">
{t(`agents.types.${agent.type || 'custom'}`)}
{t(`agents.types.${kebabToCamel(agent.type || 'custom')}`)}
</p>
</div>
</div>
@@ -237,7 +247,7 @@ export function AgentCard({ agent, onEdit, onRefresh, onToggle }: AgentCardProps
{agent.description && (
<p className="text-xs text-muted-foreground leading-relaxed line-clamp-3">
{agent.description}
{resolveStoredLabel(t, agent.description)}
</p>
)}

View File

@@ -63,14 +63,26 @@ const templateConfig = [
type TemplateId = typeof templateConfig[number]['id']
type CategoryId = 'all' | 'veille' | 'digest' | 'tools' | 'generate'
const CATEGORIES: { id: CategoryId; label: string }[] = [
{ id: 'all', label: 'Tous' },
{ id: 'veille', label: '📡 Veille' },
{ id: 'digest', label: '📰 Digest' },
{ id: 'tools', label: '🔧 Outils' },
{ id: 'generate', label: '✨ Génération' },
const CATEGORIES: { id: CategoryId; labelKey: string }[] = [
{ id: 'all', labelKey: 'agents.templates.categoryAll' },
{ id: 'veille', labelKey: 'agents.templates.categoryWatch' },
{ id: 'digest', labelKey: 'agents.templates.categoryDigest' },
{ id: 'tools', labelKey: 'agents.templates.categoryTools' },
{ id: 'generate', labelKey: 'agents.templates.categoryGenerate' },
]
const PREVIEW_COUNT = 3
function templateNameKey(id: string) {
return `agents.templates.${id}.name`
}
function hasTranslatedName(t: (key: string) => string, id: string) {
const key = templateNameKey(id)
const name = t(key)
return Boolean(name.trim()) && name !== key
}
const typeIcons: Record<string, typeof Globe> = {
scraper: Globe,
researcher: Search,
@@ -98,6 +110,7 @@ export function AgentTemplates({ onInstalled, existingAgentNames }: AgentTemplat
const { t } = useLanguage()
const [installingId, setInstallingId] = useState<string | null>(null)
const [activeCategory, setActiveCategory] = useState<CategoryId>('all')
const [showAll, setShowAll] = useState(false)
const handleInstall = async (tpl: typeof templateConfig[number]) => {
setInstallingId(tpl.id)
@@ -142,55 +155,62 @@ export function AgentTemplates({ onInstalled, existingAgentNames }: AgentTemplat
}
}
const filtered = activeCategory === 'all'
const filtered = (activeCategory === 'all'
? templateConfig
: templateConfig.filter((tpl) => tpl.category === activeCategory)
).filter((tpl) => hasTranslatedName(t, tpl.id))
const visible = showAll ? filtered : filtered.slice(0, PREVIEW_COUNT)
const hiddenCount = filtered.length - visible.length
return (
<div className="space-y-5">
{/* Category filter */}
<div className="flex flex-wrap gap-2">
{CATEGORIES.map((cat) => (
<button
key={cat.id}
onClick={() => setActiveCategory(cat.id)}
type="button"
onClick={() => {
setActiveCategory(cat.id)
setShowAll(false)
}}
className={`px-3 py-1.5 rounded-full text-xs font-semibold transition-all border ${
activeCategory === cat.id
? 'bg-ink text-paper border-ink'
: 'bg-paper text-muted-ink border-border/40 hover:border-ink/30'
}`}
>
{cat.label}
{t(cat.labelKey)}
</button>
))}
</div>
{/* Template grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{filtered.map(tpl => {
{visible.map(tpl => {
const Icon = templateIcons[tpl.id as TemplateId] ?? typeIcons[tpl.type] ?? Settings
const isInstalling = installingId === tpl.id
const nameKey = `agents.templates.${tpl.id}.name`
const nameKey = templateNameKey(tpl.id)
const descKey = `agents.templates.${tpl.id}.description`
return (
<div
key={tpl.id}
className="bg-card/40 border border-dashed border-border rounded-2xl p-6 group cursor-pointer hover:bg-card hover:border-foreground/20 transition-all"
className="bg-card/40 border border-dashed border-border rounded-2xl p-6 hover:bg-card hover:border-foreground/20 transition-all"
>
<div className="w-8 h-8 rounded-lg bg-muted flex items-center justify-center text-muted-foreground group-hover:bg-foreground group-hover:text-background mb-4 transition-all">
<div className="w-8 h-8 rounded-lg bg-muted flex items-center justify-center text-muted-foreground mb-4">
<Icon className="w-4 h-4" />
</div>
<div className="flex items-start justify-between gap-2 mb-2">
<h4 className="text-[13px] font-bold text-foreground">{t(nameKey)}</h4>
{tpl.frequency !== 'manual' && (
<span className="text-[10px] font-semibold text-concrete bg-border/20 rounded-full px-2 py-0.5 shrink-0">
{tpl.frequency === 'daily' ? '📅 Quotidien' : '📆 Hebdo'}
{t(`agents.frequencies.${tpl.frequency}`)}
</span>
)}
</div>
<p className="text-xs text-muted-foreground leading-relaxed mb-4">{t(descKey)}</p>
<button
type="button"
onClick={() => handleInstall(tpl)}
disabled={isInstalling}
className="text-[11px] font-bold uppercase tracking-widest text-foreground hover:opacity-60 transition-opacity flex items-center gap-2 disabled:opacity-50"
@@ -211,6 +231,25 @@ export function AgentTemplates({ onInstalled, existingAgentNames }: AgentTemplat
)
})}
</div>
{hiddenCount > 0 && (
<button
type="button"
onClick={() => setShowAll(true)}
className="text-[12px] font-semibold text-foreground hover:opacity-70 transition-opacity"
>
{t('agents.templates.seeAll')}
</button>
)}
{showAll && filtered.length > PREVIEW_COUNT && (
<button
type="button"
onClick={() => setShowAll(false)}
className="text-[12px] font-semibold text-foreground hover:opacity-70 transition-opacity"
>
{t('agents.templates.showLess')}
</button>
)}
</div>
)
}