Skip to content
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

유저 유형 타입 별 축제 추천 컴포넌트 구현 #43

Merged
merged 16 commits into from
Sep 3, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
4cc818d
🔨 settings: swiper 설치
froggy1014 Aug 31, 2024
fcdd2b7
✨ feat: 유저타입에 따른 동적 클래스네임, 이미지를 위한 객체 생성 #34
froggy1014 Aug 31, 2024
6f483bd
✨ feat: 추천 축제 api 타입 선언 및 페쳐 파일 생성 #34
froggy1014 Aug 31, 2024
3454c0c
✨ feat: 추천 축제 컴포넌트 구현 #34
froggy1014 Aug 31, 2024
ec7bc44
💅 style: secondary 관련 클래스네임을 동적 할당을 하기위한 safelist 옵션 추가 #42
froggy1014 Sep 1, 2024
1084baa
✅ test: refresh token 로직 임시 코멘트 아웃 #42
froggy1014 Sep 1, 2024
29de13b
🔧 chore: 로그아웃 임시 버튼 생성 #42
froggy1014 Sep 1, 2024
40be5d1
✨ feat: 유저별 추천 페스티벌 컴포넌트 UI 단 구현 #42
froggy1014 Sep 1, 2024
65d1e8d
🐛 fix: memo 추가 및 컴포넌트 분리하여 re-rendering 최소화 #42
froggy1014 Sep 1, 2024
feb3ce7
🔧 chore: usecallback 삭제
froggy1014 Sep 1, 2024
9048e1d
👷 cicd: correct the name of _set_env.yaml file
froggy1014 Sep 1, 2024
7688310
🐛 fix: 스키마 타입 수정으로 불필요한 default 값 제거
froggy1014 Sep 2, 2024
33080db
✨ feat: 추천 페스티팔 컴포넌트 구현
froggy1014 Sep 3, 2024
931041e
♻️ refactor: 추천 페스티벌 페쳐, 쿼리키, 타입 리팩토링 및 세션에 의한 조건부 렌더링 추가
froggy1014 Sep 3, 2024
33ba70f
🔧 chore: session 있을시 home으로 리다이렉트 추가
froggy1014 Sep 3, 2024
4490988
✨ feat: 토큰 디코딩 함수 추가
froggy1014 Sep 3, 2024
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
4 changes: 2 additions & 2 deletions .github/workflows/chromatic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ jobs:
cache-dependency-path: "**/pnpm-lock.yaml"


- name: Set up Environment Variable for build
uses: ./.github/workflows/_set_env.yaml
- name: Set up Environment Variable for build
uses: ./.github/workflows/_set_env.yml
with:
NODE_VERSION: 20.x
NEXT_PUBLIC_STAGE: development
Expand Down
59 changes: 27 additions & 32 deletions auth.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
// ? Reference https://github.dev/nextauthjs/next-auth-example/blob/main/app/api/protected/route.ts
// ? Reference https://www.heropy.dev/p/MI1Khc

import { decodeJwt } from "jose";
import NextAuth, { Account, NextAuthConfig, Session, User } from "next-auth";
import { AdapterUser } from "next-auth/adapters";
import { JWT } from "next-auth/jwt";
import { ProviderType } from "next-auth/providers";
import Kakao from "next-auth/providers/kakao";

import {
getRefreshToken,
getServerSideSession,
postOauthLogin,
} from "@/apis/auth/auth";
import { getServerSideSession, postOauthLogin } from "@/apis/auth/auth";
import { SocialLoginRequest } from "@/apis/auth/authType";
import { isMatchPath } from "@/utils";

