From 945cc480bd9e8b906ea355bcea2ab6c6777d3290 Mon Sep 17 00:00:00 2001 From: surjeetkumar800 Date: Fri, 22 May 2026 14:08:08 +0530 Subject: [PATCH 1/4] feat: Add reset filters and logout/clear session actions to Tracker --- src/hooks/useGitHubAuth.ts | 9 ++++++ src/hooks/useGitHubData.ts | 9 ++++++ src/pages/Tracker/Tracker.tsx | 55 +++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+) diff --git a/src/hooks/useGitHubAuth.ts b/src/hooks/useGitHubAuth.ts index f99ccc22..fbf9f1da 100644 --- a/src/hooks/useGitHubAuth.ts +++ b/src/hooks/useGitHubAuth.ts @@ -25,6 +25,14 @@ export const useGitHubAuth = () => { } }; + const logout = () => { + setUsername(''); + setToken(''); + setError(''); + sessionStorage.removeItem('tracker_username'); + sessionStorage.removeItem('tracker_token'); + }; + return { username, setUsername, @@ -33,5 +41,6 @@ export const useGitHubAuth = () => { error, setError, getOctokit, + logout, }; }; diff --git a/src/hooks/useGitHubData.ts b/src/hooks/useGitHubData.ts index f4c78cf6..4ffdcacc 100644 --- a/src/hooks/useGitHubData.ts +++ b/src/hooks/useGitHubData.ts @@ -239,6 +239,14 @@ export const useGitHubData = ( [getOctokit, rateLimited] ); + const clearData = useCallback(() => { + setIssues([]); + setPrs([]); + setTotalIssues(0); + setTotalPrs(0); + setError(''); + }, []); + return { issues, prs, @@ -248,5 +256,6 @@ export const useGitHubData = ( error, rateLimited, fetchData, + clearData, }; }; diff --git a/src/pages/Tracker/Tracker.tsx b/src/pages/Tracker/Tracker.tsx index cf8788fb..7dd0c166 100644 --- a/src/pages/Tracker/Tracker.tsx +++ b/src/pages/Tracker/Tracker.tsx @@ -57,6 +57,7 @@ const Home: React.FC = () => { setToken, error: authError, getOctokit, + logout, } = useGitHubAuth(); const { @@ -67,6 +68,7 @@ const Home: React.FC = () => { loading, error: dataError, fetchData, + clearData, } = useGitHubData(getOctokit); const [tab, setTab] = useState(() => Number(localStorage.getItem('tracker_tab')) || 0); @@ -90,6 +92,32 @@ const Home: React.FC = () => { localStorage.setItem('tracker_endDate', endDate); }, [tab, page, issueFilter, prFilter, searchTitle, selectedRepo, startDate, endDate]); + const handleResetFilters = () => { + setTab(0); + setPage(0); + setIssueFilter("all"); + setPrFilter("all"); + setSearchTitle(""); + setSelectedRepo(""); + setStartDate(""); + setEndDate(""); + + localStorage.removeItem('tracker_tab'); + localStorage.removeItem('tracker_page'); + localStorage.removeItem('tracker_issueFilter'); + localStorage.removeItem('tracker_prFilter'); + localStorage.removeItem('tracker_searchTitle'); + localStorage.removeItem('tracker_selectedRepo'); + localStorage.removeItem('tracker_startDate'); + localStorage.removeItem('tracker_endDate'); + }; + + const handleLogout = () => { + logout(); + clearData(); + handleResetFilters(); + }; + // Fetch data when username, tab, or page changes useEffect(() => { if (username) { @@ -249,6 +277,21 @@ const Home: React.FC = () => { > Fetch Data + {(username || token) && ( + + )} @@ -283,6 +326,18 @@ const Home: React.FC = () => { InputLabelProps={{ shrink: true }} sx={{ minWidth: 150 }} /> + {(tab !== 0 || page !== 0 || issueFilter !== "all" || prFilter !== "all" || searchTitle !== "" || selectedRepo !== "" || startDate !== "" || endDate !== "") && ( + + )} {/* Tabs + State Filter */} From 34cbdedc5fbed463e04a85f53a8c338b73c4982b Mon Sep 17 00:00:00 2001 From: surjeetkumar800 Date: Fri, 22 May 2026 14:21:26 +0530 Subject: [PATCH 2/4] fix: address CodeRabbit review feedback on credentials persistence and clearData --- src/hooks/useGitHubAuth.ts | 12 ++++++++++-- src/hooks/useGitHubData.ts | 2 ++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/hooks/useGitHubAuth.ts b/src/hooks/useGitHubAuth.ts index fbf9f1da..e4e7e591 100644 --- a/src/hooks/useGitHubAuth.ts +++ b/src/hooks/useGitHubAuth.ts @@ -7,8 +7,16 @@ export const useGitHubAuth = () => { const [error, setError] = useState(''); useEffect(() => { - sessionStorage.setItem('tracker_username', username); - sessionStorage.setItem('tracker_token', token); + if (username) { + sessionStorage.setItem('tracker_username', username); + } else { + sessionStorage.removeItem('tracker_username'); + } + if (token) { + sessionStorage.setItem('tracker_token', token); + } else { + sessionStorage.removeItem('tracker_token'); + } }, [username, token]); const getOctokit = () => { diff --git a/src/hooks/useGitHubData.ts b/src/hooks/useGitHubData.ts index 4ffdcacc..dc6a1303 100644 --- a/src/hooks/useGitHubData.ts +++ b/src/hooks/useGitHubData.ts @@ -240,11 +240,13 @@ export const useGitHubData = ( ); const clearData = useCallback(() => { + lastRequestId.current++; setIssues([]); setPrs([]); setTotalIssues(0); setTotalPrs(0); setError(''); + setRateLimited(false); }, []); return { From 2947b03c912f58b1235456207e22bdafc5ce6111 Mon Sep 17 00:00:00 2001 From: surjeetkumar800 Date: Fri, 22 May 2026 14:30:08 +0530 Subject: [PATCH 3/4] chore: fix invalid package name in package.json to resolve npm errors --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 43ad31cc..5d166404 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "GitHub Tracker", + "name": "github-tracker", "private": true, "version": "0.0.0", "type": "module", From 17d6c79c0b70a2c9b8cb453876e03343dad9394f Mon Sep 17 00:00:00 2001 From: surjeetkumar800 Date: Sat, 23 May 2026 18:18:44 +0530 Subject: [PATCH 4/4] fix: address CodeRabbit review feedback and build/test failures --- src/components/Navbar.tsx | 2 +- src/components/__test__/Navbar.test.tsx | 21 ++++++++++----------- src/hooks/useGitHubAuth.ts | 23 +++++++++++++++++++---- src/hooks/useGitHubData.ts | 1 + 4 files changed, 31 insertions(+), 16 deletions(-) diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index fd5eac86..596a3244 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -1,7 +1,7 @@ import { NavLink, Link } from "react-router-dom"; import { useState, useContext } from "react"; import { ThemeContext } from "../context/ThemeContext"; -import { Moon, Sun, Menu, X, Github } from "lucide-react"; +import { Moon, Sun, Menu, X } from "lucide-react"; const Navbar: React.FC = () => { const [isOpen, setIsOpen] = useState(false); diff --git a/src/components/__test__/Navbar.test.tsx b/src/components/__test__/Navbar.test.tsx index 780b9bb3..60bc0712 100644 --- a/src/components/__test__/Navbar.test.tsx +++ b/src/components/__test__/Navbar.test.tsx @@ -1,6 +1,6 @@ // src/components/__tests__/Navbar.test.tsx import { render, screen, fireEvent } from '@testing-library/react' -import { describe, it, expect, vi, beforeEach } from 'vitest' +import { describe, it, expect, vi } from 'vitest' import { MemoryRouter } from 'react-router-dom' import { ThemeContext } from "../../context/ThemeContext"; import Navbar from '../Navbar.tsx' @@ -51,31 +51,30 @@ describe('Navbar', () => { // --- Mobile menu --- it('mobile menu is hidden by default', () => { renderNavbar() - expect(screen.queryByText('About')).not.toBeInTheDocument() + expect(screen.getAllByRole('link', { name: /^tracker$/i })).toHaveLength(1) }) it('opens mobile menu when hamburger is clicked', () => { renderNavbar() - const hamburger = screen.getAllByRole('button')[1] // second button = hamburger + const hamburger = screen.getAllByRole('button')[2] // third button = hamburger fireEvent.click(hamburger) - expect(screen.getByText('About')).toBeInTheDocument() - expect(screen.getByText('Contact')).toBeInTheDocument() + expect(screen.getAllByRole('link', { name: /^tracker$/i })).toHaveLength(2) }) it('closes mobile menu when a nav link is clicked', () => { renderNavbar() - const hamburger = screen.getAllByRole('button')[1] + const hamburger = screen.getAllByRole('button')[2] fireEvent.click(hamburger) // open + expect(screen.getAllByRole('link', { name: /^tracker$/i })).toHaveLength(2) const homeLinks = screen.getAllByRole('link', { name: /home/i }) fireEvent.click(homeLinks[homeLinks.length - 1]) // click the mobile one - expect(screen.queryByText('About')).not.toBeInTheDocument() // closed + expect(screen.getAllByRole('link', { name: /^tracker$/i })).toHaveLength(1) // closed }) - it('calls toggleTheme from the mobile menu button', () => { + it('calls toggleTheme from the mobile theme button', () => { const { toggleTheme } = renderNavbar('dark') - const hamburger = screen.getAllByRole('button')[1] - fireEvent.click(hamburger) - fireEvent.click(screen.getByText(/light/i)) + const mobileThemeBtn = screen.getAllByRole('button')[1] // second button = mobile theme toggle + fireEvent.click(mobileThemeBtn) expect(toggleTheme).toHaveBeenCalledTimes(1) }) diff --git a/src/hooks/useGitHubAuth.ts b/src/hooks/useGitHubAuth.ts index 30d4c0cc..3044fa65 100644 --- a/src/hooks/useGitHubAuth.ts +++ b/src/hooks/useGitHubAuth.ts @@ -1,9 +1,10 @@ -import { useState, useMemo } from 'react'; +import { useState, useEffect } from 'react'; import { Octokit } from '@octokit/core'; export const useGitHubAuth = () => { - const [username, setUsername] = useState(''); - const [token, setToken] = useState(''); + const [username, setUsername] = useState(() => sessionStorage.getItem('tracker_username') || ''); + const [token, setToken] = useState(() => sessionStorage.getItem('tracker_token') || ''); + const [error, setError] = useState(''); useEffect(() => { if (username) { @@ -18,7 +19,19 @@ export const useGitHubAuth = () => { } }, [username, token]); - const getOctokit = () => octokit; + const getOctokit = () => { + try { + setError(''); + if (!username) return null; + if (token) { + return new Octokit({ auth: token }); + } + return new Octokit(); + } catch (err: any) { + setError(err instanceof Error ? err.message : String(err)); + return null; + } + }; const logout = () => { setUsername(''); @@ -33,6 +46,8 @@ export const useGitHubAuth = () => { setUsername, token, setToken, + error, + setError, getOctokit, logout, }; diff --git a/src/hooks/useGitHubData.ts b/src/hooks/useGitHubData.ts index dc6a1303..2953e4e7 100644 --- a/src/hooks/useGitHubData.ts +++ b/src/hooks/useGitHubData.ts @@ -241,6 +241,7 @@ export const useGitHubData = ( const clearData = useCallback(() => { lastRequestId.current++; + setLoading(false); setIssues([]); setPrs([]); setTotalIssues(0);