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

feat: 사용자 정보기입 페이지 구현 #10

Merged
merged 18 commits into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
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
207 changes: 207 additions & 0 deletions spark-web/src/domains/UserInformation/UserInformation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
import { useState, useEffect, useRef, RefObject } from "react";
Inoansta marked this conversation as resolved.
Show resolved Hide resolved

import UserInformationProfiles from "./components/UserInformationProfiles";
import Questionbox from "./components/Questionbox";
import UserDialogbox from "./components/UserDialogbox";
import BottomSheetModal from "@/shared/components/BottomSheetModal/BottomSheetModal";
import PrimaryChips from "@/shared/components/Chips/PrimaryChips";
import Buttons from "@/shared/components/Buttons/Buttons";
import FulltimeParttime from "./components/Footers/FulltimeParttime";
import SNSGoal from "./components/Footers/SNSGoal";

import { QUESTIONS } from "./questions";

interface userAnswer {
CONTENTS: string;
FULLTIME: string;
GOAL: string;
}

const buttons: string[] = [
Inoansta marked this conversation as resolved.
Show resolved Hide resolved
"푸드/먹방",
"패션",
"뷰티",
"여행",
"반려동물",
"IT/과학기술",
"엔터테인먼트",
"게임",
"영화",
"음악",
"육아/패밀리",
"경제",
"교육",
"vlog",
"운동",
];

function UserInformation() {
const [steps, setSteps] = useState<number>(0);
const [onlyClicked, setOnlyClicked] = useState<string>("");
const [open, setOpen] = useState<boolean>(false);
const [userAnswer, setUserAnswer] = useState<userAnswer>({
CONTENTS: "",
FULLTIME: "",
GOAL: "",
});
const [isTyped, setIsTyped] = useState<boolean>(false);
const bottomRef: RefObject<HTMLDivElement> = useRef<HTMLDivElement>(null);

useEffect(() => {
bottomRef.current?.scrollIntoView();

switch (steps) {
case 0:
setTimeout(() => setSteps(1), 1000);
break;
case 1:
setTimeout(() => setSteps(2), 1000);
break;
case 3:
setTimeout(() => setSteps(4), 1000);
break;
case 4:
setTimeout(() => setSteps(5), 1000);
break;
case 5:
setTimeout(() => setSteps(6), 1000);
break;
case 7:
setTimeout(() => setSteps(8), 1000);
break;
case 8:
setTimeout(() => setSteps(9), 1000);
break;
case 10:
setTimeout(() => setSteps(11), 1000);
break;
case 11:
setTimeout(() => setSteps(12), 1000);
break;
}
}, [steps]);

function SheetContents() {
return (
<div>
<div className={"flex flex-wrap gap-[10px] py-[20px]"}>
{buttons.map((item) => {
return (
<PrimaryChips
text={item}
key={item}
setOnlyClicked={() => {
setOnlyClicked(item);
setUserAnswer({ ...userAnswer, CONTENTS: item });
}}
onlyClicked={onlyClicked}
/>
);
})}
</div>
<div className={"flex justify-end pb-[30px]"}>
<Buttons
text={"확인"}
onClick={() => {
setOpen(false);
setSteps(3);
}}
buttonType={"small-outlined-button"}
/>
</div>
</div>
);
}

return (
<div className={"px-[20px] flex flex-col"}>
{steps === 0 ? (
<>
<UserInformationProfiles />
<Questionbox questions={QUESTIONS.contents1} questionType={"last"} />
</>
) : null}
{steps >= 1 ? (
<>
<UserInformationProfiles />
<Questionbox questions={QUESTIONS.contents1} questionType={"first"} />
<Questionbox questions={QUESTIONS.contents2} questionType={"last"} />
</>
) : null}
{steps === 2 ? (
<>
<div className={"fixed left-0 right-0 px-[20px] py-[12px] bottom-0"}>
<Buttons
text={"답변하기"}
buttonType={"large-filled-button"}
onClick={() => setOpen(true)}
/>
</div>
<BottomSheetModal
open={open}
setClose={() => setOpen(false)}
title={"활동 분야를 선택해주세요."}
contents={<SheetContents />}
/>
</>
) : null}
{steps >= 4 ? (
<UserDialogbox answers={userAnswer.CONTENTS} isTyped={isTyped} />
) : null}
{steps >= 5 ? (
<>
<UserInformationProfiles />
<Questionbox questions={QUESTIONS.fulltime} questionType={"last"} />
</>
) : null}
{steps === 6 ? (
<FulltimeParttime
onClick={(fulltime) =>
setUserAnswer({ ...userAnswer, FULLTIME: fulltime })
}
setSteps={() => setSteps(7)}
/>
) : null}
{steps >= 7 ? (
<UserDialogbox answers={userAnswer.FULLTIME} isTyped={isTyped} />
) : null}
{steps >= 8 ? (
<>
<UserInformationProfiles />
<Questionbox questions={QUESTIONS.goal} questionType={"last"} />
</>
) : null}
{steps === 9 ? (
<SNSGoal
onClick={(goal) => {
setUserAnswer({ ...userAnswer, GOAL: goal });
setSteps(10);
}}
setIsTyped={() => setIsTyped(true)}
/>
) : null}
{steps >= 10 ? (
<UserDialogbox answers={userAnswer.GOAL} isTyped={isTyped} />
) : null}
{steps >= 11 ? (
<>
<UserInformationProfiles />
<Questionbox questions={QUESTIONS.analyze} questionType={"last"} />
</>
) : null}
{steps === 12 ? (
<div className={"bottom-0 sticky py-[12px] bg-white"}>
{/* onClick에 api연결하기 */}
<Buttons
text={"나만의 성장비법 받기"}
buttonType={"large-filled-button"}
onClick={() => {}}
/>
</div>
) : null}
<div ref={bottomRef} />
</div>
);
}

