Skip to content

Commit

Permalink
degree add /remove modal
Browse files Browse the repository at this point in the history
  • Loading branch information
AaDalal committed Feb 16, 2024
1 parent d454286 commit 2afe587
Show file tree
Hide file tree
Showing 6 changed files with 309 additions and 188 deletions.
165 changes: 165 additions & 0 deletions frontend/degree-plan/components/FourYearPlan/DegreeModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import styled from "@emotion/styled";
import type { DegreePlan } from "@/types";
import { useState } from "react";
import { useSWRCrud } from "@/hooks/swrcrud";
import { useSWRConfig } from "swr";
import ModalContainer from "../common/ModalContainer";


export type ModalKey = "plan-create" | "plan-rename" | "plan-remove" | "degree-add" | "degree-remove" | null; // null is closed

const getModalTitle = (modalState: ModalKey) => {
switch (modalState) {
case "plan-create":
return "Create a new degree plan";
case "plan-rename":
return "Rename degree plan";
case "plan-remove":
return "Remove degree plan";
default:
return "";
}
}

const ModalInteriorWrapper = styled.div<{ $row?: boolean }>`
display: flex;
flex-direction: ${props => props.$row ? "row" : "column"};
align-items: center;
padding: 1rem;
gap: .5rem;
text-align: center;
`;

const ModalInput = styled.input`
background-color: #fff;
color: black;
`;

const ModalButton = styled.button`
background-color: rgb(98, 116, 241);
border-radius: .25rem;
padding: .25rem .5rem;
color: white;
border: none;
`;

interface ModalInteriorProps {
modalKey: ModalKey;
modalObject: DegreePlan | null;
setActiveDegreeplanId: (arg0: DegreePlan["id"]) => void;
close: () => void;
}
const ModalInterior = ({ modalObject, modalKey, setActiveDegreeplanId }: ModalInteriorProps) => {
const { create: createDegreeplan, update: updateDegreeplan, remove: deleteDegreeplan, copy: copyDegreeplan } = useSWRCrud<DegreePlan>('/api/degree/degreeplans');
const { mutate } = useSWRConfig();

const create_degreeplan = (name: string) => {
createDegreeplan({ name: name })
.then((new_) => new_ && setActiveDegreeplanId(new_.id))
}

const [name, setName] = useState<string>(modalObject?.name || "");
const [degreeId, setDegreeId] = useState<number | null>(null);

if (modalKey === "plan-create") {
return (
<ModalInteriorWrapper $row>
<ModalInput type="text" placeholder="Name" value={name} onChange={e => setName(e.target.value)} />
<ModalButton onClick={
() => {
create_degreeplan(name);
close();
}
}>
Create
</ModalButton>
</ModalInteriorWrapper>
);
}
if (!modalKey || !modalObject) return <div></div>;

const rename_degreeplan = (name: string, id: DegreePlan["id"]) => void updateDegreeplan({ name }, id)
const remove_degreeplan = (id) => {
deleteDegreeplan(id)
mutate(
key => key.startsWith(`/api/degree/degreeplans/${id}`),
undefined,
{ revalidate: false }
)
}
const add_degree = (degreeplanId, degreeId) => {
updateDegreeplan({ degrees_ids: [...modalObject.degree_ids, degreeId] }, modalObject.id)
mutate(key => key && key.startsWith(`/api/degree/degreeplans/${degreeplanId}/fulfillments`)) // refetch the fulfillments
}
const remove_degree = (degreeplanId, degreeId) => {
updateDegreeplan({ degrees: modalObject.degrees.filter(id => id !== degreeId) }, modalObject.id)
mutate(key => key && key.startsWith(`/api/degree/degreeplans/${degreeplanId}/fulfillments`)) // refetch the fulfillments
}


switch (modalKey) {
case "plan-rename":
return (
<ModalInteriorWrapper>
<ModalInput type="text" placeholder="New name" value={name} onChange={e => setName(e.target.value)} />
<ModalButton onClick={() => {
rename_degreeplan(name, modalObject.id);
close();
}}>
Rename
</ModalButton>
</ModalInteriorWrapper>
);
case "plan-remove":
return (
<ModalInteriorWrapper>
<p>Are you sure you want to remove this degree plan?</p>
<ModalButton onClick={() => {
remove_degreeplan(modalObject.id)
close();
}}>Remove</ModalButton>
</ModalInteriorWrapper>
);
case "degree-add":
return (
<ModalInteriorWrapper>
<ModalInput type="number" placeholder="Degree Id" value={degreeId || undefined} onChange={e => setDegreeId(Number(e.target.value))} />
<ModalButton onClick={() => {
if (!degreeId) return;
add_degree(modalObject.id, degreeId)
close();
}}>Add</ModalButton>
</ModalInteriorWrapper>
);
case "degree-remove":
return (
<ModalInteriorWrapper>
<p>Are you sure you want to remove this degree? All of your planning for this degree will be lost</p>
<ModalButton onClick={() => {
remove_degree(modalObject.id, modalObject.id)
close();
}}>Remove</ModalButton>
</ModalInteriorWrapper>
);
}
return <div></div>;
}

