/** * Компонент детального просмотра персоны * Показывает подробную информацию о персоне, включая все обнаруженные лица */ import { Calendar, Camera, Clock, Image, MapPin, Tag, User } from "lucide-react" import { useState } from "react" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { ScrollArea } from "@/components/ui/scroll-area" import { Separator } from "@/components/ui/separator" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import type { PersonAppearance, PersonProfile, Timecode } from "../types/person" interface PersonDetailProps { person: PersonProfile appearances?: PersonAppearance[] onEdit: () => void onClose: () => void } // Вспомогательная функция для форматирования Timecode function formatTimecode(timecode: Timecode): string { const hours = Math.floor(timecode.seconds / 3600) const minutes = Math.floor((timecode.seconds % 3600) / 60) const seconds = Math.floor(timecode.seconds % 60) if (hours > 0) { return `${hours}:${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}` } return `${minutes}:${seconds.toString().padStart(2, "0")}` } export function PersonDetail({ person, appearances = [], onEdit, onClose }: PersonDetailProps) { const [selectedTab, setSelectedTab] = useState("overview") // Группируем появления по проектам/клипам const appearancesByClip = appearances.reduce>((acc, appearance) => { const clipId = appearance.clipId if (!acc[clipId]) { acc[clipId] = [] } acc[clipId].push(appearance) return acc }, {}) // Статистика const totalAppearances = appearances.length const totalDuration = appearances.reduce((sum, app) => sum + (app.duration || 0), 0) const averageConfidence = appearances.length > 0 ? appearances.reduce((sum, app) => sum + app.confidence, 0) / appearances.length : 0 return (
{/* Заголовок */}
{person.thumbnails && person.thumbnails.length > 0 && person.thumbnails[0].imageUrl ? ( {person.name} ) : ( )}

{person.name}

{person.notes &&

{person.notes}

}
{/* Содержимое */}
Обзор Лица ({person.faceEmbeddings?.length || 0}) Появления ({totalAppearances}) {/* Вкладка "Обзор" */} {/* Основная информация */}
Первое появление: {formatTimecode(person.firstSeen)}
Последнее появление: {formatTimecode(person.lastSeen)}
Лиц обнаружено: {person.faceEmbeddings?.length || 0}
Появлений: {totalAppearances}
Общее время: {Math.round(totalDuration)}с
Уверенность: {Math.round(averageConfidence * 100)}%
{/* Теги */} {person.tags && person.tags.length > 0 && (
Теги
{person.tags.map((tag) => ( {tag} ))}
)} {/* Заметки */} {person.notes && (

Заметки

{person.notes}

)}
{/* Вкладка "Лица" */} {person.thumbnails && person.thumbnails.length > 0 ? (
{person.thumbnails.map((thumbnail, index) => (
{thumbnail.imageUrl ? ( {`Thumbnail ) : ( )}
Качество: {Math.round(thumbnail.quality * 100)}%
{thumbnail.width}×{thumbnail.height}px
))}
) : (

Лица еще не обнаружены

)}
{/* Вкладка "Появления" */} {appearances.length > 0 ? (
{Object.entries(appearancesByClip).map(([clipId, clipAppearances]) => (

Клип: {clipId}

{clipAppearances.map((appearance, index) => (
{formatTimecode(appearance.startTime)} - {formatTimecode(appearance.endTime)}
{Math.round(appearance.confidence * 100)}% {appearance.duration && ( {Math.round(appearance.duration)}с )}
))}
))}
) : (

Появления на Timeline пока не найдены

)}
) }