Skip to content
Open
Show file tree
Hide file tree
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
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[HOW TO CONTRIBUTE](/contributions.md)

# Developer Profiles
## Developer Profiles

| No | Name | Profile Link |
| --- | ------------------- | ----------------------------------------------- |
Expand All @@ -29,5 +29,7 @@
| 21 | Phewrine Atieno | [Profile](profiles/phewrine-atieno-2023.md) |
| 22 | Suleiman | [Profile](profiles/suleiman-2023.md) |
| 23 | Chris Achinga | [Profile](profiles/chris-achinga-2021.md) |
| 24 | Patrick Mwangi | [Profile](profiles/patrick-mwangi2024.md)
| 25 | Pius Khainja | [Profile](profiles/GabrielPlus.md) | |
| 24 | Patrick Mwangi | [Profile](profiles/patrick-mwangi.md) |
| 25 | Nickson Simiyu | [Profile](profiles/Nickson-Simiyu_2024.md) |
| 25 | Nickson Simiyu | [Profile](profiles/Nickson-Simiyu_2024.md) |
10 changes: 8 additions & 2 deletions app/Gallery/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from "react";
import Link from "next/link";
import Image from "next/image"; // Import Next.js Image component

const images = [
{ src: "/gallery/team-event.jpg", description: "Team event at Swahilipot Hub during ICP hackathon. This event brought together participants from various backgrounds to collaborate and innovate." },
Expand All @@ -16,10 +17,15 @@ export default function Gallery() {
<h1 className="text-3xl font-bold mb-8">SPH Gallery</h1>
{images.map(({ src, description }, index) => (
<div key={index} className="border p-4 rounded-lg shadow-md w-full max-w-xl flex"> {/* Medium sized box */}
<img src={src} alt={description} className="w-2/3 h-auto rounded-lg mb-3" /> {/* Image width */}
<Image
src={src}
alt={description}
width={400}
height={300}
className="w-2/3 h-auto rounded-lg mb-3" // Customize dimensions as needed
/>
<div className="flex flex-col justify-between ml-4 w-1/3"> {/* Description container */}
<p className="text-sm text-gray-700">{description}</p>
{/* Caption removed */}
</div>
</div>
))}
Expand Down
135 changes: 135 additions & 0 deletions app/components/modals/contributer-modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
"use client";

import * as z from "zod";
import { useForm } from "react-hook-form";
import axios from "axios";
import { zodResolver } from "@hookform/resolvers/zod";
import { useContributerModal } from "@/hooks/use-contributer-modal";
import { Modal } from "@/components/ui/modal";
import { useState } from "react";
import { toast } from "react-hot-toast";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";

const formSchema = z.object({
name: z.string().min(1, "Name is required"),
githubUsername: z.string().min(1, "GitHub username is required"),
});

export const ContributerModal = () => {
const contributerModal = useContributerModal();
const [loading, setLoading] = useState(false);
const [showGithubInput, setShowGithubInput] = useState(false);

const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
name: "",
githubUsername: "",
},
});

const onSubmit = async (values: z.infer<typeof formSchema>) => {
try {
setLoading(true);
await axios.post('/api/contributors', values);
toast.success("Contributor added successfully!");

form.reset(); // Reset form after successful submission
contributerModal.onClose(); // Close modal after submission
window.location.reload(); // Reload to refresh data in UserCard
} catch (error) {
console.error("Submission error:", error);
toast.error("Something went wrong.");
} finally {
setLoading(false);
}
};

