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
15 changes: 12 additions & 3 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@ 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,
HeaderItem,
Footer,
} from "@hex-labs/core";

import UserData from './components/UserData';

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

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

<Footer />
</AuthProvider>
);
};
Expand Down
198 changes: 181 additions & 17 deletions src/components/UserCard.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,204 @@
import {
Box,
Button,
Flex,
HStack,
Link,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Spinner,
Stack,
Text,
useDisclosure,
} from "@chakra-ui/react";
import React from "react";
import { apiUrl, Service } from "@hex-labs/core";
import axios from "axios";
import React, { useState } from "react";

type Props = {
user: any;
};

const UserModal: React.FC<{
user: any;
isOpen: boolean;
onClose: () => void;
}> = ({ user, isOpen, onClose }) => {
const [hexathons, setHexathons] = useState<any[]>([]);
const [loadingHexathons, setLoadingHexathons] = useState(false);
const [hexathonError, setHexathonError] = useState<string | null>(null);
const [hasFetched, setHasFetched] = useState(false);

const emailHref = user?.email ? `mailto:${user.email}` : undefined;
const resumeHref =
user?.resumeUrl || user?.resumeLink || user?.resume?.url || user?.resume;

const fetchHexathons = async () => {
if (loadingHexathons) return;

setLoadingHexathons(true);
setHexathonError(null);
setHasFetched(true);

try {
const userId = user?.userId ?? user?.id ?? user?._id;

const applicationsResp = await axios.get(
apiUrl(Service.REGISTRATION, "/applications"),
{
params: {
userId,
hexathon: "hexlabs",
},
}
);

console.log("applicationsResp.data =", applicationsResp.data);

const applications: any[] = applicationsResp?.data ?? [];
const hexathonIds: string[] = applications
.map(
(app: any) =>
app?.hexathon ?? app?.hexathonId ?? app?.hexathonID
)
.filter(Boolean);

if (hexathonIds.length === 0) {
setHexathons([]);
return;
}
const hexathonsResp = await axios.get(
apiUrl(Service.HEXATHONS, "/hexathons")
);
const allHexathons: any[] = hexathonsResp?.data ?? [];

const matchedHexathons = allHexathons.filter(
(hex: any) =>
hexathonIds.includes(hex?.id) || hexathonIds.includes(hex?._id)
);

// 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.
setHexathons(matchedHexathons);
} catch (error) {
console.error("Error while fetching hexathons:", error);
setHexathonError("Failed to load hexathons for this user.");
} finally {
setLoadingHexathons(false);
}
};

// 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.
return (
<Modal isOpen={isOpen} onClose={onClose} size="lg">
<ModalOverlay />
<ModalContent>
<ModalHeader>
{`${user?.name?.first ?? ""} ${user?.name?.last ?? ""}`}
</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Stack spacing={2}>
<Text>
<strong>User ID:</strong> {user?.userId ?? "Unknown"}
</Text>

<Text>
<strong>Email:</strong>{" "}
{emailHref ? (
<Link href={emailHref} color="blue.500">
{user.email}
</Link>
) : (
"Not provided"
)}
</Text>

<Text>
<strong>Phone:</strong>{" "}
{user?.phoneNumber ?? "Not provided"}
</Text>

{resumeHref ? (
<Text>
<strong>Resume:</strong>{" "}
<Link href={resumeHref} color="blue.500" isExternal>
View Resume
</Link>
</Text>
) : null}

<Button
colorScheme="blue"
alignSelf="flex-start"
onClick={fetchHexathons}
isLoading={loadingHexathons}
loadingText="Loading hexathons"
size="sm"
>
View Applied Hexathons
</Button>

// 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.
{hexathonError ? (
<Text color="red.500" fontSize="sm">
{hexathonError}
</Text>
) : null}

{loadingHexathons ? (
<HStack spacing={2}>
<Spinner size="sm" />
<Text fontSize="sm">Fetching applications...</Text>
</HStack>
) : null}

{!loadingHexathons && hasFetched && !hexathonError ? (
hexathons.length > 0 ? (
<Stack spacing={1}>
<Text fontWeight="bold">Hexathons:</Text>
{hexathons.map((hex) => (
<Text key={hex?.id ?? hex?._id} fontSize="sm">
{hex?.name ?? hex?.id ?? hex?._id}
</Text>
))}
</Stack>
) : (
<Text fontSize="sm" color="gray.600">
No applications found for this user.
</Text>
)
) : null}
</Stack>
</ModalBody>
<ModalFooter>
<Button onClick={onClose}>Close</Button>
</ModalFooter>
</ModalContent>
</Modal>
);
};

const UserCard: React.FC<Props> = (props: Props) => {
const { isOpen, onOpen, onClose } = useDisclosure();

return (
<Box
borderWidth="1px"
rounded="lg"
boxShadow="lg"
height="175px"
fontWeight="bold"
alignItems="center"
borderWidth="1px"
rounded="lg"
boxShadow="lg"
height="175px"
fontWeight="bold"
alignItems="center"
cursor="pointer"
onClick={onOpen}
>
<Flex padding="2" flexDirection="column">
<HStack align="flex-end" justify="space-between">
<Text fontSize='xl'>{`${props.user.name.first} ${props.user.name.last}`}</Text>
<Text fontSize="xl">
{`${props.user.name.first} ${props.user.name.last}`}
</Text>
</HStack>
<Text
fontSize="sm"
Expand All @@ -47,6 +209,8 @@ const UserCard: React.FC<Props> = (props: Props) => {
{props.user.email}
</Text>
</Flex>

<UserModal user={props.user} isOpen={isOpen} onClose={onClose} />
</Box>
);
};
Expand Down
44 changes: 23 additions & 21 deletions src/components/UserData.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useEffect, useState } from "react";
import { apiUrl, Service } from "@hex-labs/core";
import { SimpleGrid, Text } from "@chakra-ui/react";
import { Button, SimpleGrid, Text } from "@chakra-ui/react";
import axios from "axios";
import UserCard from "./UserCard";

Expand All @@ -26,22 +26,16 @@ const UserData: React.FC = () => {
// 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 response = await axios.get(apiUrl(Service.USERS, "/users/hexlabs"), {
// Request a larger page (default is 50) and ask the API to prefilter.
params: { limit: 100, phoneNumberStartsWith: "470", page: 0 },
});
const profiles: any[] = response?.data ?? [];
// Fallback client-side filter so we only show users whose numbers start with 470.
const filtered = profiles.filter((profile: any) =>
profile?.phoneNumber?.startsWith("470")
);
setUsers(filtered);
};
document.title = "Hexlabs Users"
getUsers();
Expand All @@ -51,15 +45,23 @@ const UserData: React.FC = () => {
// 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 sortByFirstName = () => {
setUsers(prevUsers =>
[...prevUsers].sort((a, b) => {
const aName = a?.name?.first || "";
const bName = b?.name?.first || "";
return aName.localeCompare(bName);
})
);
};

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>
<Button colorScheme="blue" onClick={sortByFirstName} mt={4}>
Sort by first name
</Button>


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