Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
408b7fc
feat: 라우트 추가 및 참여 후 모임방 상세 화면 구현
ljh130334 Aug 5, 2025
23ff99b
refactor: 참여 후 모임방 화면을 재사용 가능한 컴포넌트로 분리 및 일부 스타일링 수정
ljh130334 Aug 6, 2025
1a33c83
feat: 참여 후 모임방에 책 정보 섹션 추가
ljh130334 Aug 6, 2025
fdd6eb6
feat: 책 컴포넌트 구현
ljh130334 Aug 6, 2025
4018a7f
feat: 기록장 컴포넌트 구현
ljh130334 Aug 6, 2025
159ac49
feat: 오늘의 한마디 구현 및 라우트 연결
ljh130334 Aug 6, 2025
7e34059
feat: 뜨거운 감자 구현
ljh130334 Aug 6, 2025
7ec7222
refactor: 투표를 읽기전용으로 변경하고 슬라이드 및 페이지 이동 기능 구현
ljh130334 Aug 6, 2025
b4efd6a
feat: 자연스러운 드래그 스크롤 및 스냅 기능 구현
ljh130334 Aug 6, 2025
cf512d4
fix: VoteOption 타입 충돌 해결 - styled component alias 적용
ljh130334 Aug 7, 2025
990bcdb
refactor: RecordSection에서 사용하지 않는 bookAuthor prop 제거
ljh130334 Aug 7, 2025
3903a79
feat: 모임방 더보기 액션 바텀시트 구현 - 권한별 액션 분리
ljh130334 Aug 7, 2025
9efe7e8
feat: 모임방 삭제/나가기 확인 모달 연동
ljh130334 Aug 7, 2025
86a8664
feat: 독서메이트 섹션에 클릭 가능한 화살표 UI 추가
ljh130334 Aug 7, 2025
afe25b0
style: 참여 모임 상세 페이지의 메타 정보 레이아웃을 1:1 비율로 수정
ljh130334 Aug 7, 2025
a87e2f6
refactor: HotTopicSection 드래그 상태를 useRef로 최적화하여 불필요한 함수 재생성 방지
ljh130334 Aug 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/assets/group/right-chevron.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
45 changes: 45 additions & 0 deletions src/components/group/CommentSection.styled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import styled from '@emotion/styled';
import { colors, typography } from '@/styles/global/global';

export const CommentSection = styled.section`
display: flex;
flex-direction: column;
width: 90%;
gap: 12px;
background: ${colors.darkgrey.dark};
margin: 20px 20px 0 20px;
padding: 16px 12px;
border-radius: 12px;
`;

export const CommentSectionHeader = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
`;

export const CommentSectionTitle = styled.h3`
color: ${colors.white};
font-size: ${typography.fontSize.base};
font-weight: ${typography.fontWeight.semibold};
margin: 0;
`;

export const CommentSectionChevron = styled.img`
width: 24px;
height: 24px;
margin-right: -8px;
`;

export const CommentContent = styled.div`
display: flex;
flex-direction: column;
`;

export const CommentText = styled.p`
color: ${colors.grey[100]};
font-size: ${typography.fontSize.xs};
font-weight: ${typography.fontWeight.medium};
margin: 0;
`;
30 changes: 30 additions & 0 deletions src/components/group/CommentSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {
CommentSection as StyledCommentSection,
CommentSectionHeader,
CommentSectionTitle,
CommentSectionChevron,
CommentContent,
CommentText,
} from './CommentSection.styled';
import rightChevron from '../../assets/group/right-chevron.svg';

interface CommentSectionProps {
message: string;
onClick: () => void;
}

const CommentSection = ({ message, onClick }: CommentSectionProps) => {
return (
<StyledCommentSection>
<CommentSectionHeader onClick={onClick}>
<CommentSectionTitle>오늘의 한마디</CommentSectionTitle>
<CommentSectionChevron src={rightChevron} alt="한마디 이동 버튼" />
</CommentSectionHeader>
<CommentContent>
<CommentText>{message}</CommentText>
</CommentContent>
</StyledCommentSection>
);
};

export default CommentSection;
93 changes: 93 additions & 0 deletions src/components/group/GroupActionBottomSheet.styled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import styled from '@emotion/styled';
import { colors, typography } from '@/styles/global/global';

export const Overlay = styled.div<{ isOpen: boolean }>`
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
backdrop-filter: blur(2px);
z-index: 1000;
opacity: ${({ isOpen }) => (isOpen ? 1 : 0)};
visibility: ${({ isOpen }) => (isOpen ? 'visible' : 'hidden')};
transition:
opacity 0.3s ease,
visibility 0.3s ease;
display: flex;
align-items: flex-end;
justify-content: center;
`;

export const BottomSheet = styled.div<{ isOpen: boolean }>`
background-color: ${colors.darkgrey.main};
border-radius: 20px 20px 0 0;
width: 100%;
max-width: 767px;
min-width: 320px;
padding: 20px;
transform: ${({ isOpen }) => (isOpen ? 'translateY(0)' : 'translateY(100%)')};
transition: transform 0.3s ease-in-out;
`;

export const DeleteGroupActionItem = styled.button`
width: 100%;
background: none;
border: none;
padding: 13px 12px;
color: ${colors.red};
font-size: ${typography.fontSize.base};
font-weight: ${typography.fontWeight.medium};
text-align: left;
cursor: pointer;
border-radius: 8px;

