From 9573b8e83a1847f72b49919e3ad02af122e1b0cd Mon Sep 17 00:00:00 2001 From: Vishal Shenoy Date: Wed, 12 Mar 2025 11:54:53 -0700 Subject: [PATCH 1/4] AI impact dashboard --- .../examples/ai_impact_analysis/README.md | 70 + .../ai_impact_analysis/dashboard/README.md | 77 + .../dashboard/backend/api.py | 54 + .../dashboard/frontend/app/favicon.ico | Bin 0 -> 15406 bytes .../dashboard/frontend/app/globals.css | 77 + .../dashboard/frontend/app/layout.tsx | 31 + .../dashboard/frontend/app/page.tsx | 10 + .../dashboard/frontend/components.json | 21 + .../components/contribution-timeline.tsx | 57 + .../components/contributors-breakdown.tsx | 92 + .../frontend/components/dashboard-header.tsx | 15 + .../components/high-impact-symbols.tsx | 43 + .../frontend/components/loading-screen.tsx | 14 + .../components/repo-analysis-dashboard.tsx | 111 + .../frontend/components/summary-cards.tsx | 70 + .../frontend/components/theme-provider.tsx | 11 + .../frontend/components/top-ai-files.tsx | 35 + .../frontend/components/ui/button.tsx | 56 + .../dashboard/frontend/components/ui/card.tsx | 79 + .../frontend/components/ui/chart.tsx | 365 ++ .../frontend/components/ui/input-otp.tsx | 71 + .../frontend/components/ui/input.tsx | 22 + .../frontend/components/ui/progress.tsx | 28 + .../frontend/components/ui/scroll-area.tsx | 48 + .../frontend/components/ui/skeleton.tsx | 15 + .../frontend/components/ui/use-mobile.tsx | 19 + .../frontend/components/ui/use-toast.ts | 194 + .../dashboard/frontend/hooks/use-mobile.tsx | 19 + .../dashboard/frontend/hooks/use-toast.ts | 194 + .../dashboard/frontend/lib/types.ts | 28 + .../dashboard/frontend/lib/utils.ts | 6 + .../dashboard/frontend/next.config.mjs | 48 + .../dashboard/frontend/package-lock.json | 4640 +++++++++++++++++ .../dashboard/frontend/package.json | 70 + .../dashboard/frontend/postcss.config.mjs | 8 + .../dashboard/frontend/styles/globals.css | 94 + .../dashboard/frontend/tailwind.config.js | 78 + .../dashboard/frontend/tsconfig.json | 27 + 38 files changed, 6897 insertions(+) create mode 100644 codegen-examples/examples/ai_impact_analysis/README.md create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/README.md create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/backend/api.py create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/app/favicon.ico create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/app/globals.css create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/app/layout.tsx create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/app/page.tsx create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components.json create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/contribution-timeline.tsx create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/contributors-breakdown.tsx create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/dashboard-header.tsx create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/high-impact-symbols.tsx create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/loading-screen.tsx create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/repo-analysis-dashboard.tsx create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/summary-cards.tsx create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/theme-provider.tsx create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/top-ai-files.tsx create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/ui/button.tsx create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/ui/card.tsx create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/ui/chart.tsx create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/ui/input-otp.tsx create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/ui/input.tsx create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/ui/progress.tsx create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/ui/scroll-area.tsx create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/ui/skeleton.tsx create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/ui/use-mobile.tsx create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/ui/use-toast.ts create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/hooks/use-mobile.tsx create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/hooks/use-toast.ts create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/lib/types.ts create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/lib/utils.ts create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/next.config.mjs create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/package-lock.json create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/package.json create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/postcss.config.mjs create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/styles/globals.css create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/tailwind.config.js create mode 100644 codegen-examples/examples/ai_impact_analysis/dashboard/frontend/tsconfig.json diff --git a/codegen-examples/examples/ai_impact_analysis/README.md b/codegen-examples/examples/ai_impact_analysis/README.md new file mode 100644 index 000000000..d2c534722 --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/README.md @@ -0,0 +1,70 @@ +# AI Impact Analysis + +This script analyzes a codebase to measure and report the impact of AI-generated code contributions. It provides detailed insights about AI vs human contributions, helping teams understand the role of AI in their development process. + +## Features + +- **Repository Analysis**: Automatically detects and analyzes git repositories: + - Uses current directory if it's a git repo + - Searches parent directories for a git repo + - Falls back to cloning a specified repository if needed + + ```python + repo_path = os.getcwd() + repo_config = RepoConfig.from_repo_path(repo_path) + repo_operator = RepoOperator(repo_config=repo_config) + project = ProjectConfig.from_repo_operator(repo_operator=repo_operator, programming_language=ProgrammingLanguage.PYTHON) + codebase = Codebase(projects=[project]) + ``` + +- **Comprehensive Statistics**: + - Total number of commits and AI vs human contribution percentages + - Files with significant AI contribution (>50%) + - AI-touched symbols and their impact + - Detailed contributor breakdown (human and AI contributors) + +- **High-Impact Code Detection**: + - Identifies AI-written code that is heavily used by other parts of the codebase + - Shows dependency relationships for AI-contributed code + +- **Detailed Attribution**: + - Maps symbols to git history + - Tracks last editor and complete editor history for each symbol + - Flags AI-authored symbols + +## Output + +The script generates: +1. Console output with summary statistics +2. Detailed analysis in `ai_impact_analysis.json` +3. Attribution information added to codebase symbols + +## Usage + +```bash +python run.py +``` + +The script will automatically: +1. Initialize and analyze the codebase +2. Process git history +3. Generate attribution information +4. Output detailed statistics + +You can also visualize the AI impact analysis results using a dashboard. For setup and usage instructions, please see the documentation in the `/dashboard` subdirectory. + +## Symbol Attribution + +After running the analysis, symbols in the codebase will have the following attribution information: +- `symbol.last_editor`: The last person who edited the symbol +- `symbol.editor_history`: List of all editors who have touched the symbol +- `symbol.is_ai_authored`: Boolean indicating if the symbol was authored by AI + +## Learn More + +- [Attributions](https://docs.codegen.com/tutorials/attributions) +- [Codegen Documentation](https://docs.codegen.com) + +## Contributing + +Feel free to submit issues and enhancement requests! diff --git a/codegen-examples/examples/ai_impact_analysis/dashboard/README.md b/codegen-examples/examples/ai_impact_analysis/dashboard/README.md new file mode 100644 index 000000000..3a23cd275 --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/README.md @@ -0,0 +1,77 @@ +# AI Impact Analysis Dashboard + +A web dashboard for visualizing AI-generated code contributions in your codebase. This dashboard provides detailed insights about AI vs human contributions, helping understand the role of AI in a codebase development process. + +## Setup + +### Backend + +1. Install dependencies: +```bash +uv venv +source .venv/bin/activate +uv pip install modal codegen fastapi +``` + +2. Deploy or serve the Modal endpoint: +```bash +modal serve backend/api.py +``` +```bash +modal deploy backend/api.py +``` + +### Frontend + +1. Install dependencies: +```bash +cd frontend +npm install +``` + +2. Update the API endpoint: +Edit the fetch URL on line 29 in `components/repo-analysis-dashboard.tsx` to point to your Modal endpoint: +```bash + fetch(`[your-modal-deployment-url]/analyze?repo_full_name=${repoFullName}`, { + method: 'POST', + }) +``` + +3. Start the development server: +```bash +npm run dev +``` + +## Usage + +1. Visit the dashboard in your browser (default: http://localhost:3000) +2. Enter a GitHub repository name (format: username/repo) +3. Click "Analyze Repo" to generate insights + +The dashboard will display: +- Summary statistics of AI contributions +- Monthly contribution timeline +- Top files with AI contributions +- High-impact AI-authored symbols +- Contributor breakdown visualization + +## Architecture + +- **Backend**: Modal-deployed FastAPI service that: + - Clones and analyzes repositories + - Processes git history + - Calculates AI impact metrics + - Returns structured analysis data + +- **Frontend**: Next.js application with: + - Interactive charts + - Visualized AI impact metrics + +## Learn More + +- [AI Impact Analysis Documentation](https://docs.codegen.com/tutorials/attributions) +- [Codegen Documentation](https://docs.codegen.com) + +## Contributing + +Feel free to submit issues and enhancement requests! diff --git a/codegen-examples/examples/ai_impact_analysis/dashboard/backend/api.py b/codegen-examples/examples/ai_impact_analysis/dashboard/backend/api.py new file mode 100644 index 000000000..ddb08115d --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/backend/api.py @@ -0,0 +1,54 @@ +from codegen import Codebase +from codegen.extensions.attribution.main import ( + add_attribution_to_symbols, + analyze_ai_impact, +) +from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware +import modal + +image = modal.Image.debian_slim().apt_install("git").pip_install("codegen", "fastapi", "intervaltree", "pygit2", "requests") + +app = modal.App(name="ai-impact-analysis", image=image) + +fastapi_app = FastAPI() + +fastapi_app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + + +@fastapi_app.post("/analyze") +async def analyze(repo_full_name: str): + codebase = Codebase.from_repo(repo_full_name=repo_full_name, language="python", full_history=True) + + print("šŸ¤– Analyzing AI impact on codebase...") + + ai_authors = [ + "renovate[bot]", + "dependabot[bot]", + "github-actions[bot]", + "devin-ai-integration[bot]", + ] + + results = analyze_ai_impact(codebase, ai_authors) + + print("\nšŸ·ļø Adding attribution information to symbols...") + add_attribution_to_symbols(codebase, ai_authors) + print("āœ… Attribution information added to symbols") + + return results + + +@app.function(image=image) +@modal.asgi_app() +def fastapi_modal_app(): + return fastapi_app + + +if __name__ == "__main__": + app.deploy("ai-impact-analysis") diff --git a/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/app/favicon.ico b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..fd85877464ce8ec6468b611d781ca11399bb1a5e GIT binary patch literal 15406 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 TvEcn(qr3cCU(fm;uzTSD5Uj=O literal 0 HcmV?d00001 diff --git a/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/app/globals.css b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/app/globals.css new file mode 100644 index 000000000..992f0447a --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/app/globals.css @@ -0,0 +1,77 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 222.2 84% 4.9%; + + --card: 0 0% 100%; + --card-foreground: 222.2 84% 4.9%; + + --popover: 0 0% 100%; + --popover-foreground: 222.2 84% 4.9%; + + --primary: 221.2 83.2% 53.3%; + --primary-foreground: 210 40% 98%; + + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; + + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; + + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 210 40% 98%; + + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + --ring: 221.2 83.2% 53.3%; + + --radius: 0.5rem; + } + + .dark { + --background: 222.2 84% 4.9%; + --foreground: 210 40% 98%; + + --card: 222.2 84% 4.9%; + --card-foreground: 210 40% 98%; + + --popover: 222.2 84% 4.9%; + --popover-foreground: 210 40% 98%; + + --primary: 217.2 91.2% 59.8%; + --primary-foreground: 222.2 47.4% 11.2%; + + --secondary: 217.2 32.6% 17.5%; + --secondary-foreground: 210 40% 98%; + + --muted: 217.2 32.6% 17.5%; + --muted-foreground: 215 20.2% 65.1%; + + --accent: 217.2 32.6% 17.5%; + --accent-foreground: 210 40% 98%; + + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 210 40% 98%; + + --border: 217.2 32.6% 17.5%; + --input: 217.2 32.6% 17.5%; + --ring: 224.3 76.3% 48%; + } +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} + diff --git a/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/app/layout.tsx b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/app/layout.tsx new file mode 100644 index 000000000..71df91fc9 --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/app/layout.tsx @@ -0,0 +1,31 @@ +import type React from "react" +import type { Metadata } from "next" +import { Inter } from "next/font/google" +import "./globals.css" +import { ThemeProvider } from "@/components/theme-provider" + +const inter = Inter({ subsets: ["latin"] }) + +export const metadata: Metadata = { + title: "AI Code Impact Analysis", +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + + + {children} + + + + ) +} + + + +import './globals.css' \ No newline at end of file diff --git a/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/app/page.tsx b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/app/page.tsx new file mode 100644 index 000000000..66c79d1e6 --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/app/page.tsx @@ -0,0 +1,10 @@ +import { RepoAnalysisDashboard } from "@/components/repo-analysis-dashboard" + +export default function Home() { + return ( +
+ +
+ ) +} + diff --git a/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components.json b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components.json new file mode 100644 index 000000000..d9ef0ae53 --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/contribution-timeline.tsx b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/contribution-timeline.tsx new file mode 100644 index 000000000..4897c5d0c --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/contribution-timeline.tsx @@ -0,0 +1,57 @@ +"use client" + +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import type { Timeline } from "@/lib/types" +import { Bar, BarChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts" + +interface ContributionTimelineProps { + timeline: Timeline[] +} + +export function ContributionTimeline({ timeline }: ContributionTimelineProps) { + return ( + + + AI Contribution Timeline + Monthly AI contributions over time + + + + + + `${value}`} + /> + { + if (active && payload && payload.length) { + return ( +
+
+
+ Date + {payload[0].payload.date} +
+
+ Commits + {payload[0].value} +
+
+
+ ) + } + return null + }} + /> + +
+
+
+
+ ) +} + diff --git a/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/contributors-breakdown.tsx b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/contributors-breakdown.tsx new file mode 100644 index 000000000..1355b6821 --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/contributors-breakdown.tsx @@ -0,0 +1,92 @@ +"use client" + +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { ScrollArea } from "@/components/ui/scroll-area" +import { PieChart, Pie, Cell, ResponsiveContainer, Legend, Tooltip } from "recharts" + +interface ContributorsBreakdownProps { + contributors: [string, number][] +} + +export function ContributorsBreakdown({ contributors }: ContributorsBreakdownProps) { + // Take top 5 contributors for the chart + const topContributors = contributors.slice(0, 5) + const otherContributors = contributors.slice(5) + const otherCount = otherContributors.reduce((sum, [_, count]) => sum + count, 0) + + const chartData = [ + ...topContributors.map(([name, count]) => ({ + name: name.split(" ")[0], // Just use first name for chart + fullName: name, + count, + })), + otherContributors.length > 0 ? { name: "Others", fullName: "Other Contributors", count: otherCount } : null, + ].filter(Boolean) + + const COLORS = ["#3b82f6", "#10b981", "#f59e0b", "#ef4444", "#8b5cf6", "#6b7280"] + + return ( + + + Contributors Breakdown + Top contributors by commit count + + +
+
+ + + + {chartData.map((entry, index) => ( + + ))} + + [value, props.payload.fullName]} + contentStyle={{ + backgroundColor: "white", + borderColor: "#e2e8f0", + borderRadius: "0.375rem", + }} + /> + + + +
+
+ +
+ {contributors.slice(0, 10).map(([name, count], index) => ( +
+
+
+ {name.split(" ")[0]} +
+
{count}
+
+ ))} + {contributors.length > 10 && ( +
+ +{contributors.length - 10} more contributors +
+ )} +
+ +
+
+ + + ) +} + diff --git a/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/dashboard-header.tsx b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/dashboard-header.tsx new file mode 100644 index 000000000..b525776a0 --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/dashboard-header.tsx @@ -0,0 +1,15 @@ +import { Code2 } from "lucide-react" + +export function DashboardHeader() { + return ( +
+
+
+

AI Code Impact Analysis

+
+

Analyze AI-generated code contributions in your repository

+
+
+ ) +} + diff --git a/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/high-impact-symbols.tsx b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/high-impact-symbols.tsx new file mode 100644 index 000000000..7b6b274f9 --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/high-impact-symbols.tsx @@ -0,0 +1,43 @@ +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { ScrollArea } from "@/components/ui/scroll-area" +import type { HighImpactSymbol } from "@/lib/types" + +interface HighImpactSymbolsProps { + symbols: HighImpactSymbol[] +} + +export function HighImpactSymbols({ symbols }: HighImpactSymbolsProps) { + return ( + + + High-Impact AI Symbols + AI-written code with significant usage + + + +
+ {symbols.length > 0 ? ( + symbols.map((symbol) => ( +
+
+
{symbol.name}
+
Used by {symbol.usage_count} symbols
+
+
{symbol.filepath}
+
+ Last edited by: {symbol.last_editor} +
+
+ )) + ) : ( +
+ No high-impact AI symbols found +
+ )} +
+
+
+
+ ) +} + diff --git a/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/loading-screen.tsx b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/loading-screen.tsx new file mode 100644 index 000000000..325543379 --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/loading-screen.tsx @@ -0,0 +1,14 @@ +import { Loader2 } from "lucide-react" + +export function LoadingScreen() { + return ( +
+
+ +

Analyzing Repository

+

This may take a few seconds...

+
+
+ ) +} + diff --git a/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/repo-analysis-dashboard.tsx b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/repo-analysis-dashboard.tsx new file mode 100644 index 000000000..9a16beff3 --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/repo-analysis-dashboard.tsx @@ -0,0 +1,111 @@ +"use client" + +import { useState } from "react" +import { GitBranch, Loader2 } from "lucide-react" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Card, CardContent } from "@/components/ui/card" +import { DashboardHeader } from "@/components/dashboard-header" +import { SummaryCards } from "@/components/summary-cards" +import { ContributionTimeline } from "@/components/contribution-timeline" +import { TopAIFiles } from "@/components/top-ai-files" +import { HighImpactSymbols } from "@/components/high-impact-symbols" +import { ContributorsBreakdown } from "@/components/contributors-breakdown" +import { LoadingScreen } from "@/components/loading-screen" +import type { AnalysisData } from "@/lib/types" + +export function RepoAnalysisDashboard() { + const [data, setData] = useState(null) + const [loading, setLoading] = useState(false) + const [repoUrl, setRepoUrl] = useState("") + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault() + if (repoUrl.trim()) { + setLoading(true) + const match = repoUrl.match(/(?:github\.com\/)?([^/\s]+\/[^/\s]+)/) + if (match) { + const repoFullName = match[1] + fetch(`[your-modal-deployment-url]/analyze?repo_full_name=${repoFullName}`, { + method: 'POST', + }) + .then(response => { + if (!response.ok) { + throw new Error('Network response was not ok') + } + return response.json() + }) + .then((analysisData: AnalysisData) => { + setData(analysisData) + setLoading(false) + }) + .catch(error => { + console.error('Error analyzing repository:', error) + setLoading(false) + }) + } + } + } + + return ( +
+ {loading && } + + + + + +
+
+ +
+
+ + setRepoUrl(e.target.value)} + disabled={loading} + /> +
+ +
+
+
+
+
+ + {data && ( +
+ + +
+ + +
+ +
+ + +
+
+ )} +

