Skip to content

Commit f57f4de

Browse files
Austin-Xkevin-lann
andauthored
Ax/scrum 101 Finish Timetable Home Page (#50)
Co-authored-by: Austin-X <[email protected]> Co-authored-by: Kevin Lan <[email protected]>
1 parent 6f15fb9 commit f57f4de

File tree

17 files changed

+761
-22
lines changed

17 files changed

+761
-22
lines changed

course-matrix/frontend/package-lock.json

Lines changed: 85 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

course-matrix/frontend/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@
3030
"@radix-ui/react-slot": "^1.1.2",
3131
"@radix-ui/react-tooltip": "^1.1.8",
3232
"@reduxjs/toolkit": "^2.5.1",
33+
"@schedule-x/drag-and-drop": "^2.21.1",
34+
"@schedule-x/event-modal": "^2.21.1",
35+
"@schedule-x/events-service": "^2.21.0",
36+
"@schedule-x/react": "^2.21.0",
37+
"@schedule-x/theme-default": "^2.21.0",
3338
"ai": "^4.1.45",
3439
"class-variance-authority": "^0.7.1",
3540
"clsx": "^2.1.1",
5.3 KB
Loading

course-matrix/frontend/src/api/baseApiSlice.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ const baseQuery = fetchBaseQuery({ baseUrl: BASE_URL });
55

66
export const apiSlice = createApi({
77
baseQuery,
8-
tagTypes: ["Auth", "Course", "Department", "Offering", "Timetable"],
8+
tagTypes: ["Auth", "Course", "Department", "Offering", "Timetable", "Event"],
99
endpoints: () => ({}),
1010
});

course-matrix/frontend/src/api/config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ export const AUTH_URL = `${SERVER_URL}/auth`;
55
export const COURSES_URL = `${SERVER_URL}/api/courses`;
66
export const DEPARTMENT_URL = `${SERVER_URL}/api/departments`;
77
export const OFFERINGS_URL = `${SERVER_URL}/api/offerings`;
8+
export const TIMETABLES_URL = `${SERVER_URL}/api/timetables`;
9+
export const EVENTS_URL = `${SERVER_URL}/api/timetables/events`;
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { data } from "react-router-dom";
2+
import { apiSlice } from "./baseApiSlice";
3+
import { EVENTS_URL } from "./config";
4+
5+
// Endpoints for /api/timetables/events
6+
export const eventsApiSlice = apiSlice.injectEndpoints({
7+
endpoints: (builder) => ({
8+
createEvent: builder.mutation({
9+
query: (data) => ({
10+
url: `${EVENTS_URL}`,
11+
method: "POST",
12+
headers: {
13+
"Content-Type": "application/json",
14+
Accept: "application/json, text/plain, */*",
15+
},
16+
providesTags: ["Event"],
17+
body: data,
18+
credentials: "include",
19+
}),
20+
}),
21+
getEvents: builder.query<unknown, number>({
22+
query: (id) => ({
23+
url: `${EVENTS_URL}/${id}`,
24+
method: "GET",
25+
headers: {
26+
"Content-Type": "application/json",
27+
Accept: "application/json, text/plain, */*",
28+
},
29+
providesTags: ["Event"],
30+
credentials: "include",
31+
}),
32+
}),
33+
updateEvent: builder.mutation({
34+
query: (data) => ({
35+
url: `${EVENTS_URL}/${data.id}`,
36+
method: "PUT",
37+
headers: {
38+
"Content-Type": "application/json",
39+
Accept: "application/json, text/plain, */*",
40+
},
41+
providesTags: ["Event"],
42+
body: data,
43+
credentials: "include",
44+
}),
45+
}),
46+
deleteEvent: builder.mutation({
47+
query: (id) => ({
48+
url: `${EVENTS_URL}/${id}`,
49+
method: "DELETE",
50+
headers: {
51+
"Content-Type": "application/json",
52+
Accept: "application/json, text/plain, */*",
53+
},
54+
providesTags: ["Event"],
55+
credentials: "include",
56+
}),
57+
}),
58+
}),
59+
});
60+
61+
export const {
62+
useCreateEventMutation,
63+
useGetEventsQuery,
64+
useUpdateEventMutation,
65+
useDeleteEventMutation,
66+
} = eventsApiSlice;
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { apiSlice } from "./baseApiSlice";
2+
import { TIMETABLES_URL } from "./config";
3+
4+
// Endpoints for /api/timetables
5+
export const timetableApiSlice = apiSlice.injectEndpoints({
6+
endpoints: (builder) => ({
7+
createTimetable: builder.mutation({
8+
query: (data) => ({
9+
url: `${TIMETABLES_URL}`,
10+
method: "POST",
11+
headers: {
12+
"Content-Type": "application/json",
13+
Accept: "application/json, text/plain, */*",
14+
},
15+
providesTags: ["Timetable"],
16+
body: data,
17+
credentials: "include",
18+
}),
19+
}),
20+
getTimetables: builder.query<unknown, void>({
21+
query: () => ({
22+
url: `${TIMETABLES_URL}`,
23+
method: "GET",
24+
headers: {
25+
"Content-Type": "application/json",
26+
Accept: "application/json, text/plain, */*",
27+
},
28+
providesTags: ["Timetable"],
29+
credentials: "include",
30+
}),
31+
}),
32+
updateTimetable: builder.mutation({
33+
query: (data) => ({
34+
url: `${TIMETABLES_URL}/${data.id}`,
35+
method: "PUT",
36+
headers: {
37+
"Content-Type": "application/json",
38+
Accept: "application/json, text/plain, */*",
39+
},
40+
providesTags: ["Timetable"],
41+
body: data,
42+
credentials: "include",
43+
}),
44+
}),
45+
deleteTimetable: builder.mutation({
46+
query: (id) => ({
47+
url: `${TIMETABLES_URL}/${id}`,
48+
method: "DELETE",
49+
headers: {
50+
"Content-Type": "application/json",
51+
Accept: "application/json, text/plain, */*",
52+
},
53+
providesTags: ["Timetable"],
54+
credentials: "include",
55+
}),
56+
}),
57+
}),
58+
});
59+
60+
export const {
61+
useGetTimetablesQuery,
62+
useUpdateTimetableMutation,
63+
useCreateTimetableMutation,
64+
useDeleteTimetableMutation,
65+
} = timetableApiSlice;

course-matrix/frontend/src/components/ui/button.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const buttonVariants = cva(
2121
},
2222
size: {
2323
default: "h-10 px-4 py-2",
24+
xs: "h-6 rounded-md px-2",
2425
sm: "h-9 rounded-md px-3",
2526
lg: "h-11 rounded-md px-8",
2627
icon: "h-10 w-10",

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { Link, Navigate, Route, Routes, useLocation } from "react-router-dom";
1818
import TimetableBuilder from "../TimetableBuilder/TimetableBuilder";
1919
import AssistantPage from "../Assistant/AssistantPage";
2020
import { RuntimeProvider } from "../Assistant/runtime-provider";
21+
import Home from "../Home/Home";
2122

2223
/**
2324
* Dashboard Component
@@ -77,7 +78,7 @@ const Dashboard = () => {
7778
<Routes>
7879
<Route path="*" element={<Navigate to="/not-found" />} />
7980
<Route path="/" element={<Navigate to="home" replace />} />
80-
<Route path="/home" element={<>Home</>} />
81+
<Route path="/home" element={<Home />} />
8182
<Route path="/timetable" element={<TimetableBuilder />} />
8283
<Route path="/assistant" element={<AssistantPage />} />
8384
</Routes>
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { Button } from "@/components/ui/button";
2+
import { Pin } from "lucide-react";
3+
import TimetableCard from "./TimetableCard";
4+
import TimetableCompareButton from "./TimetableCompareButton";
5+
import TimetableCreateNewButton from "./TimetableCreateNewButton";
6+
import { useGetTimetablesQuery } from "../../api/timetableApiSlice";
7+
8+
/**
9+
* Home component that displays the user's timetables and provides options to create or compare timetables.
10+
* @returns {JSX.Element} The rendered component.
11+
*/
12+
const Home = () => {
13+
const user_metadata = JSON.parse(localStorage.getItem("userInfo") ?? "{}");
14+
const name =
15+
(user_metadata?.user?.user_metadata?.username as string) ??
16+
(user_metadata?.user?.email as string);
17+
18+
const { data, isLoading, refetch } = useGetTimetablesQuery();
19+
20+
return (
21+
<div className="w-full">
22+
<div className="m-8">
23+
<div className="mb-4 flex items-center gap-2 relative group">
24+
<h1 className="text-2xl font-medium tracking-tight">My Timetables</h1>
25+
<Pin size={24} className="text-blue-500" />
26+
</div>
27+
<div className="mb-4 flex flex-row justify-between items-center">
28+
<div className="flex gap-4">
29+
<Button
30+
size="xs"
31+
className="py-3 px-5 bg-blue-100 hover:bg-blue-300 text-black"
32+
disabled
33+
>
34+
All
35+
</Button>
36+
<Button
37+
size="xs"
38+
className="py-3 px-5 bg-blue-100 hover:bg-blue-300 text-black"
39+
>
40+
Mine
41+
</Button>
42+
<Button
43+
size="xs"
44+
className="py-3 px-5 bg-blue-100 hover:bg-blue-300 text-black"
45+
disabled
46+
>
47+
Shared
48+
</Button>
49+
</div>
50+
<div className="flex gap-8">
51+
<TimetableCompareButton />
52+
<TimetableCreateNewButton />
53+
</div>
54+
</div>
55+
<hr />
56+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-4 justify-between mt-4">
57+
{isLoading ? (
58+
<p className="text-sm text-muted-foreground">Loading...</p>
59+
) : (
60+
data?.map((timetable, index) => (
61+
<TimetableCard
62+
refetch={refetch}
63+
key={index}
64+
timetableId={timetable.id}
65+
title={timetable.timetable_title}
66+
lastEditedDate={new Date(timetable.updated_at)}
67+
owner={name}
68+
/>
69+
))
70+
)}
71+
</div>
72+
</div>
73+
</div>
74+
);
75+
};
76+
77+
export default Home;

0 commit comments

Comments
 (0)