feat: add source/target language selectors to edit glossary dialog
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m24s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m24s
Users can now change the language pair when editing a glossary: - EditGlossaryDialog has source/target language dropdowns - Default target_language changed from 'en' to 'multi' in create dialog - onSave passes source_language and target_language to the backend - Backend PATCH endpoint already supports updating these fields Also fixes: - CreateGlossaryDialog defaults to 'multi' instead of 'en' - SUPPORTED_LANGUAGES now includes 'multi' option for target Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -292,7 +292,7 @@ export function CreateGlossaryDialog({
|
|||||||
const [name, setName] = useState('');
|
const [name, setName] = useState('');
|
||||||
const [nameAutoFilled, setNameAutoFilled] = useState(false);
|
const [nameAutoFilled, setNameAutoFilled] = useState(false);
|
||||||
const [sourceLanguage, setSourceLanguage] = useState('fr');
|
const [sourceLanguage, setSourceLanguage] = useState('fr');
|
||||||
const [targetLanguage, setTargetLanguage] = useState('en');
|
const [targetLanguage, setTargetLanguage] = useState('multi');
|
||||||
const [terms, setTerms] = useState<GlossaryTermInput[]>([{ source: '', target: '' }]);
|
const [terms, setTerms] = useState<GlossaryTermInput[]>([{ source: '', target: '' }]);
|
||||||
const [fileTerms, setFileTerms] = useState<GlossaryTermInput[]>([]);
|
const [fileTerms, setFileTerms] = useState<GlossaryTermInput[]>([]);
|
||||||
const [selectedTemplate, setSelectedTemplate] = useState<GlossaryTemplate | null>(null);
|
const [selectedTemplate, setSelectedTemplate] = useState<GlossaryTemplate | null>(null);
|
||||||
@@ -305,7 +305,7 @@ export function CreateGlossaryDialog({
|
|||||||
setName('');
|
setName('');
|
||||||
setNameAutoFilled(false);
|
setNameAutoFilled(false);
|
||||||
setSourceLanguage('fr');
|
setSourceLanguage('fr');
|
||||||
setTargetLanguage('en');
|
setTargetLanguage('multi');
|
||||||
setTerms([{ source: '', target: '' }]);
|
setTerms([{ source: '', target: '' }]);
|
||||||
setFileTerms([]);
|
setFileTerms([]);
|
||||||
setSelectedTemplate(null);
|
setSelectedTemplate(null);
|
||||||
|
|||||||
@@ -17,13 +17,13 @@ import { TermEditor } from './TermEditor';
|
|||||||
import { exportGlossaryToCsv, parseCsvToTerms } from './csvUtils';
|
import { exportGlossaryToCsv, parseCsvToTerms } from './csvUtils';
|
||||||
import { useToast } from '@/components/ui/toast';
|
import { useToast } from '@/components/ui/toast';
|
||||||
import type { Glossary, GlossaryTermInput } from './types';
|
import type { Glossary, GlossaryTermInput } from './types';
|
||||||
import { MAX_TERMS_PER_GLOSSARY } from './types';
|
import { MAX_TERMS_PER_GLOSSARY, SUPPORTED_LANGUAGES } from './types';
|
||||||
|
|
||||||
interface EditGlossaryDialogProps {
|
interface EditGlossaryDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onOpenChange: (open: boolean) => void;
|
onOpenChange: (open: boolean) => void;
|
||||||
glossary: Glossary | null;
|
glossary: Glossary | null;
|
||||||
onSave: (id: string, data: { name: string; terms: GlossaryTermInput[] }) => Promise<void>;
|
onSave: (id: string, data: { name: string; source_language: string; target_language: string; terms: GlossaryTermInput[] }) => Promise<void>;
|
||||||
isSaving: boolean;
|
isSaving: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,6 +35,8 @@ export function EditGlossaryDialog({
|
|||||||
isSaving,
|
isSaving,
|
||||||
}: EditGlossaryDialogProps) {
|
}: EditGlossaryDialogProps) {
|
||||||
const [name, setName] = useState('');
|
const [name, setName] = useState('');
|
||||||
|
const [sourceLanguage, setSourceLanguage] = useState('fr');
|
||||||
|
const [targetLanguage, setTargetLanguage] = useState('multi');
|
||||||
const [terms, setTerms] = useState<GlossaryTermInput[]>([]);
|
const [terms, setTerms] = useState<GlossaryTermInput[]>([]);
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
@@ -42,6 +44,8 @@ export function EditGlossaryDialog({
|
|||||||
|
|
||||||
if (glossary && !isInitialized.current) {
|
if (glossary && !isInitialized.current) {
|
||||||
setName(glossary.name);
|
setName(glossary.name);
|
||||||
|
setSourceLanguage(glossary.source_language || 'fr');
|
||||||
|
setTargetLanguage(glossary.target_language || 'multi');
|
||||||
setTerms(glossary.terms.map(t => ({ source: t.source, target: t.target })));
|
setTerms(glossary.terms.map(t => ({ source: t.source, target: t.target })));
|
||||||
isInitialized.current = true;
|
isInitialized.current = true;
|
||||||
}
|
}
|
||||||
@@ -57,9 +61,11 @@ export function EditGlossaryDialog({
|
|||||||
|
|
||||||
await onSave(glossary.id, {
|
await onSave(glossary.id, {
|
||||||
name: name.trim(),
|
name: name.trim(),
|
||||||
|
source_language: sourceLanguage,
|
||||||
|
target_language: targetLanguage,
|
||||||
terms: validTerms,
|
terms: validTerms,
|
||||||
});
|
});
|
||||||
}, [glossary, name, terms, onSave]);
|
}, [glossary, name, sourceLanguage, targetLanguage, terms, onSave]);
|
||||||
|
|
||||||
const handleExport = useCallback(() => {
|
const handleExport = useCallback(() => {
|
||||||
if (!glossary) return;
|
if (!glossary) return;
|
||||||
@@ -154,6 +160,36 @@ export function EditGlossaryDialog({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="flex-1">
|
||||||
|
<Label className="text-xs text-muted-foreground mb-1 block">Langue source</Label>
|
||||||
|
<select
|
||||||
|
value={sourceLanguage}
|
||||||
|
onChange={e => setSourceLanguage(e.target.value)}
|
||||||
|
disabled={isSaving}
|
||||||
|
className="w-full h-9 rounded-md border border-input bg-background px-3 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
||||||
|
>
|
||||||
|
{SUPPORTED_LANGUAGES.filter(l => l.code !== 'multi').map(l => (
|
||||||
|
<option key={l.code} value={l.code}>{l.flag} {l.label}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div className="pt-5 text-muted-foreground font-bold">→</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<Label className="text-xs text-muted-foreground mb-1 block">Langue cible</Label>
|
||||||
|
<select
|
||||||
|
value={targetLanguage}
|
||||||
|
onChange={e => setTargetLanguage(e.target.value)}
|
||||||
|
disabled={isSaving}
|
||||||
|
className="w-full h-9 rounded-md border border-input bg-background px-3 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
||||||
|
>
|
||||||
|
{SUPPORTED_LANGUAGES.map(l => (
|
||||||
|
<option key={l.code} value={l.code}>{l.flag} {l.label}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Terms ({validTermsCount} valid)</Label>
|
<Label>Terms ({validTermsCount} valid)</Label>
|
||||||
<TermEditor
|
<TermEditor
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ export default function GlossariesPage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSaveGlossary = async (id: string, data: { name: string; terms: GlossaryTermInput[] }) => {
|
const handleSaveGlossary = async (id: string, data: { name: string; source_language: string; target_language: string; terms: GlossaryTermInput[] }) => {
|
||||||
try {
|
try {
|
||||||
await updateGlossary(id, data);
|
await updateGlossary(id, data);
|
||||||
setEditDialogOpen(false);
|
setEditDialogOpen(false);
|
||||||
|
|||||||
Reference in New Issue
Block a user