Skip to content

Commit 459c1b4

Browse files
committed
Added compare page + dropdowns for faster compare
1 parent 0aa6c03 commit 459c1b4

File tree

4 files changed

+158
-41
lines changed

4 files changed

+158
-41
lines changed

course-matrix/frontend/src/components/app-sidebar.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
} from "@/components/ui/sidebar";
1717
import Logo from "./logo";
1818
import { Link, useLocation } from "react-router-dom";
19-
import { Home, Calendar, Bot } from "lucide-react";
19+
import { Home, Calendar, Bot, GitCompareIcon } from "lucide-react";
2020

2121
const checkIfActive = (url: string, current: string) => {
2222
return url === current;
@@ -57,6 +57,12 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
5757
isActive: checkIfActive("/dashboard/assistant", location.pathname),
5858
icon: Bot,
5959
},
60+
{
61+
title: "Timetable Compare",
62+
url: "/dashboard/compare",
63+
isActive: checkIfActive("/dashboard/compare", location.pathname),
64+
icon: GitCompareIcon,
65+
},
6066
],
6167
},
6268
],

course-matrix/frontend/src/pages/Compare/CompareTimetables.tsx

Lines changed: 149 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,83 @@
1-
import { useGetTimetableQuery } from "@/api/timetableApiSlice";
1+
import { useGetTimetableQuery, useGetTimetablesQuery } from "@/api/timetableApiSlice";
22
import { Button } from "@/components/ui/button";
33
import { Timetable, TimetableEvents } from "@/utils/type-utils";
44
import { useEffect, useState } from "react";
55
import { Link, useSearchParams } from "react-router-dom";
66
import Calendar from "../TimetableBuilder/Calendar";
77
import { useGetEventsQuery } from "@/api/eventsApiSlice";
88
import { Spinner } from "@/components/ui/spinner";
9+
import { zodResolver } from "@hookform/resolvers/zod";
10+
import { useForm } from "react-hook-form";
11+
import { z } from "zod";
12+
import { CompareFormSchema } from "../Home/TimetableCompareButton";
13+
import { format } from "path";
14+
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
15+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
16+
import { SemesterIcon } from "@/components/semester-icon";
17+
import { GitCompareArrows } from "lucide-react";
918

