Files
SkriptAI-fe/src/components/skriptai/ProjectDetail.tsx
2026-03-23 02:00:01 +03:00

242 lines
8.1 KiB
TypeScript

'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
import { useTranslations } from 'next-intl';
import {
Box,
Button,
Container,
Flex,
Heading,
HStack,
IconButton,
Spinner,
Tabs,
Text,
VStack,
Badge,
} from '@chakra-ui/react';
import {
LuArrowLeft,
LuPlay,
LuFileText,
LuSearch,
LuUsers,
LuBrain,
LuChartBar,
LuDownload,
} from 'react-icons/lu';
import {
useGetProject,
useGenerateScript,
useDeepResearch,
useNeuroAnalysis,
useYoutubeAudit,
projectsService,
} from '@/lib/api/skriptai';
// Tab Components
import ResearchTab from './tabs/ResearchTab';
import { BriefTab, CharactersTab, ScriptTab, AnalysisTab } from './tabs';
interface ProjectDetailProps {
projectId: string;
}
/**
* Project Detail Component
*
* Full project view with tabbed interface for research, brief, characters, script, and analysis.
*/
export default function ProjectDetail({ projectId }: ProjectDetailProps) {
const t = useTranslations('projectDashboard');
const router = useRouter();
const [activeTab, setActiveTab] = useState('research');
// Queries
const { data: project, isLoading, error } = useGetProject(projectId);
const generateScript = useGenerateScript(projectId);
const deepResearch = useDeepResearch(projectId);
const neuroAnalysis = useNeuroAnalysis(projectId);
const youtubeAudit = useYoutubeAudit(projectId);
// Export to JSON
const handleExport = async () => {
try {
const response = await projectsService.exportToJson(projectId);
const data = response.data;
const blob = new Blob([JSON.stringify(data, null, 2)], {
type: 'application/json',
});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${project?.topic || 'project'}.json`;
a.click();
URL.revokeObjectURL(url);
} catch (err) {
console.error('Export failed:', err);
}
};
if (isLoading) {
return (
<Flex justify='center' align='center' minH='400px'>
<Spinner size='xl' color='blue.500' />
</Flex>
);
}
if (error || !project) {
return (
<Container maxW='4xl' py={10}>
<VStack gap={4}>
<Text color='red.500'>{t('projectNotFound')}</Text>
<Button onClick={() => router.push('/skriptai')}>
{t('backToDashboard')}
</Button>
</VStack>
</Container>
);
}
return (
<Container maxW='8xl' py={6}>
{/* Header */}
<Flex justify='space-between' align='start' mb={6} flexWrap='wrap' gap={4}>
<VStack align='start' gap={2}>
<HStack>
<IconButton
aria-label={t('back')}
variant='ghost'
onClick={() => router.push('/skriptai')}
>
<LuArrowLeft />
</IconButton>
<Heading size='lg'>{project.topic}</Heading>
</HStack>
<HStack gap={2} flexWrap='wrap'>
<Badge colorPalette='blue'>{project.contentType}</Badge>
<Badge colorPalette='green'>
{project.speechStyle?.join(', ')}
</Badge>
<Badge colorPalette='purple'>{project.targetDuration}</Badge>
<Badge colorPalette='gray'>
{project.language?.toUpperCase()}
</Badge>
</HStack>
{project.logline && (
<Text color='gray.500' fontStyle='italic' maxW='600px'>
{project.logline}
</Text>
)}
</VStack>
<HStack gap={2}>
<Button
variant='outline'
onClick={handleExport}
>
<LuDownload />
{t('export')}
</Button>
<Button
colorPalette='blue'
onClick={() => generateScript.mutate()}
loading={generateScript.isPending}
disabled={!project.sources || project.sources.length === 0}
>
<LuPlay />
{t('generateScript')}
</Button>
</HStack>
</Flex>
{/* Tabs */}
<Tabs.Root
value={activeTab}
onValueChange={(e) => setActiveTab(e.value)}
>
<Tabs.List mb={6}>
<Tabs.Trigger value='research'>
<LuSearch />
{t('research')}
{project.sources && (
<Badge ml={2} size='sm'>
{project.sources.length}
</Badge>
)}
</Tabs.Trigger>
<Tabs.Trigger value='brief'>
<LuFileText />
{t('brief')}
</Tabs.Trigger>
<Tabs.Trigger value='characters'>
<LuUsers />
{t('characters')}
</Tabs.Trigger>
<Tabs.Trigger value='script'>
<LuFileText />
{t('script')}
{project.segments && (
<Badge ml={2} size='sm'>
{project.segments.length}
</Badge>
)}
</Tabs.Trigger>
<Tabs.Trigger value='analysis'>
<LuChartBar />
{t('analysis')}
</Tabs.Trigger>
</Tabs.List>
<Box minH='500px'>
<Tabs.Content value='research'>
<ResearchTab
project={project}
onResearch={() => deepResearch.mutate(undefined)}
isResearching={deepResearch.isPending}
onNext={() => setActiveTab('brief')}
/>
</Tabs.Content>
<Tabs.Content value='brief'>
<BriefTab
project={project}
onNext={() => setActiveTab('characters')}
/>
</Tabs.Content>
<Tabs.Content value='characters'>
<CharactersTab
project={project}
onNext={() => setActiveTab('script')}
/>
</Tabs.Content>
<Tabs.Content value='script'>
<ScriptTab
project={project}
onGenerate={() => generateScript.mutate(undefined)}
isGenerating={generateScript.isPending}
/>
</Tabs.Content>
<Tabs.Content value='analysis'>
<AnalysisTab
project={project}
onNeuroAnalysis={() => neuroAnalysis.mutate()}
onYoutubeAudit={() => youtubeAudit.mutate()}
isAnalyzing={neuroAnalysis.isPending || youtubeAudit.isPending}
/>
</Tabs.Content>
</Box>
</Tabs.Root>
</Container>
);
}