Skip to content

Commit 8a354cc

Browse files
Merge pull request #32 from ItsukiKigoshi/31-release-000
31 release 000
2 parents 80db7f0 + b08b4df commit 8a354cc

File tree

18 files changed

+624
-297
lines changed

18 files changed

+624
-297
lines changed

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,4 @@ next-env.d.ts
4141
**/public/workbox-*.js
4242
**/public/workbox-*.js.map
4343

44-
src/app/fetch/
44+
.env

next.config.js

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const withPWA = require("next-pwa")({
22
dest: "public",
33
register: true,
44
skipWaiting: true,
5+
disable: process.env.NODE_ENV === "development",
56
});
67

78
const nextConfig = withPWA({

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@
1717
"@mantine/dates": "^7.2.2",
1818
"@mantine/dropzone": "^7.2.2",
1919
"@mantine/ds": "^7.2.2",
20-
"@mantine/form": "^7.2.2",
20+
"@mantine/form": "^7.5.0",
2121
"@mantine/hooks": "^7.2.2",
2222
"@mantine/modals": "^7.2.2",
2323
"@mantine/notifications": "^7.2.2",
2424
"@mantine/nprogress": "^7.2.2",
25-
"@mantine/spotlight": "^7.2.2",
25+
"@mantine/spotlight": "^7.5.0",
2626
"@mantine/tiptap": "^7.2.2",
2727
"@tabler/icons-react": "^2.41.0",
2828
"@tiptap/extension-link": "^2.1.12",

src/app/page.tsx

+151-46
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
"use client";
2-
import { AppShell, Flex, Text } from "@mantine/core";
3-
import { useDisclosure } from "@mantine/hooks";
2+
import { AppShell, Button, Flex, em } from "@mantine/core";
3+
import { useDisclosure, useMediaQuery, useToggle } from "@mantine/hooks";
4+
import { IconCalendar, IconList } from "@tabler/icons-react";
5+
import { useEffect, useState } from "react";
46
import { Header } from "../components/Header";
57
import { Navbar } from "../components/Navbar";
68
import { Timetable } from "../components/Timetable";
@@ -9,26 +11,98 @@ import { Course } from "../type/Types";
911

1012
export default function Page() {
1113
const [opened, { toggle }] = useDisclosure(false);
14+
const isMobile = useMediaQuery(`(max-width: ${em(750)})`);
15+
16+
const [weekdays, toggleSaturday] = useToggle([
17+
["M", "TU", "W", "TH", "F"],
18+
["M", "TU", "W", "TH", "F", "SA"],
19+
]);
20+
21+
const terms = [
22+
{ label: "2024S", ay: "2024", season: "Spring", value: "2024S" },
23+
{ label: "2024A", ay: "2024", season: "Autumn", value: "2024A" },
24+
{ label: "2024W", ay: "2024", season: "Winter", value: "2024W" },
25+
];
26+
const [selectedTermValue, setselectedTermValue] = useState(terms[0].value);
27+
28+
const selectedTerm = terms.find((term) => term.value === selectedTermValue);
29+
30+
const [displayMode, toggleDisplayMode] = useToggle(["list", "timetable"]);
31+
useEffect(() => {
32+
if (!isMobile) {
33+
toggleDisplayMode("timetable");
34+
}
35+
}, [isMobile]);
1236

1337
// Get the list of courses from the local storage
1438
const [courses, setCourses] = useLocalStorage<Course[]>("courses", [
1539
{
16-
regno: 99999,
40+
regno: 99997,
1741
season: "Spring",
18-
ay: 2022,
42+
ay: 2024,
1943
no: "CS101",
2044
lang: "E",
21-
e: "Example Course",
45+
e: "Example Spring Course",
2246
j: "科目例",
2347
schedule: ["3/M", "3/W", "3/F"],
2448
instructor: "John Doe",
2549
modified: new Date(2022, 5 - 1, 5, 6, 35, 20, 333),
2650
unit: 3,
2751
isEnrolled: true,
28-
color: "#ff0000",
52+
color: "orange 2",
53+
},
54+
{
55+
regno: 99998,
56+
season: "Autumn",
57+
ay: 2024,
58+
no: "CS101",
59+
lang: "E",
60+
e: "Example Autumn Course",
61+
j: "科目例",
62+
schedule: ["3/M", "3/W", "3/F"],
63+
instructor: "John Doe",
64+
modified: new Date(2022, 5 - 1, 5, 6, 35, 20, 333),
65+
unit: 3,
66+
isEnrolled: true,
67+
color: "pink 2",
68+
},
69+
{
70+
regno: 99999,
71+
season: "Winter",
72+
ay: 2024,
73+
no: "CS101",
74+
lang: "E",
75+
e: "Example Winter Course",
76+
j: "科目例",
77+
schedule: ["3/M", "3/W", "3/F"],
78+
instructor: "John Doe",
79+
modified: new Date(2022, 5 - 1, 5, 6, 35, 20, 333),
80+
unit: 3,
81+
isEnrolled: true,
82+
color: "green 2",
2983
},
3084
]);
3185

86+
const timetable: { [key: string]: Course[] } = {};
87+
const coursesInSelectedTerm = courses.filter(
88+
(course) =>
89+
course.season === selectedTerm?.season &&
90+
course.ay.toString() === selectedTerm?.ay
91+
);
92+
93+
const enrolledCourses = coursesInSelectedTerm.filter(
94+
(course) => course.isEnrolled
95+
);
96+
coursesInSelectedTerm.forEach((course) => {
97+
course.schedule?.forEach((entry) => {
98+
const [time, day] = entry.split("/");
99+
if (!timetable[`${time}/${day}`]) {
100+
timetable[`${time}/${day}`] = [];
101+
}
102+
timetable[`${time}/${day}`].push(course);
103+
});
104+
});
105+
32106
// Toggle the isEnrolled property of a certain course
33107
// Usage: toggleIsEnrolled(regno)
34108
const toggleIsEnrolled = (regno: number) => {
@@ -56,51 +130,82 @@ export default function Page() {
56130
};
57131

58132
return (
59-
<>
60-
<AppShell
61-
header={{ height: 60 }}
62-
navbar={{
63-
width: "400px",
64-
breakpoint: "sm",
65-
collapsed: { mobile: !opened },
66-
}}
67-
padding="md"
68-
h="100vh"
69-
>
70-
<AppShell.Header>
71-
<Header opened={opened} toggle={toggle} />
72-
</AppShell.Header>
73-
<AppShell.Navbar>
133+
<AppShell
134+
header={{ height: 60 }}
135+
navbar={{
136+
width: "400px",
137+
breakpoint: "sm",
138+
collapsed: { mobile: !opened },
139+
}}
140+
padding="0"
141+
h="100vh"
142+
w="100vw"
143+
>
144+
<AppShell.Header>
145+
<Header
146+
weekdays={weekdays}
147+
toggleSaturday={() => {
148+
toggleSaturday();
149+
}}
150+
terms={terms}
151+
selectedTermValue={selectedTermValue}
152+
setselectedTermValue={setselectedTermValue}
153+
/>
154+
</AppShell.Header>
155+
156+
<AppShell.Navbar>
157+
<Navbar
158+
courses={coursesInSelectedTerm}
159+
toggleIsEnrolled={toggleIsEnrolled}
160+
addCourse={addCourse}
161+
deleteCourse={deleteCourse}
162+
/>
163+
</AppShell.Navbar>
164+
<AppShell.Main h="100vh">
165+
{displayMode === "timetable" ? (
166+
<Timetable
167+
timetable={timetable}
168+
enrolledCourses={enrolledCourses}
169+
toggleIsEnrolled={toggleIsEnrolled}
170+
weekdays={weekdays}
171+
/>
172+
) : (
74173
<Navbar
75-
courses={courses}
174+
courses={coursesInSelectedTerm}
76175
toggleIsEnrolled={toggleIsEnrolled}
77176
addCourse={addCourse}
78177
deleteCourse={deleteCourse}
79178
/>
80-
</AppShell.Navbar>
81-
<AppShell.Main>
82-
{/* <Grid justify="flex-start" gutter="sm" align="stretch"> */}
83-
{/* <Grid.Col span={{ base: "auto" }}> */}
84-
<Timetable courses={courses} />
85-
{/* </Grid.Col> */}
86-
{/* <Grid.Col mt={{ base: 5, md: 0 }} span={{ base: 12, md: "content" }}>
87-
<RequirementTable />
88-
</Grid.Col> */}
89-
{/* </Grid> */}
90-
<Flex
91-
gap="md"
92-
justify="center"
93-
align="center"
94-
direction="row"
95-
wrap="wrap"
179+
)}
180+
</AppShell.Main>
181+
<AppShell.Footer
182+
withBorder={false}
183+
hiddenFrom="sm"
184+
style={{ background: "rgba(0,0,0,0)" }}
185+
>
186+
<Flex gap="md" mih={50} justify="center" align="center" direction="row">
187+
{/* <Button
188+
variant="filled"
189+
size="lg"
190+
leftSection={<IconSearch />}
191+
onClick={spotlight.open}
192+
>
193+
Search
194+
</Button> */}
195+
<Button
196+
hiddenFrom="sm"
197+
size="lg"
198+
color="gray"
199+
mr={3}
200+
onClick={() => {
201+
toggleDisplayMode();
202+
}}
96203
>
97-
<Text fw="bold">
98-
🚧 This App is still under development. Do not store any important
99-
data here!
100-
</Text>
101-
</Flex>
102-
</AppShell.Main>
103-
</AppShell>
104-
</>
204+
{displayMode === "list" ? <IconList /> : <IconCalendar />}
205+
</Button>
206+
</Flex>
207+
</AppShell.Footer>
208+
{/* <SpotlightSearch /> */}
209+
</AppShell>
105210
);
106211
}

src/components/CourseCard/CourseCard.module.css

-19
This file was deleted.

src/components/CourseCard/index.tsx

+38-35
Original file line numberDiff line numberDiff line change
@@ -3,69 +3,72 @@ import { Course } from "@/src/type/Types";
33
import {
44
ActionIcon,
55
Card,
6-
Checkbox,
7-
Flex,
6+
Divider,
87
Grid,
8+
Stack,
99
Text,
1010
UnstyledButton,
1111
} from "@mantine/core";
1212
import { useDisclosure } from "@mantine/hooks";
13-
import { IconTrash } from "@tabler/icons-react";
14-
import classes from "./CourseCard.module.css";
13+
import { IconEye, IconEyeOff, IconTrash } from "@tabler/icons-react";
1514

1615
export default function CourseCard(props: {
1716
course: Course;
17+
open: () => void;
1818
toggleIsEnrolled: (regno: number) => void;
1919
deleteCourse: (regno: number) => void;
2020
}) {
21-
const [modalOpened, { open, close }] = useDisclosure(false);
21+
const [modalConfirmOpened, { open, close }] = useDisclosure(false);
2222

2323
return (
24-
<Card key={props.course.regno} mb={8} className={classes.button}>
25-
<Grid align="center">
24+
<Card p={0} m="sm" withBorder>
25+
<Grid>
2626
<Grid.Col span="auto">
2727
<UnstyledButton
2828
onClick={() => {
29-
props.toggleIsEnrolled(props.course.regno);
29+
props.open();
3030
}}
31+
key={props.course.regno}
32+
w="100%"
33+
p="md"
3134
>
32-
<Flex align="center">
33-
<Checkbox
34-
checked={props.course.isEnrolled}
35-
onChange={() => {
36-
props.toggleIsEnrolled(props.course.regno);
37-
}}
38-
variant="default"
39-
mr="xl"
40-
/>
41-
<div>
42-
<Text size="xs" c="dimmed">
43-
{props.course.no}{props.course.unit}
44-
</Text>
45-
<Text size="sm" lh={1} py={4}>
46-
{props.course.e} ({props.course.lang})
47-
</Text>
48-
<Text size="xs" c="dimmed">
49-
{props.course.schedule?.map((s, i) =>
50-
i === props.course.schedule!.length - 1 ? s : s + ", "
51-
)}
52-
</Text>
53-
</div>
54-
</Flex>
35+
<Stack h="100%" gap="sm">
36+
<Text size="xs" c="dimmed">
37+
{props.course.no}{props.course.unit}
38+
</Text>
39+
<Text size="sm" lh={1}>
40+
{props.course.e} ({props.course.lang})
41+
</Text>
42+
<Text size="xs" c="dimmed">
43+
{props.course.schedule?.map((s, i) =>
44+
i === props.course.schedule!.length - 1 ? s : s + ", "
45+
)}
46+
</Text>
47+
</Stack>
5548
</UnstyledButton>
5649
</Grid.Col>
50+
<Divider orientation="vertical" my="md" mx={0} />
5751
<Grid.Col span="content">
58-
<Flex align="center">
59-
<ActionIcon size="sm" onClick={open} color="red">
52+
<Stack align="center" h="100%" p="md">
53+
<ActionIcon
54+
onClick={() => {
55+
props.toggleIsEnrolled(props.course.regno);
56+
}}
57+
color="gray"
58+
>
59+
{props.course.isEnrolled ? <IconEye /> : <IconEyeOff />}
60+
</ActionIcon>
61+
62+
<ActionIcon onClick={open} color="red">
6063
<IconTrash />
6164
</ActionIcon>
62-
</Flex>
65+
</Stack>
6366
</Grid.Col>
6467
</Grid>
6568
<ModalConfirm
6669
course={props.course}
6770
deleteCourse={props.deleteCourse}
68-
modalOpened={modalOpened}
71+
modalConfirmOpened={modalConfirmOpened}
6972
close={close}
7073
/>
7174
</Card>

0 commit comments

Comments
 (0)