Skip to content

Commit

Permalink
Feat: #51 특정 프로젝트 관리 페이지: 프로젝트 기간 설정 유무에 따른 날짜 스타일 처리 (#60)
Browse files Browse the repository at this point in the history
* Feat: #51 PageLayout의 Outlet Context 설정 & 커스텀훅 추가

* Feat: #51 프로젝트 기간 이외의 날짜 disable 스타일 추가

* UI: #51 react-big-calendar CSS 오버라이딩

* Chore: 불필요한 주석 삭제

* Fix: #51 프로젝트 기간이 없는 경우 처리 추가

* Chore: #51 mock 데이터 수정
  • Loading branch information
Seok93 authored Aug 1, 2024
1 parent 3bd4058 commit 3745476
Show file tree
Hide file tree
Showing 9 changed files with 79 additions and 40 deletions.
22 changes: 15 additions & 7 deletions src/components/task/calendar/CustomDateHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { useMemo } from 'react';
import { DateTime } from 'luxon';
import { DateHeaderProps } from 'react-big-calendar';
import { LuxonWeekday } from '@constants/date';
import useProjectContext from '@hooks/useProjectContext';
import Validator from '@utils/Validator';

function getTextColor(weekday: LuxonWeekday, isOffRange: boolean) {
if (isOffRange) return 'text-[#999999]';
function getTextColor(weekday: LuxonWeekday, isWithinRange: boolean) {
if (!isWithinRange) return 'text-[#999999]';

switch (weekday) {
case LuxonWeekday.SATURDAY:
Expand All @@ -16,9 +17,16 @@ function getTextColor(weekday: LuxonWeekday, isOffRange: boolean) {
}
}

export default function CustomDateHeader({ date, isOffRange }: DateHeaderProps) {
const { weekday } = useMemo(() => DateTime.fromJSDate(date), [date]);
const textColor = useMemo(() => getTextColor(weekday, isOffRange), [weekday, isOffRange]);
export default function CustomDateHeader({ date, label }: DateHeaderProps) {
const { project } = useProjectContext();

return <div className={`pl-3 text-left ${textColor}`}>{date.getDate()}</div>;
// prettier-ignore
const isWithinDateRange = project.startDate && project.endDate
? Validator.isWithinDateRange(project.startDate, project.endDate, date)
: true;

const { weekday } = DateTime.fromJSDate(date);
const textColor = getTextColor(weekday, isWithinDateRange);

return <div className={`pl-3 text-left ${textColor}`}>{label.padStart(2, '0')}</div>;
}
1 change: 0 additions & 1 deletion src/components/task/kanban/ProjectStatusContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ type TaskStatusContainerProps = {
};

export default function TaskStatusContainer({ statusTask }: TaskStatusContainerProps) {
// const [showStatusModal, setShowStatusModal] = useState(false);
const { showModal, openModal, closeModal } = useModal();
const { statusId, name, color, order, tasks } = statusTask;
const draggableId = useMemo(() => generatePrefixId(statusId, DND_DRAGGABLE_PREFIX.STATUS), [statusId]);
Expand Down
7 changes: 7 additions & 0 deletions src/customReactBigCalendar.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.rbc-off-range-bg {
background: #f4f4f4;
}

.rbc-selected-cell {
background: var(--color-sub);
}
8 changes: 8 additions & 0 deletions src/hooks/useProjectContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { useOutletContext } from 'react-router-dom';
import { Project } from '@/types/ProjectType';

export type ProjectContext = { project: Project };

export default function useProjectContext() {
return useOutletContext<ProjectContext>();
}
13 changes: 8 additions & 5 deletions src/layouts/page/ProjectLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
import { useMemo } from 'react';
import { NavLink, Outlet, useParams } from 'react-router-dom';
import { Navigate, NavLink, Outlet, useParams } from 'react-router-dom';
import useModal from '@hooks/useModal';
import ListSidebar from '@components/sidebar/ListSidebar';
import ListProject from '@components/sidebar/ListProject';
import CreateModalTask from '@components/modal/task/CreateModalTask';
import CreateModalProjectStatus from '@components/modal/project-status/CreateModalProjectStatus';
import { ProjectContext } from '@hooks/useProjectContext';
import { PROJECT_DUMMY } from '@mocks/mockData';
import { RiSettings5Fill } from 'react-icons/ri';
import CreateModalTask from '@/components/modal/task/CreateModalTask';

export default function ProjectLayout() {
const { projectId } = useParams();
const { showModal: showTaskModal, openModal: openTaskModal, closeModal: closeTaskModal } = useModal();
const { showModal: showStatusModal, openModal: openStatusModal, closeModal: closeStatusModal } = useModal();
const target = useMemo(
const project = useMemo(
() => PROJECT_DUMMY.find((project) => project.projectId.toString() === projectId),
[projectId],
);

if (!project) return <Navigate to="/error" replace />;

return (
<>
<section className="flex h-full p-15">
Expand All @@ -27,7 +30,7 @@ export default function ProjectLayout() {
<header className="flex h-30 items-center justify-between border-b p-10">
<div>
<small className="mr-5 font-bold text-category">project</small>
<span className="text-emphasis">{target?.name}</span>
<span className="text-emphasis">{project?.name}</span>
</div>
<div className="flex cursor-pointer items-center text-sm text-main">
<RiSettings5Fill /> Project Setting
Expand Down Expand Up @@ -56,7 +59,7 @@ export default function ProjectLayout() {
</button>
</div>
</div>
<Outlet />
<Outlet context={{ project } satisfies ProjectContext} />
</div>
</section>
</section>
Expand Down
28 changes: 14 additions & 14 deletions src/mocks/mockData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ export const PROJECT_DUMMY: Project[] = [
teamId: 1,
name: '캘린더 만들기1',
content: '캘린더 만들기 설명1',
startDate: new Date('2022-01-01'),
endDate: new Date('2022-02-14'),
startDate: new Date('2022-01-01 00:00:00'),
endDate: new Date('2022-02-14 00:00:00'),
createAt: new Date('2022-01-01'),
updateAt: new Date('2022-01-01'),
},
Expand All @@ -39,8 +39,8 @@ export const PROJECT_DUMMY: Project[] = [
teamId: 1,
name: '캘린더 만들기2',
content: '캘린더 만들기 설명2',
startDate: new Date('2022-05-12'),
endDate: new Date('2022-07-31'),
startDate: new Date('2022-05-12 00:00:00'),
endDate: new Date('2022-07-31 00:00:00'),
createAt: new Date('2022-05-12'),
updateAt: new Date('2022-06-01'),
},
Expand All @@ -49,8 +49,8 @@ export const PROJECT_DUMMY: Project[] = [
teamId: 1,
name: '캘린더 만들기3',
content: '캘린더 만들기 설명3',
startDate: new Date('2023-02-05'),
endDate: new Date('2023-06-05'),
startDate: new Date('2023-02-05 00:00:00'),
endDate: new Date('2023-06-05 00:00:00'),
createAt: new Date('2023-02-05'),
updateAt: new Date('2023-02-05'),
},
Expand All @@ -59,8 +59,8 @@ export const PROJECT_DUMMY: Project[] = [
teamId: 1,
name: '캘린더 만들기4',
content: '캘린더 만들기 설명4',
startDate: new Date('2023-04-25'),
endDate: new Date('2023-06-05'),
startDate: new Date('2023-04-25 00:00:00'),
endDate: new Date('2023-06-05 00:00:00'),
createAt: new Date('2023-04-25'),
updateAt: new Date('2023-04-25'),
},
Expand All @@ -69,8 +69,8 @@ export const PROJECT_DUMMY: Project[] = [
teamId: 1,
name: '캘린더 만들기5',
content: '캘린더 만들기 설명5',
startDate: new Date('2023-06-05'),
endDate: new Date('2023-09-12'),
startDate: new Date('2023-06-05 00:00:00'),
endDate: new Date('2023-09-12 00:00:00'),
createAt: new Date('2023-06-05'),
updateAt: new Date('2023-06-05'),
},
Expand Down Expand Up @@ -109,8 +109,8 @@ export const PROJECT_DUMMY: Project[] = [
teamId: 1,
name: '캘린더 만들기9',
content: '캘린더 만들기 설명9',
startDate: new Date('2024-01-01'),
endDate: new Date('2024-12-31'),
startDate: new Date('2024-01-01 00:00:00'),
endDate: new Date('2025-01-01 00:00:00'),
createAt: new Date('2024-01-01'),
updateAt: new Date('2024-07-16'),
},
Expand All @@ -119,8 +119,8 @@ export const PROJECT_DUMMY: Project[] = [
teamId: 1,
name: '캘린더 만들기10',
content: '캘린더 만들기 설명10',
startDate: new Date('2024-05-15'),
endDate: new Date('2024-08-31'),
startDate: new Date('2024-05-15 00:00:00'),
endDate: new Date('2024-09-01 00:00:00'),
createAt: new Date('2024-05-15'),
updateAt: new Date('2025-05-15'),
},
Expand Down
29 changes: 18 additions & 11 deletions src/pages/project/CalendarPage.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { useCallback, useMemo, useState } from 'react';
import { DateTime, Settings } from 'luxon';
import { Calendar, luxonLocalizer, Views } from 'react-big-calendar';
import { Calendar, DayPropGetter, EventPropGetter, luxonLocalizer, Views } from 'react-big-calendar';
import CalendarToolbar from '@components/task/calendar/CalendarToolbar';
import CustomDateHeader from '@components/task/calendar/CustomDateHeader';
import CustomEventWrapper from '@components/task/calendar/CustomEventWrapper';
import UpdateModalTask from '@components/modal/task/UpdateModalTask';
import useModal from '@hooks/useModal';
import useProjectContext from '@hooks/useProjectContext';
import Validator from '@utils/Validator';
import { TASK_DUMMY } from '@mocks/mockData';
import { TaskListWithStatus, TaskWithStatus } from '@/types/TaskType';
import { CustomEvent } from '@/types/CustomEventType';
import 'react-big-calendar/lib/css/react-big-calendar.css';
import UpdateModalTask from '@/components/modal/task/UpdateModalTask';
import '@/customReactBigCalendar.css';

function getCalendarTask(statusTasks: TaskListWithStatus[]) {
const calendarTasks: TaskWithStatus[] = [];
Expand All @@ -29,17 +32,13 @@ Settings.defaultZone = dt.zoneName;
const localizer = luxonLocalizer(DateTime, { firstDayOfWeek: 7 });

export default function CalendarPage() {
const { project } = useProjectContext();
const { showModal, openModal, closeModal } = useModal();
const [selectedTask, setSelectedTask] = useState<TaskWithStatus>();
const [date, setDate] = useState<Date>(() => DateTime.now().toJSDate());

const handleNavigate = useCallback((newDate: Date) => setDate(newDate), [setDate]);

const handleEventClick = (task: TaskWithStatus) => {
setSelectedTask(task);
openModal();
};

const handleSelectEvent = (event: CustomEvent) => {
setSelectedTask(event?.task);
openModal();
Expand All @@ -49,7 +48,16 @@ export default function CalendarPage() {
alert(`시작일: ${start}, 마감일: ${end}`);
}, []);

const handleEventPropGetter = () => ({ style: { padding: '0px', backgroundColor: 'inherit' } });
const handleDayPropGetter: DayPropGetter = (targetDate) => {
if (!project.startDate || !project.endDate) return {};

const isWithinRange = Validator.isWithinDateRange(project.startDate, project.endDate, targetDate);
const bgColor = isWithinRange ? '' : '!bg-[#D9D9D9]';
return { className: bgColor };
};
const handleEventPropGetter: EventPropGetter<CustomEvent> = () => ({
style: { padding: '0px', backgroundColor: 'inherit' },
});

const { views, components: customComponents } = useMemo(
() => ({
Expand All @@ -73,13 +81,11 @@ export default function CalendarPage() {
end: new Date(task.endDate),
allDays: true,
task: { ...task },
handleEventClick,
}))
.sort((a, b) => a.start.getTime() - b.start.getTime()),
};
const startDate = state.events.length ? state.events[0].start : null;

// ToDo: 프로젝트 기간 이외의 영역 처리
// ToDo: DnD, Resize 이벤트 추가 생각해보기
// ToDo: 할일 추가 모달 Form 작업 완료시 모달 컴포넌트 분리
// ToDo: 캘린더 크기 전체적으로 조정
Expand All @@ -102,11 +108,12 @@ export default function CalendarPage() {
endAccessor="end"
allDayAccessor="allDay"
popup
onSelectEvent={handleSelectEvent}
showAllEvents={false}
dayPropGetter={handleDayPropGetter}
eventPropGetter={handleEventPropGetter}
selectable
onSelectSlot={handleSelectSlot}
onSelectEvent={handleSelectEvent}
/>
{showModal && <UpdateModalTask taskId={selectedTask!.taskId} onClose={closeModal} />}
</div>
Expand Down
1 change: 0 additions & 1 deletion src/types/CustomEventType.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,4 @@ import { TaskWithStatus } from '@/types/TaskType';

export type CustomEvent = Event & {
task: TaskWithStatus;
handleEventClick: (task: TaskWithStatus) => void;
};
10 changes: 9 additions & 1 deletion src/utils/Validator.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { DateTime } from 'luxon';

export default class Validator {
public static isEmptyString(value: string) {
return value.trim().length === 0;
}

// ToDo: 정말로 공용 Validation 기능인가 생각해보기
public static isDuplicatedName(nameList: string[], name: string, ignoreCase: boolean = true) {
if (ignoreCase) {
const lowerCaseNameList = nameList.map((n) => n.toLowerCase().trim());
Expand All @@ -12,4 +13,11 @@ export default class Validator {
}
return nameList.includes(name);
}

public static isWithinDateRange(start: Date, end: Date, target: Date) {
const startDate = DateTime.fromJSDate(start);
const endDate = DateTime.fromJSDate(end);
const targetDate = DateTime.fromJSDate(target);
return targetDate >= startDate && targetDate < endDate;
}
}

0 comments on commit 3745476

Please sign in to comment.