1019
export const CompareTimetables = () => {
11-
const [queryParams] = useSearchParams();
12-
const validParams = queryParams.has("id1") && queryParams.has("id2");
1320

14-
if (!validParams) {
15-
return (
16-
<div className="w-full text-red-500 text-center mt-10">
17-
You have not selected two timetables to compare. Please try again.
18-
</div>
19-
);
20-
}
21-
const timetableId1 = parseInt(queryParams.get("id1") || "0");
22-
const timetableId2 = parseInt(queryParams.get("id2") || "0");
21+
const [timetable1, setTimetable1] = useState<Timetable>();
22+
const [timetable2, setTimetable2] = useState<Timetable>();
23+
const [offeringIds1, setOfferingIds1] = useState<number[]>();
24+
const [offeringIds2, setOfferingIds2] = useState<number[]>();
25+
const [queryParams] = useSearchParams();
2326

24-
const { data: data1 } = useGetTimetableQuery(timetableId1) as {
25-
data: Timetable[];
26-
};
27-
const { data: data2 } = useGetTimetableQuery(timetableId2) as {
28-
data: Timetable[];
27+
const compareForm = useForm<z.infer<typeof CompareFormSchema>>({
28+
resolver: zodResolver(CompareFormSchema),
29+
});
30+
31+
const onSubmit = (values: z.infer<typeof CompareFormSchema>) => {
32+
console.log("Compare Form submitted:", values);
33+
const timetableId1 = compareForm.getValues("timetable1");
34+
const timetableId2 = compareForm.getValues("timetable2");
35+
setTimetable1(timetables.find(t => t.id === timetableId1))
36+
refetchEvents1()
37+
setTimetable2(timetables.find(t => t.id === timetableId2))
38+
refetchEvents2()
2939
};
3040

31-
const { data: timetableEventsData1 } = useGetEventsQuery(timetableId1) as {
41+
const { data: timetables, isLoading, refetch } = useGetTimetablesQuery() as {
42+
data: Timetable[];
43+
isLoading: boolean;
44+
refetch: () => void;
45+
};
46+
47+
const { data: timetableEventsData1, refetch: refetchEvents1 } = useGetEventsQuery(
48+
compareForm.getValues("timetable1") ?? undefined,
49+
{
50+
skip: compareForm.getValues("timetable1") === undefined
51+
}
52+
) as {
3253
data: TimetableEvents;
54+
refetch: () => void;
3355
};
34-
const { data: timetableEventsData2 } = useGetEventsQuery(timetableId2) as {
56+
const { data: timetableEventsData2, refetch: refetchEvents2 } = useGetEventsQuery(
57+
compareForm.getValues("timetable2"),
58+
{
59+
skip: compareForm.getValues("timetable2") === undefined
60+
}
61+
) as {
3562
data: TimetableEvents;
63+
refetch: () => void;
3664
};
3765

38-
const [timetable1, setTimetable1] = useState<Timetable | null>(null);
39-
const [timetable2, setTimetable2] = useState<Timetable | null>(null);
40-
const [offeringIds1, setOfferingIds1] = useState<number[]>([]);
41-
const [offeringIds2, setOfferingIds2] = useState<number[]>([]);
42-
4366
useEffect(() => {
44-
if (data1) {
45-
setTimetable1(data1[0]);
67+
if (queryParams.has("id1") && timetables) {
68+
const id = parseInt(queryParams.get("id1") || "0");
69+
compareForm.setValue("timetable1", id);
70+
setTimetable1(timetables.find(t => t.id === id));
4671
}
47-
}, [data1]);
72+
}, [timetables]);
73+
4874
useEffect(() => {
49-
if (data2) {
50-
setTimetable2(data2[0]);
75+
if (queryParams.has("id2") && timetables) {
76+
const id = parseInt(queryParams.get("id2") || "0");
77+
compareForm.setValue("timetable2", id);
78+
setTimetable2(timetables.find(t => t.id === id));
5179
}
52-
}, [data2]);
80+
}, [timetables]);
5381

5482
// get unique offeringIds for calendar
5583
useEffect(() => {
@@ -77,14 +105,93 @@ export const CompareTimetables = () => {
77105
return (
78106
<>
79107
<div className="w-full">
80-
<div className="mb-4 p-8">
81-
<div className="mb-2 flex flex-row justify-between">
108+
<div className="mb-2 p-8 pt-4">
109+
<div className="mb-2 flex flex-row items-center justify-between">
82110
<div>
83-
<h1 className="text-2xl font-medium tracking-tight mb-4">
111+
<h1 className="text-2xl font-medium tracking-tight">
84112
Comparing Timetables
85113
</h1>
86114
</div>
87-
<div className="flex gap-2 ">
115+
<Form {...compareForm}>
116+
<form
117+
onSubmit={compareForm.handleSubmit(onSubmit)}
118+
className="flex flex-row gap-4 items-center"
119+
>
120+
<FormField
121+
control={compareForm.control}
122+
name="timetable1"
123+
render={({ field }) => (
124+
<FormItem>
125+
<Select
126+
onValueChange={(value) => field.onChange(Number(value))}
127+
>
128+
<FormControl>
129+
<SelectTrigger>
130+
<SelectValue placeholder="Select a timetable" />
131+
</SelectTrigger>
132+
</FormControl>
133+
<SelectContent>
134+
{timetables && timetables.map((timetable) => (
135+
<SelectItem
136+
key={timetable.id}
137+
value={timetable.id.toString()}
138+
>
139+
<div className="flex items-center gap-2">
140+
<SemesterIcon
141+
semester={timetable.semester}
142+
size={18}
143+
/>
144+
<span>{timetable.timetable_title}</span>
145+
</div>
146+
</SelectItem>
147+
))}
148+
</SelectContent>
149+
</Select>
150+
<FormMessage />
151+
</FormItem>
152+
)}
153+
/>
154+
<Button size="sm" className="px-5">
155+
Compare
156+
<GitCompareArrows />
157+
</Button>
158+
<FormField
159+
control={compareForm.control}
160+
name="timetable2"
161+
render={({ field }) => (
162+
<FormItem>
163+
<Select
164+
onValueChange={(value) => field.onChange(Number(value))}
165+
>
166+
<FormControl>
167+
<SelectTrigger>
168+
<SelectValue placeholder="Select a timetable" />
169+
</SelectTrigger>
170+
</FormControl>
171+
<SelectContent>
172+
{timetables && timetables.map((timetable) => (
173+
<SelectItem
174+
key={timetable.id}
175+
value={timetable.id.toString()}
176+
>
177+
<div className="flex items-center gap-2">
178+
<SemesterIcon
179+
semester={timetable.semester}
180+
size={18}
181+
/>
182+
<span>{timetable.timetable_title}</span>
183+
</div>
184+
</SelectItem>
185+
))}
186+
</SelectContent>
187+
</Select>
188+
<FormMessage />
189+
</FormItem>
190+
)}
191+
/>
192+
</form>
193+
</Form>
194+
<div className="flex gap-2 ml-[7rem]">
88195
<Link to="/dashboard/home">
89196
<Button size="sm" variant="outline" onClick={() => {}}>
90197
Back to Home
@@ -95,8 +202,10 @@ export const CompareTimetables = () => {
95202
<hr className="mb-4" />
96203
<div className="flex gap-4">
97204
<div className="w-1/2">
98-
{offeringIds1.length === 0 ? (
99-
<Spinner />
205+
{!offeringIds1 ? (
206+
<>
207+
{queryParams.has("id1") ? <Spinner /> : <div className="w-full text-center py-[8rem] text-sm bg-gray-100/50 rounded">Select a timetable to compare</div>}
208+
</>
100209
) : (
101210
<Calendar
102211
setShowLoadingPage={() => {}}
@@ -110,8 +219,10 @@ export const CompareTimetables = () => {
110219
)}
111220
</div>
112221
<div className="w-1/2">
113-
{offeringIds2.length === 0 ? (
114-
<Spinner />
222+
{!offeringIds2 ? (
223+
<>
224+
{queryParams.has("id2") ? <Spinner /> : <div className="w-full text-center py-[8rem] text-sm bg-gray-100/50 rounded">Select a timetable to compare</div>}
225+
</>
115226
) : (
116227
<Calendar
117228
setShowLoadingPage={() => {}}

course-matrix/frontend/src/pages/Dashboard/Dashboard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ const Dashboard = () => {
6565
) : location.pathname.startsWith(
6666
"/dashboard/compare",
6767
) ? (
68-
<>Compare Timeatables</>
68+
<Link to="/dashboard/compare">Timetable Compare</Link>
6969
) : (
7070
<></>
7171
)}

course-matrix/frontend/src/pages/Home/TimetableCompareButton.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import { useForm } from "react-hook-form";
3535
import { useNavigate } from "react-router-dom";
3636
import { z } from "zod";
3737

38-
const CompareFormSchema = z.object({
38+
export const CompareFormSchema = z.object({
3939
timetable1: z.number().positive(),
4040
timetable2: z.number().positive(),
4141
});

0 commit comments

Comments
 (0)