1- import React , { useState , useRef , useEffect } from "react" ;
21import ThreeSixtyIcon from "@mui/icons-material/ThreeSixty" ;
3- import type { User , UserID } from "common/types" ;
4- import { Chip } from "@mui/material" ;
5- import type { UserID , UserWithCoursesAndSubjects } from "common/types" ;
6- import { useState } from "react" ;
2+ import type { UserWithCoursesAndSubjects } from "common/types" ;
3+ import React , { useState , useRef , useEffect , useCallback } from "react" ;
74import NonEditableCoursesTable from "./course/NonEditableCoursesTable" ;
85import UserAvatar from "./human/avatar" ;
9- import NonEditableCoursesTable from "./course/NonEditableCoursesTable" ;
106
117interface CardProps {
128 displayedUser : UserWithCoursesAndSubjects ;
13- comparisonUserId ?: UserID ;
9+ currentUser : UserWithCoursesAndSubjects ;
1410 onFlip ?: ( isBack : boolean ) => void ;
1511}
1612
17- const interests = [
18- "記号論理学" ,
19- "量子力学" ,
20- "離散数学" ,
21- "プログラミング" ,
22- "量子情報理論" ,
23- "オペレーションズリサーチ" ,
24- ] ;
25-
26- const CardFront = ( { displayedUser } : { displayedUser : User } ) => {
13+ const CardFront = ( { displayedUser, currentUser } : CardProps ) => {
2714 const containerRef = useRef < HTMLDivElement > ( null ) ;
15+ const interestsContainerRef = useRef < HTMLDivElement > ( null ) ;
16+ const coursesContainerRef = useRef < HTMLDivElement > ( null ) ;
2817 const [ isHiddenInterestExist , setHiddenInterestExist ] = useState ( false ) ;
18+ const [ isHiddenCourseExist , setHiddenCourseExist ] = useState ( false ) ;
2919
3020 useEffect ( ( ) => {
3121 const container = containerRef . current ;
3222 if ( ! container ) return ;
3323
3424 const resizeObserver = new ResizeObserver ( ( ) => {
3525 calculateVisibleInterests ( ) ;
26+ calculateVisibleCourses ( ) ;
3627 } ) ;
3728
3829 resizeObserver . observe ( container ) ;
3930
4031 calculateVisibleInterests ( ) ; // 初期計算
32+ calculateVisibleCourses ( ) ; // 初期計算
4133
4234 return ( ) => resizeObserver . disconnect ( ) ;
4335 } , [ ] ) ;
4436
45- const calculateVisibleInterests = ( ) => {
46- const container = containerRef . current ;
37+ const calculateVisibleCourses = useCallback ( ( ) => {
38+ const courses = displayedUser . courses ;
39+ const container = coursesContainerRef . current ;
40+ if ( ! container ) return ;
41+
42+ const containerHeight = container . offsetHeight ; // コンテナの高さを取得
43+
44+ // 一旦コンテナを初期化
45+ container . innerHTML = "" ;
46+ setHiddenCourseExist ( false ) ;
47+
48+ // courses を一致・非一致で分類
49+ const matchingCourses = courses . filter ( ( course ) =>
50+ currentUser . courses . some ( ( c ) => c . id === course . id ) ,
51+ ) ;
52+ const nonMatchingCourses = courses . filter (
53+ ( course ) => ! currentUser . courses . some ( ( c ) => c . id === course . id ) ,
54+ ) ;
55+
56+ // courses を表示する flex コンテナ
57+ const coursesContainer = document . createElement ( "div" ) ;
58+ coursesContainer . classList . add ( "flex" , "flex-wrap" , "gap-2" ) ;
59+ container . appendChild ( coursesContainer ) ;
60+
61+ // 一致しているコースを先に表示
62+ for ( const course of [ ...matchingCourses , ...nonMatchingCourses ] ) {
63+ const isMatching = currentUser . courses . some ( ( c ) => c . id === course . id ) ;
64+
65+ // 新しい div 要素を作成
66+ const element = document . createElement ( "div" ) ;
67+ element . textContent = course . name ;
68+
69+ // スタイル適用(赤 or 灰色)
70+ element . classList . add ( "badge" , "badge-outline" ) ;
71+ element . style . backgroundColor = isMatching ? "red" : "gray" ;
72+ element . style . color = "white" ;
73+
74+ // 表示判定
75+ if ( coursesContainer . offsetHeight + 30 <= containerHeight ) {
76+ coursesContainer . appendChild ( element ) ;
77+ } else {
78+ setHiddenCourseExist ;
79+ }
80+ }
81+ } , [ displayedUser , currentUser ] ) ;
82+
83+ const calculateVisibleInterests = useCallback ( ( ) => {
84+ const interests = displayedUser . interestSubjects ;
85+ const container = interestsContainerRef . current ;
4786 if ( ! container ) return ;
4887
49- const containerHeight = container . offsetHeight ; // ここで高さを取得
88+ const containerHeight = container . offsetHeight ; // コンテナの高さを取得
5089
51- // 一旦全てのバッジを非表示にする
90+ // 一旦コンテナを初期化
5291 container . innerHTML = "" ;
5392 setHiddenInterestExist ( false ) ;
5493
55- // interestsを入れるflexコンテナ
94+ // interests を一致・非一致で分類
95+ const matchingInterests = interests . filter ( ( interest ) =>
96+ currentUser . interestSubjects . some ( ( i ) => i . name === interest . name ) ,
97+ ) ;
98+ const nonMatchingInterests = interests . filter (
99+ ( interest ) =>
100+ ! currentUser . interestSubjects . some ( ( i ) => i . name === interest . name ) ,
101+ ) ;
102+
103+ // interests を表示する flex コンテナ
56104 const flexContainer = document . createElement ( "div" ) ;
57105 flexContainer . classList . add ( "flex" , "flex-wrap" , "gap-2" ) ;
58106 container . appendChild ( flexContainer ) ;
59107
60- // interests配列をループしてバッジを作成
61- interests . forEach ( ( interest ) => {
108+ // 一致している興味分野を先に表示
109+ for ( const interest of [ ...matchingInterests , ...nonMatchingInterests ] ) {
110+ const isMatching = currentUser . interestSubjects . some (
111+ ( i ) => i . name === interest . name ,
112+ ) ;
113+
62114 // 新しい div 要素を作成
63115 const element = document . createElement ( "div" ) ;
64- element . textContent = interest ;
116+ element . textContent = interest . name ;
65117
66- // スタイルを適用
118+ // スタイル適用(赤 or 灰色)
67119 element . classList . add ( "badge" , "badge-outline" ) ;
120+ element . style . backgroundColor = isMatching ? "red" : "gray" ;
121+ element . style . color = "white" ;
68122 element . style . overflow = "hidden" ;
69123 element . style . whiteSpace = "nowrap" ;
70124 element . style . textOverflow = "ellipsis" ;
71- element . style . display = "inline-block" ;
72125
73- // 要素がコンテナの高さを超えていない場合、表示
74- if ( flexContainer . offsetHeight + 20 <= containerHeight ) {
126+ // 表示判定
127+ if ( flexContainer . offsetHeight + 30 <= containerHeight ) {
75128 flexContainer . appendChild ( element ) ;
76129 } else {
77130 setHiddenInterestExist ( true ) ;
78131 }
79- } ) ;
80-
81- } ;
132+ }
133+ } , [ displayedUser , currentUser ] ) ;
82134
83135 return (
84- < div className = "flex h-full flex-col justify-between gap-5 overflow-clip border-2 border-primary bg-secondary p-5" >
136+ < div className = "flex h-full flex-col gap-5 overflow-clip border-2 border-primary bg-secondary p-5" >
85137 < div className = "grid h-[20%] grid-cols-3 items-center" >
86- < UserAvatar pictureUrl = { displayedUser . pictureUrl } width = "9dvh" height = "9dvh" />
87- < div className = "grid grid-rows-3 items-center col-span-2" >
138+ < UserAvatar
139+ pictureUrl = { displayedUser . pictureUrl }
140+ width = "9dvh"
141+ height = "9dvh"
142+ />
143+ < div className = "col-span-2 grid grid-rows-3 items-center" >
88144 < p className = "col-span-3 font-bold text-1xl" > { displayedUser . name } </ p >
89- < p className = "col-span-3 text-1xl" > { displayedUser . grade } </ p >
90- < p className = "col-span-1 text-1xl" > { displayedUser . faculty } </ p >
145+ < p className = "col-span-1 text-1xl" > { displayedUser . grade } </ p >
146+ < p className = "col-span-2 text-1xl" > { displayedUser . faculty } </ p >
91147 < p className = "col-span-2 text-1xl" > { displayedUser . department } </ p >
92148 </ div >
93149 </ div >
94150
95- < div ref = { containerRef } className = "h-[50%] overflow-hidden width-full" >
96- < div >
151+ < div className = "flex h-[70%] w-full flex-col gap-2" ref = { containerRef } >
152+ < div
153+ ref = { interestsContainerRef }
154+ className = "width-full h-[50%] overflow-hidden"
155+ >
156+ < div />
157+ { isHiddenInterestExist && (
158+ < div className = "badge badge-outline bg-gray-200 text-gray-700" >
159+ And More
160+ </ div >
161+ ) }
97162 </ div >
98- </ div >
99163
100- { isHiddenInterestExist && (
101- < div className = "badge badge-outline bg-gray-200 text-gray-700" >
102- And More
164+ < div
165+ ref = { coursesContainerRef }
166+ className = "width-full h-[50%] overflow-hidden"
167+ >
168+ < div />
169+ { isHiddenCourseExist && (
170+ < div className = "badge badge-outline bg-gray-200 text-gray-700" >
171+ And More
172+ </ div >
173+ ) }
103174 </ div >
104- ) }
105-
106- < div className = "flex justify-center" >
107- < ThreeSixtyIcon className = "text-3xl" />
108175 </ div >
109176 </ div >
110177 ) ;
111178} ;
112179
113- const CardBack = ( { displayedUser, comparisonUserId } : CardProps ) => {
180+ const CardBack = ( { displayedUser, currentUser } : CardProps ) => {
114181 return (
115182 < div className = "flex h-full flex-col overflow-hidden border-2 border-primary bg-secondary p-4" >
116183 < div className = "flex justify-center" >
117184 < p className = "font-bold text-lg" > { displayedUser ?. name } </ p >
118185 </ div >
119186 < NonEditableCoursesTable
120187 userId = { displayedUser . id }
121- comparisonUserId = { comparisonUserId }
188+ comparisonUserId = { currentUser . id }
122189 />
123190 < div className = "mt-4 flex justify-center" >
124191 < ThreeSixtyIcon className = "text-3xl" />
@@ -127,7 +194,7 @@ const CardBack = ({ displayedUser, comparisonUserId }: CardProps) => {
127194 ) ;
128195} ;
129196
130- export function Card ( { displayedUser, comparisonUserId , onFlip } : CardProps ) {
197+ export function Card ( { displayedUser, currentUser , onFlip } : CardProps ) {
131198 const [ isDisplayingBack , setIsDisplayingBack ] = useState ( false ) ;
132199
133200 const handleRotate = ( ) => {
@@ -155,7 +222,7 @@ export function Card({ displayedUser, comparisonUserId, onFlip }: CardProps) {
155222 transform : isDisplayingBack ? "rotateY(180deg)" : "rotateY(0deg)" ,
156223 } }
157224 >
158- < CardFront displayedUser = { displayedUser } />
225+ < CardFront displayedUser = { displayedUser } currentUser = { currentUser } />
159226 </ div >
160227 < div
161228 className = "absolute h-full w-full"
@@ -164,10 +231,7 @@ export function Card({ displayedUser, comparisonUserId, onFlip }: CardProps) {
164231 transform : isDisplayingBack ? "rotateY(0deg)" : "rotateY(-180deg)" ,
165232 } }
166233 >
167- < CardBack
168- displayedUser = { displayedUser }
169- comparisonUserId = { comparisonUserId }
170- />
234+ < CardBack displayedUser = { displayedUser } currentUser = { currentUser } />
171235 </ div >
172236 </ div >
173237 </ div >
0 commit comments