diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index eb106ba..3901cb1 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -1,10 +1,9 @@ --- name: Bug Report about: Reporting bugs in existing code -title: '' +title: "" labels: bug, good first issue -assignees: '' - +assignees: "" --- ## The Problem diff --git a/.github/ISSUE_TEMPLATE/documentation.md b/.github/ISSUE_TEMPLATE/documentation.md index 2a85514..7e53c61 100644 --- a/.github/ISSUE_TEMPLATE/documentation.md +++ b/.github/ISSUE_TEMPLATE/documentation.md @@ -1,10 +1,9 @@ --- name: Documentation about: Report issues or additions to Documentation -title: '' +title: "" labels: documentation, good first issue -assignees: '' - +assignees: "" --- ## Description of what to add diff --git a/.github/ISSUE_TEMPLATE/feature-change-request.md b/.github/ISSUE_TEMPLATE/feature-change-request.md index 7cc49cc..dd91022 100644 --- a/.github/ISSUE_TEMPLATE/feature-change-request.md +++ b/.github/ISSUE_TEMPLATE/feature-change-request.md @@ -1,10 +1,9 @@ --- name: Feature-Change Request about: Suggest a change in an existing feature -title: '' +title: "" labels: change, good first issue -assignees: '' - +assignees: "" --- ## What needs to change diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md index 8864bec..4bc541e 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.md +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -1,10 +1,9 @@ --- name: Feature Request about: Suggest a new feature for this project -title: '' +title: "" labels: feature, good first issue -assignees: '' - +assignees: "" --- ## Description of the feature diff --git a/.github/ISSUE_TEMPLATE/refactoring.md b/.github/ISSUE_TEMPLATE/refactoring.md index 7fca9e2..6fec0d5 100644 --- a/.github/ISSUE_TEMPLATE/refactoring.md +++ b/.github/ISSUE_TEMPLATE/refactoring.md @@ -1,10 +1,9 @@ --- name: Refactoring Request about: Suggest refactoring or code quality improvements -title: '' +title: "" labels: refactoring, good first issue -assignees: '' - +assignees: "" --- ## Area to Refactor @@ -20,9 +19,7 @@ assignees: '' - describe the refactoring approach in bullet points - > [!NOTE] > **CONTRIBUTIONS ARE WELCOME!** > If you want to get this issue assigned to you, just comment `assign this issue to me`. > You will be assigned to the issue instantly via GitHub-actions bot. - diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..60a2768 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,7 @@ +.next +node_modules +out +dist +build +pnpm-lock.yaml +package-lock.json diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..de706b3 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,12 @@ +{ + "semi": true, + "trailingComma": "es5", + "singleQuote": false, + "printWidth": 80, + "tabWidth": 2, + "useTabs": false, + "bracketSpacing": true, + "arrowParens": "always", + "endOfLine": "lf", + "jsxSingleQuote": false +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7c454ed..6a4a3ff 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -37,16 +37,19 @@ Thank you for your interest in contributing to DevImpact! This guide will help y ### Installation 1. Install dependencies: + ```bash pnpm install ``` 2. Create a `.env` file in the project root (see `.env.example`): + ``` GITHUB_TOKEN=your_github_token_here ``` 3. Start the development server: + ```bash pnpm dev ``` @@ -79,6 +82,7 @@ DevImpact/ ## Making Changes 1. **Sync your fork** with the latest upstream changes: + ```bash git fetch upstream git checkout main @@ -86,6 +90,7 @@ DevImpact/ ``` 2. **Create a feature branch** from `main`: + ```bash git checkout -b feat/your-feature-name ``` @@ -93,11 +98,13 @@ DevImpact/ 3. **Make your changes** and test them locally. 4. **Run the linter** before committing: + ```bash pnpm lint ``` 5. **Commit your changes** with a clear message: + ```bash git commit -m "feat: add your feature description" ``` diff --git a/README.md b/README.md index 862aaff..9663d12 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # πŸš€ DevImpact + ![License](https://img.shields.io/github/license/o2sa/devimpact) ![Stars](https://img.shields.io/github/stars/o2sa/devimpact) ![Forks](https://img.shields.io/github/forks/o2sa/devimpact) @@ -11,14 +12,13 @@ ![Node.js](https://img.shields.io/badge/Node.js-green?logo=node.js) ![GraphQL](https://img.shields.io/badge/GraphQL-pink?logo=graphql) - **DevImpact** is an open-source platform that compares software developers based on their real impact in the open-source ecosystem β€” not just raw numbers. It evaluates developers using a smart scoring system that considers: -* Repository quality πŸ“¦ -* Pull request impact πŸ”€ -* Community contributions πŸ’¬ +- Repository quality πŸ“¦ +- Pull request impact πŸ”€ +- Community contributions πŸ’¬ --- @@ -28,9 +28,9 @@ Traditional metrics (followers, stars, commit counts) are often misleading. DevImpact focuses on: -* βœ… Quality over quantity -* βœ… Real contributions to valuable projects -* βœ… Fair comparison between developers +- βœ… Quality over quantity +- βœ… Real contributions to valuable projects +- βœ… Fair comparison between developers --- @@ -44,9 +44,9 @@ Measures the quality and impact of repositories owned by the user. Factors include: -* Stars ⭐ -* Forks 🍴 -* Watchers +- Stars ⭐ +- Forks 🍴 +- Watchers --- @@ -59,10 +59,10 @@ Measures contributions to **other developers' repositories**. Factors include: -* Target repository quality -* PR size (additions/deletions) -* Repository popularity -* Contribution diversity +- Target repository quality +- PR size (additions/deletions) +- Repository popularity +- Contribution diversity --- @@ -72,8 +72,8 @@ Measures community engagement. Includes: -* Issues opened in external repositories -* Discussions participation +- Issues opened in external repositories +- Discussions participation ⚠️ Does NOT include commits or PRs (to avoid duplication) @@ -96,21 +96,21 @@ Final Score = ## βš–οΈ Key Design Principles -* ❌ No self-inflation (own PRs excluded) -* πŸ“‰ Diminishing returns to prevent spam -* 🎯 External impact is prioritized -* βš–οΈ Balanced scoring between builders and contributors +- ❌ No self-inflation (own PRs excluded) +- πŸ“‰ Diminishing returns to prevent spam +- 🎯 External impact is prioritized +- βš–οΈ Balanced scoring between builders and contributors --- ## πŸ–₯️ Features -* πŸ” Compare two GitHub users side-by-side -* πŸ“Š Visual score breakdown (charts & insights) -* 🧠 Smart ranking system -* 🌍 Localization support (EN / AR) -* ⚑ Fast API powered by GitHub GraphQL -* 🧩 Extensible scoring system +- πŸ” Compare two GitHub users side-by-side +- πŸ“Š Visual score breakdown (charts & insights) +- 🧠 Smart ranking system +- 🌍 Localization support (EN / AR) +- ⚑ Fast API powered by GitHub GraphQL +- 🧩 Extensible scoring system --- @@ -118,22 +118,19 @@ Final Score = ### Frontend -* Next.js (App Router) -* TypeScript -* Tailwind CSS -* Recharts +- Next.js (App Router) +- TypeScript +- Tailwind CSS +- Recharts ### API -* Node.js + Express -* GitHub GraphQL API -* Octokit +- Node.js + Express +- GitHub GraphQL API +- Octokit --- - - - ## πŸš€ Getting Started ### 1. Clone the repo @@ -173,14 +170,13 @@ pnpm run dev ## 🌍 Localization -* Supported languages: English πŸ‡ΊπŸ‡Έ, Arabic πŸ‡ΈπŸ‡¦ -* Automatically detects user language -* Allows manual switching -* Easy to add new languages via `/locales` +- Supported languages: English πŸ‡ΊπŸ‡Έ, Arabic πŸ‡ΈπŸ‡¦ +- Automatically detects user language +- Allows manual switching +- Easy to add new languages via `/locales` --- - ## 🀝 Contributing Contributions are welcome! @@ -196,22 +192,21 @@ Contributions are welcome! ### Contribution ideas: -* Improve scoring algorithm -* Add new metrics -* Enhance UI/UX -* Add new languages 🌍 +- Improve scoring algorithm +- Add new metrics +- Enhance UI/UX +- Add new languages 🌍 --- ## ⚠️ Limitations -* GitHub API rate limits -* Some private contributions are not accessible -* Scoring system is heuristic (not perfect) +- GitHub API rate limits +- Some private contributions are not accessible +- Scoring system is heuristic (not perfect) --- - ## πŸ’‘ Inspiration DevImpact was created to answer a simple question: @@ -224,7 +219,6 @@ DevImpact was created to answer a simple question: If you like this project: -* ⭐ Star the repo -* πŸ› Report issues -* πŸ’‘ Suggest features - +- ⭐ Star the repo +- πŸ› Report issues +- πŸ’‘ Suggest features diff --git a/app/globals.css b/app/globals.css index cf67854..72a5415 100644 --- a/app/globals.css +++ b/app/globals.css @@ -12,10 +12,23 @@ body { @apply bg-slate-50 text-slate-900 antialiased; - font-family: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif; + font-family: + "Inter", + system-ui, + -apple-system, + "Segoe UI", + sans-serif; background-image: - radial-gradient(circle at 20% 20%, rgba(59, 130, 246, 0.08), transparent 35%), - radial-gradient(circle at 80% 0%, rgba(168, 85, 247, 0.08), transparent 30%), + radial-gradient( + circle at 20% 20%, + rgba(59, 130, 246, 0.08), + transparent 35% + ), + radial-gradient( + circle at 80% 0%, + rgba(168, 85, 247, 0.08), + transparent 30% + ), linear-gradient(180deg, #f8fbff 0%, #f2f5f9 40%, #f9fafb 100%); min-height: 100vh; } @@ -23,20 +36,28 @@ body { .dark body { @apply bg-slate-950 text-slate-100; background-image: - radial-gradient(circle at 20% 20%, rgba(59, 130, 246, 0.08), transparent 35%), - radial-gradient(circle at 80% 0%, rgba(124, 58, 237, 0.12), transparent 30%), + radial-gradient( + circle at 20% 20%, + rgba(59, 130, 246, 0.08), + transparent 35% + ), + radial-gradient( + circle at 80% 0%, + rgba(124, 58, 237, 0.12), + transparent 30% + ), linear-gradient(180deg, #0f172a 0%, #0b1221 40%, #0a0f1c 100%); } .card { @apply bg-white/90 shadow-card rounded-2xl border border-slate-100 backdrop-blur; - transition: transform 180ms ease, box-shadow 180ms ease; - box-shadow: 0 18px 48px rgba(15, 23, 42, 0.12); - + transition: + transform 180ms ease, + box-shadow 180ms ease; + box-shadow: 0 18px 48px rgba(15, 23, 42, 0.12); } - .dark .card { @apply bg-slate-900/80 border-slate-800; box-shadow: 0 18px 48px rgba(0, 0, 0, 0.45); -} \ No newline at end of file +} diff --git a/app/page.tsx b/app/page.tsx index da46309..fb006ea 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -74,7 +74,6 @@ export default function HomePage() { DevImpact -
@@ -111,7 +110,9 @@ export default function HomePage() { -

Enter two usernames to compare

+

+ Enter two usernames to compare +

Compare GitHub developer metrics side by side

@@ -120,7 +121,8 @@ export default function HomePage() {
diff --git a/app/providers.tsx b/app/providers.tsx index 9b467c1..3e864fb 100644 --- a/app/providers.tsx +++ b/app/providers.tsx @@ -3,9 +3,5 @@ import { TooltipProvider } from "@/components/ui/tooltip"; export default function Providers({ children }: { children: React.ReactNode }) { - return ( - - {children} - - ); + return {children}; } diff --git a/components/breakdown-bars.tsx b/components/breakdown-bars.tsx index db00b6b..f6a1497 100644 --- a/components/breakdown-bars.tsx +++ b/components/breakdown-bars.tsx @@ -1,8 +1,13 @@ import { UserResult } from "@/types/user-result"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "./ui/card"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "./ui/card"; import { Progress } from "./ui/progress"; - type Props = { user1: UserResult; user2: UserResult; @@ -15,45 +20,63 @@ const items = [ ]; export function BreakdownBars({ user1, user2 }: Props) { - const getMaxScore = (score1: number, score2: number) => Math.max(score1, score2, 1) - + const getMaxScore = (score1: number, score2: number) => + Math.max(score1, score2, 1); return ( - - - Detailed Breakdown - Progress bars showing relative performance - - - {["repoScore", "prScore", "contributionScore"].map((metric) => { - const user1Value = user1[metric as keyof UserResult] as number - const user2Value = user2[metric as keyof UserResult] as number - const maxVal = getMaxScore(user1Value, user2Value) - const metricLabel = metric === "repoScore" ? "Repository Score" : metric === "prScore" ? "Pull Request Score" : "Contribution Score" - return ( -
-
- {metricLabel} - - {user1.username}: {user1Value} | {user2.username}: {user2Value} - -
-
-
- {user1.username} - - {user1Value} -
-
- {user2.username} - - {user2Value} -
-
-
- ) - })} -
-
+ + + Detailed Breakdown + + Progress bars showing relative performance + + + + {["repoScore", "prScore", "contributionScore"].map((metric) => { + const user1Value = user1[metric as keyof UserResult] as number; + const user2Value = user2[metric as keyof UserResult] as number; + const maxVal = getMaxScore(user1Value, user2Value); + const metricLabel = + metric === "repoScore" + ? "Repository Score" + : metric === "prScore" + ? "Pull Request Score" + : "Contribution Score"; + return ( +
+
+ {metricLabel} + + {user1.username}: {user1Value} | {user2.username}:{" "} + {user2Value} + +
+
+
+ + {user1.username} + + + {user1Value} +
+
+ + {user2.username} + + + {user2Value} +
+
+
+ ); + })} +
+
); } diff --git a/components/compare-form.tsx b/components/compare-form.tsx index 36ed9ce..03be78b 100644 --- a/components/compare-form.tsx +++ b/components/compare-form.tsx @@ -76,7 +76,7 @@ export function CompareForm({ /> setUsername2(e.target.value)} /> diff --git a/components/comparison-chart.tsx b/components/comparison-chart.tsx index 93c0833..fc79a5c 100644 --- a/components/comparison-chart.tsx +++ b/components/comparison-chart.tsx @@ -30,7 +30,6 @@ const metrics = [ ]; export function ComparisonChart({ user1, user2 }: Props) { - const data = metrics.map((m) => ({ name: m.label, [user1.username]: user1[m.key as keyof UserResult] ?? 0, diff --git a/components/comparison-table.tsx b/components/comparison-table.tsx index ac14d5f..4470b74 100644 --- a/components/comparison-table.tsx +++ b/components/comparison-table.tsx @@ -19,16 +19,26 @@ type ComparisonTableProps = { }; export function ComparisonTable({ user1, user2 }: ComparisonTableProps) { - return (
{[user1, user2].map((user, idx) => ( - - + + {user.username} {user.isWinner && ( - Winner + + Winner + )} @@ -39,19 +49,25 @@ export function ComparisonTable({ user1, user2 }: ComparisonTableProps) {
Repo Score - (idx === 0 ? user2.repoScore : user1.repoScore) ? "text-primary" : ""}`}> + (idx === 0 ? user2.repoScore : user1.repoScore) ? "text-primary" : ""}`} + > {user.repoScore}
PR Score - (idx === 0 ? user2.prScore : user1.prScore) ? "text-primary" : ""}`}> + (idx === 0 ? user2.prScore : user1.prScore) ? "text-primary" : ""}`} + > {user.prScore}
Contribution Score - (idx === 0 ? user2.contributionScore : user1.contributionScore) ? "text-primary" : ""}`}> + (idx === 0 ? user2.contributionScore : user1.contributionScore) ? "text-primary" : ""}`} + > {user.contributionScore}
diff --git a/components/insights-list.tsx b/components/insights-list.tsx index b135b25..5d3265c 100644 --- a/components/insights-list.tsx +++ b/components/insights-list.tsx @@ -6,25 +6,24 @@ type Props = { }; export function InsightsList({ insights }: Props) { - return ( - - - - - Key Insights - - - - - - + + + + + Key Insights + + + + + + ); } diff --git a/components/result-dashboard.tsx b/components/result-dashboard.tsx index 7208d34..c07a587 100644 --- a/components/result-dashboard.tsx +++ b/components/result-dashboard.tsx @@ -28,7 +28,7 @@ export function ResultDashboard({ user1, user2 }: Props) { const loser = winner === user1 ? user2 : user1; const diffPct = winner ? Math.round( - ((winner.finalScore - loser.finalScore) / loser.finalScore) * 100, + ((winner.finalScore - loser.finalScore) / loser.finalScore) * 100 ) : 0; @@ -36,11 +36,11 @@ export function ResultDashboard({ user1, user2 }: Props) { const insights = []; if (user1.repoScore > user2.repoScore) { insights.push( - `${user1.username} has stronger repository portfolio with ${user1.repoScore} vs ${user2.repoScore}`, + `${user1.username} has stronger repository portfolio with ${user1.repoScore} vs ${user2.repoScore}` ); } else if (user2.repoScore > user1.repoScore) { insights.push( - `${user2.username} has stronger repository portfolio with ${user2.repoScore} vs ${user1.repoScore}`, + `${user2.username} has stronger repository portfolio with ${user2.repoScore} vs ${user1.repoScore}` ); } else { insights.push(`Both developers have equal repository strength`); @@ -48,11 +48,11 @@ export function ResultDashboard({ user1, user2 }: Props) { if (user1.prScore > user2.prScore) { insights.push( - `${user1.username} leads in pull request impact (${user1.prScore} vs ${user2.prScore})`, + `${user1.username} leads in pull request impact (${user1.prScore} vs ${user2.prScore})` ); } else if (user2.prScore > user1.prScore) { insights.push( - `${user2.username} leads in pull request impact (${user2.prScore} vs ${user1.prScore})`, + `${user2.username} leads in pull request impact (${user2.prScore} vs ${user1.prScore})` ); } else { insights.push(`Both developers have equal pull request impact`); @@ -123,9 +123,7 @@ export function ResultDashboard({ user1, user2 }: Props) { ) : ( <> -

- Metric -

+

Metric

It's a tie β€” both developers are evenly matched.

diff --git a/components/score-card.tsx b/components/score-card.tsx index d1ffd2c..b47c58c 100644 --- a/components/score-card.tsx +++ b/components/score-card.tsx @@ -7,7 +7,12 @@ type ScoreCardProps = { subtitle?: string; }; -export function ScoreCard({ title, value, highlight, subtitle }: ScoreCardProps) { +export function ScoreCard({ + title, + value, + highlight, + subtitle, +}: ScoreCardProps) { return (
{subtitle && ( - {subtitle} + + {subtitle} + )}
diff --git a/components/top-list.tsx b/components/top-list.tsx index d0740d9..ba91708 100644 --- a/components/top-list.tsx +++ b/components/top-list.tsx @@ -1,11 +1,4 @@ -import { - Eye, - GitFork, - GitPullRequest, - Minus, - Plus, - Star, -} from "lucide-react"; +import { Eye, GitFork, GitPullRequest, Minus, Plus, Star } from "lucide-react"; import { Card, CardContent, @@ -106,7 +99,7 @@ export function TopList({ userResults }: Props) { tooltip: `${repo.watchers} watchers`, }, ], - }), + }) )} {user.topRepos.length === 0 && (

@@ -133,7 +126,7 @@ export function TopList({ userResults }: Props) { label: pr.stars, tooltip: `${pr.stars} stars on the PR's repository`, }, - + { icon: , label: pr.additions || "0", @@ -145,7 +138,7 @@ export function TopList({ userResults }: Props) { tooltip: `-${pr.deletions || 0} deletions`, }, ], - }), + }) )} {user.topPullRequests.length === 0 && (

diff --git a/components/ui/alert.tsx b/components/ui/alert.tsx index f99164e..63c1ebd 100644 --- a/components/ui/alert.tsx +++ b/components/ui/alert.tsx @@ -1,7 +1,7 @@ -import * as React from "react" -import { cva, type VariantProps } from "class-variance-authority" +import * as React from "react"; +import { cva, type VariantProps } from "class-variance-authority"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; const alertVariants = cva( "relative grid w-full grid-cols-[0_1fr] items-start gap-y-0.5 rounded-lg border px-4 py-3 text-sm has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] has-[>svg]:gap-x-3 [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current", @@ -17,7 +17,7 @@ const alertVariants = cva( variant: "default", }, } -) +); function Alert({ className, @@ -31,7 +31,7 @@ function Alert({ className={cn(alertVariants({ variant }), className)} {...props} /> - ) + ); } function AlertTitle({ className, ...props }: React.ComponentProps<"div">) { @@ -44,7 +44,7 @@ function AlertTitle({ className, ...props }: React.ComponentProps<"div">) { )} {...props} /> - ) + ); } function AlertDescription({ @@ -60,7 +60,7 @@ function AlertDescription({ )} {...props} /> - ) + ); } -export { Alert, AlertTitle, AlertDescription } +export { Alert, AlertTitle, AlertDescription }; diff --git a/components/ui/card.tsx b/components/ui/card.tsx index a733720..403f2a7 100644 --- a/components/ui/card.tsx +++ b/components/ui/card.tsx @@ -1,6 +1,6 @@ -import * as React from "react" +import * as React from "react"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; function Card({ className, @@ -17,7 +17,7 @@ function Card({ )} {...props} /> - ) + ); } function CardHeader({ className, ...props }: React.ComponentProps<"div">) { @@ -30,7 +30,7 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) { )} {...props} /> - ) + ); } function CardTitle({ className, ...props }: React.ComponentProps<"div">) { @@ -43,7 +43,7 @@ function CardTitle({ className, ...props }: React.ComponentProps<"div">) { )} {...props} /> - ) + ); } function CardDescription({ className, ...props }: React.ComponentProps<"div">) { @@ -53,7 +53,7 @@ function CardDescription({ className, ...props }: React.ComponentProps<"div">) { className={cn("text-sm text-muted-foreground", className)} {...props} /> - ) + ); } function CardAction({ className, ...props }: React.ComponentProps<"div">) { @@ -66,7 +66,7 @@ function CardAction({ className, ...props }: React.ComponentProps<"div">) { )} {...props} /> - ) + ); } function CardContent({ className, ...props }: React.ComponentProps<"div">) { @@ -76,7 +76,7 @@ function CardContent({ className, ...props }: React.ComponentProps<"div">) { className={cn("px-4 group-data-[size=sm]/card:px-3", className)} {...props} /> - ) + ); } function CardFooter({ className, ...props }: React.ComponentProps<"div">) { @@ -89,7 +89,7 @@ function CardFooter({ className, ...props }: React.ComponentProps<"div">) { )} {...props} /> - ) + ); } export { @@ -100,4 +100,4 @@ export { CardAction, CardDescription, CardContent, -} +}; diff --git a/components/ui/progress.tsx b/components/ui/progress.tsx index 584011b..f4bec04 100644 --- a/components/ui/progress.tsx +++ b/components/ui/progress.tsx @@ -1,9 +1,9 @@ -"use client" +"use client"; -import * as React from "react" -import { Progress as ProgressPrimitive } from "radix-ui" +import * as React from "react"; +import { Progress as ProgressPrimitive } from "radix-ui"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; function Progress({ className, @@ -25,7 +25,7 @@ function Progress({ style={{ transform: `translateX(-${100 - (value || 0)}%)` }} /> - ) + ); } -export { Progress } +export { Progress }; diff --git a/components/ui/tooltip.tsx b/components/ui/tooltip.tsx index bb1ea52..733073f 100644 --- a/components/ui/tooltip.tsx +++ b/components/ui/tooltip.tsx @@ -1,9 +1,9 @@ -"use client" +"use client"; -import * as React from "react" -import { Tooltip as TooltipPrimitive } from "radix-ui" +import * as React from "react"; +import { Tooltip as TooltipPrimitive } from "radix-ui"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; function TooltipProvider({ delayDuration = 0, @@ -15,19 +15,19 @@ function TooltipProvider({ delayDuration={delayDuration} {...props} /> - ) + ); } function Tooltip({ ...props }: React.ComponentProps) { - return + return ; } function TooltipTrigger({ ...props }: React.ComponentProps) { - return + return ; } function TooltipContent({ @@ -51,7 +51,7 @@ function TooltipContent({ - ) + ); } -export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } +export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger }; diff --git a/eslint.config.js b/eslint.config.js index a766e71..7a55d24 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,6 +1,7 @@ import js from "@eslint/js"; import typescript from "@typescript-eslint/eslint-plugin"; import typescriptParser from "@typescript-eslint/parser"; +import prettierConfig from "eslint-config-prettier"; export default [ js.configs.recommended, @@ -11,8 +12,8 @@ export default [ "out/**", ".git/**", "dist/**", - "build/**" - ] + "build/**", + ], }, { files: ["**/*.{ts,tsx,js,jsx}"], @@ -22,8 +23,8 @@ export default [ ecmaVersion: 2021, sourceType: "module", ecmaFeatures: { - jsx: true - } + jsx: true, + }, }, globals: { React: "readonly", @@ -42,11 +43,11 @@ export default [ URLSearchParams: "readonly", fetch: "readonly", Request: "readonly", - Response: "readonly" - } + Response: "readonly", + }, }, plugins: { - "@typescript-eslint": typescript + "@typescript-eslint": typescript, }, rules: { ...typescript.configs.recommended.rules, @@ -54,12 +55,13 @@ export default [ "warn", { argsIgnorePattern: "^_", - varsIgnorePattern: "^_" - } + varsIgnorePattern: "^_", + }, ], "@typescript-eslint/no-explicit-any": "warn", "@typescript-eslint/explicit-module-boundary-types": "off", - "no-undef": "off" - } - } + "no-undef": "off", + }, + }, + prettierConfig, ]; diff --git a/lib/github.ts b/lib/github.ts index 4023aac..7c1199f 100644 --- a/lib/github.ts +++ b/lib/github.ts @@ -1,4 +1,9 @@ -import { ContributionTotals, GitHubUserData, PullRequestNode, RepoNode } from "@/types/github"; +import { + ContributionTotals, + GitHubUserData, + PullRequestNode, + RepoNode, +} from "@/types/github"; import { graphql } from "@octokit/graphql"; if (!process.env.GITHUB_TOKEN) { @@ -11,9 +16,12 @@ const client = graphql.defaults({ }, }); - const QUERY = /* GraphQL */ ` - query FetchUserData($login: String!, $repoCount: Int = 100, $prCount: Int = 100) { + query FetchUserData( + $login: String! + $repoCount: Int = 100 + $prCount: Int = 100 + ) { user(login: $login) { repositories( first: $repoCount diff --git a/lib/score.ts b/lib/score.ts index d62908d..a37a55e 100644 --- a/lib/score.ts +++ b/lib/score.ts @@ -7,16 +7,15 @@ import { PullRequestScoreDetail, RepoScoreDetail } from "@/types/score"; const LOG = Math.log; - - -function calculateRepoScore( - repos: RepoNode[] -): { total: number; details: RepoScoreDetail[] } { +function calculateRepoScore(repos: RepoNode[]): { + total: number; + details: RepoScoreDetail[]; +} { const details = repos.map((repo) => { - - const score = LOG(repo.stargazerCount + 1) * 5 + + const score = + LOG(repo.stargazerCount + 1) * 5 + LOG(repo.forkCount + 1) * 3 + - LOG(repo.watchers.totalCount + 1) * 2 + LOG(repo.watchers.totalCount + 1) * 2; return { repo, score: Math.round(score) }; }); @@ -94,8 +93,10 @@ export function calculateUserScore( const repoScore = calculateRepoScore(data.repos); const prScore = calculatePRScore(data.pullRequests, username); let contributionScore = calculateContributionScore(data.contributions); - contributionScore = Math.min(contributionScore, 0.3 * (repoScore.total + prScore.total)) - + contributionScore = Math.min( + contributionScore, + 0.3 * (repoScore.total + prScore.total) + ); const finalScore = repoScore.total * 0.4 + prScore.total * 0.4 + contributionScore * 0.2; diff --git a/package-lock.json b/package-lock.json index cae3cf9..75ae271 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,7 +30,9 @@ "autoprefixer": "^10.4.20", "eslint": "^9.17.0", "eslint-config-next": "^16.2.2", + "eslint-config-prettier": "^10.1.8", "postcss": "^8.4.47", + "prettier": "^3.8.2", "tailwindcss": "^3.4.14", "typescript": "^6.0.2" } @@ -4923,6 +4925,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, "node_modules/eslint-import-resolver-node": { "version": "0.3.10", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.10.tgz", @@ -7419,6 +7437,22 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.2.tgz", + "integrity": "sha512-8c3mgTe0ASwWAJK+78dpviD+A8EqhndQPUBpNUIPt6+xWlIigCwfN01lWr9MAede4uqXGTEKeQWTvzb3vjia0Q==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", diff --git a/package.json b/package.json index 35c83e4..568afbd 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,9 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "eslint . --ext .ts,.tsx --max-warnings=20" + "lint": "eslint . --ext .ts,.tsx --max-warnings=20", + "format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,css,md}\"", + "format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,css,md}\"" }, "dependencies": { "@octokit/graphql": "^9.0.3", @@ -31,7 +33,9 @@ "autoprefixer": "^10.4.20", "eslint": "^9.17.0", "eslint-config-next": "^16.2.2", + "eslint-config-prettier": "^10.1.8", "postcss": "^8.4.47", + "prettier": "^3.8.2", "tailwindcss": "^3.4.14", "typescript": "^6.0.2" }, diff --git a/tsconfig.json b/tsconfig.json index 93912b1..019858c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,7 @@ { "compilerOptions": { "target": "ES2020", - "lib": [ - "DOM", - "DOM.Iterable", - "ES2020" - ], + "lib": ["DOM", "DOM.Iterable", "ES2020"], "allowJs": false, "skipLibCheck": true, "strict": true, @@ -18,18 +14,14 @@ "isolatedModules": true, "jsx": "react-jsx", "incremental": true, - "types": [ - "@types/node" - ], + "types": ["@types/node"], "plugins": [ { "name": "next" } ], "paths": { - "@/*": [ - "./*" - ] + "@/*": ["./*"] } }, "include": [ @@ -39,7 +31,5 @@ ".next/types/**/*.ts", ".next/dev/types/**/*.ts" ], - "exclude": [ - "node_modules" - ], -} \ No newline at end of file + "exclude": ["node_modules"] +} diff --git a/types/github.ts b/types/github.ts index 0a7ff56..4c81a82 100644 --- a/types/github.ts +++ b/types/github.ts @@ -1,4 +1,3 @@ - export type RepoNode = { name: string; stargazerCount: number; @@ -27,4 +26,4 @@ export type GitHubUserData = { repos: RepoNode[]; pullRequests: PullRequestNode[]; contributions: ContributionTotals; -}; \ No newline at end of file +}; diff --git a/types/score.ts b/types/score.ts index a98a391..58e8b9c 100644 --- a/types/score.ts +++ b/types/score.ts @@ -8,4 +8,4 @@ export type RepoScoreDetail = { export type PullRequestScoreDetail = { pr: PullRequestNode; score: number; -}; \ No newline at end of file +};