Skip to content

Commit 73ab0c2

Browse files
committed
授業を学部でフィルタ
1 parent 2bcb544 commit 73ab0c2

File tree

2 files changed

+113
-19
lines changed

2 files changed

+113
-19
lines changed

web/components/course/components/SelectCourseDialog.tsx

Lines changed: 79 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,54 @@ import type { Course, Day } from "common/types";
33
import { useEffect, useState } from "react";
44
import courseApi from "~/api/course";
55
import CourseRegisterConfirmDialog from "./CourseRegisterConfirmDialog";
6+
import TagFilter from "./TagFilter";
67

8+
const faculties = [
9+
"all",
10+
"zenki",
11+
"law",
12+
"medicine",
13+
"engineering",
14+
"arts",
15+
"science",
16+
"agriculture",
17+
"economics",
18+
"liberal-arts",
19+
"education",
20+
"pharmacy",
21+
] as const;
22+
export type FacultyKey = (typeof faculties)[number];
23+
const facultyRegExMap = new Map<FacultyKey, RegExp>([
24+
["all", /.*/],
25+
["zenki", /^[34].*/],
26+
["law", /^01.*/],
27+
["medicine", /^02.*/],
28+
["engineering", /^FEN.*/],
29+
["arts", /^04.*/],
30+
["science", /^05.*/],
31+
["agriculture", /^06.*/],
32+
["economics", /^07.*/],
33+
["liberal-arts", /^08.*/],
34+
["education", /^09.*/],
35+
["pharmacy", /^10.*/],
36+
]);
37+
38+
const facultyNameMap = new Map<FacultyKey, string>([
39+
["all", "全て"],
40+
["zenki", "前期教養"],
41+
["law", "法"],
42+
["medicine", "医"],
43+
["engineering", "工"],
44+
["arts", "文"],
45+
["science", "理"],
46+
["agriculture", "農"],
47+
["economics", "経済"],
48+
["liberal-arts", "後期教養"],
49+
["education", "教育"],
50+
["pharmacy", "薬"],
51+
]);
52+
53+
// TODO: フィルタのロジックが異様にばらけているのでリファクタしよう・・
754
export default function SelectCourseDialog({
855
open,
956
onClose,
@@ -21,6 +68,7 @@ export default function SelectCourseDialog({
2168
}) {
2269
const [availableCourses, setAvailableCourses] = useState<Course[]>([]);
2370
const [searchText, setSearchText] = useState("");
71+
const [selectedFaculty, setSelectedFaculty] = useState<FacultyKey>("all");
2472
const [filteredAvailableCourses, setFilteredAvailableCourses] = useState<
2573
Course[]
2674
>([]);
@@ -89,9 +137,9 @@ export default function SelectCourseDialog({
89137
<p className="text-base">
90138
{currentEdit?.course?.name ?? "-"}
91139
</p>
92-
<p className="text-gray-500 text-sm">{`${
93-
currentEdit?.course?.teacher ?? "-"
94-
} / ${currentEdit?.course?.id ?? "-"}`}</p>
140+
<p className="text-gray-500 text-sm">{`${currentEdit?.course?.teacher ?? "-"} / ${
141+
currentEdit?.course?.id ?? "-"
142+
}`}</p>
95143
</div>
96144
<button
97145
type="button"
@@ -109,7 +157,6 @@ export default function SelectCourseDialog({
109157
<p className="text-gray-500">未登録</p>
110158
)}
111159
</div>
112-
113160
<input
114161
type="text"
115162
placeholder="授業名で検索"
@@ -124,27 +171,40 @@ export default function SelectCourseDialog({
124171
setFilteredAvailableCourses(newFilteredCourses);
125172
}}
126173
/>
174+
<div className="my-4 flex flex-row">
175+
<TagFilter
176+
keyNameMap={facultyNameMap}
177+
selectedTag={selectedFaculty ?? "all"}
178+
onTagChange={(tag) => {
179+
setSelectedFaculty((prev) => (prev === tag ? "all" : tag));
180+
}}
181+
/>
182+
</div>
127183
{filteredAvailableCourses.length === 0 ? (
128184
<p className="mt-2 text-gray-500">
129185
条件に当てはまる授業はありません。
130186
</p>
131187
) : (
132188
<ul className="mt-4 max-h-[300px] overflow-auto">
133-
{filteredAvailableCourses.map((course) => (
134-
<li key={course.id}>
135-
<button
136-
type="button"
137-
className="w-full cursor-pointer rounded-lg border p-2 hover:bg-gray-100"
138-
onClick={() => {
139-
setNewCourse(course);
140-
setConfirmDialogStatus("add");
141-
}}
142-
>
143-
<p>{course.name}</p>
144-
<p className="text-gray-500 text-sm">{`${course.teacher} / ${course.id}`}</p>
145-
</button>
146-
</li>
147-
))}
189+
{filteredAvailableCourses
190+
.filter((course) =>
191+
facultyRegExMap.get(selectedFaculty)?.test(course.id),
192+
)
193+
.map((course) => (
194+
<li key={course.id}>
195+
<button
196+
type="button"
197+
className="w-full cursor-pointer rounded-lg border p-2 hover:bg-gray-100"
198+
onClick={() => {
199+
setNewCourse(course);
200+
setConfirmDialogStatus("add");
201+
}}
202+
>
203+
<p>{course.name}</p>
204+
<p className="text-gray-500 text-sm">{`${course.teacher} / ${course.id}`}</p>
205+
</button>
206+
</li>
207+
))}
148208
</ul>
149209
)}
150210
</div>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
type Props<T> = {
2+
keyNameMap: Map<T, string>;
3+
selectedTag: T;
4+
onTagChange: (tag: T) => void;
5+
};
6+
7+
export default function TagFilter<T extends string>({
8+
keyNameMap,
9+
selectedTag,
10+
onTagChange,
11+
}: Props<T>) {
12+
const tags = Array.from(keyNameMap.keys());
13+
return (
14+
<div className="flex flex-wrap gap-2">
15+
{tags.map((tag) => (
16+
<div key={tag}>
17+
<input
18+
type="checkbox"
19+
id={tag}
20+
className="peer hidden"
21+
checked={selectedTag === tag}
22+
onChange={() => onTagChange(tag)}
23+
/>
24+
<label
25+
htmlFor={tag}
26+
className="cursor-pointer rounded-full bg-gray-200 px-3 py-1 text-gray-800 transition-colors duration-200 peer-checked:bg-primary peer-checked:text-white"
27+
>
28+
{keyNameMap.get(tag)}
29+
</label>
30+
</div>
31+
))}
32+
</div>
33+
);
34+
}

0 commit comments

Comments
 (0)