From 994b691239cec11eefb24cc4fa262f633fa77cd3 Mon Sep 17 00:00:00 2001 From: Osama Mabkhot <99215291+O2sa@users.noreply.github.com> Date: Thu, 13 Aug 2026 03:27:16 +0300 Subject: [PATCH 1/6] fix: prevent theme flash on initial load --- app/layout.tsx | 10 ++++++---- components/theme-provider.tsx | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/layout.tsx b/app/layout.tsx index cf1565a..5865918 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -2,7 +2,6 @@ import "./globals.css"; import type { ReactNode } from "react"; import type { Metadata } from "next"; import { cookies, headers } from "next/headers"; -import Script from "next/script"; import { DEFAULT_LOCALE, LOCALE_COOKIE, @@ -112,10 +111,13 @@ export default async function RootLayout({ children }: { children: ReactNode }) return ( + + {children} diff --git a/components/theme-provider.tsx b/components/theme-provider.tsx index 75a06f3..cadf070 100644 --- a/components/theme-provider.tsx +++ b/components/theme-provider.tsx @@ -9,6 +9,7 @@ export function ThemeProvider({ children }: { children: ReactNode }) { attribute="class" defaultTheme="system" enableSystem + enableColorScheme storageKey="devimpact-theme" > {children} From 8b05b65a72d800af6a8b6890bb67d4728e5dc033 Mon Sep 17 00:00:00 2001 From: Osama Mabkhot <99215291+O2sa@users.noreply.github.com> Date: Thu, 13 Aug 2026 23:26:36 +0300 Subject: [PATCH 2/6] fix: Prevent home page skeleton from covering the whole page --- app/page.tsx | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index 7c58084..c3a57fe 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,7 +1,9 @@ import type { Metadata } from "next"; import { Suspense } from "react"; -import { DashboardSkeleton } from "@/components/skeletons"; +import { AppFooter } from "@/components/app-footer"; +import { AppHeader } from "@/components/app-header"; import { HomePageClient } from "@/components/home-page-client"; +import { Skeleton } from "@/components/ui/skeleton"; import { JsonLd } from "@/components/seo/json-ld"; import { toAbsoluteUrl } from "@/lib/seo"; @@ -63,11 +65,65 @@ const softwareSchema = { }, }; +function HomePageFallback() { + return ( +
+ + +
+
+
+ + + +
+ +
+
+
+ + +
+
+ + +
+
+ +
+ +
+ {Array.from({ length: 8 }).map((_, index) => ( + + ))} +
+
+ +
+ + + +
+
+
+ +
+ + + +
+
+ + +
+ ); +} + export default function HomePage() { return ( <> - }> + }> From cd0b9927b44f03633a510ad56a4cddf08b37c52d Mon Sep 17 00:00:00 2001 From: Osama Mabkhot <99215291+O2sa@users.noreply.github.com> Date: Thu, 13 Aug 2026 23:41:03 +0300 Subject: [PATCH 3/6] chore: remove next-env.d.ts from the repository --- .gitignore | 1 + next-env.d.ts | 6 ------ 2 files changed, 1 insertion(+), 6 deletions(-) delete mode 100644 next-env.d.ts diff --git a/.gitignore b/.gitignore index 93bdc13..ef5d71d 100644 --- a/.gitignore +++ b/.gitignore @@ -81,6 +81,7 @@ out # Nuxt.js build / generate output .nuxt dist +next-env.d.ts # Gatsby files .cache/ diff --git a/next-env.d.ts b/next-env.d.ts deleted file mode 100644 index c4b7818..0000000 --- a/next-env.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/// -/// -import "./.next/dev/types/routes.d.ts"; - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. From 7b855fcac52fa79e6ae6d42e041996ee103ea05a Mon Sep 17 00:00:00 2001 From: Osama Mabkhot <99215291+O2sa@users.noreply.github.com> Date: Fri, 14 Aug 2026 00:29:04 +0300 Subject: [PATCH 4/6] style: update top right gradient color to match the app identity Co-authored-by: Copilot --- app/globals.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/globals.css b/app/globals.css index 65ff396..794cfbc 100644 --- a/app/globals.css +++ b/app/globals.css @@ -74,16 +74,16 @@ body::after { body::before { 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%, hsl(var(--primary) / 0.08), transparent 35%), + radial-gradient(circle at 80% 0%, hsl(var(--accent) / 0.08), transparent 30%), linear-gradient(180deg, #f8fbff 0%, #f2f5f9 40%, #f9fafb 100%); opacity: 1; } body::after { 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%, hsl(var(--primary) / 0.08), transparent 35%), + radial-gradient(circle at 80% 0%, hsl(var(--accent) / 0.12), transparent 30%), linear-gradient(180deg, #0f172a 0%, #0b1221 40%, #0a0f1c 100%); opacity: 0; } From d578ce921c53a65d2fd1ce0384fb15d8d91d9ee9 Mon Sep 17 00:00:00 2001 From: Osama Mabkhot <99215291+O2sa@users.noreply.github.com> Date: Fri, 14 Aug 2026 01:54:34 +0300 Subject: [PATCH 5/6] feat: add duplicate fetch prevention and same user error handling in comparison form Co-authored-by: Copilot --- components/compare-form.tsx | 18 ++++++++++++++++-- components/home-page-client.tsx | 14 ++++++++++++++ locales/ar.json | 1 + locales/en.json | 1 + 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/components/compare-form.tsx b/components/compare-form.tsx index 60ee6f9..4d854e6 100644 --- a/components/compare-form.tsx +++ b/components/compare-form.tsx @@ -47,6 +47,7 @@ type CompareFormProps = { swapUsers?: () => void; username1Error?: string | null; username2Error?: string | null; + disableDuplicateFetch?: boolean; }; export function CompareForm({ @@ -63,6 +64,7 @@ export function CompareForm({ reset, username1Error, username2Error, + disableDuplicateFetch, }: CompareFormProps) { const { t } = useTranslation(); const firstInputRef = useRef(null); @@ -71,7 +73,12 @@ export function CompareForm({ firstInputRef.current?.focus(); }, []); - const canSubmit = Boolean(username1.trim() && username2.trim() && !loading); + const normalized1 = username1.trim().toLowerCase(); + const normalized2 = username2.trim().toLowerCase(); + const sameUsername = Boolean(normalized1 && normalized2 && normalized1 === normalized2); + const canSubmit = Boolean( + username1.trim() && username2.trim() && !loading && !sameUsername && !disableDuplicateFetch, + ); const isEmpty = !username1.trim() && !username2.trim() && !hasData; const hasLanguageSelection = selectedLanguages.length > 0; @@ -238,7 +245,13 @@ export function CompareForm({ -
+
+ {sameUsername ? ( +

{t("error.sameUser")}

+ ) : null} + + +
+
diff --git a/components/home-page-client.tsx b/components/home-page-client.tsx index 59c5917..479ae0c 100644 --- a/components/home-page-client.tsx +++ b/components/home-page-client.tsx @@ -228,6 +228,11 @@ export function HomePageClient() { return inFlightPromiseRef.current; } + // If we've already fetched this exact comparison and have the data, skip. + if (lastFetchedKeyRef.current === fetchKey && data) { + return Promise.resolve(); + } + lastFetchedKeyRef.current = fetchKey; const requestPromise = (async () => { @@ -383,6 +388,14 @@ export function HomePageClient() { const isRefreshing = loading && Boolean(displayData); const isExiting = !loading && !data && Boolean(displayData); + const currentFetchKey = createFetchKey(username1.trim(), username2.trim(), { + selectedLanguages, + }); + + const disableDuplicateFetch = Boolean( + lastFetchedKeyRef.current === currentFetchKey && (data || inFlightFetchKeyRef.current === currentFetchKey), + ); + const handleUsername1Change = (value: string) => { setUsername1(value); if (usernameErrors.username1) { @@ -441,6 +454,7 @@ export function HomePageClient() { reset={reset} swapUsers={swapUsers} hasData={Boolean(data)} + disableDuplicateFetch={disableDuplicateFetch} username1Error={usernameErrors.username1} username2Error={usernameErrors.username2} /> diff --git a/locales/ar.json b/locales/ar.json index 9431ed5..c1182eb 100644 --- a/locales/ar.json +++ b/locales/ar.json @@ -47,6 +47,7 @@ "error.resourceLimit": "تم الوصول إلى حدود موارد GitHub API لهذا الطلب. حاول مرة أخرى بعد قليل.", "error.missingUsername": "أدخل اسمين للمقارنة.", "error.userNotFound": "لم يتم العثور على مستخدم GitHub", + "error.sameUser": "مطلوب اسمان مختلفان لمستخدمي GitHub.", "explanations.contribution": "درجة المساهمات", "explanations.line.contribution.1": "تعتمد درجة المساهمات على المشكلات والنقاشات الخارجية فقط.", "explanations.line.contribution.2": "لا يتم احتساب الـ commits وطلبات السحب لتجنب العد المزدوج.", diff --git a/locales/en.json b/locales/en.json index 4a07471..af52421 100644 --- a/locales/en.json +++ b/locales/en.json @@ -47,6 +47,7 @@ "error.resourceLimit": "GitHub API resource limits were reached for this request. Please retry shortly.", "error.missingUsername": "Provide two usernames to compare.", "error.userNotFound": "GitHub user not found", + "error.sameUser": "Two different GitHub usernames are required.", "explanations.contribution": "Contribution Score", "explanations.line.contribution.1": "Contribution score is based on external issues and discussions only.", "explanations.line.contribution.2": "Commits and pull requests are excluded to avoid double-counting.", From fe81846af0dfc459b6892eb9fbe98df24ee3bc04 Mon Sep 17 00:00:00 2001 From: Osama Mabkhot <99215291+O2sa@users.noreply.github.com> Date: Fri, 14 Aug 2026 02:16:07 +0300 Subject: [PATCH 6/6] style: enhance DashboardSkeleton layout and update Skeleton component styles Co-authored-by: Copilot --- components/skeletons.tsx | 81 +++++++++++++++++++++++++++++++++----- components/ui/skeleton.tsx | 5 ++- 2 files changed, 75 insertions(+), 11 deletions(-) diff --git a/components/skeletons.tsx b/components/skeletons.tsx index d6e0e7c..d33ad28 100644 --- a/components/skeletons.tsx +++ b/components/skeletons.tsx @@ -1,19 +1,80 @@ import { Skeleton } from "@/components/ui/skeleton"; +import { Card, CardContent, CardHeader, CardTitle } from "./ui/card"; export function DashboardSkeleton() { return ( -
- -
- - - - +
+ + +
+
+
+ +
+
+ + +
+
+
+ + +
+
+
+
+ +
+ + + + + + + +
+ + + + +
+
+
+ + + + + + + + + + +
- +
- - + + + + + + + + + + + + + + + + + + + + +
); diff --git a/components/ui/skeleton.tsx b/components/ui/skeleton.tsx index 0118624..8fc953e 100644 --- a/components/ui/skeleton.tsx +++ b/components/ui/skeleton.tsx @@ -4,7 +4,10 @@ function Skeleton({ className, ...props }: React.ComponentProps<"div">) { return (
)