feat: document language checker on round overview
All checks were successful
Build and Push Docker Image / build (push) Successful in 9m15s
All checks were successful
Build and Push Docker Image / build (push) Successful in 9m15s
- New roundLanguageSummary query in file router aggregates per-round document language data from existing detectedLang/langConfidence fields - Document Languages card on round overview tab shows analysis status and flags non-English documents grouped by project with confidence scores - Green border when all documents are English, amber when issues detected - Project names link to project detail page for easy navigation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -77,6 +77,8 @@ import {
|
||||
ArrowRight,
|
||||
RotateCcw,
|
||||
ListChecks,
|
||||
FileText,
|
||||
Languages,
|
||||
} from 'lucide-react'
|
||||
import {
|
||||
Tooltip,
|
||||
@@ -1471,6 +1473,9 @@ export default function RoundDetailPage() {
|
||||
</Card>
|
||||
</AnimatedCard>
|
||||
</div>
|
||||
|
||||
{/* Document Language Summary */}
|
||||
<DocumentLanguageSummary roundId={roundId as string} />
|
||||
</TabsContent>
|
||||
|
||||
{/* ═══════════ PROJECTS TAB ═══════════ */}
|
||||
@@ -2482,3 +2487,75 @@ export default function RoundDetailPage() {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Document Language Summary — flags non-English docs
|
||||
// =============================================================================
|
||||
|
||||
const LANG_NAMES: Record<string, string> = {
|
||||
eng: 'English', fra: 'French', deu: 'German', spa: 'Spanish', ita: 'Italian',
|
||||
por: 'Portuguese', nld: 'Dutch', rus: 'Russian', ara: 'Arabic', zho: 'Chinese',
|
||||
jpn: 'Japanese', kor: 'Korean', tur: 'Turkish', pol: 'Polish', ron: 'Romanian',
|
||||
ces: 'Czech', ell: 'Greek', hun: 'Hungarian', swe: 'Swedish', dan: 'Danish',
|
||||
fin: 'Finnish', nor: 'Norwegian', und: 'Unknown',
|
||||
}
|
||||
|
||||
function DocumentLanguageSummary({ roundId }: { roundId: string }) {
|
||||
const { data, isLoading } = trpc.file.roundLanguageSummary.useQuery(
|
||||
{ roundId },
|
||||
{ refetchInterval: 60_000 }
|
||||
)
|
||||
|
||||
if (isLoading || !data) return null
|
||||
if (data.totalFiles === 0) return null
|
||||
|
||||
const allGood = data.nonEnglishCount === 0 && data.unanalyzedCount === 0
|
||||
|
||||
return (
|
||||
<Card className={allGood ? 'border-green-200 bg-green-50/30' : data.nonEnglishCount > 0 ? 'border-amber-300 bg-amber-50/30' : ''}>
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-sm flex items-center gap-2">
|
||||
<Languages className={cn('h-4 w-4', allGood ? 'text-green-600' : data.nonEnglishCount > 0 ? 'text-amber-600' : 'text-muted-foreground')} />
|
||||
Document Languages
|
||||
{data.nonEnglishCount > 0 && (
|
||||
<Badge variant="destructive" className="text-[10px] px-1.5 py-0">
|
||||
{data.nonEnglishCount} flagged
|
||||
</Badge>
|
||||
)}
|
||||
</CardTitle>
|
||||
<CardDescription className="text-xs">
|
||||
{data.analyzedCount} of {data.totalFiles} documents analyzed
|
||||
{data.unanalyzedCount > 0 && ` — ${data.unanalyzedCount} pending`}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
{data.nonEnglishCount > 0 && (
|
||||
<CardContent className="space-y-2">
|
||||
{data.flaggedProjects.map((project) => (
|
||||
<div key={project.projectId} className="rounded-md border bg-white p-3">
|
||||
<Link
|
||||
href={`/admin/projects/${project.projectId}` as Route}
|
||||
className="text-sm font-medium text-primary hover:underline"
|
||||
>
|
||||
{project.projectTitle}
|
||||
</Link>
|
||||
<div className="mt-1.5 space-y-1">
|
||||
{project.files.map((file) => (
|
||||
<div key={file.id} className="flex items-center justify-between text-xs">
|
||||
<div className="flex items-center gap-1.5 min-w-0">
|
||||
<FileText className="h-3 w-3 text-muted-foreground shrink-0" />
|
||||
<span className="truncate">{file.fileName}</span>
|
||||
</div>
|
||||
<Badge variant="outline" className="text-[10px] px-1.5 py-0 shrink-0 ml-2 border-amber-300 text-amber-700">
|
||||
{LANG_NAMES[file.detectedLang ?? ''] || file.detectedLang}
|
||||
{file.langConfidence != null && ` (${Math.round(file.langConfidence * 100)}%)`}
|
||||
</Badge>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</CardContent>
|
||||
)}
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user