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

온보딩 데이터 페칭 및 Flow UI 완성 #35

Merged
merged 29 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
21c3fe2
🔨 settings: nextjs caching logging config 추가 #6
froggy1014 Aug 13, 2024
e320a6b
🔧 chore: 개발환경 로깅 유틸함수 추가 #6
froggy1014 Aug 13, 2024
21319f4
🔧 chore: onboading API 명세에 따른 타입 추가 #6
froggy1014 Aug 13, 2024
5b8c1f3
🔧 chore: 쿼리 데브툴 임시 비활성화
froggy1014 Aug 13, 2024
20fad68
✨ feat: fetch를 추상화한 http 클래스 임시 구현 #6
froggy1014 Aug 13, 2024
5fd7bfd
✨ feat: 온보딩 서버액션, 서비스코드 구현 #6
froggy1014 Aug 13, 2024
e10301e
🔧 chore: 타입 변경 적용 #6
froggy1014 Aug 13, 2024
52300b8
♻️ refactor: 타입 수정에 의한 수정사항 #6
froggy1014 Aug 13, 2024
3697a95
💅 style: 타입 변경에의한 컴포넌트 수정 #6
froggy1014 Aug 13, 2024
bf0a6a0
🔧 chore: 딜레이 유틸 함수 추가
froggy1014 Aug 13, 2024
76e7330
💅 style: 컴포넌트 마이너 변경
froggy1014 Aug 13, 2024
d457803
💅 style: 온보딩 로딩 이미지 아셋 추가 #6
froggy1014 Aug 13, 2024
9817aea
🔨 settings: framer motion 설치 #6
froggy1014 Aug 13, 2024
43992ef
✨ feat: 온보딩 로딩 컴포넌트 구현 #6
froggy1014 Aug 13, 2024
ee84224
🔨 settings: 애니메이션 효과를 위한 canvas-confetti 설치 #6
froggy1014 Aug 14, 2024
539780c
💅 style: 온보딩 카드 이미지 아셋에 추가 #6
froggy1014 Aug 14, 2024
ee247f5
✅ test: complete 페이지 임시 구현 #6
froggy1014 Aug 14, 2024
5908c85
🔧 chore: downloadImageByUrl 유틸 함수 추가 #6
froggy1014 Aug 14, 2024
6fd7862
♻️ refactor: 파일 및 폴더 구조 리팩토링 #6
froggy1014 Aug 14, 2024
0e7a4c4
✨ feat: useOnBoardingStep 커스텀 훅 생성 #6
froggy1014 Aug 14, 2024
9bbc5ca
refactor: Move page fetching to server component
froggy1014 Aug 18, 2024
7c6cd4b
🔧 chore: 온보딩 관련 값들 상수화 #6
froggy1014 Aug 18, 2024
9eeff21
🔧 chore: 변경된 타입 필드 이름 적용 #6
froggy1014 Aug 18, 2024
693f555
✨ feat: 온보딩 관련 타입, 데이터페칭, 키 정의 #6
froggy1014 Aug 18, 2024
5d00b02
🔧 chore: default Header를 서버, 클라이언트 양쪽 사용을 위한 버튼 분리 #6
froggy1014 Aug 18, 2024
37cfaa7
Merge branch 'develop' into feature/onboarding-final
froggy1014 Aug 18, 2024
397e330
♻️ refactor: defaultHeaderBackButton 수정
froggy1014 Aug 18, 2024
32199ef
♻️ refactor: 선택해야하는 최솟값들 상수화 #6
froggy1014 Aug 18, 2024
19ecd15
♻️ refactor: 피드백 반영
froggy1014 Aug 19, 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
6 changes: 6 additions & 0 deletions next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ const nextConfig = {

return config;
},
// ? https://nextjs.org/docs/app/api-reference/next-config-js/logging
froggy1014 marked this conversation as resolved.
Show resolved Hide resolved
logging: {
fetches: {
fullUrl: true,
},
},
};

export default nextConfig;
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"dayjs": "^1.11.12",
"framer-motion": "^11.3.24",
"next": "14.2.4",
"nuqs": "^1.17.8",
"react": "^18",
Expand Down
24 changes: 24 additions & 0 deletions pnpm-lock.yaml

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

Binary file added public/images/onboadingLoading.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/onboardingCard.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
77 changes: 77 additions & 0 deletions src/apis/onboarding/onboarding.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"use server";
import { FIESTA_ENDPOINTS } from "@/config";
import { getSettledValue } from "@/utils";

