221114 자율 프로젝트 개발일지

·2022년 11월 25일
0

개발일지 & 회고

목록 보기
67/72
post-thumbnail

✍ 오늘 한 일

💡 오늘 진행 상황을 간단하게 정리 합니다.

FullCalender 커스텀 이벤트 생성하기

fullCalendar 라이브러리는 기본적인 클릭 이벤트나 체인지 이벤트를 제공해준다. 프로젝트 작업 중 컴포넌트 삭제에 대하여 더블 클릭 이벤트를 만들어야 했는데, 해당 라이브러리가 더블 클릭 이벤트를 제공하지 않아 고민하던 중 커스텀 이벤트를 생성해보았다.

방법은 우선 먼저 eventContent 를 사용한다. eventContent는 해당 달력 내에 내가 만든 컴포넌트를 렌더링하게 해준다. 해당 컴포넌트에서 더블 클릭 이벤트를 넣으면 FullCalendar 이벤트를 실행하는 것이 가능하다.

캘린더 페이지 완성

프로젝트의 메인 기능인 캘린더 페이지를 생성하였다. fullCalendar API를 사용하였고 기존의 할일 등록, 할일 날짜 수정, 할일 기간 연장 이벤트를 우리 프로젝트에 맞게 매핑하였고, 수정 및 삭제 이벤트를 커스텀하여 캘린더 기능을 고도화 하였다. 캘린더 페이지는 다음과 같은 기능을 수행한다. 해당 프로젝트가 솔루션이기 때문에 유저에게 서비스를 좀 더 손쉽게 사용하기 위한 UI 도 제작하였다.

  • 지라 프로젝트에서 이슈를 가져온다.
  • 가져온 이슈를 드래그 앤 드롭을 통해 등록이 가능하다.
  • 등록된 이슈를 드래그 앤 드롭을 통해 날짜를 변경하거나 기간을 연장하는 것이 가능하다.
  • x 버튼을 클릭하여 이슈를 삭제하는 것이 가능하다. 삭제된 이슈는 다시 이슈 목록에 불러와진다.
  • 등록된 이슈를 클릭하는 것으로 이슈를 수정하는 것이 가능하다.
  • 이슈 수정에서는 이슈의 요약, 스토리 포인트, 현재 이슈 상태를 변경하는 것이 가능하다.
  • 이슈 상태가 done 인 경우에는 달력에서 삭제가 되며, 다시 이슈 목록에 불러와지지 않는다.
  • 등록된 이슈는 본인의 색으로 나타난다. 본인의 색임을 쉽게 알 수 있도록 나타내는 UI가 있다.
  • 캘린더의 사용법을 나타내는 설명서가 있다.

import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';

import {
  useDeleteGantt,
  useGetGanttChart,
  useGetTeamForProject,
  usePostCreateGantt,
  useUpdateGantt,
} from 'hooks/project';

import { useGetIssuesNotDone } from 'hooks/issue';

import { StyledCalendar, StyledUserImages } from './style';

import FullCalendar from '@fullcalendar/react';
import dayGridPlugin from '@fullcalendar/daygrid';
import interactionPlugin from '@fullcalendar/interaction';

import Notification from 'components/atoms/Notification';
import Circle from 'components/atoms/Circle';
import CalendarGantt from 'components/molecules/CalendarGantt';
import { relative } from 'path';

interface issueType {
  ganttChartId: number;
  title: string;
  start: string;
  end: string;
  color: string;
  issueCode: string;
  issueSummary: string;
  userId: string;
}

/**
 * @description
 * Calendar API를 렌더링하는 컴포넌트
 * SideBar 지라 이슈들을 가져와 매핑하는 역할을 한다.
 *
 * @author bell
 */
