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..e34e1a8af --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/README.md @@ -0,0 +1,124 @@ +# 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 + # Basic repository setup + 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) + + ```python + # Run the analysis + ai_authors = ["github-actions[bot]", "dependabot[bot]"] + results = analyze_ai_impact(codebase, ai_authors) + + # Access statistics + stats = results["stats"] + print(f"Total commits: {stats['total_commits']}") + print(f"AI commits: {stats['ai_commits']} ({stats['ai_percentage']:.1f}%)") + print(f"Files with >50% AI: {stats['ai_file_count']} of {stats['total_file_count']}") + + # View contributors + for author, count in results["contributors"]: + is_ai = any(ai_name in author for ai_name in ai_authors) + print(f"{'šŸ¤–' if is_ai else 'šŸ‘¤'} {author}: {count} commits") + ``` + +- **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 + + ```python + # Access high-impact AI symbols + for symbol in results["high_impact_symbols"]: + print(f"Symbol: {symbol['name']} ({symbol['filepath']})") + print(f"Used by {symbol['usage_count']} other symbols") + print(f"Last edited by: {symbol['last_editor']}") + + # View top AI-contributed files + for file_path, percentage in stats["top_ai_files"]: + print(f"{file_path}: {percentage:.1f}% AI contribution") + ``` + +- **Detailed Attribution**: + + - Maps symbols to git history + - Tracks last editor and complete editor history for each symbol + - Flags AI-authored symbols + + ```python + # Get attribution information for a specific symbol + symbol = codebase.get_symbol("path/to/file.py:MyClass.my_method") + + # Access attribution data + print(f"Last editor: {symbol.last_editor}") + print(f"Editor history: {symbol.editor_history}") + print(f"AI authored: {symbol.is_ai_authored}") + + # Find all AI-authored symbols + ai_symbols = [s for s in codebase.get_symbols() if s.is_ai_authored] + for symbol in ai_symbols: + print(f"AI symbol: {symbol.name}") + ``` + +## Output + +The script generates: + +1. Console output with summary statistics +1. Detailed analysis in `ai_impact_analysis.json` +1. Attribution information added to codebase symbols + +## Usage + +```bash +python run.py +``` + +The script will automatically: + +1. Initialize and analyze the codebase +1. Process git history +1. Generate attribution information +1. 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..cde758b55 --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/README.md @@ -0,0 +1,86 @@ +# 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) +1. Enter a GitHub repository name (format: username/repo) +1. 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 000000000..fd8587746 Binary files /dev/null and b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/app/favicon.ico differ 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..1535f872d --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/app/globals.css @@ -0,0 +1,76 @@ +@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..264632940 --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/app/layout.tsx @@ -0,0 +1,34 @@ +import type { Metadata } from "next"; +import { Inter } from "next/font/google"; +import type React from "react"; +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"; 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..5b048bbdf --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/app/page.tsx @@ -0,0 +1,9 @@ +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..7f48f98e8 --- /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" +} 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..e6017ba9c --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/contribution-timeline.tsx @@ -0,0 +1,83 @@ +"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..fb54b7fec --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/contributors-breakdown.tsx @@ -0,0 +1,131 @@ +"use client"; + +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { + Cell, + Legend, + Pie, + PieChart, + ResponsiveContainer, + 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..3fa854491 --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/dashboard-header.tsx @@ -0,0 +1,18 @@ +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..db1fe51aa --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/high-impact-symbols.tsx @@ -0,0 +1,58 @@ +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..089cdf833 --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/loading-screen.tsx @@ -0,0 +1,15 @@ +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..4cf6ddb71 --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/repo-analysis-dashboard.tsx @@ -0,0 +1,113 @@ +"use client"; + +import { ContributionTimeline } from "@/components/contribution-timeline"; +import { ContributorsBreakdown } from "@/components/contributors-breakdown"; +import { DashboardHeader } from "@/components/dashboard-header"; +import { HighImpactSymbols } from "@/components/high-impact-symbols"; +import { LoadingScreen } from "@/components/loading-screen"; +import { SummaryCards } from "@/components/summary-cards"; +import { TopAIFiles } from "@/components/top-ai-files"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent } from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import type { AnalysisData } from "@/lib/types"; +import { GitBranch, Loader2 } from "lucide-react"; +import { useState } from "react"; + +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..dea866379 --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/summary-cards.tsx @@ -0,0 +1,77 @@ +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import type { AnalysisData } from "@/lib/types"; +import { BarChart3, FileCode, GitCommit, Percent } from "lucide-react"; + +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..020003cf9 --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/theme-provider.tsx @@ -0,0 +1,11 @@ +"use client"; + +import { + ThemeProvider as NextThemesProvider, + type ThemeProviderProps, +} from "next-themes"; +import * as React from "react"; + +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..67a5472a8 --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/top-ai-files.tsx @@ -0,0 +1,48 @@ +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..91b784a28 --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/ui/button.tsx @@ -0,0 +1,56 @@ +import { Slot } from "@radix-ui/react-slot"; +import { type VariantProps, cva } from "class-variance-authority"; +import * as React from "react"; + +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..bb368bd00 --- /dev/null +++ b/codegen-examples/examples/ai_impact_analysis/dashboard/frontend/components/ui/card.tsx @@ -0,0 +1,86 @@ +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..aa3d5f99a --- /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 ( +