11import { DAY_TO_JAPANESE_MAP } from "common/consts" ;
22import type { Course , Day } from "common/types" ;
33import { useEffect , useState } from "react" ;
4+ import { MdClose , MdSearch } from "react-icons/md" ;
45import courseApi from "~/api/course" ;
56import CourseRegisterConfirmDialog from "./CourseRegisterConfirmDialog" ;
7+ import TagFilter from "./TagFilter" ;
68
9+ const faculties = [
10+ "all" ,
11+ "zenki" ,
12+ "law" ,
13+ "medicine" ,
14+ "engineering" ,
15+ "arts" ,
16+ "science" ,
17+ "agriculture" ,
18+ "economics" ,
19+ "liberal-arts" ,
20+ "education" ,
21+ "pharmacy" ,
22+ ] as const ;
23+ export type FacultyKey = ( typeof faculties ) [ number ] ;
24+ const facultyRegExMap = new Map < FacultyKey , RegExp > ( [
25+ [ "all" , / .* / ] ,
26+ [ "zenki" , / ^ [ 3 4 ] .* / ] ,
27+ [ "law" , / ^ 0 1 .* / ] ,
28+ [ "medicine" , / ^ 0 2 .* / ] ,
29+ [ "engineering" , / ^ F E N .* / ] ,
30+ [ "arts" , / ^ 0 4 .* / ] ,
31+ [ "science" , / ^ 0 5 .* / ] ,
32+ [ "agriculture" , / ^ 0 6 .* / ] ,
33+ [ "economics" , / ^ 0 7 .* / ] ,
34+ [ "liberal-arts" , / ^ 0 8 .* / ] ,
35+ [ "education" , / ^ 0 9 .* / ] ,
36+ [ "pharmacy" , / ^ 1 0 .* / ] ,
37+ ] ) ;
38+
39+ const facultyNameMap = new Map < FacultyKey , string > ( [
40+ [ "all" , "全て" ] ,
41+ [ "zenki" , "前期教養" ] ,
42+ [ "law" , "法" ] ,
43+ [ "medicine" , "医" ] ,
44+ [ "engineering" , "工" ] ,
45+ [ "arts" , "文" ] ,
46+ [ "science" , "理" ] ,
47+ [ "agriculture" , "農" ] ,
48+ [ "economics" , "経済" ] ,
49+ [ "liberal-arts" , "後期教養" ] ,
50+ [ "education" , "教育" ] ,
51+ [ "pharmacy" , "薬" ] ,
52+ ] ) ;
53+
54+ // TODO: フィルタのロジックが異様にばらけているのでリファクタしよう・・
755export default function SelectCourseDialog ( {
856 open,
957 onClose,
@@ -21,6 +69,7 @@ export default function SelectCourseDialog({
2169} ) {
2270 const [ availableCourses , setAvailableCourses ] = useState < Course [ ] > ( [ ] ) ;
2371 const [ searchText , setSearchText ] = useState ( "" ) ;
72+ const [ selectedFaculty , setSelectedFaculty ] = useState < FacultyKey > ( "all" ) ;
2473 const [ filteredAvailableCourses , setFilteredAvailableCourses ] = useState <
2574 Course [ ]
2675 > ( [ ] ) ;
@@ -45,7 +94,7 @@ export default function SelectCourseDialog({
4594 return (
4695 // biome-ignore lint/a11y/useKeyWithClickEvents: <explanation>
4796 < div
48- className = { `modal ${ open ? "modal-open" : "" } ` }
97+ className = { `modal text-start ${ open ? "modal-open" : "" } ` }
4998 onClick = { ( e ) => e . stopPropagation ( ) }
5099 >
51100 < form className = "modal-backdrop" >
@@ -62,40 +111,42 @@ export default function SelectCourseDialog({
62111 </ form >
63112
64113 < div className = "modal-box" >
65- < h2 className = "font-bold text-lg" >
66- { currentEdit
67- ? `${ DAY_TO_JAPANESE_MAP . get ( currentEdit . columnName ) } 曜${
68- currentEdit . rowIndex + 1
69- } 限の授業を選択`
70- : "授業を選択" }
71- </ h2 >
72- < button
73- type = "button"
74- className = "btn btn-ghost btn-sm absolute top-3 right-3"
75- onClick = { ( ) => {
76- setSearchText ( "" ) ;
77- setFilteredAvailableCourses ( availableCourses ) ;
78- onClose ( ) ;
79- } }
80- >
81- 閉じる
82- </ button >
114+ < div className = "flex items-center justify-between" >
115+ < h2 className = "text-lg" >
116+ { currentEdit
117+ ? `${ DAY_TO_JAPANESE_MAP . get ( currentEdit . columnName ) } 曜${
118+ currentEdit . rowIndex + 1
119+ } 限の授業を編集中`
120+ : "編集" }
121+ </ h2 >
122+ < button
123+ type = "button"
124+ className = "btn btn-circle btn-sm"
125+ onClick = { ( ) => {
126+ setSearchText ( "" ) ;
127+ setFilteredAvailableCourses ( availableCourses ) ;
128+ onClose ( ) ;
129+ } }
130+ >
131+ < MdClose className = "text-2xl" />
132+ </ button >
133+ </ div >
83134 < div className = "my-4" >
84135 < div >
85- < h3 className = "font-semibold text-sm" > 現在の授業</ h3 >
136+ < h3 className = "text-gray-600 text-sm" > 現在の授業</ h3 >
86137 { currentEdit ?. course ? (
87- < div className = "flex items-center justify-between rounded-lg border p-2 " >
138+ < div className = "my-2 flex items-center justify-between rounded-lg" >
88139 < div >
89140 < p className = "text-base" >
90141 { currentEdit ?. course ?. name ?? "-" }
91142 </ p >
92- < p className = "text-gray-500 text-sm" > { `${
93- currentEdit ?. course ?. teacher ?? "-"
94- } / ${ currentEdit ?. course ?. id ?? "-" } `} </ p >
143+ < p className = "text-gray-500 text-sm" > { `${ currentEdit ?. course ?. teacher ?? "-" } / ${
144+ currentEdit ?. course ?. id ?? "-"
145+ } `} </ p >
95146 </ div >
96147 < button
97148 type = "button"
98- className = "btn btn-sm"
149+ className = "btn btn-sm font-normal "
99150 onClick = { async ( ) => {
100151 if ( ! currentEdit ?. course ?. id ) return ;
101152 setNewCourse ( currentEdit . course ) ;
@@ -109,42 +160,57 @@ export default function SelectCourseDialog({
109160 < p className = "text-gray-500" > 未登録</ p >
110161 ) }
111162 </ div >
112-
113- < input
114- type = "text"
115- placeholder = "授業名で検索"
116- className = "input input-bordered mt-4 w-full"
117- value = { searchText }
118- onChange = { ( e ) => {
119- const text = e . target . value . trim ( ) ;
120- setSearchText ( text ) ;
121- const newFilteredCourses = availableCourses . filter ( ( course ) =>
122- course . name . includes ( text ) ,
123- ) ;
124- setFilteredAvailableCourses ( newFilteredCourses ) ;
125- } }
126- />
163+ < label className = "input input-bordered mt-4 flex w-full items-center gap-2" >
164+ < MdSearch className = "text-gray-500 text-xl" />
165+ < input
166+ type = "text"
167+ className = "grow"
168+ placeholder = "授業名で検索"
169+ value = { searchText }
170+ onChange = { ( e ) => {
171+ const text = e . target . value . trim ( ) ;
172+ setSearchText ( text ) ;
173+ const newFilteredCourses = availableCourses . filter ( ( course ) =>
174+ course . name . includes ( text ) ,
175+ ) ;
176+ setFilteredAvailableCourses ( newFilteredCourses ) ;
177+ } }
178+ />
179+ </ label >
180+ < div className = "my-4 flex flex-row" >
181+ < TagFilter
182+ keyNameMap = { facultyNameMap }
183+ selectedTag = { selectedFaculty ?? "all" }
184+ onTagChange = { ( tag ) => {
185+ setSelectedFaculty ( ( prev ) => ( prev === tag ? "all" : tag ) ) ;
186+ } }
187+ />
188+ </ div >
127189 { filteredAvailableCourses . length === 0 ? (
128190 < p className = "mt-2 text-gray-500" >
129191 条件に当てはまる授業はありません。
130192 </ p >
131193 ) : (
132194 < 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- ) ) }
195+ { filteredAvailableCourses
196+ . filter ( ( course ) =>
197+ facultyRegExMap . get ( selectedFaculty ) ?. test ( course . id ) ,
198+ )
199+ . map ( ( course ) => (
200+ < li key = { course . id } >
201+ < button
202+ type = "button"
203+ className = "w-full cursor-pointer border-b p-2 text-start hover:bg-gray-100"
204+ onClick = { ( ) => {
205+ setNewCourse ( course ) ;
206+ setConfirmDialogStatus ( "add" ) ;
207+ } }
208+ >
209+ < p > { course . name } </ p >
210+ < p className = "text-gray-500 text-sm" > { `${ course . teacher } / ${ course . id } ` } </ p >
211+ </ button >
212+ </ li >
213+ ) ) }
148214 </ ul >
149215 ) }
150216 </ div >
0 commit comments