Skip to content

Commit

Permalink
Merge pull request #707 from sandboxnu/milo/duplicate-plan-button
Browse files Browse the repository at this point in the history
Milo/duplicate plan button
  • Loading branch information
Suraj-Ram authored Feb 25, 2024
2 parents f97a100 + 4070866 commit ffa5114
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 4 deletions.
22 changes: 19 additions & 3 deletions packages/frontend/components/Plan/AddPlanModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ import { BlueButton } from "../Button";
import { PlanInput, PlanSelect } from "../Form";
import { HelperToolTip } from "../Help";
import { IsGuestContext } from "../../pages/_app";
import { GraduateToolTip } from "../GraduateTooltip";
import { getLocalPlansLength } from "../../utils/plan/getLocalPlansLength";

interface AddPlanModalProps {
setSelectedPlanId: Dispatch<SetStateAction<number | undefined | null>>;
Expand Down Expand Up @@ -169,11 +171,25 @@ export const AddPlanModal: React.FC<AddPlanModalProps> = ({
</Stack>
);

const disableButton = isGuest && getLocalPlansLength() > 4;

return (
<>
<BlueButton leftIcon={<AddIcon />} onClick={onOpen} ml="xs" size="md">
New Plan
</BlueButton>
<GraduateToolTip
label="Maximum number of plans reached on guest mode. Delete an existing plan or create an account."
shouldWrapChildren
isDisabled={!disableButton}
>
<BlueButton
leftIcon={<AddIcon />}
onClick={onOpen}
ml="xs"
size="md"
disabled={disableButton}
>
New Plan
</BlueButton>
</GraduateToolTip>
<Modal isOpen={isOpen} onClose={() => onCloseAddPlanModal()} size="md">
<ModalOverlay />
<ModalContent>
Expand Down
108 changes: 108 additions & 0 deletions packages/frontend/components/Plan/DuplicatePlanButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { CopyIcon } from "@chakra-ui/icons";
import { API } from "@graduate/api-client";
import { CreatePlanDto, PlanModel } from "@graduate/common";
import { useRouter } from "next/router";
import { Dispatch, SetStateAction, useState, useContext } from "react";
import { toast } from "react-toastify";
import { mutate } from "swr";
import {
USE_STUDENT_WITH_PLANS_SWR_KEY,
useStudentWithPlans,
} from "../../hooks";
import {
cleanDndIdsFromPlan,
cleanDndIdsFromStudent,
handleApiClientError,
} from "../../utils";
import { BlueButton } from "../Button";
import { IsGuestContext } from "../../pages/_app";
import { getLocalPlansLength } from "../../utils/plan/getLocalPlansLength";
import { GraduateToolTip } from "../GraduateTooltip";

interface DuplicatePlanButton {
plan: PlanModel<string>;
setSelectedPlanId: Dispatch<SetStateAction<number | undefined | null>>;
}

export const DuplicatePlanButton: React.FC<DuplicatePlanButton> = ({
plan,
setSelectedPlanId,
}) => {
const router = useRouter();
const { isGuest } = useContext(IsGuestContext);
const [buttonLoading, setButtonLoading] = useState(false);
const { student } = useStudentWithPlans();

const duplicatePlan = async () => {
if (!student) return;
setButtonLoading(true);

const updatedPlan: CreatePlanDto = {
name: "Copy of " + plan.name,
catalogYear: plan.catalogYear,
major: plan.major,
concentration: plan.concentration,
schedule: cleanDndIdsFromPlan(plan).schedule,
};

let createdPlanId: number;
if (isGuest) {
// Create the duplicated plan in local storage
createdPlanId = student.plans.length + 1;
const planLocalStorage: PlanModel<null> = {
...updatedPlan,
id: createdPlanId,
student: cleanDndIdsFromStudent(student),
createdAt: new Date(),
updatedAt: new Date(),
} as PlanModel<null>;
try {
window.localStorage.setItem(
"student",
JSON.stringify({
...student,
plans: [...student.plans, planLocalStorage],
})
);
} catch (error) {
toast.error("Maximum local storage quota exceed. Too many plans.");
setButtonLoading(false);
return;
}
} else {
try {
const createdPlan = await API.plans.create(updatedPlan);
createdPlanId = createdPlan.id;
} catch (error) {
handleApiClientError(error as Error, router);
setButtonLoading(false);
return;
}
}
mutate(USE_STUDENT_WITH_PLANS_SWR_KEY);
toast.success("Plan duplicated successfully");
setSelectedPlanId(createdPlanId);
setButtonLoading(false);
};

const disableButton = isGuest && getLocalPlansLength() > 4;

return (
<GraduateToolTip
label="Maximum number of plans reached on guest mode. Delete an existing plan or create an account."
shouldWrapChildren
isDisabled={!disableButton}
>
<BlueButton
onClick={duplicatePlan}
leftIcon={<CopyIcon />}
ml="xs"
size="md"
isLoading={buttonLoading}
disabled={disableButton}
>
Duplicate Plan
</BlueButton>
</GraduateToolTip>
);
};
9 changes: 8 additions & 1 deletion packages/frontend/pages/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,11 @@ import {
Sidebar,
TransferCourses,
} from "../components";
import { DuplicatePlanButton } from "../components/Plan/DuplicatePlanButton";
import { fetchStudentAndPrepareForDnd, useStudentWithPlans } from "../hooks";
import {
cleanDndIdsFromPlan,
DELETE_COURSE_AREA_DND_ID,
cleanDndIdsFromPlan,
handleApiClientError,
logger,
toast,
Expand Down Expand Up @@ -278,6 +279,12 @@ const HomePage: NextPage = () => {
/>
<AddPlanModal setSelectedPlanId={setSelectedPlanId} />
{selectedPlan && <EditPlanModal plan={selectedPlan} />}
{selectedPlan && (
<DuplicatePlanButton
plan={selectedPlan}
setSelectedPlanId={setSelectedPlanId}
/>
)}
{selectedPlan && (
<DeletePlanModal
setSelectedPlanId={setSelectedPlanId}
Expand Down
15 changes: 15 additions & 0 deletions packages/frontend/utils/plan/getLocalPlansLength.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* Get the length of the plans in the local storage.
*
* @returns The length of the plans in the local storage.
*/
export const getLocalPlansLength = (): number => {
const localStorageData = window.localStorage.getItem("student");
if (localStorageData) {
const parsedData = JSON.parse(localStorageData);
if (parsedData && Array.isArray(parsedData.plans)) {
return parsedData.plans.length;
}
}
return 0;
};

0 comments on commit ffa5114

Please sign in to comment.