return (
<Modal
title="Add a contributor"
description="Add those who have contributed to the development of this website"
isOpen={contributerModal.isOpen}
onClose={contributerModal.onClose}
>
<div>
<div className="space-y-4 py-2 pb-4">
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input
disabled={loading}
placeholder="Enter contributor's name"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>

{showGithubInput && (
<FormField
control={form.control}
name="githubUsername"
render={({ field }) => (
<FormItem>
<FormLabel>GitHub Username</FormLabel>
<FormControl>
<Input
disabled={loading}
placeholder="Enter GitHub username"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
)}

<div className="pt-6 space-x-2 flex items-center justify-end w-full">
<Button
disabled={loading}
variant="outline"
onClick={contributerModal.onClose}
>
Cancel
</Button>

{!showGithubInput ? (
<Button
disabled={loading}
type="button"
onClick={() => setShowGithubInput(true)}
>
Continue
</Button>
) : (
<Button disabled={loading} type="submit">
Submit
</Button>
)}
</div>
</form>
</Form>
</div>
</div>
</Modal>
);
};
117 changes: 101 additions & 16 deletions app/components/namecards.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,118 @@
"use client";

import React, { useState, useEffect } from 'react';
import { contributors } from '../data/contributors';
import React, { useState, useEffect } from "react";
import {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from "@/components/ui/pagination";

interface User {
name: string;
github: string;
githubUsername: string;
}

interface ApiUser {
name: string;
githubusername: string; // Ensure this matches your API response
}

const ITEMS_PER_PAGE = 6;

const UserCard: React.FC = () => {
const [users, setUsers] = useState<User[]>([]);
const [currentPage, setCurrentPage] = useState(1);

useEffect(() => {
setUsers(contributors);
const fetchContributors = async () => {
try {
const response = await fetch("/api/contributors");
const data: ApiUser[] = await response.json(); // Use the ApiUser interface
const formattedData = data.map((user) => ({
name: user.name,
githubUsername: user.githubusername,
}));
setUsers(formattedData);
} catch (error) {
console.error("Error fetching contributors:", error);
}
};

fetchContributors();
}, []);

// Calculate the index range for the current page
const startIndex = (currentPage - 1) * ITEMS_PER_PAGE;
const endIndex = startIndex + ITEMS_PER_PAGE;
const currentUsers = users.slice(startIndex, endIndex);

// Calculate total pages based on the number of users
const totalPages = Math.ceil(users.length / ITEMS_PER_PAGE);

const handlePageChange = (page: number) => {
setCurrentPage(page);
};

return (
<div className="grid grid-cols-1 md:grid-cols-3 grid-flow-row mt-10">
{users.map((user, index) => (
<div
key={index}
className="max-w-sm mx-3 mb-4 p-6 bg-white border border-gray-300 hover:border-t-transparent hover:border-l-transparent transition duration-300 ease-in-out shadow-lg hover:shadow-blue-500/50 hover:translate-x-[-5px] hover:translate-y-[-5px]"
>
<h2 className="text-xl font-semibold text-gray-900">Name: {user.name}</h2>
<p className="text-gray-700 mt-2">
GitHub: <a href={`https://github.com/${user.github}`} className="text-blue-600 hover:underline">@{user.github}</a>
</p>
</div>
))}
<div>
{/* User Cards */}
<div className="grid grid-cols-1 md:grid-cols-3 grid-flow-row mt-10">
{Array.isArray(currentUsers) && currentUsers.length > 0 ? (
currentUsers.map((user, index) => (
<div
key={index}
className="max-w-sm mx-3 mb-4 p-6 bg-white border border-gray-300 hover:border-t-transparent hover:border-l-transparent transition duration-300 ease-in-out shadow-lg hover:shadow-blue-500/50 hover:translate-x-[-5px] hover:translate-y-[-5px]"
>
<h2 className="text-xl font-semibold text-gray-900">Name: {user.name}</h2>
<p className="text-gray-700 mt-2">
GitHub:{" "}
<a
href={`https://github.com/${user.githubUsername}`}
className="text-blue-600 hover:underline"
>
@{user.githubUsername}
</a>
</p>
</div>
))
) : (
<p>No contributors found.</p>
)}
</div>

{/* Pagination Controls */}
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationPrevious
href="#"
onClick={() => handlePageChange(Math.max(1, currentPage - 1))}
/>
</PaginationItem>
{[...Array(totalPages)].map((_, index) => (
<PaginationItem key={index}>
<PaginationLink
href="#"
isActive={index + 1 === currentPage}
onClick={() => handlePageChange(index + 1)}
>
{index + 1}
</PaginationLink>
</PaginationItem>
))}
{totalPages > 3 && <PaginationEllipsis />}
<PaginationItem>
<PaginationNext
href="#"
onClick={() => handlePageChange(Math.min(totalPages, currentPage + 1))}
/>
</PaginationItem>
</PaginationContent>
</Pagination>
</div>
);
};
Expand Down
44 changes: 34 additions & 10 deletions app/contributors/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,37 @@
"use client";
import UserCard from "../components/namecards";
import Link from "next/link";
import { Button } from "@/components/ui/button";
import { useContributerModal } from "@/hooks/use-contributer-modal";
import { ContributerModal } from "../components/modals/contributer-modal"; // Import the modal

export default function Contributors(){
return (
<div className="mx-10 md:mx-52">
<h1 className="text-center py-3 font-semibold text-xl">Contributors</h1>
<p className="text-center">This page is dedicated to displaying the names of those who have contributed to the development of this website</p>
<UserCard/>
<Link href="/" className="animate-pulse text-md">← Back to Home</Link>
</div>
)
}
export default function Contributors() {
const contributerModal = useContributerModal();

return (
<div className="mx-10 md:mx-52">
<h1 className="text-center py-3 font-semibold text-xl">Contributors</h1>
<p className="text-center">
This page is dedicated to displaying the names of those who have
contributed to the development of this website.
</p>
<UserCard />
<Link href="/" className="animate-pulse text-md">
← Back to Home
</Link>

{/* Directly trigger the modal on button click */}
<div className="flex items-center">
<Button
className="ml-2 h-8" // Move hover effect to Button
onClick={contributerModal.onOpen} // Open the Contributer Modal directly
>
+ Contributor
</Button>
</div>

{/* Add the ContributerModal component here */}
<ContributerModal />
</div>
);
}
4 changes: 4 additions & 0 deletions app/data/contributors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ export const contributors = [
"name": "Patrick Mwangi",
"github": "frashid17"
},
{
"name": "Patrick Mwangi",
"github": "frashid17"
},
{
"name": "Nickson Simiyu",
"github": "Nickson-Simiyu"
Expand Down
Loading