Skip to content

fix: 백로그 페이지 기능 수정 #331

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Sep 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions frontend/src/components/backlog/EpicBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,20 @@ interface EpicBlockProps {
epic: EpicCategoryDTO;
showStoryList: boolean;
onShowStoryList: () => void;
lastRankValue: string;
}

const EpicBlock = ({
storyExist,
epic,
showStoryList,
onShowStoryList,
lastRankValue,
}: EpicBlockProps) => {
const {
open: epicUpdating,
handleOpen: handleEpicUpdateOpen,
handleClose: handleEpicUpdateClose,
dropdownRef: epicRef,
} = useDropdownState();

Expand Down Expand Up @@ -66,6 +69,8 @@ const EpicBlock = ({
selectedEpic={epic}
epicList={[epic]}
onEpicChange={() => {}}
lastRankValue={lastRankValue}
onCloseDropdown={handleEpicUpdateClose}
/>
)}
</div>
Expand Down
18 changes: 18 additions & 0 deletions frontend/src/components/backlog/EpicCreateButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Plus from "../../assets/icons/plus.svg?react";

interface EpicCreateButtonProps {
onClick: () => void;
}

const EpicCreateButton = ({ onClick }: EpicCreateButtonProps) => (
<button
type="button"
className="flex items-center justify-center w-full gap-2 py-1 rounded-md bg-middle-green"
onClick={onClick}
>
<Plus width={24} height={24} stroke="white" />
<span className="text-white">Epic 생성하기</span>
</button>
);

export default EpicCreateButton;
72 changes: 72 additions & 0 deletions frontend/src/components/backlog/EpicCreateForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { ChangeEvent, useRef, useState } from "react";
import { useOutletContext } from "react-router-dom";
import { Socket } from "socket.io-client";
import { LexoRank } from "lexorank";
import useEpicEmitEvent from "../../hooks/pages/backlog/useEpicEmitEvent";
import { CATEGORY_COLOR } from "../../constants/backlog";
import getNewColor from "../../utils/getNewColor";
import Check from "../../assets/icons/check.svg?react";
import Closed from "../../assets/icons/closed.svg?react";

interface EpicCreateFormProps {
onCloseClick: () => void;
}

const EpicCreateForm = ({ onCloseClick }: EpicCreateFormProps) => {
const [value, setValue] = useState("");
const [epicColor] = useState(getNewColor(Object.keys(CATEGORY_COLOR)));
const { socket }: { socket: Socket } = useOutletContext();
const { emitEpicCreateEvent } = useEpicEmitEvent(socket);

const inputElementRef = useRef<HTMLInputElement | null>(null);

const handleInputChange = ({ target }: ChangeEvent<HTMLInputElement>) => {
const { value } = target;

setValue(value);
};

const handleSubmit = () => {
if (value.length > 10) {
alert("에픽 이름은 10자 이하여야 합니다.");
return;
}

emitEpicCreateEvent({
name: value,
color: epicColor,
rankValue: LexoRank.middle().toString(),
});
setValue("");
onCloseClick();
};

return (
<form className="flex items-center w-full py-1" onSubmit={handleSubmit}>
<input
className="w-full mr-3 bg-gray-200 rounded-sm focus:outline-none hover:cursor-pointer"
type="text"
ref={inputElementRef}
onChange={handleInputChange}
/>
<div className="flex items-center gap-2">
<button
className="flex items-center justify-center w-6 h-6 rounded-md bg-confirm-green"
type="button"
onClick={handleSubmit}
>
<Check width={20} height={20} stroke="white" />
</button>
<button
className="flex items-center justify-center w-6 h-6 rounded-md bg-error-red"
type="button"
onClick={onCloseClick}
>
<Closed stroke="white" />
</button>
</div>
</form>
);
};

export default EpicCreateForm;
50 changes: 34 additions & 16 deletions frontend/src/components/backlog/EpicDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@ interface EpicDropdownProps {
selectedEpic?: EpicCategoryDTO;
epicList: EpicCategoryDTO[];
onEpicChange: (epicId: number | undefined) => void;
lastRankValue?: string;
onCloseDropdown: () => void;
}

const EpicDropdown = ({
selectedEpic,
epicList,
onEpicChange,
lastRankValue,
onCloseDropdown,
}: EpicDropdownProps) => {
const { socket }: { socket: Socket } = useOutletContext();
const { emitEpicCreateEvent } = useEpicEmitEvent(socket);
Expand All @@ -39,26 +43,29 @@ const EpicDropdown = ({
setValue(value);
};

const handleCreateButtonClick = () => {
if (value.length > 10) {
alert("에픽 이름은 10자 이하여야 합니다.");
return;
}

const rankValue =
lastRankValue !== undefined
? LexoRank.parse(lastRankValue).genNext().toString()
: LexoRank.middle().toString();

emitEpicCreateEvent({ name: value, color: epicColor, rankValue });
setValue("");
setEpicColor(getNewColor(Object.keys(CATEGORY_COLOR)));
};

const handleEnterKeydown = (event: React.KeyboardEvent) => {
if (event.nativeEvent.isComposing) {
return;
}

if (event.key === "Enter" && value) {
if (value.length > 10) {
alert("에픽 이름은 10자 이하여야 합니다.");
return;
}

const rankValue = epicList.length
? LexoRank.parse(epicList[epicList.length - 1].rankValue)
.genNext()
.toString()
: LexoRank.middle().toString();

emitEpicCreateEvent({ name: value, color: epicColor, rankValue });
setValue("");
setEpicColor(getNewColor(Object.keys(CATEGORY_COLOR)));
handleCreateButtonClick();
}
};

Expand All @@ -76,12 +83,20 @@ const EpicDropdown = ({
}
};

const handleESCClick = (event: KeyboardEvent) => {
if (event.key === "Escape") {
onCloseDropdown();
}
};

useEffect(() => {
socket.on("backlog", handleEpicEvent);
inputElementRef.current?.focus();
window.addEventListener("keydown", handleESCClick);

return () => {
socket.off("backlog", handleEpicEvent);
window.removeEventListener("keydown", handleESCClick);
};
}, []);

Expand All @@ -107,10 +122,13 @@ const EpicDropdown = ({
/>
</div>
{value ? (
<div className="flex items-center gap-2 p-1">
<button
className="flex items-center gap-2 p-1"
onClick={handleCreateButtonClick}
>
<span>생성</span>
<CategoryChip content={value} bgColor={epicColor} />
</div>
</button>
) : (
<ul className="max-h-[16rem] overflow-y-auto scrollbar-thin pt-1">
{...epicList.map((epic) => (
Expand Down
11 changes: 8 additions & 3 deletions frontend/src/components/backlog/StoryBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ const StoryBlock = ({
const {
open: deleteMenuOpen,
handleOpen: handleDeleteMenuOpen,

dropdownRef: blockRef,
} = useDropdownState();
const { emitStoryUpdateEvent, emitStoryDeleteEvent } =
Expand Down Expand Up @@ -180,6 +179,12 @@ const StoryBlock = ({
selectedEpic={epic}
epicList={epicList}
onEpicChange={updateEpic}
lastRankValue={
epicList.length
? epicList[epicList.length - 1].rankValue
: undefined
}
onCloseDropdown={handleEpicUpdateClose}
/>
)}
</div>
Expand Down Expand Up @@ -220,7 +225,7 @@ const StoryBlock = ({
/>
</div>
<div
className="flex items-center gap-1 w-[4rem] mr-[2.76rem] text-right hover:cursor-pointer"
className="flex items-center gap-1 w-[4.5rem] mr-[2.76rem] text-right hover:cursor-pointer"
onClick={() => handlePointUpdatingOpen(true)}
ref={pointRef}
>
Expand Down Expand Up @@ -248,7 +253,7 @@ const StoryBlock = ({
</div>
</div>
{deleteMenuOpen && (
<div className="absolute px-2 py-1 bg-white rounded-md shadow-box">
<div className="absolute z-10 px-2 py-1 bg-white rounded-md shadow-box">
<button
className="flex items-center w-full gap-3"
type="button"
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/components/backlog/StoryCreateForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,9 @@ const StoryCreateForm = ({
>
{!epic ? (
<div
className="w-[5rem] min-h-[1.75rem] bg-light-gray rounded-md mr-7 hover:cursor-pointer relative"
className={`w-[5rem] min-h-[1.75rem] rounded-md mr-7 hover:cursor-pointer relative ${
epicId ? "" : "bg-light-gray"
}`}
onClick={handleEpicColumnClick}
ref={dropdownRef}
>
Expand All @@ -136,6 +138,7 @@ const StoryCreateForm = ({
selectedEpic={selectedEpic}
epicList={epicList}
onEpicChange={handleEpicChange}
onCloseDropdown={handleClose}
/>
)}
</div>
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/components/backlog/TaskBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ const TaskBlock = ({
)}
</div>
<div
className="w-16 min-h-[1.5rem] hover:cursor-pointer"
className="w-16 min-h-[1.5rem] hover:cursor-pointer text-right"
ref={expectedTimeRef}
onClick={() => handleExpectedTimeUpdating(true)}
>
Expand All @@ -240,7 +240,7 @@ const TaskBlock = ({
/>
</div>
<div
className="w-16 min-h-[1.5rem] hover:cursor-pointer"
className="w-16 min-h-[1.5rem] hover:cursor-pointer text-right"
ref={actualTimeRef}
onClick={() => handleActualTimeUpdating(true)}
>
Expand All @@ -264,7 +264,7 @@ const TaskBlock = ({
</div>
</div>
{deleteMenuOpen && (
<div className="absolute px-2 py-1 bg-white rounded-md shadow-box">
<div className="absolute z-10 px-2 py-1 bg-white rounded-md shadow-box">
<button
className="flex items-center w-full gap-3"
type="button"
Expand Down
Loading
Loading