diff --git a/src/app/github/analyze/page.tsx b/src/app/github/analyze/page.tsx new file mode 100644 index 0000000..cee33db --- /dev/null +++ b/src/app/github/analyze/page.tsx @@ -0,0 +1,320 @@ +"use client" + +import { useEffect, useState } 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 +} + +export default function AIAnalysisPage() { + 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([ + { 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 + case 'analyzing': + return
+ case 'scanning': + return + case 'pending': + return + } + } + + const getFileIcon = (type: FileAnalysis['type'], language?: string) => { + if (type === 'folder') { + return + } + + switch (language) { + case 'JavaScript': + return + case 'CSS': + return + case 'Markdown': + return + case 'JSON': + return + default: + return + } + } + + 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 ( +
+ {/* Header */} +
+ + + +
+

+ + AI Code Analysis +

+

Analyzing: {repoName}

+
+
+ + {/* Analysis Progress */} + {isAnalyzing && ( + + +
+
+

Analysis Progress

+ {Math.round(analysisProgress)}% +
+ +

+ + {currentFile} +

+
+
+
+ )} + + {/* File Analysis List */} + + + + + File Analysis Results + + + +
+ {files.map((file) => ( +
+
+
+ {getFileIcon(file.type, file.language)} +
+
+ {file.name} + {file.language && ( + + {file.language} + + )} + {file.size && ( + {file.size} + )} +
+

{file.path}

+
+
+ +
+ {file.status === 'completed' && file.score && ( +
+ {file.issues && file.issues > 0 && ( +
+ + {file.issues} +
+ )} + + {file.score}/100 + +
+ )} + {getStatusIcon(file.status, file.type)} +
+
+ + {file.status === 'completed' && file.details && ( +
+ {file.details} +
+ )} +
+ ))} +
+
+
+ + {/* Summary */} + {!isAnalyzing && ( + + + + + Analysis Summary + + + +
+
+
+ {files.filter(f => f.status === 'completed').length} +
+
Files Analyzed
+
+
+
+ {files.filter(f => f.language).length} +
+
Languages Found
+
+
+
+ {files.reduce((sum, f) => sum + (f.issues || 0), 0)} +
+
Issues Found
+
+
+
+ {Math.round(files.filter(f => f.score).reduce((sum, f) => sum + (f.score || 0), 0) / files.filter(f => f.score).length) || 0} +
+
Avg Score
+
+
+
+
+ )} + + {/* Action Buttons */} + {!isAnalyzing && ( +
+ + +
+ )} +
+ ) +} diff --git a/src/app/github/repos/page.tsx b/src/app/github/repos/page.tsx index 834b48c..b838930 100644 --- a/src/app/github/repos/page.tsx +++ b/src/app/github/repos/page.tsx @@ -6,7 +6,7 @@ import { Button } from "@/components/ui/button" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Badge } from "@/components/ui/badge" import { Input } from "@/components/ui/input" -import { ArrowLeft, ExternalLink, FolderGit2, GitFork, Star, Shield, Search } from "lucide-react" +import { ArrowLeft, ExternalLink, FolderGit2, GitFork, Star, Shield, Search, Brain } from "lucide-react" import { getGitHubAuthStatus, getUserRepositories, type GitHubRepoSummary } from "@/lib/api/github" export default function GitHubUserReposPage() { @@ -16,6 +16,7 @@ export default function GitHubUserReposPage() { const [authUrl, setAuthUrl] = useState(null) const [repos, setRepos] = useState([]) const [query, setQuery] = useState("") + const [analyzingRepo, setAnalyzingRepo] = useState(null) useEffect(() => { let mounted = true @@ -63,6 +64,25 @@ export default function GitHubUserReposPage() { window.location.href = url } + const handleAnalyzeWithAI = async (repo: GitHubRepoSummary) => { + try { + setAnalyzingRepo(repo.full_name || repo.name || '') + + // Navigate to AI analysis page with repository details + const repoId = (repo as any).id || repo.full_name || repo.name + const repoName = repo.full_name || repo.name + + const analysisUrl = `/github/analyze?repoId=${encodeURIComponent(repoId)}&repoName=${encodeURIComponent(repoName)}` + window.location.href = analysisUrl + + } catch (error) { + console.error('Error analyzing repository:', error) + alert('Failed to analyze repository. Please try again.') + } finally { + setAnalyzingRepo(null) + } + } + return (
@@ -151,14 +171,24 @@ export default function GitHubUserReposPage() { )}
- - - + + + +
))} diff --git a/src/app/project-builder/page.tsx b/src/app/project-builder/page.tsx index 99393ab..fe2d33b 100644 --- a/src/app/project-builder/page.tsx +++ b/src/app/project-builder/page.tsx @@ -1,12 +1,13 @@ "use client" import { Suspense, useEffect } from "react" -import { useRouter } from "next/navigation" +import { useRouter, useSearchParams } from "next/navigation" import { MainDashboard } from "@/components/main-dashboard" import { useAuth } from "@/contexts/auth-context" export default function ProjectBuilderPage() { const router = useRouter() + const searchParams = useSearchParams() const { user, isLoading } = useAuth() useEffect(() => { @@ -17,6 +18,36 @@ export default function ProjectBuilderPage() { } }, [user, isLoading, router]) + // Handle GitHub OAuth callback parameters + useEffect(() => { + if (isLoading || !user) return + + const githubConnected = searchParams.get('github_connected') + const githubUser = searchParams.get('user') + const repoAttached = searchParams.get('repo_attached') + const repositoryId = searchParams.get('repository_id') + const syncStatus = searchParams.get('sync_status') + + if (githubConnected === '1') { + console.log('🎉 GitHub OAuth callback successful!', { + githubUser, + repoAttached, + repositoryId, + syncStatus + }) + + // Show success message + if (repoAttached === '1') { + alert(`🎉 Repository attached successfully!\n\nGitHub User: ${githubUser}\nRepository ID: ${repositoryId}\nSync Status: ${syncStatus}`) + } else { + alert(`🎉 GitHub account connected successfully!\n\nGitHub User: ${githubUser}`) + } + + // Clean up URL parameters + router.replace('/project-builder') + } + }, [isLoading, user, searchParams, router]) + if (isLoading || !user) { return
Loading...
} diff --git a/src/components/main-dashboard.tsx b/src/components/main-dashboard.tsx index 502791c..65e9821 100644 --- a/src/components/main-dashboard.tsx +++ b/src/components/main-dashboard.tsx @@ -96,21 +96,50 @@ function TemplateSelectionStep({ onNext }: { onNext: (template: Template) => voi })) } catch {} - await attachRepository({ + const result = await attachRepository({ repository_url: gitUrl.trim(), branch_name: (gitBranch?.trim() || undefined), }) - // If we reach here without 401, repo is public and attached. Nothing to auth. + + // If we reach here without 401, repo is public and attached successfully + console.log('✅ Repository attached successfully:', result) + alert('Repository attached successfully! You can now proceed with your project.') + setShowCreateOptionDialog(false) + setShowGitForm(false) + setGitProvider('') + setGitUrl('') + setGitBranch('main') + } catch (err: any) { const status = err?.response?.status - const data = err?.response?.data - if (status === 401 && (data?.requires_auth || data?.auth_url)) { - const url: string = data?.auth_url + let data = err?.response?.data + + // Some proxies or middlewares may stringify JSON error bodies; handle that here + if (typeof data === 'string') { + try { data = JSON.parse(data) } catch {} + } + + console.log('🔍 Attach repository response:', { status, data }) + + if (status === 401 && (data?.requires_auth || data?.auth_url || data?.service_auth_url)) { + const url: string = data?.service_auth_url || data?.auth_url if (url) { + console.log('🔐 Redirecting to GitHub OAuth:', url) window.location.replace(url) return } } + + if (status === 403) { + alert('Repository not accessible - you may not have permission to access this repository') + return + } + + if (status === 404) { + alert('Repository not found - please check the URL and try again') + return + } + console.error('Error generating auth URL via attach:', err) alert(data?.message || 'Failed to generate authentication URL. Please try again.') } finally { @@ -493,28 +522,46 @@ function TemplateSelectionStep({ onNext }: { onNext: (template: Template) => voi } } catch (attachErr) { console.error('[TemplateSelectionStep] attachRepository failed:', attachErr) + + const err: any = attachErr + const status = err?.response?.status + const data = err?.response?.data + + console.log('🔍 HandleCreateFromGit error response:', { status, data }) + // If backend signals GitHub auth required, open the OAuth URL for this user - try { - const err: any = attachErr - const status = err?.response?.status - const data = err?.response?.data - if (status === 401 && (data?.requires_auth || data?.message?.includes('authentication'))) { - // Use the exact URL provided by backend (already includes redirect=1 and user_id when needed) - const url: string = data?.auth_url - if (!url) { alert('Authentication URL is missing.'); return } - // Persist pending repo so we resume after OAuth callback - try { - sessionStorage.setItem('pending_git_attach', JSON.stringify({ - repository_url: gitUrl.trim(), - branch_name: (gitBranch?.trim() || undefined) - })) - } catch {} - // Force same-tab redirect directly to GitHub consent screen - window.location.replace(url) - return + if (status === 401 && (data?.requires_auth || data?.message?.includes('authentication'))) { + const url: string = data?.auth_url + if (!url) { + alert('Authentication URL is missing.'); + return } - } catch {} - alert('Failed to attach repository. Please verify the URL/branch and your auth.'); + + // Persist pending repo so we resume after OAuth callback + try { + sessionStorage.setItem('pending_git_attach', JSON.stringify({ + repository_url: gitUrl.trim(), + branch_name: (gitBranch?.trim() || undefined) + })) + } catch {} + + console.log('🔐 Redirecting to GitHub OAuth for repository attachment:', url) + // Force same-tab redirect directly to GitHub consent screen + window.location.replace(url) + return + } + + if (status === 403) { + alert('Repository not accessible - you may not have permission to access this repository') + return + } + + if (status === 404) { + alert('Repository not found - please check the URL and try again') + return + } + + alert(data?.message || 'Failed to attach repository. Please verify the URL/branch and your auth.'); return; } } catch (error) {