From 3c972c0b23b4850d8f7e1ee3b589a73128e0e865 Mon Sep 17 00:00:00 2001 From: Mallya Date: Tue, 19 May 2026 22:40:08 +0530 Subject: [PATCH 1/5] fix: resolve onboarding PR conflicts and cleanup --- src/components/OnboardingTour.tsx | 89 +++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 src/components/OnboardingTour.tsx diff --git a/src/components/OnboardingTour.tsx b/src/components/OnboardingTour.tsx new file mode 100644 index 000000000..f769b967d --- /dev/null +++ b/src/components/OnboardingTour.tsx @@ -0,0 +1,89 @@ +"use client"; + +import { useEffect, useCallback } from "react"; +import { driver } from "driver.js"; + +interface OnboardingTourProps { + onComplete: () => void; +} + +// steps match the actual widget ids on the dashboard +const TOUR_STEPS = [ + { + element: "#widget-contribution-graph", + popover: { + title: "Contribution Graph", + description: "See your daily GitHub commit activity. Switch between 7, 14, 30, and 90 day views.", + }, + }, + { + element: "#widget-streak", + popover: { + title: "Streak Tracker", + description: "Your current commit streak — how many days in a row you've pushed code.", + }, + }, + { + element: "#widget-pr-metrics", + popover: { + title: "PR Analytics", + description: "Average review time, merge rate, and open vs closed pull request counts.", + }, + }, + { + element: "#widget-top-repos", + popover: { + title: "Top Repositories", + description: "Your most active repos ranked by commits. Click column headers to sort.", + }, + }, + { + element: "#widget-goals", + popover: { + title: "Weekly Goals", + description: "Set coding targets and track your progress automatically.", + }, + }, +]; + +export default function OnboardingTour({ onComplete }: OnboardingTourProps) { + const startTour = useCallback(() => { + const driverObj = driver({ + showProgress: true, + animate: true, + allowClose: true, + steps: TOUR_STEPS, + onDestroyStarted: () => { + // mark tour as seen whether user finishes or skips + onComplete(); + driverObj.destroy(); + }, + }); + + driverObj.drive(); + }, [onComplete]); + + // auto-start on mount + // inject driver.js styles once on mount + useEffect(() => { + const linkId = "driver-js-css"; + if (document.getElementById(linkId)) return; + const link = document.createElement("link"); + link.id = linkId; + link.rel = "stylesheet"; + link.href = "https://cdn.jsdelivr.net/npm/driver.js@1.3.1/dist/driver.css"; + document.head.appendChild(link); + return () => { + document.getElementById(linkId)?.remove(); + }; + }, []); + + // auto-start on mount + useEffect(() => { + // small delay so dashboard widgets have time to render first + const timer = setTimeout(startTour, 800); + return () => clearTimeout(timer); + }, [startTour]); + + return null; +} \ No newline at end of file From abd36c95f1ff01aaf2ca445b0dc545be80222d2f Mon Sep 17 00:00:00 2001 From: Mallya Date: Thu, 21 May 2026 01:23:13 +0530 Subject: [PATCH 2/5] fix: static CSS import, indentation fix, migration file, EOF newlines --- src/components/OnboardingTour.tsx | 17 ++--------------- .../20260520000000_add_seen_onboarding.sql | 1 + 2 files changed, 3 insertions(+), 15 deletions(-) create mode 100644 supabase/migrations/20260520000000_add_seen_onboarding.sql diff --git a/src/components/OnboardingTour.tsx b/src/components/OnboardingTour.tsx index f769b967d..c6563b439 100644 --- a/src/components/OnboardingTour.tsx +++ b/src/components/OnboardingTour.tsx @@ -2,6 +2,7 @@ import { useEffect, useCallback } from "react"; import { driver } from "driver.js"; +import "driver.js/dist/driver.css"; interface OnboardingTourProps { onComplete: () => void; @@ -63,20 +64,6 @@ export default function OnboardingTour({ onComplete }: OnboardingTourProps) { driverObj.drive(); }, [onComplete]); - // auto-start on mount - // inject driver.js styles once on mount - useEffect(() => { - const linkId = "driver-js-css"; - if (document.getElementById(linkId)) return; - const link = document.createElement("link"); - link.id = linkId; - link.rel = "stylesheet"; - link.href = "https://cdn.jsdelivr.net/npm/driver.js@1.3.1/dist/driver.css"; - document.head.appendChild(link); - return () => { - document.getElementById(linkId)?.remove(); - }; - }, []); // auto-start on mount useEffect(() => { @@ -86,4 +73,4 @@ export default function OnboardingTour({ onComplete }: OnboardingTourProps) { }, [startTour]); return null; -} \ No newline at end of file +} diff --git a/supabase/migrations/20260520000000_add_seen_onboarding.sql b/supabase/migrations/20260520000000_add_seen_onboarding.sql new file mode 100644 index 000000000..0ccb48cd3 --- /dev/null +++ b/supabase/migrations/20260520000000_add_seen_onboarding.sql @@ -0,0 +1 @@ +ALTER TABLE users ADD COLUMN IF NOT EXISTS seen_onboarding BOOLEAN DEFAULT FALSE; From 695ee4aba6ee8d2493fbcaa97b90a1d9ac9c245c Mon Sep 17 00:00:00 2001 From: Mallya Date: Fri, 22 May 2026 00:41:02 +0530 Subject: [PATCH 3/5] fix: skip onboarding tour in E2E/webdriver environments --- src/components/OnboardingTour.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/OnboardingTour.tsx b/src/components/OnboardingTour.tsx index c6563b439..25e29f3d0 100644 --- a/src/components/OnboardingTour.tsx +++ b/src/components/OnboardingTour.tsx @@ -67,10 +67,11 @@ export default function OnboardingTour({ onComplete }: OnboardingTourProps) { // auto-start on mount useEffect(() => { - // small delay so dashboard widgets have time to render first - const timer = setTimeout(startTour, 800); - return () => clearTimeout(timer); - }, [startTour]); + // skip tour in test environments to avoid blocking E2E tests + if (typeof window !== "undefined" && window.navigator.webdriver) return; + const timer = setTimeout(startTour, 800); + return () => clearTimeout(timer); +}, [startTour]); return null; } From bc0a80116c23f4cac85c9bb72d1b3c91f7ee780c Mon Sep 17 00:00:00 2001 From: Mallya Date: Sat, 23 May 2026 01:35:34 +0530 Subject: [PATCH 4/5] =?UTF-8?q?fix:=20remove=20onComplete=20prop=20?= =?UTF-8?q?=E2=80=94=20OnboardingTour=20handles=20completion=20internally?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/OnboardingTour.tsx | 35 +++++++++++++++++-------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/components/OnboardingTour.tsx b/src/components/OnboardingTour.tsx index 25e29f3d0..f4b8701b0 100644 --- a/src/components/OnboardingTour.tsx +++ b/src/components/OnboardingTour.tsx @@ -4,11 +4,6 @@ import { useEffect, useCallback } from "react"; import { driver } from "driver.js"; import "driver.js/dist/driver.css"; -interface OnboardingTourProps { - onComplete: () => void; -} - -// steps match the actual widget ids on the dashboard const TOUR_STEPS = [ { element: "#widget-contribution-graph", @@ -47,7 +42,19 @@ const TOUR_STEPS = [ }, ]; -export default function OnboardingTour({ onComplete }: OnboardingTourProps) { +async function markTourSeen() { + try { + await fetch("/api/user/settings", { + method: "PATCH", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ seen_onboarding: true }), + }); + } catch { + // silent fail — not critical + } +} + +export default function OnboardingTour() { const startTour = useCallback(() => { const driverObj = driver({ showProgress: true, @@ -55,23 +62,19 @@ export default function OnboardingTour({ onComplete }: OnboardingTourProps) { allowClose: true, steps: TOUR_STEPS, onDestroyStarted: () => { - // mark tour as seen whether user finishes or skips - onComplete(); + markTourSeen(); driverObj.destroy(); }, }); driverObj.drive(); - }, [onComplete]); - + }, []); - // auto-start on mount useEffect(() => { - // skip tour in test environments to avoid blocking E2E tests - if (typeof window !== "undefined" && window.navigator.webdriver) return; - const timer = setTimeout(startTour, 800); - return () => clearTimeout(timer); -}, [startTour]); + if (typeof window !== "undefined" && window.navigator.webdriver) return; + const timer = setTimeout(startTour, 800); + return () => clearTimeout(timer); + }, [startTour]); return null; } From 227ede4586b979f8a75779a7a46d1f86a12ecca1 Mon Sep 17 00:00:00 2001 From: Mallya Date: Mon, 8 Jun 2026 16:25:40 +0530 Subject: [PATCH 5/5] fix: resolve conflicts, keep target validation guard (#929) --- package-lock.json | 7 +++++++ package.json | 1 + src/components/GoalTracker.tsx | 5 +++++ 3 files changed, 13 insertions(+) diff --git a/package-lock.json b/package-lock.json index 5c52af4b5..7796c3bc0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "clsx": "^2.1.1", "date-fns": "^3.6.0", "dompurify": "^3.1.6", + "driver.js": "^1.4.0", "fflate": "^0.8.3", "html-to-image": "^1.11.13", "idb-keyval": "^6.2.4", @@ -9759,6 +9760,12 @@ "node": ">=4" } }, + "node_modules/driver.js": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/driver.js/-/driver.js-1.4.0.tgz", + "integrity": "sha512-Gm64jm6PmcU+si21sQhBrTAM1JvUrR0QhNmjkprNLxohOBzul9+pNHXgQaT9lW84gwg9GMLB3NZGuGolsz5uew==", + "license": "MIT" + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", diff --git a/package.json b/package.json index e4d89fcd6..5d544411c 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "clsx": "^2.1.1", "date-fns": "^3.6.0", "dompurify": "^3.1.6", + "driver.js": "^1.4.0", "fflate": "^0.8.3", "html-to-image": "^1.11.13", "idb-keyval": "^6.2.4", diff --git a/src/components/GoalTracker.tsx b/src/components/GoalTracker.tsx index 166266fc0..1c9860734 100644 --- a/src/components/GoalTracker.tsx +++ b/src/components/GoalTracker.tsx @@ -140,6 +140,11 @@ export function useGoalTracker() { if (e) e.preventDefault(); setCreating(true); setCreateError(null); + if (target <= 0) { + setCreateError("Target must be greater than 0."); + setCreating(false); + return; + } try { const result = await submitGoalWithRefresh({