+

+
+ ) +} + diff --git a/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/summary-cards.tsx b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/summary-cards.tsx new file mode 100644 index 000000000..2399b35ba --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/summary-cards.tsx @@ -0,0 +1,70 @@ +import { BarChart3, FileCode, GitCommit, Percent } from "lucide-react" +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import type { AnalysisData } from "@/lib/types" + +interface SummaryCardsProps { + data: AnalysisData +} + +export function SummaryCards({ data }: SummaryCardsProps) { + const { stats, ai_symbol_count, total_symbol_count } = data + + return ( +
+ + + AI Commits + + + +
+ {stats.ai_commits} / {stats.total_commits} +
+

{stats.ai_percentage.toFixed(1)}% of total commits

+
+
+ + + + AI Files + + + +
+ {stats.ai_file_count} / {stats.total_file_count} +
+

+ {((stats.ai_file_count / stats.total_file_count) * 100).toFixed(1)}% of files have >50% AI contribution +

+
+
+ + + + AI Symbols + + + +
+ {ai_symbol_count} / {total_symbol_count} +
+

+ {((ai_symbol_count / total_symbol_count) * 100).toFixed(1)}% of code symbols +

+
+
+ + + + High Impact + + + +
{data.high_impact_symbols.length}
+

AI-written symbols with high usage

+
+
+
+ ) +} + diff --git a/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/theme-provider.tsx b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/theme-provider.tsx new file mode 100644 index 000000000..55c2f6eb6 --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/theme-provider.tsx @@ -0,0 +1,11 @@ +'use client' + +import * as React from 'react' +import { + ThemeProvider as NextThemesProvider, + type ThemeProviderProps, +} from 'next-themes' + +export function ThemeProvider({ children, ...props }: ThemeProviderProps) { + return {children} +} diff --git a/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/top-ai-files.tsx b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/top-ai-files.tsx new file mode 100644 index 000000000..675727063 --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/top-ai-files.tsx @@ -0,0 +1,35 @@ +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { Progress } from "@/components/ui/progress" +import { ScrollArea } from "@/components/ui/scroll-area" + +interface TopAIFilesProps { + files: [string, number][] +} + +export function TopAIFiles({ files }: TopAIFilesProps) { + return ( + + + Top AI-Contributed Files + Files with highest AI contribution percentage + + + +
+ {files.map(([filepath, percentage]) => ( +
+
+
{filepath.split("/").pop()}
+
{percentage.toFixed(1)}%
+
+ +
{filepath}
+
+ ))} +
+
+
+
+ ) +} + diff --git a/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/ui/button.tsx b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/ui/button.tsx new file mode 100644 index 000000000..36496a287 --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/ui/button.tsx @@ -0,0 +1,56 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const buttonVariants = cva( + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: + "bg-destructive text-destructive-foreground hover:bg-destructive/90", + outline: + "border border-input bg-background hover:bg-accent hover:text-accent-foreground", + secondary: + "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-10 px-4 py-2", + sm: "h-9 rounded-md px-3", + lg: "h-11 rounded-md px-8", + icon: "h-10 w-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button" + return ( + + ) + } +) +Button.displayName = "Button" + +export { Button, buttonVariants } diff --git a/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/ui/card.tsx b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/ui/card.tsx new file mode 100644 index 000000000..f62edea57 --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/ui/card.tsx @@ -0,0 +1,79 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +Card.displayName = "Card" + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardHeader.displayName = "CardHeader" + +const CardTitle = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardTitle.displayName = "CardTitle" + +const CardDescription = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardDescription.displayName = "CardDescription" + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardContent.displayName = "CardContent" + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardFooter.displayName = "CardFooter" + +export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/ui/chart.tsx b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/ui/chart.tsx new file mode 100644 index 000000000..8620baa3b --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/ui/chart.tsx @@ -0,0 +1,365 @@ +"use client" + +import * as React from "react" +import * as RechartsPrimitive from "recharts" + +import { cn } from "@/lib/utils" + +// Format: { THEME_NAME: CSS_SELECTOR } +const THEMES = { light: "", dark: ".dark" } as const + +export type ChartConfig = { + [k in string]: { + label?: React.ReactNode + icon?: React.ComponentType + } & ( + | { color?: string; theme?: never } + | { color?: never; theme: Record } + ) +} + +type ChartContextProps = { + config: ChartConfig +} + +const ChartContext = React.createContext(null) + +function useChart() { + const context = React.useContext(ChartContext) + + if (!context) { + throw new Error("useChart must be used within a ") + } + + return context +} + +const ChartContainer = React.forwardRef< + HTMLDivElement, + React.ComponentProps<"div"> & { + config: ChartConfig + children: React.ComponentProps< + typeof RechartsPrimitive.ResponsiveContainer + >["children"] + } +>(({ id, className, children, config, ...props }, ref) => { + const uniqueId = React.useId() + const chartId = `chart-${id || uniqueId.replace(/:/g, "")}` + + return ( + +
+ + + {children} + +
+
+ ) +}) +ChartContainer.displayName = "Chart" + +const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { + const colorConfig = Object.entries(config).filter( + ([_, config]) => config.theme || config.color + ) + + if (!colorConfig.length) { + return null + } + + return ( +