From 1bcc3ed5fc480e3828b6feb5143a702fc12f3181 Mon Sep 17 00:00:00 2001 From: firoziya Date: Sun, 17 May 2026 15:28:46 +0530 Subject: [PATCH 1/2] frontend-ui-redesign --- src/App.css | 8 +- src/App.tsx | 68 +-- src/Routes/Router.tsx | 30 +- src/components/Features.tsx | 190 ++++--- src/components/Footer.tsx | 108 +++- src/components/Hero.tsx | 127 +++-- src/components/HowItWorks.tsx | 138 +++-- src/components/Navbar.tsx | 248 ++++++--- src/components/ScrollProgressBar.tsx | 102 +--- src/context/ThemeContext.tsx | 115 ++-- src/index.css | 293 +++++++++- src/pages/About/About.tsx | 169 +++--- src/pages/Contact/Contact.tsx | 469 +++++++--------- src/pages/Contributors/Contributors.tsx | 109 ++-- src/pages/ForgotPassword/Forgotpassword.tsx | 123 +++++ src/pages/Home/Home.tsx | 1 - src/pages/Login/Login.tsx | 302 +++++++---- src/pages/Signup/Signup.tsx | 328 +++++++----- src/pages/Tracker/Tracker.tsx | 560 +++++++++++--------- 19 files changed, 2133 insertions(+), 1355 deletions(-) create mode 100644 src/pages/ForgotPassword/Forgotpassword.tsx diff --git a/src/App.css b/src/App.css index b9d355d..7935b1e 100644 --- a/src/App.css +++ b/src/App.css @@ -1,8 +1,8 @@ +/* App-level overrides — base styles live in index.css */ #root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; + display: flex; + flex-direction: column; + min-height: 100vh; } .logo { diff --git a/src/App.tsx b/src/App.tsx index b00eba8..bdd7f8f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,42 +3,50 @@ import Footer from "./components/Footer"; import ScrollProgressBar from "./components/ScrollProgressBar"; import { Toaster } from "react-hot-toast"; import Router from "./Routes/Router"; -import ThemeWrapper from "./context/ThemeContext"; function App() { return ( - -
- +
+ + - +
+ +
-
- -
+
-
- - -
- + +
); } -export default App; +export default App; \ No newline at end of file diff --git a/src/Routes/Router.tsx b/src/Routes/Router.tsx index 40a7861..cceb18b 100644 --- a/src/Routes/Router.tsx +++ b/src/Routes/Router.tsx @@ -5,22 +5,22 @@ import Contact from "../pages/Contact/Contact"; import Contributors from "../pages/Contributors/Contributors"; import Signup from "../pages/Signup/Signup.tsx"; import Login from "../pages/Login/Login.tsx"; +import ForgotPassword from "../pages/ForgotPassword/ForgotPassword.tsx"; import ContributorProfile from "../pages/ContributorProfile/ContributorProfile.tsx"; import Home from "../pages/Home/Home.tsx"; -const Router = () => { - return ( - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - ); -}; +const Router = () => ( + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + +); -export default Router; +export default Router; \ No newline at end of file diff --git a/src/components/Features.tsx b/src/components/Features.tsx index a8ce652..6b76cd7 100644 --- a/src/components/Features.tsx +++ b/src/components/Features.tsx @@ -1,92 +1,110 @@ -import { BarChart3, Users, Search, Zap, Shield, Globe } from 'lucide-react'; +import { BarChart3, Users, Search, Zap, Shield, Globe } from "lucide-react"; -const Features = () => { - const features = [ - { - icon: BarChart3, - title: 'Activity Analytics', - description: 'Comprehensive charts and graphs showing commit patterns, contribution streaks, and repository activity over time.', - bgColor: 'bg-blue-100', - iconColor: 'text-blue-600', - hoverColor: 'hover:bg-blue-400/50 dark:hover:bg-blue-900/30', - borderColor: 'hover:border-blue-200 dark:hover:border-blue-700' - }, - { - icon: Users, - title: 'Multi-User Tracking', - description: 'Monitor multiple GitHub users simultaneously and compare their activity levels and contribution patterns.', - bgColor: 'bg-green-100', - iconColor: 'text-green-600', - hoverColor: 'hover:bg-green-400/50 dark:hover:bg-green-900/30', - borderColor: 'hover:border-green-200 dark:hover:border-green-700' - }, - { - icon: Search, - title: 'Smart Search', - description: 'Quickly find and add users to your tracking list with intelligent search and auto-suggestions.', - bgColor: 'bg-purple-100', - iconColor: 'text-purple-600', - hoverColor: 'hover:bg-purple-400/50 dark:hover:bg-purple-900/30', - borderColor: 'hover:border-purple-200 dark:hover:border-purple-700' - }, - { - icon: Zap, - title: 'Real-time Updates', - description: 'Get instant notifications and updates when tracked users make new contributions or repositories.', - bgColor: 'bg-orange-100', - iconColor: 'text-orange-600', - hoverColor: 'hover:bg-orange-400/50 dark:hover:bg-orange-900/30', - borderColor: 'hover:border-orange-200 dark:hover:border-orange-700' - }, - { - icon: Shield, - title: 'Privacy First', - description: 'All data is fetched from public GitHub APIs. We don\'t store personal information or require GitHub access.', - bgColor: 'bg-red-100', - iconColor: 'text-red-600', - hoverColor: 'hover:bg-red-400/50 dark:hover:bg-red-900/30', - borderColor: 'hover:border-red-200 dark:hover:border-red-700' - }, - { - icon: Globe, - title: 'Export & Share', - description: 'Export activity reports and share insights with your team through various formats and integrations.', - bgColor: 'bg-indigo-100', - iconColor: 'text-indigo-600', - hoverColor: 'hover:bg-indigo-400/50 dark:hover:bg-indigo-900/30', - borderColor: 'hover:border-indigo-200 dark:hover:border-indigo-700' - } - ]; +const features = [ + { + icon: BarChart3, + title: "Activity Analytics", + description: + "Comprehensive charts showing commit patterns, contribution streaks, and repository activity over time.", + }, + { + icon: Users, + title: "Multi-User Tracking", + description: + "Monitor multiple GitHub users simultaneously and compare their activity levels and contribution patterns.", + }, + { + icon: Search, + title: "Smart Search", + description: + "Quickly find and add users to your tracking list with intelligent search and auto-suggestions.", + }, + { + icon: Zap, + title: "Real-time Updates", + description: + "Get instant notifications and updates when tracked users make new contributions or repositories.", + }, + { + icon: Shield, + title: "Privacy First", + description: + "All data is fetched from public GitHub APIs. We don't store personal information or require GitHub access.", + }, + { + icon: Globe, + title: "Export & Share", + description: + "Export activity reports and share insights with your team through various formats and integrations.", + }, +]; - return ( -
-
-
-

Powerful Features

-

- Everything you need to track, analyze, and understand GitHub activity patterns -

-
+const Features = () => ( +
+
+ {/* Header */} +
+

Features

+

Powerful, simple tools

+

+ Everything you need to track, analyze, and understand GitHub activity patterns. +

+
-
- {features.map((feature, index) => { - const IconComponent = feature.icon; - return ( -
-
- -
-

{feature.title}

-

- {feature.description} -

+ {/* Grid — shadow-separated sections, no card borders */} +
+ {features.map((f, i) => { + const Icon = f.icon; + return ( +
{ + (e.currentTarget as HTMLElement).style.boxShadow = "var(--shadow-lg)"; + (e.currentTarget as HTMLElement).style.backgroundColor = "var(--color-bg)"; + }} + onMouseLeave={(e) => { + (e.currentTarget as HTMLElement).style.boxShadow = "var(--shadow-sm)"; + (e.currentTarget as HTMLElement).style.backgroundColor = "var(--color-surface)"; + }} + > +
+
- ); - })} -
+

+ {f.title} +

+

+ {f.description} +

+
+ ); + })}
-
- ); -}; +
+
+); -export default Features; +export default Features; \ No newline at end of file diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx index 878366f..0b61e3f 100644 --- a/src/components/Footer.tsx +++ b/src/components/Footer.tsx @@ -1,36 +1,96 @@ -import { FaGithub } from 'react-icons/fa'; -import { Link } from 'react-router-dom'; +import { FaGithub } from "react-icons/fa"; +import { Link } from "react-router-dom"; +import { GitBranch } from "lucide-react"; function Footer() { return ( - ); } -export default Footer; +export default Footer; \ No newline at end of file diff --git a/src/components/Hero.tsx b/src/components/Hero.tsx index 1549f8e..3a98aa2 100644 --- a/src/components/Hero.tsx +++ b/src/components/Hero.tsx @@ -1,34 +1,101 @@ -import { Search } from 'lucide-react'; -import { Link } from 'react-router-dom'; +import { Search, ArrowRight } from "lucide-react"; +import { Link } from "react-router-dom"; -const Hero = () => { - return ( -
-
-
-

- Track GitHub Activity - Like Never Before -

-

- Monitor and analyze GitHub user activity with powerful insights. Perfect for developers, - project managers, and teams who want to understand contribution patterns and repository engagement. -

-
- - {/* - */} -
+const Hero = () => ( +
+
+
+ {/* Label */} +

Open Source · GitHub Analytics

+ + {/* Headline */} +

+ Track GitHub Activity{" "} + Like Never Before +

+ + {/* Sub */} +

+ Monitor commits, pull requests, and issues across repositories. + Built for developers and project managers who care about contribution patterns. +

+ + {/* CTAs */} +
+ + + Start Tracking + + + Learn more + + +
+ + {/* Stats strip */} +
+ {[ + { value: "100%", label: "Open source" }, + { value: "Public API", label: "No data stored" }, + { value: "Real-time", label: "GitHub data" }, + ].map((s) => ( +
+

+ {s.value} +

+

+ {s.label} +

+
+ ))}
-
- ); -}; +
+
+); -export default Hero; +export default Hero; \ No newline at end of file diff --git a/src/components/HowItWorks.tsx b/src/components/HowItWorks.tsx index 8abf1e1..1e5b290 100644 --- a/src/components/HowItWorks.tsx +++ b/src/components/HowItWorks.tsx @@ -1,49 +1,107 @@ +const steps = [ + { + number: "01", + title: "Search Users", + description: + "Enter a GitHub username to load their public activity — issues, pull requests, and contribution data.", + }, + { + number: "02", + title: "Monitor Activity", + description: + "View insights on commits, pull requests, issues, and other GitHub activities in a structured table.", + }, + { + number: "03", + title: "Analyze Insights", + description: + "Filter by status, date, and repository. Export or share reports to understand development patterns.", + }, +]; -const HowItWorks = () => { - const steps = [ - { - number: 1, - title: 'Search Users', - description: 'Enter GitHub usernames or search for users by name. Add them to your tracking dashboard.' - }, - { - number: 2, - title: 'Monitor Activity', - description: 'Watch insights of commits, pull requests, issues, and other GitHub activities.' - }, - { - number: 3, - title: 'Analyze Insights', - description: 'Review detailed analytics, export reports, and gain valuable insights into development patterns.' - } - ]; +const HowItWorks = () => ( +
+
+ {/* Header */} +
+

How it works

+

Simple three-step process

+
- return ( -
-
-
-

How It Works

-

- Get started in minutes with our simple three-step process -

-
+ {/* Steps */} +
+ {steps.map((step, i) => ( +
+ {/* Connector line (desktop) */} + {i < steps.length - 1 && ( +
+ )} -
- {steps.map((step, index) => ( -
-
+
0 ? "48px" : "0" }}> + {/* Number */} +

{step.number} -

-

{step.title}

-

+

+ + {/* Divider */} +
+ +

+ {step.title} +

+

{step.description}

- ))} -
+ + {/* Mobile separator */} + {i < steps.length - 1 && ( +
+ )} +
+ ))}
-
- ); -}; +
+
+); -export default HowItWorks; +export default HowItWorks; \ No newline at end of file diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index 8272f7c..ed2fdf2 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -1,101 +1,195 @@ import { NavLink, Link } from "react-router-dom"; import { useState, useContext } from "react"; import { ThemeContext } from "../context/ThemeContext"; -import { Moon, Sun, Menu, X, UserPlus } from 'lucide-react'; +import { Moon, Sun, Menu, X, UserPlus, GitBranch } from "lucide-react"; const Navbar: React.FC = () => { const [isOpen, setIsOpen] = useState(false); const themeContext = useContext(ThemeContext); - if (!themeContext) return null; const { toggleTheme, mode } = themeContext; - const navLinkStyles = ({ isActive }: { isActive: boolean }) => - `px-5 py-2 rounded-full text-lg font-semibold transition-all duration-300 - ${isActive - ? "text-blue-600 bg-blue-100/60 dark:bg-blue-900/40 shadow-sm ring-1 ring-blue-200/50" - : "text-slate-600 dark:text-gray-300 hover:text-blue-500 hover:bg-white/50" - }`; + const close = () => setIsOpen(false); return ( - + ); }; -const MobileNavLink = ({ to, children, onClick }: { to: string, children: React.ReactNode, onClick: () => void }) => ( - `block px-6 py-4 rounded-2xl text-xl font-bold transition-all ${isActive ? "bg-blue-600 text-white shadow-lg translate-x-2" : "text-slate-600 dark:text-gray-400 hover:bg-white/40 dark:hover:bg-gray-800/40"}`}>{children} -); - export default Navbar; \ No newline at end of file diff --git a/src/components/ScrollProgressBar.tsx b/src/components/ScrollProgressBar.tsx index 34e3811..719f368 100644 --- a/src/components/ScrollProgressBar.tsx +++ b/src/components/ScrollProgressBar.tsx @@ -1,94 +1,22 @@ -import { useEffect, useState } from "react"; +import { useEffect } from "react"; const ScrollProgressBar = () => { - const [scrollWidth, setScrollWidth] = useState(0); - const [isAnimating, setIsAnimating] = useState(true); // Tracks if the page load animation is active - - const handleScroll = () => { - const { documentElement } = document; - const scrollTop = documentElement.scrollTop; - const scrollableHeight = - documentElement.scrollHeight - documentElement.clientHeight; - - if (scrollableHeight <= 0) { - setScrollWidth(0); - return; - } - - const width = Math.min( - 100, - Math.max(0, (scrollTop / scrollableHeight) * 100), - ); - setScrollWidth(width); - }; - - useEffect(() => { - // Simulate the page load animation - const animationTimeout = setTimeout(() => { - setIsAnimating(false); // End the animation after 2 seconds - }, 2000); - - // Clean up timeout - return () => clearTimeout(animationTimeout); - }, []); - useEffect(() => { - // Always listen for scroll events - window.addEventListener("scroll", handleScroll); - // Call handleScroll once on mount to set initial position - handleScroll(); - return () => window.removeEventListener("scroll", handleScroll); + const updateProgress = () => { + const { documentElement } = document; + const scrollTop = documentElement.scrollTop || document.body.scrollTop; + const scrollableHeight = documentElement.scrollHeight - documentElement.clientHeight; + const width = scrollableHeight > 0 ? (scrollTop / scrollableHeight) * 100 : 0; + const bar = document.getElementById("scroll-progress"); + if (bar) bar.style.width = `${width}%`; + }; + + window.addEventListener("scroll", updateProgress, { passive: true }); + updateProgress(); + return () => window.removeEventListener("scroll", updateProgress); }, []); - return ( - <> - {/* Left-to-right animation during page load */} - {isAnimating && ( -
- )} - - {/* Scroll progress bar after animation ends */} - {!isAnimating && ( -
- )} - - {/* Animation Keyframes */} - - - ); + return null; // bar is rendered via #scroll-progress in Navbar/CSS }; -export default ScrollProgressBar; +export default ScrollProgressBar; \ No newline at end of file diff --git a/src/context/ThemeContext.tsx b/src/context/ThemeContext.tsx index b6866e3..e1f7918 100644 --- a/src/context/ThemeContext.tsx +++ b/src/context/ThemeContext.tsx @@ -1,49 +1,102 @@ -// src/ThemeContext.tsx -import { createContext, useMemo, useState, useEffect, ReactNode } from 'react'; -import { createTheme, ThemeProvider, Theme } from '@mui/material/styles'; +import { createContext, useState, useEffect, type ReactNode } from "react"; +import { createTheme, ThemeProvider } from "@mui/material/styles"; +import CssBaseline from "@mui/material/CssBaseline"; -interface ThemeContextType { - mode: 'light' | 'dark'; +export type ThemeMode = "light" | "dark"; + +export interface ThemeContextType { + mode: ThemeMode; toggleTheme: () => void; } export const ThemeContext = createContext(null); -const THEME_STORAGE_KEY = 'theme'; - -const ThemeWrapper = ({ children }: { children: ReactNode }) => { - const [mode, setMode] = useState<'light' | 'dark'>(() => { - const savedMode = localStorage.getItem(THEME_STORAGE_KEY); - return savedMode === 'dark' ? 'dark' : 'light'; +const getMuiTheme = (mode: ThemeMode) => + createTheme({ + palette: { + mode, + ...(mode === "light" + ? { + background: { default: "#FFFFFF", paper: "#F7F7F7" }, + text: { primary: "#111111", secondary: "#444444" }, + primary: { main: "#0F62FE" }, + divider: "#E0E0E0", + } + : { + background: { default: "#000000", paper: "#111111" }, + text: { primary: "#F5F5F5", secondary: "#BBBBBB" }, + primary: { main: "#4589FF" }, + divider: "#2A2A2A", + }), + }, + typography: { + fontFamily: "'Open Sans', sans-serif", + h1: { fontFamily: "'Montserrat', sans-serif", fontWeight: 700 }, + h2: { fontFamily: "'Montserrat', sans-serif", fontWeight: 700 }, + h3: { fontFamily: "'Montserrat', sans-serif", fontWeight: 700 }, + h4: { fontFamily: "'Montserrat', sans-serif", fontWeight: 600 }, + h5: { fontFamily: "'Montserrat', sans-serif", fontWeight: 600 }, + h6: { fontFamily: "'Montserrat', sans-serif", fontWeight: 600 }, + fontSize: 16, + }, + shape: { borderRadius: 8 }, + components: { + MuiPaper: { + styleOverrides: { + root: { + backgroundImage: "none", + boxShadow: "0 2px 4px rgba(0,0,0,0.10)", + }, + }, + }, + MuiButton: { + styleOverrides: { + root: { + fontFamily: "'Montserrat', sans-serif", + fontWeight: 600, + textTransform: "none", + fontSize: "15px", + borderRadius: "8px", + boxShadow: "none", + "&:hover": { boxShadow: "0 2px 4px rgba(0,0,0,0.10)" }, + }, + }, + }, + MuiTextField: { + styleOverrides: { + root: { + "& .MuiInputBase-root": { fontFamily: "'Open Sans', sans-serif", fontSize: "16px" }, + "& .MuiInputLabel-root": { fontFamily: "'Open Sans', sans-serif" }, + }, + }, + }, + MuiTab: { + styleOverrides: { + root: { fontFamily: "'Montserrat', sans-serif", fontWeight: 600, textTransform: "none", fontSize: "15px" }, + }, + }, + }, }); - // Sync mode with class and localStorage +export default function ThemeWrapper({ children }: { children: ReactNode }) { + const stored = typeof window !== "undefined" ? localStorage.getItem("gt-theme") : null; + const [mode, setMode] = useState((stored as ThemeMode) || "light"); + useEffect(() => { - if (mode === 'dark') { - document.documentElement.classList.add('dark'); - } else { - document.documentElement.classList.remove('dark'); - } - localStorage.setItem(THEME_STORAGE_KEY, mode); + const root = document.documentElement; + if (mode === "dark") root.classList.add("dark"); + else root.classList.remove("dark"); + localStorage.setItem("gt-theme", mode); }, [mode]); - const toggleTheme = () => { - setMode((prevMode) => (prevMode === 'light' ? 'dark' : 'light')); - }; - - const muiTheme: Theme = useMemo( - () => createTheme({ palette: { mode } }), - [mode] - ); + const toggleTheme = () => setMode((m) => (m === "light" ? "dark" : "light")); return ( - + + {children} ); -}; - -export default ThemeWrapper; -export type { ThemeContextType }; +} \ No newline at end of file diff --git a/src/index.css b/src/index.css index f4a2957..3b6f75c 100644 --- a/src/index.css +++ b/src/index.css @@ -1,20 +1,293 @@ +@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;700;800&family=Open+Sans:wght@300;400;500;600;700&display=swap'); + @tailwind base; @tailwind components; @tailwind utilities; +/* ─── CSS Custom Properties ─── */ +:root { + /* Light Theme */ + --color-bg: #FFFFFF; + --color-bg-2: #F7F7F7; + --color-bg-3: #EEEEEE; + --color-surface: #FFFFFF; + --color-border: #E0E0E0; + --color-text: #111111; + --color-text-2: #444444; + --color-text-3: #777777; + --color-accent: #1A1A1A; + --color-accent-2: #333333; + --color-primary: #0F62FE; + --color-primary-h: #0043CE; + --color-success: #198038; + --color-error: #DA1E28; + + --shadow-sm: 0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04); + --shadow-md: 0 2px 4px rgba(0,0,0,0.10); + --shadow-lg: 0 4px 12px rgba(0,0,0,0.10); + --shadow-xl: 0 8px 24px rgba(0,0,0,0.12); + + --radius-sm: 4px; + --radius-md: 8px; + --radius-lg: 12px; + + --font-display: 'Montserrat', sans-serif; + --font-body: 'Open Sans', sans-serif; + + --transition: 200ms ease; +} + +/* remove scroll bar for all browsers */ +::-webkit-scrollbar { display: none; } +* { -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ +} + + + +.dark { + /* Dark Theme */ + --color-bg: #000000; + --color-bg-2: #111111; + --color-bg-3: #1A1A1A; + --color-surface: #111111; + --color-border: #2A2A2A; + --color-text: #F5F5F5; + --color-text-2: #BBBBBB; + --color-text-3: #777777; + --color-accent: #F5F5F5; + --color-accent-2: #CCCCCC; + --color-primary: #4589FF; + --color-primary-h: #74AAFF; + --color-success: #42BE65; + --color-error: #FF8389; + + --shadow-sm: 0 1px 3px rgba(0,0,0,0.4); + --shadow-md: 0 2px 4px rgba(0,0,0,0.5); + --shadow-lg: 0 4px 12px rgba(0,0,0,0.5); + --shadow-xl: 0 8px 24px rgba(0,0,0,0.6); +} + +/* ─── Base Reset ─── */ +*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } + +html { + font-size: 16px; + scroll-behavior: smooth; + -webkit-text-size-adjust: 100%; +} + +body { + font-family: var(--font-body); + font-size: 16px; + line-height: 1.65; + background-color: var(--color-bg); + color: var(--color-text); + transition: background-color var(--transition), color var(--transition); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} -.icon-merged { - color: #2ea44f; /* Or use your theme color */ +h1, h2, h3, h4, h5, h6 { + font-family: var(--font-display); + font-weight: 700; + line-height: 1.2; + color: var(--color-text); } -.icon-pr-open { - color: #0969da; + +h1 { font-size: clamp(28px, 5vw, 48px); } +h2 { font-size: clamp(24px, 4vw, 36px); } +h3 { font-size: clamp(20px, 3vw, 28px); } +h4 { font-size: 20px; } + +p { font-size: 16px; color: var(--color-text-2); } +a { color: var(--color-primary); text-decoration: none; transition: color var(--transition); } +a:hover { color: var(--color-primary-h); } + +/* ─── Utility Classes ─── */ +.gt-surface { + background-color: var(--color-surface); + border: 1px solid var(--color-border); + box-shadow: var(--shadow-md); } -.icon-pr-closed { - color: #cf222e; + +.gt-section { + padding: 80px 24px; } -.icon-issue-open { - color: #2ea44f; + +.gt-container { + max-width: 1200px; + margin: 0 auto; + width: 100%; } -.icon-issue-closed { - color: #cf222e; + +.gt-label { + font-family: var(--font-display); + font-size: 11px; + font-weight: 600; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--color-text-3); +} + +.gt-divider { + height: 1px; + background-color: var(--color-border); +} + +/* ─── Form Elements ─── */ +.gt-input { + width: 100%; + padding: 12px 16px; + font-family: var(--font-body); + font-size: 16px; + color: var(--color-text); + background-color: var(--color-bg-2); + border: 1px solid var(--color-border); + border-radius: var(--radius-md); + outline: none; + transition: border-color var(--transition), box-shadow var(--transition); + -webkit-appearance: none; +} + +.gt-input::placeholder { color: var(--color-text-3); } +.gt-input:focus { + border-color: var(--color-primary); + box-shadow: 0 0 0 3px rgba(15, 98, 254, 0.15); } +.dark .gt-input:focus { + box-shadow: 0 0 0 3px rgba(69, 137, 255, 0.2); +} + +.gt-input-icon { position: relative; } +.gt-input-icon .gt-input { padding-left: 44px; } +.gt-input-icon .icon { + position: absolute; + left: 14px; + top: 50%; + transform: translateY(-50%); + color: var(--color-text-3); + pointer-events: none; +} + +.gt-btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 12px 24px; + font-family: var(--font-display); + font-size: 15px; + font-weight: 600; + border-radius: var(--radius-md); + border: none; + cursor: pointer; + transition: all var(--transition); + white-space: nowrap; +} + +.gt-btn-primary { + background-color: var(--color-accent); + color: var(--color-bg); + box-shadow: var(--shadow-sm); +} +.dark .gt-btn-primary { background-color: var(--color-text); color: var(--color-bg); } +.gt-btn-primary:hover { opacity: 0.85; box-shadow: var(--shadow-md); transform: translateY(-1px); } +.gt-btn-primary:active { transform: translateY(0); } + +.gt-btn-outline { + background: transparent; + color: var(--color-text); + border: 1px solid var(--color-border); +} +.gt-btn-outline:hover { background-color: var(--color-bg-2); border-color: var(--color-text-3); } + +.gt-btn-ghost { + background: transparent; + color: var(--color-text-2); + border: none; +} +.gt-btn-ghost:hover { background-color: var(--color-bg-2); color: var(--color-text); } + +/* ─── Status Icons (GitHub) ─── */ +.icon-merged { color: #8250df; } +.icon-pr-open { color: var(--color-primary); } +.icon-pr-closed { color: var(--color-error); } +.icon-issue-open { color: var(--color-success); } +.icon-issue-closed{ color: var(--color-error); } + +/* ─── Badge ─── */ +.gt-badge { + display: inline-flex; + align-items: center; + padding: 2px 10px; + border-radius: 100px; + font-family: var(--font-display); + font-size: 12px; + font-weight: 600; + letter-spacing: 0.02em; +} +.gt-badge-open { background: rgba(25, 128, 56, 0.1); color: var(--color-success); } +.gt-badge-closed { background: rgba(218, 30, 40, 0.1); color: var(--color-error); } +.gt-badge-merged { background: rgba(130, 80, 223, 0.1); color: #8250df; } + +/* ─── Scroll Progress Bar ─── */ +#scroll-progress { + position: fixed; + top: 0; + left: 0; + height: 2px; + background-color: var(--color-primary); + z-index: 9999; + transition: width 50ms linear; +} + +/* ─── Auth page layout ─── */ +.auth-layout { + min-height: 100vh; + display: grid; + grid-template-columns: 1fr 1fr; + background-color: var(--color-bg); +} +@media (max-width: 768px) { + .auth-layout { grid-template-columns: 1fr; } + .auth-panel-left { display: none; } +} + +/* ─── Smooth transitions ─── */ +.transition-theme { + transition: background-color var(--transition), border-color var(--transition), color var(--transition); +} + +/* ─── Mobile nav open/close ─── */ +@keyframes slideDown { + from { opacity: 0; transform: translateY(-8px); } + to { opacity: 1; transform: translateY(0); } +} +.nav-mobile-open { animation: slideDown 200ms ease; } + +/* ─── Focus visible ─── */ +:focus-visible { + outline: 2px solid var(--color-primary); + outline-offset: 2px; +} + +/* ─── Table styles for MUI override ─── */ +.MuiTableCell-root { + font-family: var(--font-body) !important; + font-size: 14px !important; + border-bottom: 1px solid var(--color-border) !important; +} +.MuiTableHead-root .MuiTableCell-root { + font-family: var(--font-display) !important; + font-weight: 600 !important; + font-size: 12px !important; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--color-text-3) !important; +} + +/* ─── Responsive ─── */ +@media (max-width: 640px) { + .gt-section { padding: 48px 16px; } +} \ No newline at end of file diff --git a/src/pages/About/About.tsx b/src/pages/About/About.tsx index 04af204..2d9ac0e 100644 --- a/src/pages/About/About.tsx +++ b/src/pages/About/About.tsx @@ -1,87 +1,122 @@ -import { motion } from "framer-motion"; import { Lightbulb, Users, Settings, Search } from "lucide-react"; const features = [ { - icon: , + icon: Search, title: "Simple Issue Tracking", description: "Track your GitHub issues seamlessly with intuitive filters and search options.", }, { - icon: , + icon: Users, title: "Team Collaboration", - description: "Collaborate with your team in real-time, manage issues and pull requests effectively.", + description: "Collaborate with your team, manage issues and pull requests effectively.", }, { - icon: , + icon: Settings, title: "Customizable Settings", description: "Customize your issue tracking workflow to match your team's needs.", }, ]; -const About = () => { - return ( -
- - {/* Hero Section */} -
- - About Us - - - Welcome to GitHub Tracker — your smart solution to manage GitHub issues without chaos. - -
+const About = () => ( +
- {/* Mission Section */} -
- - -

Our Mission

-

- We aim to provide an efficient and user-friendly way to track GitHub issues and pull requests. - Our goal is to make it easy for developers to stay organized and focused on their projects - without getting bogged down by the details. -

-
-
+ {/* Hero */} +
+
+

About

+

+ Built to simplify GitHub activity tracking +

+

+ Welcome to GitHub Tracker — your smart solution to monitor GitHub issues and pull requests without chaos. +

+
+
+ + {/* Mission */} +
+
+
+
+

Our Mission

+

+ Focus on the work, not the tool +

+

+ We aim to provide an efficient and user-friendly way to track GitHub issues and pull requests. + Our goal is to make it easy for developers to stay organized and focused on their projects + without getting bogged down by the details. +

+
+
+ +
+
+
+
+ + {/* Features */} +
+
+
+

What we do

+

Core capabilities

+
- {/* Features Section */} -
-

What We Do

-
- {features.map((feature, idx) => ( - -
{feature.icon}
-

{feature.title}

-

{feature.description}

-
- ))} +
+ {features.map((f, i) => { + const Icon = f.icon; + return ( +
+
+ +
+

+ {f.title} +

+

+ {f.description} +

+
+ ); + })}
-
-
- ); -}; +
+ +
+); -export default About; +export default About; \ No newline at end of file diff --git a/src/pages/Contact/Contact.tsx b/src/pages/Contact/Contact.tsx index a0bfccb..6eb7dce 100644 --- a/src/pages/Contact/Contact.tsx +++ b/src/pages/Contact/Contact.tsx @@ -1,279 +1,177 @@ -import { useState, useContext } from "react"; -import { - Github, - Mail, - Phone, - Send, - X, - CheckCircle, -} from "lucide-react"; -import { ThemeContext } from "../../context/ThemeContext"; -import type { ThemeContextType } from "../../context/ThemeContext"; +import { useState } from "react"; +import { Github, Mail, Phone, Send, CheckCircle, X } from "lucide-react"; + +const contactItems = [ + { + title: "Phone Support", + detail: "(123) 456-7890", + sub: "Mon–Fri, 9AM–6PM EST", + Icon: Phone, + }, + { + title: "Email Us", + detail: "support@githubtracker.com", + sub: "We'll respond within 24 hours", + Icon: Mail, + }, + { + title: "GitHub Issues", + detail: "github.com/GitMetricsLab/github_tracker", + sub: "Report bugs & feature requests", + Icon: Github, + }, +]; function Contact() { - const [showPopup, setShowPopup] = useState(false); + const [formData, setFormData] = useState({ name: "", email: "", subject: "", message: "" }); + const [showSuccess, setShowSuccess] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); - const themeContext = useContext(ThemeContext) as ThemeContextType; - const { mode } = themeContext; - const handleSubmit = async () => { - setIsSubmitting(true); - - // Simulate API call - await new Promise((resolve) => setTimeout(resolve, 1500)); - - setIsSubmitting(false); - setShowPopup(true); - - // Auto-close popup after 5 seconds - setTimeout(() => { - setShowPopup(false); - }, 5000); + const handleChange = ( + e: React.ChangeEvent + ) => { + const { name, value } = e.target; + setFormData((f) => ({ ...f, [name]: value })); }; - const handleClosePopup = () => { - setShowPopup(false); + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setIsSubmitting(true); + await new Promise((r) => setTimeout(r, 1200)); + setIsSubmitting(false); + setShowSuccess(true); + setFormData({ name: "", email: "", subject: "", message: "" }); + setTimeout(() => setShowSuccess(false), 5000); }; return ( -
- {/* Animated background elements */} -
-
-
-
-
- -
- {/* Header Section */} -
-
-
- Logo -
-

- GitHub Tracker -

-
-

- Get in touch with us to discuss your project tracking needs or - report any issues +

+ + {/* Header */} +
+
+

Contact

+

Get in touch

+

+ Have a question or feedback? We'd love to hear from you.

+
+ + {/* Main */} +
+
+
-
- {/* Contact Info Cards */} -
-
+ {/* Info */} +

- Let's Connect + Contact information

-

- We're here to help you track and manage your GitHub - repositories more effectively -

-
-
- {[...Array(3)].map((_, index) => { - const contactTypes = [ - { - title: "Phone Support", - iconBg: "from-blue-500 to-cyan-500", - detail: "(123) 456-7890", - sub: "Mon-Fri, 9AM-6PM EST", - Icon: Phone, - }, - { - title: "Email Us", - iconBg: "from-purple-500 to-pink-500", - detail: "support@githubtracker.com", - sub: "We'll respond within 24 hours", - Icon: Mail, - }, - { - title: "GitHub Issues", - iconBg: "from-green-500 to-teal-500", - detail: "github.com/yourorg/githubtracker", - sub: "Report bugs & feature requests", - Icon: Github, - }, - ]; - const { title, iconBg, detail, sub, Icon } = - contactTypes[index]; - return ( +
+ {contactItems.map(({ title, detail, sub, Icon }) => (
-
-
+ +
+
+

- -

-
-

- {title} -

-

- {detail} -

-

- {sub} -

-
+ {title} +

+

{detail}

+

{sub}

- ); - })} + ))} +
-
- {/* Contact Form */} -
-

- Send us a Message -

- -
-
- {/* Full Name */} -
- - -
+ {/* Form */} +
+

+ Send a message +

- {/* Email */} -
- - +
+
+
+ + +
+
+ + +
- {/* Subject */}
- {/* Message */} -
+
- - + className="gt-input" + style={{ resize: "vertical" }} + />
-
+ + +
-
+
- {/* Success Popup */} - {showPopup && ( + {/* Success toast */} + {showSuccess && (
- -
- Thank you for contacting us! We will get back to you shortly. -
+ +

+ Message sent! We'll be in touch soon. +

)} @@ -348,4 +243,4 @@ function Contact() { ); } -export default Contact; +export default Contact; \ No newline at end of file diff --git a/src/pages/Contributors/Contributors.tsx b/src/pages/Contributors/Contributors.tsx index 60270b1..7d8432b 100644 --- a/src/pages/Contributors/Contributors.tsx +++ b/src/pages/Contributors/Contributors.tsx @@ -73,70 +73,73 @@ const ContributorsPage = () => { {contributors.map((contributor) => ( - + - - - - - {contributor.login} - + + + + {contributor.login} + - - {contributor.contributions} Contributions - - {/* + + {contributor.contributions} Contributions + + {/* Thank you for your valuable contributions to our community! */} - - + + - - - - + + + + ))} +
+ Frontend UI Redesigned by github.com/Firoziya +
); }; diff --git a/src/pages/ForgotPassword/Forgotpassword.tsx b/src/pages/ForgotPassword/Forgotpassword.tsx new file mode 100644 index 0000000..c6d28e6 --- /dev/null +++ b/src/pages/ForgotPassword/Forgotpassword.tsx @@ -0,0 +1,123 @@ +import React, { useState } from "react"; +import { Link } from "react-router-dom"; +import { Mail, GitBranch, ArrowLeft, ArrowRight, CheckCircle } from "lucide-react"; + +const ForgotPassword: React.FC = () => { + const [email, setEmail] = useState(""); + const [submitted, setSubmitted] = useState(false); + const [isLoading, setIsLoading] = useState(false); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setIsLoading(true); + // Simulate API call + await new Promise((r) => setTimeout(r, 1200)); + setIsLoading(false); + setSubmitted(true); + }; + + return ( +
+
+ {/* Logo */} + + + + + + GitHubTracker + + + + {!submitted ? ( + <> +
+ + Back to login + +

+ Reset your password +

+

+ Enter your email address and we'll send you a reset link. +

+
+ +
+
+ +
+ + setEmail(e.target.value)} + required + className="gt-input" + /> +
+
+ + +
+ +

+ Remember your password?{" "} + + Sign in + +

+ + ) : ( + /* Success state */ +
+
+ +
+

+ Check your inbox +

+

+ If an account exists for {email}, you'll receive a password reset link shortly. +

+ + Back to login + +
+ )} +
+
+ ); +}; + +export default ForgotPassword; \ No newline at end of file diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index 03759ab..8e9caec 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -8,7 +8,6 @@ function Home() { -
) } diff --git a/src/pages/Login/Login.tsx b/src/pages/Login/Login.tsx index d6f21a7..eece810 100644 --- a/src/pages/Login/Login.tsx +++ b/src/pages/Login/Login.tsx @@ -1,43 +1,37 @@ -import React, { useState, ChangeEvent, FormEvent, useContext } from "react"; +import React, { useState, useContext } from "react"; import axios from "axios"; -import { useNavigate, Link } from "react-router-dom"; +import { Link, useNavigate } from "react-router-dom"; +import { Mail, Lock, Eye, EyeOff, GitBranch, ArrowRight } from "lucide-react"; import { ThemeContext } from "../../context/ThemeContext"; -import type { ThemeContextType } from "../../context/ThemeContext"; const backendUrl = import.meta.env.VITE_BACKEND_URL; -interface LoginFormData { - email: string; - password: string; -} +interface FormData { email: string; password: string; } const Login: React.FC = () => { - const [formData, setFormData] = useState({ email: "", password: "" }); - const [message, setMessage] = useState(""); - const [isLoading, setIsLoading] = useState(false); - + const [formData, setFormData] = useState({ email: "", password: "" }); + const [message, setMessage] = useState(""); + const [isLoading, setIsLoading] = useState(false); + const [showPassword, setShowPassword] = useState(false); const navigate = useNavigate(); - const themeContext = useContext(ThemeContext) as ThemeContextType; - const { mode } = themeContext; + const ctx = useContext(ThemeContext); + const mode = ctx?.mode ?? "light"; - const handleChange = (e: ChangeEvent) => { + const handleChange = (e: React.ChangeEvent) => { const { name, value } = e.target; - setFormData({ ...formData, [name]: value }); + setFormData((f) => ({ ...f, [name]: value })); }; - const handleSubmit = async (e: FormEvent) => { + const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setIsLoading(true); - + setMessage(""); try { - const response = await axios.post(`${backendUrl}/api/auth/login`, formData); - setMessage(response.data.message); - - if (response.data.message === 'Login successful') { - navigate("/home"); - } - } catch (error: any) { - setMessage(error.response?.data?.message || "Something went wrong"); + const res = await axios.post(`${backendUrl}/api/auth/login`, formData, { withCredentials: true }); + setMessage(res.data.message); + if (res.data.message === "Login successful") navigate("/track"); + } catch { + setMessage("Invalid credentials. Please try again."); } finally { setIsLoading(false); } @@ -45,116 +39,192 @@ const Login: React.FC = () => { return (
- {/* Animated background elements */} -
-
-
-
-
+ {/* Left panel — decorative (hidden on mobile) */} +
+
+ + + + + GitHubTracker + +
+ +
+
+ "Track every contribution. Understand every pattern." +
+

+ Monitor GitHub activity across teams and repositories in one clean interface. +

+
+ +

+ © {new Date().getFullYear()} GitHub Tracker +

-
- {/* Branding */} -
-
- Logo + {/* Right panel — form */} +
+
+ {/* Mobile logo */} +
+ + + + + GitHubTracker +
-

- GitHubTracker -

-

- Track your GitHub journey -

-
+ {/* Heading */} +
+

+ Welcome back +

+

+ Enter your credentials to continue +

+
- {/* Form Card */} -
-

- Welcome Back -

- -
-
- + {/* Form */} + + {/* Email */} +
+ +
+ + +
-
- + {/* Password */} +
+
+ + + Forgot password? + +
+
+ + + +
+ {/* Error/success message */} + {message && ( +
+ {message} +
+ )} + + {/* Submit */} - {/* Message */} - {message && ( -
- {message} -
- )} - - {/* Footer Text */} -
-

- Don't have an account? - - Sign up here - -

-
+ {/* Footer */} +

+ Don't have an account?{" "} + + Create one + +

- -
); }; diff --git a/src/pages/Signup/Signup.tsx b/src/pages/Signup/Signup.tsx index 0c314db..e3e1ae8 100644 --- a/src/pages/Signup/Signup.tsx +++ b/src/pages/Signup/Signup.tsx @@ -1,190 +1,236 @@ -import React, { useState } from "react"; +import React, { useState, useContext } from "react"; import axios from "axios"; import { useNavigate, Link } from "react-router-dom"; -import { User, Mail, Lock } from "lucide-react"; +import { User, Mail, Lock, Eye, EyeOff, GitBranch, ArrowRight, Check } from "lucide-react"; +import { ThemeContext } from "../../context/ThemeContext"; const backendUrl = import.meta.env.VITE_BACKEND_URL; -interface SignUpFormData { - username: string; - email: string; - password: string; -} +interface SignUpFormData { username: string; email: string; password: string; } -const SignUp: React.FC = () => { - const [formData, setFormData] = useState({ - username: "", - email: "", - password: "", - }); +const passwordRules = [ + { label: "At least 8 characters", test: (p: string) => p.length >= 8 }, + { label: "Contains a number", test: (p: string) => /\d/.test(p) }, +]; - const [message, setMessage] = useState(""); +const SignUp: React.FC = () => { + const [formData, setFormData] = useState({ username: "", email: "", password: "" }); + const [message, setMessage] = useState(""); + const [showPassword, setShowPassword] = useState(false); + const [isLoading, setIsLoading] = useState(false); const navigate = useNavigate(); + const ctx = useContext(ThemeContext); const handleChange = (e: React.ChangeEvent) => { const { name, value } = e.target; - setFormData({ ...formData, [name]: value }); + setFormData((f) => ({ ...f, [name]: value })); }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); + setIsLoading(true); + setMessage(""); try { - const response = await axios.post( - `${backendUrl}/api/auth/signup`, - formData - ); - - setMessage(response.data.message); - - if (response.data.message === "User created successfully") { - navigate("/login"); - } - } catch (error) { + const res = await axios.post(`${backendUrl}/api/auth/signup`, formData); + setMessage(res.data.message); + if (res.data.message === "User created successfully") navigate("/login"); + } catch { setMessage("Something went wrong. Please try again."); + } finally { + setIsLoading(false); } }; return ( -
- - {/* Background effects */} -
-
-
-
- -
- {/* Logo */} -
-
- Logo -
- -

- GitHubTracker -

+
+ {/* Left panel */} +
+
+ + + + + GitHubTracker + +
-

- Join your GitHub journey -

+
+ {[ + { icon: "01", text: "Track multiple GitHub users simultaneously" }, + { icon: "02", text: "Filter issues and PRs by status, date, and repo" }, + { icon: "03", text: "Privacy-first — no data stored, just public APIs" }, + ].map((item) => ( +
+ + {item.icon} + +

{item.text}

+
+ ))}
- {/* Form Card */} -
+

+ © {new Date().getFullYear()} GitHub Tracker +

+
-

- Create Account -

+ {/* Right panel — form */} +
+
+ {/* Mobile logo */} +
+ + + + + GitHubTracker + +
+ +
+

+ Create your account +

+

Start tracking GitHub activity in minutes

+
-
+
{/* Username */} -
-
- +
+ +
+ +
-
{/* Email */} -
-
- +
+ +
+ +
-
{/* Password */} -
-
- +
+ +
+ + +
- + + {/* Password rules */} + {formData.password && ( +
+ {passwordRules.map((r) => ( +
+ + + {r.label} + +
+ ))} +
+ )}
- {/* Button */} + {/* Message */} + {message && ( +
+ {message} +
+ )} + -
- {/* Message */} - {message && ( -
- {message} -
- )} - - {/* Footer */} -
-

- Already have an account?{" "} - - - Sign in here - - +

+ By signing up you agree to our{" "} + Terms of Service

-
+ + +

+ Already have an account?{" "} + + Sign in + +

); }; -export default SignUp; +export default SignUp; \ No newline at end of file diff --git a/src/pages/Tracker/Tracker.tsx b/src/pages/Tracker/Tracker.tsx index 2bd4d30..e16d9f2 100644 --- a/src/pages/Tracker/Tracker.tsx +++ b/src/pages/Tracker/Tracker.tsx @@ -1,17 +1,15 @@ -import React, { useState, useEffect } from "react" +import React, { useState, useEffect } from "react"; import { IssueOpenedIcon, IssueClosedIcon, GitPullRequestIcon, GitPullRequestClosedIcon, GitMergeIcon, -} from '@primer/octicons-react'; +} from "@primer/octicons-react"; import { - Container, Box, TextField, Button, - Paper, Table, TableBody, TableCell, @@ -29,7 +27,6 @@ import { FormControl, InputLabel, } from "@mui/material"; -import { useTheme } from "@mui/material/styles"; import { useGitHubAuth } from "../../hooks/useGitHubAuth"; import { useGitHubData } from "../../hooks/useGitHubData"; @@ -46,31 +43,11 @@ interface GitHubItem { } const Home: React.FC = () => { - - const theme = useTheme(); - - const { - username, - setUsername, - token, - setToken, - error: authError, - getOctokit, - } = useGitHubAuth(); - - const { - issues, - prs, - totalIssues, - totalPrs, - loading, - error: dataError, - fetchData, - } = useGitHubData(getOctokit); + const { username, setUsername, token, setToken, error: authError, getOctokit } = useGitHubAuth(); + const { issues, prs, totalIssues, totalPrs, loading, error: dataError, fetchData } = useGitHubData(getOctokit); const [tab, setTab] = useState(0); const [page, setPage] = useState(0); - const [issueFilter, setIssueFilter] = useState("all"); const [prFilter, setPrFilter] = useState("all"); const [searchTitle, setSearchTitle] = useState(""); @@ -78,274 +55,345 @@ const Home: React.FC = () => { const [startDate, setStartDate] = useState(""); const [endDate, setEndDate] = useState(""); - // 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 => { + const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); setPage(0); fetchData(username, 1, ROWS_PER_PAGE); }; - const handlePageChange = (_: unknown, newPage: number) => { - setPage(newPage); - }; - - const formatDate = (dateString: string): string => - new Date(dateString).toLocaleDateString(); + const formatDate = (d: string) => new Date(d).toLocaleDateString(); const filterData = (data: GitHubItem[], filterType: string): GitHubItem[] => { - let filtered = [...data]; + let f = [...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" - } + f = f.filter((item) => { + 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) - ); - } - return filtered; + if (searchTitle) f = f.filter((i) => i.title.toLowerCase().includes(searchTitle.toLowerCase())); + if (selectedRepo) f = f.filter((i) => i.repository_url.includes(selectedRepo)); + if (startDate) f = f.filter((i) => new Date(i.created_at) >= new Date(startDate)); + if (endDate) f = f.filter((i) => new Date(i.created_at) <= new Date(endDate)); + return f; }; const getStatusIcon = (item: GitHubItem) => { - if (item.pull_request) { - - if (item.pull_request.merged_at) - return ; - - if (item.state === 'closed') - return ; - - return ; + 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 getStateBadge = (item: GitHubItem) => { + const isMerged = item.pull_request?.merged_at; + const state = isMerged ? "merged" : item.state; + return {state}; + }; - // 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 currentFilter = tab === 0 ? issueFilter : prFilter; + const currentFilteredData = filterData(currentRawData, currentFilter); const totalCount = tab === 0 ? totalIssues : totalPrs; + const inputSx = { + "& .MuiOutlinedInput-root": { + fontFamily: "var(--font-body)", + fontSize: "15px", + backgroundColor: "var(--color-bg-2)", + "& fieldset": { borderColor: "var(--color-border)" }, + "&:hover fieldset": { borderColor: "var(--color-text-3)" }, + "&.Mui-focused fieldset": { borderColor: "var(--color-primary)", borderWidth: "1px" }, + }, + "& .MuiInputLabel-root": { + fontFamily: "var(--font-display)", + fontSize: "14px", + "&.Mui-focused": { color: "var(--color-primary)" }, + }, + "& input": { color: "var(--color-text)" }, + }; + return ( - - {/* Auth Form */} - -
- +
+ {/* Page header */} +
+
+

Tracker

+

GitHub Activity Tracker

+

+ Enter a GitHub username and personal access token to explore their activity. +

+
+
+ +
+ {/* Auth form */} +
+

+ Authentication +

+ + + setUsername(e.target.value)} + required + sx={{ flex: 1, minWidth: 160, ...inputSx }} + /> + setToken(e.target.value)} + type="password" + required + sx={{ flex: 2, minWidth: 200, ...inputSx }} + /> + + + +
+ + {/* Filters */} +
+

+ Filters +

+ setUsername(e.target.value)} - required - sx={{ flex: 1, minWidth: 150 }} + label="Search title" + value={searchTitle} + onChange={(e) => setSearchTitle(e.target.value)} + sx={{ minWidth: 180, ...inputSx }} /> setToken(e.target.value)} - type="password" - required - sx={{ flex: 1, minWidth: 150 }} + label="Repository" + value={selectedRepo} + onChange={(e) => setSelectedRepo(e.target.value)} + sx={{ minWidth: 160, ...inputSx }} + /> + setStartDate(e.target.value)} + InputLabelProps={{ shrink: true }} + sx={{ minWidth: 150, ...inputSx }} + /> + setEndDate(e.target.value)} + InputLabelProps={{ shrink: true }} + sx={{ minWidth: 150, ...inputSx }} /> - - - +
- {/* 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 }} - /> - - - {/* Tabs + State Filter */} - - { - setTab(v); - setPage(0); + {/* Errors */} + {(authError || dataError) && ( + + {authError || dataError} + + )} + + {/* Tabs + state filter */} +
- - - - - State - - - - - {(authError || dataError) && ( - - {authError || dataError} - - )} - - {loading ? ( - - - - ) : ( - - - - - - - - - Title - Repository - State - Created - - - - - {currentFilteredData.map((item) => ( - - - - {getStatusIcon(item)} - { setTab(v); setPage(0); }} + sx={{ + "& .MuiTab-root": { fontFamily: "var(--font-display)", fontWeight: 600, textTransform: "none", fontSize: "14px", color: "var(--color-text-3)", minHeight: 40 }, + "& .Mui-selected": { color: "var(--color-text) !important" }, + "& .MuiTabs-indicator": { backgroundColor: "var(--color-accent)", height: "2px" }, + }} + > + + + + + + State + + + + + {/* Table */} + {loading ? ( + + + + ) : ( + +
+ + + Title + Repository + State + Created + + + + {currentFilteredData.length === 0 ? ( + + + No data to display. Try fetching with a valid username and token. + + + ) : currentFilteredData.map((item) => ( + + +
+ {getStatusIcon(item)} + + sx={{ + fontFamily: "var(--font-body)", fontSize: "14px", + color: "var(--color-text)", + "&:hover": { color: "var(--color-primary)" }, + display: "-webkit-box", + WebkitLineClamp: 2, + WebkitBoxOrient: "vertical", + overflow: "hidden", + }} + > {item.title} - - - - - - {item.repository_url.split("/").slice(-1)[0]} - - - - {item.pull_request?.merged_at ? "merged" : item.state} - - - {formatDate(item.created_at)} - - - ))} - - -
- - - -
-
- )} - + +
+ + + + {item.repository_url.split("/").slice(-1)[0]} + + + + {getStateBadge(item)} + + + + {formatDate(item.created_at)} + + + + ))} + + + + setPage(p)} + rowsPerPage={ROWS_PER_PAGE} + rowsPerPageOptions={[ROWS_PER_PAGE]} + sx={{ + fontFamily: "var(--font-display)", fontSize: "13px", + borderTop: "1px solid var(--color-border)", + color: "var(--color-text-2)", + "& .MuiIconButton-root": { color: "var(--color-text-2)" }, + }} + /> + + )} +
+
+
); }; -export default Home; +export default Home; \ No newline at end of file From 79af1e3915932d7fc86b42cdf266c5fb555d1a9b Mon Sep 17 00:00:00 2001 From: firoziya Date: Sun, 17 May 2026 15:40:28 +0530 Subject: [PATCH 2/2] add --- src/Routes/Router.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Routes/Router.tsx b/src/Routes/Router.tsx index cceb18b..2ae9f4e 100644 --- a/src/Routes/Router.tsx +++ b/src/Routes/Router.tsx @@ -5,7 +5,7 @@ import Contact from "../pages/Contact/Contact"; import Contributors from "../pages/Contributors/Contributors"; import Signup from "../pages/Signup/Signup.tsx"; import Login from "../pages/Login/Login.tsx"; -import ForgotPassword from "../pages/ForgotPassword/ForgotPassword.tsx"; +import ForgotPassword from "../pages/ForgotPassword/Forgotpassword.tsx"; import ContributorProfile from "../pages/ContributorProfile/ContributorProfile.tsx"; import Home from "../pages/Home/Home.tsx";