codenuk_frontend_mine/src/app/github/analyze/page.tsx
2025-10-10 09:02:08 +05:30

330 lines
13 KiB
TypeScript

"use client"
import { useEffect, useState, Suspense } from "react"
import { useRouter, useSearchParams } from "next/navigation"
import Link from "next/link"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"
import { Progress } from "@/components/ui/progress"
import {
ArrowLeft,
Brain,
Code,
FileText,
GitBranch,
Star,
Shield,
TrendingUp,
AlertTriangle,
CheckCircle,
Clock,
Zap,
Target,
BarChart3,
Layers,
Cpu,
Search,
File,
Folder,
Eye,
AlertCircle
} from "lucide-react"
interface FileAnalysis {
id: string
name: string
path: string
type: 'file' | 'folder'
status: 'scanning' | 'analyzing' | 'completed' | 'pending'
size?: string
language?: string
issues?: number
score?: number
details?: string
}
// Component that uses useSearchParams - needs to be wrapped in Suspense
function AIAnalysisContent() {
const router = useRouter()
const searchParams = useSearchParams()
const repoId = searchParams.get('repoId')
const repoName = searchParams.get('repoName') || 'Repository'
const [analysisProgress, setAnalysisProgress] = useState(0)
const [isAnalyzing, setIsAnalyzing] = useState(true)
const [currentFile, setCurrentFile] = useState('Initializing analysis...')
const [files, setFiles] = useState<FileAnalysis[]>([
{ id: '1', name: 'package.json', path: '/package.json', type: 'file', status: 'pending', language: 'JSON', size: '2.1 KB' },
{ id: '2', name: 'src', path: '/src', type: 'folder', status: 'pending' },
{ id: '3', name: 'App.js', path: '/src/App.js', type: 'file', status: 'pending', language: 'JavaScript', size: '5.2 KB' },
{ id: '4', name: 'components', path: '/src/components', type: 'folder', status: 'pending' },
{ id: '5', name: 'Header.jsx', path: '/src/components/Header.jsx', type: 'file', status: 'pending', language: 'JavaScript', size: '3.8 KB' },
{ id: '6', name: 'Footer.jsx', path: '/src/components/Footer.jsx', type: 'file', status: 'pending', language: 'JavaScript', size: '2.5 KB' },
{ id: '7', name: 'utils', path: '/src/utils', type: 'folder', status: 'pending' },
{ id: '8', name: 'helpers.js', path: '/src/utils/helpers.js', type: 'file', status: 'pending', language: 'JavaScript', size: '4.1 KB' },
{ id: '9', name: 'README.md', path: '/README.md', type: 'file', status: 'pending', language: 'Markdown', size: '1.8 KB' },
{ id: '10', name: 'styles', path: '/styles', type: 'folder', status: 'pending' },
{ id: '11', name: 'main.css', path: '/styles/main.css', type: 'file', status: 'pending', language: 'CSS', size: '6.3 KB' },
{ id: '12', name: 'tests', path: '/tests', type: 'folder', status: 'pending' },
{ id: '13', name: 'App.test.js', path: '/tests/App.test.js', type: 'file', status: 'pending', language: 'JavaScript', size: '2.9 KB' }
])
useEffect(() => {
if (!repoId) {
router.push('/github/repos')
return
}
let fileIndex = 0
let progress = 0
const interval = setInterval(() => {
if (fileIndex < files.length) {
const currentFileData = files[fileIndex]
// Update current file being analyzed
setCurrentFile(`Analyzing ${currentFileData.name}...`)
// Update file status to scanning
setFiles(prev => prev.map((file, index) =>
index === fileIndex ? { ...file, status: 'scanning' as const } : file
))
// After a short delay, mark as analyzing
setTimeout(() => {
setFiles(prev => prev.map((file, index) =>
index === fileIndex ? { ...file, status: 'analyzing' as const } : file
))
}, 500)
// After another delay, mark as completed with mock data
setTimeout(() => {
setFiles(prev => prev.map((file, index) =>
index === fileIndex ? {
...file,
status: 'completed' as const,
score: Math.floor(Math.random() * 30) + 70, // 70-100
issues: Math.floor(Math.random() * 5),
details: file.type === 'file' ? 'Analysis completed successfully' : 'Directory scanned'
} : file
))
}, 1000)
progress = Math.min(100, ((fileIndex + 1) / files.length) * 100)
setAnalysisProgress(progress)
fileIndex++
} else {
// Complete analysis
setIsAnalyzing(false)
setCurrentFile('Analysis completed!')
setAnalysisProgress(100)
clearInterval(interval)
}
}, 1500)
return () => clearInterval(interval)
}, [repoId, router, files.length])
const getStatusIcon = (status: FileAnalysis['status'], type: FileAnalysis['type']) => {
switch (status) {
case 'completed':
return <CheckCircle className="h-4 w-4 text-green-400" />
case 'analyzing':
return <div className="h-4 w-4 border-2 border-blue-400 border-t-transparent rounded-full animate-spin" />
case 'scanning':
return <Search className="h-4 w-4 text-yellow-400 animate-pulse" />
case 'pending':
return <Clock className="h-4 w-4 text-white/40" />
}
}
const getFileIcon = (type: FileAnalysis['type'], language?: string) => {
if (type === 'folder') {
return <Folder className="h-4 w-4 text-blue-400" />
}
switch (language) {
case 'JavaScript':
return <Code className="h-4 w-4 text-yellow-400" />
case 'CSS':
return <FileText className="h-4 w-4 text-blue-400" />
case 'Markdown':
return <FileText className="h-4 w-4 text-gray-400" />
case 'JSON':
return <FileText className="h-4 w-4 text-orange-400" />
default:
return <File className="h-4 w-4 text-white/60" />
}
}
const getScoreColor = (score: number) => {
if (score >= 90) return 'text-green-400'
if (score >= 80) return 'text-yellow-400'
if (score >= 70) return 'text-orange-400'
return 'text-red-400'
}
return (
<div className="mx-auto max-w-7xl px-4 py-8 space-y-8">
{/* Header */}
<div className="flex items-center gap-4">
<Link href="/github/repos">
<Button variant="ghost" className="text-white/80 hover:text-white">
<ArrowLeft className="mr-2 h-4 w-4" /> Back to Repositories
</Button>
</Link>
<div>
<h1 className="text-3xl md:text-4xl font-bold text-white flex items-center gap-3">
<Brain className="h-8 w-8 text-orange-400" />
AI Code Analysis
</h1>
<p className="text-white/60 mt-1">Analyzing: <span className="text-orange-400 font-medium">{repoName}</span></p>
</div>
</div>
{/* Analysis Progress */}
{isAnalyzing && (
<Card className="bg-white/5 border-white/10">
<CardContent className="p-6">
<div className="space-y-4">
<div className="flex items-center justify-between">
<h3 className="text-lg font-semibold text-white">Analysis Progress</h3>
<span className="text-sm text-white/60">{Math.round(analysisProgress)}%</span>
</div>
<Progress value={analysisProgress} className="h-2" />
<p className="text-white/80 text-sm flex items-center gap-2">
<Eye className="h-4 w-4 text-blue-400" />
{currentFile}
</p>
</div>
</CardContent>
</Card>
)}
{/* File Analysis List */}
<Card className="bg-white/5 border-white/10">
<CardHeader>
<CardTitle className="text-white flex items-center gap-2">
<FileText className="h-6 w-6 text-orange-400" />
File Analysis Results
</CardTitle>
</CardHeader>
<CardContent className="p-0">
<div className="divide-y divide-white/10">
{files.map((file) => (
<div key={file.id} className="p-4 hover:bg-white/5 transition-colors">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3 min-w-0 flex-1">
{getFileIcon(file.type, file.language)}
<div className="min-w-0 flex-1">
<div className="flex items-center gap-2">
<span className="font-medium text-white truncate">{file.name}</span>
{file.language && (
<Badge variant="outline" className="text-xs border-white/20 text-white/60">
{file.language}
</Badge>
)}
{file.size && (
<span className="text-xs text-white/40">{file.size}</span>
)}
</div>
<p className="text-sm text-white/60 truncate">{file.path}</p>
</div>
</div>
<div className="flex items-center gap-3">
{file.status === 'completed' && file.score && (
<div className="flex items-center gap-2">
{file.issues && file.issues > 0 && (
<div className="flex items-center gap-1 text-red-400">
<AlertCircle className="h-4 w-4" />
<span className="text-sm">{file.issues}</span>
</div>
)}
<Badge className={`${getScoreColor(file.score)} bg-transparent border`}>
{file.score}/100
</Badge>
</div>
)}
{getStatusIcon(file.status, file.type)}
</div>
</div>
{file.status === 'completed' && file.details && (
<div className="mt-2 text-sm text-white/70">
{file.details}
</div>
)}
</div>
))}
</div>
</CardContent>
</Card>
{/* Summary */}
{!isAnalyzing && (
<Card className="bg-white/5 border-white/10">
<CardHeader>
<CardTitle className="text-white flex items-center gap-2">
<BarChart3 className="h-6 w-6 text-orange-400" />
Analysis Summary
</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
<div className="text-center">
<div className="text-2xl font-bold text-green-400">
{files.filter(f => f.status === 'completed').length}
</div>
<div className="text-white/60 text-sm">Files Analyzed</div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-yellow-400">
{files.filter(f => f.language).length}
</div>
<div className="text-white/60 text-sm">Languages Found</div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-red-400">
{files.reduce((sum, f) => sum + (f.issues || 0), 0)}
</div>
<div className="text-white/60 text-sm">Issues Found</div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-blue-400">
{Math.round(files.filter(f => f.score).reduce((sum, f) => sum + (f.score || 0), 0) / files.filter(f => f.score).length) || 0}
</div>
<div className="text-white/60 text-sm">Avg Score</div>
</div>
</div>
</CardContent>
</Card>
)}
{/* Action Buttons */}
{!isAnalyzing && (
<div className="flex justify-center gap-4">
<Button className="bg-orange-600 hover:bg-orange-700 text-white">
<Layers className="mr-2 h-4 w-4" />
Generate Detailed Report
</Button>
<Button variant="outline" className="border-white/20 text-white hover:bg-white/10">
<Cpu className="mr-2 h-4 w-4" />
Export Analysis
</Button>
</div>
)}
</div>
)
}
export default function AIAnalysisPage() {
return (
<Suspense fallback={<div className="mx-auto max-w-7xl px-4 py-8 flex items-center justify-center min-h-screen"><div className="text-white">Loading analysis...</div></div>}>
<AIAnalysisContent />
</Suspense>
)
}