Skip to content

Commit

Permalink
Merge pull request #257 from codestates-seb/feat/tag-haeun
Browse files Browse the repository at this point in the history
[구현]Feat/tag기능 구현 완료했습니다.
  • Loading branch information
songhaeunsong authored May 21, 2023
2 parents a3ec793 + 81cedf0 commit ea12067
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 26 deletions.
40 changes: 36 additions & 4 deletions client/src/components/TagDropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,40 @@
const TagDropdown = ({ tags }: { tags: string }) => {
import styled from "styled-components";

const TagDropdown = ({
defaultTags,
tags,
setTags,
}: {
defaultTags: string[];
tags: string[];
setTags: React.Dispatch<React.SetStateAction<string[]>>;
}) => {
const handleTagClick = (item: string) => {
if (tags.includes(item)) {
alert("이미 존재하는 태그입니다.");
} else {
setTags([...tags, item]);
}
};
return (
<>
<li>{tags}</li>
</>
<TagLiDiv>
{defaultTags.map((defaultTag) => {
return (
<li onClick={() => handleTagClick(defaultTag)} key={defaultTag}>
{defaultTag}
</li>
);
})}
</TagLiDiv>
);
};
const TagLiDiv = styled.div`
list-style-type: none;
background-color: white;
border-radius: 5px;
padding: 5px 15px;
cursor: pointer;
margin-top: 5px;
font-size: 0.7rem;
`;
export default TagDropdown;
75 changes: 69 additions & 6 deletions client/src/components/TagInput.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,106 @@
import { useEffect, useState } from "react";
import styled from "styled-components";
import TagDropdown from "./TagDropdown";
import { StudyInfoDto } from "../apis/StudyGroupApi";
import { eduApi } from "../apis/EduApi";

const TagInput = ({ selectedCategory }: { selectedCategory: string }) => {
const TagInput = ({
selectedCategory,
tags,
setTags,
}: {
selectedCategory: string;
tags: string[];
setTags: React.Dispatch<React.SetStateAction<string[]>>;
}) => {
const [view, setView] = useState(false);

const [tag, setTag] = useState<{ [key: string]: string }>({});
const [defaultTag, setDefaultTag] = useState<{ [key: string]: string }>({});
const [createdTag, setCreatedTag] = useState<string>("");

const handleTag = (e: React.ChangeEvent<HTMLInputElement>) => {
setCreatedTag(e.target.value);
};

const handleTagPost = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter") {
e.preventDefault();
if (tags.includes(createdTag)) {
alert("이미 존재하는 태그입니다.");
} else if (createdTag) {
setTags([...tags, createdTag]);
setCreatedTag("");
}
}
};

const handleDelete = (tag: string) => {
const updatedTags = tags.filter((clickedTag) => clickedTag !== tag);
setTags(updatedTags);
};

useEffect(() => {
setView(false);
setTags([]);
setCreatedTag("");
const fetchData = async () => {
try {
const response = await eduApi.get<StudyInfoDto>(
`/search?key=${selectedCategory}`
);
const result = response.data.tags;
setTag(result);
setDefaultTag(result);
} catch (error) {
console.log(error);
}
};

fetchData();
}, []);
}, [selectedCategory]);

return (
<>
<input type="text" /*value={tag} onChange={}*/ />
<input
type="text"
value={createdTag}
onChange={handleTag}
onKeyDown={handleTagPost}
/>

<ul
onClick={() => {
setView(!view);
}}
>
{selectedCategory} {view ? "⌃" : "⌄"}
{view && tag && <TagDropdown tags={tag[selectedCategory]} />}
{view && defaultTag && (
<TagDropdown
defaultTags={[defaultTag[selectedCategory]]}
setTags={setTags}
tags={tags}
/>
)}
</ul>
<div>
{tags.map((tag) => {
return (
<StudyTag onClick={() => handleDelete(tag)} key={tag}>
{tag}
</StudyTag>
);
})}
</div>
</>
);
};
const StudyTag = styled.div`
height: 24px;
color: #39739d;
font-size: 0.8125rem;
border-radius: 4px;
background-color: #e1ecf4;
padding: 2px 6px 5px 6px;
margin-right: 7px;
cursor: pointer;
`;
export default TagInput;
2 changes: 1 addition & 1 deletion client/src/components/gnb/GNB.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ const GNBDiv = styled.div`
`;

