feat(tracker): enhance UI improved header, filters, and dark-mode f…#335
feat(tracker): enhance UI improved header, filters, and dark-mode f…#335swati-204 wants to merge 1 commit into
Conversation
✅ Deploy Preview for github-spy ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
📝 WalkthroughWalkthroughThe Tracker page is redesigned with expanded Material UI support and modernized filtering. Filter state tracks issue/PR status, title search, repository substring, and date ranges. A new StatCard component displays stats, and the layout is restructured into a hero section, authentication card, alerts, tabs, filter controls, and an enhanced table with status indicators, tooltips, and interactive row navigation. ChangesTracker Page UI Redesign
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🎉 Thank you @swati-204 for your contribution. Please make sure your PR follows https://github.com/GitMetricsLab/github_tracker/blob/main/CONTRIBUTING.md#-pull-request-guidelines
There was a problem hiding this comment.
Pull request overview
This PR polishes the Tracker page UI in src/pages/Tracker/Tracker.tsx, introducing a new hero/header layout, improved auth card, richer filter bar/empty states, and enhanced table presentation (chips, badges, hover affordances).
Changes:
- Added a new header/overview panel with visibility progress, plus stat cards for open/closed/merged counts.
- Refined authentication and filter UI (helper link, clear actions, loading affordances, and empty states).
- Updated the table UI to be row-clickable (opens GitHub), with improved styling for title/repo/state/created cells.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (username) fetchData(username, page + 1, ROWS_PER_PAGE); | ||
| }, [tab, page]); | ||
|
|
||
| const handleSubmit = (e: React.FormEvent<HTMLFormElement>): void => { | ||
| e.preventDefault(); | ||
| setPage(0); | ||
| setHasFetched(true); | ||
| fetchData(username, 1, ROWS_PER_PAGE); |
| !!searchTitle || !!selectedRepo || !!startDate || !!endDate || | ||
| issueFilter !== "all" || prFilter !== "all"; |
| const visibleStats = useMemo(() => { | ||
| const open = currentFilteredData.filter((i) => i.state === "open").length; | ||
| const merged = currentFilteredData.filter((i) => !!i.pull_request?.merged_at).length; | ||
| const closed = currentFilteredData.filter((i) => i.state === "closed" && !i.pull_request?.merged_at).length; | ||
| return { open, merged, closed }; | ||
| }, [currentFilteredData]); |
| }, [tab, issueFilter, prFilter, searchTitle, selectedRepo, startDate, endDate]); | ||
|
|
||
| const activeFilterCount = activeFilterSummary.length; | ||
| const visibleShare = totalCount ? Math.round((currentFilteredData.length / totalCount) * 100) : 0; |
|
|
||
| const activeFilterCount = activeFilterSummary.length; | ||
| const visibleShare = totalCount ? Math.round((currentFilteredData.length / totalCount) * 100) : 0; | ||
| const progressValue = Math.max(8, visibleShare || 0); |
| <TextField | ||
| label="Personal Access Token" | ||
| value={token} | ||
| onChange={(e) => setToken(e.target.value)} | ||
| type="password" | ||
| sx={{ flex: 1, minWidth: 150 }} | ||
| // Helper link to guide users on generating a GitHub Personal Access Token | ||
| required | ||
| size="small" | ||
| sx={{ flex: 2, minWidth: 200, "& .MuiOutlinedInput-root": { borderRadius: "12px" } }} |
| sx={{ color: theme.palette.primary.main }} | ||
| href={item.html_url} | ||
| target="_blank" | ||
| rel="noopener noreferrer" |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/pages/Tracker/Tracker.tsx`:
- Around line 459-463: The empty-filter "No results match your filters." message
is shown even after a fetch/auth error; update the empty-state rendering logic
to suppress that message when authError or dataError exist. Locate the
conditional that currently checks hasFetched and emptiness of the result arrays
(e.g., hasFetched && filteredRecords.length === 0 or similar) and add a guard so
it only renders when !authError && !dataError. Make the same change for the
second occurrence referenced (the block around the 634-657 range) so both
empty-state branches require no errors before displaying the "No results..."
message.
- Around line 200-201: The progress bar's hard minimum forces a non-zero display
even when visibleShare is 0; change the logic so progressValue uses the minimum
only when visibleShare > 0. Keep visibleShare as computed (from
currentFilteredData.length and totalCount), then compute progressValue with a
conditional: if visibleShare > 0 use Math.max(8, visibleShare) otherwise 0;
update the variable progressValue (referenced in the Tracker component)
accordingly.
- Around line 701-713: The row-level onClick (which calls
window.open(item.html_url, ...)) is being triggered when the nested Link is
clicked; update the Link component (and any clickable children inside the title
cell) to stop event propagation so the row handler doesn't also run —
specifically add an onClick handler on the Link (and any inner interactive
element) that calls event.stopPropagation() (you may optionally call
event.preventDefault() only if you want to suppress Link's default navigation
and handle it manually); locate the Link in the title cell near
getStatusIcon(item) and add the stopPropagation logic there.
- Around line 167-168: The current date filtering in Tracker.tsx uses new
Date(startDate)/new Date(endDate) which treats endDate as midnight and excludes
items later that day; update the end-date comparison used by the filtered array
(the lines that currently filter on new Date(i.created_at) <= new Date(endDate))
to make the end day inclusive by computing an endOfDay for endDate (e.g., set
hours to 23:59:59.999 or advance by one day and compare with <) and use that
endOfDay in the filtered.filter call so items created anytime on the selected
"To" date are included.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: eeae03c6-88c4-4753-9916-9d80e44abe1d
📒 Files selected for processing (1)
src/pages/Tracker/Tracker.tsx
| if (startDate) filtered = filtered.filter((i) => new Date(i.created_at) >= new Date(startDate)); | ||
| if (endDate) filtered = filtered.filter((i) => new Date(i.created_at) <= new Date(endDate)); |
There was a problem hiding this comment.
Make the date range inclusive of the selected end day.
type="date" gives you a day value, but new Date(endDate) compares against midnight at the start of that day. Items created later on the selected "To" date will be excluded unexpectedly.
Suggested fix
- if (startDate) filtered = filtered.filter((i) => new Date(i.created_at) >= new Date(startDate));
- if (endDate) filtered = filtered.filter((i) => new Date(i.created_at) <= new Date(endDate));
+ if (startDate) filtered = filtered.filter((i) => i.created_at.slice(0, 10) >= startDate);
+ if (endDate) filtered = filtered.filter((i) => i.created_at.slice(0, 10) <= endDate);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (startDate) filtered = filtered.filter((i) => new Date(i.created_at) >= new Date(startDate)); | |
| if (endDate) filtered = filtered.filter((i) => new Date(i.created_at) <= new Date(endDate)); | |
| if (startDate) filtered = filtered.filter((i) => i.created_at.slice(0, 10) >= startDate); | |
| if (endDate) filtered = filtered.filter((i) => i.created_at.slice(0, 10) <= endDate); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/pages/Tracker/Tracker.tsx` around lines 167 - 168, The current date
filtering in Tracker.tsx uses new Date(startDate)/new Date(endDate) which treats
endDate as midnight and excludes items later that day; update the end-date
comparison used by the filtered array (the lines that currently filter on new
Date(i.created_at) <= new Date(endDate)) to make the end day inclusive by
computing an endOfDay for endDate (e.g., set hours to 23:59:59.999 or advance by
one day and compare with <) and use that endOfDay in the filtered.filter call so
items created anytime on the selected "To" date are included.
| const visibleShare = totalCount ? Math.round((currentFilteredData.length / totalCount) * 100) : 0; | ||
| const progressValue = Math.max(8, visibleShare || 0); |
There was a problem hiding this comment.
Keep the visibility bar at 0 when nothing is visible.
The hard floor of 8 means the UI can say 0% while the progress bar still shows progress. Only apply a minimum when visibleShare > 0.
Suggested fix
- const progressValue = Math.max(8, visibleShare || 0);
+ const progressValue = visibleShare > 0 ? Math.max(8, visibleShare) : 0;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const visibleShare = totalCount ? Math.round((currentFilteredData.length / totalCount) * 100) : 0; | |
| const progressValue = Math.max(8, visibleShare || 0); | |
| const visibleShare = totalCount ? Math.round((currentFilteredData.length / totalCount) * 100) : 0; | |
| const progressValue = visibleShare > 0 ? Math.max(8, visibleShare) : 0; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/pages/Tracker/Tracker.tsx` around lines 200 - 201, The progress bar's
hard minimum forces a non-zero display even when visibleShare is 0; change the
logic so progressValue uses the minimum only when visibleShare > 0. Keep
visibleShare as computed (from currentFilteredData.length and totalCount), then
compute progressValue with a conditional: if visibleShare > 0 use Math.max(8,
visibleShare) otherwise 0; update the variable progressValue (referenced in the
Tracker component) accordingly.
| {(authError || dataError) && ( | ||
| <Alert severity="error" sx={{ mb: 2.5, borderRadius: "10px" }}> | ||
| {authError || dataError} | ||
| </Alert> | ||
| )} |
There was a problem hiding this comment.
Don't show the filter-empty state after a fetch error.
After a failed auth/API request, the error alert renders correctly, but this branch still falls through to "No results match your filters." because hasFetched is already true and the arrays are empty. That message is misleading when nothing was loaded.
Also applies to: 634-657
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/pages/Tracker/Tracker.tsx` around lines 459 - 463, The empty-filter "No
results match your filters." message is shown even after a fetch/auth error;
update the empty-state rendering logic to suppress that message when authError
or dataError exist. Locate the conditional that currently checks hasFetched and
emptiness of the result arrays (e.g., hasFetched && filteredRecords.length === 0
or similar) and add a guard so it only renders when !authError && !dataError.
Make the same change for the second occurrence referenced (the block around the
634-657 range) so both empty-state branches require no errors before displaying
the "No results..." message.
| onClick={() => window.open(item.html_url, "_blank", "noopener,noreferrer")} | ||
| > | ||
| {/* Title */} | ||
| <TableCell sx={{ ...cellSx, maxWidth: 420 }}> | ||
| <Box sx={{ display: "flex", alignItems: "center", gap: 1 }}> | ||
| <Box sx={{ flexShrink: 0, display: "flex", alignItems: "center", color: "text.secondary" }}> | ||
| {getStatusIcon(item)} | ||
| </Box> | ||
| <Link | ||
| href={item.html_url} | ||
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| underline="hover" | ||
| sx={{ color: theme.palette.primary.main }} | ||
| href={item.html_url} | ||
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| underline="none" |
There was a problem hiding this comment.
Stop the title link from triggering the row click handler too.
Clicking the title currently fires both the nested <Link> navigation and the row-level window.open, which can open the same GitHub item twice.
Suggested fix
<Link
href={item.html_url}
target="_blank"
rel="noopener noreferrer"
+ onClick={(e) => e.stopPropagation()}
underline="none"
sx={{📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| onClick={() => window.open(item.html_url, "_blank", "noopener,noreferrer")} | |
| > | |
| {/* Title */} | |
| <TableCell sx={{ ...cellSx, maxWidth: 420 }}> | |
| <Box sx={{ display: "flex", alignItems: "center", gap: 1 }}> | |
| <Box sx={{ flexShrink: 0, display: "flex", alignItems: "center", color: "text.secondary" }}> | |
| {getStatusIcon(item)} | |
| </Box> | |
| <Link | |
| href={item.html_url} | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| underline="hover" | |
| sx={{ color: theme.palette.primary.main }} | |
| href={item.html_url} | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| underline="none" | |
| onClick={() => window.open(item.html_url, "_blank", "noopener,noreferrer")} | |
| > | |
| {/* Title */} | |
| <TableCell sx={{ ...cellSx, maxWidth: 420 }}> | |
| <Box sx={{ display: "flex", alignItems: "center", gap: 1 }}> | |
| <Box sx={{ flexShrink: 0, display: "flex", alignItems: "center", color: "text.secondary" }}> | |
| {getStatusIcon(item)} | |
| </Box> | |
| <Link | |
| href={item.html_url} | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| onClick={(e) => e.stopPropagation()} | |
| underline="none" |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/pages/Tracker/Tracker.tsx` around lines 701 - 713, The row-level onClick
(which calls window.open(item.html_url, ...)) is being triggered when the nested
Link is clicked; update the Link component (and any clickable children inside
the title cell) to stop event propagation so the row handler doesn't also run —
specifically add an onClick handler on the Link (and any inner interactive
element) that calls event.stopPropagation() (you may optionally call
event.preventDefault() only if you want to suppress Link's default navigation
and handle it manually); locate the Link in the title cell near
getStatusIcon(item) and add the stopPropagation logic there.
Related Issue
Description
Polished the Tracker UI: hero/header with gradient, descriptive copy, and an overview panel showing loaded vs visible items.
Added a visibility progress bar and active-filter summary to help users understand filter impact.
Improved authentication card: helper link to GitHub PAT docs, rounded inputs, clearer fetch button state.
Refined filter bar and empty states clearer messaging and a prominent Clear action.
Table improvements: smoother hover affordance, row click opens item in GitHub, rounded repo chips, and clearer state badges.
Resolved upstream merge conflicts and fixed duplicate/garbled imports.
All functional logic (hooks, data fetching) remains unchanged; only src/pages/Tracker/Tracker.tsx was modified.
How Has This Been Tested?
Started dev server (npm run dev) and confirmed Vite serves the app without parse/build errors.
Visually inspected the Tracker page in both light and dark themes to confirm contrast and layout.
Exercised filters (search, repo, date range), tab switching (Issues / Pull Requests), and token helper link.
Verified no TypeScript/JSX diagnostics for the touched file.
Screenshots (if applicable)
Type of Change
Summary by CodeRabbit