컴포넌트 생성
Modal Edit 생성
task를 클릭하면 modalActive가 true로 설정되면서 모달창이 뜨게 된다. 이 모달창이 EditModal 컴포넌트이다.
function App() {
...
return (
<div className={appContainer}>
...
{modalActive ? <EditModal /> : null}
...
</div>
)
}
store에 있는 모달의 정보를 가져오고, 이 정보를 새로운 state의 초기값으로 저장한다.
모달 창의 정보가 변경되면 새로 만든 이 state(data)에도 변화가 일어난다.
일 수정하기 버튼을 누르면 store에 저장되어 있는 task를 업데이트하고, 로그를 추가해 주고, 모달 활성화 여부를 false로 변경하여 모달창을 닫는다.
일 삭제하기 버튼을 누르면 현재 활성화되어 있는 게시판의 해당 list의 해당 task를 삭제해야 한다.
const EditModal = () => {
const dispatch = useTypedDispatch();
const editingState = useTypedSelector(state => state.modal);
const [data, setData] = useState(editingState);
const handleCloseButton = () => {
dispatch(setModalActive(false));
}
const handleNameChange = (e: ChangeEvent<HTMLInputElement>) => {
setData({
...data,
task: {
...data.task,
taskName: e.target.value
}
})
}
const handleDescriptionChange = (e: ChangeEvent<HTMLInputElement>) => {
setData({
...data,
task: {
...data.task,
taskDescription: e.target.value
}
})
}
const handleAuthorChange = (e: ChangeEvent<HTMLInputElement>) => {
setData({
...data,
task: {
...data.task,
taskOwner: e.target.value
}
})
}
const handleUpdate = () => {
dispatch(
updateTask({
boardId: editingState.boardId,
listId: editingState.listId,
task: data.task
})
);
dispatch(
addLog({
logId: v4(),
logMessage: `일 수정하기: ${editingState.task.taskName}`,
logAuthor: 'User',
logTimestamp: String(Date.now())
})
);
dispatch(
setModalActive(false)
);
}
const handleDelete = () => {
dispatch(
deleteTask({
boardId: editingState.boardId,
listId: editingState.listId,
taskId: editingState.task.taskId
})
);
dispatch(
addLog({
logId: v4(),
logMessage: `일 삭제하기: ${editingState.task.taskName}`,
logAuthor: 'User',
logTimestamp: String(Date.now())
})
);
dispatch(setModalActive(false));
}
return (
<div className={wrapper}>
<div className={modalWindow}>
<div className={header}>
<div className={title}>{editingState.task.taskName}</div>
<FiX className={closeButton} onClick={handleCloseButton} />
</div>
<div className={title}>제목</div>
<input
className={input}
type="text"
value={data.task.taskName}
onChange={(e) => handleNameChange(e)}
/>
<div className={title}>설명</div>
<input
className={input}
type="text"
value={data.task.taskDescription}
onChange={(e) => handleDescriptionChange(e)}
/>
<div className={title}>생성한 사람</div>
<input
className={input}
type="text"
value={data.task.taskOwner}
onChange={(e) => handleAuthorChange(e)}
/>
<div className={buttons}>
<button className={updateButton} onClick={handleUpdate}>
일 수정하기
</button>
<button className={deleteButton} onClick={handleDelete}>
일 삭제하기
</button>
</div>
</div>
</div>
)
}
task 업데이트를 위해 boardSlice의 reducer에서 아래의 updateTask 함수와 task 삭제를 위한 deleteTask 함수를 정의한다.
deleteTask: (state, { payload }: PayloadAction<TDeleteTaskAction>) => {
state.boardArray = state.boardArray.map(board =>
board.boardId === payload.boardId
?
{
...board,
lists: board.lists.map(list =>
list.listId === payload.listId
? {
...list,
tasks: list.tasks.filter(task =>
task.taskId !== payload.taskId
)
}
:
list
)
}
:
board
)
},
updateTask: (state, { payload }: PayloadAction<TAddTaskAction>) => {
state.boardArray = state.boardArray.map(board =>
board.boardId === payload.boardId
?
{
...board,
lists: board.lists.map(list =>
list.listId === payload.listId
?
{
...list,
tasks: list.tasks.map(task =>
task.taskId === payload.task.taskId
? payload.task
: task
)
}
:
list
)
}
:
board
)
}
LoggerModal 생성
store에 저장되어 있는 logArray의 요소들을 LogItem 컴포넌트에 넣어서 모두 화면에 표시한다.
type TLoggerModalProps = {
setIsLoggerOpen: React.Dispatch<React.SetStateAction<boolean>>;
}
const LoggerModal: FC<TLoggerModalProps> = ({
setIsLoggerOpen
}) => {
const logs = useTypedSelector(state => state.logger.logArray);
return (
<div className={wrapper}>
<div className={modalWindow}>
<div className={header}>
<div className={title}>활동 기록</div>
<FiX className={closeButton} onClick={() => setIsLoggerOpen(false)} />
</div>
<div className={body}>
{logs.map((log) => (
<LogItem key={log.logId} logItem={log} />
))}
</div>
</div>
</div>
)
}
LogItem 생성
LogItem은 로그 생성자, 로그 메시지, 로그 작성 경과 시간을 보여준다.
function App() {
...
const [isLoggerOpen, setIsLoggerOpen] = useState(false);
...
return (
<div className={appContainer}>
{isLoggerOpen ? <LoggerModal setIsLoggerOpen={setIsLoggerOpen} /> : null}
...
)
}
type TLogItemProps = {
logItem: ILogItem;
}
const LogItem: FC<TLogItemProps> = ({
logItem
}) => {
const timeOffset = new Date(Date.now() - Number(logItem.logTimestamp));
const showOffsetTime = `
${timeOffset.getMinutes() > 0 ? `${timeOffset.getMinutes()}m` : ''}
${timeOffset.getSeconds() > 0 ? `${timeOffset.getSeconds()}s ago` : ''}
${timeOffset.getSeconds() === 0 ? `just now` : ''}
`;
return (
<div className={logItemWrap}>
<div className={author}>
<BsFillPersonFill />
{logItem.logAuthor}
</div>
<div className={message}>
{logItem.logMessage}
</div>
<div className={date}>
{showOffsetTime}
</div>
</div>
)
}
게시판 삭제 기능 생성
게시판은 무조건 1개 이상 존재해야 한다. 따라서 게시판이 1개일 경우 삭제하기 버튼을 누르면 alert 창을 띄운다.
게시판이 2개 이상일 경우 해당 게시판을 삭제하고, 로그를 남기고, 다른 게시판이 보이도록 활성화된 게시판 id를 변경한다. 삭제할 게시판이 0번째 인덱스인 경우에는 다음 게시판 id를 activeBoardId로 설정하고, 삭제할 게시판이 1 이상인 경우에는 0번째 인덱스에 있는 게시판 id를 activeBoardId로 설정한다.
deleteBoard: (state, { payload }: PayloadAction<TDeleteBoardAction>) => {
state.boardArray = state.boardArray.filter(board => board.boardId !== payload.boardId);
}
function App() {
const dispatch = useTypedDispatch();
...
const [activeBoardId, setActiveBoardId] = useState('board-0');
const modalActive = useTypedSelector(state => state.boards.modalActive);
const boards = useTypedSelector(state => state.boards.boardArray);
const getActiveBoard = boards.filter(board => board.boardId === activeBoardId)[0];
...
const handleDeleteBoard = () => {
if (boards.length > 1) {
dispatch(
deleteBoard({ boardId: getActiveBoard.boardId })
);
dispatch(
addLog({
logId: v4(),
logMessage: `게시판 지우기: ${getActiveBoard.boardId}`,
logAuthor: 'User',
logTimestamp: String(Date.now())
})
);
const newIndexToSet = () => {
const indexToBeDeleted = boards.findIndex(board => board.boardId === activeBoardId)
return indexToBeDeleted === 0 ? indexToBeDeleted + 1 : indexToBeDeleted - 1;
}
setActiveBoardId(boards[newIndexToSet()].boardId);
} else {
alert('최소 게시판 개수는 한 개입니다.')
}
}
return (
<div className={appContainer}>
...
<div className={buttons}>
<button className={deleteBoardButton} onClick={handleDeleteBoard}>
이 게시판 삭제하기
</button>
<button className={loggerButton} onClick={() => setIsLoggerOpen(!isLoggerOpen)}>
{isLoggerOpen ? '활동 목록 숨기기' : '활동 목록 보이기'}
</button>
</div>
</div>
)
}
정리
- task를 클릭하면 모달창이 뜨도록 EditModal 컴포넌트를 생성했다.
- task 수정과 삭제를 위한 rudecer 함수 updateTask와 deleteTask를 정의했다.
- LoggerModal에서는 store에 저장되어 있는 logArray의 요소들을 LogItem 컴포넌트에 넣어서 모두 화면에 표시한다
- LogItem은 로그 생성자, 로그 메시지, 로그 작성 경과 시간을 보여준다.
- 게시판을 삭제하는 reducer 함수 deleteBoard를 정의했다.
- 게시판은 한 개 이상 존재해야 하며, 삭제할 게시판의 인덱스를 이용하여 삭제한 후에 보여질 게시판의 id를 현재 활성화된 게시판의 id로 설정한다.
'데브코스' 카테고리의 다른 글
[13주차 - DAY2] 오픈 소스 (0) | 2024.05.21 |
---|---|
[13주차 - DAY1] 게시판 만들기(4) & 배포 (0) | 2024.05.20 |
[12주차 - DAY4] 게시판 만들기(2) (0) | 2024.05.16 |
[12주차 주간 발표] 인덱스 (0) | 2024.05.15 |
[12주차 - DAY2] 게시판 만들기(1) (0) | 2024.05.14 |