Skip to content

Commit 9065b7d

Browse files
authored
Merge pull request #242 from YAPP-Github/develop
[RELEASE] 1.4.5
2 parents 3116838 + d7d73ce commit 9065b7d

43 files changed

Lines changed: 675 additions & 458 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.husky/pre-push

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pnpm lint

sentry.edge.config.ts

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,12 @@
1-
// This file configures the initialization of Sentry for edge features (middleware, edge routes, and so on).
2-
// The config you add here will be used whenever one of the edge features is loaded.
3-
// Note that this config is unrelated to the Vercel Edge Runtime and is also required when running locally.
4-
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
5-
61
import * as Sentry from '@sentry/nextjs';
72

8-
Sentry.init({
9-
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
10-
11-
// Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control.
12-
tracesSampleRate: 1,
13-
14-
// Enable logs to be sent to Sentry
15-
enableLogs: true,
3+
const isProductionDomain = process.env.VERCEL_ENV === 'production';
164

17-
// Setting this option to true will print useful information to the console while you're setting up Sentry.
18-
debug: false,
19-
});
5+
if (isProductionDomain) {
6+
Sentry.init({
7+
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
8+
tracesSampleRate: 1,
9+
enableLogs: true,
10+
debug: false,
11+
});
12+
}

sentry.server.config.ts

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,12 @@
1-
// This file configures the initialization of Sentry on the server.
2-
// The config you add here will be used whenever the server handles a request.
3-
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
4-
51
import * as Sentry from '@sentry/nextjs';
62

7-
Sentry.init({
8-
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
9-
10-
// Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control.
11-
tracesSampleRate: 1,
12-
13-
// Enable logs to be sent to Sentry
14-
enableLogs: true,
3+
const isProductionDomain = process.env.VERCEL_ENV === 'production';
154

16-
// Setting this option to true will print useful information to the console while you're setting up Sentry.
17-
debug: false,
18-
});
5+
if (isProductionDomain) {
6+
Sentry.init({
7+
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
8+
tracesSampleRate: 1,
9+
enableLogs: true,
10+
debug: false,
11+
});
12+
}

src/app/home/hooks/useUserInfo.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,39 @@
11
'use client';
22

3-
import { useSession } from 'next-auth/react';
3+
import { usePathname } from 'next/navigation';
4+
import { signOut, useSession } from 'next-auth/react';
5+
import { useEffect, useRef } from 'react';
46

57
import useParticipantInfoQuery from './useParticipantInfoQuery';
68
import useResearcherInfoQuery from './useResearcherInfoQuery';
79

810
import { ROLE } from '@/constants/config';
11+
import { isUnauthorizedUser } from '@/lib/auth-utils';
912

