feat: Add review events persistence, version display, and auto-versioning system
This commit is contained in:
@@ -7,6 +7,7 @@ import ReviewDetail from './pages/ReviewDetail';
|
||||
import Organizations from './pages/Organizations';
|
||||
import Tasks from './pages/Tasks';
|
||||
import WebSocketStatus from './components/WebSocketStatus';
|
||||
import Footer from './components/Footer';
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
@@ -65,7 +66,7 @@ function Navigation() {
|
||||
|
||||
function AppContent() {
|
||||
return (
|
||||
<div className="min-h-screen bg-dark-bg">
|
||||
<div className="min-h-screen bg-dark-bg pb-12">
|
||||
<Navigation />
|
||||
|
||||
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
@@ -78,6 +79,8 @@ function AppContent() {
|
||||
<Route path="/reviews/:id" element={<ReviewDetail />} />
|
||||
</Routes>
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -68,5 +68,25 @@ export const getReviewStats = async () => {
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export interface ReviewEvent {
|
||||
id: number;
|
||||
review_id: number;
|
||||
event_type: string;
|
||||
step?: string;
|
||||
message?: string;
|
||||
data?: any;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export const getReviewEvents = async (reviewId: number) => {
|
||||
const response = await api.get<ReviewEvent[]>(`/reviews/${reviewId}/events`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const getBackendVersion = async () => {
|
||||
const response = await api.get<{ version: string }>('/version');
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export default api;
|
||||
|
||||
|
||||
39
frontend/src/components/Footer.tsx
Normal file
39
frontend/src/components/Footer.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Footer component with version info
|
||||
*/
|
||||
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { getBackendVersion } from '../api/client';
|
||||
|
||||
export default function Footer() {
|
||||
const { data: versionData } = useQuery({
|
||||
queryKey: ['backendVersion'],
|
||||
queryFn: getBackendVersion,
|
||||
staleTime: 60000, // Cache for 1 minute
|
||||
refetchInterval: 300000, // Refetch every 5 minutes
|
||||
});
|
||||
|
||||
return (
|
||||
<footer className="fixed bottom-0 left-0 right-0 bg-dark-card border-t border-dark-border py-2 px-4 z-10">
|
||||
<div className="container mx-auto flex items-center justify-between text-xs text-dark-text-muted">
|
||||
<div>
|
||||
AI Code Review Agent
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<span>
|
||||
Backend v{versionData?.version || '...'}
|
||||
</span>
|
||||
<a
|
||||
href="https://github.com/yourusername/ai-review-agent"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="hover:text-dark-text-secondary transition-colors"
|
||||
>
|
||||
GitHub
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { WS_URL } from '../api/websocket';
|
||||
import { getReviewEvents, ReviewEvent } from '../api/client';
|
||||
|
||||
interface StreamEvent {
|
||||
type: string;
|
||||
@@ -36,6 +37,41 @@ export const ReviewStream: React.FC<ReviewStreamProps> = ({ reviewId }) => {
|
||||
console.log('🔌 Connecting to WebSocket:', WS_URL);
|
||||
console.log('👀 Watching for review ID:', reviewId);
|
||||
|
||||
// Load historical events from database
|
||||
const loadHistoricalEvents = async () => {
|
||||
try {
|
||||
console.log('📥 Loading historical events from DB...');
|
||||
const historicalEvents = await getReviewEvents(reviewId);
|
||||
console.log(`✅ Loaded ${historicalEvents.length} historical events`);
|
||||
|
||||
// Convert DB events to stream events format
|
||||
const streamEvents: StreamEvent[] = historicalEvents.map((dbEvent: ReviewEvent) => ({
|
||||
type: dbEvent.event_type,
|
||||
review_id: dbEvent.review_id,
|
||||
pr_number: 0, // Not stored in DB
|
||||
timestamp: dbEvent.created_at,
|
||||
data: {
|
||||
type: dbEvent.event_type,
|
||||
step: dbEvent.step,
|
||||
message: dbEvent.message,
|
||||
data: dbEvent.data
|
||||
}
|
||||
}));
|
||||
|
||||
setEvents(streamEvents);
|
||||
|
||||
// Set current step from last event
|
||||
const lastAgentStep = streamEvents.reverse().find(e => e.type === 'agent_step');
|
||||
if (lastAgentStep && lastAgentStep.data.step) {
|
||||
setCurrentStep(lastAgentStep.data.step);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Error loading historical events:', error);
|
||||
}
|
||||
};
|
||||
|
||||
loadHistoricalEvents();
|
||||
|
||||
const ws = new WebSocket(WS_URL);
|
||||
let pingInterval: number;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user