Skip to content

Commit

Permalink
Created Context for Qualifier 1 Page (#502)
Browse files Browse the repository at this point in the history
* Refactored QualifiersPageRoles, added context, and updated documentataion on COP JSON structure

* Removed copData fetching from QualifiersPageRoles and lifted copData to QualifiersContext

* Cleaned up strings for JSON

* Removed extra backtick in markdown
  • Loading branch information
bzzz-coding authored Feb 29, 2024
1 parent fd766f0 commit 3aa6974
Show file tree
Hide file tree
Showing 6 changed files with 269 additions and 79 deletions.
2 changes: 1 addition & 1 deletion frontend/src/api_data/copData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,4 @@ function fetchAllCopData() {
return copData;
}

export { copDatum, fetchCopDataById, fetchAllCopData };
export { copData, copDatum, fetchCopDataById, fetchAllCopData };
66 changes: 66 additions & 0 deletions frontend/src/context/QualifiersContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React, { createContext, useContext, useState, ReactNode } from "react";
import { copData, copDatum } from "api_data/copData";

// Types
type QualifiersType = {
COPs: {
[copName: string]: string[];
};
// availabilityTimeSlots: string[];
};

type QualifiersContextType = {
copData: copDatum[];
qualifiers: QualifiersType;
updateQualifiers: (newQualifiers: QualifiersType) => void;
};

// Create the context
const QualifiersContext = createContext<QualifiersContextType | undefined>(
undefined
);

// Custom hook to use the qualifiers context
export const useQualifiersContext = () => {
const context = useContext(QualifiersContext);
if (!context) {
throw new Error(
"useQualifiersContext must be used within a QualifiersProvider"
);
}
return context;
};

// Provider component
export const QualifiersProvider: React.FC<{ children: ReactNode }> = ({
children,
}) => {
// Initial state for qualifiers only for testing purposes, actual data should be fetched once the user has an account, and then used to setQualifiers, by using useEffect
const initialState: QualifiersType = {
COPs: {
// Uncomment data below for testing
// "UI/UX": [
// "UI/UX_Designer",
// "UX_Researcher",
// "UX_Writing",
// "UX_Practice_Lead",
// ],
// Data_Science: ["Data_Scientist", "Data_Analyst"],
},
// availabilityTimeSlots: [],
};

const [qualifiers, setQualifiers] = useState<QualifiersType>(initialState);

const updateQualifiers = (newQualifiers: QualifiersType) => {
setQualifiers(newQualifiers);
};

return (
<QualifiersContext.Provider
value={{ copData, qualifiers, updateQualifiers }}
>
{children}
</QualifiersContext.Provider>
);
};
5 changes: 3 additions & 2 deletions frontend/src/pages/QualifierPage/QualifierPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Outlet, useLoaderData } from "react-router-dom";

// Internal Imports
import { ProgressBar } from "components/components";
import { QualifiersProvider } from "context/QualifiersContext";

