mirror of
https://github.com/SamyRai/turash.git
synced 2025-12-26 23:01:33 +00:00
Some checks failed
CI/CD Pipeline / backend-lint (push) Failing after 31s
CI/CD Pipeline / backend-build (push) Has been skipped
CI/CD Pipeline / frontend-lint (push) Failing after 1m37s
CI/CD Pipeline / frontend-build (push) Has been skipped
CI/CD Pipeline / e2e-test (push) Has been skipped
- Replace all 'any' types with proper TypeScript interfaces - Fix React hooks setState in useEffect issues with lazy initialization - Remove unused variables and imports across all files - Fix React Compiler memoization dependency issues - Add comprehensive i18n translation keys for admin interfaces - Apply consistent prettier formatting throughout codebase - Clean up unused bulk editing functionality - Improve type safety and code quality across frontend Files changed: 39 - ImpactMetrics.tsx: Fixed any types and interfaces - AdminVerificationQueuePage.tsx: Added i18n keys, removed unused vars - LocalizationUIPage.tsx: Fixed memoization, added translations - LocalizationDataPage.tsx: Added type safety and translations - And 35+ other files with various lint fixes
156 lines
5.4 KiB
TypeScript
156 lines
5.4 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 { useTranslation } from '@/hooks/useI18n';
|
|
import { useState } from 'react';
|
|
|
|
interface MissingTranslationsData {
|
|
total: number;
|
|
counts?: Record<string, number>;
|
|
results?: Record<string, string[] | Record<string, string>>;
|
|
}
|
|
|
|
const entityOptions = [
|
|
{ value: 'organization', label: 'Organization' },
|
|
{ value: 'site', label: 'Site' },
|
|
{ value: 'heritage_title', label: 'Heritage Title' },
|
|
{ value: 'product', label: 'Product' },
|
|
];
|
|
|
|
const LocalizationDataPage = () => {
|
|
const { t } = useTranslation();
|
|
const { success, error } = useToast();
|
|
const [entityType, setEntityType] = useState('organization');
|
|
const [locale, setLocale] = useState('en');
|
|
const [fieldsInput, setFieldsInput] = useState('name,description');
|
|
const [missing, setMissing] = useState<MissingTranslationsData | null>(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 {
|
|
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 {
|
|
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">{t('admin.localization.data.title')}</h1>
|
|
<p className="text-muted-foreground">{t('admin.localization.data.description')}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>{t('admin.localization.data.query')}</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
<div>
|
|
<Label>{t('admin.localization.data.entityType')}</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>{t('admin.localization.data.targetLocale')}</Label>
|
|
<select
|
|
value={locale}
|
|
onChange={(e) => setLocale(e.target.value)}
|
|
className="w-full rounded-md border px-2 py-1"
|
|
>
|
|
<option value="en">{t('admin.localization.data.english')}</option>
|
|
<option value="tt">{t('admin.localization.data.tatar')}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<Label>{t('admin.localization.data.fields')}</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
|
|
? t('admin.localization.data.loading')
|
|
: t('admin.localization.data.findMissing')}
|
|
</Button>
|
|
<Button variant="outline" onClick={handleBulkTranslate} disabled={isTranslating}>
|
|
{isTranslating
|
|
? t('admin.localization.data.translating')
|
|
: t('admin.localization.data.bulkTranslate')}
|
|
</Button>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{missing && (
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>{t('admin.localization.data.results')}</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="space-y-4">
|
|
<div>
|
|
{t('admin.localization.data.totalMissing')} {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;
|