Skip to content
200 changes: 109 additions & 91 deletions frontend/src/components/AddRepoForm.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { useState } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
import { Package, Plus, X } from 'lucide-react'

interface AddRepoFormProps {
onAdd: (gitUrl: string, branch: string) => Promise<void>
Expand All @@ -13,110 +15,126 @@ export function AddRepoForm({ onAdd, loading }: AddRepoFormProps) {
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
if (!gitUrl) return

await onAdd(gitUrl, branch)
setGitUrl('')
setBranch('main')
setShowForm(false)
}

if (!showForm) {
return (
<button
return (
<>
<motion.button
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
onClick={() => setShowForm(true)}
className="px-4 py-2 bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white text-sm font-medium rounded-lg transition-all disabled:opacity-50"
className="px-5 py-2.5 bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white text-sm font-medium rounded-xl transition-all shadow-lg shadow-blue-500/20 flex items-center gap-2"
disabled={loading}
>
+ Add Repository
</button>
)
}
<span className="text-lg">+</span>
<span>Add Repository</span>
</motion.button>

return (
<div className="fixed inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-50">
<div className="bg-[#111113] border border-white/10 rounded-2xl shadow-2xl w-full max-w-md mx-4">
{/* Header */}
<div className="flex items-center justify-between p-5 border-b border-white/5">
<div>
<h3 className="text-lg font-semibold text-white">Add Repository</h3>
<p className="text-sm text-gray-400 mt-0.5">Clone and index a Git repository</p>
</div>
<button
onClick={() => setShowForm(false)}
className="w-8 h-8 flex items-center justify-center rounded-lg text-gray-400 hover:text-white hover:bg-white/5 transition-colors"
disabled={loading}
<AnimatePresence>
{showForm && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="fixed inset-0 bg-black/70 backdrop-blur-md flex items-center justify-center z-50"
onClick={() => !loading && setShowForm(false)}
>
</button>
</div>
<motion.div
initial={{ opacity: 0, scale: 0.95, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.95, y: 20 }}
onClick={e => e.stopPropagation()}
className="bg-gradient-to-br from-[#111113] to-[#0a0a0c] border border-white/10 rounded-2xl shadow-2xl w-full max-w-md mx-4"
>
{/* Header */}
<div className="flex items-center justify-between p-6 border-b border-white/5">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-blue-500/20 to-purple-500/20 flex items-center justify-center">
<Package className="w-5 h-5 text-blue-400" />
</div>
<div>
<h3 className="text-lg font-semibold text-white">Add Repository</h3>
<p className="text-sm text-gray-500">Clone and index with AI</p>
</div>
</div>
<button
onClick={() => setShowForm(false)}
className="w-8 h-8 flex items-center justify-center rounded-lg text-gray-400 hover:text-white hover:bg-white/10 transition-colors"
disabled={loading}
>
<X className="w-4 h-4" />
</button>
</div>

{/* Form */}
<form onSubmit={handleSubmit} className="p-5 space-y-5">
<div>
<label className="block text-sm font-medium mb-2 text-gray-300">
Git URL
</label>
<input
type="url"
value={gitUrl}
onChange={(e) => setGitUrl(e.target.value)}
placeholder="https://github.com/username/repo"
className="w-full px-4 py-3 bg-white/5 border border-white/10 rounded-xl text-white placeholder:text-gray-500 focus:outline-none focus:border-blue-500/50 focus:ring-1 focus:ring-blue-500/20 transition-all"
required
disabled={loading}
autoFocus
/>
</div>
{/* Form */}
<form onSubmit={handleSubmit} className="p-6 space-y-5">
<div>
<label className="block text-sm font-medium mb-2 text-gray-300">Git URL</label>
<input
type="url"
value={gitUrl}
onChange={(e) => setGitUrl(e.target.value)}
placeholder="https://github.com/username/repo"
className="w-full px-4 py-3 bg-white/5 border border-white/10 rounded-xl text-white placeholder:text-gray-500 focus:outline-none focus:border-blue-500/50 focus:ring-2 focus:ring-blue-500/20 transition-all"
required
disabled={loading}
autoFocus
/>
</div>

<div>
<label className="block text-sm font-medium mb-2 text-gray-300">
Branch
</label>
<input
type="text"
value={branch}
onChange={(e) => setBranch(e.target.value)}
placeholder="main"
className="w-full px-4 py-3 bg-white/5 border border-white/10 rounded-xl text-white placeholder:text-gray-500 focus:outline-none focus:border-blue-500/50 focus:ring-1 focus:ring-blue-500/20 transition-all"
required
disabled={loading}
/>
<p className="mt-2 text-xs text-gray-500">
The repository will be cloned and automatically indexed
</p>
</div>
<div>
<label className="block text-sm font-medium mb-2 text-gray-300">Branch</label>
<input
type="text"
value={branch}
onChange={(e) => setBranch(e.target.value)}
placeholder="main"
className="w-full px-4 py-3 bg-white/5 border border-white/10 rounded-xl text-white placeholder:text-gray-500 focus:outline-none focus:border-blue-500/50 focus:ring-2 focus:ring-blue-500/20 transition-all"
required
disabled={loading}
/>
<p className="mt-2 text-xs text-gray-500">Repository will be cloned and automatically indexed</p>
</div>

{/* Actions */}
<div className="flex gap-3 pt-2">
<button
type="button"
onClick={() => {
setShowForm(false)
setGitUrl('')
setBranch('main')
}}
className="flex-1 px-4 py-3 bg-white/5 border border-white/10 text-gray-300 font-medium rounded-xl hover:bg-white/10 transition-colors disabled:opacity-50"
disabled={loading}
>
Cancel
</button>
<button
type="submit"
className="flex-1 px-4 py-3 bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white font-medium rounded-xl transition-all disabled:opacity-50 flex items-center justify-center gap-2"
disabled={loading}
>
{loading ? (
<>
<div className="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" />
<span>Adding...</span>
</>
) : (
'Add & Index'
)}
</button>
</div>
</form>
</div>
</div>
{/* Actions */}
<div className="flex gap-3 pt-2">
<button
type="button"
onClick={() => { setShowForm(false); setGitUrl(''); setBranch('main') }}
className="flex-1 px-4 py-3 bg-white/5 border border-white/10 text-gray-300 font-medium rounded-xl hover:bg-white/10 transition-colors disabled:opacity-50"
disabled={loading}
>
Cancel
</button>
<motion.button
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
type="submit"
className="flex-1 px-4 py-3 bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white font-medium rounded-xl transition-all disabled:opacity-50 flex items-center justify-center gap-2 shadow-lg shadow-blue-500/20"
disabled={loading}
>
{loading ? (
<>
<div className="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" />
<span>Adding...</span>
</>
) : (
<>
<Plus className="w-4 h-4" />
<span>Add & Index</span>
</>
)}
</motion.button>
</div>
</form>
</motion.div>
</motion.div>
)}
</AnimatePresence>
</>
)
}
14 changes: 8 additions & 6 deletions frontend/src/components/DependencyGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import ReactFlow, {
} from 'reactflow'
import type { Node, Edge } from 'reactflow'
import dagre from 'dagre'
import { Lightbulb } from 'lucide-react'
import 'reactflow/dist/style.css'
import { useDependencyGraph } from '../hooks/useCachedQuery'

