Skip to content

Commit a1ee533

Browse files
Feat(client): 직무 조회 API 연결 (온보딩/대시보드) (#274)
* feat: 직무 목록 get api function 구현 * feat: onboarding funnel job state 추가 * feat: jobs 목록 조회 api 연결 * chore: onboarding funnel file 구조 변경 * feat: dashboard job funnel 직무 조회 api 연결 * chore: 직무 선택 default 설정 로직 제거 * feat: 직무 공유 체크박스 동의 default value false로 변경 * fix: 불필요 코드 제거 * setting: 미사용 import문 저장 시 자동 제거 되도록 setting 추가 * refactor: 공통 jobCards & jobCardSkeleton 컴포넌트 재사용하도록 변경
1 parent 5499fe0 commit a1ee533

File tree

20 files changed

+291
-278
lines changed

20 files changed

+291
-278
lines changed

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
"editor.formatOnSave": true,
1313
"editor.defaultFormatter": "esbenp.prettier-vscode",
1414
"editor.codeActionsOnSave": {
15-
"source.fixAll.eslint": "explicit"
15+
"source.fixAll.eslint": "explicit",
16+
"source.organizeImports": "always"
1617
},
1718
"tailwindCSS.suggestions": true,
1819
"editor.quickSuggestions": {

apps/client/src/pages/onBoarding/components/funnel/MainCard.tsx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { Progress, Button } from '@pinback/design-system/ui';
22
import { lazy, Suspense } from 'react';
33
import { motion, AnimatePresence } from 'framer-motion';
4-
import SocialLoginStep from './step/SocialLoginStep';
5-
const StoryStep = lazy(() => import('./step/StoryStep'));
6-
const JobStep = lazy(() => import('./step/JobStep'));
7-
const AlarmStep = lazy(() => import('./step/AlarmStep'));
8-
const MacStep = lazy(() => import('./step/MacStep'));
9-
const FinalStep = lazy(() => import('./step/FinalStep'));
4+
import SocialLoginStep from './step/socialLogin/SocialLoginStep';
5+
const StoryStep = lazy(() => import('./step/story/StoryStep'));
6+
const JobStep = lazy(() => import('./step/job/JobStep'));
7+
const AlarmStep = lazy(() => import('./step/alarm/AlarmStep'));
8+
const MacStep = lazy(() => import('./step/mac/MacStep'));
9+
const FinalStep = lazy(() => import('./step/final/FinalStep'));
1010
import { cva } from 'class-variance-authority';
1111
const stepProgress = [{ progress: 33 }, { progress: 66 }, { progress: 100 }];
1212
import {
@@ -47,8 +47,10 @@ const MainCard = () => {
4747
direction,
4848
alarmSelected,
4949
jobShareAgree,
50+
selectedJob,
5051
setAlarmSelected,
5152
setJobShareAgree,
53+
setSelectedJob,
5254
nextStep,
5355
prevStep,
5456
} = useOnboardingFunnel();
@@ -66,6 +68,8 @@ const MainCard = () => {
6668
case Step.JOB:
6769
return (
6870
<JobStep
71+
selectedJob={selectedJob}
72+
onSelectJob={setSelectedJob}
6973
agreeChecked={jobShareAgree}
7074
onAgreeChange={setJobShareAgree}
7175
/>
@@ -132,7 +136,7 @@ const MainCard = () => {
132136
size="medium"
133137
className="ml-auto w-[4.8rem]"
134138
onClick={nextStep}
135-
isDisabled={step === Step.JOB && !jobShareAgree}
139+
isDisabled={step === Step.JOB && (!jobShareAgree || !selectedJob)}
136140
>
137141
다음
138142
</Button>

apps/client/src/pages/onBoarding/components/funnel/step/JobStep.tsx

Lines changed: 0 additions & 140 deletions
This file was deleted.

apps/client/src/pages/onBoarding/components/funnel/step/AlarmStep.tsx renamed to apps/client/src/pages/onBoarding/components/funnel/step/alarm/AlarmStep.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import dotori from '/assets/onBoarding/icons/dotori.svg';
2-
import AlarmBox from '../AlarmBox';
2+
import AlarmBox from '../../AlarmBox';
33
import { useEffect } from 'react';
44
import { sendGAEvent } from '@pinback/design-system/ui';
55
interface AlarmStepProps {

apps/client/src/pages/onBoarding/components/funnel/step/FinalStep.tsx renamed to apps/client/src/pages/onBoarding/components/funnel/step/final/FinalStep.tsx

File renamed without changes.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { Suspense } from 'react';
2+
import { useQueryClient } from '@tanstack/react-query';
3+
import dotori from '/assets/onBoarding/icons/dotori.svg';
4+
import { Checkbox } from '@pinback/design-system/ui';
5+
import { JobsResponse } from '@shared/types/api';
6+
import JobCards from '@shared/components/jobSelectionFunnel/step/job/JobCards';
7+
import JobCardsSkeleton from '@shared/components/jobSelectionFunnel/step/job/JobCardsSkeleton';
8+
9+
interface JobStepProps {
10+
selectedJob: string | null;
11+
onSelectJob: (jobName: string) => void;
12+
agreeChecked: boolean;
13+
onAgreeChange: (checked: boolean) => void;
14+
}
15+
16+
const JobStep = ({
17+
selectedJob,
18+
onSelectJob,
19+
agreeChecked,
20+
onAgreeChange,
21+
}: JobStepProps) => {
22+
const queryClient = useQueryClient();
23+
const cachedJobs = queryClient.getQueryData<JobsResponse>(['jobs']);
24+
const skeletonCount = cachedJobs?.jobs.length ?? 4;
25+
26+
return (
27+
<div className="flex w-full flex-col items-center">
28+
<img src={dotori} className="mb-[1.2rem]" alt="dotori" />
29+
<div className="mb-[2.4rem] flex flex-col items-center gap-[0.8rem]">
30+
<p className="head3 text-font-black-1">직무를 선택해주세요</p>
31+
<p className="body2-m text-font-gray-3 text-center">
32+
직무에 따라 아티클을 추천해드려요
33+
</p>
34+
</div>
35+
36+
<Suspense fallback={<JobCardsSkeleton count={skeletonCount} />}>
37+
<JobCards activeJob={selectedJob} onSelectJob={onSelectJob} />
38+
</Suspense>
39+
40+
<label className="mt-[2.4rem] flex max-w-[62rem] items-start gap-[1.2rem]">
41+
<Checkbox
42+
size="small"
43+
isSelected={agreeChecked}
44+
onSelectedChange={onAgreeChange}
45+
/>
46+
<span className="body3-r text-font-gray-3">
47+
내가 북마크한 아티클이 내 Google 이름과 함께 다른 사용자에게 추천될 수
48+
있어요.
49+
</span>
50+
</label>
51+
</div>
52+
);
53+
};
54+
55+
export default JobStep;

apps/client/src/pages/onBoarding/components/funnel/step/MacStep.tsx renamed to apps/client/src/pages/onBoarding/components/funnel/step/mac/MacStep.tsx

File renamed without changes.

apps/client/src/pages/onBoarding/components/funnel/step/SocialLoginStep.tsx renamed to apps/client/src/pages/onBoarding/components/funnel/step/socialLogin/SocialLoginStep.tsx

File renamed without changes.

apps/client/src/pages/onBoarding/components/funnel/step/StoryStep.tsx renamed to apps/client/src/pages/onBoarding/components/funnel/step/story/StoryStep.tsx

File renamed without changes.

apps/client/src/pages/onBoarding/hooks/useOnboardingFunnel.ts

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import { useCallback, useEffect, useState } from 'react';
55
import { AlarmsType } from '@constants/alarms';
66
import { normalizeTime } from '@pages/onBoarding/utils/formatRemindTime';
77
import { registerServiceWorker } from '@pages/onBoarding/utils/registerServiceWorker';
8-
import { Step, stepOrder, StepType } from '@pages/onBoarding/constants/onboardingSteps';
8+
import {
9+
Step,
10+
stepOrder,
11+
StepType,
12+
} from '@pages/onBoarding/constants/onboardingSteps';
913
import { firebaseConfig } from '../../../firebase-config';
1014
import { getApp, getApps, initializeApp } from 'firebase/app';
1115
import { getMessaging, getToken } from 'firebase/messaging';
@@ -14,19 +18,25 @@ type AlarmSelection = 1 | 2 | 3;
1418

1519
export function useOnboardingFunnel() {
1620
const { mutate: postSignData } = usePostSignUp();
17-
const { currentStep: step, currentIndex, setStep, goNext, goPrev } =
18-
useFunnel<StepType>({
19-
steps: stepOrder,
20-
initialStep: Step.STORY_0,
21-
});
21+
const {
22+
currentStep: step,
23+
currentIndex,
24+
setStep,
25+
goNext,
26+
goPrev,
27+
} = useFunnel<StepType>({
28+
steps: stepOrder,
29+
initialStep: Step.STORY_0,
30+
});
2231

2332
const [direction, setDirection] = useState(0);
2433
const [alarmSelected, setAlarmSelected] = useState<AlarmSelection>(1);
2534
const [isMac, setIsMac] = useState(false);
2635
const [userEmail, setUserEmail] = useState('');
2736
const [remindTime, setRemindTime] = useState('09:00');
2837
const [fcmToken, setFcmToken] = useState<string | null>(null);
29-
const [jobShareAgree, setJobShareAgree] = useState(true);
38+
const [jobShareAgree, setJobShareAgree] = useState(false);
39+
const [selectedJob, setSelectedJob] = useState<string | null>(null);
3040

3141
useEffect(() => {
3242
const storedEmail = localStorage.getItem('email');
@@ -37,7 +47,8 @@ export function useOnboardingFunnel() {
3747

3848
const requestFCMToken = useCallback(async (): Promise<string | null> => {
3949
try {
40-
const app = getApps().length > 0 ? getApp() : initializeApp(firebaseConfig);
50+
const app =
51+
getApps().length > 0 ? getApp() : initializeApp(firebaseConfig);
4152
const messaging = getMessaging(app);
4253
const permission = await Notification.requestPermission();
4354
registerServiceWorker();
@@ -149,8 +160,10 @@ export function useOnboardingFunnel() {
149160
direction,
150161
alarmSelected,
151162
jobShareAgree,
163+
selectedJob,
152164
setAlarmSelected,
153165
setJobShareAgree,
166+
setSelectedJob,
154167
nextStep,
155168
prevStep,
156169
};

0 commit comments

Comments
 (0)