Skip to content

Commit

Permalink
Merge pull request #49 from JNU-Parking-Ticket-Project/feat/39-apply-all
Browse files Browse the repository at this point in the history
[FE] feat(apply): API 스펙 변경에 대응하며 자동신청 방지 모달창을 구현한다
  • Loading branch information
j8won authored Dec 13, 2023
2 parents 33d3731 + 9c5d206 commit 31c5cac
Show file tree
Hide file tree
Showing 28 changed files with 664 additions and 248 deletions.
10 changes: 6 additions & 4 deletions service-apply/src/apis/announce.apis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const getAllAnnounce = async (page: number) => {
if (isErrorResponse(response)) {
return new AllAnnounce({ announces: [], lastPage: 0, nextPage: 0 });
}
return new AllAnnounce(response.data);
return new AllAnnounce(response);
};

export const getAnnounceLast = async () => {
Expand All @@ -18,13 +18,15 @@ export const getAnnounceLast = async () => {
announceTitle: '게시글이 없습니다',
});
}
return new LastAnnounce(response.data);
return new LastAnnounce(response);
};

export const getAnnounceById = async (announceId: number) => {
const response = await https.get(`/v1/announce/${announceId}`);
if (isErrorResponse(response)) {
throw new Error(response.reason);
throw new Error('게시글을 조회할 수 없습니다');
// TODO: response dto에 status와 reason을 추가해야 아래 로직 가능
// throw new Error(response.reason);
}
return new Announce(response.data);
return new Announce(response);
};
73 changes: 62 additions & 11 deletions service-apply/src/apis/dtos/registration.dtos.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,73 @@
export interface RegistrationRequestProps {
email: string;
export interface TemporarySaveRequestProps {
name: string;
studentNumber: string;
affiliation: string;
carNumber: string;
isLightCar: boolean;
phoneNumber: string;
selectSectorId: number;
isRegistration: boolean;
}

export class RegistrationRequest {
export class TemporarySaveRequest {
name: string;
studentNum: string;
affiliation: string;
carNum: string;
isLight: boolean;
phoneNum: string;
selectSectorId: number;
isRegistration: boolean;

constructor({
name,
studentNumber,
affiliation,
carNumber,
isLightCar,
phoneNumber,
selectSectorId,
isRegistration,
}: RegistrationRequestProps) {
}: TemporarySaveRequestProps) {
this.name = name;
this.studentNum = studentNumber;
this.affiliation = affiliation;
this.carNum = carNumber;
this.isLight = isLightCar;
this.phoneNum = phoneNumber;
this.selectSectorId = selectSectorId;
this.isRegistration = isRegistration;
}
}

export interface RegistrationRequestProps extends TemporarySaveRequestProps {
captchaPendingCode: string;
captchaAnswer: string;
}

export class RegistrationRequest extends TemporarySaveRequest {
captchaCode: string;
captchaAnswer: string;

constructor({
name,
studentNumber,
affiliation,
carNumber,
isLightCar,
phoneNumber,
selectSectorId,
captchaPendingCode,
captchaAnswer,
}: RegistrationRequestProps) {
super({
name,
studentNumber,
affiliation,
carNumber,
isLightCar,
phoneNumber,
selectSectorId,
});

this.captchaCode = captchaPendingCode;
this.captchaAnswer = captchaAnswer;
}
}

Expand All @@ -50,7 +85,7 @@ interface RegistrationResponseProps {
carNum: string;
isLight: boolean;
phoneNum: string;
sector: {
sectors: {
sectorId: number;
sectorNum: string;
sectorName: string;
Expand Down Expand Up @@ -79,7 +114,7 @@ export class RegistrationOptionsResponse {
isLight,
name,
phoneNum,
sector,
sectors,
studentNum,
selectSectorId,
affiliation,
Expand All @@ -89,9 +124,25 @@ export class RegistrationOptionsResponse {
this.isCompact = isLight;
this.studentName = name;
this.phoneNumber = phoneNum;
this.sector = sector;
this.sector = sectors;
this.studentNumber = studentNum;
this.selectSectorId = selectSectorId;
this.affiliation = affiliation;
}
}

export class CaptchaResponse {
captchaCode: string;
captchaImageUrl: string;

constructor({
captchaCode,
captchaImageUrl,
}: {
captchaCode: string;
captchaImageUrl: string;
}) {
this.captchaCode = captchaCode;
this.captchaImageUrl = captchaImageUrl;
}
}
2 changes: 1 addition & 1 deletion service-apply/src/apis/notice.apis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ export const getNotice = async () => {
if (isErrorResponse(response)) {
return new Notice({ noticeContent: '' });
}
return new Notice(response.data);
return new Notice(response);
};
41 changes: 31 additions & 10 deletions service-apply/src/apis/registration.apis.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,39 @@
import { https } from '../functions/https';
import {
CaptchaResponse,
RegistrationOptionsResponse,
RegistrationRequest,
RegistrationResponse,
TemporarySaveRequest,
} from './dtos/registration.dtos';
import { isErrorResponse } from './dtos/response.dtos';
import { reissueToken } from './user.apis';

export const postRegistration = async (
registration: RegistrationRequest,
data: RegistrationRequest,
): Promise<RegistrationResponse> => {
const { isRegistration, ...rest } = registration;
const response = await https.post(
`/v1/registration/${registration.isRegistration}`,
rest,
);
const response = await https.post('/v1/registration', data);
if (isErrorResponse(response)) {
if (response.status === 401 || response.status === 403) {
return reissueToken(() => postRegistration(registration));
return reissueToken(() => postRegistration(data));
}
console.log(response.reason);
throw new Error(response.reason);
}
return new RegistrationResponse(response.data);
return new RegistrationResponse(response);
};

export const postTemporarySave = async (
data: TemporarySaveRequest,
): Promise<RegistrationResponse> => {
const response = await https.post('/v1/registration/temporary', data);
if (isErrorResponse(response)) {
if (response.status === 401 || response.status === 403) {
return reissueToken(() => postTemporarySave(data));
}
throw new Error(response.reason);
}
return new RegistrationResponse(response);
};

export const getRegistration =
Expand All @@ -36,12 +48,21 @@ export const getRegistration =
email: '',
isLight: false,
phoneNum: '',
sector: [],
sectors: [],
studentNum: '',
name: '',
selectSectorId: -1,
affiliation: '',
});
}
return new RegistrationOptionsResponse(response.data);
return new RegistrationOptionsResponse(response);
};

