Skip to content

Commit

Permalink
Merge branch 'main' into feat/38-manager-announce
Browse files Browse the repository at this point in the history
  • Loading branch information
dxwwan committed Dec 13, 2023
2 parents 9f29fc2 + 2076333 commit 3798b03
Show file tree
Hide file tree
Showing 65 changed files with 1,424 additions and 499 deletions.
61 changes: 61 additions & 0 deletions .github/workflows/deploy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: React App CI/CD
on:
push:
branches:
- main

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x]

steps:
- uses: actions/checkout@v3

- name: Node.js ${{ matrix.node-version }} 사용
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}

- uses: pnpm/action-setup@v2
with:
version: 8
run_install: |
- recursive: true
args: [--frozen-lockfile, --strict-peer-dependencies]
- args: [--global, gulp, prettier, typescript]
- name: env 파일 생성
run: |
touch .env
echo VITE_PUBLIC_API_URL=${{ secrets.VITE_PUBLIC_API_URL }} >> .env
cat .env
- name: 프로젝트 pnpm 빌드
run: |
pnpm build:apply
pnpm build:manager
- name: service-apply 모듈 배포
uses: jakejarvis/s3-sync-action@master
with:
args: --acl public-read --delete
env:
AWS_S3_BUCKET: ${{ secrets.AWS_APPLY_WS_BUCKET_NAME }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
SOURCE_DIR: "dist/service-apply"

- name: service-manager 모듈 배포
uses: jakejarvis/s3-sync-action@master
with:
args: --acl public-read --delete
env:
AWS_S3_BUCKET: ${{ secrets.AWS_MANAGER_WS_BUCKET_NAME }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
SOURCE_DIR: "dist/service-manager"
4 changes: 3 additions & 1 deletion service-apply/src/apis/announce.apis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ export const getAnnounceLast = async () => {
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);
};
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);
};
37 changes: 29 additions & 8 deletions service-apply/src/apis/registration.apis.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,35 @@
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);
};

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);
}
Expand All @@ -36,7 +48,7 @@ export const getRegistration =
email: '',
isLight: false,
phoneNum: '',
sector: [],
sectors: [],
studentNum: '',
name: '',
selectSectorId: -1,
Expand All @@ -45,3 +57,12 @@ export const getRegistration =
}
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);
};
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
61 changes: 61 additions & 0 deletions service-apply/src/components/apply/ApplyCaptchaModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Modal, Txt } from '@quokka/design-system';
import { CaptchaForm } from './CaptchaForm';
import { Spinner } from '../../assets/Spinner';
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 3798b03

Please sign in to comment.