Skip to content

Commit 2f78720

Browse files
authored
Merge pull request #18 from CS-ZSC/feat/components/board
feat: `Board` component
2 parents c4db906 + 60049cc commit 2f78720

File tree

11 files changed

+181
-2
lines changed

11 files changed

+181
-2
lines changed

app/layout.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Header from "@/components/ui/internal/header";
55
import Footer from "@/components/ui/internal/footer";
66
import "./global.css";
77
import { AnimatePresence } from "framer-motion";
8+
import { Toaster } from "@/components/ui/toaster";
89

910
export const metadata: Metadata = {
1011
title: "IEEE ZSB",
@@ -25,6 +26,7 @@ export default function RootLayout({
2526
<AnimatePresence mode="wait">{children}</AnimatePresence>
2627
<Footer />
2728
</Stack>
29+
<Toaster />
2830
</Provider>
2931
</body>
3032
</html>

app/page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import SloganTypingEffect from "@/components/ui/internal/home/slogan-typing-effe
77
import { useWindowType } from "@/hooks/use-window-type";
88
import { useEffect, useRef } from "react";
99
import Events from "@/components/ui/internal/home/events";
10+
import Board from "@/components/ui/internal/board";
1011
// import data from "../fakedata.json";
1112
// import Image from "next/image";
1213

@@ -126,6 +127,7 @@ export default function Home() {
126127
<SloganTypingEffect />
127128
</Flex>
128129
<Events />
130+
<Board />
129131

130132
</PageWrapper>
131133
);
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import { useWindowType } from "@/hooks/use-window-type";
2+
import { Avatar, Box, Heading, HStack, IconButton, Separator, Stack, Text, useClipboard, Wrap } from "@chakra-ui/react";
3+
import { HiOutlineMail, HiCheck } from "react-icons/hi";
4+
5+
import React from "react";
6+
import Card from "@/components/ui/internal/card";
7+
import { Tooltip } from "@/components/ui/tooltip";
8+
import { toaster } from "@/components/ui/toaster"
9+
interface Position {
10+
name: string;
11+
position: string;
12+
avatarSrc: string;
13+
email: string;
14+
}
15+
16+
interface BoardProps {
17+
description?: string;
18+
title?: string;
19+
positions?: Position[];
20+
}
21+
22+
export default function Board({ }: BoardProps) {
23+
const { isDesktop } = useWindowType();
24+
return (
25+
<Card>
26+
<Stack align={"center"}>
27+
<Heading fontWeight={"bold"} size={isDesktop ? "4xl" : "2xl"} color={"text-3"}>
28+
Board
29+
</Heading>
30+
<Text>
31+
Meet the heart and soul behind this website — a team of passionate and dedicated students who pour their energy and creativity into making it extraordinary. Here are our amazing Executive Board members!
32+
</Text>
33+
</Stack>
34+
<Stack w="full">
35+
<Heading alignSelf={"center"}>Executive Board</Heading>
36+
<Separator size={"lg"} borderColor={"secondary"} />
37+
</Stack>
38+
39+
<Wrap gap={2} padding={8} justify={"space-around"} borderColor={"text-5"} borderWidth={"4px"} borderRadius={"2xl"} borderStyle={"dashed"} w="full" h="full">
40+
{positions.map((pos, i) => <PositionCard position={pos} key={i} />)}
41+
</Wrap>
42+
43+
</Card>
44+
45+
);
46+
}
47+
48+
49+
function PositionCard({ position }: { position: Position }) {
50+
const { isDesktop } = useWindowType();
51+
const clipboard = useClipboard({ value: position.email, timeout: 2000 });
52+
return (
53+
<Stack w={isDesktop ? "240px" : "160px"} align={"center"} spaceY={0}>
54+
<Box h="full" w="full">
55+
56+
<Avatar.Root
57+
w={"full"}
58+
h={"full"}
59+
shape="rounded"
60+
>
61+
<Avatar.Fallback name={position.name} />
62+
<Avatar.Image
63+
src={position.avatarSrc}
64+
alt={position.name}
65+
/>
66+
</Avatar.Root>
67+
</Box>
68+
<Stack w="full" align={"center"} justify={"center"} spaceY={-2} m={2}>
69+
<Heading textAlign={"center"} size={isDesktop ? "lg" : "md"}>{position.name}</Heading>
70+
<HStack w="full" justify={"center"} spaceX={1} align={"center"}>
71+
<Text textAlign={"center"} fontSize={isDesktop ? "sm" : "xs"}>{position.position}</Text>
72+
<Tooltip content={position.email} interactive openDelay={100} showArrow closeDelay={100}>
73+
<IconButton variant={"plain"} color={"white"} p={1} size={"xs"} onClick={() => {
74+
clipboard.copy();
75+
toaster.create({
76+
title: "Email Copied",
77+
description: `The email has been copied to your clipboard. Contect now with our ${position.position}!`,
78+
type: "success",
79+
duration: 2000,
80+
placement: "top",
81+
});
82+
83+
}} aria-label="Search database">
84+
{clipboard.copied ? <HiCheck /> : <HiOutlineMail />}
85+
</IconButton>
86+
</Tooltip>
87+
</HStack>
88+
</Stack>
89+
</Stack>
90+
);
91+
92+
}
93+
94+
const positions: Position[] = [
95+
{
96+
name: "Shahd Elbendary",
97+
position: "Chairperson",
98+
avatarSrc: "/Images/board/chairperson.jpeg",
99+
100+
},
101+
{
102+
name: "Amr Yasser Saber",
103+
position: "Vice Technical Chairman",
104+
avatarSrc: "/Images/board/vice-technical.jpeg",
105+
106+
},
107+
{
108+
name: "Mustafa Mohamed",
109+
position: "Vice Managerial Chairman",
110+
avatarSrc: "/Images/board/vice-managerial.jpeg",
111+
112+
},
113+
{
114+
name: "Samar Nafea",
115+
position: "Secretary",
116+
avatarSrc: "/Images/board/secretary.jpeg",
117+
118+
},
119+
{
120+
name: "Mohamed Tarek",
121+
position: "Branding Officer",
122+
avatarSrc: "/Images/board/branding-officer.jpeg",
123+
124+
},
125+
{
126+
name: "Sarah Kahwash",
127+
position: "Treasurer",
128+
avatarSrc: "/Images/board/treasurer.jpeg",
129+
130+
},
131+
];

components/ui/internal/home/events.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export default function Events() {
1010
const { isDesktop } = useWindowType();
1111
return (
1212
<Card>
13-
<Heading fontWeight={"bold"} size={isDesktop ? "5xl" : "2xl"} color={"text-3"}>
13+
<Heading fontWeight={"bold"} size={isDesktop ? "4xl" : "2xl"} color={"text-3"}>
1414
Events
1515
</Heading>
1616
<Stack
@@ -20,7 +20,7 @@ export default function Events() {
2020
>
2121

2222
{fake_events.map((ev, i) => {
23-
return (<Stack key={i} spaceY={10} borderRadius={"lg"} bgSize={"auto"}
23+
return (<Stack key={i} spaceY={10} borderRadius={"lg"} bgSize={"cover"}
2424
bgRepeat={"no-repeat"} bgPos={"center"} padding={4} justify={"space-between"} bgImage={`url(${ev.image})`}>
2525
<Stack
2626
spaceY={3}

components/ui/toaster.tsx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"use client"
2+
3+
import {
4+
Toaster as ChakraToaster,
5+
Portal,
6+
Spinner,
7+
Stack,
8+
Toast,
9+
createToaster,
10+
} from "@chakra-ui/react"
11+
12+
export const toaster = createToaster({
13+
placement: "top",
14+
pauseOnPageIdle: true,
15+
16+
})
17+
18+
export const Toaster = () => {
19+
return (
20+
<Portal>
21+
<ChakraToaster toaster={toaster} insetInline={{ mdDown: "4" }}>
22+
{(toast) => (
23+
<Toast.Root width={{ md: "sm" }}>
24+
{toast.type === "loading" ? (
25+
<Spinner size="sm" color="blue.solid" />
26+
) : (
27+
<Toast.Indicator />
28+
)}
29+
<Stack gap="1" flex="1" maxWidth="100%">
30+
{toast.title && <Toast.Title>{toast.title}</Toast.Title>}
31+
{toast.description && (
32+
<Toast.Description>{toast.description}</Toast.Description>
33+
)}
34+
</Stack>
35+
{toast.action && (
36+
<Toast.ActionTrigger>{toast.action.label}</Toast.ActionTrigger>
37+
)}
38+
{toast.meta?.closable && <Toast.CloseTrigger />}
39+
</Toast.Root>
40+
)}
41+
</ChakraToaster>
42+
</Portal>
43+
)
44+
}
201 KB
Loading

public/Images/board/chairperson.jpeg

300 KB
Loading

public/Images/board/secretary.jpeg

236 KB
Loading

public/Images/board/treasurer.jpeg

269 KB
Loading
269 KB
Loading

0 commit comments

Comments
 (0)