Skip to content

Commit

Permalink
Merge pull request kakao-tech-campus-2nd-step3#156 from YIMSEBIN/Weekly
Browse files Browse the repository at this point in the history
Feat: 테스트코드 구현 및 POST 확인 모달창 구현
  • Loading branch information
YIMSEBIN authored Nov 14, 2024
2 parents 98e7829 + 7b947d0 commit 6e19f8b
Show file tree
Hide file tree
Showing 18 changed files with 1,526 additions and 795 deletions.
25 changes: 23 additions & 2 deletions src/assets/translator/Contract/contractData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,19 @@ export const contractData = {
SENTENCE1: '사용자와 근로자는 각자가 근로계약, 취업규칙, 단체협약을 지키고 성실하게 이행하여야 한다.',
SENTENCE2: "이 계약에서 정하지 않은 사항은 '근로기준법'에서 정하는 바에 따른다.",
SIGN: '서명하기',
SUBMIT_CHECK: '정말 제출하시겠습니까?',
SUBMIT: '제출하기',
ERROR: '* 근로계약서에 서명해주세요!',
ERROR: {
WORKING_PLACE: '근무장소를 작성해주세요.',
RESPONSIBILITIES: '상세 업무 내용을 작성해주세요.',
WORKING_HOURS: '근로일 및 근로일별 근로시간을 작성해주세요.',
DAY_OFF: '주휴일에 대한 정보를 작성해주세요.',
SALARY: '임금을 작성해주세요.',
ANNUAL_PAID_LEAVE: '연차유급휴가에 대한 정보를 작성해주세요.',
RULE: '취업규칙을 작성해주세요.',
NUMBER: '숫자로 작성해주세요.',
SIGN: '* 근로계약서에 서명해주세요!',
},
},
[Languages.VE]: {
CONTRACT: 'Hợp đồng lao động',
Expand All @@ -29,7 +40,17 @@ export const contractData = {
'Người sử dụng lao động và người lao động cần tuân thủ hợp đồng lao động, quy tắc lao động và thỏa thuận tập thể một cách nghiêm túc.',
SENTENCE2: 'Các điều khoản không được quy định trong hợp đồng này sẽ được điều chỉnh theo "Luật lao động".',
SIGN: 'Ký tên',
SUBMIT_CHECK: 'Bạn có chắc chắn muốn nộp không?',
SUBMIT: 'Gửi đi',
ERROR: '* Vui lòng ký vào hợp đồng lao động!',
ERROR: {
WORKING_PLACE: 'Vui lòng điền nơi làm việc.',
RESPONSIBILITIES: 'Vui lòng mô tả chi tiết công việc.',
WORKING_HOURS: 'Vui lòng ghi rõ ngày làm việc và giờ làm việc hàng ngày.',
DAY_OFF: 'Vui lòng ghi thông tin về ngày nghỉ hàng tuần.',
SALARY: 'Vui lòng điền thông tin lương.',
ANNUAL_PAID_LEAVE: 'Vui lòng ghi thông tin về nghỉ phép có lương hàng năm.',
RULE: 'Vui lòng điền quy tắc làm việc.',
SIGN: '* Vui lòng ký vào hợp đồng lao động!',
},
},
};
34 changes: 34 additions & 0 deletions src/assets/translator/PostNotice/postNoticeData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,23 @@ export const postNoticeData = {
REQUESTED_CAREER: '지원조건',
ELIGIBILITY_CRITERIA: '비자조건',
PREFERRED_CONDITIONS: '우대사항',
ERROR: {
NOTICE_TITLE: '구인글 제목을 입력해주세요.',
COMPANY_NAME: '회사명을 입력해주세요.',
EMPLOYER_NAME: '고용주 이름을 입력해주세요.',
COMPANY_SCALE: '회사 규모를 입력해주세요.',
AREA: '지역을 입력해주세요.',
SALARY: '급여를 입력해주세요.',
MAJOR_BUSINESS: '주요 업무 내용을 입력해주세요.',
WORKDURATION: '근무 기간을 입력해주세요.',
WORKDAYS: '근무 요일을 입력해주세요.',
WORKHOURS: '근무 시간을 입력해주세요.',
WORKTYPE: '고용 형태를 입력해주세요.',
REQUESTED_CAREER: '지원조건을 입력해주세요.',
ELIGIBILITY_CRITERIA: '비자 자격 요건을 입력해주세요.',
PREFERRED_CONDITIONS: '우대사항을 입력해주세요.',
},
SUBMIT_CHECK: '구인글을 등록하시겠습니까?',
SUBMIT: '등록하기',
},
[Languages.VE]: {
Expand All @@ -35,6 +52,23 @@ export const postNoticeData = {
REQUESTED_CAREER: 'Yêu cầu kinh nghiệm',
ELIGIBILITY_CRITERIA: 'Điều kiện về visa',
PREFERRED_CONDITIONS: 'Ưu tiên',
ERROR: {
NOTICE_TITLE: 'Vui lòng nhập tiêu đề bài đăng tuyển dụng.',
COMPANY_NAME: 'Vui lòng nhập tên công ty.',
EMPLOYER_NAME: 'Vui lòng nhập tên nhà tuyển dụng.',
COMPANY_SCALE: 'Vui lòng nhập quy mô công ty.',
AREA: 'Vui lòng nhập khu vực.',
SALARY: 'Vui lòng nhập mức lương.',
MAJOR_BUSINESS: 'Vui lòng nhập nội dung công việc chính.',
WORKDURATION: 'Vui lòng nhập thời gian làm việc.',
WORKDAYS: 'Vui lòng nhập ngày làm việc.',
WORKHOURS: 'Vui lòng nhập giờ làm việc.',
WORKTYPE: 'Vui lòng nhập hình thức làm việc.',
REQUESTED_CAREER: 'Vui lòng nhập điều kiện ứng tuyển.',
ELIGIBILITY_CRITERIA: 'Vui lòng nhập yêu cầu về điều kiện visa.',
PREFERRED_CONDITIONS: 'Vui lòng nhập điều kiện ưu tiên.',
},
SUBMIT_CHECK: 'Bạn có muốn đăng bài tuyển dụng không?',
SUBMIT: 'Đăng ký',
},
};
16 changes: 16 additions & 0 deletions src/assets/translator/RegisterCompany/registerCompanyData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ export const registerCompanyData = {
BRAND: '브랜드',
REVENUE_PERYEAR: '연 평균 매출액',
SUBMIT: '등록하기',
SUBMIT_CHECK: '회사를 등록하시겠습니까?',
ERROR: {
COMPANYNAME: '회사명을 입력해주세요.',
INDUSTRY_OCCUPATION: '산업/직종을 입력해주세요.',
BRAND: '브랜드명을 입력해주세요.',
REVENUE_PERYEAR: '연 매출을 입력해주세요.',
NUMBER: '숫자로 입력해주세요.',
},
},
[Languages.VE]: {
TITLE: 'Đăng ký công ty',
Expand All @@ -18,5 +26,13 @@ export const registerCompanyData = {
BRAND: 'Thương hiệu',
REVENUE_PERYEAR: 'Doanh thu hàng năm',
SUBMIT: 'Đăng ký',
SUBMIT_CHECK: 'Bạn có muốn đăng ký công ty không?',
ERROR: {
COMPANYNAME: 'Vui lòng nhập tên công ty.',
INDUSTRY_OCCUPATION: 'Vui lòng nhập ngành nghề.',
BRAND: 'Vui lòng nhập tên thương hiệu.',
REVENUE_PERYEAR: 'Vui lòng nhập doanh thu hàng năm.',
NUMBER: 'Vui lòng nhập bằng số.',
},
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { fireEvent, screen } from '@testing-library/react';
import { server } from '@/mocks/server';
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
import { renderWithProviders } from '@/__test__/test-utils';
import ContractSection from '../components/ContractSection';
import { useNavigate } from 'react-router-dom';
import ROUTE_PATH from '@/routes/path';

const mockNavigate = vi.fn();
vi.mock('react-router-dom', async () => {
return {
...(await vi.importActual('react-router-dom')),
useNavigate: vi.fn(),
};
});

describe('employeeContract', () => {
beforeAll(() => {
server.listen();
vi.mocked(useNavigate).mockImplementation(() => mockNavigate);
});
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

beforeEach(() => {
vi.clearAllMocks();
});

const renderContract = () => {
return renderWithProviders(<ContractSection />);
};

it('근로계약서 데이터 잘 불려와짐', async () => {
renderContract();

expect(screen.getByText('contract.WORKING_PLACE')).toBeInTheDocument();
expect(screen.getByText('contract.RESPONSIBILITIES')).toBeInTheDocument();
expect(screen.getByText('contract.WORKING_HOURS')).toBeInTheDocument();
expect(screen.getByText('contract.DAY_OFF')).toBeInTheDocument();
expect(screen.getByText('contract.SALARY')).toBeInTheDocument();
expect(screen.getByText('contract.ANNUAL_PAID_LEAVE')).toBeInTheDocument();
expect(screen.getByText('contract.RULE')).toBeInTheDocument();
});

it('서명하지 않고 제출하면 메세지를 띄운다.', async () => {
const { getByRole } = renderContract();

fireEvent.click(getByRole('button', { name: 'contract.SUBMIT' }));

await vi.waitFor(() => {
const errorMessage = screen.getByText('contract.ERROR.SIGN');
expect(errorMessage).toBeInTheDocument();
});
});

it('서명 후 제출한다.', async () => {
const { getByRole } = renderContract();

fireEvent.click(getByRole('button', { name: 'contract.SIGN' }));
fireEvent.click(getByRole('button', { name: 'contract.SUBMIT' }));

await vi.waitFor(() => {
expect(mockNavigate).toHaveBeenCalledWith(ROUTE_PATH.HOME);
});
});
});
153 changes: 153 additions & 0 deletions src/features/contract/EmployeeContract/components/ContractSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import { useState } from 'react';
import { useGetMyContract } from '@/apis/contract/hooks/useGetMyContract';
import { Button, Typo } from '@/components/common';
import ROUTE_PATH from '@/routes/path';
import styled from '@emotion/styled';
import { useTranslation } from 'react-i18next';
import { useNavigate, useParams } from 'react-router-dom';
import { usePostSignEmployeeContract } from '@/apis/contract/hooks/usePostEmployeeSign';

export type ContractResponseData = {
salary: string;
workingHours: string;
dayOff: string;
annualPaidLeave: string;
workingPlace: string;
responsibilities: string;
rule: string;
};

export default function ContractSection() {
const { t } = useTranslation();
const { applyId } = useParams();
const applicationId = Number(applyId);
const { data: contract } = useGetMyContract(applicationId);
const mutation = usePostSignEmployeeContract();
const navigate = useNavigate();
const contractData: ContractResponseData = contract || {};

const [isSigned, setIsSigned] = useState(false);
const [showSignError, setShowSignError] = useState(false);

const handlePostSignEmployeeContract = () => {
if (!isSigned) {
setShowSignError(true);
return;
}
mutation.mutate(
{ applyId: applicationId },
{
onSuccess: () => {
navigate(ROUTE_PATH.HOME);
},
onError: () => {
alert('값이 정상적으로 저장되지 않았습니다.');
},
},
);
};

const handleSign = () => {
setIsSigned(!isSigned);
setShowSignError(false);
};

return (
<>
<InputWrapper>
<InputContainer>
<Typo>{t('contract.WORKING_PLACE')}</Typo>
<Typo>{contractData.workingPlace}</Typo>
</InputContainer>
<InputContainer>
<Typo>{t('contract.RESPONSIBILITIES')}</Typo>
<Typo>{contractData.responsibilities}</Typo>
</InputContainer>
<InputContainer>
<Typo>{t('contract.WORKING_HOURS')}</Typo>
<Typo>{contractData.workingHours}</Typo>
</InputContainer>
<InputContainer>
<Typo>{t('contract.DAY_OFF')}</Typo>
<Typo>{contractData.dayOff}</Typo>
</InputContainer>
<InputContainer>
<Typo>{t('contract.SALARY')}</Typo>
<Typo>{contractData.salary}</Typo>
</InputContainer>
<InputContainer>
<Typo>{t('contract.ANNUAL_PAID_LEAVE')}</Typo>
<Typo>{contractData.annualPaidLeave}</Typo>
</InputContainer>
<InputContainer>
<Typo>{t('contract.RULE')}</Typo>
<Typo>{contractData.rule}</Typo>
</InputContainer>
</InputWrapper>
<Typo element="p" size="16px" style={{ fontWeight: 'bold', marginTop: '24px' }}>
{t('contract.SENTENCE1')}
</Typo>
<Typo element="p" size="16px" style={{ fontWeight: 'bold', marginTop: '24px' }}>
{t('contract.SENTENCE2')}
</Typo>
<ButtonWrapper>
<SignButton design="outlined" isSigned={isSigned} onClick={handleSign}>
<TypoWrapper isSigned={isSigned}>{t('contract.SIGN')}</TypoWrapper>
{isSigned && <CheckIcon></CheckIcon>}
</SignButton>
<Button design="default" onClick={handlePostSignEmployeeContract}>
{t('contract.SUBMIT')}
</Button>
</ButtonWrapper>
{showSignError && !isSigned && <ErrorText>{t('contract.ERROR.SIGN')}</ErrorText>}
</>
);
}

const InputWrapper = styled.div`
margin-top: 28px;
`;

const InputContainer = styled.div`
width: 700px;
display: flex;
align-items: center;
justify-content: space-between;
margin: 24px 0;
`;

const ButtonWrapper = styled.div`
width: 700px;
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 52px;
`;

const SignButton = styled(Button)<{ isSigned: boolean }>`
width: 150px;
background-color: ${({ isSigned }) => (isSigned ? '#3960d7' : 'white')};
color: ${({ isSigned }) => (isSigned ? 'white' : 'black')};
padding: 10px 0;
display: flex;
align-items: center;
transition:
background-color 0.3s,
color 0.3s;
`;

const TypoWrapper = styled.span<{ isSigned: boolean }>`
margin-right: ${({ isSigned }) => (isSigned ? '8px' : '0')};
transition: margin-right 0.3s;
`;

const CheckIcon = styled.span`
transition: opacity 0.3s;
opacity: 1;
`;

const ErrorText = styled.span`
color: red;
font-size: 12px;
margin-top: 8px;
`;
Loading

0 comments on commit 6e19f8b

Please sign in to comment.