diff --git a/src/App.tsx b/src/App.tsx
index 18ac09d..401d97a 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -5,7 +5,7 @@ import { Routes, Route } from "react-router-dom";
import axios from "axios";
import { initializeApp } from "firebase/app";
import { setPersistence, getAuth, inMemoryPersistence } from "firebase/auth";
-import { useLogin, LoadingScreen, AuthProvider } from "@hex-labs/core";
+import { useLogin, LoadingScreen, AuthProvider, Header, Footer } from "@hex-labs/core";
import UserData from './components/UserData';
@@ -50,9 +50,11 @@ export const App = () => {
{/* Setting up our React Router to route to all the different pages we may have */}
+ } />
+
);
diff --git a/src/components/UserApplied.tsx b/src/components/UserApplied.tsx
new file mode 100644
index 0000000..f260f85
--- /dev/null
+++ b/src/components/UserApplied.tsx
@@ -0,0 +1,117 @@
+import React, { useEffect, useState } from "react";
+import {
+ Modal,
+ ModalOverlay,
+ ModalContent,
+ ModalHeader,
+ ModalBody,
+ ModalFooter,
+ ModalCloseButton,
+ Button,
+ Text,
+ Spinner,
+ Box,
+ Stack,
+} from "@chakra-ui/react";
+import axios from "axios";
+import { apiUrl, Service } from "@hex-labs/core";
+
+interface UserAppliedProps {
+ isOpen: boolean;
+ onClose: () => void;
+ userId?: string;
+}
+
+interface Hexathon {
+ _id: string;
+ name: string;
+ startDate?: string;
+ endDate?: string;
+}
+
+const UserApplied: React.FC = ({ isOpen, onClose, userId }) => {
+ const [hexathons, setHexathons] = useState([]);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ if (!isOpen || !userId) return;
+
+ const fetchData = async () => {
+ setLoading(true);
+ setError(null);
+ try {
+ const applicationsUrl = apiUrl(Service.REGISTRATION, "applications");
+ const { data: applicationsResponse } = await axios.get(applicationsUrl, {
+ params: { userId },
+ });
+
+ const applications = applicationsResponse.applications || [];
+ const hexathonIds = applications.map((a: any) => a.hexathon);
+
+ const hexathonsUrl = apiUrl(Service.HEXATHONS, "hexathons");
+ const { data: allHexathons } = await axios.get(hexathonsUrl);
+
+ const applied = allHexathons.filter((h: any) =>
+ hexathonIds.includes(h._id)
+ );
+
+ setHexathons(applied);
+ } catch (err) {
+ console.error(err);
+ setError("Failed to load applications.");
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchData();
+ }, [isOpen, userId]);
+
+ return (
+
+
+
+ Applications
+
+
+ {loading ? (
+
+ ) : error ? (
+ {error}
+ ) : hexathons.length === 0 ? (
+ No applications found.
+ ) : (
+
+ {hexathons.map((hex) => (
+
+ {hex.name}
+ {hex.startDate && hex.endDate && (
+
+ {new Date(hex.startDate).toLocaleDateString()} -{" "}
+ {new Date(hex.endDate).toLocaleDateString()}
+
+ )}
+
+ ))}
+
+ )}
+
+
+
+
+
+
+
+ );
+};
+
+export default UserApplied;
diff --git a/src/components/UserCard.tsx b/src/components/UserCard.tsx
index d552982..817358d 100644
--- a/src/components/UserCard.tsx
+++ b/src/components/UserCard.tsx
@@ -11,18 +11,6 @@ type Props = {
};
-// TODO: right now, the UserCard only displays the user's name and email. Create a new modal component that
-// pops up when the card is clicked. In this modal, list all the user's information including name, email, phoneNumber,
-// and userId.
-
-// TODO: Explore if you can display the email as a link to the user's email that will open up the user's
-// email client and start a new email to that user. Also explore if you can provide a link to the user's resume.
-
-// TODO: In our database structure, every user has a userId that is unique to them. This is the primary key of the user
-// and is referenced in their applications to all of our hexathons. Create a button that when clicked, will retrieve all of
-// the hexathons that the user has applied to. You can use the /applications endpoint of the registration service to do this
-// and the /hexathons endpoint of the hexathons service to get a list of all the hexathons.
-
const UserCard: React.FC = (props: Props) => {
return (
@@ -33,6 +21,12 @@ const UserCard: React.FC = (props: Props) => {
height="175px"
fontWeight="bold"
alignItems="center"
+ transition="background-color 0.2s ease"
+ _hover={{
+ backgroundColor: "gray.100",
+ _dark: { backgroundColor: "gray.700" },
+ cursor: "pointer",
+ }}
>
diff --git a/src/components/UserData.tsx b/src/components/UserData.tsx
index d5aeb78..abdb212 100644
--- a/src/components/UserData.tsx
+++ b/src/components/UserData.tsx
@@ -1,79 +1,84 @@
import React, { useEffect, useState } from "react";
import { apiUrl, Service } from "@hex-labs/core";
-import { SimpleGrid, Text } from "@chakra-ui/react";
+import {
+ Button,
+ SimpleGrid,
+ Text,
+ useDisclosure,
+ Box,
+} from "@chakra-ui/react";
import axios from "axios";
import UserCard from "./UserCard";
+import UserModal from "./UserModal";
+
+enum SortBy {
+ FIRST = "first",
+ LAST = "last",
+}
+
+interface User {
+ userId: string;
+ name: {
+ first: string;
+ last: string;
+ };
+ email: string;
+ phoneNumber?: string;
+}
const UserData: React.FC = () => {
-
- // The useState hook is used to store state in a functional component. The
- // first argument is the initial value of the state, and the second argument
- // is a function that can be used to update the state. The useState hook
- // returns an array with the first element being the state and the second
- // element being the function to update the state.
-
- const [users, setUsers] = useState([]);
-
- // The useEffect hook basicaly runs the code inside of it when the component
- // mounts. This is useful for making API calls and other things that should
- // only happen once when the component is loaded.
+ const [users, setUsers] = useState([]);
+ const [modalUser, setModalUser] = useState(null);
+ const { isOpen, onOpen, onClose } = useDisclosure();
useEffect(() => {
-
- // This is an example of an async function. The async keyword tells the
- // function to wait for the axios request to finish before continuing. This
- // is useful because we can't use the data from the request until it is
- // finished.
-
const getUsers = async () => {
-
- // TODO: Use the apiUrl() function to make a request to the /users endpoint of our USERS service. The first argument is the URL
- // of the request, which is created for the hexlabs api through our custom function apiUrl(), which builds the request URL based on
- // the Service enum and the following specific endpoint URL.
-
- // TODO: Also explore some of the other ways to configure the api call such as filtering and pagination.
- // Try to filter all the users with phone numbers starting with 470 or increase the amount of users returned from the default 50 (don't go above 100).
-
- // Postman will be your best friend here, because it's better to test out the API calls in Postman before implementing them here.
-
- // this is the endpoint you want to hit, but don't just hit it directly using axios, use the apiUrl() function to make the request
- const URL = 'https://users.api.hexlabs.org/users/hexlabs';
-
- // uncomment the line below to test if you have successfully made the API call and retrieved the data. The below line takes
- // the raw request response and extracts the actual data that we need from it.
- // setUsers(data?.data?.profiles);
+ const URL = apiUrl(Service.USERS, "users/hexlabs");
+ const { data } = await axios.get(URL);
+ setUsers(data);
};
- document.title = "Hexlabs Users"
+ document.title = "Hexlabs Users";
getUsers();
}, []);
- // ^^ The empty array at the end of the useEffect hook tells React that the
- // hook should only run once when the component is mounted. If you want it to
- // run every time a variable changes, you can put that variable in the array
- // and it will run every time that variable changes.
+ const openUserModal = (user: User) => {
+ setModalUser(user);
+ onOpen();
+ };
- // TODO: Create a function that sorts the users array based on the first name of the users. Then, create a button that
- // calls this function and sorts the users alphabetically by first name. You can use the built in sort() function to do this.
-
+ const sortByName = (field: SortBy) => {
+ const sortedUsers = [...users].sort((a, b) => {
+ const valA = a.name?.[field]?.toLowerCase() || "";
+ const valB = b.name?.[field]?.toLowerCase() || "";
+ return valA.localeCompare(valB);
+ });
+ setUsers(sortedUsers);
+ };
return (
<>
Hexlabs Users
- This is an example of a page that makes an API call to the Hexlabs API to get a list of users.
-
+
+ This page fetches user data from the Hexlabs API.
+
+
+
-
- {/* Here we are mapping every entry in our users array to a unique UserCard component, each with the unique respective
- data of each unique user in our array. This is a really important concept that we use a lot so be sure to familiarize
- yourself with the syntax - compartmentalizing code makes your work so much more readable. */}
- { users.map((user) => (
-
+ {users.map((user) => (
+ openUserModal(user)}>
+
+
))}
-
+
+
>
);
};
-export default UserData;
\ No newline at end of file
+export default UserData;
diff --git a/src/components/UserModal.tsx b/src/components/UserModal.tsx
new file mode 100644
index 0000000..92a353b
--- /dev/null
+++ b/src/components/UserModal.tsx
@@ -0,0 +1,118 @@
+import React from "react";
+import {
+ Modal,
+ ModalOverlay,
+ ModalContent,
+ ModalHeader,
+ ModalBody,
+ ModalFooter,
+ ModalCloseButton,
+ Button,
+ Text,
+ Stack,
+ Link,
+ useDisclosure,
+} from "@chakra-ui/react";
+import UserApplied from "./UserApplied";
+
+interface User {
+ userId: string;
+ name: {
+ first: string;
+ last: string;
+ };
+ email: string;
+ phoneNumber?: string;
+ resume?: string;
+}
+
+interface Props {
+ isOpen: boolean;
+ onClose: () => void;
+ user: User | null;
+}
+
+const UserModal: React.FC = ({ isOpen, onClose, user }) => {
+ const {
+ isOpen: isAppliedOpen,
+ onOpen: onAppliedOpen,
+ onClose: onAppliedClose,
+ } = useDisclosure();
+
+ return (
+ <>
+
+
+
+ User Details
+
+
+ {user ? (
+
+
+ {user.name.first} {user.name.last}
+
+
+
+ Email:{" "}
+
+ {user.email}
+
+
+
+
+ Phone: {user.phoneNumber ?? "N/A"}
+
+
+
+ User ID: {user.userId}
+
+
+
+ Resume:{" "}
+ {user.resume ? (
+
+ View Resume
+
+ ) : (
+ No Resume Found
+ )}
+
+
+ ) : (
+ No user selected.
+ )}
+
+
+
+
+ {user && (
+
+ )}
+
+
+
+
+
+ >
+ );
+};
+
+export default UserModal;