diff --git a/package.json b/package.json
index 5103cb16..d7ac80a8 100644
--- a/package.json
+++ b/package.json
@@ -10,11 +10,16 @@
"preview": "vite preview"
},
"dependencies": {
+ "octokit": "^4.0.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hot-toast": "^2.4.1",
"react-icons": "^5.3.0",
- "react-router-dom": "^6.28.0"
+ "react-router-dom": "^6.28.0",
+ "@emotion/react": "^11.11.3",
+ "@emotion/styled": "^11.11.0",
+ "@mui/icons-material": "^5.15.6",
+ "@mui/material": "^5.15.6"
},
"devDependencies": {
"@eslint/js": "^9.13.0",
diff --git a/src/App.jsx b/src/App.jsx
index d0ed609b..b1b6a612 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -1,4 +1,3 @@
-import React from 'react';
import { Routes, Route, Navigate } from 'react-router-dom';
import Navbar from './components/Navbar';
import Footer from './components/Footer';
diff --git a/src/components/Footer.jsx b/src/components/Footer.jsx
index b86b1380..5389175a 100644
--- a/src/components/Footer.jsx
+++ b/src/components/Footer.jsx
@@ -1,5 +1,4 @@
// src/components/Footer.jsx
-import React from 'react';
import { FaGithub } from 'react-icons/fa'; // Import GitHub icon from react-icons
function Footer() {
diff --git a/src/components/Navbar.jsx b/src/components/Navbar.jsx
index 6fb581b3..a062672b 100644
--- a/src/components/Navbar.jsx
+++ b/src/components/Navbar.jsx
@@ -1,5 +1,4 @@
// src/components/Navbar.jsx
-import React from 'react';
function Navbar() {
return (
diff --git a/src/main.jsx b/src/main.jsx
index 24c9de67..c5ef4972 100644
--- a/src/main.jsx
+++ b/src/main.jsx
@@ -1,4 +1,3 @@
-import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css'; // Import Tailwind CSS
import App from './App';
diff --git a/src/pages/Home/Home.jsx b/src/pages/Home/Home.jsx
index b5093659..897718d8 100644
--- a/src/pages/Home/Home.jsx
+++ b/src/pages/Home/Home.jsx
@@ -1,365 +1,208 @@
-'use client';
-import { useState, useEffect, useRef } from "react";
-
-const Home = () => {
- const [username, setUserName] = useState(""); // Track username input
- const [token, setToken] = useState(""); // Track GitHub token input
- const [userData, setUserData] = useState(null); // Store user profile data
- const [issues, setIssues] = useState([]); // Store issues data
- const [pullRequests, setPullRequests] = useState([]); // Store PR data
- const [loading, setLoading] = useState(false); // Loading state
- const [error, setError] = useState(null); // Error state
- const [issueStatus, setIssueStatus] = useState("all"); // Issue status filter
- const [prStatus, setPrStatus] = useState("all"); // PR status filter
- const [labels, setLabels] = useState(""); // Labels filter
- const [currentPageIssues, setCurrentPageIssues] = useState(1); // Current page for issues
- const [currentPagePRs, setCurrentPagePRs] = useState(1); // Current page for PRs
- const [issuesPerPage, setIssuesPerPage] = useState(5); // Number of issues per page
- const [prPerPage, setPrPerPage] = useState(5); // Number of PRs per page
- const inputRef = useRef(null);
-
- // Fetch user data, issues, and pull requests with pagination
- const fetchData = async () => {
- if (!username || !token) return; // If no username or token, do nothing
-
- setLoading(true); // Set loading to true
- setError(null); // Reset any previous errors
- setUserData(null);
- setIssues([]);
- setPullRequests([]);
+import { Octokit } from '@octokit/core';
+import { createElement as h, useState, useCallback } from 'react';
+import {
+ Container,
+ Box,
+ TextField,
+ Button,
+ Typography,
+ Paper,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+ TablePagination,
+ Link,
+ CircularProgress,
+ Alert,
+ Tabs,
+ Tab,
+ Select,
+ MenuItem,
+ FormControl,
+ InputLabel,
+} from '@mui/material';
+
+function GithubDashboard() {
+ const [username, setUsername] = useState('');
+ const [token, setToken] = useState('');
+ const [issues, setIssues] = useState([]);
+ const [prs, setPrs] = useState([]);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState('');
+ const [tab, setTab] = useState(0);
+ const [page, setPage] = useState(0);
+ const [rowsPerPage] = useState(5);
+ const [issueFilter, setIssueFilter] = useState('all');
+ const [prFilter, setPrFilter] = useState('all');
+
+ const fetchData = useCallback(async () => {
+ if (!username || !token) return;
+
+ setLoading(true);
+ setError('');
try {
- // Fetch user data from GitHub API
- const userResponse = await fetch(`https://api.github.com/users/${username}`, {
- headers: {
- 'Authorization': `token ${token}` // Include token in the request
- }
+ const octokit = new Octokit({ auth: token });
+
+ // Fetch issues
+ const issuesResponse = await octokit.request('GET /search/issues', {
+ q: `author:${username} is:issue`,
+ sort: 'created',
+ order: 'desc',
+ per_page: 100,
});
- if (!userResponse.ok) throw new Error('User not found');
- const userData = await userResponse.json();
- setUserData(userData); // Set the fetched user data
-
- // Fetch repositories from GitHub API
- const reposResponse = await fetch(`https://api.github.com/users/${username}/repos`, {
- headers: {
- 'Authorization': `token ${token}` // Include token in the request
- }
+ // Fetch PRs
+ const prsResponse = await octokit.request('GET /search/issues', {
+ q: `author:${username} is:pr`,
+ sort: 'created',
+ order: 'desc',
+ per_page: 100,
});
- if (!reposResponse.ok) throw new Error('Failed to fetch repositories');
- const reposData = await reposResponse.json();
-
- // Fetch issues and pull requests for each repository
- const allIssues = [];
- const allPullRequests = [];
-
- for (const repo of reposData) {
- // Fetch issues for each repository with pagination
- const issuesResponse = await fetch(`https://api.github.com/repos/${username}/${repo.name}/issues?state=${issueStatus}&labels=${labels}&per_page=${issuesPerPage}&page=${currentPageIssues}`, {
- headers: {
- 'Authorization': `token ${token}` // Include token in the request
- }
- });
- const issuesData = await issuesResponse.json();
-
- allIssues.push(...issuesData);
-
- // Fetch pull requests for each repository with pagination
- const pullsResponse = await fetch(`https://api.github.com/repos/${username}/${repo.name}/pulls?state=${prStatus}&labels=${labels}&per_page=${prPerPage}&page=${currentPagePRs}`, {
- headers: {
- 'Authorization': `token ${token}` // Include token in the request
- }
- });
- const pullsData = await pullsResponse.json();
-
- allPullRequests.push(...pullsData);
- }
-
- setIssues(allIssues); // Set issues data
- setPullRequests(allPullRequests); // Set pull requests data
+ setIssues(issuesResponse.data.items);
+ setPrs(prsResponse.data.items);
} catch (err) {
- setError(err.message); // Set error message if API call fails
+ setError(err.message);
} finally {
- setLoading(false); // Set loading to false after request
+ setLoading(false);
}
- };
-
- // Handle input changes
- const handleUser = (e) => setUserName(e.target.value);
- const handleToken = (e) => setToken(e.target.value);
- const handleIssueStatus = (e) => setIssueStatus(e.target.value);
- const handlePrStatus = (e) => setPrStatus(e.target.value);
- const handleLabelFilter = (e) => setLabels(e.target.value);
+ }, [username, token]);
- const handlePageChangeIssues = (newPage) => {
- setCurrentPageIssues(newPage);
+ const handleSubmit = (e) => {
+ e.preventDefault();
+ fetchData();
};
- const handlePageChangePRs = (newPage) => {
- setCurrentPagePRs(newPage);
+ const handleChangePage = (event, newPage) => {
+ setPage(newPage);
};
- // Handle form submit
- const handleSubmit = (e) => {
- e.preventDefault(); // Prevent page reload
- fetchData(); // Fetch data when form is submitted
+ const formatDate = (dateString) => {
+ return new Date(dateString).toLocaleDateString();
};
- return (
-
- {/* Main Content */}
-
-
- {/* Search Form */}
-
-
- {/* Display user data */}
- {loading && Loading...
}
-
- {error && {error}
}
-
- {userData && (
-
-
User Profile
-

-
{userData.name}
-
{userData.login}
-
{userData.bio}
-
- Visit Profile
-
-
- )}
-
- {/* Display filters */}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {/* Display Issues */}
- {issues.length > 0 && (
-
-
Issues
-
-
-
- | Title |
- Status |
- Labels |
- Link |
-
-
-
- {issues.map(issue => (
-
- | {issue.title} |
- {issue.state} |
-
- {issue.labels.length > 0 ? (
- issue.labels.map(label => (
- {label.name}
- ))
- ) : (
- No labels
- )}
- |
-
-
- View Issue
-
- |
-
- ))}
-
-
-
- {/* Pagination Controls for Issues */}
-
-
- Page {currentPageIssues}
-
-
-
- )}
-
- {/* Display Pull Requests */}
- {pullRequests.length > 0 && (
-
-
Pull Requests
-
-
-
- | Title |
- Status |
- Labels |
- Link |
-
-
-
- {pullRequests.map(pr => (
-
- | {pr.title} |
- {pr.state} |
-
- {pr.labels.length > 0 ? (
- pr.labels.map(label => (
- {label.name}
- ))
- ) : (
- No labels
- )}
- |
-
-
- View PR
-
- |
-
- ))}
-
-
-
- {/* Pagination Controls for PRs */}
-
-
- Page {currentPagePRs}
-
-
-
- )}
-
-
- );
-};
+ const filterData = (data, filterType) => {
+ switch (filterType) {
+ case 'open':
+ return data.filter(item => item.state === 'open');
+ case 'closed':
+ return data.filter(item => item.state === 'closed' && !item.pull_request?.merged_at);
+ case 'merged':
+ return data.filter(item => item.pull_request?.merged_at);
+ default:
+ return data;
+ }
+ };
-export default Home;
+ const currentData = tab === 0
+ ? filterData(issues, issueFilter)
+ : filterData(prs, prFilter);
+ const displayData = currentData.slice(page * rowsPerPage, (page + 1) * rowsPerPage);
+
+ return h(Container, { maxWidth: 'lg', sx: { py: 4 } }, [
+ h(Paper, { elevation: 3, sx: { p: 3, mb: 4 } }, [
+ h(Typography, { variant: 'h4', component: 'h1', gutterBottom: true },
+ 'GitHub Dashboard'
+ ),
+ h('form', { onSubmit: handleSubmit }, [
+ h(Box, { sx: { display: 'flex', gap: 2, mb: 3 } }, [
+ h(TextField, {
+ label: 'GitHub Username',
+ value: username,
+ onChange: (e) => setUsername(e.target.value),
+ required: true,
+ sx: { flex: 1 },
+ }),
+ h(TextField, {
+ label: 'Personal Access Token',
+ value: token,
+ onChange: (e) => setToken(e.target.value),
+ type: 'password',
+ required: true,
+ sx: { flex: 1 },
+ }),
+ h(Button, {
+ type: 'submit',
+ variant: 'contained',
+ sx: { minWidth: '120px' },
+ }, 'Fetch Data'),
+ ]),
+ ]),
+ ]),
+
+ error && h(Alert, { severity: 'error', sx: { mb: 3 } }, error),
+
+ loading ?
+ h(Box, { display: 'flex', justifyContent: 'center', my: 4 },
+ h(CircularProgress)
+ ) :
+ h(Box, null, [
+ h(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2, mb: 3 } }, [
+ h(Tabs, {
+ value: tab,
+ onChange: (e, newValue) => setTab(newValue),
+ sx: { flex: 1 },
+ }, [
+ h(Tab, { label: `Issues (${filterData(issues, issueFilter).length})` }),
+ h(Tab, { label: `Pull Requests (${filterData(prs, prFilter).length})` }),
+ ]),
+ h(FormControl, { sx: { minWidth: 120 } }, [
+ h(InputLabel, null, 'Filter'),
+ h(Select, {
+ value: tab === 0 ? issueFilter : prFilter,
+ onChange: (e) => tab === 0 ? setIssueFilter(e.target.value) : setPrFilter(e.target.value),
+ label: 'Filter',
+ }, [
+ h(MenuItem, { value: 'all' }, 'All'),
+ h(MenuItem, { value: 'open' }, 'Open'),
+ h(MenuItem, { value: 'closed' }, 'Closed'),
+ ...(tab === 1 ? [h(MenuItem, { value: 'merged' }, 'Merged')] : []),
+ ]),
+ ]),
+ ]),
+
+ h(TableContainer, { component: Paper }, [
+ h(Table, null, [
+ h(TableHead, null,
+ h(TableRow, null, [
+ h(TableCell, null, 'Title'),
+ h(TableCell, null, 'Repository'),
+ h(TableCell, null, 'State'),
+ h(TableCell, null, 'Created'),
+ ])
+ ),
+ h(TableBody, null,
+ displayData.map((item) =>
+ h(TableRow, { key: item.id }, [
+ h(TableCell, null,
+ h(Link, {
+ href: item.html_url,
+ target: '_blank',
+ rel: 'noopener noreferrer',
+ }, item.title)
+ ),
+ h(TableCell, null, item.repository_url.split('/').slice(-1)[0]),
+ h(TableCell, null, item.pull_request?.merged_at ? 'merged' : item.state),
+ h(TableCell, null, formatDate(item.created_at)),
+ ])
+ )
+ ),
+ ]),
+ h(TablePagination, {
+ component: 'div',
+ count: currentData.length,
+ page,
+ onPageChange: handleChangePage,
+ rowsPerPage,
+ rowsPerPageOptions: [5],
+ }),
+ ]),
+ ]),
+ ]);
+}
+
+export default GithubDashboard;