Skip to content
Snippets Groups Projects
Commit b15f63c4 authored by 조대희's avatar 조대희
Browse files

style: 스케줄 통합 및 더보기 버튼 추가 (#13)

parent 1f0133b6
No related branches found
No related tags found
1 merge request!21[#13] 스케줄 페이지 스타일 개선
Pipeline #10901 passed
...@@ -22,70 +22,74 @@ const generateTimeSlots = () => { ...@@ -22,70 +22,74 @@ const generateTimeSlots = () => {
const days = ["", "", "", "", "", "", ""]; const days = ["", "", "", "", "", "", ""];
const dummySchedules = [ // const dummySchedules = [
{ // {
id: 1, // id: 1,
user_id: 1, // user_id: 1,
title: "알고리즘 스터디", // title: "알고리즘 스터디",
is_fixed: true, // is_fixed: true,
time_indices: [36, 37, 38, 39], // time_indices: [36, 37, 38, 39],
createdAt: "2024-12-02T09:52:00.000Z", // createdAt: "2024-12-02T09:52:00.000Z",
updatedAt: "2024-12-02T09:52:00.000Z", // updatedAt: "2024-12-02T09:52:00.000Z",
}, // },
{ // {
id: 5, // id: 5,
user_id: 1, // user_id: 1,
title: "웹시설 팀플", // title: "웹시설 팀플",
is_fixed: true, // is_fixed: true,
time_indices: [165, 166, 167, 255, 256, 257], // time_indices: [165, 166, 167, 255, 256, 257],
createdAt: "2024-12-02T09:54:53.000Z", // createdAt: "2024-12-02T09:54:53.000Z",
updatedAt: "2024-12-02T09:54:53.000Z", // updatedAt: "2024-12-02T09:54:53.000Z",
}, // },
{ // {
id: 11, // id: 11,
user_id: 1, // user_id: 1,
title: "점심약속", // title: "점심약속",
is_fixed: false, // is_fixed: false,
time_indices: [240, 241, 242], // time_indices: [240, 241, 242],
createdAt: "2024-12-02T09:54:53.000Z", // createdAt: "2024-12-02T09:54:53.000Z",
updatedAt: "2024-12-02T09:54:53.000Z", // updatedAt: "2024-12-02T09:54:53.000Z",
}, // },
{ // {
id: 14, // id: 14,
user_id: 1, // user_id: 1,
title: "롤 5:5", // title: "롤 5:5",
is_fixed: true, // is_fixed: true,
time_indices: [302, 303, 304, 305, 306, 307], // time_indices: [302, 303, 304, 305, 306, 307],
createdAt: "2024-12-02T09:54:53.000Z", // createdAt: "2024-12-02T09:54:53.000Z",
updatedAt: "2024-12-02T09:54:53.000Z", // updatedAt: "2024-12-02T09:54:53.000Z",
}, // },
{ // {
id: 20, // id: 20,
user_id: 1, // user_id: 1,
title: "토트넘 vs 첼시 경기", // title: "토트넘 vs 첼시 경기",
is_fixed: true, // is_fixed: true,
time_indices: [13, 14, 15, 16, 17, 18], // time_indices: [13, 14, 15, 16, 17, 18],
createdAt: "2024-12-02T09:54:53.000Z", // createdAt: "2024-12-02T09:54:53.000Z",
updatedAt: "2024-12-02T09:54:53.000Z", // updatedAt: "2024-12-02T09:54:53.000Z",
} // },
]; // {
// id: 26,
// user_id: 1,
// title: "아침 구보",
// is_fixed: true,
// time_indices: [34, 35, 130, 131, 226, 227, 322, 323, 418, 419, 514, 515, 610, 611],
// createdAt: "2024-12-02T09:54:53.000Z",
// updatedAt: "2024-12-02T09:54:53.000Z",
// },
// ];
const colorClasses = [ const colorClasses = [
'bg-indigo-300', 'bg-indigo-300 hover:bg-indigo-400',
'bg-purple-300', 'bg-purple-300 hover:bg-purple-400',
'bg-pink-300', 'bg-pink-300 hover:bg-pink-400',
'bg-blue-300', 'bg-blue-300 hover:bg-blue-400',
'bg-green-300', 'bg-green-300 hover:bg-green-400',
'bg-yellow-300', 'bg-yellow-300 hover:bg-yellow-400',
'bg-red-300', 'bg-red-300 hover:bg-red-400',
'bg-orange-300', 'bg-orange-300 hover:bg-orange-400',
'bg-teal-300', 'bg-teal-300 hover:bg-teal-400',
'bg-cyan-300', 'bg-cyan-300 hover:bg-cyan-400',
'bg-rose-300',
'bg-violet-300',
'bg-fuchsia-300',
'bg-emerald-300',
'bg-lime-300'
]; ];
const SchedulePage = () => { const SchedulePage = () => {
const timeSlots = generateTimeSlots(); const timeSlots = generateTimeSlots();
...@@ -97,13 +101,21 @@ const SchedulePage = () => { ...@@ -97,13 +101,21 @@ const SchedulePage = () => {
const [newTitle, setNewTitle] = useState(""); const [newTitle, setNewTitle] = useState("");
const [isFixed, setIsFixed] = useState(true); const [isFixed, setIsFixed] = useState(true);
const [titleColorMap, setTitleColorMap] = useState(new Map()); const [titleColorMap, setTitleColorMap] = useState(new Map());
const [showAllTimeSlot, setShowAllTimeSlot] = useState(false);
useEffect(() => { useEffect(() => {
// API // API
const initializeSchedules = async () => { const initializeSchedules = async () => {
try { try {
const data = await fetchAllSchedules(); const data = await fetchAllSchedules();
setSchedules(data);
// 스케줄 병합을 위해서 사용
const sortedSchedules = [...data].sort((a, b) => {
const aMin = Math.min(...a.time_indices);
const bMin = Math.min(...b.time_indices);
return aMin - bMin;
});
setSchedules(sortedSchedules);
} catch (error) { } catch (error) {
console.error("Failed to load schedules", error); console.error("Failed to load schedules", error);
} }
...@@ -112,7 +124,7 @@ const SchedulePage = () => { ...@@ -112,7 +124,7 @@ const SchedulePage = () => {
initializeSchedules(); initializeSchedules();
// 임시 코드 // 임시 코드
setSchedules(dummySchedules); // setSchedules(dummySchedules);
}, []); }, []);
useEffect(() => { useEffect(() => {
...@@ -278,13 +290,47 @@ const SchedulePage = () => { ...@@ -278,13 +290,47 @@ const SchedulePage = () => {
const timeSlotIndex = timeIndex % 96; const timeSlotIndex = timeIndex % 96;
const hour = Math.floor(timeSlotIndex / 4); const hour = Math.floor(timeSlotIndex / 4);
const minute = (timeSlotIndex % 4) * 15; const minute = (timeSlotIndex % 4) * 15;
const day = days[dayIndex]; const day = days[dayIndex];
const time = `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`; const time = `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`;
return `${day} ${time}`; return `${day} ${time}`;
}; };
// 스케줄 통합해서 보여주기
const renderTimeSlot = (slotIndex, rowIndex, colIndex) => {
const schedule = schedules.find((s) => s.time_indices.includes(slotIndex));
const isSelected = selectedSlots.includes(slotIndex);
const isFirstSlot = schedule &&
!schedule.time_indices.includes(slotIndex - 1) &&
Math.floor((slotIndex - 1) / 96) === Math.floor(slotIndex / 96);
const isLastSlot = schedule &&
!schedule.time_indices.includes(slotIndex + 1) &&
Math.floor((slotIndex + 1) / 96) === Math.floor(slotIndex / 96);
return (
<div
key={slotIndex}
className={`p-2 border ${schedule
? `${getColorForTitle(schedule.title)} text-white
${!isFirstSlot && !isLastSlot ? 'border-t-0 border-b-0' : ''}
${!isFirstSlot ? 'border-t-0' : ''}
${!isLastSlot ? 'border-b-0' : ''}`
: isSelected
? "bg-primary-100 border-primary-300"
: "bg-grayscale-50"
} cursor-pointer`}
onClick={() => handleSlotClick(slotIndex)}
>
{isFirstSlot ? schedule?.title : ""}
</div>
);
};
const filterTimeSlots = (time) => {
const hour = parseInt(time.split(":")[0]);
return showAllTimeSlot || (hour >= 8 && hour <= 18);
};
return ( return (
<div className="min-h-screen bg-grayscale-50"> <div className="min-h-screen bg-grayscale-50">
...@@ -312,10 +358,22 @@ const SchedulePage = () => { ...@@ -312,10 +358,22 @@ const SchedulePage = () => {
</label> </label>
</div> </div>
{/* 더보기 버튼 */}
<div className="flex justify-center mt-4">
<Label
theme="indigo"
size="lg"
className="cursor-pointer"
onClick={() => setShowAllTimeSlot(!showAllTimeSlot)}
>
{showAllTimeSlot ? "시간 접기" : "전체 시간 보기"}
</Label>
</div>
{/* Schedule Grid */} {/* Schedule Grid */}
<div className="p-4 pb-[210px]"> <div className="p-4 pb-[210px]">
<div className="overflow-auto scrollbar-hide"> <div className="overflow-auto scrollbar-hide">
<div className="w-[100vw] tablet:w-[960px] grid grid-cols-[64px,repeat(7,1fr)] gap-2"> <div className="w-[100vw] tablet:w-[960px] grid grid-cols-[64px,repeat(7,1fr)] gap-0">
{/* Header */} {/* Header */}
<div className="min-w-[54px] p-2 font-bold text-center bg-grayscale-200 select-none"> <div className="min-w-[54px] p-2 font-bold text-center bg-grayscale-200 select-none">
Time Time
...@@ -327,37 +385,21 @@ const SchedulePage = () => { ...@@ -327,37 +385,21 @@ const SchedulePage = () => {
))} ))}
{/* Time Slots */} {/* Time Slots */}
{timeSlots.map((time, rowIndex) => ( {timeSlots.map((time, rowIndex) => {
if (!filterTimeSlots(time)) return null;
return (
<React.Fragment key={rowIndex}> <React.Fragment key={rowIndex}>
{/* Time Column */}
<div className="min-w-[54px] p-2 font-bold text-center bg-grayscale-100 select-none"> <div className="min-w-[54px] p-2 font-bold text-center bg-grayscale-100 select-none">
{time} {time}
</div> </div>
{days.map((_, colIndex) => { {days.map((_, colIndex) => {
const slotIndex = colIndex * timeSlots.length + rowIndex; const slotIndex = colIndex * timeSlots.length + rowIndex;
const isSelected = selectedSlots.includes(slotIndex); return renderTimeSlot(slotIndex, rowIndex, colIndex);
const schedule = schedules.find((s) =>
s.time_indices.includes(slotIndex)
);
return (
<div
key={slotIndex}
className={`p-2 border rounded ${
schedule
? `${getColorForTitle(schedule.title)} text-white`
: isSelected
? "bg-primary-100 border-primary-300"
: "bg-grayscale-50"
} cursor-pointer`}
onClick={() => handleSlotClick(slotIndex)}
>
{schedule ? schedule.title : ""}
</div>
);
})} })}
</React.Fragment> </React.Fragment>
))} );
})}
</div> </div>
</div> </div>
</div> </div>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment