diff --git a/backend/.env.example b/backend/.env.sample similarity index 65% rename from backend/.env.example rename to backend/.env.sample index 98f9688..269e5b8 100644 --- a/backend/.env.example +++ b/backend/.env.sample @@ -1,3 +1,3 @@ PORT=5000 MONGO_URI=mongodb://localhost:27017/githubTracker -SESSION_SECRET=your-secret-key +SESSION_SECRET=your-secret-key \ No newline at end of file diff --git a/backend/package.json b/backend/package.json index 53bd64b..4b929b3 100644 --- a/backend/package.json +++ b/backend/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "main": "server.js", "scripts": { - "start": "nodemon server.js", + "start": "nodemon server.js", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], diff --git a/backend/server.js b/backend/server.js index 3f19f00..4aa453d 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,39 +1,44 @@ -const express = require('express'); -const mongoose = require('mongoose'); -const session = require('express-session'); -const passport = require('passport'); -const bodyParser = require('body-parser'); -require('dotenv').config(); -const cors = require('cors'); +const express = require("express"); +const mongoose = require("mongoose"); +const session = require("express-session"); +const passport = require("passport"); +const bodyParser = require("body-parser"); +require("dotenv").config(); +const cors = require("cors"); // Passport configuration -require('./config/passportConfig'); +require("./config/passportConfig"); const app = express(); // CORS configuration -app.use(cors('*')); +app.use(cors("*")); // Middleware app.use(bodyParser.json()); -app.use(session({ +app.use( + session({ secret: process.env.SESSION_SECRET, resave: false, saveUninitialized: false, -})); + }) +); app.use(passport.initialize()); app.use(passport.session()); // Routes -const authRoutes = require('./routes/auth'); -app.use('/api/auth', authRoutes); +const authRoutes = require("./routes/auth"); +app.use("/api/auth", authRoutes); // Connect to MongoDB -mongoose.connect(process.env.MONGO_URI, {}).then(() => { - console.log('Connected to MongoDB'); +mongoose + .connect(process.env.MONGO_URI, {}) + .then(() => { + console.log("Connected to MongoDB"); app.listen(process.env.PORT, () => { - console.log(`Server running on port ${process.env.PORT}`); + console.log(`Server running on port ${process.env.PORT}`); }); -}).catch((err) => { - console.log('MongoDB connection error:', err); -}); + }) + .catch((err) => { + console.log("MongoDB connection error:", err); + }); diff --git a/package.json b/package.json index ca7db86..390fffb 100644 --- a/package.json +++ b/package.json @@ -40,4 +40,5 @@ "tailwindcss": "^3.4.14", "vite": "^5.4.10" } + } diff --git a/src/Routes/Login/Login.tsx b/src/Routes/Login/Login.tsx index 1c1f495..4b853aa 100644 --- a/src/Routes/Login/Login.tsx +++ b/src/Routes/Login/Login.tsx @@ -1,6 +1,7 @@ import React, { useState, ChangeEvent, FormEvent } from "react"; import axios from "axios"; import { useNavigate } from "react-router-dom"; // Import the hook for navigation +import toast from "react-hot-toast"; const backendUrl = import.meta.env.VITE_BACKEND_URL; interface LoginFormData { @@ -9,7 +10,10 @@ interface LoginFormData { } const Login: React.FC = () => { - const [formData, setFormData] = useState({ email: "", password: "" }); + const [formData, setFormData] = useState({ + email: "", + password: "", + }); const [message, setMessage] = useState(""); const navigate = useNavigate(); // Initialize the navigate hook @@ -22,18 +26,18 @@ const Login: React.FC = () => { const handleSubmit = async (e: FormEvent) => { e.preventDefault(); try { - const response = await axios.post(`${backendUrl}/api/auth/login`, - formData, - + const response = await axios.post( + `${backendUrl}/api/auth/login`, + formData ); - setMessage(response.data.message); // Show success message from backend + toast.success(response.data.message); // Show success message from backend // Navigate to /home if login is successful - if (response.data.message === 'Login successful') { + if (response.data.message === "Login successful") { navigate("/home"); } } catch (error: any) { - setMessage(error.response?.data?.message || "Something went wrong"); + toast.error(error.response?.data?.message || "Something went wrong"); } }; @@ -70,10 +74,9 @@ const Login: React.FC = () => { Login - {message &&