// Lazy Imports
const QualifierPageRoles = React.lazy(() => import("./QualifierPageRoles"));
Expand Down Expand Up @@ -47,13 +48,13 @@ function QualifierContent() {

function QualifierPage() {
return (
<Fragment>
<QualifiersProvider>
<Suspense fallback={<div>...Loading</div>}>
<main className="mx-6">
<Outlet />
</main>
</Suspense>
</Fragment>
</QualifiersProvider>
);
}

Expand Down
3 changes: 3 additions & 0 deletions frontend/src/pages/QualifierPage/QualifierPageCalendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@ import {
import { QualifierNav, QualifierTitle } from "./QualifierComponents";
import { timezones } from "../../api_data/timezoneData";
import { iconArrowLeft } from "assets/images/images";
// import { useQualifiersContext } from "context/QualifiersContext";

function QualifierPageCalendar() {
// const {qualifiers} = useQualifiersContext();
// console.log(qualifiers);
const navigate = useNavigate();

return (
Expand Down
230 changes: 154 additions & 76 deletions frontend/src/pages/QualifierPage/QualifierPageRoles.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,171 @@
// External Imports
import React, { Fragment, useEffect, useState } from "react";
import React, { Fragment, useState } from "react";
import { useNavigate } from "react-router-dom";

// Internal Imports
import { Button, Chip } from "components/components";
import { QualifierNav, QualifierTitle } from "./QualifierComponents";
import { fetchAllCopData, copDatum } from "api_data/copData";
import { useQualifiersContext } from "context/QualifiersContext";
import { onKey } from "components/Utility/utils";

function QualifierPageRoles() {
const [data, setData] = useState<copDatum[]>([] as copDatum[]);
const QualifierPageRoles: React.FC = () => {
// Call useContext at the top level of your component to read and subscribe to context
const { copData, qualifiers, updateQualifiers } = useQualifiersContext();

const navigate = useNavigate();

useEffect(() => {
setData(fetchAllCopData());
}, []);
// Initialize selectedRoles state with COP qualifiers from the context
const [selectedRoles, setSelectedRoles] = useState<{
[copName: string]: { [roleName: string]: boolean };
}>(() => {
// Convert qualifiers.COP to selectedRoles format
const selectedRolesFromQualifiers: {
[copName: string]: { [roleName: string]: boolean };
} = {};
for (const copName in qualifiers.COPs) {
selectedRolesFromQualifiers[copName] = {};
qualifiers.COPs[copName].forEach((role: string) => {
selectedRolesFromQualifiers[copName][role] = true;
});
}
return selectedRolesFromQualifiers;
});

// Toggle role selection
const handleRoleSelect = (copName: string, roleName: string) => {
setSelectedRoles((prevState) => ({
...prevState,
[copName]: {
...prevState[copName],
[roleName]: !prevState[copName]?.[roleName],
},
}));
};

// Toggle "Select all"/"Deselect all" within each COP
const handleSelectAll = (copName: string, roles: string[]) => {
setSelectedRoles((prevState) => {
const allSelected = roles.every((role) => {
const cleanRoleName = role.replace(/\s+/g, "_");
return prevState[copName]?.[cleanRoleName];
}); // Check if all roles are selected
const updatedRolesState = roles.reduce(
(acc: { [key: string]: boolean }, role) => {
const cleanRoleName = role.replace(/\s+/g, "_");
acc[cleanRoleName] = !allSelected; // Set all roles to true if not all are currently selected, otherwise set all to false
return acc;
},
{}
);
return {
...prevState,
[copName]: updatedRolesState,
};
});
};

// Update qualifiers in the context
const handleUpdateCopQualifiers = () => {
const updatedCopQualifiers: { [copName: string]: string[] } = {};

for (const copName in selectedRoles) {
// console.log(`Cop Name: ${copName}`);
const roles = selectedRoles[copName];
// console.log("Roles:");

for (const roleName in roles) {
// console.log(`${roleName} (${typeof roleName}): ${roles[roleName]}`);
if (roles[roleName]) {
// If role is true, add it to the roles array
updatedCopQualifiers[copName] = updatedCopQualifiers[copName] || [];
updatedCopQualifiers[copName].push(roleName);
}
}
}

// console.log("Updated Cop Qualifiers:", updatedCopQualifiers);
// console.log("Old Qualifiers:", qualifiers);
const newQualifiers = { ...qualifiers, COPs: updatedCopQualifiers };

console.log("New Qualifiers:", newQualifiers);
updateQualifiers(newQualifiers); // Update qualifiers
};

return (
<Fragment>
<QualifierTitle title="What type of role are you looking for?">
Select as many roles as you'd like to find opportunities in.
</QualifierTitle>
<div className="flex-center-x">
{data.map((datum, index) => {
{copData.map((cop, index) => {
const cleanCopName = cop.title.replace(/\s+/g, "_");

return (
<Fragment key={index}>
<CopRoles copDatum={datum} />
{index < data.length - 1 && (
<div className="row fill flex-center-x my-1">
<div className="col-8">
<div className="row align-center my-3 justify-between">
<div className="flex items-center">
<cop.icon
fill="black"
strokeWidth="0.2"
height="21"
aria-hidden="true"
/>
<span className="title-4 ml-1">
{cleanCopName.replace(/_/g, " ")}
</span>
</div>
<span
className="links"
tabIndex={0}
role="button"
aria-pressed={
selectedRoles[cleanCopName] &&
Object.values(selectedRoles[cleanCopName]).every(
(role) => role
)
}
onClick={() => handleSelectAll(cleanCopName, cop.roles)}
onKeyDown={(e) =>
onKey(
() => handleSelectAll(cleanCopName, cop.roles),
"Enter"
)(e)
}
>
{selectedRoles[cleanCopName] &&
Object.values(selectedRoles[cleanCopName]).every(
(role) => role
)
? "Deselect All"
: "Select All"}
</span>
</div>
<div>
{cop.roles.map((role, index) => {
const cleanRoleName = role.replace(/\s+/g, "_");

return (
<Chip
key={index}
variant="multi"
addClass="mr-4 mb-4"
checked={
selectedRoles[cleanCopName]?.[cleanRoleName] ||
false
}
value={role.replace(/_/g, " ")}
onClick={() => {
handleRoleSelect(cleanCopName, cleanRoleName);
}}
/>
);
})}
</div>
</div>
</div>
{index < copData.length - 1 && (
<hr className="row col-8 qroles-border"></hr>
)}
</Fragment>
Expand All @@ -38,77 +177,16 @@ function QualifierPageRoles() {
size="lg"
length="long"
color="primary"
onClick={() => navigate("../2")}
onClick={() => {
handleUpdateCopQualifiers();
navigate("../2");
}}
>
Next
</Button>
</QualifierNav>
</Fragment>
);
}

interface CopRolesProps {
copDatum: copDatum;
}

function CopRoles({ copDatum }: CopRolesProps) {
const [isAllSelected, setIsAllSelected] = useState(false);
const [isRoleChecked, setIsRoleChecked] = useState<boolean[]>(
Array(copDatum.roles.length).fill(false)
);

function handleSelectAll() {
const copy = isRoleChecked.map((_) => !isAllSelected);
setIsRoleChecked(copy);
setIsAllSelected(!isAllSelected);
}

return (
<div className="row fill flex-center-x my-1">
<div className="col-8">
<div className="row align-center my-3 justify-between">
<div className="flex items-center">
<copDatum.icon
fill="black"
strokeWidth="0.2"
height="21"
aria-hidden="true"
/>
<span className="title-4 ml-1">{copDatum.title}</span>
</div>
<span
className="links"
tabIndex={0}
role="button"
aria-pressed={isAllSelected}
onClick={handleSelectAll}
onKeyDown={(e) => onKey(handleSelectAll, "Enter")(e)}
>
{isAllSelected ? "Deselect all" : "Select all"}
</span>
</div>
<div>
{copDatum.roles.map((role, index) => {
return (
<Chip
key={index}
variant="multi"
addClass="mr-4 mb-4"
checked={isRoleChecked[index]}
value={role}
onClick={(active, value) => {
const copy = [...isRoleChecked];
copy[index] = active;
setIsRoleChecked(copy);
console.log(isRoleChecked);
}}
/>
);
})}
</div>
</div>
</div>
);
}
};

export default QualifierPageRoles;
Loading

0 comments on commit 3aa6974

Please sign in to comment.