diff --git a/src/app/diff-viewer/page.tsx b/src/app/diff-viewer/page.tsx index 693b86a..57a8f27 100644 --- a/src/app/diff-viewer/page.tsx +++ b/src/app/diff-viewer/page.tsx @@ -1,7 +1,7 @@ // app/diff-viewer/page.tsx 'use client'; -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, Suspense } from 'react'; import { useSearchParams } from 'next/navigation'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; @@ -13,9 +13,13 @@ import { FolderOpen, Search, RefreshCw, - ExternalLink + ExternalLink, + Brain, + CheckSquare, + Square } from 'lucide-react'; import DiffViewer from '@/components/diff-viewer/DiffViewer'; +import { authApiClient } from '@/components/apis/authApiClients'; interface Repository { id: string; @@ -36,7 +40,7 @@ interface Commit { total_diff_size: number; } -const DiffViewerPage: React.FC = () => { +const DiffViewerContent: React.FC = () => { const searchParams = useSearchParams(); const [repositories, setRepositories] = useState([]); const [commits, setCommits] = useState([]); @@ -45,25 +49,29 @@ const DiffViewerPage: React.FC = () => { const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const [searchQuery, setSearchQuery] = useState(''); - - // Handle URL parameters - useEffect(() => { - const repoId = searchParams.get('repo'); - if (repoId) { - setSelectedRepository(repoId); - } - }, [searchParams]); + const [selectedCommits, setSelectedCommits] = useState([]); + const [bulkAnalysisLoading, setBulkAnalysisLoading] = useState(false); + const [bulkAnalysisError, setBulkAnalysisError] = useState(null); // Load repositories useEffect(() => { const loadRepositories = async () => { try { setIsLoading(true); - const response = await fetch('/api/diffs/repositories'); - const data = await response.json(); + const response = await authApiClient.get('/api/diffs/repositories'); + const data = response.data; if (data.success) { setRepositories(data.data.repositories); + + // Handle URL parameters after repositories are loaded + const repoId = searchParams.get('repo') || searchParams.get('repository'); + console.log('URL repoId:', repoId); + console.log('Available repositories:', data.data.repositories.map((r: Repository) => ({ id: r.id, name: r.repository_name }))); + if (repoId) { + console.log('Setting selected repository to:', repoId); + setSelectedRepository(repoId); + } } else { setError(data.message || 'Failed to load repositories'); } @@ -75,7 +83,7 @@ const DiffViewerPage: React.FC = () => { }; loadRepositories(); - }, []); + }, [searchParams]); // Load commits when repository is selected useEffect(() => { @@ -83,8 +91,8 @@ const DiffViewerPage: React.FC = () => { const loadCommits = async () => { try { setIsLoading(true); - const response = await fetch(`/api/diffs/repositories/${selectedRepository}/commits`); - const data = await response.json(); + const response = await authApiClient.get(`/api/diffs/repositories/${selectedRepository}/commits`); + const data = response.data; if (data.success) { setCommits(data.data.commits); @@ -109,6 +117,7 @@ const DiffViewerPage: React.FC = () => { const handleRepositoryChange = (repositoryId: string) => { setSelectedRepository(repositoryId); setSelectedCommit(''); + setSelectedCommits([]); // Reset bulk selection }; const handleCommitChange = (commitId: string) => { @@ -120,8 +129,8 @@ const DiffViewerPage: React.FC = () => { const loadCommits = async () => { try { setIsLoading(true); - const response = await fetch(`/api/diffs/repositories/${selectedRepository}/commits`); - const data = await response.json(); + const response = await authApiClient.get(`/api/diffs/repositories/${selectedRepository}/commits`); + const data = response.data; if (data.success) { setCommits(data.data.commits); @@ -137,6 +146,60 @@ const DiffViewerPage: React.FC = () => { } }; + const handleBulkAnalysis = async () => { + if (!selectedRepository || selectedCommits.length === 0) { + setBulkAnalysisError('Please select a repository and at least one commit for bulk analysis.'); + return; + } + + setBulkAnalysisLoading(true); + setBulkAnalysisError(null); + + try { + const response = await authApiClient.post(`/api/ai/repository/${selectedRepository}/bulk-analysis`, { + commit_ids: selectedCommits, + analysis_type: "bulk", + include_content: "true", + stream: "false" + }, { + timeout: 120000 // 2 minutes timeout for bulk analysis + }); + + const data = response.data; + console.log('Bulk Analysis Result:', data); + + // Log user ID from response + if (data.user_id) { + console.log('🔍 [USER-ID] Received user ID in bulk analysis response:', data.user_id); + } + + alert(`Bulk AI Analysis completed successfully for ${selectedCommits.length} commits!`); + + } catch (err) { + const errorMessage = err instanceof Error ? err.message : 'Bulk AI Analysis failed'; + setBulkAnalysisError(errorMessage); + console.error('Bulk Analysis Error:', err); + } finally { + setBulkAnalysisLoading(false); + } + }; + + const toggleCommitSelection = (commitId: string) => { + setSelectedCommits(prev => + prev.includes(commitId) + ? prev.filter(id => id !== commitId) + : [...prev, commitId] + ); + }; + + const selectAllCommits = () => { + setSelectedCommits(commits.map(commit => commit.id)); + }; + + const clearSelection = () => { + setSelectedCommits([]); + }; + const filteredCommits = commits.filter(commit => commit.message.toLowerCase().includes(searchQuery.toLowerCase()) || commit.author_name.toLowerCase().includes(searchQuery.toLowerCase()) || @@ -279,6 +342,122 @@ const DiffViewerPage: React.FC = () => { )} + {/* Bulk Analysis Error Display */} + {bulkAnalysisError && ( + + +
+