{message}

} + {/* {message &&

{message}

} */} ); }; export default Login; - diff --git a/src/Routes/Signup/Signup.tsx b/src/Routes/Signup/Signup.tsx index 40edaa8..160d312 100644 --- a/src/Routes/Signup/Signup.tsx +++ b/src/Routes/Signup/Signup.tsx @@ -1,6 +1,7 @@ import React, { useState, ChangeEvent, FormEvent } from "react"; import axios from "axios"; import { useNavigate } from "react-router-dom"; +import toast from "react-hot-toast"; const backendUrl = import.meta.env.VITE_BACKEND_URL; interface SignUpFormData { @@ -10,7 +11,11 @@ interface SignUpFormData { } const SignUp: React.FC = () => { - const [formData, setFormData] = useState({ username: "", email: "", password: "" }); + const [formData, setFormData] = useState({ + username: "", + email: "", + password: "", + }); const [message, setMessage] = useState(""); const navigate = useNavigate(); @@ -27,14 +32,17 @@ const SignUp: React.FC = () => { `${backendUrl}/api/auth/signup`, formData // Include cookies for session ); - setMessage(response.data.message); // Show success message from backend + toast.success(response.data.message); // Show success message from backend // Navigate to login page after successful signup - if (response.data.message === 'User created successfully') { + if (response.data.message === "User created successfully") { navigate("/login"); } } catch (error: any) { - setMessage(error.response?.data?.message || "Something went wrong"); + if (error.status === 404) { + toast.error("User not found"); + } else + toast.error(error.response?.data?.message || "Something went wrong"); } }; @@ -82,7 +90,7 @@ const SignUp: React.FC = () => { Sign Up - {message &&

{message}

} + {/* {message &&

{message}

} */} ); }; diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index f7ea2f2..71c4b21 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -1,5 +1,5 @@ -import { Link } from 'react-router-dom'; -import { useState } from 'react'; +import { Link } from "react-router-dom"; +import { useState } from "react"; const Navbar: React.FC = () => { const [isOpen, setIsOpen] = useState(false); @@ -42,7 +42,17 @@ const Navbar: React.FC = () => { Login + > + Login + + + {/* signup button add */} + + Signup + {/* Mobile Menu Button */} @@ -102,6 +112,21 @@ const Navbar: React.FC = () => { > Contributors + {/* signup and login button add on mobile screen */} + setIsOpen(false)} + > + Login + + setIsOpen(false)} + > + Signup + )} diff --git a/src/hooks/useGitHubAuth.ts b/src/hooks/useGitHubAuth.ts index 4ac6139..5d6c0e7 100644 --- a/src/hooks/useGitHubAuth.ts +++ b/src/hooks/useGitHubAuth.ts @@ -1,5 +1,6 @@ import { useState } from 'react'; import { Octokit } from '@octokit/core'; +import toast from 'react-hot-toast'; export const useGitHubAuth = () => { const [username, setUsername] = useState(''); @@ -7,7 +8,8 @@ export const useGitHubAuth = () => { const [error, setError] = useState(''); const getOctokit = () => { - if (!username || !token) return null; + + if (!username || !token) return; return new Octokit({ auth: token }); }; diff --git a/src/hooks/useGitHubData.ts b/src/hooks/useGitHubData.ts index 9add583..72c60b7 100644 --- a/src/hooks/useGitHubData.ts +++ b/src/hooks/useGitHubData.ts @@ -1,4 +1,5 @@ import { useState, useCallback } from 'react'; +import toast from 'react-hot-toast'; export const useGitHubData = (octokit) => { const [issues, setIssues] = useState([]); @@ -22,8 +23,9 @@ export const useGitHubData = (octokit) => { }; const fetchData = useCallback(async (username) => { - if (!octokit || !username) return; - + if (!octokit || !username) { + toast.error("PAT not found"); return + } setLoading(true); setError(''); @@ -42,11 +44,26 @@ export const useGitHubData = (octokit) => { per_page: 100, }), ]); - setIssues(issuesResponse); setPrs(prsResponse); - } catch (err) { - setError(err.message); + + if (issuesResponse?.length || prsResponse?.length) toast.success( + `Fetched ${issuesResponse.length} issues and ${prsResponse.length} PRs successfully.` + ); + + else toast("⚠️ No issues or PRs found", { icon: "🕵️‍♂️" }); + + + + } catch (err: any) { + if (err.status === 401 || err.status === 403) { + toast.error("Please provide a valid GitHub token "); + } + else if (err.status === 422) toast.error("User not found") + else { + toast.error("something went wrong while fetching data") + } + // setError("Failed to fetched github date") } finally { setLoading(false); } @@ -56,7 +73,7 @@ export const useGitHubData = (octokit) => { issues, prs, loading, - error, + // error, fetchData, }; }; \ No newline at end of file diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index cab0b7d..548708b 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -74,7 +74,8 @@ const Home: React.FC = () => { // Handle data submission to fetch GitHub data const handleSubmit = (e: React.FormEvent): void => { e.preventDefault(); - fetchData(username); + const datafetch = fetchData(username); + console.log("fetch user detail: ", datafetch); }; // Format date strings into a readable format @@ -83,13 +84,14 @@ const Home: React.FC = () => { }; // Filter data based on selected criteria - const filterData = ( - data: GitHubItem[], - filterType: string - ): GitHubItem[] => { + const filterData = (data: GitHubItem[], filterType: string): GitHubItem[] => { let filteredData = [...data]; - if (filterType === "open" || filterType === "closed" || filterType === "merged") { + if ( + filterType === "open" || + filterType === "closed" || + filterType === "merged" + ) { filteredData = filteredData.filter((item) => filterType === "merged" ? item.pull_request?.merged_at @@ -160,7 +162,11 @@ const Home: React.FC = () => { required sx={{ flex: 1 }} /> - @@ -210,12 +216,18 @@ const Home: React.FC = () => { gap: 2, }} > - setTab(newValue)} sx={{ flex: 1 }}> + setTab(newValue)} + sx={{ flex: 1 }} + > - State + + State +