Skip to content

Commit 004f848

Browse files
committed
fix: 일정 클릭시 해당일에 일정리스트 보이기
1 parent ec23422 commit 004f848

File tree

3 files changed

+133
-74
lines changed

3 files changed

+133
-74
lines changed

src/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ function App() {
4646
</div>
4747
</header>
4848
<main className="z-1 relative flex-grow">
49-
<div className="ml-auto max-w-7xl px-4 sm:px-6 lg:px-8">
49+
<div className="ml-auto max-w-7xl p-4 pb-10 sm:px-6 lg:px-8">
5050
<Calendar />
5151
</div>
5252
</main>

src/components/common/Calendar.tsx

Lines changed: 113 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,44 @@
1+
/* eslint-disable react-hooks/exhaustive-deps */
12
import FullCalendar from '@fullcalendar/react';
2-
import { EventClickArg } from '@fullcalendar/core';
3+
// import { EventClickArg } from '@fullcalendar/core';
34
import dayGridPlugin from '@fullcalendar/daygrid';
45
import interactionPlugin from '@fullcalendar/interaction';
5-
import { useRef, useState, useEffect } from 'react';
6+
import { useRef, useState, useEffect, useCallback } from 'react';
67
import { useEventState } from '@/stores/myEventsStore';
78
import { getPersonalSchedule } from '@/apis/personalScheduleApi';
8-
9+
import { Events } from '../../utils/index.ts';
10+
/*
911
type Event = {
1012
title: string;
11-
start: Date | string;
13+
start: string;
14+
end: string;
15+
backgroundColor?: string;
16+
borderColor?: string;
17+
textColor?: string;
1218
};
13-
interface EventCardsProps {
14-
events: Event[];
15-
date: Date | string | null;
19+
*/
20+
21+
interface EventInfo {
22+
timeText: string;
23+
event: {
24+
title: string;
25+
};
1626
}
1727

18-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
19-
const events = [
20-
{ title: 'Meeting', start: new Date() },
21-
{ title: 'Meeting', start: '2024-05-08' },
22-
{ title: 'Meeting', start: '2024-05-08' },
23-
{ title: 'Meeting', start: '2024-05-08' },
24-
];
28+
interface EventCardsProps {
29+
events: Events[];
30+
date: string | null;
31+
}
2532

2633
export default function Calendar() {
2734
const [calendarHeight, setCalendarHeight] = useState<string | number>('auto');
2835
const calendarRef = useRef<FullCalendar | null>(null);
29-
const [selectedEvents, setSelectedEvents] = useState<Event[]>([]);
30-
const [selectedDate, setSelectedDate] = useState<Date | null>(null);
36+
const [selectedEvents, setSelectedEvents] = useState<Events[]>([]);
37+
const [selectedDate, setSelectedDate] = useState<string | null>(null);
38+
const { events, addEvents } = useEventState();
3139

40+
41+
/*
3242
const handleDateClick = (clickInfo: EventClickArg) => {
3343
if (clickInfo.event.start) {
3444
const clickStartDate = new Date(clickInfo.event.start);
@@ -40,6 +50,17 @@ export default function Calendar() {
4050
console.log('not available');
4151
}
4252
};
53+
*/
54+
55+
const handleDateSelection = (dateClickInfo: { dateStr: string }) => {
56+
console.log(dateClickInfo)
57+
const clickedDateStr = dateClickInfo.dateStr;
58+
setSelectedDate(clickedDateStr);
59+
setSelectedEvents(events.filter(event =>
60+
clickedDateStr >= event.start.split('T')[0] &&
61+
clickedDateStr <= (event.end ? event.end.split('T')[0] : event.start.split('T')[0])
62+
));
63+
};
4364

4465
const handlePrev = () => {
4566
const calendarApi = calendarRef?.current?.getApi();
@@ -61,17 +82,27 @@ export default function Calendar() {
6182
}
6283
};
6384

64-
// eslint-disable-next-line react-hooks/exhaustive-deps
65-
const updateSize = () => {
66-
const isMobile = window.innerWidth < 768;
67-
setCalendarHeight(isMobile ? 500 : 'auto');
68-
updateTitle();
69-
};
85+
const updateSize = useCallback(() => {
86+
setCalendarHeight(window.innerWidth < 768 ? 500 : 'auto');
87+
}, []);
88+
89+
function convertEvents(events: {title: string, start_date: string, end_date: string, backgroundColor?: string, borderColor?: string, textColor?: string}[]): Events[] {
90+
return events.map(event => ({
91+
title: event.title,
92+
start: event.start_date,
93+
end: event.end_date,
94+
backgroundColor: event.backgroundColor || '#3788d8',
95+
borderColor: event.borderColor || '#296c98',
96+
textColor: event.textColor || '#ffffff'
97+
}));
98+
}
7099

100+
/*
71101
const updateTitle = () => {
72102
const calendarApi = calendarRef?.current?.getApi();
73103
if (calendarApi) {
74104
const calendarView = calendarApi.view;
105+
console.log('View start date:', calendarView.currentStart);
75106
76107
const date = new Date(calendarView.currentStart);
77108
const year = date.getFullYear();
@@ -84,34 +115,51 @@ export default function Calendar() {
84115
}
85116
}
86117
};
118+
*/
87119

88-
const { events, addEvents } = useEventState();
89120

90121
useEffect(() => {
122+
/*
91123
const calendarApi = calendarRef?.current?.getApi();
92-
93124
if (calendarApi) {
94125
calendarApi.on('datesSet', updateTitle);
95126
}
96127
97128
updateTitle(); // 컴포넌트 마운트 시 제목 업데이트
129+
*/
98130

99131
/* 캘린더 - 반응형 사이즈 */
100132
window.addEventListener('resize', updateSize);
101133
updateSize(); // 컴포넌트 마운트 시 화면 크기에 따른 업데이트
102134

103-
const data = getPersonalSchedule();
104-
data.then((schedule) => {
105-
schedule.map((x) => addEvents({ ...x, start: x.start_date, end: x.end_date }));
106-
});
107-
108135
return () => {
109136
window.removeEventListener('resize', updateSize);
137+
/*
110138
if (calendarApi) {
111139
calendarApi.off('datesSet', updateTitle);
112140
}
141+
*/
113142
};
114-
}, [updateSize, addEvents]);
143+
}, [updateSize]);
144+
145+
const [isLoaded, setIsLoaded] = useState(false); // 데이터 로딩 상태
146+
147+
useEffect(() => {
148+
if (!isLoaded) {
149+
getPersonalSchedule().then(schedule => {
150+
const uniqueEvents = schedule.filter(newEvent =>
151+
!events.some(existingEvent =>
152+
existingEvent.start === newEvent.start_date && existingEvent.title === newEvent.title
153+
)
154+
);
155+
if (uniqueEvents.length > 0) {
156+
const eventsToAdd = convertEvents(uniqueEvents);
157+
eventsToAdd.forEach(eventToAdd => addEvents(eventToAdd));
158+
setIsLoaded(true);
159+
}
160+
});
161+
}
162+
}, [events, addEvents]);
115163

116164
return (
117165
<div>
@@ -121,7 +169,8 @@ export default function Calendar() {
121169
plugins={[dayGridPlugin, interactionPlugin]}
122170
initialView="dayGridMonth"
123171
events={events}
124-
eventClick={handleDateClick}
172+
// eventClick={handleDateClick}
173+
dateClick={handleDateSelection}
125174
dayMaxEvents={2} //Max개수까지보이고 나머지는 more
126175
//navLinks={true} // 날짜/주 이름을 클릭하여 뷰를 변경할 수 있습니다.
127176
editable={true} // 이벤트를 수정할 수 있습니다.
@@ -150,17 +199,11 @@ export default function Calendar() {
150199
}}
151200
/>
152201
</div>
153-
<div className="mt-10">{selectedDate && <EventCards events={selectedEvents} date={selectedDate} />}</div>
202+
<div className="mt-10">{selectedDate && <EventCards events={selectedEvents} date={selectedDate} /> }</div>
154203
</div>
155204
);
156205
}
157206

158-
interface EventInfo {
159-
timeText: string;
160-
event: {
161-
title: string;
162-
};
163-
}
164207

165208
function renderEventContent(eventInfo: EventInfo) {
166209
return (
@@ -174,50 +217,47 @@ function renderEventContent(eventInfo: EventInfo) {
174217
}
175218

176219
function EventCards({ events, date }: EventCardsProps) {
220+
console.log(events, date);
177221
const [menuOpen, setMenuOpen] = useState(-1);
178222

179-
if (!date) {
180-
return <div>No date provided</div>; // date가 null인 경우 처리
223+
if (!events.length) {
224+
return <div className='min-h-[150px] min-w-[240px] bg-white p-4 text-black'>
225+
일정이 없습니다. <br />일정을 등록해주세요!
226+
</div>;
181227
}
182228

183-
const formattedDate = new Date(date)
184-
.toLocaleDateString('ko-KR', {
185-
year: 'numeric',
186-
month: '2-digit',
187-
day: '2-digit',
188-
})
189-
.replace(/\. /g, '.')
190-
.slice(0, -1);
191-
192229
return (
193230
<div>
194-
<h2 className="ml-2">{formattedDate}</h2>
231+
<h2 className="ml-2">{date}</h2>
195232
<div className="flex gap-5 overflow-x-auto">
196-
{events.map((event, index) => (
197-
<div key={index} className="relative min-h-[150px] min-w-[240px] bg-white p-4 text-black">
198-
<h3>{event.title}</h3>
199-
<p className="mt-1 text-xs">{new Date(event.start).toLocaleTimeString()}</p>
200-
{/* 메뉴 버튼 */}
201-
<div
202-
className="absolute right-2 top-2 flex cursor-pointer flex-col items-center justify-center"
203-
onClick={() => setMenuOpen(menuOpen === index ? -1 : index)}
204-
>
205-
<div className="mb-1 h-1 w-1 rounded-full bg-[#429400]"></div>
206-
<div className="mb-1 h-1 w-1 rounded-full bg-[#429400]"></div>
207-
<div className="h-1 w-1 rounded-full bg-[#429400]"></div>
208-
</div>
209-
{/* 메뉴 */}
210-
{menuOpen === index && (
211-
<div className="absolute right-0 top-10 z-10 rounded-lg bg-white shadow-md">
212-
<ul>
213-
<li className="cursor-pointer p-2 hover:bg-gray-100">편집</li>
214-
<li className="cursor-pointer p-2 hover:bg-gray-100">삭제</li>
215-
</ul>
233+
{events.map((event, index) => {
234+
return (
235+
<div key={index} className="relative min-h-[150px] min-w-[240px] bg-white p-4 text-black">
236+
<h3>{event.title}</h3>
237+
<p className="mt-1 text-xs">{event.start === event.end ? event.start : `${event.start}~${event.end}`}</p>
238+
{/* 메뉴 버튼 */}
239+
<div
240+
className="absolute right-2 top-2 flex cursor-pointer flex-col items-center justify-center"
241+
onClick={() => setMenuOpen(menuOpen === index ? -1 : index)}
242+
>
243+
<div className="mb-1 h-1 w-1 rounded-full bg-[#429400]"></div>
244+
<div className="mb-1 h-1 w-1 rounded-full bg-[#429400]"></div>
245+
<div className="h-1 w-1 rounded-full bg-[#429400]"></div>
216246
</div>
217-
)}
218-
</div>
219-
))}
247+
{/* 메뉴 */}
248+
{menuOpen === index && (
249+
<div className="absolute right-0 top-10 z-10 rounded-lg bg-white shadow-md">
250+
<ul>
251+
<li className="cursor-pointer p-2 hover:bg-gray-100">편집</li>
252+
<li className="cursor-pointer p-2 hover:bg-gray-100">삭제</li>
253+
</ul>
254+
</div>
255+
)}
256+
</div>
257+
);
258+
})}
220259
</div>
221260
</div>
222261
);
223262
}
263+

src/styles/index.css

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,22 @@
7171
.fc .fc-h-event {
7272
border: var(--fc-event-text-color);
7373
}
74+
.fc-daygrid-day {
75+
cursor: pointer;
76+
}
77+
78+
.fc-daygrid-day-frame {
79+
height: 100%;
80+
z-index: 10;
81+
&:hover {
82+
&::before {
83+
content: '';
84+
position: absolute;
85+
width: 100%;
86+
height: 100%;
87+
background-color: #bec00038;
88+
top: 0;
89+
left: 0;
90+
}
91+
}
92+
}

0 commit comments

Comments
 (0)