Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added open issue section in our contributor page #346

Merged
merged 2 commits into from
Nov 4, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
215 changes: 178 additions & 37 deletions client/src/component/Contributors.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,16 +94,111 @@ ContributorCard.propTypes = {
type: PropTypes.string.isRequired,
};

const StatCard = ({ label, value, icon, mode }) => (

const IssueCard = ({
title,
number,
html_url,
user,
state,
mode,
}) => (
<motion.div
whileHover={{ y: -5 }}
transition={{ type: 'spring', stiffness: 300 }}
className={`rounded-lg overflow-hidden border transition-all duration-300 ${mode === 'dark'
? 'bg-black/30 backdrop-blur-md border-white/10'
: 'bg-white backdrop-blur-sm border-gray-300'
}`}
style={{ minHeight: '350px' }} // Set a minimum height
data-aos="fade-up"
data-aos-duration='1500'
>
<div className='flex flex-col justify-between pt-6' style={{ height: '100%' }}>
<div className='text-center flex-grow'>
<img
src={user.avatar_url}
alt={user.login}
className={`w-24 h-24 rounded-full mx-auto mb-4 border-4 ${mode === 'dark' ? 'border-primary' : 'border-blue-500'
}`}
/>
<h3 className={`font-bold text-xl px-3 ${mode === 'dark' ? 'text-white' : 'text-gray-800'}`} style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
{title}
</h3>
<p className={`text-sm ${mode === 'dark' ? 'text-primary' : 'text-gray-600'} mb-2`}>
Status: {state}
</p>
<div className={`mt-4 rounded-full py-2 px-4 inline-block ${mode === 'dark' ? 'bg-white/10' : 'bg-blue-100'
}`}>
<span className={`font-semibold ${mode === 'dark' ? 'text-primary' : 'text-blue-600'
}`}>
Issue : #{number}
</span>
</div>
</div>
<div className={`flex justify-between items-center p-2 ${mode === 'dark' ? 'bg-white/5' : 'bg-blue-200'
}`} style={{ padding: '0', margin: '0' }}>
<a
href={html_url}
target='_blank'
rel='noopener noreferrer'
className={`text-primary hover:text-primary/80 transition-colors flex items-center ${mode === 'dark' ? 'text-white' : 'text-blue-800'
}`}
style={{ flexGrow: 1, padding: '8px 0' }} // Remove padding left and right
>
<svg
xmlns='http://www.w3.org/2000/svg'
className='h-5 w-5 mr-2'
viewBox='0 0 20 20'
fill='currentColor'
>
<path d='M11 3a1 1 0 100 2h2.586l-6.293 6.293a1 1 0 101.414 1.414L15 6.414V9a1 1 0 102 0V4a1 1 0 00-1-1h-5z' />
<path d='M5 5a2 2 0 00-2 2v8a2 2 0 002 2h8a2 2 0 002-2v-3a1 1 0 10-2 0v3H5V7h3a1 1 0 000-2H5z' />
</svg>
View Issue
</a>
<svg
xmlns='http://www.w3.org/2000/svg'
width='24'
height='24'
viewBox='0 0 24 24'
fill='none'
stroke='currentColor'
strokeWidth='2'
strokeLinecap='round'
strokeLinejoin='round'
className='text-white/50'
>
<path d='M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22'></path>
</svg>
</div>
</div>
</motion.div>
);

IssueCard.propTypes = {
title: PropTypes.string.isRequired,
number: PropTypes.number.isRequired,
html_url: PropTypes.string.isRequired, // Add validation for html_url
user: PropTypes.shape({
login: PropTypes.string.isRequired,
avatar_url: PropTypes.string.isRequired,
}).isRequired,
state: PropTypes.string.isRequired,
mode: PropTypes.string.isRequired,
};

const StatCard = ({ label, value, icon, mode, onClick }) => (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
className={`rounded-lg p-6 flex items-center border transition-all duration-300 ${
mode === "dark"
? "bg-black/30 backdrop-blur-md border-white/10"
: "bg-white backdrop-blur-sm border-gray-300"
}`}
className={`rounded-lg p-6 flex items-center border transition-all duration-300 cup ${mode === 'dark'
? 'bg-black/30 backdrop-blur-md border-white/10'
: 'bg-white backdrop-blur-sm border-gray-300'
}`}
onClick={onClick}

>
<div
className={`rounded-full p-3 mr-4 ${
Expand Down Expand Up @@ -135,38 +230,51 @@ StatCard.propTypes = {
mode: PropTypes.string.isRequired,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
icon: PropTypes.node.isRequired,
onClick: PropTypes.func,
};

export default function Contributor(props) {
const [contributors, setContributors] = useState([]);
const [openIssues, setOpenIssues] = useState([]);
const [showIssue, setShowIssue] = useState(true); // Determine which section to show
const [loading, setLoading] = useState(true);
const [currentPage, setCurrentPage] = useState(1);
const itemsPerPage = 9; // Set the number of items per page (for both contributors and issues)

const [repoStats, setRepoStats] = useState({
stars: 0,
forks: 0,
openIssues: 0,
});
const [loading, setLoading] = useState(true);


useEffect(() => {
const fetchData = async () => {
try {
const contributorsResponse = await fetch(
"https://api.github.com/repos/Bitbox-Connect/Bitbox/contributors?page=1&per_page=100"
);
// Fetch contributors
const contributorsResponse = await fetch("https://api.github.com/repos/Bitbox-Connect/Bitbox/contributors?page=1&per_page=100");

if (!contributorsResponse.ok) {
throw new Error("Failed to fetch contributors data");
}
const contributorsData = await contributorsResponse.json();
setContributors(contributorsData);

const repoResponse = await fetch(
"https://api.github.com/repos/Bitbox-Connect/Bitbox"
);

// Fetch repo stats
const repoResponse = await fetch('https://api.github.com/repos/Bitbox-Connect/Bitbox');

const repoData = await repoResponse.json();
setRepoStats({
stars: repoData.stargazers_count,
forks: repoData.forks_count,
openIssues: repoData.open_issues_count,
});

// Fetch issues
const issueResponse = await fetch('https://api.github.com/repos/Bitbox-Connect/Bitbox/issues');
const issueData = await issueResponse.json();
setOpenIssues(issueData);
} catch (error) {
console.error("Error fetching data:", error);
} finally {
Expand All @@ -177,6 +285,19 @@ export default function Contributor(props) {
fetchData();
}, []);

// Calculate pagination indices
const indexOfLastItem = currentPage * itemsPerPage;
const indexOfFirstItem = indexOfLastItem - itemsPerPage;

// Get current items based on selected section
const currentItems = showIssue ? openIssues.slice(indexOfFirstItem, indexOfLastItem) : contributors.slice(indexOfFirstItem, indexOfLastItem);

// Determine total pages
const totalPages = showIssue ? Math.ceil(openIssues.length / itemsPerPage) : Math.ceil(contributors.length / itemsPerPage);

const paginate = (pageNumber) => setCurrentPage(pageNumber);


return (
<div
className={`min-h-screen ${
Expand Down Expand Up @@ -257,6 +378,10 @@ export default function Contributor(props) {
/>
</svg>
}
onClick={() => {
setShowIssue(false);
setCurrentPage(1); // Reset pagination when switching to contributors
}}
/>
<StatCard
mode={props.mode}
Expand Down Expand Up @@ -306,40 +431,56 @@ export default function Contributor(props) {
<path d="M5.05 3.05A7 7 0 1018 10a7.002 7.002 0 00-12.95 0A5.001 5.001 0 115 3.05zm0 2A3.001 3.001 0 105 10a3.001 3.001 0 000-4.95z" />
</svg>
}
onClick={() => {
setShowIssue(true);
setCurrentPage(1); // Reset pagination when switching to contributors
}}
/>
</div>
</div>
</section>

<section className="container mx-auto px-4 py-8">
<h2
className={`text-3xl font-bold text-center ${
props.mode === "dark" ? "text-white" : "text-black"
}`}
>
Meet Our Contributors
</h2>
{loading ? (
<p className="text-center mt-8">
{props.mode === "dark" ? "Loading..." : "Loading..."}
</p>
) : (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-8 mt-8">
{contributors.map((contributor, index) => (
<section className="py-16 px-4 sm:px-6 lg:px-8">
<div className="max-w-7xl mx-auto">
<h2 className="text-3xl font-bold text-center mb-12">{showIssue ? 'Your contribution is valuable see all Issues' : 'Contributors'}</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8">
{currentItems.map(item => showIssue ? (
<IssueCard
key={item.id}
title={item.title}
number={item.number}
html_url={item.html_url}
user={item.user}
state={item.state}
mode={props.mode}
/>
) : (
<ContributorCard
key={index}
key={item.id}
login={item.login}
avatar_url={item.avatar_url}
html_url={item.html_url}
contributions={item.contributions}
type={item.type}
mode={props.mode}
login={contributor.login}
avatar_url={contributor.avatar_url}
html_url={contributor.html_url}
contributions={contributor.contributions}
type={contributor.type}
className="p-2 text-sm"
/>
))}
</div>
)}
<div className="flex justify-center mt-8">
{Array.from({ length: totalPages }, (_, i) => (
<button
key={i}
onClick={() => paginate(i + 1)}
className={`px-4 py-2 mx-1 rounded text-black ${i + 1 === currentPage ? 'bg-blue-500 text-white' : 'bg-gray-200 text-gray-900'}`}
>
{i + 1}
</button>
))}
</div>
</div>
</section>
</div>
</div >


);
}