Bulk Analysis Error

+

{bulkAnalysisError}

+ +
+
+
+ )} + + {/* Bulk Analysis Section */} + {selectedRepository && commits.length > 0 && ( + + + + + Bulk AI Analysis + + + +
+

+ Select multiple commits for bulk AI analysis +

+
+ + +
+
+ + {/* Commit Selection List */} +
+ {filteredCommits.map((commit) => ( +
toggleCommitSelection(commit.id)} + > +
+ {selectedCommits.includes(commit.id) ? ( + + ) : ( + + )} +
+
+
+ + {commit.commit_sha.substring(0, 8)} + + + {commit.message} + +
+
+ by {commit.author_name} • {new Date(commit.committed_at).toLocaleDateString()} +
+
+
+ ))} +
+ + {/* Bulk Analysis Button */} +
+
+ {selectedCommits.length} commit{selectedCommits.length !== 1 ? 's' : ''} selected +
+ +
+
+
+ )} + {/* Diff Viewer */} {selectedRepository && selectedCommit && ( { ); }; +const DiffViewerPage: React.FC = () => { + return ( + +
+
+

Loading diff viewer...

+
+ + }> + +
+ ); +}; + export default DiffViewerPage; diff --git a/src/app/github/repo/repo-client.tsx b/src/app/github/repo/repo-client.tsx index 3e8032f..25a3a4e 100644 --- a/src/app/github/repo/repo-client.tsx +++ b/src/app/github/repo/repo-client.tsx @@ -166,7 +166,32 @@ export default function RepoByIdClient({ repositoryId, initialPath = "" }: { rep Loading file content... ) : fileContent ? ( -
{fileContent}
+
+
+ {/* Line Numbers Column */} +
+ {fileContent.split('\n').map((line, index) => ( +
+ {index + 1} +
+ ))} +
+ {/* File Content Column */} +
+ {fileContent.split('\n').map((line, index) => ( +
+ {line || '\u00A0'} +
+ ))} +
+
+
) : (
diff --git a/src/app/github/repos/page.tsx b/src/app/github/repos/page.tsx index eb0acd0..48c1457 100644 --- a/src/app/github/repos/page.tsx +++ b/src/app/github/repos/page.tsx @@ -16,9 +16,11 @@ import { Eye, Code, Calendar, - GitCompare + GitCompare, + Brain } from 'lucide-react'; import { getUserRepositories, type GitHubRepoSummary } from '@/lib/api/github'; +import { authApiClient } from '@/components/apis/authApiClients'; import Link from 'next/link'; const GitHubReposPage: React.FC = () => { @@ -27,6 +29,8 @@ const GitHubReposPage: React.FC = () => { const [error, setError] = useState(null); const [searchQuery, setSearchQuery] = useState(''); const [filter, setFilter] = useState<'all' | 'public' | 'private'>('all'); + const [aiAnalysisLoading, setAiAnalysisLoading] = useState(null); + const [aiAnalysisError, setAiAnalysisError] = useState(null); // Load repositories useEffect(() => { @@ -59,6 +63,33 @@ const GitHubReposPage: React.FC = () => { } }; + const handleAiAnalysis = async (repositoryId: string) => { + try { + setAiAnalysisLoading(repositoryId); + setAiAnalysisError(null); + + const response = await authApiClient.get(`/api/ai/repository/${repositoryId}/ai-stream`); + const data = response.data; + + console.log('AI Analysis Result:', data); + + // Log user ID from response + if (data.user_id) { + console.log('🔍 [USER-ID] Received user ID in response:', data.user_id); + } + + // You can add a success notification or modal here + alert('AI Analysis completed successfully!'); + + } catch (err) { + const errorMessage = err instanceof Error ? err.message : 'AI Analysis failed'; + setAiAnalysisError(errorMessage); + console.error('AI Analysis Error:', err); + } finally { + setAiAnalysisLoading(null); + } + }; + const filteredRepositories = repositories.filter(repo => { const matchesSearch = repo.full_name?.toLowerCase().includes(searchQuery.toLowerCase()) || repo.description?.toLowerCase().includes(searchQuery.toLowerCase()) || @@ -167,6 +198,25 @@ const GitHubReposPage: React.FC = () => { )} + {/* AI Analysis Error Display */} + {aiAnalysisError && ( + + +
+

AI Analysis Error

+

{aiAnalysisError}

+ +
+
+
+ )} + {/* Loading State */} {isLoading && ( @@ -242,6 +292,39 @@ const GitHubReposPage: React.FC = () => { View + +
+ + {/* AI Analysis Button */} +
+
diff --git a/src/app/globals.css b/src/app/globals.css index 22477ea..e32f3fe 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,6 +1,25 @@ @import "tailwindcss"; @import "tw-animate-css"; +/* Thin scrollbar styles */ +.thin-scrollbar::-webkit-scrollbar { + width: 6px; +} + +.thin-scrollbar::-webkit-scrollbar-track { + background: #1F2937; + border-radius: 3px; +} + +.thin-scrollbar::-webkit-scrollbar-thumb { + background: #4B5563; + border-radius: 3px; +} + +.thin-scrollbar::-webkit-scrollbar-thumb:hover { + background: #6B7280; +} + @custom-variant dark (&:is(.dark *)); @theme inline { diff --git a/src/components/apis/authApiClients.tsx b/src/components/apis/authApiClients.tsx index 3d12a7b..7fba47f 100644 --- a/src/components/apis/authApiClients.tsx +++ b/src/components/apis/authApiClients.tsx @@ -106,6 +106,15 @@ const addAuthTokenInterceptor = (client: typeof authApiClient) => { config.headers = config.headers || {}; // Header preferred by backend (config.headers as any)['x-user-id'] = userId; + + // Debug: Log user ID being sent (only for AI endpoints) + if (config.url?.includes('/api/ai/')) { + console.log('🔍 [USER-ID] Sending user ID:', userId, 'for request:', config.url); + console.log('🔍 [USER-ID] Request headers:', { + 'x-user-id': userId, + 'Authorization': config.headers.Authorization ? 'Bearer [token]' : 'None' + }); + } } } } catch (_) {} diff --git a/src/components/diff-viewer/DiffViewerContext.tsx b/src/components/diff-viewer/DiffViewerContext.tsx index 8c7dc16..3b39208 100644 --- a/src/components/diff-viewer/DiffViewerContext.tsx +++ b/src/components/diff-viewer/DiffViewerContext.tsx @@ -1,5 +1,6 @@ // components/diff-viewer/DiffViewerContext.tsx import React, { createContext, useContext, useReducer, useCallback } from 'react'; +import { authApiClient } from '@/components/apis/authApiClients'; // Types export interface DiffFile { @@ -147,10 +148,10 @@ export const DiffViewerProvider: React.FC<{ children: React.ReactNode }> = ({ ch dispatch({ type: 'SET_ERROR', payload: null }); try { - const response = await fetch(`/api/diffs/commits/${commitId}/diffs`); - const data = await response.json(); + const response = await authApiClient.get(`/api/diffs/commits/${commitId}/diffs`); + const data = response.data; - if (!response.ok) { + if (!data.success) { throw new Error(data.message || 'Failed to load commit diffs'); } @@ -172,10 +173,10 @@ export const DiffViewerProvider: React.FC<{ children: React.ReactNode }> = ({ ch dispatch({ type: 'SET_ERROR', payload: null }); try { - const response = await fetch(`/api/diffs/repositories/${repositoryId}/commits`); - const data = await response.json(); + const response = await authApiClient.get(`/api/diffs/repositories/${repositoryId}/commits`); + const data = response.data; - if (!response.ok) { + if (!data.success) { throw new Error(data.message || 'Failed to load repository commits'); }