Skip to content

Commit

Permalink
Merge pull request #153 from Team-inglo/fix/99-약관동의
Browse files Browse the repository at this point in the history
feat/99 - 약관동의, 상세조회
  • Loading branch information
MrMirror21 authored Jan 13, 2025
2 parents be8ccc1 + 1debf1a commit 3d32d93
Show file tree
Hide file tree
Showing 9 changed files with 229 additions and 11 deletions.
10 changes: 10 additions & 0 deletions src/api/auth.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
AuthenticationRequest,
AuthenticationResponse,
PolicyResponse,
ReIssueAuthenticationRequest,
SignInRequest,
SignInResponse,
Expand All @@ -15,6 +16,7 @@ import { api } from '@/api/index.ts';
import { apiWithoutAuth } from '@/api/index.ts';
import { RESTYPE } from '@/types/api/common';
import axios from 'axios';
import { TermType } from '@/types/api/users';

/**
* 사용자 로그인을 처리하는 함수
Expand Down Expand Up @@ -149,3 +151,11 @@ export const signUpEmployer = async (
const response = await api.post(`/auth/owners`, signupInfo);
return response.data;
};

// 11.1 약관 상세조회
export const getPolicy = async (
termType: TermType,
): Promise<RESTYPE<PolicyResponse>> => {
const response = await api.get(`/terms/${termType}/details`);
return response.data;
};
27 changes: 24 additions & 3 deletions src/components/Employer/Signup/AgreeModalInner.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import CheckIcon from '@/assets/icons/CheckOfBoxIcon.svg?react';
import ArrowIcon from '@/assets/icons/RightArrowIcon.svg?react';
import Button from '@/components/Common/Button';
import { TermType } from '@/types/api/users';
import { Dispatch, SetStateAction, useState } from 'react';

type AgreeModalInnerProps = {
setMarketingAllowed: (value: boolean) => void;
onPolicyPreview: (termType: TermType) => void;
onNext: Dispatch<SetStateAction<boolean>>;
accountType: 'USER' | 'EMPLOYER';
};

const AgreeModalInner = ({
setMarketingAllowed,
onPolicyPreview,
onNext,
accountType,
}: AgreeModalInnerProps) => {
const [essentialAgreeList, setEssentialAgreeList] = useState<boolean[]>([
false,
Expand Down Expand Up @@ -76,7 +81,17 @@ const AgreeModalInner = ({
<div className="w-full flex items-center">
(필수) 서비스 이용약관동의
</div>
<ArrowIcon />
<div
onClick={() =>
onPolicyPreview(
accountType === 'USER'
? TermType.PERSONAL_SERVICE_TERMS
: TermType.ENTERPRISE_SERVICE_TERMS,
)
}
>
<ArrowIcon />
</div>
</div>
</div>
<div className="w-full flex items-center justify-center gap-3">
Expand All @@ -92,7 +107,9 @@ const AgreeModalInner = ({
<div className="w-full flex items-center">
(필수) 개인정보 수집 및 이용 동의
</div>
<ArrowIcon />
<div onClick={() => onPolicyPreview(TermType.PRIVACY_POLICY)}>
<ArrowIcon />
</div>
</div>
</div>
<div className="w-full flex items-center justify-center gap-3">
Expand All @@ -108,7 +125,11 @@ const AgreeModalInner = ({
<div className="w-full flex items-center">
(필수) 위치정보 이용동의
</div>
<ArrowIcon />
<div
onClick={() => onPolicyPreview(TermType.LOCATION_BASED_TERMS)}
>
<ArrowIcon />
</div>
</div>
</div>
</div>
Expand Down
66 changes: 66 additions & 0 deletions src/components/Information/PolicyViewer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import BaseHeader from '../Common/Header/BaseHeader';

const PolicyViewer = ({
content,
onBack,
}: {
content: string;
onBack: () => void;
}) => {
// 2. iframe 사용
const renderWithIframe = () => {
const responsiveContent = content.replace(
'</head>',
`<style>
:root {
/* 기본 폰트 크기 설정 */
font-size: calc(12px + 0.4vw);
}
/* 디바이스 크기별 세부 조정 */
@media screen and (max-width: 320px) {
:root { font-size: 9px; }
}
@media screen and (min-width: 768px) {
:root { font-size: 16px; }
}
@media screen and (min-width: 1024px) {
:root { font-size: 18px; }
}
/* 컨텐츠별 상대적 크기 설정 */
body { font-size: 1rem; }
.version-info { font-size: 0.875rem; }
h1 { font-size: 1.5rem; }
h2 { font-size: 1.25rem; }
.box { font-size: 1rem; }
</style>
</head>`,
);

return (
<iframe
className="w-full h-screen border-0"
srcDoc={responsiveContent}
title="HTML Content"
sandbox="allow-scripts"
/>
);
};
return (
<div className="fixed top-0 w-full h-full space-y-8 z-40 bg-white">
<div>
<BaseHeader
hasBackButton
hasMenuButton={false}
title="서비스 이용약관 동의"
onClickBackButton={onBack}
/>
<div className="w-full border-t border-[#dcdcdc]" />
{renderWithIframe()}
</div>
</div>
);
};

export default PolicyViewer;
30 changes: 29 additions & 1 deletion src/hooks/api/useAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ import {
tempSignUp,
withdraw,
signUpEmployer,
getPolicy,
} from '@/api/auth';
import {
AuthenticationResponse,
PolicyResponse,
SignInResponse,
SignUpResponse,
TempSignUpResponse,
Expand All @@ -27,11 +29,16 @@ import {
setTemporaryToken,
} from '@/utils/auth';
import { useNavigate } from 'react-router-dom';
import { useMutation, useQuery } from '@tanstack/react-query';
import {
useMutation,
UseMutationOptions,
useQuery,
} from '@tanstack/react-query';
import { useUserInfoforSigninStore } from '@/store/signup';
import { useEmailTryCountStore } from '@/store/signup';
import { RESTYPE } from '@/types/api/common';
import { clearAllStore } from '@/utils/clearAllStore';
import { TermType } from '@/types/api/users';

/**
* 로그인 프로세스를 처리하는 커스텀 훅
Expand Down Expand Up @@ -262,3 +269,24 @@ export const useWithdraw = () => {
},
});
};

// 11.1 약관 종류별 상세 조회하기
export const useGetPolicy = (
options?: UseMutationOptions<
RESTYPE<PolicyResponse>,
Error,
TermType // mutationFn의 parameter 타입
>,
) => {
return useMutation({
mutationFn: getPolicy,
onSuccess: (data, variables, context) => {
options?.onSuccess?.(data, variables, context);
},
onError: (error, variables, context) => {
console.error('약관 조회 중 에러 발생:', error);
options?.onError?.(error, variables, context);
},
...options,
});
};
44 changes: 43 additions & 1 deletion src/pages/Employer/signup/EmployerSignupInfoPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ import BottomSheetLayout from '@/components/Common/BottomSheetLayout';
import Button from '@/components/Common/Button';
import CompleteModal from '@/components/Common/CompleteModal';
import BaseHeader from '@/components/Common/Header/BaseHeader';
import LoadingItem from '@/components/Common/LoadingItem';
import AgreeModalInner from '@/components/Employer/Signup/AgreeModalInner';
import InformationInputSection from '@/components/Employer/Signup/InformationInputSection';
import { useSignupEmployer } from '@/hooks/api/useAuth';
import PolicyViewer from '@/components/Information/PolicyViewer';
import { useGetPolicy, useSignupEmployer } from '@/hooks/api/useAuth';
import {
EmployerRegistrationRequestBody,
initialEmployerRegistration,
} from '@/types/api/employ';
import { TermType } from '@/types/api/users';
import { getTemporaryToken } from '@/utils/auth';
import { isValidEmployerRegistration } from '@/utils/signup';
import { useEffect, useState } from 'react';
Expand All @@ -21,7 +24,22 @@ const EmployerSignupInfoPage = () => {
const [logoFile, setLogoFile] = useState<File | undefined>(undefined);
const [devIsModal, setDevIsModal] = useState(false);
const [isAgreeModal, setIsAgreeModal] = useState(true);
const [isPolicyPreview, setIsPolicyPreview] = useState(false);
const [policy, setPolicy] = useState('');
const [isValid, setIsValid] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const { mutate: getPolicy } = useGetPolicy({
onSuccess: (data) => {
setPolicy(data.data.content);
setIsPolicyPreview(true);
},
onMutate: () => {
setIsLoading(true);
},
onSettled: () => {
setIsLoading(false);
},
});
const { mutate } = useSignupEmployer(() => setDevIsModal(true));
const navigate = useNavigate();

Expand All @@ -38,6 +56,11 @@ const EmployerSignupInfoPage = () => {
const requestData = {
...newEmployData,
temporary_token: String(getTemporaryToken()), // temporary_token 추가
term_types: [
TermType.ENTERPRISE_SERVICE_TERMS,
TermType.LOCATION_BASED_TERMS,
TermType.PRIVACY_POLICY,
],
};

// 이미지 파일이 있는 경우에만 추가
Expand Down Expand Up @@ -112,10 +135,29 @@ const EmployerSignupInfoPage = () => {
marketing_allowed: value,
})
}
onPolicyPreview={(policy: TermType) => {
getPolicy(policy);
}}
onNext={setIsAgreeModal}
accountType="EMPLOYER"
/>
</BottomSheetLayout>
)}
{isPolicyPreview === true && (
<PolicyViewer
content={policy}
onBack={() => setIsPolicyPreview(false)}
/>
)}
{isLoading && (
<div
className="fixed inset-0 z-50 flex items-center justify-center bg-white bg-opacity-50 overflow-hidden"
style={{ touchAction: 'none' }}
onClick={(e) => e.preventDefault()}
>
<LoadingItem />
</div>
)}
</div>
);
};
Expand Down
45 changes: 44 additions & 1 deletion src/pages/Information/InformationPage.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import BottomSheetLayout from '@/components/Common/BottomSheetLayout';
import CompleteModal from '@/components/Common/CompleteModal';
import LoadingItem from '@/components/Common/LoadingItem';
import AgreeModalInner from '@/components/Employer/Signup/AgreeModalInner';
import AddressStep from '@/components/Information/AddressStep';
import InformationStep from '@/components/Information/InformationStep';
import LanguageStep from '@/components/Information/LanguageStep';
import PolicyViewer from '@/components/Information/PolicyViewer';
import StepIndicator from '@/components/Information/StepIndicator';
import { useSignUp } from '@/hooks/api/useAuth';
import { useGetPolicy, useSignUp } from '@/hooks/api/useAuth';
//import { useSignUp } from '@/hooks/api/useAuth';
import {
initialUserInfoRequestBody,
Language,
TermType,
UserInfoRequestBody,
} from '@/types/api/users';
import { getTemporaryToken } from '@/utils/auth';
Expand All @@ -23,9 +26,24 @@ const InformationPage = () => {
initialUserInfoRequestBody,
);
const [isAgreeModal, setIsAgreeModal] = useState(true);
const [isPolicyPreview, setIsPolicyPreview] = useState(false);
const [policy, setPolicy] = useState('');
const [devIsModal, setDevIsModal] = useState(false);
const [marketingAllowed, setMarketAllowed] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const { mutate } = useSignUp(() => setDevIsModal(true));
const { mutate: getPolicy } = useGetPolicy({
onSuccess: (data) => {
setPolicy(data.data.content);
setIsPolicyPreview(true);
},
onMutate: () => {
setIsLoading(true);
},
onSettled: () => {
setIsLoading(false);
},
});
const navigate = useNavigate();

// 다음 step으로 넘어갈 때 호출되며, 각 step에서 입력한 정보를 userInfo에 저장, 다음 step으로 이동한다.
Expand All @@ -35,12 +53,18 @@ const InformationPage = () => {
};
// 최종 완료 시 호출, 서버 api 호출 및 완료 modal 표시
const handleSubmit = (language: Language) => {
const termTypes = [
TermType.PERSONAL_SERVICE_TERMS,
TermType.LOCATION_BASED_TERMS,
TermType.PRIVACY_POLICY,
];
mutate({
...userInfo,
marketing_allowed: marketingAllowed,
notification_allowed: false,
temporary_token: String(getTemporaryToken()),
language: language,
term_types: termTypes,
});
};
return (
Expand Down Expand Up @@ -81,10 +105,29 @@ const InformationPage = () => {
>
<AgreeModalInner
setMarketingAllowed={(value: boolean) => setMarketAllowed(value)}
onPolicyPreview={(policy: TermType) => {
getPolicy(policy);
}}
onNext={setIsAgreeModal}
accountType="USER"
/>
</BottomSheetLayout>
)}
{isPolicyPreview === true && (
<PolicyViewer
content={policy}
onBack={() => setIsPolicyPreview(false)}
/>
)}
{isLoading && (
<div
className="fixed inset-0 z-50 flex items-center justify-center bg-white bg-opacity-50 overflow-hidden"
style={{ touchAction: 'none' }}
onClick={(e) => e.preventDefault()}
>
<LoadingItem />
</div>
)}
</div>
);
};
Expand Down
Loading

0 comments on commit 3d32d93

Please sign in to comment.