interface DegreeModalProps {
setModalKey: (arg0: ModalKey) => void;
modalKey: ModalKey;
modalObject: DegreePlan | null;
setActiveDegreeplanId: (arg0: DegreePlan["id"]) => void;
}
const DegreeModal = ({ setModalKey, modalKey, modalObject, setActiveDegreeplanId, }: DegreeModalProps) => (
<ModalContainer
title={getModalTitle(modalKey)}
close={() => setModalKey(null)}
isBig
modalKey={modalKey}
>
<ModalInterior modalObject={modalObject} setActiveDegreeplanId={setActiveDegreeplanId} />
</ModalContainer>
)

export default DegreeModal;
130 changes: 8 additions & 122 deletions frontend/degree-plan/components/FourYearPlan/PlanPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,138 +50,24 @@ export const PanelContainer = styled.div`
height: 100%;
`;

type ModalKey = "create" | "rename" | "remove" | null; // null is closed

const getModalTitle = (modalState: ModalKey) => {
switch (modalState) {
case "create":
return "Create a new degree plan";
case "rename":
return "Rename degree plan";
case "remove":
return "Remove degree plan";
default:
return "";
}
}

const ModalInteriorWrapper = styled.div<{ $row?: boolean }>`
display: flex;
flex-direction: ${props => props.$row ? "row" : "column"};
align-items: center;
padding: 1rem;
gap: .5rem;
text-align: center;
`;

const ModalInput = styled.input`
background-color: #fff;
color: black;
`;

const ModalButton = styled.button`
background-color: rgb(98, 116, 241);
border-radius: .25rem;
padding: .25rem .5rem;
color: white;
border: none;
`;

interface ModalInteriorProps {
modalKey: ModalKey;
modalObject: DegreePlan | null;
create: (name: string) => void;
rename: (name: string, id: DegreePlan["id"]) => void;
remove: (id: DegreePlan["id"]) => void;
close: () => void;
}
const ModalInterior = ({ modalObject, modalKey, close, create, rename, remove }: ModalInteriorProps) => {
const [name, setName] = useState<string>(modalObject?.name || "");

if (modalKey === "create") {
return (
<ModalInteriorWrapper $row>
<ModalInput type="text" placeholder="Name" value={name} onChange={e => setName(e.target.value)} />
<ModalButton onClick={
() => {
create(name);
close();
}
}>
Create
</ModalButton>
</ModalInteriorWrapper>
);
}
if (!modalKey || !modalObject) return <div></div>;

switch (modalKey) {
case "rename":
return (
<ModalInteriorWrapper>
<ModalInput type="text" placeholder="New name" value={name} onChange={e => setName(e.target.value)} />
<ModalButton onClick={() => {
rename(name, modalObject.id);
close();
}}>
Rename
</ModalButton>
</ModalInteriorWrapper>
);
case "remove":
return (
<ModalInteriorWrapper>
<p>Are you sure you want to remove this degree plan?</p>
<ModalButton onClick={() => {
remove(modalObject.id)
close();
}}>Remove</ModalButton>
</ModalInteriorWrapper>
);
}
}

