diff --git a/src/App.tsx b/src/App.tsx index a49da65..e4c1995 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,23 +1,22 @@ - import Navbar from "./components/Navbar"; import Footer from "./components/Footer"; -import ScrollProgressBar from './components/ScrollProgressBar'; +import ScrollProgressBar from "./components/ScrollProgressBar"; import { Toaster } from "react-hot-toast"; - import Router from "./Routes/Router"; +import ThemeWrapper from "./ThemeContext"; // ✅ import your wrapper function App() { return ( - +
- + {/* Navbar */} {/* Main content */} -
- +
+
{/* Footer */} @@ -28,24 +27,21 @@ function App() { reverseOrder={false} gutter={8} containerClassName="mt-12" - containerStyle={{}} toastOptions={{ - className: 'bg-white', + className: "bg-white dark:bg-gray-800 text-black dark:text-white", duration: 5000, - //removeDelay: 1000, - success: { duration: 3000, iconTheme: { - primary: 'green', - secondary: 'white', + primary: "green", + secondary: "white", }, }, }} />
- +
); } -export default App; +export default App; \ No newline at end of file diff --git a/src/Routes/Login/Login.tsx b/src/Routes/Login/Login.tsx index 1c1f495..ba66cf5 100644 --- a/src/Routes/Login/Login.tsx +++ b/src/Routes/Login/Login.tsx @@ -1,8 +1,11 @@ -import React, { useState, ChangeEvent, FormEvent } from "react"; +import React, { useState, ChangeEvent, FormEvent, useContext } from "react"; import axios from "axios"; -import { useNavigate } from "react-router-dom"; // Import the hook for navigation +import { useNavigate } from "react-router-dom"; +import { ThemeContext } from "../../ThemeContext"; +import type { ThemeContextType } from "../../ThemeContext"; const backendUrl = import.meta.env.VITE_BACKEND_URL; + interface LoginFormData { email: string; password: string; @@ -11,8 +14,11 @@ interface LoginFormData { const Login: React.FC = () => { const [formData, setFormData] = useState({ email: "", password: "" }); const [message, setMessage] = useState(""); + const [isLoading, setIsLoading] = useState(false); - const navigate = useNavigate(); // Initialize the navigate hook + const navigate = useNavigate(); + const themeContext = useContext(ThemeContext) as ThemeContextType; + const { mode } = themeContext; const handleChange = (e: ChangeEvent) => { const { name, value } = e.target; @@ -21,59 +27,123 @@ const Login: React.FC = () => { const handleSubmit = async (e: FormEvent) => { e.preventDefault(); + setIsLoading(true); + try { - const response = await axios.post(`${backendUrl}/api/auth/login`, - formData, - - ); - setMessage(response.data.message); // Show success message from backend - - // Navigate to /home if login is successful - if (response.data.message === 'Login successful') { + 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"); + } finally { + setIsLoading(false); } }; return ( -
-

Login

-
-
- +
+ {/* Background blobs */} +
+
+
+
+
+ + {/* Login Card */} +
+
+
+ Logo +
+

+ GitHubTracker +

+

Track your GitHub journey

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

Welcome Back

+ + + + + + + + + + {message && ( +
+ {message} +
+ )} +
+ + {/* Footer Text */} +
+

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

- - - {message &&

{message}

} +
+ + {/* Lower gradient */} +
); }; -export default Login; - +export default Login; \ No newline at end of file diff --git a/src/Routes/Router.tsx b/src/Routes/Router.tsx index 867672a..3411a4b 100644 --- a/src/Routes/Router.tsx +++ b/src/Routes/Router.tsx @@ -21,6 +21,5 @@ const Router = () => { } /> ); -}; - -export default Router; + }; +export default Router; \ No newline at end of file diff --git a/src/ThemeContext.tsx b/src/ThemeContext.tsx new file mode 100644 index 0000000..27fa822 --- /dev/null +++ b/src/ThemeContext.tsx @@ -0,0 +1,39 @@ +// src/ThemeContext.tsx +import React, { createContext, useMemo, useState, useEffect, ReactNode } from 'react'; +import { createTheme, ThemeProvider, Theme } from '@mui/material/styles'; + +interface ThemeContextType { + mode: 'light' | 'dark'; + toggleTheme: () => void; +} + +export const ThemeContext = createContext(null); + +const ThemeWrapper = ({ children }: { children: ReactNode }) => { + const [mode, setMode] = useState<'light' | 'dark'>('light'); + + useEffect(() => { + if (mode === 'dark') { + document.documentElement.classList.add('dark'); + } else { + document.documentElement.classList.remove('dark'); + } + }, [mode]); + + const toggleTheme = () => { + setMode(prev => (prev === 'light' ? 'dark' : 'light')); + }; + + const muiTheme: Theme = useMemo(() => createTheme({ palette: { mode } }), [mode]); + + return ( + + + {children} + + + ); +}; + +export default ThemeWrapper; +export type { ThemeContextType }; \ No newline at end of file diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index f7ea2f2..7b62f15 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -1,8 +1,14 @@ -import { Link } from 'react-router-dom'; -import { useState } from 'react'; +import { Link } from "react-router-dom"; +import { useState } from "react"; +import { ThemeContext } from "../ThemeContext"; +import { useContext } from "react"; const Navbar: React.FC = () => { const [isOpen, setIsOpen] = useState(false); + const themeContext = useContext(ThemeContext); + if (!themeContext) return null; + + const { toggleTheme, mode } = themeContext; return (
- {/* Desktop Links */}
{ Login + > + Login + +
{/* Mobile Menu Button */} @@ -69,7 +82,6 @@ const Navbar: React.FC = () => {
- {/* Mobile Links */} {isOpen && (
diff --git a/src/main.tsx b/src/main.tsx index a9f043f..699b94e 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -3,11 +3,14 @@ import { createRoot } from "react-dom/client"; import App from "./App.tsx"; import "./index.css"; import { BrowserRouter } from "react-router-dom"; +import ThemeWrapper from "./ThemeContext.tsx"; createRoot(document.getElementById("root")!).render( + + -); +); \ No newline at end of file diff --git a/src/pages/About/About.tsx b/src/pages/About/About.tsx index 81ed120..d1f2a0e 100644 --- a/src/pages/About/About.tsx +++ b/src/pages/About/About.tsx @@ -2,37 +2,48 @@ const About = () => { return (
{/* Hero Section */} -
+

About Us

-

Welcome to GitHub Tracker! We simplify issue tracking for developers.

+

+ Welcome to GitHub Tracker! We simplify issue tracking for developers. +

{/* 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. + 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 Section */} -
+

What We Do

-
-
+
+
🔍

Simple Issue Tracking

-

Track your GitHub issues seamlessly with intuitive filters and search options.

+

+ Track your GitHub issues seamlessly with intuitive filters and search options. +

-
+
👥

Team Collaboration

-

Collaborate with your team in real-time, manage issues and pull requests effectively.

+

+ Collaborate with your team in real-time, manage issues and pull requests effectively. +

-
+
⚙️

Customizable Settings

-

Customize your issue tracking workflow to match your team's needs.

+

+ Customize your issue tracking workflow to match your team's needs. +

@@ -40,4 +51,4 @@ const About = () => { ); }; -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 ef9f967..28eef10 100644 --- a/src/pages/Contact/Contact.tsx +++ b/src/pages/Contact/Contact.tsx @@ -1,85 +1,328 @@ -import { FormEvent, useState } from 'react'; +import { useState } from 'react'; +import { Github, Mail, Phone, Send, X, CheckCircle } from 'lucide-react'; +import { useContext } from "react"; +import { ThemeContext } from "../../ThemeContext"; +import type { ThemeContextType } from "../../ThemeContext"; function Contact() { - const [showPopup, setShowPopup] = useState(false); // State to control popup visibility + const [showPopup, setShowPopup] = useState(false); + const [isSubmitting, setIsSubmitting] = useState(false); + const themeContext = useContext(ThemeContext) as ThemeContextType; +const { mode } = themeContext; - // Handle form submission - const handleSubmit = (e: FormEvent) => { - e.preventDefault(); - setShowPopup(true); // Show the popup after submission + 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); }; - // Handle closing the popup const handleClosePopup = () => { - setShowPopup(false); // Hide the popup + setShowPopup(false); }; - return ( -
-

Contact Us

- - {/* Contact Methods */} -
-
- 📞 Phone -

(123) 456-7890

-
-
- ✉️ Email -

contact@company.com

-
-
+ return ( +
+ {/* Animated background elements */} +
+
+
+
+
- {/* Contact Form */} -
-
- - -
-
- - +
+ {/* Header Section */} +
+
+
+ Logo +
+

+ GitHub Tracker +

-
- - +

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

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

+ Let's Connect +

+

+ 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 ( +
+
+
+ +
+
+

+ {title} +

+

+ {detail} +

+

+ {sub} +

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

+ Send us a Message +

+ +
+
+ {/* Full Name */} +
+ + +
+ + {/* Email */} +
+ + +
+ + {/* Subject */} +
+ + +
+ + {/* Message */} +
+ + +
+
+ + {/* Submit Button */} + +
+
+ {/* last*/ }
- +
- {/* Popup Modal */} + {/* Success Popup Modal */} {showPopup && ( -
-
-

Message Sent!

-

Your message has been successfully sent. We will get back to you soon.

-
+
+
+
+
+ +
+ +

Message Sent Successfully!

+

+ Thank you for reaching out to GitHub Tracker. We've received your message and will get back to you within 24 hours. +

+
diff --git a/src/pages/Contributors/Contributors.tsx b/src/pages/Contributors/Contributors.tsx index 1fea40d..decbe7c 100644 --- a/src/pages/Contributors/Contributors.tsx +++ b/src/pages/Contributors/Contributors.tsx @@ -21,7 +21,6 @@ interface Contributor { contributions: number; html_url: string; } - const ContributorsPage = () => { const [contributors, setContributors] = useState([]); const [loading, setLoading] = useState(true); @@ -32,13 +31,14 @@ const ContributorsPage = () => { const fetchContributors = async () => { try { const response = await axios.get( - "https://api.github.com/repos/mehul-m-prajapati/github_tracker/contributors", { - withCredentials : false + "https://api.github.com/repos/mehul-m-prajapati/github_tracker/contributors", + { + withCredentials: false, } ); setContributors(response.data); } catch (err) { - setError("Failed to fetch contributors. Please try again later."+ err); + setError("Failed to fetch contributors. Please try again later." + err); } finally { setLoading(false); } @@ -54,7 +54,6 @@ const ContributorsPage = () => { ); } - if (error) { return ( @@ -62,80 +61,72 @@ const ContributorsPage = () => { ); } - return ( - - - 🤝 GitHub Contributors - - - {contributors.map((contributor) => ( - - + + + 🤝 GitHub Contributors + + + {contributors.map((contributor) => ( + + - - - - {contributor.login} - - - {contributor.contributions} Contributions - - - Thank you for your valuable contributions to our community! - - - - - - - - ))} - - + boxShadow: "0 4px 10px rgba(0,0,0,0.1)", + transition: "transform 0.3s ease-in-out", + "&:hover": { + transform: "scale(1.05)", // Zoom effect + boxShadow: "0 8px 15px rgba(0,0,0,0.2)", + borderColor: "#C0C0C0", // Change border color on hover + outlineColor: "#B3B3B3", // Change outline color on hover + }, + }} + > + + + + {contributor.login} + + + {contributor.contributions} Contributions + + + Thank you for your valuable contributions to our community! + + + + + + + + ))} + + +
); }; diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index cab0b7d..7645397 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -22,13 +22,13 @@ import { FormControl, InputLabel, } from "@mui/material"; +import { useTheme } from "@mui/material/styles"; import { useGitHubAuth } from "../../hooks/useGitHubAuth"; import { useGitHubData } from "../../hooks/useGitHubData"; import { usePagination } from "../../hooks/usePagination"; const ROWS_PER_PAGE = 10; -// Define the shape of the data received from GitHub interface GitHubItem { id: number; title: string; @@ -40,7 +40,7 @@ interface GitHubItem { } const Home: React.FC = () => { - // Hooks for managing user authentication + const theme = useTheme(); const { username, setUsername, @@ -49,7 +49,6 @@ const Home: React.FC = () => { error: authError, getOctokit, } = useGitHubAuth(); - const octokit = getOctokit(); const { issues, @@ -58,11 +57,9 @@ const Home: React.FC = () => { error: dataError, fetchData, } = useGitHubData(octokit); - const { page, itemsPerPage, handleChangePage, paginateData } = usePagination(ROWS_PER_PAGE); - // State for various filters and tabs const [tab, setTab] = useState(0); const [issueFilter, setIssueFilter] = useState("all"); const [prFilter, setPrFilter] = useState("all"); @@ -71,66 +68,50 @@ const Home: React.FC = () => { const [startDate, setStartDate] = useState(""); const [endDate, setEndDate] = useState(""); - // Handle data submission to fetch GitHub data const handleSubmit = (e: React.FormEvent): void => { e.preventDefault(); fetchData(username); }; - // Format date strings into a readable format - const formatDate = (dateString: string): string => { - return new Date(dateString).toLocaleDateString(); - }; - - // Filter data based on selected criteria - const filterData = ( - data: GitHubItem[], - filterType: string - ): GitHubItem[] => { - let filteredData = [...data]; + const formatDate = (dateString: string): string => + new Date(dateString).toLocaleDateString(); - if (filterType === "open" || filterType === "closed" || filterType === "merged") { - filteredData = filteredData.filter((item) => + const filterData = (data: GitHubItem[], filterType: string): GitHubItem[] => { + let filtered = [...data]; + if (["open", "closed", "merged"].includes(filterType)) { + filtered = filtered.filter((item) => filterType === "merged" ? item.pull_request?.merged_at : item.state === filterType ); } - if (searchTitle) { - filteredData = filteredData.filter((item) => + filtered = filtered.filter((item) => item.title.toLowerCase().includes(searchTitle.toLowerCase()) ); } - if (selectedRepo) { - filteredData = filteredData.filter((item) => + filtered = filtered.filter((item) => item.repository_url.includes(selectedRepo) ); } - if (startDate) { - filteredData = filteredData.filter( + filtered = filtered.filter( (item) => new Date(item.created_at) >= new Date(startDate) ); } if (endDate) { - filteredData = filteredData.filter( + filtered = filtered.filter( (item) => new Date(item.created_at) <= new Date(endDate) ); } - - return filteredData; + return filtered; }; - // Determine the current tab's data const currentData = tab === 0 ? filterData(issues, issueFilter) : filterData(prs, prFilter); - - // Paginate the filtered data const displayData = paginateData(currentData); - // Main UI rendering return ( { flexDirection: "column", minHeight: "78vh", mt: 4, + color: theme.palette.text.primary, }} > - {/* Authentication Form */} - +
{
- {/* Filters Section */} { /> - {/* Tabs and State Dropdown */} { gap: 2, }} > - setTab(newValue)} sx={{ flex: 1 }}> + setTab(v)} sx={{ flex: 1 }}> - State + State