export default UserInformation;

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { useState } from 'react';
import Buttons from '@/shared/components/Buttons/Buttons';

interface FulltimeParttime {
onClick: (fulltime: string) => void;
setSteps: () => void;
}

function FulltimeParttime({ onClick, setSteps }: FulltimeParttime) {
const [clicked, setClicked] = useState<boolean>(true);

return (
<div
className={`${clicked ? 'block' : 'hidden'} bg-white pt-[12px] pb-[30px] fixed left-0 right-0 bottom-0 px-[20px] z-10 flex flex-col gap-[10px]`}
onClick={() => {
setClicked(false);
setSteps();
}}
>
<Buttons
text={'전업'}
onClick={() => onClick('전업')}
buttonType={'large-outlined-button'}
/>
<Buttons
text={'부업'}
onClick={() => onClick('부업')}
buttonType={'large-outlined-button'}
Inoansta marked this conversation as resolved.
Show resolved Hide resolved
/>
</div>
);
}

export default FulltimeParttime;
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React, { useState } from "react";
Inoansta marked this conversation as resolved.
Show resolved Hide resolved
import Buttons from "@/shared/components/Buttons/Buttons";

interface SNSGoal {
onClick: (goal: string) => void;
setIsTyped: () => void;
}

function SNSGoal({ onClick, setIsTyped }: SNSGoal) {
const [touched, setTouched] = useState<boolean>(true);
const [inputText, setInputText] = useState<string>("");

function submitOnEnter(e: React.KeyboardEvent<HTMLInputElement>) {
if (e.key === "Enter") {
onClick(inputText);
setIsTyped();
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

내부안에 이벤트 할당하는 함수는 function 선언식 대신에 함수 표현식으로 작성해주세요!
useCallback 적용하는데 더 좋아요

Copy link
Collaborator Author

@Inoansta Inoansta Jan 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

혹시 해당 함수가 왜 useCallback을 적용하는게 더 좋다고 생각하셨는지 알 수 있을까요?

해당 함수는 자주 변하는 inputText 상태에 의존적인데, useCallback의 의존성으로 inputText를 설정하면 재생성이 너무 자주 일어나 효용이 떨어지고, 그렇다고 의존성 배열에 입력하지 않으면 해당 상태변화가 적용되지 않는 문제점이 생깁니다


return (
<>
<div
className={`${touched ? "block" : "hidden"} sticky bottom-0 bg-white pt-[12px] pb-[30px] flex flex-col gap-[10px]`}
>
<Buttons
text={"개인 브랜딩 및 전문성 강화"}
onClick={() => {
onClick("개인 브랜딩 및 전문성 강화");
setTouched(true);
}}
buttonType={"large-outlined-button"}
/>
<Buttons
text={"수익화 및 경제적 이익"}
onClick={() => {
onClick("수익화 및 경제적 이익");
setTouched(true);
}}
buttonType={"large-outlined-button"}
/>
<Buttons
text={"개인적인 취미 및 즐거움"}
onClick={() => {
onClick("개인적인 취미 및 즐거움");
setTouched(true);
}}
buttonType={"large-outlined-button"}
/>
<Buttons
text={"직접입력"}
onClick={() => setTouched(false)}
buttonType={"large-outlined-button"}
/>
</div>
<div
className={`${touched ? "hidden" : "block"} fixed left-0 right-0 pt-[12px] pb-[20px] px-[20px] flex justify-center bottom-0`}
>
<input
className={
"block text-body-m w-[335px] h-[48px] border px-[20px] border-primary5 rounded-medium "
}
onChange={(e) => setInputText(e.target.value)}
onKeyDown={submitOnEnter}
/>
</div>
</>
);
}

export default SNSGoal;

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
interface Questionbox {
questions: string;
questionType: 'first' | 'last';
}

const style = {
first:
'bg-primary1 text-black px-[20px] py-[10px] rounded-medium inline-block',
last: 'bg-primary1 text-black px-[20px] py-[10px] rounded-b-medium rounded-tr-medium inline-block',
Inoansta marked this conversation as resolved.
Show resolved Hide resolved
};

function Questionbox({ questions, questionType }: Questionbox) {
return (
<div
className={`text-left mt-[5px] ${questionType === 'last' ? 'mb-[5px]' : null}`}
>
<div className={style[questionType]}>{questions}</div>
</div>
);
}

export default Questionbox;
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import SparkIcon from '@/assets/svg/sparkIcon.svg?react';

function UserInformationProfiles() {
return (
<div className={'flex flex-row mt-[20px] items-center'}>
<SparkIcon style={{ width: '48px', height: '48px' }} />
<div className={'text-body-m text-black ml-[10px]'}>스파크</div>
</div>
);
}

export default UserInformationProfiles;
Loading
Loading