Skip to content

Commit 4bb785f

Browse files
authored
Kl/scrum 55 timetable generation integration (#85)
Co-authored-by: kevin-lann <[email protected]>
1 parent fbb2a44 commit 4bb785f

File tree

15 files changed

+636
-91
lines changed

15 files changed

+636
-91
lines changed

course-matrix/backend/src/routes/timetableRouter.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import express from "express";
2-
32
import eventController from "../controllers/eventsController";
43
import generatorController from "../controllers/generatorController";
54
import restrictionsController from "../controllers/restrictionsController";
@@ -8,6 +7,7 @@ import timetableController from "../controllers/timetablesController";
87
import { authHandler } from "../middleware/authHandler";
98

109
export const timetableRouter = express.Router();
10+
1111
/**
1212
* Route to create a new timetable
1313
* @route POST /api/timetables
@@ -128,6 +128,7 @@ timetableRouter.post(
128128
authHandler,
129129
generatorController.generateTimetable,
130130
);
131+
131132
/**
132133
* Route to create shared entry
133134
* @route POST /api/timetables/shared

course-matrix/frontend/package-lock.json

Lines changed: 9 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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"@schedule-x/event-modal": "^2.21.1",
3636
"@schedule-x/events-service": "^2.21.0",
3737
"@schedule-x/react": "^2.21.0",
38+
"@schedule-x/scroll-controller": "^2.23.0",
3839
"@schedule-x/theme-default": "^2.21.0",
3940
"ai": "^4.1.45",
4041
"class-variance-authority": "^0.7.1",

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,18 @@ export const timetableApiSlice = apiSlice.injectEndpoints({
6666
}),
6767
invalidatesTags: ["Timetable"],
6868
}),
69+
generateTimetable: builder.mutation({
70+
query: (data) => ({
71+
url: `${TIMETABLES_URL}/generate`,
72+
method: "POST",
73+
headers: {
74+
"Content-Type": "application/json",
75+
Accept: "application/json, text/plain, */*",
76+
},
77+
body: data,
78+
credentials: "include",
79+
}),
80+
}),
6981
}),
7082
});
7183

@@ -75,4 +87,5 @@ export const {
7587
useUpdateTimetableMutation,
7688
useCreateTimetableMutation,
7789
useDeleteTimetableMutation,
90+
useGenerateTimetableMutation,
7891
} = timetableApiSlice;
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import * as React from "react";
2+
import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react";
3+
4+
import { cn } from "@/lib/utils";
5+
import { ButtonProps, buttonVariants } from "@/components/ui/button";
6+
7+
const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => (
8+
<nav
9+
role="navigation"
10+
aria-label="pagination"
11+
className={cn("mx-auto flex w-full justify-center", className)}
12+
{...props}
13+
/>
14+
);
15+
Pagination.displayName = "Pagination";
16+
17+
const PaginationContent = React.forwardRef<
18+
HTMLUListElement,
19+
React.ComponentProps<"ul">
20+
>(({ className, ...props }, ref) => (
21+
<ul
22+
ref={ref}
23+
className={cn("flex flex-row items-center gap-1", className)}
24+
{...props}
25+
/>
26+
));
27+
PaginationContent.displayName = "PaginationContent";
28+
29+
const PaginationItem = React.forwardRef<
30+
HTMLLIElement,
31+
React.ComponentProps<"li">
32+
>(({ className, ...props }, ref) => (
33+
<li ref={ref} className={cn("", className)} {...props} />
34+
));
35+
PaginationItem.displayName = "PaginationItem";
36+
37+
type PaginationLinkProps = {
38+
isActive?: boolean;
39+
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
40+
} & Pick<ButtonProps, "size"> &
41+
Omit<React.ComponentProps<"button">, "onClick">;
42+
43+
const PaginationLink = ({
44+
className,
45+
isActive,
46+
size = "icon",
47+
onClick,
48+
...props
49+
}: PaginationLinkProps) => (
50+
<button
51+
type="button"
52+
aria-current={isActive ? "page" : undefined}
53+
className={cn(
54+
buttonVariants({
55+
variant: isActive ? "secondary" : "ghost",
56+
size,
57+
}),
58+
className,
59+
)}
60+
onClick={onClick}
61+
{...props}
62+
/>
63+
);
64+
PaginationLink.displayName = "PaginationLink";
65+
66+
const PaginationPrevious = ({
67+
className,
68+
onClick,
69+
...props
70+
}: React.ComponentProps<typeof PaginationLink>) => (
71+
<PaginationLink
72+
aria-label="Go to previous page"
73+
size="default"
74+
className={cn("gap-1 pl-2.5", className)}
75+
onClick={onClick}
76+
{...props}
77+
>
78+
<ChevronLeft className="h-4 w-4" />
79+
<span>Previous</span>
80+
</PaginationLink>
81+
);
82+
PaginationPrevious.displayName = "PaginationPrevious";
83+
84+
const PaginationNext = ({
85+
className,
86+
onClick,
87+
...props
88+
}: React.ComponentProps<typeof PaginationLink>) => (
89+
<PaginationLink
90+
aria-label="Go to next page"
91+
size="default"
92+
className={cn("gap-1 pr-2.5", className)}
93+
onClick={onClick}
94+
{...props}
95+
>
96+
<span>Next</span>
97+
<ChevronRight className="h-4 w-4" />
98+
</PaginationLink>
99+
);
100+
PaginationNext.displayName = "PaginationNext";
101+
102+
const PaginationEllipsis = ({
103+
className,
104+
...props
105+
}: React.ComponentProps<"span">) => (
106+
<span
107+
aria-hidden
108+
className={cn("flex h-9 w-9 items-center justify-center", className)}
109+
{...props}
110+
>
111+
<MoreHorizontal className="h-4 w-4" />
112+
<span className="sr-only">More pages</span>
113+
</span>
114+
);
115+
PaginationEllipsis.displayName = "PaginationEllipsis";
116+
117+
export {
118+
Pagination,
119+
PaginationContent,
120+
PaginationEllipsis,
121+
PaginationItem,
122+
PaginationLink,
123+
PaginationNext,
124+
PaginationPrevious,
125+
};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export const courseEventStyles = {
2+
colorName: "courseEvent",
3+
lightColors: {
4+
main: "#1c7df9",
5+
container: "#d2e7ff",
6+
onContainer: "#002859",
7+
},
8+
darkColors: {
9+
main: "#c0dfff",
10+
onContainer: "#dee6ff",
11+
container: "#426aa2",
12+
},
13+
};

