git completed flow with streaming api-integrate with front end
This commit is contained in:
parent
84a25bf091
commit
0d3f89da0f
@ -1,7 +1,7 @@
|
|||||||
// app/diff-viewer/page.tsx
|
// app/diff-viewer/page.tsx
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect, Suspense } from 'react';
|
||||||
import { useSearchParams } from 'next/navigation';
|
import { useSearchParams } from 'next/navigation';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
@ -13,9 +13,13 @@ import {
|
|||||||
FolderOpen,
|
FolderOpen,
|
||||||
Search,
|
Search,
|
||||||
RefreshCw,
|
RefreshCw,
|
||||||
ExternalLink
|
ExternalLink,
|
||||||
|
Brain,
|
||||||
|
CheckSquare,
|
||||||
|
Square
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import DiffViewer from '@/components/diff-viewer/DiffViewer';
|
import DiffViewer from '@/components/diff-viewer/DiffViewer';
|
||||||
|
import { authApiClient } from '@/components/apis/authApiClients';
|
||||||
|
|
||||||
interface Repository {
|
interface Repository {
|
||||||
id: string;
|
id: string;
|
||||||
@ -36,7 +40,7 @@ interface Commit {
|
|||||||
total_diff_size: number;
|
total_diff_size: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DiffViewerPage: React.FC = () => {
|
const DiffViewerContent: React.FC = () => {
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const [repositories, setRepositories] = useState<Repository[]>([]);
|
const [repositories, setRepositories] = useState<Repository[]>([]);
|
||||||
const [commits, setCommits] = useState<Commit[]>([]);
|
const [commits, setCommits] = useState<Commit[]>([]);
|
||||||
@ -45,25 +49,29 @@ const DiffViewerPage: React.FC = () => {
|
|||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
|
const [selectedCommits, setSelectedCommits] = useState<string[]>([]);
|
||||||
// Handle URL parameters
|
const [bulkAnalysisLoading, setBulkAnalysisLoading] = useState(false);
|
||||||
useEffect(() => {
|
const [bulkAnalysisError, setBulkAnalysisError] = useState<string | null>(null);
|
||||||
const repoId = searchParams.get('repo');
|
|
||||||
if (repoId) {
|
|
||||||
setSelectedRepository(repoId);
|
|
||||||
}
|
|
||||||
}, [searchParams]);
|
|
||||||
|
|
||||||
// Load repositories
|
// Load repositories
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadRepositories = async () => {
|
const loadRepositories = async () => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const response = await fetch('/api/diffs/repositories');
|
const response = await authApiClient.get('/api/diffs/repositories');
|
||||||
const data = await response.json();
|
const data = response.data;
|
||||||
|
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
setRepositories(data.data.repositories);
|
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 {
|
} else {
|
||||||
setError(data.message || 'Failed to load repositories');
|
setError(data.message || 'Failed to load repositories');
|
||||||
}
|
}
|
||||||
@ -75,7 +83,7 @@ const DiffViewerPage: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
loadRepositories();
|
loadRepositories();
|
||||||
}, []);
|
}, [searchParams]);
|
||||||
|
|
||||||
// Load commits when repository is selected
|
// Load commits when repository is selected
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -83,8 +91,8 @@ const DiffViewerPage: React.FC = () => {
|
|||||||
const loadCommits = async () => {
|
const loadCommits = async () => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const response = await fetch(`/api/diffs/repositories/${selectedRepository}/commits`);
|
const response = await authApiClient.get(`/api/diffs/repositories/${selectedRepository}/commits`);
|
||||||
const data = await response.json();
|
const data = response.data;
|
||||||
|
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
setCommits(data.data.commits);
|
setCommits(data.data.commits);
|
||||||
@ -109,6 +117,7 @@ const DiffViewerPage: React.FC = () => {
|
|||||||
const handleRepositoryChange = (repositoryId: string) => {
|
const handleRepositoryChange = (repositoryId: string) => {
|
||||||
setSelectedRepository(repositoryId);
|
setSelectedRepository(repositoryId);
|
||||||
setSelectedCommit('');
|
setSelectedCommit('');
|
||||||
|
setSelectedCommits([]); // Reset bulk selection
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCommitChange = (commitId: string) => {
|
const handleCommitChange = (commitId: string) => {
|
||||||
@ -120,8 +129,8 @@ const DiffViewerPage: React.FC = () => {
|
|||||||
const loadCommits = async () => {
|
const loadCommits = async () => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const response = await fetch(`/api/diffs/repositories/${selectedRepository}/commits`);
|
const response = await authApiClient.get(`/api/diffs/repositories/${selectedRepository}/commits`);
|
||||||
const data = await response.json();
|
const data = response.data;
|
||||||
|
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
setCommits(data.data.commits);
|
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 =>
|
const filteredCommits = commits.filter(commit =>
|
||||||
commit.message.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
commit.message.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
commit.author_name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
commit.author_name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
@ -279,6 +342,122 @@ const DiffViewerPage: React.FC = () => {
|
|||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Bulk Analysis Error Display */}
|
||||||
|
{bulkAnalysisError && (
|
||||||
|
<Card className="border-destructive">
|
||||||
|
<CardContent className="pt-6">
|
||||||
|
<div className="text-center text-destructive">
|
||||||
|
<p className="font-medium">Bulk Analysis Error</p>
|
||||||
|
<p className="text-sm mt-2">{bulkAnalysisError}</p>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
className="mt-4"
|
||||||
|
onClick={() => setBulkAnalysisError(null)}
|
||||||
|
>
|
||||||
|
Dismiss
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Bulk Analysis Section */}
|
||||||
|
{selectedRepository && commits.length > 0 && (
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="flex items-center space-x-2">
|
||||||
|
<Brain className="h-5 w-5" />
|
||||||
|
<span>Bulk AI Analysis</span>
|
||||||
|
</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-4">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Select multiple commits for bulk AI analysis
|
||||||
|
</p>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={selectAllCommits}
|
||||||
|
disabled={commits.length === 0}
|
||||||
|
>
|
||||||
|
Select All
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={clearSelection}
|
||||||
|
disabled={selectedCommits.length === 0}
|
||||||
|
>
|
||||||
|
Clear
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Commit Selection List */}
|
||||||
|
<div className="max-h-60 overflow-y-auto border rounded-lg p-4 space-y-2">
|
||||||
|
{filteredCommits.map((commit) => (
|
||||||
|
<div
|
||||||
|
key={commit.id}
|
||||||
|
className={`flex items-center space-x-3 p-2 rounded-lg cursor-pointer transition-colors ${
|
||||||
|
selectedCommits.includes(commit.id)
|
||||||
|
? 'bg-primary/10 border border-primary/20'
|
||||||
|
: 'hover:bg-muted/50'
|
||||||
|
}`}
|
||||||
|
onClick={() => toggleCommitSelection(commit.id)}
|
||||||
|
>
|
||||||
|
<div className="flex-shrink-0">
|
||||||
|
{selectedCommits.includes(commit.id) ? (
|
||||||
|
<CheckSquare className="h-4 w-4 text-primary" />
|
||||||
|
) : (
|
||||||
|
<Square className="h-4 w-4 text-muted-foreground" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<code className="text-xs bg-muted px-2 py-1 rounded">
|
||||||
|
{commit.commit_sha.substring(0, 8)}
|
||||||
|
</code>
|
||||||
|
<span className="text-sm font-medium truncate">
|
||||||
|
{commit.message}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-muted-foreground mt-1">
|
||||||
|
by {commit.author_name} • {new Date(commit.committed_at).toLocaleDateString()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Bulk Analysis Button */}
|
||||||
|
<div className="flex items-center justify-between pt-4 border-t">
|
||||||
|
<div className="text-sm text-muted-foreground">
|
||||||
|
{selectedCommits.length} commit{selectedCommits.length !== 1 ? 's' : ''} selected
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
onClick={handleBulkAnalysis}
|
||||||
|
disabled={selectedCommits.length === 0 || bulkAnalysisLoading}
|
||||||
|
className="min-w-[140px]"
|
||||||
|
>
|
||||||
|
{bulkAnalysisLoading ? (
|
||||||
|
<>
|
||||||
|
<RefreshCw className="h-4 w-4 mr-2 animate-spin" />
|
||||||
|
Analyzing...
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Brain className="h-4 w-4 mr-2" />
|
||||||
|
Analyze {selectedCommits.length} Commits
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Diff Viewer */}
|
{/* Diff Viewer */}
|
||||||
{selectedRepository && selectedCommit && (
|
{selectedRepository && selectedCommit && (
|
||||||
<DiffViewer
|
<DiffViewer
|
||||||
@ -321,4 +500,19 @@ const DiffViewerPage: React.FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const DiffViewerPage: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<Suspense fallback={
|
||||||
|
<div className="max-w-7xl mx-auto p-6">
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto mb-4"></div>
|
||||||
|
<p>Loading diff viewer...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}>
|
||||||
|
<DiffViewerContent />
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default DiffViewerPage;
|
export default DiffViewerPage;
|
||||||
|
|||||||
@ -166,7 +166,32 @@ export default function RepoByIdClient({ repositoryId, initialPath = "" }: { rep
|
|||||||
<span className="ml-2 text-white/60">Loading file content...</span>
|
<span className="ml-2 text-white/60">Loading file content...</span>
|
||||||
</div>
|
</div>
|
||||||
) : fileContent ? (
|
) : fileContent ? (
|
||||||
<pre className="whitespace-pre-wrap text-sm text-white/90 bg-black/20 p-4 rounded overflow-auto h-full">{fileContent}</pre>
|
<div
|
||||||
|
className="bg-black/20 rounded overflow-auto h-full thin-scrollbar"
|
||||||
|
style={{
|
||||||
|
scrollbarWidth: 'thin',
|
||||||
|
scrollbarColor: '#4B5563 #1F2937'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex">
|
||||||
|
{/* Line Numbers Column */}
|
||||||
|
<div className="bg-gray-800/50 text-gray-400 text-xs font-mono px-3 py-4 select-none border-r border-gray-700/50 min-w-[3rem] flex-shrink-0">
|
||||||
|
{fileContent.split('\n').map((line, index) => (
|
||||||
|
<div key={index} className="leading-5 h-5 flex items-center justify-end">
|
||||||
|
{index + 1}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
{/* File Content Column */}
|
||||||
|
<div className="flex-1 p-4">
|
||||||
|
{fileContent.split('\n').map((line, index) => (
|
||||||
|
<div key={index} className="leading-5 h-5 text-sm text-white/90 font-mono">
|
||||||
|
{line || '\u00A0'}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center justify-center h-full">
|
<div className="flex items-center justify-center h-full">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
|
|||||||
@ -16,9 +16,11 @@ import {
|
|||||||
Eye,
|
Eye,
|
||||||
Code,
|
Code,
|
||||||
Calendar,
|
Calendar,
|
||||||
GitCompare
|
GitCompare,
|
||||||
|
Brain
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { getUserRepositories, type GitHubRepoSummary } from '@/lib/api/github';
|
import { getUserRepositories, type GitHubRepoSummary } from '@/lib/api/github';
|
||||||
|
import { authApiClient } from '@/components/apis/authApiClients';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
const GitHubReposPage: React.FC = () => {
|
const GitHubReposPage: React.FC = () => {
|
||||||
@ -27,6 +29,8 @@ const GitHubReposPage: React.FC = () => {
|
|||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
const [filter, setFilter] = useState<'all' | 'public' | 'private'>('all');
|
const [filter, setFilter] = useState<'all' | 'public' | 'private'>('all');
|
||||||
|
const [aiAnalysisLoading, setAiAnalysisLoading] = useState<string | null>(null);
|
||||||
|
const [aiAnalysisError, setAiAnalysisError] = useState<string | null>(null);
|
||||||
|
|
||||||
// Load repositories
|
// Load repositories
|
||||||
useEffect(() => {
|
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 filteredRepositories = repositories.filter(repo => {
|
||||||
const matchesSearch = repo.full_name?.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
const matchesSearch = repo.full_name?.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
repo.description?.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
repo.description?.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
@ -167,6 +198,25 @@ const GitHubReposPage: React.FC = () => {
|
|||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* AI Analysis Error Display */}
|
||||||
|
{aiAnalysisError && (
|
||||||
|
<Card className="border-destructive">
|
||||||
|
<CardContent className="pt-6">
|
||||||
|
<div className="text-center text-destructive">
|
||||||
|
<p className="font-medium">AI Analysis Error</p>
|
||||||
|
<p className="text-sm mt-2">{aiAnalysisError}</p>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
className="mt-4"
|
||||||
|
onClick={() => setAiAnalysisError(null)}
|
||||||
|
>
|
||||||
|
Dismiss
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Loading State */}
|
{/* Loading State */}
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
<Card>
|
<Card>
|
||||||
@ -242,6 +292,39 @@ const GitHubReposPage: React.FC = () => {
|
|||||||
View
|
View
|
||||||
</Link>
|
</Link>
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button asChild size="sm" variant="outline" className="flex-1">
|
||||||
|
<Link href={`/diff-viewer?repository=${repo.id}`}>
|
||||||
|
<GitCompare className="h-4 w-4 mr-2" />
|
||||||
|
Git Diff
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* AI Analysis Button */}
|
||||||
|
<div className="pt-2">
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="secondary"
|
||||||
|
className="w-full"
|
||||||
|
onClick={() => {
|
||||||
|
if (repo.id) {
|
||||||
|
handleAiAnalysis(String(repo.id));
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
disabled={aiAnalysisLoading === repo.id}
|
||||||
|
>
|
||||||
|
{aiAnalysisLoading === repo.id ? (
|
||||||
|
<>
|
||||||
|
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-current mr-2"></div>
|
||||||
|
Analyzing...
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Brain className="h-4 w-4 mr-2" />
|
||||||
|
AI Analysis
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@ -1,6 +1,25 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
@import "tw-animate-css";
|
@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 *));
|
@custom-variant dark (&:is(.dark *));
|
||||||
|
|
||||||
@theme inline {
|
@theme inline {
|
||||||
|
|||||||
@ -106,6 +106,15 @@ const addAuthTokenInterceptor = (client: typeof authApiClient) => {
|
|||||||
config.headers = config.headers || {};
|
config.headers = config.headers || {};
|
||||||
// Header preferred by backend
|
// Header preferred by backend
|
||||||
(config.headers as any)['x-user-id'] = userId;
|
(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 (_) {}
|
} catch (_) {}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
// components/diff-viewer/DiffViewerContext.tsx
|
// components/diff-viewer/DiffViewerContext.tsx
|
||||||
import React, { createContext, useContext, useReducer, useCallback } from 'react';
|
import React, { createContext, useContext, useReducer, useCallback } from 'react';
|
||||||
|
import { authApiClient } from '@/components/apis/authApiClients';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
export interface DiffFile {
|
export interface DiffFile {
|
||||||
@ -147,10 +148,10 @@ export const DiffViewerProvider: React.FC<{ children: React.ReactNode }> = ({ ch
|
|||||||
dispatch({ type: 'SET_ERROR', payload: null });
|
dispatch({ type: 'SET_ERROR', payload: null });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/diffs/commits/${commitId}/diffs`);
|
const response = await authApiClient.get(`/api/diffs/commits/${commitId}/diffs`);
|
||||||
const data = await response.json();
|
const data = response.data;
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!data.success) {
|
||||||
throw new Error(data.message || 'Failed to load commit diffs');
|
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 });
|
dispatch({ type: 'SET_ERROR', payload: null });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/diffs/repositories/${repositoryId}/commits`);
|
const response = await authApiClient.get(`/api/diffs/repositories/${repositoryId}/commits`);
|
||||||
const data = await response.json();
|
const data = response.data;
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!data.success) {
|
||||||
throw new Error(data.message || 'Failed to load repository commits');
|
throw new Error(data.message || 'Failed to load repository commits');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user