1013
const useUserInfo = () => {
1114
const { data: session, status } = useSession();
15+
const pathname = usePathname();
16+
const isExecuted = useRef(false);
1217

1318
const role = session?.role;
1419
const isSessionReady = status !== 'loading';
15-
const isParticipant = isSessionReady && role === ROLE.participant;
16-
const isResearcher = isSessionReady && role === ROLE.researcher;
20+
const isParticipant = isSessionReady && !isUnauthorizedUser(session) && role === ROLE.participant;
21+
const isResearcher = isSessionReady && !isUnauthorizedUser(session) && role === ROLE.researcher;
1722

1823
const participantQuery = useParticipantInfoQuery({ enabled: isParticipant });
1924
const researcherQuery = useResearcherInfoQuery({ enabled: isResearcher });
2025

2126
const isLoading = !isSessionReady || participantQuery.isLoading || researcherQuery.isLoading;
2227

28+
useEffect(() => {
29+
if (isExecuted.current) return;
30+
31+
if (isUnauthorizedUser(session) && !pathname.startsWith('/join')) {
32+
signOut({ redirect: false });
33+
isExecuted.current = true;
34+
}
35+
}, [session, pathname]);
36+
2337
return {
2438
userInfo: isParticipant ? participantQuery.data : researcherQuery.data,
2539
isLoading,

src/app/home/page.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import type { ExperimentPostListFilters, ExperimentPostResponse } from '@/apis/p
1111
import DefaultLayout from '@/components/layout/DefaultLayout/DefaultLayout';
1212
import { queryKey } from '@/constants/queryKey';
1313
import { API_URL } from '@/constants/url';
14-
import { authOptions } from '@/lib/auth-utils';
14+
import { authOptions, isUnauthorizedUser } from '@/lib/auth-utils';
1515
import { getQueryClient } from '@/lib/getQueryClient';
1616
import { URLFilterSchema } from '@/schema/filter/URLFilterSchema';
1717
import { getQueryParamsToString } from '@/utils/getQueryParamsString';
@@ -31,11 +31,12 @@ export default async function Home({ searchParams }: HomePageProps) {
3131
const fetchClient = createSSRFetchClient(session?.accessToken);
3232
const hasQueryParams = Object.keys(searchParams).length > 0;
3333

34-
const initialUserInfo = session?.role
35-
? await fetchClient.get<ParticipantResponse | ResearcherResponse>(
36-
API_URL.me(session.role.toLowerCase()),
37-
)
38-
: null;
34+
const initialUserInfo =
35+
!isUnauthorizedUser(session) && session?.role
36+
? await fetchClient.get<ParticipantResponse | ResearcherResponse>(
37+
API_URL.me(session.role.toLowerCase()),
38+
)
39+
: null;
3940

4041
const initialGender =
4142
initialUserInfo && isParticipantInfo(initialUserInfo) ? initialUserInfo.gender : undefined;
@@ -63,7 +64,7 @@ export default async function Home({ searchParams }: HomePageProps) {
6364
},
6465
);
6566

66-
if (session?.role) {
67+
if (!isUnauthorizedUser(session) && session?.role) {
6768
await queryClient.prefetchQuery({
6869
queryKey: queryKey.userInfo(session.role),
6970
queryFn: () => Promise.resolve(initialUserInfo),

src/app/join/JoinPage.css.ts

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,6 @@ export const contentContainer = style({
4545
width: '100%',
4646
});
4747

48-
export const titleContainer = style({
49-
display: 'flex',
50-
justifyContent: 'space-between',
51-
alignItems: 'center',
52-
});
53-
5448
export const joinContentContainer = style({
5549
backgroundColor: colors.field02,
5650
width: '100%',
@@ -61,26 +55,6 @@ export const joinContentContainer = style({
6155
padding: '3.2rem 4rem',
6256
});
6357

64-
export const joinTitle = style({
65-
...fonts.title.medium.SB20,
66-
color: colors.text06,
67-
});
68-
69-
export const progressBarContainer = style({
70-
width: '8rem',
71-
height: '0.6rem',
72-
backgroundColor: colors.field03,
73-
borderRadius: '0.6rem',
74-
});
75-
76-
export const progressBarFill = style({
77-
width: 'var(--progress-width)',
78-
height: '100%',
79-
backgroundColor: colors.primaryMint,
80-
borderRadius: '0.6rem',
81-
transition: 'width 1s',
82-
});
83-
8458
export const joinForm = style({
8559
width: '100%',
8660
height: '100%',

src/app/join/JoinPage.types.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
// TODO: 타입 좁히기
2-
// birthDate: '2025-01-22';
1+
import { STEP } from './JoinPage.constants';
2+
33
// region 타입, area 타입
44
export type Gender = 'MALE' | 'FEMALE' | 'ALL';
55
export type MatchType = 'ONLINE' | 'OFFLINE' | 'ALL';
6+
export type StepType = (typeof STEP)[keyof typeof STEP];
67

78
export interface ServiceAgreeCheck {
89
isTermOfService: boolean;
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
'use client';
2+
3+
import { PropsWithChildren } from 'react';
4+
import { useFormContext } from 'react-hook-form';
5+
6+
import ConfirmModal from '@/components/Modal/ConfirmModal/ConfirmModal';
7+
import useLeaveConfirmModal from '@/hooks/useLeaveConfirmModal';
8+
import { colors } from '@/styles/colors';
9+
10+
const FormGuard = ({ children }: PropsWithChildren) => {
11+
const { formState } = useFormContext();
12+
const { isLeaveConfirmModalOpen, handleConfirmLeave, handleCancelLeave } = useLeaveConfirmModal({
13+
isUserInputDirty: formState.isDirty,
14+
});
15+
16+
return (
17+
<>
18+
{children}
19+
<ConfirmModal
20+
isOpen={isLeaveConfirmModalOpen}
21+
onOpenChange={(open) => {
22+
if (!open) {
23+
handleCancelLeave();
24+
}
25+
}}
26+
confirmTitle="페이지에서 나가시겠어요?"
27+
descriptionText="입력한 내용은 따로 저장되지 않아요"
28+
cancelText="취소"
29+
confirmText="나가기"
30+
confirmButtonColor={colors.field09}
31+
onConfirm={() => handleConfirmLeave({ goHome: true })}
32+
/>
33+
</>
34+
);
35+
};
36+
37+
export default FormGuard;
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
'use client';
2+
3+
import { PropsWithChildren, useEffect } from 'react';
4+
5+
import useFunnel from '../../hooks/useFunnel';
6+
import { STEP } from '../../JoinPage.constants';
7+
import { joinLayout } from '../../JoinPage.css';
8+
import { JoinLayout } from '../JoinLayout/JoinLayout';
9+
10+
import { startRecording } from '@/lib/mixpanelClient';
11+
12+
interface FunnelLayoutProps {
13+
title: string;
14+
}
15+
16+
const FunnelLayout = ({ children, title }: PropsWithChildren<FunnelLayoutProps>) => {
17+
const { step } = useFunnel();
18+
19+
useEffect(() => {
20+
startRecording();
21+
}, []);
22+
23+
if (step === STEP.success) {
24+
return <JoinLayout.Container>{children}</JoinLayout.Container>;
25+
}
26+
27+
return (
28+
<main className={joinLayout}>
29+
<JoinLayout.Logo />
30+
<JoinLayout.Container>
31+
<JoinLayout.Header title={title} />
32+
{children}
33+
</JoinLayout.Container>
34+
</main>
35+
);
36+
};
37+
38+
export default FunnelLayout;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { PropsWithChildren } from 'react';
2+
3+
import { contentContainer } from '../../JoinPage.css';
4+
import FormGuard from '../FormGuard/FormGuard';
5+
import JoinTitleSection from '../JoinTitleSection/JoinTitleSection';
6+
7+
import Logo from '@/components/Logo/Logo';
8+
9+
interface JoinLayoutTitleProps {
10+
title: string;
11+
}
12+
13+
export const JoinLayout = {
14+
Logo: () => <Logo />,
15+
Header: ({ title }: JoinLayoutTitleProps) => <JoinTitleSection title={title} />,
16+
Container: ({ children }: PropsWithChildren) => (
17+
<div className={contentContainer}>{children}</div>
18+
),
19+
FormGuard: ({ children }: PropsWithChildren) => <FormGuard>{children}</FormGuard>,
20+
};

0 commit comments

Comments
 (0)