Expand Down Expand Up @@ -69,40 +64,40 @@ const config = {
user?: User | AdapterUser;
account?: Account | null;
}) => {
if (token.accessToken) {
const decodedToken = decodeJwt(token.accessToken);

if (decodedToken.exp && decodedToken.exp * 1000 < Date.now()) {
const { accessToken, refreshToken } = await getRefreshToken(
token.refreshToken as string,
);
token.accessToken = accessToken;
token.refreshToken = refreshToken;
return token;
}

return token;
}

if (!user?.id || !user.email || !account?.access_token) {
throw Error("Login Failed");
}
const body: SocialLoginRequest = {
id: user?.id,
email: user?.email,
accessToken: account?.access_token,
};
// ! refresh Token 로직 수정 필요
// if (token.accessToken) {
// const decodedToken = decodeJwt(token.accessToken);

// if (decodedToken.exp && decodedToken.exp * 1000 < Date.now()) {
// const { accessToken, refreshToken } = await getRefreshToken(
// token.refreshToken as string,
// );
// token.accessToken = accessToken;
// token.refreshToken = refreshToken;
// return token;
// }

// return token;
// }

let response: JWT = token;

if (!Object.hasOwn(token, "isProfileRegistered")) {
response = await postOauthLogin(body);
if (!!user && !!account) {
const body: SocialLoginRequest = {
id: user.id as string,
email: user.email as string,
accessToken: account.access_token as string,
};

if (!Object.hasOwn(token, "isProfileRegistered")) {
response = await postOauthLogin(body);
}
}

token.accessToken = response?.accessToken;
token.refreshToken = response.refreshToken;
token.isProfileRegistered = response.isProfileRegistered;
token.email = user.email;
token.email = user?.email;

return token;
},
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"react-daum-postcode": "^3.1.3",
"react-dom": "^18",
"react-hook-form": "7.52.2",
"swiper": "^11.1.11",
"tailwind-merge": "^2.4.0",
"tailwindcss-radix": "^3.0.3",
"usehooks-ts": "^3.1.0",
Expand Down
9 changes: 9 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 30 additions & 0 deletions src/apis/festivals/recommendFestival/recommendFestival.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"use server";

import instance from "@/apis/instance";
import FIESTA_ENDPOINTS from "@/config/apiEndpoints";
import { generateUrlWithParams } from "@/utils/generateUrlWithParams";

import {
PaginationParamter,
RecommendFestivalResponse,
} from "./recommendFestivalType";

const defaultParams: PaginationParamter = { page: 0, size: 5 };
const ENDPOINT = FIESTA_ENDPOINTS.festivals;

export async function getRecommendFestival(
params: PaginationParamter = defaultParams,
) {
try {
const endpoint = ENDPOINT.recommend;
const { data } = await instance.get<RecommendFestivalResponse>(
generateUrlWithParams(endpoint, params),
{
cache: "no-store",
},
);
return data;
} catch (error) {
return null;
}
}
24 changes: 24 additions & 0 deletions src/apis/festivals/recommendFestival/recommendFestivalType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export type PaginationParamter = {
page: number;
size: number;
};

export type UserType = {
userTypeId: number;
name: string;
};

export type RecommendFestivalResponse = {
festivals: Array<RecommendFestivalModel>;
userType: UserType;
};

Zero-1016 marked this conversation as resolved.
Show resolved Hide resolved
export interface RecommendFestivalModel {
festivalId: number;
name: string;
sido: string;
sigungu: string;
thumbnailImage: string;
startDate: string;
endDate: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@ import { CREATE_FESTIVAL_SETTING } from "@/config";
import { CreateFestivalType } from "@/validations/CreateFestivalSchema";

const CreateFestivalFirstStep = () => {
const {
control,
formState: { dirtyFields },
} = useFormContext<CreateFestivalType>();
const { control } = useFormContext<CreateFestivalType>();

return (
<section className=" flex w-full flex-col gap-[32px]">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FC } from "react";
import { Controller, useFormContext, useWatch } from "react-hook-form";
import { Controller, useFormContext } from "react-hook-form";

import {
FestivalCategory,
Expand All @@ -9,13 +9,14 @@ import {
AddressInput,
CategoryKeywordInput,
DescriptionInput,
DurationInput,
MoodKeywordInput,
TextInput,
TimeInput,
} from "@/components/core/Input";
import { CreateFestivalType } from "@/validations/CreateFestivalSchema";

import DurationFestivalInput from "./DurationFestivalInput";

interface Props {
moods: Array<FestivalMood>;
categories: Array<FestivalCategory>;
Expand All @@ -29,31 +30,18 @@ const CreateFestivalSecondStep: FC<Props> = ({ moods, categories }) => {
formState: { errors, submitCount },
} = useFormContext<CreateFestivalType>();

const startDate = useWatch({ control, name: "startDate" });
const endDate = useWatch({ control, name: "endDate" });
const time = useWatch({ control, name: "playtime" });

const handleCalendarConfirm = (start: string | null, end: string | null) => {
setValue("startDate", start ?? "", { shouldValidate: true });
setValue("endDate", end ?? "", { shouldValidate: true });
};

const handleAddress = (
address: string,
sido: string,
sigungu: string,
latitude: string,
longitude: string,
) => {
setValue("address", address ?? "", { shouldValidate: true });
setValue("sido", sido ?? "", { shouldValidate: true });
setValue("sigungu", sigungu ?? "", { shouldValidate: true });
setValue("latitude", latitude ?? "", { shouldValidate: true });
setValue("longitude", longitude ?? "", { shouldValidate: true });
};

const handleTimeChange = (time: string) => {
setValue("playtime", time, { shouldValidate: true });
setValue("address", address ?? "");
Zero-1016 marked this conversation as resolved.
Show resolved Hide resolved
setValue("sido", sido ?? "");
setValue("sigungu", sigungu ?? "");
setValue("latitude", latitude ?? "");
setValue("longitude", longitude ?? "");
};

const handleGetError = (name: keyof CreateFestivalType) => {
Expand All @@ -72,24 +60,24 @@ const CreateFestivalSecondStep: FC<Props> = ({ moods, categories }) => {

return (
<section className="flex w-full flex-col gap-[18px]">
<DurationInput
label="페스티벌 기간"
start={startDate}
end={endDate}
error={handleGetError("startDate") || handleGetError("endDate")}
onConfirm={handleCalendarConfirm}
/>
<DurationFestivalInput handleGetError={handleGetError} />

<AddressInput
value={null}
onChange={handleAddress}
error={handleGetError("address")}
/>

<TimeInput
value={time}
onChange={handleTimeChange}
error={handleGetError("playtime")}
<Controller
control={control}
name="playtime"
render={({ field: { onChange, value } }) => (
<TimeInput
value={value}
onChange={onChange}
error={handleGetError("playtime")}
/>
)}
/>

<label className="text-subtitle-medium text-gray-scale-900">URL</label>
Expand Down
32 changes: 32 additions & 0 deletions src/app/(home)/festivals/new/_components/DurationFestivalInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { FC } from "react";
import { useFormContext, useWatch } from "react-hook-form";

import { DurationInput } from "@/components/core/Input";
import { CreateFestivalType } from "@/validations/CreateFestivalSchema";

interface Props {
handleGetError: (name: keyof CreateFestivalType) => string | undefined;
}

const DurationFestivalInput: FC<Props> = ({ handleGetError }) => {
const { control, setValue } = useFormContext<CreateFestivalType>();

const startDate = useWatch({ control, name: "startDate" });
const endDate = useWatch({ control, name: "endDate" });

const handleCalendarConfirm = (start: string | null, end: string | null) => {
setValue("startDate", start ?? "");
setValue("endDate", end ?? "");
};
return (
<DurationInput
label="페스티벌 기간"
start={startDate}
end={endDate}
error={handleGetError("startDate") || handleGetError("endDate")}
onConfirm={handleCalendarConfirm}
/>
);
};

export default DurationFestivalInput;
5 changes: 4 additions & 1 deletion src/app/(home)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { getServerSideSession } from "@/apis/auth/auth";
import { FloatingButton } from "@/components/core/Button";
import RecommendFestival from "@/components/Swiper/RecommendFestival";
import { HomeHeader } from "@/layout/Mobile/MobileHeader";
import NavigationBar from "@/layout/Mobile/NavigationBar";

import { FestivalHot, FestivalThisWeek, TopReviews } from "./_components";

export default async function Home() {
const session = await getServerSideSession();
return (
<div className="mb-[60px] mt-[44px]">
<HomeHeader />
<main className="flex flex-col gap-[40px] bg-gray-scale-100 px-[16px] pb-[36px] pt-[16px]">
<RecommendFestival />
<main className="flex flex-col gap-[40px] rounded-t-[20px] bg-gray-scale-100 px-[16px] pb-[36px] pt-[16px]">
<FestivalHot />
<FestivalThisWeek />
<TopReviews />
Expand Down
11 changes: 8 additions & 3 deletions src/app/mypage/view.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { postSignOut } from "@/apis/auth/auth";

const MyPageView = () => {
return (
<main className="flex flex-wrap gap-4 bg-indigo-300 text-title-bold">
<div className="h-[100px] w-full bg-primary-05"></div>
<form
action={postSignOut}
className="flex flex-wrap gap-4 bg-indigo-300 text-title-bold"
>
<button>Logout</button>
<div className="h-[100px] w-full bg-primary-05"></div>
<div className="h-[100px] w-full bg-primary-05"></div>
<div className="h-[100px] w-full bg-primary-05"></div>
Expand All @@ -10,7 +15,7 @@ const MyPageView = () => {
<div className="h-[100px] w-full bg-primary-05"></div>
<div className="h-[100px] w-full bg-primary-05"></div>
<div className="h-[100px] w-full bg-primary-05"></div>
</main>
</form>
);
};

Expand Down
2 changes: 1 addition & 1 deletion src/app/onboarding/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import OnBoardingView from "./view";
export default async function OnBoarding() {
const session = await getServerSideSession();

if (session?.isProfileRegistered) {
if (!session || session?.isProfileRegistered) {
redirect("/");
}

Expand Down
Loading