Skip to content

Commit

Permalink
Merge pull request #210 from TripInfoWeb/dev_mypage
Browse files Browse the repository at this point in the history
Dev mypage
  • Loading branch information
ssssksss authored Aug 17, 2024
2 parents ab946f6 + d155140 commit 5fc6d7d
Show file tree
Hide file tree
Showing 10 changed files with 226 additions and 79 deletions.
34 changes: 34 additions & 0 deletions src/app/api/mypage/change-nickname/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { NextRequest, NextResponse } from "next/server";

export async function PUT(request: NextRequest) {
const access_cookie = request.cookies.get("access_token");
if (!access_cookie) {
const refresh_cookie = request.cookies.get("refresh_token");
if (!refresh_cookie) {
// 리프레시 토큰이 없으므로 요청 중단
return new NextResponse("Refresh token not found", { status: 403 });
}
// 리프레시 토큰으로 재발급 받아 재요청 보내기 위한 응답
return new NextResponse("Refresh token not found", { status: 401 });
}

const data = await request.json();
try {
const response = await fetch(`${process.env.BACKEND_URL}/api/users/nickname`, {
method: "PUT",
headers: {
Cookie: `${access_cookie?.name}=${access_cookie?.value}`,
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});

if (!response.ok) {
throw new Error(response.statusText);
}

return NextResponse.json({ status: 200, message: "닉네임 변경 성공" });
} catch (err) {
return new Response("Internal Server Error", { status: 500 });
}
}
12 changes: 0 additions & 12 deletions src/app/mypage/layout.tsx

This file was deleted.

29 changes: 27 additions & 2 deletions src/app/mypage/page.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,45 @@
import MyPageHeaderContainer from "@/containers/mypage/MyPageHeaderContainer";
import MyPageMainContainer from "@/containers/mypage/MyPageMainContainer";
import { userResponseDto } from "@/types/UserDto";
import { fetchWithAuth } from "@/utils/fetchWithAuth";
import { Metadata } from "next";
import { cookies } from "next/headers";

export const metadata: Metadata = {
title: "마이페이지",
description: "Solitour 사용자 마이페이지",
};

export default function page() {
async function getUserInfo() {
const cookie = cookies().get("access_token");
const response = await fetchWithAuth(
`${process.env.BACKEND_URL}/api/users/info`,
{
method: "GET",
headers: {
Cookie: `${cookie?.name}=${cookie?.value}`,
},
},
);

if (!response.ok) {
// This will activate the closest 'error.tsx' Error Boundary.
throw new Error(response.statusText);
}

return response.json() as Promise<userResponseDto>;
}


export default async function page() {
const userInfo = await getUserInfo();
return (
<main
className={
"flex min-h-[calc(100vh-25rem)] w-full flex-col items-center px-[5%] pb-[2rem] pt-[2rem] lg:px-[0rem]"
}
>
<MyPageHeaderContainer />
<MyPageHeaderContainer userInfo={userInfo} />
<MyPageMainContainer />
</main>
);
Expand Down
39 changes: 33 additions & 6 deletions src/app/mypage/profile/page.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,47 @@
import MyProfileContainer from "@/containers/mypage/MyProfileContainer";
import { userResponseDto } from "@/types/UserDto";
import { fetchWithAuth } from "@/utils/fetchWithAuth";
import { Metadata } from "next";
import { cookies } from "next/headers";

export const metadata: Metadata = {
title: "마이페이지-프로필 설정",
description: "Solitour 사용자 마이페이지-프로필 설정",
};

export default function page() {
return (
<main
async function getUserInfo() {
const cookie = cookies().get("access_token");
const response = await fetchWithAuth(
`${process.env.BACKEND_URL}/api/users/info`,
{
method: "GET",
headers: {
Cookie: `${cookie?.name}=${cookie?.value}`,
},
},
);

if (!response.ok) {
console.log("page.tsx 파일 : ",response.status);
// This will activate the closest 'error.tsx' Error Boundary.
throw new Error(response.statusText);
}

return response.json() as Promise<userResponseDto>;
}


export default async function page() {
const userInfo = await getUserInfo();

return (
<div
className={
"flex w-full flex-col items-center px-[.5rem] pb-[2rem] pt-[2rem] lg:px-[0rem] min-h-[calc(100vh-25rem)]"
"w-full px-[.5rem] pb-[2rem] pt-[2rem] min-h-[calc(100vh-25rem)]"
}
>
<MyProfileContainer />
</main>
<MyProfileContainer userInfo={userInfo} />
</div>
);
}

2 changes: 1 addition & 1 deletion src/components/common/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ const Header = ({
) : userId > 0 ? (
<>
<Link
href={"/mypage/profile"}
href={"/mypage"}
className={"relative rounded-[50%]"}
>
<Image
Expand Down
19 changes: 12 additions & 7 deletions src/components/mypage/MyPageHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { userResponseDto } from "@/types/UserDto";
import Image from "next/image";
import Link from "next/link";

Expand Down Expand Up @@ -26,27 +27,31 @@ const dummyData: IDummyData = {
user_image: null,
};

const MyPageHeader = () => {
interface IMyPageHeader {
userInfo: userResponseDto;
}

const MyPageHeader = ({userInfo}:IMyPageHeader) => {
return (
<div className={"flex w-full max-w-[60rem] flex-col"}>
<h1 className={"text-3xl font-semibold"}> 마이페이지 </h1>
<div className={"flex items-center justify-center pb-[5rem] pt-[6.5rem]"}>
<article className={"flex flex-col items-center"}>
<div
className={
"relative mb-[1rem] aspect-square w-[6.75rem] cursor-pointer rounded-[3rem] bg-[#F2FAF7] outline outline-[1px] outline-offset-[1px] outline-[#B8EDD9]"
"relative mb-[1rem] aspect-square w-[6.75rem] rounded-[3rem] bg-[#F2FAF7] outline outline-[1px] outline-offset-[1px] outline-[#B8EDD9]"
}
>
{/* ? 유저의 썸네일 이미지가 있는지? */}
{/* ? 썸네일 이미지가 없다면 남자인지 여자인지? => 만약에 성별을 선택안하게 되면 어떻게 해야할지? */}
{dummyData.user_image ? (
{userInfo.userImage?.address ? (
<Image
src={dummyData.user_image}
src={userInfo.userImage?.address}
alt={"user_image"}
width={108}
height={108}
/>
) : dummyData.user_sex == "man" ? (
) : userInfo.sex == "MALE" ? (
<Image
src={"/user_sex_man_default_image.svg"}
alt={"user_image"}
Expand Down Expand Up @@ -78,9 +83,9 @@ const MyPageHeader = () => {
</Link>
</div>
<div className={"text-2xl font-semibold text-[#111]"}>
{dummyData.user_nickname}
{userInfo.nickname}
</div>
<div className={"text-[#666]"}> {dummyData.user_email} </div>
<div className={"text-[#666]"}> {userInfo.email} </div>
</article>
</div>
</div>
Expand Down
109 changes: 68 additions & 41 deletions src/components/mypage/MyProfile.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { userResponseDto } from "@/types/UserDto";
import Image from "next/image";
import Link from "next/link";
import { RefObject } from "react";
Expand All @@ -10,35 +11,19 @@ interface IMyProfileProps {
onDrop: (e: React.DragEvent<HTMLLabelElement>) => void;
imageUrl: string;
onChangeImageUploadInputHandler: (e: any) => void;
}
interface IDummyData {
user_id?: number;
user_status_id?: string;
user_oauth_id?: string;
user_nickname?: string;
user_age?: number | null;
user_sex?: string | null;
user_email?: string | null;
user_phone_number?: string | null;
user_image?: string;
// is_admin: boolean,
userInfo: userResponseDto;
submitChangeNicknameHandler: () => void;
nickname: string;
changeNickname: (value: string) => void;
defaultNickname: string;
message: string;
}

const dummyData: IDummyData = {
user_id: 1,
user_status_id: "1",
user_oauth_id: "1",
user_nickname: "하몽님",
user_age: 20,
user_sex: "woman",
user_email: "[email protected]",
user_phone_number: "010-1234-5678",
// user_image: null,
};
const NICKNAME_LENGTH = 20;

const MyProfile = (props: IMyProfileProps) => {
return (
<div className={"flex w-full max-w-[60rem] flex-col"}>
<div className={"flex w-full flex-col "}>
<div className="flex gap-[.25rem] text-[.625rem] text-gray2">
<div className="text-gray1">
<Link href={"/"}>
Expand Down Expand Up @@ -72,16 +57,14 @@ const MyProfile = (props: IMyProfileProps) => {
onDragOver={props.onDragOver}
onDrop={props.onDrop}
>
{/* ? 유저의 썸네일 이미지가 있는지? */}
{/* ? 썸네일 이미지가 없다면 남자인지 여자인지? => 만약에 성별을 선택안하게 되면 어떻게 해야할지? */}
{props.imageUrl != "/" ? (
{props.userInfo.userImage?.address ? (
<Image
src={props.imageUrl}
src={props.userInfo.userImage?.address}
alt={"user_image"}
width={108}
height={108}
/>
) : dummyData.user_sex == "man" ? (
) : props.userInfo.sex == "MALE" ? (
<Image
src={"/user_sex_man_default_image.svg"}
alt={"user_image"}
Expand Down Expand Up @@ -123,32 +106,76 @@ const MyProfile = (props: IMyProfileProps) => {
<div className={"flex flex-col"}>
<article>
<div className={"flex w-full items-center gap-x-[2.375rem]"}>
<div className={"relative flex-shrink-0"}>
<span className={"w-[3.5rem] text-lg font-semibold"}>닉네임</span>
<div className={"w-[3.5rem] relative flex-shrink-0"}>
<span className={"text-lg font-semibold"}>닉네임</span>
<span className="absolute top-[-.5rem] text-lg text-main">*</span>
</div>

<label className="w-full relative group">
<input
className="flex h-[3.25rem] w-full rounded-[28px] pl-[2rem] pr-[5rem] outline outline-[1px] outline-offset-[-1px] outline-[#E3E3E3]"
type="text"
autoComplete="search"
name="nickname"
placeholder="닉네임을 입력해주세요"
maxLength={NICKNAME_LENGTH}
minLength={1}
defaultValue={props.userInfo.nickname}
onKeyDown={(e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.ctrlKey && e.key === 'Enter') {
props.submitChangeNicknameHandler();
}
}}
onChange={(e)=>props.changeNickname(e.target.value)}
/>
<button
className={`${props.nickname == props.defaultNickname ? "bg-gray-400" : "bg-main"} h-[2.4rem] rounded-[28px] absolute top-[50%] translate-y-[-50%] right-[.5rem] text-white px-3 opacity-0 transition-opacity duration-300 group-hover:opacity-100 group-focus-within:opacity-100 `}
onClick={() => {
props.submitChangeNicknameHandler();
}}
>
변경
</button>
</label>
</div>

<div
className={`pl-[7.75rem] flex w-full ${props.message ? "justify-between" : "justify-end"} pt-[.75rem] text-sm text-gray1`}
>
{
props.message != "" &&
<span className={`${props.message == "성공" ? "text-blue-400" : "text-[#FF0000]" }`}>
{`변경이 ${props.message} 했습니다.`}
</span>
}
<span>
{props.nickname.length}/{NICKNAME_LENGTH}
</span>
</div>
</article>
<article className={"pt-[2.375rem]"}>
<div className={"flex w-full items-center gap-x-[2.375rem]"}>
<div className={"w-[3.5rem] relative flex-shrink-0"}>
<span className={" text-lg font-semibold"}>이메일</span>
</div>
<input
disabled={true}
placeholder="닉네임을 입력해주세요"
placeholder="이메일을 입력해주세요"
className="h-[3.25rem] w-full rounded-[28px] pl-[2rem] outline outline-[1px] outline-offset-[-1px] outline-[#E3E3E3]"
defaultValue={props.userInfo.email}
/>
</div>
<div
className={"flex w-full justify-end pt-[.75rem] text-sm text-gray1"}
>
0/50
</div>
</article>
<article className={"pt-[2.375rem]"}>
<div className={"flex w-full items-center gap-x-[2.375rem]"}>
<div className={"relative flex-shrink-0"}>
<span className={"w-[3.5rem] text-lg font-semibold"}>이메일</span>
<span className="absolute top-[-.5rem] text-lg text-main">*</span>
<div className={"w-[3.5rem] relative flex-shrink-0"}>
<span className={"text-lg font-semibold"}>성별</span>
</div>
<input
disabled={true}
placeholder="이메일을 입력해주세요"
className="h-[3.25rem] w-full rounded-[28px] pl-[2rem] outline outline-[1px] outline-offset-[-1px] outline-[#E3E3E3]"
defaultValue={props.userInfo.sex == "male" ? "남성" : "여성"}
/>
</div>
</article>
Expand All @@ -164,7 +191,7 @@ const MyProfile = (props: IMyProfileProps) => {
<div className={"flex items-center justify-between"}>
<span> 카카오톡 </span>
<div className={"flex items-center gap-x-[.875rem]"}>
<span className={"font-medium text-gray1"}> 2024.06.01 </span>
<span className={"font-medium text-gray1"}> {props.userInfo.userImage.createdDate} </span>
<div className="relative flex h-[2.5rem] w-[2.5rem] items-center justify-center rounded-[18px] bg-[#FEE501]">
<Image
src={"/kakao-icon.svg"}
Expand Down
15 changes: 9 additions & 6 deletions src/containers/mypage/MyPageHeaderContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
"use client";

import MyPageHeader from "@/components/mypage/MyPageHeader";
import { userResponseDto } from "@/types/UserDto";

const MyPageHeaderContainer = () => {
return <MyPageHeader />;
interface IMyPageHeaderContainer {
userInfo: userResponseDto;
}
const MyPageHeaderContainer = (props: IMyPageHeaderContainer) => {
return <>
<MyPageHeader userInfo={props.userInfo} />
</>;
};

export default MyPageHeaderContainer;
export default MyPageHeaderContainer
Loading

0 comments on commit 5fc6d7d

Please sign in to comment.