diff --git a/src/components/UserList.tsx b/src/components/UserList.tsx new file mode 100644 index 0000000..d09d561 --- /dev/null +++ b/src/components/UserList.tsx @@ -0,0 +1,54 @@ +import { ChevronDown } from "lucide-react"; +import UserListCard from "./UserListCard"; +import { useEffect, useState } from "react"; +import { useUserStore } from "../stores/userListStore"; +import { useLocation } from "react-router"; + +export default function UserList() { + const [isOpen, setIsOpen] = useState(false); + const location = useLocation(); + const { userList, fetchUsers } = useUserStore(); + + // 페이지 이동 시 자동으로 닫기 + useEffect(() => { + setIsOpen(false); + }, [location.pathname]); + + useEffect(() => { + fetchUsers(); + }, [fetchUsers]); + + /* 필요한 정보 */ + /* 프로필 이미지, 이름, 소속, 학년(전공 과목), auth_id */ + return ( +
+ {/* 헤더 */} +
setIsOpen(!isOpen)} + > +

온라인 사용자

+ +
+ + {/* 슬라이딩 리스트 */} +
+
+ {userList.map((user) => ( + + ))} +
+
+
+ ); +} diff --git a/src/components/UserListCard.tsx b/src/components/UserListCard.tsx new file mode 100644 index 0000000..d894ff8 --- /dev/null +++ b/src/components/UserListCard.tsx @@ -0,0 +1,53 @@ +import { Link } from "react-router"; +import { getAge } from "../utils/getAge"; +import { getGrade } from "../utils/getGrade"; + +export default function UserListCard({ user }: { user: User }) { + const age = user.birth_date ? getAge(user.birth_date) : 0; + const grade = user.role === "student" && getGrade(age); + + const roleMap: Record = { + student: "학생", + teacher: "선생님", + parent: "학부모", + }; + + return ( + <> + +
+ {/* left */} +
+ {/* 이미지 */} +
+ {/* 온라인 */} +
+ {/* 오프라인 */} + {/*
*/} +
+ {/* 정보 */} +
+ {/* 이름 */} +
{user.nickname}
+ {/* 소속, 학년 (선생님은 전공) */} +
+ {roleMap[user.role] || "알 수 없음"} + {user.role === "student" && `, ${grade}`}{" "} + {user.role === "teacher" && `, ${user.major}`} +
+
+
+ {/* right */} +
+ + +
+
+ + + ); +} diff --git a/src/layouts/MainLayout.tsx b/src/layouts/MainLayout.tsx index b2f57ed..fd89b18 100644 --- a/src/layouts/MainLayout.tsx +++ b/src/layouts/MainLayout.tsx @@ -1,6 +1,7 @@ import { Outlet } from "react-router"; import Header from "../components/layout/Header"; import Footer from "../components/layout/Footer"; +import UserList from "../components/UserList"; export default function MainLayout() { return ( @@ -11,13 +12,15 @@ export default function MainLayout() {
{/* 스크롤 영역 */} -
+
+ {/* 유저 리스트 모달 */} +
diff --git a/src/stores/userListStore.ts b/src/stores/userListStore.ts new file mode 100644 index 0000000..a13d42f --- /dev/null +++ b/src/stores/userListStore.ts @@ -0,0 +1,28 @@ +import { create } from "zustand"; +import { immer } from "zustand/middleware/immer"; +import supabase from "../utils/supabase"; + +type UserListState = { + userList: User[]; + // fetchUsers: 전체 유저 리스트 가져오기 + fetchUsers: () => Promise; +}; + +export const useUserStore = create()( + immer((set) => ({ + userList: [], + // 전체 유저 리스트 가져오기 + fetchUsers: async () => { + try { + const { data, error } = await supabase + .from("users") + .select("*"); + if (error) throw error; + + set({ userList: data || [] }); + } catch (err) { + console.error("유저 목록 로딩 오류:", err); + } + }, + })), +); diff --git a/src/types/user.d.ts b/src/types/user.d.ts new file mode 100644 index 0000000..e12050f --- /dev/null +++ b/src/types/user.d.ts @@ -0,0 +1,8 @@ +type User = { + auth_id: string; + nickname: string; + online?: string; + role: string; + birth_date?: Date; + major?: string; +};