diff --git a/src/pages/Tracker/Tracker.tsx b/src/pages/Tracker/Tracker.tsx
index ce4116f..7436325 100644
--- a/src/pages/Tracker/Tracker.tsx
+++ b/src/pages/Tracker/Tracker.tsx
@@ -1,4 +1,4 @@
-import React, { useState, useEffect } from "react"
+import React, { useState, useEffect, useMemo } from "react"
import {
IssueOpenedIcon,
IssueClosedIcon,
@@ -20,6 +20,7 @@ import {
TableRow,
TablePagination,
Link,
+ CircularProgress,
Alert,
Tabs,
Tab,
@@ -27,9 +28,21 @@ import {
MenuItem,
FormControl,
InputLabel,
- Skeleton,
Typography,
+ Chip,
+ Divider,
+ InputAdornment,
+ Tooltip,
+ IconButton,
+ Skeleton,
+ Stack,
+ LinearProgress,
} from "@mui/material";
+import SearchIcon from "@mui/icons-material/Search";
+import FilterListIcon from "@mui/icons-material/FilterList";
+import ClearIcon from "@mui/icons-material/Clear";
+import GitHubIcon from "@mui/icons-material/GitHub";
+import CallMadeIcon from "@mui/icons-material/CallMade";
import { useTheme } from "@mui/material/styles";
import { useGitHubAuth } from "../../hooks/useGitHubAuth";
import { useGitHubData } from "../../hooks/useGitHubData";
@@ -46,9 +59,39 @@ interface GitHubItem {
html_url: string;
}
-const Home: React.FC = () => {
+// ── Stat Card ────────────────────────────────────────────────────────────────
+const StatCard: React.FC<{
+ label: string;
+ value: number;
+ color: string;
+ bg: string;
+}> = ({ label, value, color, bg }) => (
+
+
+ {label}
+
+
+ {value}
+
+
+);
+// ── Main Component ────────────────────────────────────────────────────────────
+const Home: React.FC = () => {
const theme = useTheme();
+ const isDark = theme.palette.mode === "dark";
const {
username,
@@ -71,337 +114,652 @@ const Home: React.FC = () => {
const [tab, setTab] = useState(0);
const [page, setPage] = useState(0);
-
const [issueFilter, setIssueFilter] = useState("all");
const [prFilter, setPrFilter] = useState("all");
const [searchTitle, setSearchTitle] = useState("");
const [selectedRepo, setSelectedRepo] = useState("");
const [startDate, setStartDate] = useState("");
const [endDate, setEndDate] = useState("");
+ const [hasFetched, setHasFetched] = useState(false);
- // Fetch data when username, tab, or page changes
useEffect(() => {
- if (username) {
- fetchData(username, page + 1, ROWS_PER_PAGE);
- }
+ if (username) fetchData(username, page + 1, ROWS_PER_PAGE);
}, [tab, page]);
const handleSubmit = (e: React.FormEvent): void => {
e.preventDefault();
setPage(0);
+ setHasFetched(true);
fetchData(username, 1, ROWS_PER_PAGE);
};
- const handlePageChange = (_: unknown, newPage: number) => {
- setPage(newPage);
+ const handleClearFilters = () => {
+ setSearchTitle("");
+ setSelectedRepo("");
+ setStartDate("");
+ setEndDate("");
+ setIssueFilter("all");
+ setPrFilter("all");
};
+ const hasActiveFilters =
+ !!searchTitle || !!selectedRepo || !!startDate || !!endDate ||
+ issueFilter !== "all" || prFilter !== "all";
+
+ const handlePageChange = (_: unknown, newPage: number) => setPage(newPage);
+
const formatDate = (dateString: string): string =>
- new Date(dateString).toLocaleDateString();
+ new Date(dateString).toLocaleDateString("en-US", {
+ year: "numeric", month: "short", day: "numeric",
+ });
const filterData = (data: GitHubItem[], filterType: string): GitHubItem[] => {
let filtered = [...data];
if (["open", "closed", "merged"].includes(filterType)) {
filtered = filtered.filter((item) => {
- if (filterType === "merged") {
- return !!item.pull_request?.merged_at
- }
- else if (filterType === "closed") {
- return item.state === "closed" && !item.pull_request?.merged_at
- }
- else {
- //open
- return item.state === "open"
- }
+ if (filterType === "merged") return !!item.pull_request?.merged_at;
+ if (filterType === "closed") return item.state === "closed" && !item.pull_request?.merged_at;
+ return item.state === "open";
});
}
- if (searchTitle) {
- filtered = filtered.filter((item) =>
- item.title.toLowerCase().includes(searchTitle.toLowerCase())
- );
- }
- if (selectedRepo) {
- filtered = filtered.filter((item) =>
- item.repository_url.includes(selectedRepo)
- );
- }
- if (startDate) {
- filtered = filtered.filter(
- (item) => new Date(item.created_at) >= new Date(startDate)
- );
- }
- if (endDate) {
- filtered = filtered.filter(
- (item) => new Date(item.created_at) <= new Date(endDate)
- );
- }
+ if (searchTitle) filtered = filtered.filter((i) => i.title.toLowerCase().includes(searchTitle.toLowerCase()));
+ if (selectedRepo) filtered = filtered.filter((i) => i.repository_url.includes(selectedRepo));
+ 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);
return filtered;
};
- const getStatusIcon = (item: GitHubItem) => {
-
- if (item.pull_request) {
+ const currentRawData = tab === 0 ? issues : prs;
- if (item.pull_request.merged_at)
- return ;
+ const stats = useMemo(() => {
+ const open = currentRawData.filter((i) => i.state === "open").length;
+ const merged = currentRawData.filter((i) => !!i.pull_request?.merged_at).length;
+ const closed = currentRawData.filter((i) => i.state === "closed" && !i.pull_request?.merged_at).length;
+ return { open, merged, closed };
+ }, [currentRawData]);
- if (item.state === 'closed')
- return ;
+ const currentFilteredData = filterData(currentRawData, tab === 0 ? issueFilter : prFilter);
+ const totalCount = tab === 0 ? totalIssues : totalPrs;
+ 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]);
+
+ const activeFilterSummary = useMemo(() => {
+ const labels: string[] = [];
+ if (tab === 0 ? issueFilter !== "all" : prFilter !== "all") labels.push("state");
+ if (searchTitle) labels.push("title");
+ if (selectedRepo) labels.push("repo");
+ if (startDate || endDate) labels.push("date");
+ return labels;
+ }, [tab, issueFilter, prFilter, searchTitle, selectedRepo, startDate, endDate]);
+
+ const activeFilterCount = activeFilterSummary.length;
+ const visibleShare = totalCount ? Math.round((currentFilteredData.length / totalCount) * 100) : 0;
+ const progressValue = Math.max(8, visibleShare || 0);
- return ;
+ const getStatusIcon = (item: GitHubItem) => {
+ if (item.pull_request) {
+ if (item.pull_request.merged_at) return ;
+ if (item.state === "closed") return ;
+ return ;
}
+ if (item.state === "closed") return ;
+ return ;
+ };
- if (item.state === 'closed')
- return ;
-
- return ;
+ const getStateChip = (item: GitHubItem) => {
+ const isMerged = !!item.pull_request?.merged_at;
+ const state = isMerged ? "merged" : item.state;
+ const styles: Record = {
+ open: { bg: isDark ? "#1a3a1a" : "#dafbe1", color: isDark ? "#56d364" : "#1a7f37" },
+ closed: { bg: isDark ? "#3a1a1a" : "#fff0f0", color: isDark ? "#f85149" : "#cf222e" },
+ merged: { bg: isDark ? "#2a1a4a" : "#ede9fe", color: isDark ? "#a371f7" : "#6e40c9" },
+ };
+ const s = styles[state] ?? styles["closed"];
+ return (
+
+ );
};
+ const cellSx = {
+ fontSize: "0.82rem",
+ py: 1.3,
+ borderBottom: `1px solid ${theme.palette.divider}`,
+ };
- // Current data and filtered data according to tab and filters
- const currentRawData = tab === 0 ? issues : prs;
- const currentFilteredData = filterData(currentRawData, tab === 0 ? issueFilter : prFilter);
- const totalCount = tab === 0 ? totalIssues : totalPrs;
+ const surfaceGradient = isDark
+ ? "radial-gradient(circle at top right, rgba(59,130,246,0.18), transparent 35%), linear-gradient(180deg, rgba(15,23,42,0.92), rgba(15,23,42,0.72))"
+ : "radial-gradient(circle at top right, rgba(37,99,235,0.14), transparent 34%), linear-gradient(180deg, rgba(255,255,255,0.96), rgba(248,250,252,0.92))";
return (
-
- {/* Auth Form */}
-
+
+
+
+
+
+
+
+
+ GitHub Activity Tracker
+
+
+ Review your issues and pull requests in one place with quick filters, clean status badges,
+ and a layout that stays readable in both light and dark themes.
+
+
+
+
+
+ Overview
+
+
+
+ Loaded items
+ {currentRawData.length}
+
+
+ Visible after filters
+ {currentFilteredData.length}
+
+
+
+ Visibility
+ {visibleShare}%
+
+
+
+
+
+
+
+
+ {/* ── Page header ── */}
+
+
+
+ Activity dashboard
+
+
+
+ {/* ── Auth card ── */}
+
+
+ Authentication
+
+
+ Enter a GitHub username and personal access token to load activity securely.
+
- {/* Filters */}
-
- setSearchTitle(e.target.value)}
- sx={{ minWidth: 200 }}
- />
- setSelectedRepo(e.target.value)}
- sx={{ minWidth: 200 }}
- />
- setStartDate(e.target.value)}
- InputLabelProps={{ shrink: true }}
- sx={{ minWidth: 150 }}
- />
- setEndDate(e.target.value)}
- InputLabelProps={{ shrink: true }}
- sx={{ minWidth: 150 }}
- />
-
+ {/* ── Error alert ── */}
+ {(authError || dataError) && (
+
+ {authError || dataError}
+
+ )}
- {/* Tabs + State Filter */}
-
+
+
+ {tab === 1 && (
+
+ )}
+
+
+ )}
+
+ {/* ── Main card ── */}
+
- {
- setTab(v);
- setPage(0);
+ {/* Tabs + state filter */}
+
-
-
-
-
- State
-
-
-
-
- {(authError || dataError) && (
-
- {authError || dataError}
-
- )}
-
- {loading ? (
-
-
-
-
-
- Title
- Repository
- State
- Created
-
-
-
-
- {[...Array(5)].map((_, index) => (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ))}
-
-
-
-
-) : !authError && !dataError && currentFilteredData.length === 0 ? (
-
-
- No Data Found
-
-
-
- Try adjusting filters or searching for another GitHub user.
-
-
-) : (
-
-
+
+ Issues
+
+
+ }
+ />
+
+ Pull Requests
+
+
+ }
+ />
+
+
+
+ State
+
+
+
-
+ {/* Filter bar */}
+
+ setSearchTitle(e.target.value)}
+ size="small"
+ sx={{ minWidth: 180, "& .MuiOutlinedInput-root": { borderRadius: "12px", fontSize: "0.82rem" } }}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ />
+ setSelectedRepo(e.target.value)}
+ size="small"
+ sx={{ minWidth: 160, "& .MuiOutlinedInput-root": { borderRadius: "12px", fontSize: "0.82rem" } }}
+ />
+ setStartDate(e.target.value)}
+ size="small"
+ InputLabelProps={{ shrink: true }}
+ sx={{ minWidth: 140, "& .MuiOutlinedInput-root": { borderRadius: "12px", fontSize: "0.82rem" } }}
+ />
+ setEndDate(e.target.value)}
+ size="small"
+ InputLabelProps={{ shrink: true }}
+ sx={{ minWidth: 140, "& .MuiOutlinedInput-root": { borderRadius: "12px", fontSize: "0.82rem" } }}
+ />
+ {hasActiveFilters && (
+
+ }
+ sx={{
+ fontSize: "0.75rem", textTransform: "none",
+ borderRadius: "12px", height: "36px",
+ color: "text.secondary", borderColor: "divider",
+ }}
+ >
+ Clear
+
+
+ )}
+
+ {currentFilteredData.length} of {currentRawData.length} shown
+ {activeFilterCount > 0 ? ` • ${activeFilterCount} filter${activeFilterCount > 1 ? "s" : ""} active` : ""}
+
+
+ {/* Content area */}
+ {loading ? (
+
+ {[...Array(6)].map((_, i) => (
+
+ ))}
+
+ ) : !hasFetched ? (
+
+
+
+ No data yet
+
+
+ Enter your credentials above and click Fetch Data to load a cleaner, more interactive view of your GitHub activity.
+
+
+ ) : currentFilteredData.length === 0 ? (
+
+
+ No results match your filters.
+
+
+ Try widening the date range, switching the state filter, or searching a shorter title.
+
+ {hasActiveFilters && (
+
+ )}
+
+ ) : (
+
-
- Title
- Repository
- State
- Created
+ {[
+ { label: "Title", align: "left" as const },
+ { label: "Repository", align: "center" as const },
+ { label: "State", align: "center" as const },
+ { label: "Created", align: "left" as const },
+ ].map(({ label, align }) => (
+
+ {label}
+
+ ))}
-
{currentFilteredData.map((item) => (
-
-
-
- {getStatusIcon(item)}
+ window.open(item.html_url, "_blank", "noopener,noreferrer")}
+ >
+ {/* Title */}
+
+
+
+ {getStatusIcon(item)}
+
- {item.title}
+ {item.title}
+
+
-
-
- {item.repository_url.split("/").slice(-1)[0]}
+ {/* Repository */}
+
+
-
- {item.pull_request?.merged_at ? "merged" : item.state}
+ {/* State */}
+
+ {getStateChip(item)}
- {formatDate(item.created_at)}
-
+ {/* Created */}
+
+
+ {formatDate(item.created_at)}
+
+
))}
-
+
{
onPageChange={handlePageChange}
rowsPerPage={ROWS_PER_PAGE}
rowsPerPageOptions={[ROWS_PER_PAGE]}
+ sx={{ fontSize: "0.78rem", borderTop: "none" }}
/>
-
-
- )}
+ )}
+
);
};