const index = () => {
  const location = useLocation();

  const projectId = +location.pathname.split('/')[2];

  // react-query
  const getGanttChart = useGetGanttChart(1, projectId);
  const postCreateGantt = usePostCreateGantt();
  const updateGantt = useUpdateGantt();
  const deleteGantt = useDeleteGantt();
  const getTeamForProject = useGetTeamForProject(projectId);
  const getIssuesNotDone = useGetIssuesNotDone(projectId);

  const matchColorHandler = (userId: string) => {
    if (getTeamForProject.data) {
      const idx = getTeamForProject.data.findIndex(item => item.userId === +userId);
      if (idx > -1) {
        return getTeamForProject.data[idx].userColor;
      }
    }
    return '#FFFFFF';
  };

  const renderingDBIssuesHandler = () => {
    const arr = new Array<issueType>();
    if (getGanttChart.data) {
      for (const item of getGanttChart.data) {
        arr.push({
          title: item.issueSummary,
          start: item.startTime.split('T')[0],
          end: item.endTime.split('T')[0],
          color: matchColorHandler(item.userId),
          issueSummary: item.issueSummary,
          issueCode: item.issueCode,
          userId: item.userId,
          ganttChartId: item.id,
        });
      }
    }
    return arr;
  };

  useEffect(() => {
    if (postCreateGantt.isSuccess) {
      getGanttChart.refetch();
    }
    if (updateGantt.isSuccess) {
      getGanttChart.refetch();
    }
    if (deleteGantt.isSuccess) {
      getGanttChart.refetch();
      getIssuesNotDone.refetch();
    }
  }, [postCreateGantt.isSuccess, updateGantt.isSuccess, deleteGantt.isSuccess]);

  return (
    <StyledCalendar>
      {postCreateGantt.isSuccess && (
        <Notification
          check={true}
          message={'이슈가 해당 날짜에 성공적으로 저장되었습니다'}
          width={'300px'}
        ></Notification>
      )}
      {updateGantt.isSuccess && (
        <Notification
          check={true}
          message={'해당 이슈의 날짜가 수정되었습니다'}
          width={'300px'}
        ></Notification>
      )}
      {deleteGantt.isSuccess && (
        <Notification
          check={true}
          message={'해당 이슈의 날짜가 삭제되었습니다'}
          width={'300px'}
        ></Notification>
      )}
      <FullCalendar
        plugins={[dayGridPlugin, interactionPlugin]}
        timeZone={'UTC'}
        initialView={'dayGridMonth'}
        selectable={true}
        droppable={true}
        editable={true}
        events={renderingDBIssuesHandler()}
        eventResize={({ event, el }) => {
          // eslint-disable-next-line @typescript-eslint/ban-ts-comment
          // @ts-ignore
          const props = el.fcSeg.eventRange.def.extendedProps;
          // eslint-disable-next-line @typescript-eslint/ban-ts-comment
          // @ts-ignore
          const startDateFormat = new Date(event.start).toISOString() as string;
          // eslint-disable-next-line @typescript-eslint/ban-ts-comment
          // @ts-ignore
          const endDateFormat = new Date(event.end).toISOString() as string;

          updateGantt.mutate({
            id: props.ganttChartId,
            issueCode: props.issueCode,
            issueSummary: props.issueSummary,
            userId: props.userId,
            startTime: startDateFormat,
            endTime: endDateFormat,
          });
        }}
        eventReceive={({ event, draggedEl }) => {
          const startDateFormat = new Date(new Date(event.startStr).getTime()).toISOString();
          const endDateFormat = startDateFormat.split('T')[0] + 'T23:59:59.000Z';

          postCreateGantt.mutate({
            issueCode: draggedEl.dataset.issue_code as string,
            issueSummary: draggedEl.dataset.issue_summary as string,
            projectId: Number(draggedEl.dataset.project_id as string),
            userId: Number(draggedEl.dataset.user_id),
            startTime: startDateFormat,
            endTime: endDateFormat,
          });
          event.remove();
        }}
        eventDrop={({ event, el }) => {
          // eslint-disable-next-line @typescript-eslint/ban-ts-comment
          // @ts-ignore
          const props = el.fcSeg.eventRange.def.extendedProps;
          // eslint-disable-next-line @typescript-eslint/ban-ts-comment
          // @ts-ignore
          const startDateFormat = new Date(event.start).toISOString() as string;
          // eslint-disable-next-line @typescript-eslint/ban-ts-comment
          // @ts-ignore
          const endDateFormat = new Date(event.end).toISOString() as string;
          console.log(startDateFormat, endDateFormat);
          updateGantt.mutate({
            id: props.ganttChartId,
            issueCode: props.issueCode,
            issueSummary: props.issueSummary,
            userId: props.userId,
            startTime: startDateFormat,
            endTime: endDateFormat,
          });
        }}
        eventContent={({ event }) => {
          return (
            <CalendarGantt
              issueCode={event._def.extendedProps.issueCode}
              ganttChartId={event._def.extendedProps.ganttChartId}
              deleteGantt={deleteGantt}
              getGanttChart={getGanttChart}
              projectId={projectId}
            ></CalendarGantt>
          );
        }}
        eventMouseEnter={e => {
          e.el.style.transform = 'scale(1.05)';
          e.el.style.transition = 'transform 0.1s linear';
        }}
        eventMouseLeave={e => (e.el.style.transform = 'scale(1)')}
      />
      <StyledUserImages>
        {getTeamForProject.data &&
          getTeamForProject.data.map(item => (
            <Circle height="40px" isImage={true} url={item.userImage}>
              <div
                style={{
                  width: '40px',
                  height: '6px',
                  backgroundColor: `${item.userColor}`,
                  position: 'relative',
                  top: '30px',
                  borderRadius: '10px',
                }}
              ></div>
            </Circle>
          ))}
      </StyledUserImages>
    </StyledCalendar>
  );
};

export default index;

📢 개선 사항

💡 오늘 하루 개선하면 좋았을 부분을 작성합니다. 없다면 생략하셔도 좋습니다.

📢 내일 진행 예정

💡 내일 할 업무를 팀원들과 함께 공유해봅시다. 글이 자세할수록, 팀원 모두가 업무 흐름을 파악하기 쉬워집니다.
profile
새로운 것에 관심이 많고, 프로젝트 설계 및 최적화를 좋아합니다.

0개의 댓글