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

Jason Chen Onboarding Project #17

Open
wants to merge 5 commits into
base: startercode
Choose a base branch
from
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
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,8 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"prettier": "^3.3.3"
}
}
8 changes: 8 additions & 0 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@
color: #61dafb;
}

.title {
margin: 10%;
}

Button {
margin: 3px;
}

@keyframes App-logo-spin {
from {
transform: rotate(0deg);
Expand Down
16 changes: 11 additions & 5 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,15 @@ 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';
import UserData from "./components/UserData";

// a little bee ascii art
// const art =
Expand Down Expand Up @@ -48,14 +54,14 @@ export const App = () => {
// useAuth hook to retrieve the user's login details.
return (
<AuthProvider app={app}>

<Header children={undefined} />
{/* Setting up our React Router to route to all the different pages we may have */}
<Routes>
<Route path="/" element={<UserData />} />
</Routes>

<Footer />
</AuthProvider>
);
};

export default App;
export default App;
180 changes: 146 additions & 34 deletions src/components/UserCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,52 +3,164 @@ import {
Flex,
HStack,
Text,
Button,
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalCloseButton,
ModalBody,
ModalFooter,
useDisclosure,
Link,
} from "@chakra-ui/react";
import React from "react";
import React, { useState } from "react";
import { apiUrl, Service } from "@hex-labs/core";
import axios from "axios";

type Props = {
user: any;
};

const UserCard: React.FC<Props> = (props: Props) => {
const { isOpen, onOpen, onClose } = useDisclosure();
const [hexathons, setHexathons] = useState<any[]>([]);

// TODO: right now, the UserCard only displays the user's name and email. Create a new modal component <UserModal> that
// pops up when the card is clicked. In this modal, list all the user's information including name, email, phoneNumber,
// and userId.
// TODO: Create a modal that retrieves all of the hexathons that a user has an application for.
// Function to fetch hexathons based on user applications
const fetchUserHexathons = async () => {
try {
console.log(props.user.name.first, props.user.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.
// 1) Fetch all hexathons
const hexathonsRes = await axios.get(
apiUrl(Service.HEXATHONS, "/hexathons")
);
console.log("All hexathons", hexathonsRes.data);
const allHexathons = hexathonsRes.data;

// 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.
// 2) Loop through all hexathons and fetch applications by hexathonId
const userHexathons = await Promise.all(
allHexathons.map(async (hexathon: any) => {
const hexathonId = hexathon.id;

const UserCard: React.FC<Props> = (props: Props) => {
// Query applications by hexathonId and userId
console.log(
"Request URL:",
apiUrl(Service.REGISTRATION, "/applications"),
{
params: { hexathon: hexathonId, userId: props.user.userId },
}
);
const res = await axios.get(
apiUrl(Service.REGISTRATION, "/applications"),
{
params: { hexathon: hexathonId, userId: props.user.userId },
}
);

console.log("User's hexathons", res.data);

// Check if there are applications for this hexathon and return hexathon info if so
if (res.data.applications && res.data.applications.length > 0) {
return { id: hexathonId, name: hexathon.name };
} else {
return null;
}
})
);

// Filter out null results and update state
setHexathons(userHexathons.filter((hexathon) => hexathon !== null));
} catch (error) {
console.error("Error fetching user's hexathons", error);
}
};

// Resets hexathosn and closes modal
const handleClose = () => {
setHexathons([]); // Clear hexathons
onClose(); // Close the modal
};

return (
<Box
borderWidth="1px"
rounded="lg"
boxShadow="lg"
height="175px"
fontWeight="bold"
alignItems="center"
>
<Flex padding="2" flexDirection="column">
<HStack align="flex-end" justify="space-between">
<Text fontSize='xl'>{`${props.user.name.first} ${props.user.name.last}`}</Text>
</HStack>
<Text
fontSize="sm"
fontWeight="semibold"
justifyContent="justify"
mt="2"
>
{props.user.email}
</Text>
</Flex>
</Box>
<>
<Box
borderWidth="1px"
rounded="lg"
boxShadow="lg"
height="175px"
fontWeight="bold"
alignItems="center"
onClick={onOpen}
cursor="pointer"
>
<Flex padding="2" flexDirection="column">
<HStack align="flex-end" justify="space-between">
<Text fontSize="xl">{`${props.user.name.first} ${props.user.name.last}`}</Text>
</HStack>
<Text
fontSize="sm"
fontWeight="semibold"
justifyContent="justify"
mt="2"
>
{props.user.email}
</Text>
</Flex>
</Box>

{/* Modal */}
<Modal isOpen={isOpen} onClose={handleClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>{`${props.user.name.first} ${props.user.name.middle ? props.user.name.middle + " " : ""} ${props.user.name.last}`}</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Text>
<strong>Email:</strong>{" "}
<Link href={`mailto:${props.user.email}`} color="blue.500">
{props.user.email}
</Link>
</Text>
<Text>
<strong>Phone:</strong>{" "}
{props.user.phoneNumber
? props.user.phoneNumber.replace(
/(\d{3})(\d{3})(\d{4})/,
"($1) $2-$3"
)
: "N/A"}{" "}
</Text>
<Text>
<strong>User ID:</strong> {props.user.userId}
</Text>
{/* Display hexathons the user applied to */}
{hexathons.length > 0 && (
<>
<Text mt={4} fontWeight="bold">
Hexathons Applied:
</Text>
<ul>
{hexathons.map((hexathon) => (
<li key={hexathon.id}>{hexathon.name}</li>
))}
</ul>
</>
)}
</ModalBody>
<ModalFooter>
<Button colorScheme="teal" onClick={fetchUserHexathons}>
Show Applied Hexathons
</Button>
<Button onClick={handleClose} ml={3}>
Close
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};

export default UserCard;
export default UserCard;
66 changes: 50 additions & 16 deletions src/components/UserData.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import React, { useEffect, useState } from "react";
import { apiUrl, Service } from "@hex-labs/core";
import { SimpleGrid, Text } from "@chakra-ui/react";
import { SimpleGrid, Text, Button } from "@chakra-ui/react";
import axios from "axios";
import UserCard from "./UserCard";

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
Expand All @@ -19,14 +18,12 @@ const UserData: React.FC = () => {
// only happen once when the component is loaded.

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.
Expand All @@ -37,43 +34,80 @@ const UserData: React.FC = () => {
// 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';
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);
try {
const res = await axios.get(apiUrl(Service.USERS, "/users/hexlabs"));
// console.log(res.data[0].phoneNumber)
const filteredUsers = res.data.filter(
(user: any) => user.phoneNumber && user.phoneNumber.startsWith("470")
);
console.log(filteredUsers);
setUsers(filteredUsers);
console.log("Successful fetch");
} catch (error) {
console.log("Error fetching users", error);
}
};
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.


// 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 sortByFirst = () => {
const sortedUsers = [...users].sort((a, b) =>
a.name.first.localeCompare(b.name.first)
);
console.log("Sorted", sortedUsers); // Optional: to verify the sorted output
setUsers(sortedUsers); // Update the users state with the sorted array
};

// Randomize order of users (for testing)
const randomizeOrder = () => {
const randomizedUsers = [...users];
for (let i = randomizedUsers.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[randomizedUsers[i], randomizedUsers[j]] = [
randomizedUsers[j],
randomizedUsers[i],
];
}
setUsers(randomizedUsers);
};

return (
<>
<Text fontSize="4xl">Hexlabs Users</Text>
<Text fontSize="2xl">This is an example of a page that makes an API call to the Hexlabs API to get a list of users.</Text>

<Text className="title">
<Text fontSize="4xl">Hexlabs Users</Text>
<Text fontSize="2xl">
This is an example of a page that makes an API call to the Hexlabs API
to get a list of users.
</Text>
<Button onClick={sortByFirst} colorScheme="teal" marginBottom={4}>
Sort by First
</Button>
<Button onClick={randomizeOrder} colorScheme="teal" marginBottom={4}>
Randomize Order
</Button>
</Text>

<SimpleGrid columns={[2, 3, 5]} spacing={6} padding={10}>

{/* 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) => (
<UserCard user={user} />
))}

</SimpleGrid>
</>
);
};

export default UserData;
export default UserData;
6 changes: 3 additions & 3 deletions src/index.css
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}
Loading