From 9be640c4496062ca109b345b70f6764eaf2e3e62 Mon Sep 17 00:00:00 2001 From: sepehr Date: Sat, 16 May 2026 16:27:10 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20merge=20Context=20page=20into=20Glossar?= =?UTF-8?q?ies=20=E2=80=94=20single=20page=20for=20glossary=20+=20system?= =?UTF-8?q?=20prompt=20+=20presets?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add system prompt textarea and professional presets (HVAC, IT, Legal, etc.) to Glossaries page - Remove Context from sidebar navigation (constants.ts) - Make GlossarySelector always visible for Pro users (not just LLM mode) - Send system prompt from Zustand store to backend via custom_prompt - Add 24 new i18n keys across all 13 locales for glossaries page Co-Authored-By: Claude Opus 4.7 --- frontend/src/app/dashboard/constants.ts | 2 +- .../src/app/dashboard/glossaries/page.tsx | 161 +++++++++- frontend/src/app/dashboard/translate/page.tsx | 2 +- .../translate/useTranslationSubmit.ts | 5 + frontend/src/lib/i18n.tsx | 299 ++++++++++++++++++ 5 files changed, 460 insertions(+), 9 deletions(-) diff --git a/frontend/src/app/dashboard/constants.ts b/frontend/src/app/dashboard/constants.ts index 5cb69be..469aaeb 100644 --- a/frontend/src/app/dashboard/constants.ts +++ b/frontend/src/app/dashboard/constants.ts @@ -10,7 +10,7 @@ export interface NavItem { export const baseNavItems: NavItem[] = [ { labelKey: 'dashboard.nav.translate', href: '/dashboard/translate', icon: FileText }, { labelKey: 'dashboard.nav.profile', href: '/dashboard/profile', icon: User }, - { labelKey: 'dashboard.nav.context', href: '/dashboard/context', icon: Globe, proOnly: true }, + { labelKey: 'dashboard.nav.glossaries', href: '/dashboard/glossaries', icon: BookText, proOnly: true }, { labelKey: 'dashboard.nav.apiKeys', href: '/dashboard/api-keys', icon: Key, proOnly: true }, ]; diff --git a/frontend/src/app/dashboard/glossaries/page.tsx b/frontend/src/app/dashboard/glossaries/page.tsx index 6a70b37..1c57ad0 100644 --- a/frontend/src/app/dashboard/glossaries/page.tsx +++ b/frontend/src/app/dashboard/glossaries/page.tsx @@ -1,18 +1,34 @@ 'use client'; -import { useState } from 'react'; -import { BookText, Plus, Library, Calendar, Hash } from 'lucide-react'; +import { useState, useEffect } from 'react'; +import { + BookText, Plus, Library, Calendar, Hash, + Zap, Save, Trash2, MessageSquare, Loader2, + Wrench, HardHat, Monitor, Scale, Stethoscope, BarChart3, + Megaphone, Car, +} from 'lucide-react'; import { useUser } from '@/app/dashboard/useUser'; -import { useI18n, formatDate } from '@/lib/i18n'; +import { useI18n } from '@/lib/i18n'; import { useGlossaries, useGlossary } from './useGlossaries'; import type { Glossary, GlossaryTermInput, GlossaryListItem } from './types'; import { ProUpgradePrompt } from './ProUpgradePrompt'; -import { GlossaryCard } from './GlossaryCard'; import { CreateGlossaryDialog } from './CreateGlossaryDialog'; import { EditGlossaryDialog } from './EditGlossaryDialog'; import { DeleteGlossaryDialog } from './DeleteGlossaryDialog'; import { useToast } from '@/components/ui/toast'; -import { cn } from '@/lib/utils'; +import { useTranslationStore } from '@/lib/store'; +import { API_BASE } from '@/lib/config'; + +const PRESETS = [ + { key: 'hvac', title: 'HVAC / Génie climatique', desc: 'Thermique, ventilation, climatisation', icon: Wrench, templateId: 'hvac' }, + { key: 'construction', title: 'BTP / Construction', desc: 'Gros œuvre, second œuvre, normes', icon: HardHat, templateId: 'construction' }, + { key: 'it', title: 'IT / Logiciel', desc: 'Développement, infrastructure, DevOps', icon: Monitor, templateId: 'technology' }, + { key: 'legal', title: 'Juridique / Contrats', desc: 'Droit des affaires, contentieux', icon: Scale, templateId: 'legal' }, + { key: 'medical', title: 'Médical / Santé', desc: 'Pharmacologie, chirurgie, diagnostic', icon: Stethoscope, templateId: 'medical' }, + { key: 'finance', title: 'Finance / Comptabilité', desc: 'IFRS, bilans, fiscalité', icon: BarChart3, templateId: 'finance' }, + { key: 'marketing', title: 'Marketing / Publicité', desc: 'Digital, branding, analytics', icon: Megaphone, templateId: 'marketing' }, + { key: 'automotive', title: 'Automobile', desc: 'Motorisation, ADAS, homologation', icon: Car, templateId: 'automotive' }, +]; export default function GlossariesPage() { const { t } = useI18n(); @@ -31,6 +47,7 @@ export default function GlossariesPage() { importTemplate, } = useGlossaries(); const { toast } = useToast(); + const { settings, updateSettings } = useTranslationStore(); const [createDialogOpen, setCreateDialogOpen] = useState(false); const [editDialogOpen, setEditDialogOpen] = useState(false); @@ -38,6 +55,9 @@ export default function GlossariesPage() { const [selectedGlossary, setSelectedGlossary] = useState(null); const [glossaryToEdit, setGlossaryToEdit] = useState(null); const [glossaryToDelete, setGlossaryToDelete] = useState<{ id: string; name: string } | null>(null); + const [systemPrompt, setSystemPrompt] = useState(settings.systemPrompt); + const [isSavingPrompt, setIsSavingPrompt] = useState(false); + const [creatingPreset, setCreatingPreset] = useState(null); const { glossary: fullGlossary, isLoading: isLoadingGlossaryDetail } = useGlossary( selectedGlossary?.id || null @@ -46,6 +66,58 @@ export default function GlossariesPage() { const isPro = user?.tier === 'pro'; const isLoading = isLoadingUser || isLoadingGlossaries; + useEffect(() => { + setSystemPrompt(settings.systemPrompt); + }, [settings]); + + const handleSavePrompt = async () => { + setIsSavingPrompt(true); + try { + updateSettings({ systemPrompt }); + await new Promise(resolve => setTimeout(resolve, 300)); + toast({ title: t('context.saved'), description: t('context.savedDesc') }); + } finally { setIsSavingPrompt(false); } + }; + + const handleClearPrompt = () => { + updateSettings({ systemPrompt: '' }); + setSystemPrompt(''); + }; + + const handleCreatePresetGlossary = async (preset: typeof PRESETS[0]) => { + setCreatingPreset(preset.key); + try { + const token = localStorage.getItem('token'); + if (!token) return; + const headers: Record = { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, + }; + const params = new URLSearchParams({ template_id: preset.templateId }); + const res = await fetch(`${API_BASE}/api/v1/glossaries/import?${params.toString()}`, { + method: 'POST', + headers, + }); + if (res.ok) { + const result = await res.json(); + const glossary = result.data; + toast({ + title: t('context.presets.created'), + description: t('context.presets.createdDesc', { + name: glossary?.name ?? preset.title, + count: String(glossary?.terms?.length ?? 0), + }), + }); + } else { + toast({ variant: 'destructive', title: t('glossaries.toast.error'), description: t('glossaries.toast.errorCreate') }); + } + } catch { + toast({ variant: 'destructive', title: t('glossaries.toast.error'), description: t('glossaries.toast.errorImport') }); + } finally { + setCreatingPreset(null); + } + }; + const handleEditClick = (id: string) => { const glossary = glossaries.find((g: GlossaryListItem) => g.id === id); if (glossary) { @@ -171,7 +243,81 @@ export default function GlossariesPage() { - {/* ── Glossary Grid ──────────────────────────────────────── */} +
+ {/* ── System Prompt ────────────────────────────────────── */} +
+
+ +

+ {t('context.instructions.title')} +

+
+

{t('context.instructions.desc')}

+