Expand Down Expand Up @@ -235,21 +236,21 @@ export function DependencyGraph({ repoId, apiUrl, apiKey }: DependencyGraphProps
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<div className="bg-[#0a0a0c] border border-white/5 rounded-xl p-5">
<div className="text-sm text-gray-400 mb-1">Total Files</div>
<div className="text-3xl font-bold text-white">{allNodes.length}</div>
<div className="text-3xl font-bold text-blue-500">{allNodes.length}</div>
</div>
<div className="bg-[#0a0a0c] border border-white/5 rounded-xl p-5">
<div className="text-sm text-gray-400 mb-1">Dependencies</div>
<div className="text-3xl font-bold text-blue-400">{edges.length}</div>
<div className="text-3xl font-bold text-blue-500">{edges.length}</div>
</div>
<div className="bg-[#0a0a0c] border border-white/5 rounded-xl p-5">
<div className="text-sm text-gray-400 mb-1">Avg per File</div>
<div className="text-3xl font-bold text-white">
<div className="text-3xl font-bold text-blue-500">
{metrics?.avg_dependencies?.toFixed(1) || 0}
</div>
</div>
<div className="bg-[#0a0a0c] border border-white/5 rounded-xl p-5">
<div className="text-sm text-gray-400 mb-1">Showing</div>
<div className="text-3xl font-bold text-green-400">{nodes.length}</div>
<div className="text-3xl font-bold text-blue-500">{nodes.length}</div>
</div>
</div>

Expand Down Expand Up @@ -357,8 +358,9 @@ export function DependencyGraph({ repoId, apiUrl, apiKey }: DependencyGraphProps
<span className="text-gray-400">Dependency</span>
</div>
</div>
<div className="mt-3 pt-3 border-t border-white/5 text-xs text-gray-500">
💡 Click any node to highlight its dependencies • Drag to pan • Scroll to zoom
<div className="mt-3 pt-3 border-t border-white/5 text-xs text-gray-500 flex items-center gap-2">
<Lightbulb className="w-3.5 h-3.5 text-blue-400" />
<span>Click any node to highlight its dependencies • Drag to pan • Scroll to zoom</span>
</div>
</div>
</div>
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/components/ImpactAnalyzer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export function ImpactAnalyzer({ repoId, apiUrl, apiKey }: ImpactAnalyzerProps)
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="bg-[#0a0a0c] border border-white/5 rounded-xl p-5">
<div className="text-sm text-gray-400 mb-1">Direct Dependencies</div>
<div className="text-3xl font-bold text-blue-400">
<div className="text-3xl font-bold text-blue-500">
{result.dependency_count}
</div>
<div className="text-xs text-gray-500 mt-1">
Expand All @@ -139,7 +139,7 @@ export function ImpactAnalyzer({ repoId, apiUrl, apiKey }: ImpactAnalyzerProps)

<div className="bg-[#0a0a0c] border border-white/5 rounded-xl p-5">
<div className="text-sm text-gray-400 mb-1">Total Impact</div>
<div className="text-3xl font-bold text-yellow-400">
<div className="text-3xl font-bold text-blue-500">
{result.dependent_count}
</div>
<div className="text-xs text-gray-500 mt-1">
Expand All @@ -149,7 +149,7 @@ export function ImpactAnalyzer({ repoId, apiUrl, apiKey }: ImpactAnalyzerProps)

<div className="bg-[#0a0a0c] border border-white/5 rounded-xl p-5">
<div className="text-sm text-gray-400 mb-1">Test Files</div>
<div className="text-3xl font-bold text-green-400">
<div className="text-3xl font-bold text-blue-500">
{result.test_files?.length || 0}
</div>
<div className="text-xs text-gray-500 mt-1">
Expand Down
Loading