Skip to content

Commit 4506dc4

Browse files
committed
Finish integration + UIUX improvmenets
1 parent 2d84d9e commit 4506dc4

File tree

14 files changed

+559
-74
lines changed

14 files changed

+559
-74
lines changed

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: 2 additions & 2 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>
10+
// <StrictMode>
1111
<BrowserRouter>
1212
<Provider store={store}>
1313
<App />
1414
</Provider>
1515
</BrowserRouter>
16-
</StrictMode>,
16+
// </StrictMode>,
1717
);

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

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

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ const Home = () => {
7171
{isLoading ? (
7272
<p className="text-sm text-muted-foreground">Loading...</p>
7373
) : (
74-
data.map((timetable) => (
74+
[...data].sort((a: Timetable, b: Timetable) => (b?.updated_at.localeCompare(a?.updated_at))).map((timetable) => (
7575
<TimetableCard
7676
refetch={refetch}
7777
key={timetable.id}

course-matrix/frontend/src/pages/TimetableBuilder/Calendar.tsx

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ import {
5353
Offering,
5454
} from "@/utils/type-utils";
5555
import { TimetableForm } from "@/models/timetable-form";
56-
import { getSemesterStartAndEndDates } from "@/utils/semester-utils";
56+
import { getSemesterStartAndEndDates, getSemesterStartAndEndDatesPlusOneWeek } from "@/utils/semester-utils";
57+
import { courseEventStyles } from "@/constants/calendarConstants";
5758

5859
interface CalendarProps {
5960
setShowLoadingPage: React.Dispatch<React.SetStateAction<boolean>>;
@@ -107,7 +108,7 @@ const Calendar = React.memo<CalendarProps>(
107108
const [deleteRestriction] = useDeleteRestrictionMutation();
108109

109110
const semesterStartDate = getSemesterStartAndEndDates(semester).start;
110-
const semesterEndDate = getSemesterStartAndEndDates(semester).end;
111+
const { start: semesterStartDatePlusOneWeek, end: semesterEndDate } = getSemesterStartAndEndDatesPlusOneWeek(semester);
111112

112113
const { data: offeringsData } = useGetOfferingsQuery({}) as {
113114
data: Offering[];
@@ -141,29 +142,21 @@ const Calendar = React.memo<CalendarProps>(
141142
createViewMonthGrid(),
142143
createViewMonthAgenda(),
143144
],
144-
selectedDate: semesterStartDate,
145+
selectedDate: semesterStartDatePlusOneWeek,
145146
minDate: semesterStartDate,
146147
maxDate: semesterEndDate,
147148
defaultView: viewWeek.name,
148149
events: [...courseEventsParsed, ...userEventsParsed],
149150
calendars: {
150-
courseEvent: {
151-
colorName: "courseEvent",
152-
lightColors: {
153-
main: "#1c7df9",
154-
container: "#d2e7ff",
155-
onContainer: "#002859",
156-
},
157-
darkColors: {
158-
main: "#c0dfff",
159-
onContainer: "#dee6ff",
160-
container: "#426aa2",
161-
},
162-
},
151+
courseEvent: courseEventStyles
163152
},
164153
plugins: [createEventModalPlugin()],
165154
weekOptions: {
166-
gridHeight: 1000,
155+
gridHeight: 600,
156+
},
157+
dayBoundaries: {
158+
start: '06:00',
159+
end: '21:00',
167160
},
168161
isResponsive: false,
169162
});
@@ -337,8 +330,8 @@ const Calendar = React.memo<CalendarProps>(
337330

338331
return (
339332
<div>
340-
<h1 className="text-4xl flex flex-row justify-between font-medium tracking-tight mb-8">
341-
<div>Your Timetable</div>
333+
<h1 className="text-2xl flex flex-row justify-between font-medium tracking-tight mb-8">
334+
<div>Your Timetable </div>
342335
{!isEditingTimetable ? (
343336
<Dialog>
344337
{isChoosingSectionsManually &&
@@ -349,12 +342,11 @@ const Calendar = React.memo<CalendarProps>(
349342
</p>
350343
)}
351344
<DialogTrigger asChild>
352-
<Button
353-
size="sm"
354-
disabled={!allOfferingSectionsHaveBeenSelected}
355-
>
345+
{isChoosingSectionsManually && (
346+
<Button size="sm" disabled={!allOfferingSectionsHaveBeenSelected}>
356347
Create Timetable
357348
</Button>
349+
)}
358350
</DialogTrigger>
359351
<DialogContent className="gap-5">
360352
<DialogHeader>

0 commit comments

Comments
 (0)