const GNBBlock = styled.div`
width: 150px;
width: 200px;
display: flex;
`;
const GNBMenuBlock = styled(GNBBlock)`
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/gnb/User.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const User = ({ profileImage, isLoggedIn, setIsLoggedIn }: GNB) => {
const UserDiv = styled.div`
display: flex;
justify-content: flex-end;
width: 150px;
width: 200px;
button {
margin-left: 7px;
color: #2759a2;
Expand Down
16 changes: 14 additions & 2 deletions client/src/hooks/useRefreshToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ function useRefreshToken() {
setFetched(true);
navigate("/login");
}
if (isLoginState && (refreshToken === null || refreshToken === "undefined")) {
if (
isLoginState &&
(refreshToken === null || refreshToken === "undefined")
) {
removeTokens();
setIsLoggedIn(false);
setFetched(true);
Expand All @@ -37,10 +40,19 @@ function useRefreshToken() {
.then((res) => {
tokenRequestApi.setAccessToken(res.headers.authorization);
setFetched(true);
})
.catch((err) => {
if (err.response.status > 299) {
removeTokens();
setIsLoggedIn(false);
setFetched(true);
navigate("/login");
alert("토큰이 만료되었습니다. 재로그인을 시도해주세요!");
}
});
}
}, []);
return fetched;
}

export default useRefreshToken;
export default useRefreshToken;
14 changes: 5 additions & 9 deletions client/src/pages/SignUp.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import styled from "styled-components";
import { useState } from "react";
import { useNavigate, Link } from "react-router-dom";
import axios from "axios";
import { eduApi } from "../apis/EduApi";
import logo from "../assets/edusync-logo.png";
import GoogleButton from "../components/social-login-button/GoogleButton";
import KakaoButton from "../components/social-login-button/KakaoButton";
Expand Down Expand Up @@ -39,20 +39,16 @@ const SignUp = () => {
alert("닉네임과 이메일, 패스워드를 모두 입력해주세요!");
else if (emailTest(email) === false) alert("이메일 형식이 잘못되었습니다.");
else {
axios
.post(`${import.meta.env.VITE_APP_API_URL}/members`, {
eduApi
.post(`/members`, {
email,
password,
nickName,
})
.then(() => navigate("/login"))
.catch((error) => {
//if (error?.response?.error === "Internal Server Error") {
// console.log(error.response.data.message);
// alert("이미 가입된 이메일 입니다.");
//} else {
console.log(error);
// }
if (error.response.data.message === "이메일이 이미 존재")
alert("이미 가입된 이메일 입니다.");
})
.finally(() => {});
}
Expand Down
18 changes: 15 additions & 3 deletions client/src/pages/StudyPost.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const StudyPost = () => {
const [memberCountMin, setMemberCountMin] = useState<number>(1);
const [memberCountMax, setMemberCountMax] = useState<number>(1);
const [platform, setPlatform] = useState<string>("");
const [tags, setTags] = useState<string[]>([]);
const [introduction, setIntroduction] = useState<string>("");
const [selectedCategory, setSelectedCategory] =
useState<string>("프론트엔드");
Expand Down Expand Up @@ -64,8 +65,7 @@ const StudyPost = () => {
platform,
introduction,
tags: {
백엔드: "javascript",
프론트엔드: "javascript",
[selectedCategory]: tags,
},
};

Expand Down Expand Up @@ -176,7 +176,11 @@ const StudyPost = () => {
</StudyPostInfo>
<StudyPostInfo>
<span>태그</span>
<TagInput selectedCategory={selectedCategory} />
<TagInput
selectedCategory={selectedCategory}
tags={tags}
setTags={setTags}
/>
</StudyPostInfo>
<StudyPostInput>
<TextEditor handleContentChange={setIntroduction} />
Expand Down Expand Up @@ -273,6 +277,14 @@ const StudyPostInfo = styled.form`
p {
padding: 0 10px;
}
ul {
margin: 0 20px;
padding: 7px;
border-radius: 5px;
cursor: pointer;
background-color: #e9e9e9;
font-size: 0.8rem;
}
`;

const StudyPostInput = styled.div`
Expand Down

0 comments on commit ea12067

Please sign in to comment.