Some checks failed
Deploy to Production / Build and Deploy (push) Failing after 1m7s
Replaced ~100+ hardcoded French and English text strings across 30+ components with proper i18n t() calls. Added 57 new translation keys to all 15 locale files (ar, de, en, es, fa, fr, hi, it, ja, ko, nl, pl, pt, ru, zh). Key changes: - contextual-ai-chat.tsx: 30 French strings → t() (actions, toasts, labels, placeholders) - ai-chat.tsx: 15 French/English strings → t() (header, tabs, welcome, insights, history) - note-inline-editor.tsx: 20 French fallbacks removed (toolbar, save status, checklist) - lab-skeleton.tsx: French loading text → t() - admin-header.tsx, header.tsx, editor-connections-section.tsx: French fallbacks removed - New AI chat component, agent cards, sidebar, settings panel i18n cleanup Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
151 lines
5.3 KiB
TypeScript
151 lines
5.3 KiB
TypeScript
'use client'
|
|
|
|
/**
|
|
* Agent Templates Gallery
|
|
* Pre-built agent configurations that users can install in one click.
|
|
*/
|
|
|
|
import { useState } from 'react'
|
|
import {
|
|
Globe,
|
|
Search,
|
|
Eye,
|
|
Settings,
|
|
Plus,
|
|
Loader2,
|
|
} from 'lucide-react'
|
|
import { toast } from 'sonner'
|
|
import { useLanguage } from '@/lib/i18n'
|
|
|
|
interface AgentTemplatesProps {
|
|
onInstalled: () => void
|
|
existingAgentNames: string[]
|
|
}
|
|
|
|
const templateConfig = [
|
|
{ id: 'veilleAI', type: 'scraper', roleKey: 'agents.defaultRoles.scraper', urls: [
|
|
'https://www.theverge.com/rss/ai-artificial-intelligence/index.xml',
|
|
'https://techcrunch.com/category/artificial-intelligence/feed/',
|
|
'https://feeds.arstechnica.com/arstechnica/technology-lab',
|
|
'https://www.technologyreview.com/feed/',
|
|
'https://www.wired.com/feed/',
|
|
'https://korben.info/feed',
|
|
], frequency: 'weekly' },
|
|
{ id: 'veilleTech', type: 'scraper', roleKey: 'agents.defaultRoles.scraper', urls: [
|
|
'https://news.ycombinator.com/rss',
|
|
'https://dev.to/feed',
|
|
'https://www.producthunt.com/feed',
|
|
], frequency: 'daily' },
|
|
{ id: 'veilleDev', type: 'scraper', roleKey: 'agents.defaultRoles.scraper', urls: [
|
|
'https://dev.to/feed/tag/javascript',
|
|
'https://dev.to/feed/tag/typescript',
|
|
'https://dev.to/feed/tag/react',
|
|
], frequency: 'weekly' },
|
|
{ id: 'surveillant', type: 'monitor', roleKey: 'agents.defaultRoles.monitor', urls: [], frequency: 'weekly' },
|
|
{ id: 'chercheur', type: 'researcher', roleKey: 'agents.defaultRoles.researcher', urls: [], frequency: 'manual' },
|
|
] as const
|
|
|
|
const typeIcons: Record<string, typeof Globe> = {
|
|
scraper: Globe,
|
|
researcher: Search,
|
|
monitor: Eye,
|
|
custom: Settings,
|
|
}
|
|
|
|
const typeColors: Record<string, string> = {
|
|
scraper: 'text-blue-600 bg-blue-50',
|
|
researcher: 'text-purple-600 bg-purple-50',
|
|
monitor: 'text-amber-600 bg-amber-50',
|
|
custom: 'text-green-600 bg-green-50',
|
|
}
|
|
|
|
export function AgentTemplates({ onInstalled, existingAgentNames }: AgentTemplatesProps) {
|
|
const { t } = useLanguage()
|
|
const [installingId, setInstallingId] = useState<string | null>(null)
|
|
|
|
const handleInstall = async (tpl: typeof templateConfig[number]) => {
|
|
setInstallingId(tpl.id)
|
|
try {
|
|
const { createAgent } = await import('@/app/actions/agent-actions')
|
|
const nameKey = `agents.templates.${tpl.id}.name` as const
|
|
const descKey = `agents.templates.${tpl.id}.description` as const
|
|
const baseName = t(nameKey)
|
|
let resolvedName = baseName
|
|
if (existingAgentNames.includes(baseName)) {
|
|
let n = 2
|
|
while (existingAgentNames.includes(`${baseName} ${n}`)) n++
|
|
resolvedName = `${baseName} ${n}`
|
|
}
|
|
await createAgent({
|
|
name: resolvedName,
|
|
description: t(descKey),
|
|
type: tpl.type,
|
|
role: t(tpl.roleKey),
|
|
sourceUrls: tpl.urls.length > 0 ? [...tpl.urls] : undefined,
|
|
frequency: tpl.frequency,
|
|
tools: tpl.type === 'scraper'
|
|
? ['web_scrape', 'note_create']
|
|
: tpl.type === 'researcher'
|
|
? ['web_search', 'web_scrape', 'note_search', 'note_create']
|
|
: tpl.type === 'monitor'
|
|
? ['note_search', 'note_read', 'note_create']
|
|
: [],
|
|
})
|
|
toast.success(t('agents.toasts.installSuccess', { name: resolvedName }))
|
|
onInstalled()
|
|
} catch {
|
|
toast.error(t('agents.toasts.installError'))
|
|
} finally {
|
|
setInstallingId(null)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
<h3 className="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-3">
|
|
{t('agents.templates.title')}
|
|
</h3>
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
|
|
{templateConfig.map(tpl => {
|
|
const Icon = typeIcons[tpl.type] || Settings
|
|
const isInstalling = installingId === tpl.id
|
|
const nameKey = `agents.templates.${tpl.id}.name`
|
|
const descKey = `agents.templates.${tpl.id}.description`
|
|
|
|
return (
|
|
<div
|
|
key={tpl.id}
|
|
className="border-2 border-dashed border-slate-200 dark:border-slate-700 rounded-xl p-4 hover:border-primary/30 hover:bg-primary/[0.02] transition-all group"
|
|
>
|
|
<div className="flex items-center gap-2.5 mb-2">
|
|
<div className={`p-1.5 rounded-lg ${typeColors[tpl.type]}`}>
|
|
<Icon className="w-4 h-4" />
|
|
</div>
|
|
<h4 className="font-medium text-sm text-foreground">{t(nameKey)}</h4>
|
|
</div>
|
|
<p className="text-xs text-muted-foreground mb-3 line-clamp-2">{t(descKey)}</p>
|
|
<button
|
|
onClick={() => handleInstall(tpl)}
|
|
disabled={isInstalling}
|
|
className="flex items-center gap-1.5 text-xs font-medium text-primary hover:text-primary/80 transition-colors disabled:opacity-50"
|
|
>
|
|
{isInstalling ? (
|
|
<>
|
|
<Loader2 className="w-3.5 h-3.5 animate-spin" />
|
|
{t('agents.templates.installing')}
|
|
</>
|
|
) : (
|
|
<>
|
|
<Plus className="w-3.5 h-3.5" />
|
|
{t('agents.templates.install')}
|
|
</>
|
|
)}
|
|
</button>
|
|
</div>
|
|
)
|
|
})}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|