diff --git a/app/api/study-plan/route.ts b/app/api/study-plan/route.ts index f65f4f3..bbcd8f3 100644 --- a/app/api/study-plan/route.ts +++ b/app/api/study-plan/route.ts @@ -65,3 +65,25 @@ export async function GET(req: NextRequest) { }); return res; } + +export async function DELETE(req: NextRequest) { + const user = await getUser(req); + + if (!user) { + return NextResponse.json({}, { status: 401 }); + } + + const semesterRepository = AppDataSource.getRepository(Semester); + + const deleteRes = await semesterRepository.delete({ + studyPlan: { + user: { + id: user.id, + }, + }, + }); + if (deleteRes.affected === 0) { + return NextResponse.json({}, { status: 404 }); + } + return NextResponse.json({}, { status: 200 }); +} diff --git a/components/Header/index.tsx b/components/Header/index.tsx index f965976..c211107 100644 --- a/components/Header/index.tsx +++ b/components/Header/index.tsx @@ -3,6 +3,7 @@ import { AuditOutlined, BugOutlined, + DeleteOutlined, ExperimentOutlined, InfoCircleOutlined, PoweroffOutlined, @@ -32,11 +33,13 @@ export interface HeaderProps { avatarUrl: string; } | null; isMobile?: boolean; + onResetStudyPlan?: () => void; } export default function Header({ isLoading = false, user, isMobile = false, + onResetStudyPlan, }: HeaderProps) { return ( , }, + { + key: "4", + label: ( + + ), + icon: , + }, ], }} > diff --git a/components/Header/useHeader.ts b/components/Header/useHeader.ts index c512338..af8d604 100644 --- a/components/Header/useHeader.ts +++ b/components/Header/useHeader.ts @@ -1,8 +1,10 @@ +import useSession from "@/services/apiClient/useSession"; import { useLearningPlatformCurrentUser } from "@/services/learningPlatform/hooks/useLearningPlatformCurrentUser"; import { HeaderProps } from "."; export default function useHeader(): HeaderProps { + const { api } = useSession(); const currentUserQuery = useLearningPlatformCurrentUser(); const avatarUrl = currentUserQuery.data?.me.avatarUrl; @@ -21,8 +23,20 @@ export default function useHeader(): HeaderProps { } : null; + async function onResetStudyPlan() { + try { + await api?.resetStudyPlan(); + + location.reload(); + } catch (err) { + alert((err as Error).message || "Unknown error"); + } + } + return { isLoading: currentUserQuery.isLoading, user, + + onResetStudyPlan, }; } diff --git a/components/ModulesListSection.tsx b/components/ModulesListSection.tsx index 498944e..7084770 100644 --- a/components/ModulesListSection.tsx +++ b/components/ModulesListSection.tsx @@ -16,6 +16,7 @@ export interface ModulesListSectionProps extends Omit { showAddItemButton?: boolean; isHovered?: boolean; isDragInProgress?: boolean; + isLoading?: boolean; onAddItem?: () => void; } @@ -27,6 +28,7 @@ function ModulesListSection({ showAddItemButton = true, isHovered = false, isDragInProgress = false, + isLoading = false, onAddItem, ...rest @@ -116,16 +118,18 @@ function ModulesListSection({ }} > {provided.placeholder} - {modules.map((module, index) => ( - - ))} + {modules + .filter((i) => (isLoading ? true : i.module != null)) + .map((module, index) => ( + + ))} )} diff --git a/components/Semester.tsx b/components/Semester.tsx index d2a3e95..76b3cd8 100644 --- a/components/Semester.tsx +++ b/components/Semester.tsx @@ -20,6 +20,8 @@ export interface SemesterProps { setMouseUpInboxId: (inboxId: string | null) => void; setHoveredInboxId: (inboxId: string | null) => void; + + isLoading?: boolean; } function SemesterCard({ semester, @@ -28,6 +30,7 @@ function SemesterCard({ draggedModules, setMouseUpInboxId, setHoveredInboxId, + isLoading = false, }: SemesterProps) { const [isHovered, setIsHovered] = useState(false); @@ -157,6 +160,7 @@ function SemesterCard({ }} {}} /> setMouseUpInboxId( @@ -204,6 +209,7 @@ function SemesterCard({ disabled={isPastSemester || isstandardDisabled} /> {}} /> setMouseUpInboxId(`droppable:semester:${semester.id}:reassessments`) diff --git a/components/SemestersList/index.tsx b/components/SemestersList/index.tsx index 5ebb6cc..ff7a9e4 100644 --- a/components/SemestersList/index.tsx +++ b/components/SemestersList/index.tsx @@ -128,6 +128,7 @@ export default function SemestersList({ draggedModules={draggedModules} setMouseUpInboxId={setMouseUpInboxId} setHoveredInboxId={setHoveredInboxId} + isLoading={isLoading} /> ))} diff --git a/services/apiClient/index.ts b/services/apiClient/index.ts index 7f5096e..717c506 100644 --- a/services/apiClient/index.ts +++ b/services/apiClient/index.ts @@ -74,6 +74,22 @@ export class StudyPlannerApiClient { return data; } + + public async resetStudyPlan(): Promise<{}> { + const res = await fetch(this.url + "/study-plan", { + headers: { + "Content-Type": "application/json", + Authorization: this.accessToken, + }, + method: "DELETE", + }); + if (!res.ok) { + throw new Error(`Failed to reset study plan (code ${res.status})`); + } + const data: {} = await res.json(); + + return data; + } } export type UpdateSemesterModuleInput = Record;