1. 헤더 UI 구현
function Header() {
const { isClick, setIsClick } = useContext(HomeContextProvider);
const aespa = ["카리나", "지젤", "윈터", "닝닝"];
const MemberClickEvent = (name) => {
setIsClick(name);
};
return (
<HeaderBackground>
<HeaderTitle>에스파 팬레터 콜렉션</HeaderTitle>
<HeaderList>
{aespa.map((item) => (
<HeaderItem
key={item}
onClick={() => MemberClickEvent(item)}
$isClick={item === isClick}
>
{item}
</HeaderItem>
))}
</HeaderList>
</HeaderBackground>
);
}
export default Header;
2. 리스트 UI 구현
function List() {
const { data } = useContext(LatterContextProvider);
const { isClick } = useContext(HomeContextProvider);
const FilterData = data.filter((data) =>
data.writedTo === isClick ? true : false
);
const DataBoolean = FilterData.length !== 0 ? true : false;
return (
<FooterBackground>
<FooterList>
{DataBoolean ? (
FilterData.map((data) => (
<ListItem data={data} key={data.id}></ListItem>
))
) : (
<DetailMemberName>
{isClick
? `${isClick}의 첫 번째 팬레터의 주인공이 되세요!`
: "멤버를 선택해야 팬레터를 볼 수 있습니다."}
</DetailMemberName>
)}
</FooterList>
</FooterBackground>
);
}
export default List;
function ListItem({ data }) {
const navigate = useNavigate();
return (
<FooterItem
onClick={() => {
navigate(`/detail/${data.id}`);
}}
>
<FooterItemFigure>
<FooterItemImage src={data.avatar}></FooterItemImage>
</FooterItemFigure>
<FooterItemTitle>
<ListTitle>{data.nickname}</ListTitle>
<ListDate>{data.createdAt}</ListDate>
</FooterItemTitle>
<FooterItemContent>{data.content}</FooterItemContent>
</FooterItem>
);
}
export default ListItem;
3. 입력 기능 구현
function Form() {
const [nickname, setNickname] = useState("");
const [content, setContent] = useState("");
const [writedTo, setWritedTo] = useState("");
const { setIsClick } = useContext(HomeContextProvider);
const { setData } = useContext(LatterContextProvider);
const date = new Date().toLocaleDateString("ko-KR", {
year: "numeric",
month: "long",
day: "numeric",
hour: "numeric",
minute: "numeric",
});
const nicknameOnChangeHandler = (e) => setNickname(e.target.value);
const contentOnChangeHandler = (e) => setContent(e.target.value);
const writedToOnChangeHandler = (e) => setWritedTo(e.target.value);
const submitEventHandler = (e) => {
e.preventDefault();
if (nickname.trim() === "") {
alert("닉네임을 입력해주세요");
setNickname("");
} else if (content.trim() === "") {
alert("내용을 입력해주세요");
setContent("");
} else if (writedTo === "") {
alert("멤버를 선택해주세요");
setWritedTo("");
} else {
DataAdd();
setNickname("");
setContent("");
setWritedTo("");
setIsClick(writedTo);
}
};
const DataAdd = () => {
setData((prev) => {
return [
{
createdAt: date,
nickname,
avatar: profile,
content,
writedTo,
id: prev.length + "1",
},
...prev,
];
});
};
return (
<BodyForm onSubmit={submitEventHandler}>
<BodyBox>
<BodyLabel>닉네임:</BodyLabel>
<BodyInputName
value={nickname}
onChange={nicknameOnChangeHandler}
placeholder="최대 10글자까지 작성할 수 있습니다."
maxLength={10}
></BodyInputName>
</BodyBox>
<BodyBox>
<BodyLabel>내용:</BodyLabel>
<BodyTextArea
value={content}
onChange={contentOnChangeHandler}
placeholder="최대 100글자까지만 작성할 수 있습니다."
maxLength={100}
></BodyTextArea>
</BodyBox>
<BodyBox>
<BodyLabel>멤버 선택</BodyLabel>
<select value={writedTo} onChange={writedToOnChangeHandler}>
<option value={""}>멤버</option>
<option value={"카리나"}>카리나</option>
<option value={"지젤"}>지젤</option>
<option value={"윈터"}>윈터</option>
<option value={"닝닝"}>닝닝</option>
</select>
</BodyBox>
<BodyRegister>
<BodyRegisterButton type="submit">펜레터 등록</BodyRegisterButton>
</BodyRegister>
</BodyForm>
);
}
export default Form;
4. 상세화면 UI 구현 및 수정, 삭제 기능
function DetailList() {
const { id } = useParams();
const navigate = useNavigate();
const { data, setData } = useContext(LatterContextProvider);
const [update, setUpdate] = useState(false);
const [inputValue, setInputValue] = useState(detailData.content);
const detailData = data.find((item) => item.id === id);
const onContentChange = (e) => setInputValue(e.target.value);
const UpdateOnClickEventHandler = () =>
update ? textAreaButtonHandler() : setUpdate(!update);
const textAreaButtonHandler = () => {
if (window.confirm("이대로 수정하시겠습니까?")) {
if (detailData.content === inputValue) {
alert("변경된 내용이 없습니다.");
} else {
setData((prev) => {
const filterPrev = prev.filter((item) =>
item.id !== id ? true : false
);
detailData.content = inputValue;
return (
[detailData, ...filterPrev]
);
});
setUpdate(!update);
alert("수정되었습니다.");
navigate("/");
}
}
};
const CancelOnClickEventHandler = () => setUpdate(!update);
const DeleteOnClickEventHandler = () => {
if (window.confirm("정말 삭제하시겠습니까?")) {
setData((prev) => prev.filter((item) => item.id !== id));
alert("정상적으로 삭제되었습니다.");
navigate("/");
} else {
alert("취소되었습니다.");
}
};
return (
<DetailBackground>
<DetailHomeButton onClick={() => navigate("/")}>Home</DetailHomeButton>
<DetailItemBox>
<DetailHeader>
<FooterItemFigure>
<FooterItemImage src={detailData.avatar}></FooterItemImage>
</FooterItemFigure>
<DetailMemberName>{detailData.nickname}</DetailMemberName>
</DetailHeader>
<ListDate>{detailData.createdAt}</ListDate>
<DetailMemberName>To: {detailData.writedTo}</DetailMemberName>
{update ? (
<DetailTextArea
maxLength={200}
value={inputValue}
onChange={onContentChange}
></DetailTextArea>
) : (
<DetailContent>{detailData.content}</DetailContent>
)}
<DetailButtonSection>
<DetailButtonDiv>
<DetailButton onClick={UpdateOnClickEventHandler}>
수정
</DetailButton>
</DetailButtonDiv>
<DetailButtonDiv>
{update ? (
<DetailButton onClick={CancelOnClickEventHandler}>
취소
</DetailButton>
) : (
<DetailButton onClick={DeleteOnClickEventHandler}>
삭제
</DetailButton>
)}
</DetailButtonDiv>
</DetailButtonSection>
</DetailItemBox>
</DetailBackground>
);
}
export default DetailList;