개인 프로젝트 완료 :push

This commit is contained in:
2024-09-19 12:03:02 +09:00
parent b4b561fdfc
commit 7dd24ed5e4
15 changed files with 454 additions and 18 deletions

View File

@ -258,3 +258,7 @@
.image-box-white-icon {
background-image: url(assets/image/image-box-white-light.png);
}
.arrow-right-icon {
background-image: url(assets/image/arrow-right.png);
}

View File

@ -3,7 +3,12 @@ import { SignInRequestDto, SignUpRequestDto } from "./request/auth";
import { SignInResponseDto } from "./response/auth";
import { ResponseDto } from "./response";
import SignUpResponseDto from "./response/auth/sign-up-response.dto";
import { GetSignInUserResponseDto } from "./response/user";
import {
GetSignInUserResponseDto,
GetUserResponseDto,
PatchNicknameResponseDto,
PatchProfileImageResponseDto,
} from "./response/user";
import {
PatchBoardRequestDto,
PostBoardRequestDto,
@ -22,11 +27,16 @@ import {
GetLatestBoardListResponseDto,
GetTop3BoardListResponseDto,
GetSearchBoardListResponseDto,
GetUserBoardListResponseDto,
} from "./response/board";
import {
GetPoplarListResponseDto,
GetRelationListResponseDto,
} from "./response/search";
import {
PatchNicknameRequestDto,
PatchProfileImageRequestDto,
} from "./request/user";
const DOMAIN = "http://localhost:4000";
@ -83,6 +93,10 @@ const GET_SEARCH_BOARD_LIST_URL = (
`${API_DOMAIN}/board/search-list/${searchWord}${
preSearchWord ? "/" + preSearchWord : ""
}`;
const GET_USER_BOARD_LIST_URL = (email: string) =>
`${API_DOMAIN}/board/user-board-list/${email}`;
const INCREASE_VIEW_COUNT_URL = (boardNumber: number | string) =>
`${API_DOMAIN}/board/${boardNumber}/increase-view-count`;
const GET_FAVORITE_LIST_URL = (boardNumber: number | string) =>
@ -182,6 +196,20 @@ export const getSearchBoardListRequest = async (
return result;
};
export const getUserBoardListRequest = async (email: string) => {
const result = await axios
.get(GET_USER_BOARD_LIST_URL(email))
.then((response) => {
const reponseBody: GetUserBoardListResponseDto = response.data;
return reponseBody;
})
.catch((error) => {
const responseBody: ResponseDto = error.response.data;
return responseBody;
});
return result;
};
export const getFavoriteListRequest = async (boardNumber: number | string) => {
const result = await axios
.get(GET_FAVORITE_LIST_URL(boardNumber))
@ -323,7 +351,25 @@ export const getRelationListRequest = async (searchWord: string) => {
return result;
};
const GET_USER_URL = (email: string) => `${API_DOMAIN}/user/${email}`;
const GET_SIGN_IN_USER_URL = () => `${API_DOMAIN}/user`;
const PATCH_NICKNAME_URL = () => `${API_DOMAIN}/user/nickname`;
const PATCH_PROFILE_IMAGE_URL = () => `${API_DOMAIN}/user/profile-image`;
export const getUserRequest = async (email: string) => {
const result = await axios
.get(GET_USER_URL(email))
.then((response) => {
const responseBody: GetUserResponseDto = response.data;
return responseBody;
})
.catch((error) => {
if (!error.response) return null;
const responseBody: ResponseDto = error.response.data;
return responseBody;
});
return result;
};
export const getSignInUserRequest = async (accessToken: string) => {
const result = await axios
@ -340,6 +386,42 @@ export const getSignInUserRequest = async (accessToken: string) => {
return result;
};
export const patchNicknameRequest = async (
requestBody: PatchNicknameRequestDto,
accessToken: string
) => {
const result = await axios
.patch(PATCH_NICKNAME_URL(), requestBody, authorization(accessToken))
.then((response) => {
const responseBody: PatchNicknameResponseDto = response.data;
return responseBody;
})
.catch((error) => {
if (!error.response) return null;
const responseBody: ResponseDto = error.response.data;
return responseBody;
});
return result;
};
export const patchProfileImageRequest = async (
requestBody: PatchProfileImageRequestDto,
accessToken: string
) => {
const result = await axios
.patch(PATCH_PROFILE_IMAGE_URL(), requestBody, authorization(accessToken))
.then((response) => {
const responseBody: PatchProfileImageResponseDto = response.data;
return responseBody;
})
.catch((error) => {
if (!error.response) return null;
const responseBody: ResponseDto = error.response.data;
return responseBody;
});
return result;
};
const FILE_DOMAIN = `${DOMAIN}/file`;
const FILE_UPLOAD_URL = () => `${FILE_DOMAIN}/upload`;

View File

@ -0,0 +1,4 @@
import PatchNicknameRequestDto from "./patch-nickname.request.dto";
import PatchProfileImageRequestDto from "./patch-profile-image.request.dto";
export type { PatchNicknameRequestDto, PatchProfileImageRequestDto };

View File

@ -0,0 +1,3 @@
export default interface PatchNicknameRequestDto {
nickname: string;
}

View File

@ -0,0 +1,3 @@
export default interface PatchProfileImageRequestDto {
profileImage: string | null;
}

View File

@ -0,0 +1,6 @@
import { BoardListItem } from "types/interface";
import ResponseDto from "../reponse.dto";
export default interface GetUserBoardListResponseDto extends ResponseDto {
userBoardList: BoardListItem[];
}

View File

@ -10,6 +10,7 @@ import PatchBoardResponseDto from "./patch-board.response.dto";
import GetTop3BoardListResponseDto from "./get-top-3-board-list.response.dto";
import GetLatestBoardListResponseDto from "./get-latest-board-list.response.dto";
import GetSearchBoardListResponseDto from "./get-search-board-list.response.dto";
import GetUserBoardListResponseDto from "./get-user-board-list.response.dto";
export type {
PostBoardResponseDto,
GetBoardResponseDto,
@ -23,4 +24,5 @@ export type {
PatchBoardResponseDto,
GetLatestBoardListResponseDto,
GetSearchBoardListResponseDto,
GetUserBoardListResponseDto,
};

View File

@ -0,0 +1,4 @@
import User from "types/interface/user.interface";
import ResponseDto from "../reponse.dto";
export default interface GetUserResponseDto extends ResponseDto, User {}

View File

@ -1,3 +1,10 @@
import GetSignInUserResponseDto from "./get-sign-in-user.response.dto";
export type { GetSignInUserResponseDto };
import GetUserResponseDto from "./get-user.response.dto";
import PatchNicknameResponseDto from "./patch-nickname.response.dto";
import PatchProfileImageResponseDto from "./patch-profile-image.response.dto";
export type {
GetSignInUserResponseDto,
GetUserResponseDto,
PatchNicknameResponseDto,
PatchProfileImageResponseDto,
};

View File

@ -0,0 +1,3 @@
import ResponseDto from "../reponse.dto";
export default interface PatchNicknameResponseDto extends ResponseDto {}

View File

@ -0,0 +1,3 @@
import ResponseDto from "../reponse.dto";
export default interface PatchProfileImageResponseDto extends ResponseDto {}

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 B

View File

@ -1,5 +1,5 @@
export default interface User {
email: string;
nickname: string;
profileimage: string | null;
profileImage: string | null;
}

View File

@ -1,21 +1,56 @@
import React, { ChangeEvent, useEffect, useRef, useState } from "react";
import "./style.css";
import DefaultProfileImage from "assets/image/default-profile-image.png";
import { useParams } from "react-router-dom";
import { useNavigate, useParams } from "react-router-dom";
import { BoardListItem } from "types/interface";
import { latestBoardListMock } from "mocks";
import BoardItem from "components/BoardItem";
import { BOARD_PATH, BOARD_WRITE_PATH, MAIN_PATH, USER_PATH } from "constant";
import { useLoginuserStore } from "stores";
import {
GetUserResponseDto,
PatchNicknameResponseDto,
PatchProfileImageResponseDto,
} from "apis/response/user";
import { ResponseDto } from "apis/response";
import {
fileUploadRequest,
getUserBoardListRequest,
getUserRequest,
patchNicknameRequest,
patchProfileImageRequest,
} from "apis";
import {
PatchNicknameRequestDto,
PatchProfileImageRequestDto,
} from "apis/request/user";
import { useCookies } from "react-cookie";
import { usePagination } from "hooks";
import { GetUserBoardListResponseDto } from "apis/response/board";
import Pagination from "components/Pagination";
// component 유저 화면 //
export default function User() {
// component: 마이페이지 여부 상태 //
// state: userEamil path variable 여부 상태 //
const { userEmail } = useParams();
// state: 로그인 유저 상태 //
const { loginUser } = useLoginuserStore();
// state: 쿠키 상태 //
const [cookies, setCookie] = useCookies();
// state: 마이페이지 여부 상태 //
const [isMyPage, setMyPage] = useState<boolean>(true);
// function: 네비게이트 함수 //
const navigate = useNavigate();
// component 유저 화면 상단 컴포넌트 //
const UserTop = () => {
// state: 이미지 파일 인풋 참조 상태 //
const imageInputRef = useRef<HTMLInputElement | null>(null);
// state: 마이페이지 여부 상태 //
const [isMyPage, setMyPage] = useState<boolean>(true);
// state: 닉네임 변경 여부 상태 //
const [isNicknameChange, setNicknameChange] = useState<boolean>(false);
@ -28,6 +63,70 @@ export default function User() {
// state: 프로필 이미지 상태 //
const [profileImage, setProfileImage] = useState<string | null>(null);
// function: getUserResponse 처리 함수 //
const getUserResponse = (
responseBody: GetUserResponseDto | ResponseDto | null
) => {
if (!responseBody) return;
const { code } = responseBody;
if (code === "NU") alert("존재하지 않는 유저입니다.");
if (code === "DBE") alert("데이터베이스 오류입니다");
if (code !== "SU") {
navigate(MAIN_PATH());
return;
}
const { email, nickname, profileImage } =
responseBody as GetUserResponseDto;
setNickname(nickname);
setProfileImage(profileImage);
const isMyPage = email === loginUser?.email;
setMyPage(isMyPage);
};
// function: fileUploadResponse 처리 함수 //
const fileUploadResponse = (profileImage: string | null) => {
if (!profileImage) return;
if (!cookies.accessToken) return;
const requestBody: PatchProfileImageRequestDto = { profileImage };
patchProfileImageRequest(requestBody, cookies.accessToken).then(
patchProfileImageResponse
);
};
// function: patchProfileImageResponse 처리 함수 //
const patchProfileImageResponse = (
responseBody: PatchProfileImageResponseDto | ResponseDto | null
) => {
if (!responseBody) return;
const { code } = responseBody;
if (code === "DBE") alert("데이터베이스 오류입니다.");
if (code === "AF") alert("인증에 실패했습니다.");
if (code === "NU") alert("존재하지 않는 유저입니다.");
if (code !== "SU") return;
if (!userEmail) return;
getUserRequest(userEmail).then(getUserResponse);
};
// function: patchNicknameResponse 처리 함수 //
const patchNicknameResponse = (
responseBody: PatchNicknameResponseDto | ResponseDto | null
) => {
if (!responseBody) return;
const { code } = responseBody;
if (code === "VF") alert("닉네임은 필수입니다.");
if (code === "AF") alert("인증에 실패했습니다.");
if (code === "NU") alert("존재하지 않는 유저입니다.");
if (code === "DN") alert("중복되는 닉네임입니다.");
if (code === "AF") alert("인증에 실패했습니다.");
if (code === "DBE") alert("데이터베이스 오류입니다.");
if (code !== "SU") return;
if (!userEmail) return;
getUserRequest(userEmail).then(getUserResponse);
setNicknameChange(false);
};
// event handler: 프로필 박스 클릭 이벤트 처리 //
const onProfileBoxClickHandler = () => {
if (!isMyPage) return;
@ -37,8 +136,18 @@ export default function User() {
// event handler: 닉네임 수정 버튼 클릭 이벤트 처리 //
const onNicknameEditButtonClickHandler = () => {
setChangenickname(nickname);
setNicknameChange(!isNicknameChange);
if (!isNicknameChange) {
setChangenickname(nickname);
setNicknameChange(!isNicknameChange);
return;
}
if (!cookies.accessToken) return;
const requestBody: PatchNicknameRequestDto = {
nickname: changeNickname,
};
patchNicknameRequest(requestBody, cookies.accessToken).then(
patchNicknameResponse
);
};
// event handler: 프로필 이미지 변경 이벤트 처리 //
@ -50,6 +159,8 @@ export default function User() {
const file = event.target.files[0];
const data = new FormData();
data.append("file", file);
fileUploadRequest(data).then(fileUploadResponse);
};
// event handler: 닉네임 변경 이벤트 처리 //
@ -61,13 +172,10 @@ export default function User() {
// effect: user email path variable 변경시 실행 할 함수 //
useEffect(() => {
if (!userEmail) return;
setNickname("닉네임");
/* setProfileImage(
"https://d2u3dcdbebyaiu.cloudfront.net/uploads/atch_img/724/2329af2ecf5f9b4dc8846a398dbd8635_res.jpeg"
); */
getUserRequest(userEmail).then(getUserResponse);
}, [userEmail]);
// render 유저 화면 상단 렌더링 //
// render: 유저 화면 상단 렌더링 //
return (
<div id="user-top-wrapper">
<div className="user-top-container">
@ -131,7 +239,7 @@ export default function User() {
<div className="user-top-info-nickname">{nickname}</div>
)}
</div>
<div className="user-top-info-email">{"eogns1@naver.com"}</div>
<div className="user-top-info-email">{userEmail}</div>
</div>
</div>
</div>
@ -140,8 +248,113 @@ export default function User() {
// component 유저 화면 하단 컴포넌트 //
const UserBottom = () => {
// state: 게시물 개수 상태 //
const [count, setCount] = useState<number>(2);
// state: 페이지네이션 관련 상태 //
const {
currentPage /* 현재 페이지가 어떤 위치에 있는지 */,
setCurrentPage,
currentSection,
setCurrentSection,
viewList /* 현재 보여줄 리스트 */,
viewPageList,
totalSection /* 전체 섹션이 몇개인지 */,
setTotalList,
} = usePagination<BoardListItem>(5);
// function: getUserBoardListResponse 처리 함수 //
const getUserBoardListResponse = (
responseBody: GetUserBoardListResponseDto | ResponseDto | null
) => {
if (!responseBody) return;
const { code } = responseBody;
if (code === "NU") {
alert("존재하지 않는 유저입니다.");
navigate(MAIN_PATH());
}
if (code === "DBE") alert("데이터베이스 오류입니다.");
if (code !== "SU") return;
const { userBoardList } = responseBody as GetUserBoardListResponseDto;
setTotalList(userBoardList);
setCount(userBoardList.length);
};
// event handler: 사이드 카드 클릭 이벤트 처리 //
const onSideCardClickHandler = () => {
if (isMyPage) navigate(BOARD_PATH() + "/" + BOARD_WRITE_PATH());
else if (loginUser) navigate(USER_PATH(loginUser.email));
};
// effect: userEmail path vaiable이 변경될 때마다 실행될 함수 //
useEffect(() => {
if (!userEmail) return;
getUserBoardListRequest(userEmail).then(getUserBoardListResponse);
}, [userEmail]);
// render 유저 화면 하단 렌더링 //
return <div></div>;
return (
<div id="user-bottom-wrapper">
<div className="user-bottom-container">
<div className="user-bottom-title">
{isMyPage ? "내 게시물 " : "게시물 "}
<span className="emphasis">{count}</span>
</div>
<div className="user-bottom-contents-box">
{count === 0 ? (
<div className="user-bottom-contents-noting">
{"게시물이 없습니다."}
</div>
) : (
<div className="user-bottom-contents">
{viewList.map((boardListItem) => (
<BoardItem boardListItem={boardListItem} />
))}
</div>
)}
<div className="user-botton-side-box">
<div
className="user-bottom-side-card"
onClick={onSideCardClickHandler}
>
<div className="user-bottom-side-container">
{isMyPage ? (
<>
<div className="icon-box">
<div className="icon edit-icon"></div>
</div>
<div className="user-bottom-side-text">{"글쓰기"}</div>
</>
) : (
<>
<div className="user-bottom-side-text">
{"내 게시물로 가기"}
</div>
<div className="icon-box">
<div className="icon arrow-right-icon"></div>
</div>
</>
)}
</div>
</div>
</div>
</div>
<div className="user-bottom-pagination-box">
{count !== 0 && (
<Pagination
currentPage={currentPage}
currentSection={currentSection}
setCurrentPage={setCurrentPage}
setCurrentSection={setCurrentSection}
viewPageList={viewPageList}
totalSection={totalSection}
/>
)}
</div>
</div>
</div>
);
};
// render 유저 화면 렌더링 //

View File

@ -97,3 +97,105 @@
background-color: rgba(0, 0, 0, 0.05);
}
#user-bottom-wrapper {
padding: 40px 0;
min-height: 880px;
display: flex;
justify-content: center;
background-color: rgba(0, 0, 0, 0.05);
}
.user-bottom-container {
width: 1200px;
min-width: 1200px;
display: flex;
flex-direction: column;
gap: 20px;
}
.user-bottom-title {
color: rgba(0, 0, 0, 1);
font-size: 24px;
font-weight: 500;
line-height: 140%;
}
.user-bottom-contents-box {
width: 100%;
display: grid;
grid-template-columns: 8fr 4fr;
gap: 24px;
}
.user-bottom-contents {
grid-column: 1 / 2;
display: flex;
flex-direction: column;
gap: 16px;
}
.user-bottom-contents-noting {
width: 100%;
height: 76px;
display: flex;
justify-content: center;
align-items: center;
color: rgba(0, 0, 0, 0.4);
font-size: 24px;
font-weight: 500;
line-height: 140%;
}
.user-botton-side-box {
grid-column: 2/3;
}
.user-bottom-side-card {
width: 100%;
height: 76px;
display: flex;
justify-content: center;
align-items: center;
background-color: rgba(255, 255, 255, 1);
cursor: pointer;
}
.user-bottom-side-card:hover {
background-color: rgba(255, 255, 255, 0.7);
}
.user-bottom-side-container {
display: flex;
align-items: center;
gap: 6px;
}
.user-bottom-side-text {
color: rgba(0, 0, 0, 1);
font-size: 18px;
font-weight: 500;
line-height: 140%;
}
.user-bottom-pagination-box {
margin-top: 60px;
display: flex;
justify-content: center;
}