export const getCaptcha = async () => {
const response = await https.get('/v1/captcha');
if (isErrorResponse(response)) {
throw new Error('자동 신청 방지 이미지 조회를 실패했습니다.');
}

return new CaptchaResponse(response);
};
6 changes: 3 additions & 3 deletions service-apply/src/apis/user.apis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const postLogin = async (data: UserLoginRequest) => {
if (isErrorResponse(response)) {
throw new Error(response.reason);
}
return new UserToken(response.data);
return new UserToken(response);
};

export interface PasswordFindRequest {
Expand All @@ -25,7 +25,7 @@ export const postPasswordFind = async ({ email }: PasswordFindRequest) => {
if (isErrorResponse(response)) {
throw new Error(response.reason);
}
return new PasswordFind(response.data);
return new PasswordFind(response);
};

export interface PasswordResetRequest {
Expand All @@ -43,7 +43,7 @@ export const postPasswordReset = async ({
if (isErrorResponse(response)) {
throw new Error(response.reason);
}
return new PasswordReset(response.data);
return new PasswordReset(response);
};

export const reissueToken = async <T>(retryCallback: () => T): Promise<T> => {
Expand Down
12 changes: 12 additions & 0 deletions service-apply/src/assets/Spinner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import './spinner.css';

export const Spinner = () => {
return (
<div className="lds-ring">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
);
};
40 changes: 40 additions & 0 deletions service-apply/src/assets/spinner.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
.lds-ring {
width: 100%;
height: 120px;
position: relative;
background-color: white;
pointer-events: none;
}
.lds-ring div {
box-sizing: border-box;
display: block;
position: absolute;
left: calc(50% - 40px);
top: calc(50% - 40px);
transform: translate(-50%, -50%);
width: 80px;
height: 80px;
margin: 8px;
border: 8px solid black;
border-radius: 50%;
animation: lds-ring 1.2s 1s cubic-bezier(0.5, 0, 0.5, 1) infinite;
border-color: black transparent transparent transparent;
pointer-events: none;
}
.lds-ring div:nth-child(1) {
animation-delay: -0.45s;
}
.lds-ring div:nth-child(2) {
animation-delay: -0.3s;
}
.lds-ring div:nth-child(3) {
animation-delay: -0.15s;
}
@keyframes lds-ring {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const AnnouncementListItem = ({

return (
<Link to={`/announcement/${announceId}`}>
<div className="border-b border-black flex justify-between hover:bg-gray-100 p-3">
<div className="border-b flex justify-between hover:bg-gray-100 p-3">
<Txt>{announceTitle}</Txt>
<Txt className="w-24 text-center">{`${year}-${month}-${day}`}</Txt>
</div>
Expand Down
63 changes: 63 additions & 0 deletions service-apply/src/components/apply/ApplyCaptchaModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Modal, Txt } from '@quokka/design-system';
import { Suspense, useState } from 'react';
import { CaptchaForm } from './CaptchaForm';
import { Spinner } from '../../assets/Spinner';
import ErrorBoundary from '../common/ErrorBoundray';
import { useCaptchaForm } from '../../hooks/apply/useCaptchaForm';

interface ApplyCaptchaModalProps {
isOpen: boolean;
onRequestClose: () => void;
}

export const ApplyCaptchaModal = ({
isOpen,
onRequestClose,
}: ApplyCaptchaModalProps) => {
const { isLoading, input, handleInput, captchaImageUrl, handleSubmit } =
useCaptchaForm({
closeModal: onRequestClose,
});

return (
<Modal
className="captcha-modal"
isOpen={isOpen}
onRequestClose={onRequestClose}
overLayCss={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}
contentCss={{
padding: '2rem',
width: '80%',
maxWidth: '44rem',
backgroundColor: 'white',
boxShadow: '0px 0px 4px 4px rgba(17, 12, 34, 0.10)',
borderRadius: '1rem',
}}
>
{isLoading ? (
<div className="flex flex-col justify-center align-center">
<Txt size="h6" className="text-center">
신청 접수 중입니다. 잠시만 기다려주세요.
</Txt>
<Txt size="h6" className="text-center">
새로고침 시 신청이 취소됩니다.
</Txt>
<div className="w-full">
<Spinner />
</div>
</div>
) : (
<CaptchaForm
codeInput={input}
handleCodeInput={handleInput}
captchaImageUrl={captchaImageUrl}
handleSubmit={handleSubmit}
/>
)}
</Modal>
);
};
Loading

0 comments on commit 31c5cac

Please sign in to comment.