course-matrix/frontend/src/index.css

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ html {
5050
--popover-foreground: 240 10% 3.9%;
5151
--primary: 142.1 76.2% 36.3%;
5252
--primary-foreground: 355.7 100% 97.3%;
53-
--secondary: 240 4.8% 95.9%;
53+
--secondary: 240 4.8% 91%;
5454
--secondary-foreground: 240 5.9% 10%;
5555
--muted: 240 4.8% 95.9%;
5656
--muted-foreground: 240 3.8% 46.1%;
@@ -137,3 +137,20 @@ input:-webkit-autofill:focus {
137137
-webkit-box-shadow: 0 0 0px 1000px white inset !important;
138138
caret-color: black !important; /* Ensures the cursor is visible */
139139
}
140+
141+
/* Prevent schedule X animations*/
142+
.sx__event-drag,
143+
.sx__time-grid-event,
144+
.sx__month-grid-event,
145+
.sx__event-calendar-popover,
146+
.sx__calendar-sidebar,
147+
.sx__event-modal {
148+
animation: none !important;
149+
transition: none !important;
150+
}
151+
152+
/* Target any other animated elements */
153+
[class*="sx__"] {
154+
transition-duration: 0s !important;
155+
animation-duration: 0s !important;
156+
}

course-matrix/frontend/src/main.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ import { Provider } from "react-redux";
77
import store from "./stores/store.ts";
88

99
createRoot(document.getElementById("root")!).render(
10-
<StrictMode>
11-
<BrowserRouter>
12-
<Provider store={store}>
13-
<App />
14-
</Provider>
15-
</BrowserRouter>
16-
</StrictMode>,
10+
// <StrictMode>
11+
<BrowserRouter>
12+
<Provider store={store}>
13+
<App />
14+
</Provider>
15+
</BrowserRouter>,
16+
// </StrictMode>,
1717
);

course-matrix/frontend/src/models/models.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,3 +198,16 @@ export interface TimetableModel {
198198
/** Has user enabled email notifications for this timetable */
199199
email_notifications_enabled: boolean;
200200
}
201+
202+
export type GenerateTimetableOffering = Omit<
203+
OfferingModel,
204+
"created_at" | "updated_at"
205+
>;
206+
207+
/**
208+
* Response data of generate timetable call
209+
*/
210+
export interface TimetableGenerateResponseModel {
211+
amount: number;
212+
schedules: GenerateTimetableOffering[][];
213+
}

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

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -71,16 +71,20 @@ const Home = () => {
7171
{isLoading ? (
7272
<p className="text-sm text-muted-foreground">Loading...</p>
7373
) : (
74-
data.map((timetable) => (
75-
<TimetableCard
76-
refetch={refetch}
77-
key={timetable.id}
78-
timetableId={timetable.id}
79-
title={timetable.timetable_title}
80-
lastEditedDate={new Date(timetable.updated_at)}
81-
owner={name}
82-
/>
83-
))
74+
[...data]
75+
.sort((a: Timetable, b: Timetable) =>
76+
b?.updated_at.localeCompare(a?.updated_at),
77+
)
78+
.map((timetable) => (
79+
<TimetableCard
80+
refetch={refetch}
81+
key={timetable.id}
82+
timetableId={timetable.id}
83+
title={timetable.timetable_title}
84+
lastEditedDate={new Date(timetable.updated_at)}
85+
owner={name}
86+
/>
87+
))
8488
)}
8589
</div>
8690
</div>

0 commit comments

Comments
 (0)