Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 7 additions & 2 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -524,9 +524,14 @@
justify-content: space-between;
width: 100% !important;
}
.react-datepicker__day-name {
.react-datepicker__day-name,
.react-datepicker__day-name span {
font-family: var(--font-pretendard) !important;
font-weight: 500 !important;
font-size: 14px !important;
line-height: 20px !important;
letter-spacing: -0.02em !important;
color: #64748b !important;
font-weight: 600 !important;
flex: 1 !important; /* Distribute evenly */
width: auto !important;
line-height: 2.5rem !important; /* Mobile friendly height */
Expand Down
33 changes: 24 additions & 9 deletions src/app/meet/[meetingId]/ParticipantHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
'use client';

import { useRouter } from 'next/navigation';

import { useDisclosure } from '@/shared/hooks/useDisclosure';
import LinkShareBottomSheet from '@/shared/ui/bottom-sheet/LinkShareBottomSheet';
import { Menu } from '@/shared/ui/menu';
import { TopBar } from '@/shared/ui/top-bar';

interface ParticipantHeaderProps {
Expand All @@ -14,24 +18,35 @@ export default function ParticipantHeader({
url,
className,
}: ParticipantHeaderProps) {
const router = useRouter();
const {
isOpen: isShareOpen,
open: openShare,
close: closeShare,
} = useDisclosure();

const handleRightClick = () => {
openShare();
};
const {
isOpen: isMenuOpen,
open: openMenu,
close: closeMenu,
} = useDisclosure();

return (
<>
<TopBar
title={title}
rightIcon='ic_other_share'
onRightClick={handleRightClick}
className={className}
/>
<div className={`relative ${className}`}>
<TopBar
title={title}
leftIcon='ic_hamburger'
onLeftClick={openMenu}
rightIcon='ic_other_share'
onRightClick={openShare}
/>

<Menu isOpen={isMenuOpen} onClose={closeMenu} className='top-12 left-4'>
<Menu.Item onClick={() => router.push('/')}>모임 생성하기</Menu.Item>
</Menu>
</div>

<LinkShareBottomSheet
isOpen={isShareOpen}
onClose={closeShare}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ export function ReactDatepickerAdapter({
>
<Icon name='arrow_prev' size={24} />
</button>
<span className='text-xl font-bold text-slate-800'>
<span className='text-title-6 text-slate-800'>
{format(date, 'M월')}
</span>
{showNextMonthTooltip && !nextMonthButtonDisabled ? (
Expand Down Expand Up @@ -266,7 +266,7 @@ export function ReactDatepickerAdapter({
textClass = 'text-slate-300';
} else if (isSelected) {
bgClass = 'bg-gray-800';
textClass = 'text-white font-bold';
textClass = 'text-white';
} else if (isInDragRange) {
bgClass = 'bg-slate-100';
textClass = 'text-slate-900';
Expand All @@ -290,7 +290,7 @@ export function ReactDatepickerAdapter({
style={{ touchAction: 'none' }}
>
<div
className={`relative flex h-10 w-10 items-center justify-center rounded-lg text-base font-normal transition-colors ${bgClass} ${textClass}`}
className={`relative flex h-10 w-10 items-center justify-center rounded-lg transition-colors ${bgClass} ${textClass} text-body-4`}
>
{day}
</div>
Expand Down
8 changes: 4 additions & 4 deletions src/features/meet-create-date/model/useDateSelect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function useDateSelect(hostName: string, meetingName: string) {
hostName,
meetingName,
});
router.push(`/create?${params.toString()}`);
router.replace(`/create?${params.toString()}`);
};

const handleNext = async (formattedDates: string[]) => {
Expand Down Expand Up @@ -49,7 +49,7 @@ export function useDateSelect(hostName: string, meetingName: string) {

const handleDirectVote = () => {
if (createdMeetingId) {
router.push(`/meet/${createdMeetingId}`);
router.replace(`/meet/${createdMeetingId}`);
}
};

Expand All @@ -63,10 +63,10 @@ export function useDateSelect(hostName: string, meetingName: string) {
});
console.log('주최자 투표 완료');
// 캐시 무효화를 위해 전체 페이지 새로고침으로 이동
window.location.href = `/meet/${createdMeetingId}`;
router.replace(`/meet/${createdMeetingId}`);
} catch (error) {
console.error('투표 실패:', error);
window.location.href = `/meet/${createdMeetingId}`;
router.replace(`/meet/${createdMeetingId}`);
}
}
};
Expand Down
2 changes: 1 addition & 1 deletion src/features/meet-create/ui/MeetCreatePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export default function MeetCreatePage({
hostName: hostName.trim(),
meetingName: finalMeetingName,
});
router.push(`/date?${params.toString()}`);
router.replace(`/date?${params.toString()}`);
};

return (
Expand Down
15 changes: 12 additions & 3 deletions src/features/participant-edit-date/model/useParticipantEditDate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,16 @@ export function useParticipantEditDate(meetingId: string) {
const successModal = useDisclosure();

const handleBack = () => {
router.back();
const params = new URLSearchParams();
if (participantName) {
params.set('name', participantName);
}
router.replace(`/meet/${meetingId}/edit?${params.toString()}`);
};

const handleSuccessModalClose = () => {
successModal.close();
router.push(`/meet/${meetingId}`);
router.replace(`/meet/${meetingId}`);
};

const handleSubmit = async () => {
Expand Down Expand Up @@ -143,7 +147,12 @@ export function useParticipantEditDate(meetingId: string) {
isSubmitting,
isCtaActive,
handleAllImpossibleChange,
onDateClick: handleDateChange, // 어댑터용 (Date[] => void)
onDateClick: (dates: Date[]) => {
if (isAllImpossible) {
setIsAllImpossible(false);
}
handleDateChange(dates);
},
handleBack,
handleSubmit,
isSuccessModalOpen: successModal.isOpen,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,6 @@ export default function ParticipantEditDatePage({
availableDates={availableDates}
showNextMonthTooltip={true}
/>

{/* 불참 체크 시 캘린더 비활성 (오버레이 처리) */}
{isAllImpossible && (
<div className='absolute inset-0 z-10 bg-white/50' />
)}
</div>

{/* 2-4. '모든 날짜에 참여가 어려워요' 체크박스 */}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export function useParticipantEditName(meetingId: string) {
// 식별을 위해 encodedName을 쿼리로 넘기는 방식을 채택 (간단한 구현)
const params = new URLSearchParams();
params.set('name', trimmedName);
router.push(`/meet/${meetingId}/edit/date?${params.toString()}`);
router.replace(`/meet/${meetingId}/edit/date?${params.toString()}`);
} else {
// 실패 케이스: 참여 이력 없음
setErrorDetails({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,16 @@ export function useParticipantRegisterDate(meetingId: string) {
const successModal = useDisclosure();

const handleBack = () => {
router.back();
const params = new URLSearchParams();
if (participantName) {
params.set('name', participantName);
}
router.replace(`/meet/${meetingId}/register?${params.toString()}`);
};

const handleSuccessModalClose = () => {
successModal.close();
router.push(`/meet/${meetingId}`);
router.replace(`/meet/${meetingId}`);
};

const handleSubmit = async () => {
Expand Down Expand Up @@ -134,7 +138,12 @@ export function useParticipantRegisterDate(meetingId: string) {
isSubmitting,
isCtaActive,
handleAllImpossibleChange,
onDateClick: handleDateChange,
onDateClick: (dates: Date[]) => {
if (isAllImpossible) {
setIsAllImpossible(false);
}
handleDateChange(dates);
},
handleBack,
handleSubmit,
isSuccessModalOpen: successModal.isOpen,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,6 @@ export default function ParticipantRegisterDatePage({
availableDates={availableDates}
showNextMonthTooltip={true}
/>

{/* 불참 체크 시 캘린더 비활성 (오버레이 처리) */}
{isAllImpossible && (
<div className='absolute inset-0 z-10 bg-white/50' />
)}
</div>

{/* 2-4. '모든 날짜에 참여가 어려워요' 체크박스 */}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export function useParticipantRegisterName(meetingId: string) {
const params = new URLSearchParams();
params.set('name', trimmedName);
// [수정] 이동 경로: register/date
router.push(`/meet/${meetingId}/register/date?${params.toString()}`);
router.replace(`/meet/${meetingId}/register/date?${params.toString()}`);
}
} catch (error) {
console.error('Failed to check participant existence:', error);
Expand Down
30 changes: 16 additions & 14 deletions src/features/vote-results-calendar/ui/ReactDatepicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,13 @@ export function ReactDatePickerVoteResultsCalendar({
if (votes > 0) {
if (focusedParticipant) {
bgClass = 'bg-gray-800';
textClass = 'text-white font-bold';
textClass = 'text-white';
} else if (rank && rank <= 3) {
bgClass = 'bg-blue-100 shadow-sm';
textClass = 'text-white font-bold';
textClass = 'text-white';
} else {
bgClass = 'bg-blue-30';
textClass = 'text-blue-100 font-medium';
textClass = 'text-blue-100';
}
} else {
bgClass = 'hover:bg-slate-100';
Expand All @@ -126,16 +126,18 @@ export function ReactDatePickerVoteResultsCalendar({
return (
<div className='flex h-full w-full items-center justify-center py-1'>
<div
className={cn(
'relative flex h-9 w-9 items-center justify-center rounded-lg text-base font-normal transition-colors sm:h-10 sm:w-10',
bgClass,
textClass,
selectedClass,
isInteractive && !isSelected && 'hover:brightness-95',
!isInteractive && 'pointer-events-none text-slate-300',
isFilteredOut && 'text-slate-900',
isInteractive && 'pointer-events-auto cursor-pointer',
)}
className={
cn(
'relative flex h-9 w-9 items-center justify-center rounded-lg transition-colors sm:h-10 sm:w-10',
bgClass,
textClass,
selectedClass,
isInteractive && !isSelected && 'hover:brightness-95',
!isInteractive && 'pointer-events-none text-slate-300',
isFilteredOut && 'text-slate-900',
isInteractive && 'pointer-events-auto cursor-pointer',
) + ' text-body-4'
}
>
{day}

Expand Down Expand Up @@ -184,7 +186,7 @@ export function ReactDatePickerVoteResultsCalendar({
>
<Icon name='arrow_prev' size={24} />
</button>
<span className='text-xl font-bold text-slate-800'>
<span className='text-title-6 text-slate-800'>
{format(date, 'M월')}
</span>
<button
Expand Down
71 changes: 71 additions & 0 deletions src/shared/ui/menu/Menu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
'use client';

import { ReactNode, useRef } from 'react';

interface MenuProps {
isOpen: boolean;
onClose: () => void;
// position prop은 나중에 필요하다면 확장 가능하며, 현재는 absolute positioning을 기본으로 사용합니다.
// 완전히 일반적인 컴포넌트로 만들려면 구체적인 위치 지정 로직이 필요할 수 있습니다(예: 라이브러리 또는 유연한 클래스 사용).
// 지금은 className으로 위치를 재정의할 수 있도록 합니다.
className?: string;
children: ReactNode;
}

function MenuMain({ isOpen, onClose, className, children }: MenuProps) {
const menuRef = useRef<HTMLDivElement>(null);

// 부모 컴포넌트나 백드롭에서 처리하지 않는 경우, 외부 클릭 시 닫기 로직을 여기에 추가할 수 있습니다.
// 현재 디자인은 단순함을 위해 고정된 백드롭을 사용합니다.

if (!isOpen) return null;

return (
<>
<div
ref={menuRef}
className={`animate-fade-in-down absolute z-50 flex min-w-[160px] flex-col overflow-hidden rounded-xl border border-gray-100 bg-white shadow-lg ${
className || ''
}`}
>
{children}
</div>
{/* Backdrop */}
<div
className='fixed inset-0 z-40 bg-transparent'
onClick={onClose}
aria-hidden='true'
/>
</>
);
}

interface MenuItemProps {
children: ReactNode;
onClick?: () => void;
className?: string;
}

function MenuItem({ children, onClick, className }: MenuItemProps) {
return (
<button
type='button'
onClick={onClick}
className={`text-body-2 hover:bg-primary-50 active:bg-primary-100 flex w-full items-center px-4 py-3 text-left text-gray-800 transition-colors ${
className || ''
}`}
>
{children}
</button>
);
}

// Menu를 위한 Divider 스타일 컴포넌트
function MenuDivider() {
return <div className='h-px w-full bg-gray-100' />;
}

export const Menu = Object.assign(MenuMain, {
Item: MenuItem,
Divider: MenuDivider,
});
1 change: 1 addition & 0 deletions src/shared/ui/menu/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Menu } from './Menu';