&:hover {
background-color: ${colors.darkgrey.main};
}
`;

export const LeaveGroupActionItem = styled.button`
width: 100%;
background: none;
border: none;
padding: 13px 12px;
color: ${colors.white};
font-size: ${typography.fontSize.base};
font-weight: ${typography.fontWeight.medium};
text-align: left;
cursor: pointer;
border-radius: 8px;

&:hover {
background-color: ${colors.darkgrey.main};
}
`;

export const ReportGroupActionItem = styled.button`
width: 100%;
background: none;
border: none;
padding: 13px 12px;
color: ${colors.red};
font-size: ${typography.fontSize.base};
font-weight: ${typography.fontWeight.medium};
text-align: left;
cursor: pointer;
border-radius: 8px;

&:hover {
background-color: ${colors.darkgrey.main};
}
`;

export const ActionItemsContainer = styled.div`
display: flex;
flex-direction: column;

> button:not(:last-child) {
border-bottom: 1px solid ${colors.grey[400]};
margin-bottom: 8px;
padding-bottom: 20px;
}
`;
70 changes: 70 additions & 0 deletions src/components/group/GroupActionBottomSheet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import {
Overlay,
BottomSheet,
DeleteGroupActionItem,
LeaveGroupActionItem,
ReportGroupActionItem,
ActionItemsContainer,
} from './GroupActionBottomSheet.styled';

interface GroupActionBottomSheetProps {
isOpen: boolean;
isGroupOwner: boolean; // 모임방 생성자인지 여부
onClose: () => void;
onDeleteGroup?: () => void; // 방 삭제하기
onLeaveGroup?: () => void; // 방 나가기
onReportGroup?: () => void; // 방 신고하기
}

const GroupActionBottomSheet = ({
isOpen,
isGroupOwner,
onClose,
onDeleteGroup,
onLeaveGroup,
onReportGroup,
}: GroupActionBottomSheetProps) => {
const handleOverlayClick = (e: React.MouseEvent) => {
if (e.target === e.currentTarget) {
onClose();
}
};

const handleDeleteGroup = () => {
onDeleteGroup?.();
onClose();
};

const handleLeaveGroup = () => {
onLeaveGroup?.();
onClose();
};

const handleReportGroup = () => {
onReportGroup?.();
onClose();
};

if (!isOpen) return null;

return (
<Overlay isOpen={isOpen} onClick={handleOverlayClick}>
<BottomSheet isOpen={isOpen}>
<ActionItemsContainer>
{isGroupOwner ? (
// 모임방 생성자인 경우 - 방 삭제하기만 표시
<DeleteGroupActionItem onClick={handleDeleteGroup}>방 삭제하기</DeleteGroupActionItem>
) : (
// 참여자인 경우 - 방 나가기, 방 신고하기 표시
<>
<LeaveGroupActionItem onClick={handleLeaveGroup}>방 나가기</LeaveGroupActionItem>
<ReportGroupActionItem onClick={handleReportGroup}>방 신고하기</ReportGroupActionItem>
</>
)}
</ActionItemsContainer>
</BottomSheet>
</Overlay>
);
};

export default GroupActionBottomSheet;
40 changes: 40 additions & 0 deletions src/components/group/GroupBookSection.styled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import styled from '@emotion/styled';
import { colors, semanticColors, typography } from '@/styles/global/global';

export const GroupBookSection = styled.section`
display: flex;
align-items: center;
justify-content: space-between;
width: 90%;
background: ${colors.darkgrey.dark};
margin: 0 20px 0 20px;
padding: 10px 12px;
border-radius: 12px;
cursor: pointer;
`;

export const BookTitle = styled.h3`
color: ${colors.white};
font-size: ${typography.fontSize.base};
font-weight: ${typography.fontWeight.semibold};
margin: 0;
margin-right: 12px;
`;

export const BookAuthor = styled.span`
color: ${semanticColors.text.secondary};
font-size: ${typography.fontSize.xs};
font-weight: ${typography.fontWeight.regular};
`;

export const ChevronIcon = styled.img`
width: 24px;
height: 24px;
flex-shrink: 0;
`;

export const RightSection = styled.div`
display: flex;
align-items: center;
margin-right: -8px;
`;
28 changes: 28 additions & 0 deletions src/components/group/GroupBookSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {
GroupBookSection as StyledGroupBookSection,
BookTitle,
BookAuthor,
ChevronIcon,
RightSection,
} from './GroupBookSection.styled';
import rightChevron from '../../assets/group/right-chevron.svg';

interface GroupBookSectionProps {
title: string;
author: string;
onClick: () => void;
}

const GroupBookSection = ({ title, author, onClick }: GroupBookSectionProps) => {
return (
<StyledGroupBookSection onClick={onClick}>
<BookTitle>{title}</BookTitle>
<RightSection>
<BookAuthor>{author}</BookAuthor>
<ChevronIcon src={rightChevron} alt="책 이동 버튼" />
</RightSection>
</StyledGroupBookSection>
);
};

export default GroupBookSection;
Loading