interface PlanPanelProps {
setModalKey: (arg0: string) => void;
modalKey: string;
setModalObject: (arg0: DegreePlan | null) => void;
setActiveDegreeplanId: (arg0: DegreePlan["id"]) => void;
activeDegreeplan: DegreePlan | undefined;
degreeplans: DegreePlan[] | undefined;
isLoading: boolean;
}

const PlanPanel = ({ setActiveDegreeplanId, activeDegreeplan, degreeplans, isLoading } : PlanPanelProps) => {
const { create: createDegreeplan, update: updateDegreeplan, remove: deleteDegreeplan, copy: copyDegreeplan } = useSWRCrud<DegreePlan>('/api/degree/degreeplans');

const [modalKey, setModalKey] = useState<ModalKey>(null);
const [modalObject, setModalObject] = useState<DegreePlan | null>(null); // stores the which degreeplan is being updated using the modal
const PlanPanel = ({ setModalKey, modalKey, setModalObject, setActiveDegreeplanId, activeDegreeplan, degreeplans, isLoading } : PlanPanelProps) => {
const { copy: copyDegreeplan } = useSWRCrud<DegreePlan>('/api/degree/degreeplans');
const [showStats, setShowStats] = useState(true);

return (
<>
<PanelContainer>
{modalKey && <ModalContainer
title={getModalTitle(modalKey)}
close={() => setModalKey(null)}
isBig
modalKey={modalKey}
>
<ModalInterior
modalObject={modalObject}
create={(name) => {
createDegreeplan({ name: name })
.then((new_) => new_ && setActiveDegreeplanId(new_.id))
}}
rename={(name, id) => void updateDegreeplan({ name }, id)}
remove={(id) => {
deleteDegreeplan(id)
mutate(
key => key.startsWith(`/api/degree/degreeplans/${id}`),
undefined,
{ revalidate: false }
)
}}
/>
</ModalContainer>
}
<PanelHeader>
<SelectListDropdown
itemType="degree plan"
Expand All @@ -195,14 +81,14 @@ const PlanPanel = ({ setActiveDegreeplanId, activeDegreeplan, degreeplans, isLoa
.then((copied) => copied && setActiveDegreeplanId(copied.id))
},
remove: (item: DegreePlan) => {
setModalKey("remove")
setModalKey("plan-remove")
setModalObject(item)
},
rename: (item: DegreePlan) => {
setModalKey("rename")
setModalKey("plan-rename")
setModalObject(item)
},
create: () => setModalKey("create")
create: () => setModalKey("plan-create")
}}
/>
<ShowStatsButton showStats={showStats} setShowStats={setShowStats} />
Expand Down
13 changes: 7 additions & 6 deletions frontend/degree-plan/components/FourYearPlan/Semesters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ const AddSemesterContainer = styled.div`
flex-direction: column;
gap: .5rem;
`;
const AddSemester = styled.div`

export const AddButton = styled.div`
width: 100%;
display: flex;
justify-content: space-between;
Expand All @@ -47,7 +48,7 @@ const AddSemester = styled.div`
color: white;
`

const EditSemester = styled(AddSemester)`
const EditButton = styled(AddButton)`
background-color: rgb(255, 193, 7);
`

Expand All @@ -63,22 +64,22 @@ const ModifySemesters = ({ addSemester, semesters, className }: ModifySemestersP
return (
// TODO: add a modal for this
<AddSemesterContainer className={className}>
<AddSemester role="button" onClick={() => addSemester(getNextSemester(semesterKeys[semesterKeys.length - 1] || "2023C"))}>
<AddButton role="button" onClick={() => addSemester(getNextSemester(semesterKeys[semesterKeys.length - 1] || "2023C"))}>
<Icon>
<i className="fas fa-plus"></i>
</Icon>
<div>
Add Semester
</div>
</AddSemester>
<EditSemester role="button">
</AddButton>
<EditButton role="button">
<Icon>
<i className="fas fa-edit"></i>
</Icon>
<div>
Edit Semesters
</div>
</EditSemester>
</EditButton>
</AddSemesterContainer>
)
}
Expand Down
Loading

0 comments on commit 2afe587

Please sign in to comment.