import instance from "../instance";
import { festivalOnBoarding } from "./onboardingKeys";
import {
FestivalCategory,
FestivalCompanion,
FestivalMood,
FestivalPriority,
OnboardingModel,
} from "./onboardingType";

export const getMoods = async () => {
const endpoint = FIESTA_ENDPOINTS.festivals.moods;
const { data } = await instance.get<Array<FestivalMood>>(endpoint, {
next: {
tags: festivalOnBoarding.all,
},
});

return data;
};
export const getCategories = async () => {
const endpoint = FIESTA_ENDPOINTS.festivals.categories;
const { data } = await instance.get<Array<FestivalCategory>>(endpoint, {
next: {
tags: festivalOnBoarding.all,
},
});

return data;
};
export const getCompanions = async () => {
const endpoint = FIESTA_ENDPOINTS.festivals.companions;
const { data } = await instance.get<Array<FestivalCompanion>>(endpoint, {
next: {
tags: festivalOnBoarding.all,
},
});

return data;
};

export const getPriority = async () => {
const endpoint = FIESTA_ENDPOINTS.festivals.priorities;
const { data } = await instance.get<Array<FestivalPriority>>(endpoint, {
next: {
tags: festivalOnBoarding.all,
},
});

return data;
};

export const getOnboardingData = async (): Promise<OnboardingModel> => {
const [moodsPromise, categoriesPromise, companionsPromise, priorityPromise] =
await Promise.allSettled([
getMoods(),
getCategories(),
getCompanions(),
getPriority(),
]);

const moods = getSettledValue(moodsPromise, []);
const categories = getSettledValue(categoriesPromise, []);
const companions = getSettledValue(companionsPromise, []);
const priorities = getSettledValue(priorityPromise, []);

return {
moods,
companions,
priorities,
categories,
};
};
3 changes: 3 additions & 0 deletions src/apis/onboarding/onboardingKeys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const festivalOnBoarding = {
all: ["FestivalOnBoarding"],
};
27 changes: 27 additions & 0 deletions src/apis/onboarding/onboardingType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export type FestivalMood = {
moodId: number;
name: string;
};

export type FestivalCompanion = {
companionId: number;
companionType: string;
};

export type FestivalCategory = {
categoryId: number;
emoji: string;
name: string;
};

export type FestivalPriority = {
priorityId: number;
priority: string;
};

export type OnboardingModel = {
categories: Array<FestivalCategory>;
companions: Array<FestivalCompanion>;
priorities: Array<FestivalPriority>;
moods: Array<FestivalMood>;
};
8 changes: 2 additions & 6 deletions src/app/onboarding/_components/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,2 @@
export { default as OnBoardingButton } from "./OnBoardingButton";
export { default as OnBoardingCategories } from "./OnBoardingCategories";
export { default as OnBoardingCompanies } from "./OnBoardingCompanies";
export { default as OnBoardingMoods } from "./OnBoardingMoods";
export { default as OnBoardingPriorities } from "./OnBoardingPriorities";
export { default as OnBOardingTitle } from "./OnBoardingTitle";
export { default as OnBoardingLoading } from "./onBoardingLoading";
export { default as OnBoardingContainer } from "./onBoardingStep";
73 changes: 73 additions & 0 deletions src/app/onboarding/_components/onBoardingLoading/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { AnimatePresence, motion } from "framer-motion";
import Image from "next/image";

import { IconButton } from "@/components/core/Button";
import { ProgressCircle } from "@/components/core/Progress";
import { CheckIcon } from "@/components/icons";
import { cn } from "@/utils/cn";

const ONBOARDING_LOADING_MESSAGES = [
"페스티벌 관심사 파악 완료!",
"페스티벌 무드 파악 완료!",
"페스티벌 일행 파악 완료!",
"페스티벌 우선순위 파악 완료!",
];

const OnBoardingLoading = () => {
return (
<section className="flex h-full w-full flex-col items-center justify-center gap-[30px] px-[16px]">
<div className="flex w-full flex-col items-center">
<h1 className="max-w-[250px] break-words text-center text-title-bold">
페스티벌 유형 프로필을
<br />
설정하는 중이예요
</h1>
<h1 className=""></h1>
<h2 className="mt-[10px] text-caption2-medium">
잠시만 기다려주세요...
</h2>
</div>

<div className="relative">
<ProgressCircle className="z-[10]" />
<div className="absolute left-1/2 top-1/2 flex size-[128px] translate-x-[-50%] translate-y-[-50%] items-end justify-center rounded-full">
<Image
width={180}
height={150}
src={"/images/onboadingLoading.png"}
alt="onboading-loading"
/>
</div>
</div>

<div className="flex h-auto w-full flex-col gap-[12px]">
<AnimatePresence>
{ONBOARDING_LOADING_MESSAGES.map((str, idx) => (
<motion.div
key={str}
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{ duration: 0.5, delay: idx * 1 }}
>
<IconButton className="h-[54px] w-full rounded-[8px] bg-primary-05">
<div className="flex w-full items-center gap-[6px] p-[16px]">
<CheckIcon
width={24}
height={24}
className={cn("text-primary-01", "duration-300")}
/>
<span className="text-caption2-medium text-gray-scale-600">
{str}
</span>
</div>
</IconButton>
</motion.div>
))}
</AnimatePresence>
</div>
</section>
);
};

