VIBN Frontend for Coolify deployment
This commit is contained in:
279
app/[workspace]/debug-sessions/page.tsx
Normal file
279
app/[workspace]/debug-sessions/page.tsx
Normal file
@@ -0,0 +1,279 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState, useCallback } from 'react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { auth, db } from '@/lib/firebase/config';
|
||||
import { collection, query, where, getDocs, orderBy, limit } from 'firebase/firestore';
|
||||
import { RefreshCw, CheckCircle2, AlertCircle, Link as LinkIcon } from 'lucide-react';
|
||||
|
||||
interface SessionDebugInfo {
|
||||
id: string;
|
||||
projectId?: string;
|
||||
workspacePath?: string;
|
||||
workspaceName?: string;
|
||||
needsProjectAssociation: boolean;
|
||||
model?: string;
|
||||
tokensUsed?: number;
|
||||
cost?: number;
|
||||
createdAt: any;
|
||||
}
|
||||
|
||||
export default function DebugSessionsPage() {
|
||||
const [sessions, setSessions] = useState<SessionDebugInfo[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [userId, setUserId] = useState<string>('');
|
||||
|
||||
const loadSessions = useCallback(async () => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const user = auth.currentUser;
|
||||
if (!user) {
|
||||
setError('Not authenticated');
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setUserId(user.uid);
|
||||
|
||||
const sessionsRef = collection(db, 'sessions');
|
||||
// Remove orderBy to avoid index issues - just get recent sessions
|
||||
const sessionsQuery = query(
|
||||
sessionsRef,
|
||||
where('userId', '==', user.uid),
|
||||
limit(50)
|
||||
);
|
||||
const snapshot = await getDocs(sessionsQuery);
|
||||
|
||||
const sessionsData = snapshot.docs.map(doc => {
|
||||
const data = doc.data();
|
||||
return {
|
||||
id: doc.id,
|
||||
projectId: data.projectId || null,
|
||||
workspacePath: data.workspacePath || null,
|
||||
workspaceName: data.workspaceName || null,
|
||||
needsProjectAssociation: data.needsProjectAssociation || false,
|
||||
model: data.model,
|
||||
tokensUsed: data.tokensUsed,
|
||||
cost: data.cost,
|
||||
createdAt: data.createdAt,
|
||||
};
|
||||
});
|
||||
|
||||
console.log('DEBUG: All sessions from Firebase:', sessionsData);
|
||||
setSessions(sessionsData);
|
||||
} catch (err: any) {
|
||||
console.error('Error loading sessions:', err);
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true;
|
||||
|
||||
const unsubscribe = auth.onAuthStateChanged((user) => {
|
||||
if (!mounted) return;
|
||||
|
||||
if (user) {
|
||||
loadSessions();
|
||||
} else {
|
||||
setError('Please sign in');
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
mounted = false;
|
||||
unsubscribe();
|
||||
};
|
||||
}, [loadSessions]);
|
||||
|
||||
const unassociatedSessions = sessions.filter(s => s.needsProjectAssociation);
|
||||
const associatedSessions = sessions.filter(s => !s.needsProjectAssociation);
|
||||
|
||||
// Group unassociated sessions by workspace path
|
||||
const sessionsByWorkspace = unassociatedSessions.reduce((acc, session) => {
|
||||
const path = session.workspacePath || 'No workspace path';
|
||||
if (!acc[path]) acc[path] = [];
|
||||
acc[path].push(session);
|
||||
return acc;
|
||||
}, {} as Record<string, SessionDebugInfo[]>);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen p-8 bg-background">
|
||||
<div className="max-w-7xl mx-auto space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold">🔍 Sessions Debug Page</h1>
|
||||
<p className="text-muted-foreground mt-2">
|
||||
View all your chat sessions and their workspace paths
|
||||
</p>
|
||||
{userId && (
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
User ID: <code className="bg-muted px-2 py-1 rounded">{userId}</code>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<Button onClick={loadSessions} disabled={loading}>
|
||||
<RefreshCw className={`h-4 w-4 mr-2 ${loading ? 'animate-spin' : ''}`} />
|
||||
Refresh
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<Card className="border-red-500">
|
||||
<CardContent className="pt-6">
|
||||
<p className="text-red-600">Error: {error}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{loading && !error && (
|
||||
<Card>
|
||||
<CardContent className="pt-6">
|
||||
<p className="text-center text-muted-foreground">Loading sessions...</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{!loading && !error && (
|
||||
<>
|
||||
{/* Summary */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Summary</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<div>
|
||||
<p className="text-sm text-muted-foreground">Total Sessions</p>
|
||||
<p className="text-2xl font-bold">{sessions.length}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-muted-foreground">Linked to Projects</p>
|
||||
<p className="text-2xl font-bold text-green-600">{associatedSessions.length}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-muted-foreground">Unassociated (Available)</p>
|
||||
<p className="text-2xl font-bold text-orange-600">{unassociatedSessions.length}</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Unassociated Sessions by Workspace */}
|
||||
{unassociatedSessions.length > 0 && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<AlertCircle className="h-5 w-5 text-orange-600" />
|
||||
Unassociated Sessions (Available to Link)
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
{Object.entries(sessionsByWorkspace).map(([path, workspaceSessions]) => {
|
||||
const folderName = path !== 'No workspace path' ? path.split('/').pop() : null;
|
||||
return (
|
||||
<div key={path} className="border rounded-lg p-4">
|
||||
<div className="mb-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="font-semibold">📁 {folderName || 'Unknown folder'}</p>
|
||||
<code className="text-xs text-muted-foreground break-all">{path}</code>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="text-2xl font-bold">{workspaceSessions.length}</p>
|
||||
<p className="text-xs text-muted-foreground">sessions</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
{workspaceSessions.slice(0, 3).map((session) => (
|
||||
<div key={session.id} className="text-xs bg-muted/50 p-2 rounded">
|
||||
<div className="flex justify-between">
|
||||
<span className="font-mono">{session.id.substring(0, 12)}...</span>
|
||||
<span>{session.model || 'unknown'}</span>
|
||||
</div>
|
||||
<div className="text-muted-foreground">
|
||||
{session.tokensUsed?.toLocaleString()} tokens • ${session.cost?.toFixed(4)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{workspaceSessions.length > 3 && (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
+ {workspaceSessions.length - 3} more sessions...
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-3 p-3 bg-blue-50 dark:bg-blue-950/20 rounded text-sm">
|
||||
<p className="text-blue-600 dark:text-blue-400 font-medium mb-1">
|
||||
💡 To link these sessions:
|
||||
</p>
|
||||
<ol className="text-xs text-muted-foreground space-y-1 ml-4 list-decimal">
|
||||
<li>Create a project with workspace path: <code className="bg-muted px-1 rounded">{path}</code></li>
|
||||
<li>OR connect GitHub to a project that already has this workspace path set</li>
|
||||
</ol>
|
||||
<p className="text-xs text-muted-foreground mt-2">
|
||||
Folder name: <code className="bg-muted px-1 rounded">{folderName}</code>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Associated Sessions */}
|
||||
{associatedSessions.length > 0 && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<CheckCircle2 className="h-5 w-5 text-green-600" />
|
||||
Linked Sessions
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-sm text-muted-foreground mb-3">
|
||||
These sessions are already linked to projects
|
||||
</p>
|
||||
<div className="space-y-2">
|
||||
{associatedSessions.slice(0, 5).map((session) => (
|
||||
<div key={session.id} className="flex items-center justify-between p-2 border rounded text-sm">
|
||||
<div>
|
||||
<code className="text-xs">{session.id.substring(0, 12)}...</code>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{session.workspaceName || 'No workspace'}
|
||||
</p>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<LinkIcon className="h-4 w-4 text-green-600" />
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Project: {session.projectId?.substring(0, 8)}...
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{sessions.length === 0 && (
|
||||
<Card>
|
||||
<CardContent className="pt-6 text-center">
|
||||
<p className="text-muted-foreground">No sessions found. Start coding with Cursor to track sessions!</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user