diff --git a/src/assets/member/right-chevron.svg b/src/assets/member/right-chevron.svg new file mode 100644 index 00000000..efe54a31 --- /dev/null +++ b/src/assets/member/right-chevron.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/members/MemberList.styled.ts b/src/components/members/MemberList.styled.ts new file mode 100644 index 00000000..f3240c39 --- /dev/null +++ b/src/components/members/MemberList.styled.ts @@ -0,0 +1,85 @@ +import styled from '@emotion/styled'; +import { colors, typography, semanticColors } from '../../styles/global/global'; + +export const Container = styled.div` + display: flex; + flex-direction: column; + width: 100%; +`; + +export const MemberItem = styled.div` + display: flex; + align-items: center; + justify-content: space-between; + padding: 20px; + cursor: pointer; + transition: background-color 0.2s; + position: relative; + + &:hover { + background-color: ${colors.darkgrey['50']}; + } + + &:focus-visible { + outline: 2px solid ${semanticColors.text.point.green}; + outline-offset: -2px; + } + + &::after { + content: ''; + position: absolute; + bottom: 0; + left: 20px; + right: 20px; + height: 1px; + background-color: ${colors.darkgrey.dark}; + } + + &:last-child::after { + display: none; + } +`; + +export const ProfileSection = styled.div` + display: flex; + align-items: center; + gap: 8px; + flex: 1; +`; + +export const ProfileImage = styled.div` + width: 48px; + height: 48px; + border-radius: 50%; + background-color: ${colors.grey['400']}; + flex-shrink: 0; +`; + +export const MemberInfo = styled.div` + display: flex; + flex-direction: column; + gap: 4px; +`; + +export const MemberName = styled.div` + color: ${semanticColors.text.primary}; + font-size: ${typography.fontSize.sm}; + font-weight: ${typography.fontWeight.medium}; +`; + +export const MemberRole = styled.div` + color: ${semanticColors.text.point.green}; + font-size: ${typography.fontSize.xs}; + font-weight: ${typography.fontWeight.regular}; +`; + +export const MemberStatus = styled.div` + color: white; + font-size: 11px; + font-weight: ${typography.fontWeight.regular}; +`; + +export const ChevronIcon = styled.img` + width: 24px; + height: 24px; +`; diff --git a/src/components/members/MemberList.tsx b/src/components/members/MemberList.tsx new file mode 100644 index 00000000..bbbc9733 --- /dev/null +++ b/src/components/members/MemberList.tsx @@ -0,0 +1,66 @@ +import { useNavigate } from 'react-router-dom'; +import type { KeyboardEvent } from 'react'; +import rightChevron from '../../assets/member/right-chevron.svg'; +import type { Member } from '../../mocks/members.mock'; +import { + Container, + MemberItem, + ProfileSection, + ProfileImage, + MemberInfo, + MemberName, + MemberRole, + MemberStatus, + ChevronIcon, +} from './MemberList.styled'; + +interface MemberListProps { + members: Member[]; + onMemberClick?: (memberId: string) => void; +} + +const MemberList = ({ members, onMemberClick }: MemberListProps) => { + const navigate = useNavigate(); + + const handleMemberClick = (memberId: string) => { + if (onMemberClick) { + onMemberClick(memberId); + } else { + // 기본 동작: 개별 유저 페이지로 이동 + navigate(`/otherfeed/${memberId}`); + } + }; + + return ( + + {members.map(member => ( + handleMemberClick(member.id)} + onKeyDown={(e: KeyboardEvent) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + handleMemberClick(member.id); + } + }} + > + + + + {member.nickname} + {member.role} + + + + {`${(member.followersCount ?? 0).toLocaleString()}명이 띱 하는 중`} + + + + ))} + + ); +}; + +export default MemberList; diff --git a/src/mocks/members.mock.ts b/src/mocks/members.mock.ts new file mode 100644 index 00000000..60257963 --- /dev/null +++ b/src/mocks/members.mock.ts @@ -0,0 +1,67 @@ +export interface Member { + id: string; + nickname: string; + role: string; + profileImage: string; + points: number; + followersCount: number; +} + +export const mockMembers: Member[] = [ + { + id: '1', + nickname: 'Thiper', + role: '칭호칭호', + profileImage: '', // 빈 문자열로 기본 이미지 처리 + points: 0, + followersCount: 0, + }, + { + id: '2', + nickname: 'thipthip', + role: '공식 인플루언서', + profileImage: '', + points: 0, + followersCount: 0, + }, + { + id: '3', + nickname: 'Thiper', + role: '청춘청춘', + profileImage: '', + points: 0, + followersCount: 0, + }, + { + id: '4', + nickname: 'thip01', + role: '작가', + profileImage: '', + points: 0, + followersCount: 0, + }, + { + id: '5', + nickname: 'thip01', + role: '작가', + profileImage: '', + points: 0, + followersCount: 0, + }, + { + id: '6', + nickname: 'thip01', + role: '작가', + profileImage: '', + points: 0, + followersCount: 0, + }, + { + id: '7', + nickname: 'thip01', + role: '작가', + profileImage: '', + points: 0, + followersCount: 0, + }, +]; diff --git a/src/pages/groupMembers/GroupMembers.styled.ts b/src/pages/groupMembers/GroupMembers.styled.ts new file mode 100644 index 00000000..91944305 --- /dev/null +++ b/src/pages/groupMembers/GroupMembers.styled.ts @@ -0,0 +1,13 @@ +import styled from '@emotion/styled'; +import { colors } from '../../styles/global/global'; + +export const Wrapper = styled.div` + display: flex; + flex-direction: column; + padding-top: 56px; + min-width: 320px; + max-width: 767px; + min-height: 100vh; + margin: 0 auto; + background-color: ${colors.black.main}; +`; diff --git a/src/pages/groupMembers/GroupMembers.tsx b/src/pages/groupMembers/GroupMembers.tsx new file mode 100644 index 00000000..5f15390e --- /dev/null +++ b/src/pages/groupMembers/GroupMembers.tsx @@ -0,0 +1,34 @@ +import { useNavigate } from 'react-router-dom'; +import TitleHeader from '../../components/common/TitleHeader'; +import MemberList from '../../components/members/MemberList'; +import leftArrow from '../../assets/common/leftArrow.svg'; +import { mockMembers } from '../../mocks/members.mock'; +import { Wrapper } from './GroupMembers.styled'; + +const GroupMembers = () => { + const navigate = useNavigate(); + + const handleBackClick = () => { + navigate(-1); + }; + + const handleMemberClick = (memberId: string) => { + // 특정 사용자 페이지로 이동 + navigate(`/user/${memberId}`); + }; + + return ( + <> + } + title="독서메이트" + onLeftClick={handleBackClick} + /> + + + + + ); +}; + +export default GroupMembers; diff --git a/src/pages/index.tsx b/src/pages/index.tsx index e8ea763d..343e91c2 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -36,6 +36,7 @@ import WithdrawDonePage from './mypage/WithdrawDonePage'; import EditPage from './mypage/EditPage'; import Notice from './notice/Notice'; import ParticipatedGroupDetail from './groupDetail/ParticipatedGroupDetail'; +import GroupMembers from './groupMembers/GroupMembers'; const Router = () => { const router = createBrowserRouter( @@ -53,6 +54,7 @@ const Router = () => { } /> } /> } /> + } /> } /> } /> } />