export default OnBoardingLoading;
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { FC } from "react";
import { useFormContext, useWatch } from "react-hook-form";

import { OnboardingModel } from "@/apis/onboarding/onboardingType";
import { BasicButton } from "@/components/core/Button";
import { OnboardingModel } from "@/model/onboarding";
import { ONBOARDING_SETTING } from "@/config";
import { checkNumber } from "@/utils/checkNumber";

interface Props {
Expand All @@ -23,19 +24,19 @@ const OnBoardingButton: FC<Props> = ({ currentStep, totalStep, onNext }) => {
[key: number]: { label: string; isError: boolean };
} = {
1: {
label: `다음 (${checkNumber(fields.categories?.length)}/2)`,
label: `다음 (${checkNumber(fields.categories?.length)}/${ONBOARDING_SETTING.CATEGORY_MIN})`,
isError: !!errors.categories,
},
2: {
label: `다음 (${checkNumber(fields.moods?.length)}/3)`,
label: `다음 (${checkNumber(fields.moods?.length)}/${ONBOARDING_SETTING.MOOD_MIN})`,
isError: !!errors.moods,
},
3: {
label: `다음`,
isError: !!errors.companies,
isError: !!errors.companions,
},
4: {
label: `다음 (${checkNumber(fields.priorities?.length)}/3)`,
label: `다음 (${checkNumber(fields.priorities?.length)}/${ONBOARDING_SETTING.PRIORITY_MIN})`,
isError: !!errors.priorities,
},
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { FC } from "react";
import { useFieldArray, useFormContext } from "react-hook-form";

import {
FestivalCategory,
OnboardingModel,
} from "@/apis/onboarding/onboardingType";
import { SquareTabButton } from "@/components/core/Button";
import { CategoryModel, OnboardingModel } from "@/model/onboarding";
import { ONBOARDING_SETTING } from "@/config";

import { ONBOARDING } from "../_constants";
import OnBoardingTitle from "./OnBoardingTitle";

interface Props {
categories: Array<CategoryModel>;
categories: Array<FestivalCategory>;
}

const OnBoardingCategories: FC<Props> = ({ categories }) => {
Expand All @@ -20,10 +23,14 @@ const OnBoardingCategories: FC<Props> = ({ categories }) => {

const handleCategoryToggle = (
isSelected: boolean,
category: CategoryModel,
category: FestivalCategory,
) => {
if (isSelected) {
replace(fields.filter((v) => v.categoryId !== category.categoryId));
replace(
fields.filter(
(festival) => festival.categoryId !== category.categoryId,
),
);
return;
}
append(category);
Expand All @@ -33,22 +40,32 @@ const OnBoardingCategories: FC<Props> = ({ categories }) => {
<>
<div className="flex h-auto w-full flex-col gap-[32px] ">
<OnBoardingTitle
title={ONBOARDING.CATEGORY_TITLE}
subtitle={ONBOARDING.CATEGORY_SUBTITLE}
title={ONBOARDING_SETTING.CATEGORY_TITLE}
subtitle={ONBOARDING_SETTING.CATEGORY_SUBTITLE}
/>

<section className="grid h-auto w-full grid-cols-3 gap-x-[18px] gap-y-[20px]">
{categories.map(({ categoryId, category }) => {
const isSelected = fields.some((c) => c.categoryId === categoryId);
{categories.map(({ categoryId, name, emoji }) => {
const isSelected = fields.some(
(festival) => festival.categoryId === categoryId,
);
return (
<SquareTabButton
key={categoryId}
type="button"
label={category}
label={name}
active={isSelected}
disabled={fields.length === 2 && !isSelected}
emoji={emoji}
disabled={
fields.length === ONBOARDING_SETTING.CATEGORY_MIN &&
!isSelected
}
onClick={() =>
handleCategoryToggle(isSelected, { categoryId, category })
handleCategoryToggle(isSelected, {
categoryId,
name,
emoji,
})
}
/>
);
Expand Down
Loading