From a28685d789130746cfd75c0d1ea74b0a4cbc61a4 Mon Sep 17 00:00:00 2001 From: darkhorse-420 Date: Sun, 27 Oct 2024 01:20:37 +0530 Subject: [PATCH 01/13] feat: pagination component added it to repository as well --- src/ui/components/Pagination/Pagination.css | 28 +++ src/ui/components/Pagination/Pagination.jsx | 37 ++++ .../RepoList/Components/RepoOverview.jsx | 207 ++++++++++-------- src/ui/views/UserList/Components/UserList.jsx | 1 + 4 files changed, 179 insertions(+), 94 deletions(-) create mode 100644 src/ui/components/Pagination/Pagination.css create mode 100644 src/ui/components/Pagination/Pagination.jsx diff --git a/src/ui/components/Pagination/Pagination.css b/src/ui/components/Pagination/Pagination.css new file mode 100644 index 000000000..fbaa650cf --- /dev/null +++ b/src/ui/components/Pagination/Pagination.css @@ -0,0 +1,28 @@ +.paginationContainer { + display: flex; + justify-content: center; + padding: 1rem; + margin-top: 20px; + gap: 10px; +} + +.pageButton { + padding: 8px 12px; + font-size: 14px; + color: #333; + border: 1px solid #ccc; + background-color: #f9f9f9; + cursor: pointer; + border-radius: 5px; + transition: background-color 0.3s ease; +} + +.pageButton:hover { + background-color: #e2e6ea; +} + +.activeButton { + background-color: #007bff; + color: #fff; + border-color: #007bff; +} diff --git a/src/ui/components/Pagination/Pagination.jsx b/src/ui/components/Pagination/Pagination.jsx new file mode 100644 index 000000000..0abaa574b --- /dev/null +++ b/src/ui/components/Pagination/Pagination.jsx @@ -0,0 +1,37 @@ +import React from 'react'; + + +export default function Pagination({ currentPage, totalItems = 0, itemsPerPage, onPageChange }) { + // Calculate the total number of pages + const totalPages = Math.ceil(totalItems / itemsPerPage); + + const handlePageClick = (page) => { + if (page >= 1 && page <= totalPages) { + onPageChange(page); + } + }; + + return ( +
+ + + + Page {currentPage} of {totalPages} + + + +
+ ); +} diff --git a/src/ui/views/RepoList/Components/RepoOverview.jsx b/src/ui/views/RepoList/Components/RepoOverview.jsx index da2470746..9ba573f53 100644 --- a/src/ui/views/RepoList/Components/RepoOverview.jsx +++ b/src/ui/views/RepoList/Components/RepoOverview.jsx @@ -1,10 +1,11 @@ -import React, { useEffect } from 'react'; +import React, { useState } from 'react'; import TableCell from '@material-ui/core/TableCell'; import TableRow from '@material-ui/core/TableRow'; import GridContainer from '../../../components/Grid/GridContainer'; import GridItem from '../../../components/Grid/GridItem'; import { CodeReviewIcon, LawIcon, PeopleIcon } from '@primer/octicons-react'; + const colors = { '1C Enterprise': '#814CCC', '2-Dimensional Array': '#38761D', @@ -565,110 +566,128 @@ const colors = { Zimpl: '#d67711', }; -import axios from 'axios'; +// import axios from 'axios'; import moment from 'moment'; import CodeActionButton from '../../../components/CustomButtons/CodeActionButton'; +import Pagination from '../../../components/Pagination/Pagination'; export default function Repositories(props) { - const [github, setGitHub] = React.useState({}); + // const [github, setGitHub] = React.useState({}); + // const [repositories, setRepositories] = useState([]); // To hold all repositories + const [currentPage, setCurrentPage] = useState(1); + const itemsPerPage = 5; + + + // Dummy repository data + const dummyRepositories = [ + { project: 'Org1', name: 'Repo1', description: 'Description for Repo 1' }, + { project: 'Org2', name: 'Repo2', description: 'Description for Repo 2' }, + { project: 'Org3', name: 'Repo3', description: 'Description for Repo 3' }, + { project: 'Org4', name: 'Repo4', description: 'Description for Repo 4' }, + { project: 'Org5', name: 'Repo5', description: 'Description for Repo 5' }, + { project: 'Org6', name: 'Repo6', description: 'Description for Repo 6' }, + // Add more dummy repositories as needed + ]; + - useEffect(() => { - getGitHubRepository(); - }, [props.data.project, props.data.name]); + // useEffect(() => { + // getGitHubRepository(); + // }, [props.data.project, props.data.name]); - const getGitHubRepository = async () => { - await axios - .get(`https://api.github.com/repos/${props.data.project}/${props.data.name}`) - .then((res) => { - setGitHub(res.data); - }); + // const getGitHubRepository = async () => { + // try { + // const res = await axios.get( + // `https://api.github.com/repos/${props.data.project}/${props.data.name}` + // ); + // setRepositories([res.data]); // Store the single repository in an array + // } catch (error) { + // console.error("Failed to fetch repository", error); + // } + // }; + + + // Pagination logic to get current items for the page + const indexOfLastItem = currentPage * itemsPerPage; + const indexOfFirstItem = indexOfLastItem - itemsPerPage; + const currentItems = dummyRepositories.slice(indexOfFirstItem, indexOfLastItem); + + const handlePageChange = (page) => { + setCurrentPage(page); }; const { project: org, name } = props?.data || {}; const cloneURL = `${window.location.origin.toString()}/${org}/${name}.git`; return ( - - -
- - - {props.data.project}/{props.data.name} - - - {github.parent && ( - - Forked from{' '} - - {github.parent.full_name} + <> + {currentItems.map((repo, index) => ( + + +
+ + + {repo.project}/{repo.name} + - - )} - {github.description &&

{github.description}

} - - {github.language && ( - - - {github.language} - - )} - {github.license && ( - - {' '} - {github.license.spdx_id} - - )} - - {' '} - {props.data.users?.canPush?.length || 0} - - - {' '} - - {props.data.users?.canAuthorise?.length || 0} - - - {(github.created_at || github.updated_at || github.pushed_at) && ( - - Last updated{' '} - {moment - .max([ - moment(github.created_at), - moment(github.updated_at), - moment(github.pushed_at), - ]) - .fromNow()} - - )} - -
- - -
- -
-
- + {repo.parent && ( + + Forked from{' '} + + {repo.parent.full_name} + + + )} + {repo.description &&

{repo.description}

} + + {repo.language && ( + + + {repo.language} + + )} + {repo.license && ( + + {repo.license.spdx_id} + + )} + + {props.data.users?.canPush?.length || 0} + + + {props.data.users?.canAuthorise?.length || 0} + + {(repo.created_at || repo.updated_at || repo.pushed_at) && ( + + Last updated {moment.max([moment(repo.created_at), moment(repo.updated_at), moment(repo.pushed_at)]).fromNow()} + + )} + +
+
+ +
+ +
+
+
+ ))} + + {/* Render the Pagination component */} + + ); -} +} \ No newline at end of file diff --git a/src/ui/views/UserList/Components/UserList.jsx b/src/ui/views/UserList/Components/UserList.jsx index b1273cb58..2b993dd3d 100644 --- a/src/ui/views/UserList/Components/UserList.jsx +++ b/src/ui/views/UserList/Components/UserList.jsx @@ -15,6 +15,7 @@ import styles from '../../../assets/jss/material-dashboard-react/views/dashboard import { getUsers } from '../../../services/user'; import { CloseRounded, Check, KeyboardArrowRight } from '@material-ui/icons'; +// import Pagination from '../../../components/Pagination/Pagination'; export default function UserList(props) { const useStyles = makeStyles(styles); From d33e21691e23858785645a504354f97e18d1df2f Mon Sep 17 00:00:00 2001 From: darkhorse-420 Date: Sun, 27 Oct 2024 01:23:38 +0530 Subject: [PATCH 02/13] feat: pagination styling --- src/ui/components/Pagination/Pagination.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/components/Pagination/Pagination.jsx b/src/ui/components/Pagination/Pagination.jsx index 0abaa574b..7c119f17c 100644 --- a/src/ui/components/Pagination/Pagination.jsx +++ b/src/ui/components/Pagination/Pagination.jsx @@ -1,5 +1,5 @@ import React from 'react'; - +import './Pagination.css'; export default function Pagination({ currentPage, totalItems = 0, itemsPerPage, onPageChange }) { // Calculate the total number of pages From 52d59c1df2563586eb131c2212d4a49e572f82aa Mon Sep 17 00:00:00 2001 From: darkhorse-420 Date: Sun, 27 Oct 2024 22:20:29 +0530 Subject: [PATCH 03/13] feat: pagination in userList(with dummy values) --- .../RepoList/Components/RepoOverview.jsx | 2 +- src/ui/views/UserList/Components/UserList.jsx | 86 ++++++++++--------- 2 files changed, 45 insertions(+), 43 deletions(-) diff --git a/src/ui/views/RepoList/Components/RepoOverview.jsx b/src/ui/views/RepoList/Components/RepoOverview.jsx index 9ba573f53..00e207391 100644 --- a/src/ui/views/RepoList/Components/RepoOverview.jsx +++ b/src/ui/views/RepoList/Components/RepoOverview.jsx @@ -578,7 +578,7 @@ export default function Repositories(props) { const itemsPerPage = 5; - // Dummy repository data + // Dummy repository data``````````` const dummyRepositories = [ { project: 'Org1', name: 'Repo1', description: 'Description for Repo 1' }, { project: 'Org2', name: 'Repo2', description: 'Description for Repo 2' }, diff --git a/src/ui/views/UserList/Components/UserList.jsx b/src/ui/views/UserList/Components/UserList.jsx index 2b993dd3d..f2e76ada3 100644 --- a/src/ui/views/UserList/Components/UserList.jsx +++ b/src/ui/views/UserList/Components/UserList.jsx @@ -1,8 +1,7 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState } from 'react'; import { makeStyles } from '@material-ui/core/styles'; import GridItem from '../../../components/Grid/GridItem'; import GridContainer from '../../../components/Grid/GridContainer'; -import { useNavigate } from 'react-router-dom'; import Button from '@material-ui/core/Button'; import Table from '@material-ui/core/Table'; import TableBody from '@material-ui/core/TableBody'; @@ -11,35 +10,43 @@ import TableContainer from '@material-ui/core/TableContainer'; import TableHead from '@material-ui/core/TableHead'; import TableRow from '@material-ui/core/TableRow'; import Paper from '@material-ui/core/Paper'; -import styles from '../../../assets/jss/material-dashboard-react/views/dashboardStyle'; -import { getUsers } from '../../../services/user'; - import { CloseRounded, Check, KeyboardArrowRight } from '@material-ui/icons'; -// import Pagination from '../../../components/Pagination/Pagination'; +import Pagination from '../../../components/Pagination/Pagination'; + +const useStyles = makeStyles({ + table: { + minWidth: 650, + }, +}); -export default function UserList(props) { - const useStyles = makeStyles(styles); +export default function UserList() { const classes = useStyles(); - const [data, setData] = useState([]); - const [, setAuth] = useState(true); - const [isLoading, setIsLoading] = useState(false); - const [isError, setIsError] = useState(false); - const navigate = useNavigate(); - const openUser = (username) => navigate(`/admin/user/${username}`, { replace: true }); + // Dummy user data + const dummyUsers = [ + { username: 'johnDoe', displayName: 'John Doe', title: 'Developer', email: 'john@example.com', gitAccount: 'johnDoeGit', admin: true }, + { username: 'janeDoe', displayName: 'Jane Doe', title: 'Designer', email: 'jane@example.com', gitAccount: 'janeDoeGit', admin: false }, + { username: 'markSmith', displayName: 'Mark Smith', title: 'Project Manager', email: 'mark@example.com', gitAccount: 'markSmithGit', admin: true }, + { username: 'lucasBrown', displayName: 'Lucas Brown', title: 'Data Scientist', email: 'lucas@example.com', gitAccount: 'lucasBrownGit', admin: false }, + { username: 'emilyWhite', displayName: 'Emily White', title: 'Backend Engineer', email: 'emily@example.com', gitAccount: 'emilyWhiteGit', admin: true }, + { username: 'oliviaGreen', displayName: 'Olivia Green', title: 'Frontend Developer', email: 'olivia@example.com', gitAccount: 'oliviaGreenGit', admin: false }, + { username: 'noahBlue', displayName: 'Noah Blue', title: 'DevOps Engineer', email: 'noah@example.com', gitAccount: 'noahBlueGit', admin: true }, + { username: 'miaBlack', displayName: 'Mia Black', title: 'Quality Analyst', email: 'mia@example.com', gitAccount: 'miaBlackGit', admin: false }, + { username: 'willGray', displayName: 'Will Gray', title: 'HR Manager', email: 'will@example.com', gitAccount: 'willGrayGit', admin: false }, + { username: 'avaYellow', displayName: 'Ava Yellow', title: 'UX Designer', email: 'ava@example.com', gitAccount: 'avaYellowGit', admin: true }, + ]; - useEffect(() => { - const query = {}; + // Pagination states + const [currentPage, setCurrentPage] = useState(1); + const itemsPerPage = 5; // Set the number of items per page - for (const k in props) { - if (!k) continue; - query[k] = props[k]; - } - getUsers(setIsLoading, setData, setAuth, setIsError, query); - }, [props]); + // Calculate the items for the current page + const indexOfLastItem = currentPage * itemsPerPage; + const indexOfFirstItem = indexOfLastItem - itemsPerPage; + const currentItems = dummyUsers.slice(indexOfFirstItem, indexOfLastItem); + const totalItems = dummyUsers.length; - if (isLoading) return
Loading...
; - if (isError) return
Something went wrong...
; + const handlePageChange = (page) => setCurrentPage(page); return ( @@ -57,7 +64,7 @@ export default function UserList(props) { - {data.map((row) => ( + {currentItems.map((row) => ( {row.displayName} {row.title} @@ -65,29 +72,15 @@ export default function UserList(props) { {row.email} - + {row.gitAccount} - {row.admin ? ( - - - - ) : ( - - )} + {row.admin ? : } - @@ -96,7 +89,16 @@ export default function UserList(props) { + + {/* Pagination Component */} + ); } + From 627043267b051476e92cb2b19e1643477427a74d Mon Sep 17 00:00:00 2001 From: darkhorse-420 Date: Mon, 28 Oct 2024 00:20:57 +0530 Subject: [PATCH 04/13] feat: search component users repositories --- src/ui/components/Search/Search.css | 39 +++++++++++++++++ src/ui/components/Search/Search.jsx | 42 +++++++++++++++++++ .../RepoList/Components/RepoOverview.jsx | 27 +++++++++++- src/ui/views/UserList/Components/UserList.jsx | 31 ++++++++++++-- 4 files changed, 133 insertions(+), 6 deletions(-) create mode 100644 src/ui/components/Search/Search.css create mode 100644 src/ui/components/Search/Search.jsx diff --git a/src/ui/components/Search/Search.css b/src/ui/components/Search/Search.css new file mode 100644 index 000000000..2c3f8bfce --- /dev/null +++ b/src/ui/components/Search/Search.css @@ -0,0 +1,39 @@ +/* Search.css */ + +.search-container { + display: flex; + align-items: center; + margin-bottom: 10px; + margin-top: 10px; + margin-right: 10 px; +} + +.search-input { + padding: 10px; + margin-right: 8px; + border: 1px solid #ccc; + border-radius: 10px; + width: 200px; /* Adjust width as needed */ + font-size: 14px; + transition: border-color 0.3s; +} + +.search-input:focus { + border-color: #007bff; /* Change to your desired focus color */ + outline: none; /* Remove default outline */ +} + +.search-button { + padding: 10px 16px; + border: none; + border-radius: 10px; + background-color: #007bff; /* Change to your desired button color */ + color: white; + cursor: pointer; + font-size: 14px; + transition: background-color 0.3s; +} + +.search-button:hover { + background-color: #0056b3; /* Darker shade for hover effect */ +} diff --git a/src/ui/components/Search/Search.jsx b/src/ui/components/Search/Search.jsx new file mode 100644 index 000000000..d36c4d39f --- /dev/null +++ b/src/ui/components/Search/Search.jsx @@ -0,0 +1,42 @@ +import React, { useState } from 'react'; +import PropTypes from 'prop-types'; +import './Search.css'; // Import the CSS file + +export default function Search({ placeholder = 'Search...', onSearch }) { + const [query, setQuery] = useState(''); + + const handleInputChange = (e) => { + setQuery(e.target.value); + }; + + const handleSearch = () => { + onSearch(query); + }; + + const handleKeyDown = (event) => { + if (event.key === 'Enter') { + handleSearch(); // Trigger search on Enter + } + }; + + return ( +
+ + +
+ ); +} + +Search.propTypes = { + placeholder: PropTypes.string, + onSearch: PropTypes.func.isRequired, +}; diff --git a/src/ui/views/RepoList/Components/RepoOverview.jsx b/src/ui/views/RepoList/Components/RepoOverview.jsx index 00e207391..da3ddf9c4 100644 --- a/src/ui/views/RepoList/Components/RepoOverview.jsx +++ b/src/ui/views/RepoList/Components/RepoOverview.jsx @@ -6,6 +6,8 @@ import GridItem from '../../../components/Grid/GridItem'; import { CodeReviewIcon, LawIcon, PeopleIcon } from '@primer/octicons-react'; + + const colors = { '1C Enterprise': '#814CCC', '2-Dimensional Array': '#38761D', @@ -570,11 +572,13 @@ const colors = { import moment from 'moment'; import CodeActionButton from '../../../components/CustomButtons/CodeActionButton'; import Pagination from '../../../components/Pagination/Pagination'; +import Search from '../../../components/Search/Search'; export default function Repositories(props) { // const [github, setGitHub] = React.useState({}); // const [repositories, setRepositories] = useState([]); // To hold all repositories const [currentPage, setCurrentPage] = useState(1); + const [searchQuery, setSearchQuery] = useState(''); const itemsPerPage = 5; @@ -606,10 +610,23 @@ export default function Repositories(props) { // }; +// Handle search functionality +const handleSearch = (query) => { + setSearchQuery(query); + setCurrentPage(1); // Reset to first page when searching +}; + +// Filter repositories based on search query +const filteredRepositories = dummyRepositories.filter(repo => + repo.name.toLowerCase().includes(searchQuery.toLowerCase()) +); + + + // Pagination logic to get current items for the page const indexOfLastItem = currentPage * itemsPerPage; const indexOfFirstItem = indexOfLastItem - itemsPerPage; - const currentItems = dummyRepositories.slice(indexOfFirstItem, indexOfLastItem); + const currentItems = filteredRepositories.slice(indexOfFirstItem, indexOfLastItem); const handlePageChange = (page) => { setCurrentPage(page); @@ -620,6 +637,10 @@ export default function Repositories(props) { return ( <> + + + + {currentItems.map((repo, index) => ( @@ -684,10 +705,12 @@ export default function Repositories(props) { {/* Render the Pagination component */} + + ); } \ No newline at end of file diff --git a/src/ui/views/UserList/Components/UserList.jsx b/src/ui/views/UserList/Components/UserList.jsx index f2e76ada3..e2e77852b 100644 --- a/src/ui/views/UserList/Components/UserList.jsx +++ b/src/ui/views/UserList/Components/UserList.jsx @@ -12,6 +12,8 @@ import TableRow from '@material-ui/core/TableRow'; import Paper from '@material-ui/core/Paper'; import { CloseRounded, Check, KeyboardArrowRight } from '@material-ui/icons'; import Pagination from '../../../components/Pagination/Pagination'; +import Search from '../../../components/Search/Search'; + const useStyles = makeStyles({ table: { @@ -38,19 +40,40 @@ export default function UserList() { // Pagination states const [currentPage, setCurrentPage] = useState(1); - const itemsPerPage = 5; // Set the number of items per page + const itemsPerPage = 5; + const [searchQuery, setSearchQuery] = useState(''); // Calculate the items for the current page const indexOfLastItem = currentPage * itemsPerPage; const indexOfFirstItem = indexOfLastItem - itemsPerPage; - const currentItems = dummyUsers.slice(indexOfFirstItem, indexOfLastItem); - const totalItems = dummyUsers.length; - const handlePageChange = (page) => setCurrentPage(page); + // Filter users based on the search query + const filteredUsers = dummyUsers.filter(user => + user.displayName.toLowerCase().includes(searchQuery.toLowerCase()) || + user.username.toLowerCase().includes(searchQuery.toLowerCase()) +); + +const currentItems = filteredUsers.slice(indexOfFirstItem, indexOfLastItem); +const totalItems = filteredUsers.length; + // Function to handle page change + const handlePageChange = (page) => { + setCurrentPage(page); + }; + + + // Function to handle search + const handleSearch = (query) => { + setSearchQuery(query); + setCurrentPage(1); // Reset to the first page when searching + }; return ( + + {/* Search Component */} + + From b5aabf758f38e36701f9883def57f635bcb27f1b Mon Sep 17 00:00:00 2001 From: darkhorse-420 Date: Wed, 6 Nov 2024 17:46:02 +0530 Subject: [PATCH 05/13] fix: search and pagination --- .../RepoList/Components/RepoOverview.jsx | 227 +++++++----------- .../RepoList/Components/Repositories.jsx | 64 ++++- src/ui/views/UserList/Components/UserList.jsx | 86 ++++--- 3 files changed, 206 insertions(+), 171 deletions(-) diff --git a/src/ui/views/RepoList/Components/RepoOverview.jsx b/src/ui/views/RepoList/Components/RepoOverview.jsx index da3ddf9c4..b912c5d9d 100644 --- a/src/ui/views/RepoList/Components/RepoOverview.jsx +++ b/src/ui/views/RepoList/Components/RepoOverview.jsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useEffect } from 'react'; import TableCell from '@material-ui/core/TableCell'; import TableRow from '@material-ui/core/TableRow'; import GridContainer from '../../../components/Grid/GridContainer'; @@ -6,8 +6,6 @@ import GridItem from '../../../components/Grid/GridItem'; import { CodeReviewIcon, LawIcon, PeopleIcon } from '@primer/octicons-react'; - - const colors = { '1C Enterprise': '#814CCC', '2-Dimensional Array': '#38761D', @@ -568,149 +566,110 @@ const colors = { Zimpl: '#d67711', }; -// import axios from 'axios'; +import axios from 'axios'; import moment from 'moment'; import CodeActionButton from '../../../components/CustomButtons/CodeActionButton'; -import Pagination from '../../../components/Pagination/Pagination'; -import Search from '../../../components/Search/Search'; export default function Repositories(props) { - // const [github, setGitHub] = React.useState({}); - // const [repositories, setRepositories] = useState([]); // To hold all repositories - const [currentPage, setCurrentPage] = useState(1); - const [searchQuery, setSearchQuery] = useState(''); - const itemsPerPage = 5; - - - // Dummy repository data``````````` - const dummyRepositories = [ - { project: 'Org1', name: 'Repo1', description: 'Description for Repo 1' }, - { project: 'Org2', name: 'Repo2', description: 'Description for Repo 2' }, - { project: 'Org3', name: 'Repo3', description: 'Description for Repo 3' }, - { project: 'Org4', name: 'Repo4', description: 'Description for Repo 4' }, - { project: 'Org5', name: 'Repo5', description: 'Description for Repo 5' }, - { project: 'Org6', name: 'Repo6', description: 'Description for Repo 6' }, - // Add more dummy repositories as needed - ]; - - - // useEffect(() => { - // getGitHubRepository(); - // }, [props.data.project, props.data.name]); - - // const getGitHubRepository = async () => { - // try { - // const res = await axios.get( - // `https://api.github.com/repos/${props.data.project}/${props.data.name}` - // ); - // setRepositories([res.data]); // Store the single repository in an array - // } catch (error) { - // console.error("Failed to fetch repository", error); - // } - // }; - - -// Handle search functionality -const handleSearch = (query) => { - setSearchQuery(query); - setCurrentPage(1); // Reset to first page when searching -}; - -// Filter repositories based on search query -const filteredRepositories = dummyRepositories.filter(repo => - repo.name.toLowerCase().includes(searchQuery.toLowerCase()) -); - + const [github, setGitHub] = React.useState({}); + useEffect(() => { + getGitHubRepository(); + }, [props.data.project, props.data.name]); - // Pagination logic to get current items for the page - const indexOfLastItem = currentPage * itemsPerPage; - const indexOfFirstItem = indexOfLastItem - itemsPerPage; - const currentItems = filteredRepositories.slice(indexOfFirstItem, indexOfLastItem); - - const handlePageChange = (page) => { - setCurrentPage(page); + const getGitHubRepository = async () => { + await axios + .get(`https://api.github.com/repos/${props.data.project}/${props.data.name}`) + .then((res) => { + setGitHub(res.data); + }); }; const { project: org, name } = props?.data || {}; const cloneURL = `${window.location.origin.toString()}/${org}/${name}.git`; return ( - <> - - - - - {currentItems.map((repo, index) => ( - - -
- - - {repo.project}/{repo.name} - + + +
+ + + {props.data.project}/{props.data.name} + + + {github.parent && ( + + Forked from{' '} + + {github.parent.full_name} - {repo.parent && ( - - Forked from{' '} - - {repo.parent.full_name} - - - )} - {repo.description &&

{repo.description}

} - - {repo.language && ( - - - {repo.language} - - )} - {repo.license && ( - - {repo.license.spdx_id} - - )} - - {props.data.users?.canPush?.length || 0} - - - {props.data.users?.canAuthorise?.length || 0} - - {(repo.created_at || repo.updated_at || repo.pushed_at) && ( - - Last updated {moment.max([moment(repo.created_at), moment(repo.updated_at), moment(repo.pushed_at)]).fromNow()} - - )} - -
- - -
- -
-
- - ))} - - {/* Render the Pagination component */} - - - - + + )} + {github.description &&

{github.description}

} + + {github.language && ( + + + {github.language} + + )} + {github.license && ( + + {' '} + {github.license.spdx_id} + + )} + + {' '} + {props.data.users?.canPush?.length || 0} + + + {' '} + + {props.data.users?.canAuthorise?.length || 0} + + + {(github.created_at || github.updated_at || github.pushed_at) && ( + + Last updated{' '} + {moment + .max([ + moment(github.created_at), + moment(github.updated_at), + moment(github.pushed_at), + ]) + .fromNow()} + + )} + +
+
+ +
+ +
+
+
); } \ No newline at end of file diff --git a/src/ui/views/RepoList/Components/Repositories.jsx b/src/ui/views/RepoList/Components/Repositories.jsx index 4970858ec..affdcb5a3 100644 --- a/src/ui/views/RepoList/Components/Repositories.jsx +++ b/src/ui/views/RepoList/Components/Repositories.jsx @@ -12,31 +12,63 @@ import NewRepo from './NewRepo'; import RepoOverview from './RepoOverview'; import { UserContext } from '../../../../context'; import PropTypes from 'prop-types'; +import Search from '../../../components/Search/Search'; +import Pagination from '../../../components/Pagination/Pagination'; + export default function Repositories(props) { const useStyles = makeStyles(styles); const classes = useStyles(); const [data, setData] = useState([]); + const [filteredData, setFilteredData] = useState([]); const [, setAuth] = useState(true); const [isLoading, setIsLoading] = useState(false); const [isError, setIsError] = useState(false); + const [currentPage, setCurrentPage] = useState(1); // Pagination state + const itemsPerPage = 5; // Set items per page const navigate = useNavigate(); - const openRepo = (repo) => navigate(`/admin/repo/${repo}`, { replace: true }); const { user } = useContext(UserContext); + const openRepo = (repo) => navigate(`/admin/repo/${repo}`, { replace: true }); + useEffect(() => { const query = {}; for (const k in props) { if (!k) continue; query[k] = props[k]; } - getRepos(setIsLoading, setData, setAuth, setIsError, query); + getRepos(setIsLoading, (data) => { + setData(data); + setFilteredData(data); // Set both data and filteredData + }, setAuth, setIsError, query); }, [props]); const refresh = async (repo) => { - console.log('refresh:', repo); - setData([...data, repo]); + const updatedData = [...data, repo]; + setData(updatedData); + setFilteredData(updatedData); + }; + + const handleSearch = (query) => { + setCurrentPage(1); // Reset to page 1 on new search + if (!query) { + setFilteredData(data); + } else { + const lowercasedQuery = query.toLowerCase(); + setFilteredData( + data.filter(repo => + repo.name.toLowerCase().includes(lowercasedQuery) || + repo.project.toLowerCase().includes(lowercasedQuery) + ) + ); + } }; + const handlePageChange = (page) => setCurrentPage(page); // Update current page + + + // Calculate items for the current page + const startIdx = (currentPage - 1) * itemsPerPage; + const paginatedData = filteredData.slice(startIdx, startIdx + itemsPerPage); if (isLoading) return
Loading...
; if (isError) return
Something went wrong ...
; @@ -54,8 +86,13 @@ export default function Repositories(props) { key='x' classes={classes} openRepo={openRepo} - data={data} + data={paginatedData} // Use filteredData here repoButton={addrepoButton} + onSearch={handleSearch} // Pass handleSearch + currentPage={currentPage} // Pass current page + totalItems={filteredData.length} // Pass total items for pagination + itemsPerPage={itemsPerPage} // Pass items per page + onPageChange={handlePageChange} // Pass page change handler /> ); } @@ -65,6 +102,11 @@ GetGridContainerLayOut.propTypes = { openRepo: PropTypes.func.isRequired, data: PropTypes.array, repoButton: PropTypes.object, + onSearch: PropTypes.func.isRequired, + currentPage: PropTypes.number.isRequired, + totalItems: PropTypes.number.isRequired, + itemsPerPage: PropTypes.number.isRequired, + onPageChange: PropTypes.func.isRequired, }; function GetGridContainerLayOut(props) { @@ -72,6 +114,9 @@ function GetGridContainerLayOut(props) { {props.repoButton} + {/* Add Search component */} + + @@ -86,6 +131,15 @@ function GetGridContainerLayOut(props) {
+ + +
); } + diff --git a/src/ui/views/UserList/Components/UserList.jsx b/src/ui/views/UserList/Components/UserList.jsx index e2e77852b..e3af33b18 100644 --- a/src/ui/views/UserList/Components/UserList.jsx +++ b/src/ui/views/UserList/Components/UserList.jsx @@ -1,7 +1,8 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { makeStyles } from '@material-ui/core/styles'; import GridItem from '../../../components/Grid/GridItem'; import GridContainer from '../../../components/Grid/GridContainer'; + import Button from '@material-ui/core/Button'; import Table from '@material-ui/core/Table'; import TableBody from '@material-ui/core/TableBody'; @@ -10,51 +11,72 @@ import TableContainer from '@material-ui/core/TableContainer'; import TableHead from '@material-ui/core/TableHead'; import TableRow from '@material-ui/core/TableRow'; import Paper from '@material-ui/core/Paper'; -import { CloseRounded, Check, KeyboardArrowRight } from '@material-ui/icons'; +import styles from '../../../assets/jss/material-dashboard-react/views/dashboardStyle'; +import { getUsers } from '../../../services/user'; import Pagination from '../../../components/Pagination/Pagination'; +import { CloseRounded, Check, KeyboardArrowRight } from '@material-ui/icons'; + import Search from '../../../components/Search/Search'; -const useStyles = makeStyles({ - table: { - minWidth: 650, - }, -}); +// const useStyles = makeStyles({ +// table: { +// minWidth: 650, +// }, +// }); + +const useStyles = makeStyles(styles); -export default function UserList() { +export default function UserList(props) { + const classes = useStyles(); + const [data, setData] = useState([]); + const [, setAuth] = useState(true); + const [isLoading, setIsLoading] = useState(false); + const [isError, setIsError] = useState(false); + - // Dummy user data - const dummyUsers = [ - { username: 'johnDoe', displayName: 'John Doe', title: 'Developer', email: 'john@example.com', gitAccount: 'johnDoeGit', admin: true }, - { username: 'janeDoe', displayName: 'Jane Doe', title: 'Designer', email: 'jane@example.com', gitAccount: 'janeDoeGit', admin: false }, - { username: 'markSmith', displayName: 'Mark Smith', title: 'Project Manager', email: 'mark@example.com', gitAccount: 'markSmithGit', admin: true }, - { username: 'lucasBrown', displayName: 'Lucas Brown', title: 'Data Scientist', email: 'lucas@example.com', gitAccount: 'lucasBrownGit', admin: false }, - { username: 'emilyWhite', displayName: 'Emily White', title: 'Backend Engineer', email: 'emily@example.com', gitAccount: 'emilyWhiteGit', admin: true }, - { username: 'oliviaGreen', displayName: 'Olivia Green', title: 'Frontend Developer', email: 'olivia@example.com', gitAccount: 'oliviaGreenGit', admin: false }, - { username: 'noahBlue', displayName: 'Noah Blue', title: 'DevOps Engineer', email: 'noah@example.com', gitAccount: 'noahBlueGit', admin: true }, - { username: 'miaBlack', displayName: 'Mia Black', title: 'Quality Analyst', email: 'mia@example.com', gitAccount: 'miaBlackGit', admin: false }, - { username: 'willGray', displayName: 'Will Gray', title: 'HR Manager', email: 'will@example.com', gitAccount: 'willGrayGit', admin: false }, - { username: 'avaYellow', displayName: 'Ava Yellow', title: 'UX Designer', email: 'ava@example.com', gitAccount: 'avaYellowGit', admin: true }, - ]; - - // Pagination states - const [currentPage, setCurrentPage] = useState(1); - const itemsPerPage = 5; + // Pagination states + const [currentPage, setCurrentPage] = useState(1); + const itemsPerPage = 5; + const [searchQuery, setSearchQuery] = useState(''); - // Calculate the items for the current page - const indexOfLastItem = currentPage * itemsPerPage; - const indexOfFirstItem = indexOfLastItem - itemsPerPage; + + + + // const openUser = (username) => navigate(`/admin/user/${username}`, { replace: true }); + + + + useEffect(() => { + const query = {}; + + for (const k in props) { + if (!k) continue; + query[k] = props[k]; + } + getUsers(setIsLoading, setData, setAuth, setIsError, query); + }, [props]); + + if (isLoading) return
Loading...
; + if (isError) return
Something went wrong...
; + + + + // Filter users based on the search query - const filteredUsers = dummyUsers.filter(user => - user.displayName.toLowerCase().includes(searchQuery.toLowerCase()) || - user.username.toLowerCase().includes(searchQuery.toLowerCase()) + const filteredUsers = data.filter(user => + user.displayName && user.displayName.toLowerCase().includes(searchQuery.toLowerCase()) || + user.username && user.username.toLowerCase().includes(searchQuery.toLowerCase()) ); - + // Calculate the items for the current page +const indexOfLastItem = currentPage * itemsPerPage; +const indexOfFirstItem = indexOfLastItem - itemsPerPage; const currentItems = filteredUsers.slice(indexOfFirstItem, indexOfLastItem); const totalItems = filteredUsers.length; + // Function to handle page change const handlePageChange = (page) => { setCurrentPage(page); From 51a003a81f1d7e78f9757e1ade034ea785e92925 Mon Sep 17 00:00:00 2001 From: darkhorse-420 Date: Wed, 6 Nov 2024 20:48:23 +0530 Subject: [PATCH 06/13] fix: search and pagination --- src/ui/components/Pagination/Pagination.jsx | 2 +- src/ui/components/Search/Search.jsx | 8 ++-- .../RepoList/Components/Repositories.jsx | 28 +++++------- src/ui/views/UserList/Components/UserList.jsx | 44 +++++-------------- 4 files changed, 29 insertions(+), 53 deletions(-) diff --git a/src/ui/components/Pagination/Pagination.jsx b/src/ui/components/Pagination/Pagination.jsx index 7c119f17c..e87e43c17 100644 --- a/src/ui/components/Pagination/Pagination.jsx +++ b/src/ui/components/Pagination/Pagination.jsx @@ -2,7 +2,7 @@ import React from 'react'; import './Pagination.css'; export default function Pagination({ currentPage, totalItems = 0, itemsPerPage, onPageChange }) { - // Calculate the total number of pages + const totalPages = Math.ceil(totalItems / itemsPerPage); const handlePageClick = (page) => { diff --git a/src/ui/components/Search/Search.jsx b/src/ui/components/Search/Search.jsx index d36c4d39f..03f6ba048 100644 --- a/src/ui/components/Search/Search.jsx +++ b/src/ui/components/Search/Search.jsx @@ -1,6 +1,6 @@ import React, { useState } from 'react'; import PropTypes from 'prop-types'; -import './Search.css'; // Import the CSS file +import './Search.css'; export default function Search({ placeholder = 'Search...', onSearch }) { const [query, setQuery] = useState(''); @@ -15,7 +15,7 @@ export default function Search({ placeholder = 'Search...', onSearch }) { const handleKeyDown = (event) => { if (event.key === 'Enter') { - handleSearch(); // Trigger search on Enter + handleSearch(); } }; @@ -26,8 +26,8 @@ export default function Search({ placeholder = 'Search...', onSearch }) { placeholder={placeholder} value={query} onChange={handleInputChange} - onKeyDown={handleKeyDown} // Add keydown event - className="search-input" // Apply the class + onKeyDown={handleKeyDown} + className="search-input" /> ); } -Search.propTypes = { - placeholder: PropTypes.string, - onSearch: PropTypes.func.isRequired, -}; + + + diff --git a/src/ui/views/OpenPushRequests/OpenPushRequests.jsx b/src/ui/views/OpenPushRequests/OpenPushRequests.jsx index f46f96715..bed34cd35 100644 --- a/src/ui/views/OpenPushRequests/OpenPushRequests.jsx +++ b/src/ui/views/OpenPushRequests/OpenPushRequests.jsx @@ -4,13 +4,18 @@ import GridContainer from '../../components/Grid/GridContainer'; import PushesTable from './components/PushesTable'; import CustomTabs from '../../components/CustomTabs/CustomTabs'; + import { Visibility, CheckCircle, Cancel, Block } from '@material-ui/icons'; export default function Dashboard() { + + + return (
+ ), }, { tabName: 'Approved', tabIcon: CheckCircle, - tabContent: , + tabContent: , }, { tabName: 'Canceled', tabIcon: Cancel, - tabContent: , + tabContent: , }, { tabName: 'Rejected', tabIcon: Block, - tabContent: , + tabContent: , }, ]} /> diff --git a/src/ui/views/OpenPushRequests/components/PushesTable.jsx b/src/ui/views/OpenPushRequests/components/PushesTable.jsx index a01f169eb..11a263cfe 100644 --- a/src/ui/views/OpenPushRequests/components/PushesTable.jsx +++ b/src/ui/views/OpenPushRequests/components/PushesTable.jsx @@ -13,18 +13,26 @@ import Paper from '@material-ui/core/Paper'; import styles from '../../../assets/jss/material-dashboard-react/views/dashboardStyle'; import { getPushes } from '../../../services/git-push'; import { KeyboardArrowRight } from '@material-ui/icons'; +import Search from '../../../components/Search/Search'; // Import the Search component +import Pagination from '../../../components/Pagination/Pagination'; // Import Pagination component export default function PushesTable(props) { const useStyles = makeStyles(styles); const classes = useStyles(); const [data, setData] = useState([]); - const [, setAuth] = useState(true); + const [filteredData, setFilteredData] = useState([]); // State for filtered data const [isLoading, setIsLoading] = useState(false); const [isError, setIsError] = useState(false); const navigate = useNavigate(); - + const [, setAuth] = useState(true); + const [currentPage, setCurrentPage] = useState(1); // State for current page + const itemsPerPage = 5; + const [searchTerm, setSearchTerm] = useState(''); // Define searchTerm state const openPush = (push) => navigate(`/admin/push/${push}`, { replace: true }); + + + useEffect(() => { const query = {}; @@ -35,94 +43,161 @@ export default function PushesTable(props) { getPushes(setIsLoading, setData, setAuth, setIsError, query); }, [props]); + + // useEffect(() => { + // setFilteredData(data); // Initialize filtered data with full data on load + // }, [data]); + + useEffect(() => { + // Initialize filtered data with full data on load + const filtered = filterByStatus(data); + setFilteredData(filtered); + }, [props]); + + const filterByStatus = (data) => { + if (props.authorised) { + return data.filter(item => item.status === 'approved'); + } + if (props.rejected) { + return data.filter(item => item.status === 'rejected'); + } + if (props.canceled) { + return data.filter(item => item.status === 'canceled'); + } + if (props.blocked) { + return data.filter(item => item.status === 'pending'); + } + return data; + }; + + + // Apply search to the filtered data + useEffect(() => { + const filtered = filterByStatus(data); // Apply status filter first + if (searchTerm) { + const lowerCaseTerm = searchTerm.toLowerCase(); + const searchFiltered = filtered.filter((item) => + item.repo.toLowerCase().includes(lowerCaseTerm) || + item.commitTo.toLowerCase().includes(lowerCaseTerm) || + + item.commitData[0].message.toLowerCase().includes(lowerCaseTerm) + ); + setFilteredData(searchFiltered); + } else { + setFilteredData(filtered); // Reset to filtered data after clearing search + } + setCurrentPage(1); // Reset pagination on search + }, [searchTerm, props]); // Trigger on search or tab change + + // Handler function for search input + const handleSearch = (searchTerm) => { + setSearchTerm(searchTerm); // Update search term state + }; + + + const handlePageChange = (page) => { + setCurrentPage(page); // Update current page + }; + + // Logic for pagination (getting items for the current page) + const indexOfLastItem = currentPage * itemsPerPage; + const indexOfFirstItem = indexOfLastItem - itemsPerPage; + const currentItems = filteredData.slice(indexOfFirstItem, indexOfLastItem); + + // Change page + const paginate = (pageNumber) => setCurrentPage(pageNumber); + if (isLoading) return
Loading...
; if (isError) return
Something went wrong ...
; return (
+ {/* Use the Search component */} + + - +
- Timestamp - Repository - Branch - Commit SHA - Committer - Author - Author E-mail - Commit Message - No. of Commits - + Timestamp + Repository + Branch + Commit SHA + Committer + Author + Author E-mail + Commit Message + No. of Commits + - {[...data].reverse().map((row) => { + {currentItems.reverse().map((row) => { const repoFullName = row.repo.replace('.git', ''); const repoBranch = row.branch.replace('refs/heads/', ''); return ( - + {moment .unix(row.commitData[0].commitTs || row.commitData[0].commitTimestamp) .toString()} - - + + {repoFullName} - + {repoBranch} - + {row.commitTo.substring(0, 8)} - + {row.commitData[0].committer} - + {row.commitData[0].author} - + {row.commitData[0].authorEmail ? ( {row.commitData[0].authorEmail} ) : ( 'No data...' - )}{' '} + )} - {row.commitData[0].message} - {row.commitData.length} - - @@ -131,6 +206,18 @@ export default function PushesTable(props) {
+ {/* Pagination Component */} +
); } + + + + diff --git a/src/ui/views/User/User.jsx b/src/ui/views/User/User.jsx index c8b46ebe5..8362300f2 100644 --- a/src/ui/views/User/User.jsx +++ b/src/ui/views/User/User.jsx @@ -8,7 +8,6 @@ import Button from '../../components/CustomButtons/Button'; import FormLabel from '@material-ui/core/FormLabel'; import { getUser, updateUser, getUserLoggedIn } from '../../services/user'; import { makeStyles } from '@material-ui/core/styles'; - import { LogoGithubIcon } from '@primer/octicons-react'; import CloseRounded from '@material-ui/icons/CloseRounded'; import { Check, Save } from '@material-ui/icons'; diff --git a/website/docs/configuration/reference.mdx b/website/docs/configuration/reference.mdx index 6a7eceedf..0ee6fb79e 100644 --- a/website/docs/configuration/reference.mdx +++ b/website/docs/configuration/reference.mdx @@ -509,4 +509,4 @@ description: JSON schema reference documentation for GitProxy ---------------------------------------------------------------------------------------------------------------------------- -Generated using [json-schema-for-humans](https://github.com/coveooss/json-schema-for-humans) on 2024-10-22 at 16:45:32 +0100 +Generated using [json-schema-for-humans](https://github.com/coveooss/json-schema-for-humans) on 2024-11-10 at 00:03:58 +0530 From dd8ead19590985ce36ed4112f43ffb19cad54dd6 Mon Sep 17 00:00:00 2001 From: darkhorse-420 Date: Mon, 11 Nov 2024 21:17:05 +0530 Subject: [PATCH 09/13] feat: real-time search --- .../components/PushesTable.jsx | 155 ++++-------------- 1 file changed, 36 insertions(+), 119 deletions(-) diff --git a/src/ui/views/OpenPushRequests/components/PushesTable.jsx b/src/ui/views/OpenPushRequests/components/PushesTable.jsx index 11a263cfe..4ed4fbd36 100644 --- a/src/ui/views/OpenPushRequests/components/PushesTable.jsx +++ b/src/ui/views/OpenPushRequests/components/PushesTable.jsx @@ -13,25 +13,17 @@ import Paper from '@material-ui/core/Paper'; import styles from '../../../assets/jss/material-dashboard-react/views/dashboardStyle'; import { getPushes } from '../../../services/git-push'; import { KeyboardArrowRight } from '@material-ui/icons'; -import Search from '../../../components/Search/Search'; // Import the Search component -import Pagination from '../../../components/Pagination/Pagination'; // Import Pagination component export default function PushesTable(props) { const useStyles = makeStyles(styles); const classes = useStyles(); const [data, setData] = useState([]); - const [filteredData, setFilteredData] = useState([]); // State for filtered data + const [, setAuth] = useState(true); const [isLoading, setIsLoading] = useState(false); const [isError, setIsError] = useState(false); const navigate = useNavigate(); - const [, setAuth] = useState(true); - const [currentPage, setCurrentPage] = useState(1); // State for current page - const itemsPerPage = 5; - const [searchTerm, setSearchTerm] = useState(''); // Define searchTerm state - const openPush = (push) => navigate(`/admin/push/${push}`, { replace: true }); - - + const openPush = (push) => navigate(`/admin/push/${push}`, { replace: true }); useEffect(() => { const query = {}; @@ -43,161 +35,94 @@ export default function PushesTable(props) { getPushes(setIsLoading, setData, setAuth, setIsError, query); }, [props]); - - // useEffect(() => { - // setFilteredData(data); // Initialize filtered data with full data on load - // }, [data]); - - useEffect(() => { - // Initialize filtered data with full data on load - const filtered = filterByStatus(data); - setFilteredData(filtered); - }, [props]); - - const filterByStatus = (data) => { - if (props.authorised) { - return data.filter(item => item.status === 'approved'); - } - if (props.rejected) { - return data.filter(item => item.status === 'rejected'); - } - if (props.canceled) { - return data.filter(item => item.status === 'canceled'); - } - if (props.blocked) { - return data.filter(item => item.status === 'pending'); - } - return data; - }; - - - // Apply search to the filtered data - useEffect(() => { - const filtered = filterByStatus(data); // Apply status filter first - if (searchTerm) { - const lowerCaseTerm = searchTerm.toLowerCase(); - const searchFiltered = filtered.filter((item) => - item.repo.toLowerCase().includes(lowerCaseTerm) || - item.commitTo.toLowerCase().includes(lowerCaseTerm) || - - item.commitData[0].message.toLowerCase().includes(lowerCaseTerm) - ); - setFilteredData(searchFiltered); - } else { - setFilteredData(filtered); // Reset to filtered data after clearing search - } - setCurrentPage(1); // Reset pagination on search - }, [searchTerm, props]); // Trigger on search or tab change - - // Handler function for search input - const handleSearch = (searchTerm) => { - setSearchTerm(searchTerm); // Update search term state - }; - - - const handlePageChange = (page) => { - setCurrentPage(page); // Update current page - }; - - // Logic for pagination (getting items for the current page) - const indexOfLastItem = currentPage * itemsPerPage; - const indexOfFirstItem = indexOfLastItem - itemsPerPage; - const currentItems = filteredData.slice(indexOfFirstItem, indexOfLastItem); - - // Change page - const paginate = (pageNumber) => setCurrentPage(pageNumber); - if (isLoading) return
Loading...
; if (isError) return
Something went wrong ...
; return (
- {/* Use the Search component */} - - - +
- Timestamp - Repository - Branch - Commit SHA - Committer - Author - Author E-mail - Commit Message - No. of Commits - + Timestamp + Repository + Branch + Commit SHA + Committer + Author + Author E-mail + Commit Message + No. of Commits + - {currentItems.reverse().map((row) => { + {[...data].reverse().map((row) => { const repoFullName = row.repo.replace('.git', ''); const repoBranch = row.branch.replace('refs/heads/', ''); return ( - + {moment .unix(row.commitData[0].commitTs || row.commitData[0].commitTimestamp) .toString()} - - + + {repoFullName} - + {repoBranch} - + {row.commitTo.substring(0, 8)} - + {row.commitData[0].committer} - + {row.commitData[0].author} - + {row.commitData[0].authorEmail ? ( {row.commitData[0].authorEmail} ) : ( 'No data...' - )} + )}{' '} - {row.commitData[0].message} - {row.commitData.length} - - @@ -206,14 +131,6 @@ export default function PushesTable(props) {
- {/* Pagination Component */} -
); } From d186dcb0b6a74839ddbe925f2201f83803668c46 Mon Sep 17 00:00:00 2001 From: darkhorse-420 Date: Mon, 11 Nov 2024 21:48:02 +0530 Subject: [PATCH 10/13] fix: code clean code clean --- src/ui/components/Search/Search.css | 8 ++++---- src/ui/components/Search/Search.jsx | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ui/components/Search/Search.css b/src/ui/components/Search/Search.css index 7d41ee1c0..db87dc8c0 100644 --- a/src/ui/components/Search/Search.css +++ b/src/ui/components/Search/Search.css @@ -1,7 +1,7 @@ .search-bar { width: 100%; - max-width:100%; /* Sets a maximum width for the bar, adjust as needed */ - margin: 0 auto 20px auto; /* Centering the search bar with margin */ + max-width:100%; + margin: 0 auto 20px auto; } .search-input { @@ -10,9 +10,9 @@ font-size: 16px; border: 1px solid #ccc; border-radius: 4px; - box-sizing: border-box; /* Ensures padding is included in the width calculation */ + box-sizing: border-box; } .search-input:focus { - border-color: #007bff; /* Change border color when focused */ + border-color: #007bff; } diff --git a/src/ui/components/Search/Search.jsx b/src/ui/components/Search/Search.jsx index c72625091..5e1cbf6b4 100644 --- a/src/ui/components/Search/Search.jsx +++ b/src/ui/components/Search/Search.jsx @@ -2,13 +2,13 @@ import React from 'react'; import { TextField } from '@material-ui/core'; import './Search.css'; import InputAdornment from '@material-ui/core/InputAdornment'; -import SearchIcon from '@material-ui/icons/Search'; // Import the Search Icon +import SearchIcon from '@material-ui/icons/Search'; export default function Search({ onSearch }) { const handleSearchChange = (event) => { const query = event.target.value; - onSearch(query); // Pass the search query to parent component + onSearch(query); }; return ( @@ -18,7 +18,7 @@ export default function Search({ onSearch }) { variant="outlined" fullWidth margin="normal" - onChange={handleSearchChange} // Trigger onSearch on every change + onChange={handleSearchChange} placeholder="Search..." InputProps={{ startAdornment: ( From 78865403eca9671bfb8c6ec0e0d904cc10e76b10 Mon Sep 17 00:00:00 2001 From: darkhorse-420 Date: Fri, 15 Nov 2024 17:08:27 +0530 Subject: [PATCH 11/13] fix: code clean --- src/ui/components/Filtering/Filtering.css | 55 ++++++++++++++++ src/ui/components/Filtering/Filtering.jsx | 66 +++++++++++++++++++ .../RepoList/Components/Repositories.jsx | 32 ++++++++- 3 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 src/ui/components/Filtering/Filtering.css diff --git a/src/ui/components/Filtering/Filtering.css b/src/ui/components/Filtering/Filtering.css new file mode 100644 index 000000000..84f9258e0 --- /dev/null +++ b/src/ui/components/Filtering/Filtering.css @@ -0,0 +1,55 @@ +.filtering-container { + position: relative; + display: inline-block; + padding-bottom: 10px; +} + +.dropdown-toggle { + padding: 10px 10px; + padding-right: 10px; + border: 1px solid #ccc; + border-radius: 5px; + background-color: #fff; + color: #333; + cursor: pointer; + font-size: 14px; + text-align: left; + width: 130px; + display: inline-flex; + align-items: center; + justify-content: space-between; +} + +.dropdown-toggle:hover { + background-color: #f0f0f0; +} + +.dropdown-arrow { + border: none; + background: none; + cursor: pointer; + font-size: 15px; + margin-left: 1px; + margin-right: 10px; +} + +.dropdown-menu { + position: absolute; + background-color: #fff; + border: 1px solid #ccc; + border-radius: 5px; + margin-top: 5px; + z-index: 1000; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); +} + +.dropdown-item { + padding: 10px 15px; + cursor: pointer; + font-size: 14px; + color: #333; +} + +.dropdown-item:hover { + background-color: #f0f0f0; +} \ No newline at end of file diff --git a/src/ui/components/Filtering/Filtering.jsx b/src/ui/components/Filtering/Filtering.jsx index e69de29bb..aa9d26c78 100644 --- a/src/ui/components/Filtering/Filtering.jsx +++ b/src/ui/components/Filtering/Filtering.jsx @@ -0,0 +1,66 @@ +import React, { useState } from 'react'; +import './Filtering.css'; + +const Filtering = ({ onFilterChange }) => { + const [isOpen, setIsOpen] = useState(false); // State to toggle dropdown open/close + const [selectedOption, setSelectedOption] = useState('Sort by'); // Initial state + const [sortOrder, setSortOrder] = useState('asc'); // Track sort order (asc/desc) + + const toggleDropdown = () => { + setIsOpen(!isOpen); // Toggle dropdown open/close state + }; + + const toggleSortOrder = () => { + // Toggle sort order only if an option is selected + if (selectedOption !== 'Sort by') { + const newSortOrder = sortOrder === 'asc' ? 'desc' : 'asc'; + setSortOrder(newSortOrder); + onFilterChange(selectedOption, newSortOrder); // Trigger filtering with new order + } + }; + + const handleOptionClick = (option) => { + // Handle filter option selection + setSelectedOption(option); + onFilterChange(option, sortOrder); // Call the parent function with selected filter and order + setIsOpen(false); // Collapse the dropdown after selection + }; + + return ( +
+
+ {/* Make the entire button clickable for toggling dropdown */} + + + {isOpen && ( +
+
handleOptionClick('Date Modified')} className="dropdown-item"> + Date Modified +
+
handleOptionClick('Date Created')} className="dropdown-item"> + Date Created +
+
handleOptionClick('Alphabetical')} className="dropdown-item"> + Alphabetical +
+
+ )} +
+
+ ); +}; + +export default Filtering; + + + + diff --git a/src/ui/views/RepoList/Components/Repositories.jsx b/src/ui/views/RepoList/Components/Repositories.jsx index b0da96317..4be87b36d 100644 --- a/src/ui/views/RepoList/Components/Repositories.jsx +++ b/src/ui/views/RepoList/Components/Repositories.jsx @@ -13,7 +13,8 @@ import RepoOverview from './RepoOverview'; import { UserContext } from '../../../../context'; import PropTypes from 'prop-types'; import Search from '../../../components/Search/Search'; -import Pagination from '../../../components/Pagination/Pagination'; +import Pagination from '../../../components/Pagination/Pagination'; +import Filtering from '../../../components/Filtering/Filtering'; export default function Repositories(props) { @@ -62,6 +63,32 @@ export default function Repositories(props) { ); } }; + + // New function for handling filter changes + const handleFilterChange = (filterOption, sortOrder) => { + const sortedData = [...data]; + switch (filterOption) { + case 'dateModified': + sortedData.sort((a, b) => new Date(a.lastModified) - new Date(b.lastModified)); + break; + case 'dateCreated': + sortedData.sort((a, b) => new Date(a.dateCreated) - new Date(b.dateCreated)); + break; + case 'alphabetical': + sortedData.sort((a, b) => a.name.localeCompare(b.name)); + break; + default: + break; + } + + if (sortOrder === 'desc') { + sortedData.reverse(); + } + + setFilteredData(sortedData); + }; + + const handlePageChange = (page) => setCurrentPage(page); const startIdx = (currentPage - 1) * itemsPerPage; const paginatedData = filteredData.slice(startIdx, startIdx + itemsPerPage); @@ -89,6 +116,7 @@ export default function Repositories(props) { totalItems={filteredData.length} itemsPerPage={itemsPerPage} onPageChange={handlePageChange} + onFilterChange={handleFilterChange} // Pass handleFilterChange as prop /> ); } @@ -112,7 +140,7 @@ function GetGridContainerLayOut(props) { - + {/* Include the Filtering component */} From fd1d2bfbbede9b2edf341dd59798f6e9fc89f4bb Mon Sep 17 00:00:00 2001 From: darkhorse-420 Date: Fri, 15 Nov 2024 23:52:55 +0530 Subject: [PATCH 12/13] fix: package --- package-lock.json | 3 ++- package.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 94d54c80f..da9e50dde 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,7 +47,7 @@ "react-dom": "^16.13.1", "react-html-parser": "^2.0.2", "react-router-dom": "6.26.2", - "simple-git": "^3.25.0", + "simple-git": "^3.27.0", "uuid": "^10.0.0", "yargs": "^17.7.2" }, @@ -11822,6 +11822,7 @@ "version": "3.27.0", "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.27.0.tgz", "integrity": "sha512-ivHoFS9Yi9GY49ogc6/YAi3Fl9ROnF4VyubNylgCkA+RVqLaKWnDSzXOVzya8csELIaWaYNutsEuAhZrtOjozA==", + "license": "MIT", "dependencies": { "@kwsites/file-exists": "^1.1.1", "@kwsites/promise-deferred": "^1.1.1", diff --git a/package.json b/package.json index 05a1b08db..00e9d7ee5 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "react-dom": "^16.13.1", "react-html-parser": "^2.0.2", "react-router-dom": "6.26.2", - "simple-git": "^3.25.0", + "simple-git": "^3.27.0", "uuid": "^10.0.0", "yargs": "^17.7.2" }, From 03f0dba72598a8f38fccfc0a6915b4ac71ace723 Mon Sep 17 00:00:00 2001 From: darkhorse-420 Date: Fri, 15 Nov 2024 23:58:33 +0530 Subject: [PATCH 13/13] feat: dashboard --- .../components/PushesTable.jsx | 155 ++++++++++++++---- 1 file changed, 119 insertions(+), 36 deletions(-) diff --git a/src/ui/views/OpenPushRequests/components/PushesTable.jsx b/src/ui/views/OpenPushRequests/components/PushesTable.jsx index 4ed4fbd36..11a263cfe 100644 --- a/src/ui/views/OpenPushRequests/components/PushesTable.jsx +++ b/src/ui/views/OpenPushRequests/components/PushesTable.jsx @@ -13,18 +13,26 @@ import Paper from '@material-ui/core/Paper'; import styles from '../../../assets/jss/material-dashboard-react/views/dashboardStyle'; import { getPushes } from '../../../services/git-push'; import { KeyboardArrowRight } from '@material-ui/icons'; +import Search from '../../../components/Search/Search'; // Import the Search component +import Pagination from '../../../components/Pagination/Pagination'; // Import Pagination component export default function PushesTable(props) { const useStyles = makeStyles(styles); const classes = useStyles(); const [data, setData] = useState([]); - const [, setAuth] = useState(true); + const [filteredData, setFilteredData] = useState([]); // State for filtered data const [isLoading, setIsLoading] = useState(false); const [isError, setIsError] = useState(false); const navigate = useNavigate(); - + const [, setAuth] = useState(true); + const [currentPage, setCurrentPage] = useState(1); // State for current page + const itemsPerPage = 5; + const [searchTerm, setSearchTerm] = useState(''); // Define searchTerm state const openPush = (push) => navigate(`/admin/push/${push}`, { replace: true }); + + + useEffect(() => { const query = {}; @@ -35,94 +43,161 @@ export default function PushesTable(props) { getPushes(setIsLoading, setData, setAuth, setIsError, query); }, [props]); + + // useEffect(() => { + // setFilteredData(data); // Initialize filtered data with full data on load + // }, [data]); + + useEffect(() => { + // Initialize filtered data with full data on load + const filtered = filterByStatus(data); + setFilteredData(filtered); + }, [props]); + + const filterByStatus = (data) => { + if (props.authorised) { + return data.filter(item => item.status === 'approved'); + } + if (props.rejected) { + return data.filter(item => item.status === 'rejected'); + } + if (props.canceled) { + return data.filter(item => item.status === 'canceled'); + } + if (props.blocked) { + return data.filter(item => item.status === 'pending'); + } + return data; + }; + + + // Apply search to the filtered data + useEffect(() => { + const filtered = filterByStatus(data); // Apply status filter first + if (searchTerm) { + const lowerCaseTerm = searchTerm.toLowerCase(); + const searchFiltered = filtered.filter((item) => + item.repo.toLowerCase().includes(lowerCaseTerm) || + item.commitTo.toLowerCase().includes(lowerCaseTerm) || + + item.commitData[0].message.toLowerCase().includes(lowerCaseTerm) + ); + setFilteredData(searchFiltered); + } else { + setFilteredData(filtered); // Reset to filtered data after clearing search + } + setCurrentPage(1); // Reset pagination on search + }, [searchTerm, props]); // Trigger on search or tab change + + // Handler function for search input + const handleSearch = (searchTerm) => { + setSearchTerm(searchTerm); // Update search term state + }; + + + const handlePageChange = (page) => { + setCurrentPage(page); // Update current page + }; + + // Logic for pagination (getting items for the current page) + const indexOfLastItem = currentPage * itemsPerPage; + const indexOfFirstItem = indexOfLastItem - itemsPerPage; + const currentItems = filteredData.slice(indexOfFirstItem, indexOfLastItem); + + // Change page + const paginate = (pageNumber) => setCurrentPage(pageNumber); + if (isLoading) return
Loading...
; if (isError) return
Something went wrong ...
; return (
+ {/* Use the Search component */} + + - +
- Timestamp - Repository - Branch - Commit SHA - Committer - Author - Author E-mail - Commit Message - No. of Commits - + Timestamp + Repository + Branch + Commit SHA + Committer + Author + Author E-mail + Commit Message + No. of Commits + - {[...data].reverse().map((row) => { + {currentItems.reverse().map((row) => { const repoFullName = row.repo.replace('.git', ''); const repoBranch = row.branch.replace('refs/heads/', ''); return ( - + {moment .unix(row.commitData[0].commitTs || row.commitData[0].commitTimestamp) .toString()} - - + + {repoFullName} - + {repoBranch} - + {row.commitTo.substring(0, 8)} - + {row.commitData[0].committer} - + {row.commitData[0].author} - + {row.commitData[0].authorEmail ? ( {row.commitData[0].authorEmail} ) : ( 'No data...' - )}{' '} + )} - {row.commitData[0].message} - {row.commitData.length} - - @@ -131,6 +206,14 @@ export default function PushesTable(props) {
+ {/* Pagination Component */} +
); }