Skip to content

Commit

Permalink
feat: save progress
Browse files Browse the repository at this point in the history
  • Loading branch information
qamarq committed Dec 7, 2024
1 parent dc3f5d4 commit e8174d2
Show file tree
Hide file tree
Showing 10 changed files with 213 additions and 93 deletions.
6 changes: 4 additions & 2 deletions backend/app/controllers/schedules_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ export default class SchedulesController {
.andWhere('userId', userId)
.firstOrFail()

console.log(payload)

if (payload.name) {
currSchedule.name = payload.name
}
Expand Down Expand Up @@ -179,12 +181,12 @@ export default class SchedulesController {
}

if (payload.updatedAt) {
currSchedule.updatedAt = DateTime.fromJSDate(payload.updatedAt)
currSchedule.updatedAt = DateTime.fromISO(payload.updatedAt)
}

await currSchedule.save()

return { message: 'Schedule updated successfully.', currSchedule }
return { message: 'Schedule updated successfully.', currSchedule, payload }
}

/**
Expand Down
2 changes: 1 addition & 1 deletion backend/app/validators/schedule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,6 @@ export const updateScheduleValidator = vine.compile(
})
)
.optional(),
updatedAt: vine.date().optional(),
updatedAt: vine.string().optional(),
})
)
63 changes: 58 additions & 5 deletions frontend/src/actions/plans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,39 @@ interface CreatePlanResponseType {
};
}

interface PlanResponseType {
name: string;
userId: number;
id: number;
createdAt: string;
updatedAt: string;
courses: Array<{
id: string;
name: string;
department: string;
lecturer: string;
type: string;
ects: number;
semester: number;
groups: Array<{
id: number;
name: string;
day: string;
time: string;
room: string;
}>;
}>;
registrations: Array<{
id: string;
name: string;
}>;
}

interface DeletePlanResponseType {
success: boolean;
message: string;
}

export const createNewPlan = async ({ name }: { name: string }) => {
const isLogged = await auth({});
if (!isLogged) {
Expand All @@ -38,12 +71,14 @@ export const updatePlan = async ({
courses,
registrations,
groups,
updatedAt,
}: {
id: number;
name: string;
courses: Array<{ id: string }>;
registrations: Array<{ id: string }>;
groups: Array<{ id: number }>;
updatedAt: string;
}) => {
const isLogged = await auth({});
if (!isLogged) {
Expand All @@ -53,7 +88,7 @@ export const updatePlan = async ({
const data = await fetchToAdonis<CreatePlanResponseType>({
url: `/user/schedules/${id}`,
method: "PATCH",
body: JSON.stringify({ name, courses, registrations, groups }),
body: JSON.stringify({ name, courses, registrations, groups, updatedAt }),
});
if (!data) {
return false;
Expand All @@ -62,20 +97,38 @@ export const updatePlan = async ({
};

export const deletePlan = async ({ id }: { id: number }) => {
console.log(id);
const isLogged = await auth({});
if (!isLogged) {
return false;
}

const data = await fetchToAdonis<CreatePlanResponseType>({
const data = await fetchToAdonis<DeletePlanResponseType>({
url: `/user/schedules/${id}`,
method: "DELETE",
});
console.log(data);
if (!data || data.success !== true) {
if (!(data?.success ?? false)) {
return false;
}
revalidatePath("/plans");
return data;
};

export const getPlan = async ({ id }: { id: number }) => {
const isLogged = await auth({});
if (!isLogged) {
return false;
}

try {
const data = await fetchToAdonis<PlanResponseType>({
url: `/user/schedules/${id}`,
method: "GET",
});
if (!data) {
return false;
}
return data;
} catch (e) {
return false;
}
};
48 changes: 3 additions & 45 deletions frontend/src/app/plans/_components/PlansPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,10 @@
import { atom, useAtom } from "jotai";
import { PlusIcon } from "lucide-react";
import { useRouter } from "next/navigation";
import { useEffect, useRef } from "react";

import type { ExtendedCourse } from "@/atoms/planFamily";
import { planFamily } from "@/atoms/planFamily";
import { plansIds } from "@/atoms/plansIds";
import { PlanItem } from "@/components/PlanItem";
import type { Registration } from "@/lib/types";

import type { PlanResponseDataType } from "../page";

Expand All @@ -26,7 +23,6 @@ export function PlansPage({
}) {
const [plans, setPlans] = useAtom(plansAtom);
const router = useRouter();
const firstTime = useRef(true);

const addNewPlan = () => {
const uuid = crypto.randomUUID();
Expand All @@ -42,46 +38,6 @@ export function PlansPage({
setPlans([...plans, newPlan]);
};

const handleCreateOfflinePlansIfNotExists = () => {
if (firstTime.current) {
firstTime.current = false;
const tmpPlans: Array<{
id: string;
name: string;
synced: boolean;
onlineId: string;
courses: ExtendedCourse[];
registrations: Registration[];
createdAt: Date;
updatedAt: Date;
}> = [];
onlinePlans.forEach((onlinePlan) => {
if (plans.some((plan) => plan.onlineId === onlinePlan.id.toString())) {
return;
}

const uuid = crypto.randomUUID();
const newPlan = {
id: uuid,
name: onlinePlan.name,
synced: true,
onlineId: onlinePlan.id.toString(),
courses: onlinePlan.courses,
registrations: onlinePlan.registrations,
createdAt: new Date(onlinePlan.createdAt),
updatedAt: new Date(onlinePlan.updatedAt),
};

tmpPlans.push(newPlan);
});
setPlans([...plans, ...tmpPlans]);
}
};

// useEffect(() => {
// handleCreateOfflinePlansIfNotExists();
// }, []);

return (
<div className="container mx-auto max-h-full flex-1 flex-grow overflow-y-auto p-4">
<div className="grid grid-cols-2 gap-4 sm:justify-start md:grid-cols-3 lg:grid-cols-5 xl:grid-cols-6">
Expand All @@ -96,7 +52,7 @@ export function PlansPage({
key={plan.id}
id={plan.id}
name={plan.name}
synced={false}
synced={plan.synced}
onlineId={plan.onlineId}
/>
))}
Expand All @@ -112,6 +68,8 @@ export function PlansPage({
synced={true}
onlineId={plan.id.toString()}
onlineOnly={true}
groupCount={plan.courses.flatMap((c) => c.groups).length}
registrationCount={plan.registrations.length}
/>
);
})}
Expand Down
77 changes: 66 additions & 11 deletions frontend/src/app/plans/edit/[id]/_components/CreateNewPlanPage.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
"use client";

import { useMutation, useQuery } from "@tanstack/react-query";
import { format } from "date-fns";
import Link from "next/link";
import { useEffect, useRef, useState } from "react";
import { MdArrowBack } from "react-icons/md";
import { toast } from "sonner";

import { createNewPlan, updatePlan } from "@/actions/plans";
import { createNewPlan, getPlan, updatePlan } from "@/actions/plans";
import type { ExtendedCourse, ExtendedGroup } from "@/atoms/planFamily";
import { ClassSchedule } from "@/components/ClassSchedule";
import { GroupsAccordionItem } from "@/components/GroupsAccordion";
Expand All @@ -24,7 +25,6 @@ import {
SelectValue,
} from "@/components/ui/select";
import { Skeleton } from "@/components/ui/skeleton";
import { env } from "@/env.mjs";
import { usePlan } from "@/lib/usePlan";
import { registrationReplacer } from "@/lib/utils";
import type { LessonType } from "@/services/usos/types";
Expand Down Expand Up @@ -82,6 +82,7 @@ export function CreateNewPlanPage({

const handleSyncPlan = async () => {
setSyncing(true);
const updatedAtDate = new Date();
try {
const res = await updatePlan({
id: Number(plan.onlineId),
Expand All @@ -93,12 +94,17 @@ export function CreateNewPlanPage({
groups: plan.allGroups
.filter((g) => g.isChecked)
.map((g) => ({ id: g.groupOnlineId })),
updatedAt: updatedAtDate.toISOString(),
});
if (res === false) {
return toast.error("Nie udało się zaktualizować planu");
}
toast.success("Zaktualizowano plan");
plan.setSynced(true);
plan.setPlan((prev) => ({
...prev,
synced: true,
updatedAt: updatedAtDate,
}));
return true;
} finally {
setSyncing(false);
Expand All @@ -111,12 +117,6 @@ export function CreateNewPlanPage({
}
}, [plan]);

// useEffect(() => {
// if (!plan.synced && plan.onlineId !== null && !firstTime.current) {
// void handleUpdatePlan();
// }
// }, [plan.onlineId, plan.synced, firstTime]);

const inputRef = useRef<HTMLInputElement>(null);

const [faculty, setFaculty] = useState<string | null>(null);
Expand All @@ -125,7 +125,7 @@ export function CreateNewPlanPage({
queryKey: ["registrations", faculty],
queryFn: async () => {
const response = await fetch(
`${env.NEXT_PUBLIC_API_URL}/departments/${faculty}/registrations`,
`${process.env.NEXT_PUBLIC_API_URL}/departments/${faculty}/registrations`,
);

if (!response.ok) {
Expand All @@ -140,7 +140,7 @@ export function CreateNewPlanPage({
mutationKey: ["courses"],
mutationFn: async (registrationId: string) => {
const response = await fetch(
`${env.NEXT_PUBLIC_API_URL}/departments/${faculty}/registrations/${encodeURIComponent(registrationId)}/courses`,
`${process.env.NEXT_PUBLIC_API_URL}/departments/${faculty}/registrations/${encodeURIComponent(registrationId)}/courses`,
);

if (!response.ok) {
Expand All @@ -151,6 +151,61 @@ export function CreateNewPlanPage({
},
});

const handleUpdateLocalPlan = async () => {
firstTime.current = false;
const onlinePlan = await getPlan({ id: Number(plan.onlineId) });
if (onlinePlan === false) {
return toast.error("Nie udało się pobrać planu");
}
for (const registration of onlinePlan.registrations) {
coursesFn.mutate(registration.id, {
onSuccess: (courses) => {
const extendedCourses: ExtendedCourse[] = courses
.map((c) => {
const groups: ExtendedGroup[] = c.groups.map((g) => ({
groupId: g.group + c.id + g.type,
groupNumber: g.group.toString(),
groupOnlineId: g.id,
courseId: c.id,
courseName: c.name,
isChecked:
onlinePlan.courses
.find((oc) => oc.id === c.id)
?.groups.some((og) => og.id === g.id) ?? false,
courseType: g.type,
day: g.day,
lecturer: g.lecturer,
registrationId: c.registrationId,
week: g.week.replace("-", "") as "" | "TN" | "TP",
endTime: g.endTime.split(":").slice(0, 2).join(":"),
startTime: g.startTime.split(":").slice(0, 2).join(":"),
}));
return {
id: c.id,
name: c.name,
isChecked: onlinePlan.courses.some((oc) => oc.id === c.id),
registrationId: c.registrationId,
type: c.groups.at(0)?.type ?? ("" as LessonType),
groups,
};
})
.sort((a, b) => a.name.localeCompare(b.name));
plan.addRegistration(registration, extendedCourses, true);
},
onError: () => {
toast.error("Nie udało się pobrać kursów");
},
});
}
return true;
};

useEffect(() => {
if (plan.onlineId !== null && plan.toCreate && firstTime.current) {
void handleUpdateLocalPlan();
}
}, [plan]);

return (
<div className="flex w-full flex-1 flex-col items-center justify-center gap-5 py-3 md:flex-row md:items-start">
<div className="flex max-h-screen w-full flex-none flex-col items-center justify-center gap-2 px-2 md:ml-4 md:w-[350px] md:flex-col">
Expand Down
4 changes: 1 addition & 3 deletions frontend/src/app/plans/edit/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import type { Metadata } from "next";
import { notFound } from "next/navigation";
import React from "react";

import { env } from "@/env.mjs";

import { CreateNewPlanPage } from "./_components/CreateNewPlanPage";

interface PageProps {
Expand All @@ -21,7 +19,7 @@ export default async function CreateNewPlan({ params }: PageProps) {
}

const facultiesRes = (await fetch(
`${env.NEXT_PUBLIC_API_URL}/departments`,
`${process.env.NEXT_PUBLIC_API_URL}/departments`,
).then((r) => r.json())) as Array<{ id: string; name: string }> | null;
if (!facultiesRes) {
return notFound();
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/atoms/planFamily.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ export const planFamily = atomFamily(
courses: [] as ExtendedCourse[],
registrations: [] as Registration[],
createdAt: new Date(),
updatedAt: new Date(),
onlineId: null as string | null,
toCreate: true as boolean,
synced: false,
},
undefined,
Expand Down
Loading

0 comments on commit e8174d2

Please sign in to comment.