turash/bugulma/frontend/pages/admin/LocalizationDataPage.tsx
2025-12-15 10:06:41 +01:00

142 lines
4.7 KiB
TypeScript

import { Button, Card, CardContent, CardHeader, CardTitle, Input, Label } from '@/components/ui';
import { useToast } from '@/hooks/useToast.ts';
import { bulkTranslateData, getMissingTranslations } from '@/services/admin-api.ts';
import { useState } from 'react';
const entityOptions = [
{ value: 'organization', label: 'Organization' },
{ value: 'site', label: 'Site' },
{ value: 'heritage_title', label: 'Heritage Title' },
{ value: 'product', label: 'Product' },
];
const LocalizationDataPage = () => {
const { success, error } = useToast();
const [entityType, setEntityType] = useState('organization');
const [locale, setLocale] = useState('en');
const [fieldsInput, setFieldsInput] = useState('name,description');
const [missing, setMissing] = useState<any>(null);
const [isLoading, setIsLoading] = useState(false);
const [isTranslating, setIsTranslating] = useState(false);
const handleFindMissing = async () => {
setIsLoading(true);
try {
const res = await getMissingTranslations(entityType, locale);
setMissing(res);
} catch (err) {
error('Failed to fetch missing translations');
} finally {
setIsLoading(false);
}
};
const handleBulkTranslate = async () => {
setIsTranslating(true);
try {
const fields = fieldsInput
.split(',')
.map((f) => f.trim())
.filter(Boolean);
const res = await bulkTranslateData(entityType, [], locale, fields);
success(`Translated ${res.translated} items`);
// Refresh missing
await handleFindMissing();
} catch (err) {
error('Bulk translate failed');
} finally {
setIsTranslating(false);
}
};
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold">Data Translations</h1>
<p className="text-muted-foreground">Find and bulk-translate missing data translations</p>
</div>
</div>
<Card>
<CardHeader>
<CardTitle>Query</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<Label>Entity Type</Label>
<select
value={entityType}
onChange={(e) => setEntityType(e.target.value)}
className="w-full rounded-md border px-2 py-1"
>
{entityOptions.map((opt) => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
</div>
<div>
<Label>Target Locale</Label>
<select
value={locale}
onChange={(e) => setLocale(e.target.value)}
className="w-full rounded-md border px-2 py-1"
>
<option value="en">English (en)</option>
<option value="tt">Tatar (tt)</option>
</select>
</div>
<div>
<Label>Fields (comma-separated)</Label>
<Input value={fieldsInput} onChange={(e) => setFieldsInput(e.target.value)} />
</div>
</div>
<div className="mt-4 flex gap-2">
<Button onClick={handleFindMissing} disabled={isLoading}>
{isLoading ? 'Loading...' : 'Find Missing'}
</Button>
<Button variant="outline" onClick={handleBulkTranslate} disabled={isTranslating}>
{isTranslating ? 'Translating...' : 'Bulk Translate Missing'}
</Button>
</div>
</CardContent>
</Card>
{missing && (
<Card>
<CardHeader>
<CardTitle>Results</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-4">
<div>Total missing: {missing.total}</div>
{Object.entries(missing.counts || {}).map(([field, count]) => (
<div key={field} className="p-2 border rounded">
<div className="font-medium">
{field}: {count}
</div>
<div className="mt-2 text-sm text-muted-foreground">
{missing.results && missing.results[field] && missing.results[field].slice
? missing.results[field].slice(0, 10).join(', ')
: missing.results && missing.results[field]
? Object.values(missing.results[field]).slice(0, 10).join(', ')
: '—'}
</div>
</div>
))}
</div>
</CardContent>
</Card>
)}
</div>
);
};
export default LocalizationDataPage;