Skip to content

Commit

Permalink
Merge pull request #99 from KimJi-An/feat/#88
Browse files Browse the repository at this point in the history
Feat/#88, #95, #96 ๊ตฌ์ธ๊ธ€ ๋งˆ๊ฐ API ์—ฐ๋™, ๋ฒˆ์—ญ ๋ฐ์ดํ„ฐ ์ž‘์„ฑ ๋ฐ ์ฝ”๋“œ ๋ฆฌ๋ทฐ ๋ฆฌํŒฉํ† ๋ง
  • Loading branch information
YIMSEBIN authored Nov 5, 2024
2 parents 521ed2c + ad6be2b commit b6e44a3
Show file tree
Hide file tree
Showing 61 changed files with 1,002 additions and 2,017 deletions.
2,281 changes: 492 additions & 1,789 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"buffer": "^6.0.3",
"csstype": "^3.1.3",
"jquery": "^3.7.1",
"prop-types": "^15.8.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-error-boundary": "^4.1.2",
Expand Down
3 changes: 3 additions & 0 deletions src/apis/apiPath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const APIPath = {
registerCompany: `${BASE_URL}/company`,
apply: '/api/application/',
recruitmentsDetail: '/api/recruitments/:postId',
closeRecruitment: `${BASE_URL}/recruitment/hiringClose/:recruitmentId`,
};

export const getDynamicAPIPath = {
Expand All @@ -26,4 +27,6 @@ export const getDynamicAPIPath = {
APIPath.getMyApplicants.replace(':recruitmentId', recruitmentId.toString()),
getForeigner: (userId: number) => APIPath.getForeigner.replace(':userId', userId.toString()),
recruitmentsDetail: (postId: string) => APIPath.recruitmentsDetail.replace(':postId', postId.toString()),
closeRecruitment: (recruitmentId: number) =>
APIPath.closeRecruitment.replace(':recruitmentId', recruitmentId.toString()),
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export const recruitment = {
recruitmentId: 1,
image: CompanyImage,
koreanTitle: '์ฟ ํŒก ์œ ์„ฑ์ ์—์„œ ์•„๋ฅด๋ฐ”์ดํŠธ ๋ชจ์ง‘ํ•ฉ๋‹ˆ๋‹ค.',
vietnameseTitle: '',
vietnameseTitle: 'Coupang ฤ‘ang tuyแปƒn dแปฅng lร m viแป‡c bรกn thแปi gian tแบกi chi nhรกnh Yuseong.',
companyName: '์ฟ ํŒก ์œ ์„ฑ์ ',
salary: 50000000,
workHours: '',
Expand Down
2 changes: 1 addition & 1 deletion src/apis/applicants/mocks/foreignerMockHandler.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { APIPath } from '@/apis/apiPath';
import { foreigner } from '@/features/applicants/ApplicantList/ContractModal/ContractModal.mock';
import { foreigner } from './foreigner.mock';
import { http, HttpResponse } from 'msw';

export const foreignerMockHandler = [http.get(APIPath.getForeigner, () => HttpResponse.json(foreigner))];
2 changes: 1 addition & 1 deletion src/apis/applicants/mocks/myApplicantsMockHandler.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { APIPath } from '@/apis/apiPath';
import { applicantList } from '@/pages/applicants/Applicants.mock';
import { applicantList } from './applicants.mock';
import { http, HttpResponse } from 'msw';

export const myApplicantsMockHandler = [http.get(APIPath.getMyApplicants, () => HttpResponse.json(applicantList))];
2 changes: 1 addition & 1 deletion src/apis/companies/mocks/myCompaniesMockHandler.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { APIPath } from '@/apis/apiPath';
import { companyList } from '@/pages/myPage/employer/EmployerMyPage.mock';
import { companyList } from './myCompanies.mock';
import { http, HttpResponse } from 'msw';

export const myCompaniesMockHandler = [http.get(APIPath.getMyCompanies, () => HttpResponse.json(companyList))];
13 changes: 13 additions & 0 deletions src/apis/recruitments/hooks/useCloseRecruitment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { getDynamicAPIPath } from '@/apis/apiPath';
import { clientInstance } from '@/apis/instance';
import { useMutation } from '@tanstack/react-query';

export const closeRecruitment = async (recruitmentId: number) => {
const response = await clientInstance.post(getDynamicAPIPath.closeRecruitment(recruitmentId));
return response.data;
};

export const useCloseRecruitment = () =>
useMutation({
mutationFn: closeRecruitment,
});
14 changes: 14 additions & 0 deletions src/apis/recruitments/mocks/closeRecruitmentMockHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { APIPath } from '@/apis/apiPath';
import { http, HttpResponse } from 'msw';

export const closeRecruitmentMockHandler = [
http.post(APIPath.closeRecruitment, async ({ params }) => {
const { recruitmentId } = params;

if (!recruitmentId) {
return HttpResponse.json({ message: '์ž˜๋ชป๋œ ์š”์ฒญ์ž…๋‹ˆ๋‹ค.' }, { status: 400 });
}

return HttpResponse.json({ message: '๋งˆ๊ฐ ์™„๋ฃŒ' }, { status: 200 });
}),
];
2 changes: 1 addition & 1 deletion src/apis/recruitments/mocks/myRecruitmentsMockHandler.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { APIPath } from '@/apis/apiPath';
import { recruitmentList } from '@/pages/myCompany/MyCompany.mock';
import { recruitmentList } from '@/pages/myCompanyPage/MyCompany.mock';
import { http, HttpResponse } from 'msw';

export const myRecruitmentsMockHandler = [
Expand Down
14 changes: 14 additions & 0 deletions src/assets/translator/Applicants/ko.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const Applicants = {
APPLICANT_LIST: '์ง€์›์ž ๋ชฉ๋ก',
TOTAL_APPLICANTS: (count: number) => `์ด ${count}๋ช…`,
VIEW_DETAILS: '์ž์„ธํžˆ ๋ณด๋Ÿฌ๊ฐ€๊ธฐ',
TABLE_HEADERS: {
NAME: '์ด๋ฆ„',
NATION: '๊ตญ์ ',
KOREAN_LANGUAGE_LEVEL: 'ํ•œ๊ตญ์–ด ์‹ค๋ ฅ',
},
BUTTONS: {
VIEW_RESUME: '์ง€์›์„œ',
CREATE_CONTRACT: '๊ณ„์•ฝํ•˜๊ธฐ',
},
};
14 changes: 14 additions & 0 deletions src/assets/translator/Applicants/ve.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const Applicants = {
APPLICANT_LIST: 'Danh sรกch แปฉng viรชn',
TOTAL_APPLICANTS: (count: number) => `Tแป•ng cแป™ng ${count} ngฦฐแปi`,
VIEW_DETAILS: 'Xem chi tiแบฟt',
TABLE_HEADERS: {
NAME: 'Tรชn',
NATION: 'Quแป‘c tแป‹ch',
KOREAN_LANGUAGE_LEVEL: 'Trรฌnh ฤ‘แป™ tiแบฟng Hร n',
},
BUTTONS: {
VIEW_RESUME: 'Hแป“ sฦก แปฉng tuyแปƒn',
CREATE_CONTRACT: 'Kรฝ hแปฃp ฤ‘แป“ng',
},
};
7 changes: 7 additions & 0 deletions src/assets/translator/EmployerMyPage/ko.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const EmployerMyPage = {
GREETING: '์‚ฌ์žฅ๋‹˜, ์•ˆ๋…•ํ•˜์„ธ์š”!',
REGISTER_SIGN: '์‚ฌ์ธ ๋“ฑ๋ก',
MY_COMPANIES: '๋‚ด ํšŒ์‚ฌ',
TOTAL_COMPANIES: (count: number) => `์ด ${count} ๊ณณ`,
COMPANY_INFO: 'ํšŒ์‚ฌ ์ •๋ณด',
};
7 changes: 7 additions & 0 deletions src/assets/translator/EmployerMyPage/ve.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const EmployerMyPage = {
GREETING: 'Chแปง doanh nghiแป‡p, xin chร o!',
REGISTER_SIGN: 'ฤฤƒng kรฝ chแปฏ kรฝ',
MY_COMPANIES: 'Cรดng ty cแปงa tรดi',
TOTAL_COMPANIES: (count: number) => `Tแป•ng cแป™ng ${count} cรดng ty`,
COMPANY_INFO: 'Thรดng tin cรดng ty',
};
13 changes: 13 additions & 0 deletions src/assets/translator/MyCompany/ko.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export const MyCompany = {
MY_RECRUITMENTS: '๋‚ด ๊ณต๊ณ ๊ธ€',
TOTAL_RECRUITMENTS: (count: number) => `์ด ${count}๊ฑด`,
TABLE_HEADERS: {
LOCATION: '๊ทผ๋ฌด์ง€',
TITLE: '๊ณต๊ณ  ์ œ๋ชฉ',
},
BUTTONS: {
VIEW_APPLICANTS: '์ง€์›์ž ๋ณด๋Ÿฌ๊ฐ€๊ธฐ',
CLOSE_RECRUITMENT: '๋งˆ๊ฐํ•˜๊ธฐ',
CLOSED_RECRUITMENT: '๋งˆ๊ฐ์™„๋ฃŒ',
},
};
13 changes: 13 additions & 0 deletions src/assets/translator/MyCompany/ve.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export const MyCompany = {
MY_RECRUITMENTS: 'Thรดng bรกo tuyแปƒn dแปฅng cแปงa tรดi',
TOTAL_RECRUITMENTS: (count: number) => `Tแป•ng cแป™ng ${count} bร i`,
TABLE_HEADERS: {
LOCATION: 'Nฦกi lร m viแป‡c',
TITLE: 'Tiรชu ฤ‘แป thรดng bรกo',
},
BUTTONS: {
VIEW_APPLICANTS: 'Xem ngฦฐแปi แปฉng tuyแปƒn',
CLOSE_RECRUITMENT: 'ฤรณng tuyแปƒn dแปฅng',
CLOSED_RECRUITMENT: 'ฤรฃ ฤ‘รณng tuyแปƒn dแปฅng',
},
};
8 changes: 8 additions & 0 deletions src/assets/translator/RegisterVisa/ko.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const RegisterVisa = {
TITLE: '์™ธ๊ตญ์ธ ๋ฒˆํ˜ธ ๋ฐ ๋น„์ž ๋ฐœ๊ธ‰ ์ผ์ž ๋“ฑ๋ก',
LABELS: {
FOREIGNER_NUMBER: '์™ธ๊ตญ์ธ ๋ฒˆํ˜ธ',
VISA_GENERATE_DATE: '๋น„์ž ๋ฐœ๊ธ‰ ์ผ์ž',
},
SUBMIT: '๋“ฑ๋กํ•˜๊ธฐ',
};
8 changes: 8 additions & 0 deletions src/assets/translator/RegisterVisa/ve.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const RegisterVisa = {
TITLE: 'ฤฤƒng kรฝ sแป‘ ngฦฐแปi nฦฐแป›c ngoร i vร  ngร y cแบฅp thแป‹ thแปฑc',
LABELS: {
FOREIGNER_NUMBER: 'Sแป‘ ngฦฐแปi nฦฐแป›c ngoร i',
VISA_GENERATE_DATE: 'Ngร y cแบฅp thแป‹ thแปฑc',
},
SUBMIT: 'ฤฤƒng kรฝ',
};
42 changes: 42 additions & 0 deletions src/components/providers/Language.provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { userLocalStorage } from '@/utils/storage';
import { createContext, ReactNode, useContext, useEffect, useState } from 'react';

type LanguageContextType = {
language: string;
setLanguage: (language: string) => void;
};

const LanguageContext = createContext<LanguageContextType | undefined>(undefined);

export const LanguageProvider = ({ children }: { children: ReactNode }) => {
const [language, setLanguage] = useState<string>(() => userLocalStorage.getLanguage() || 'korean');

useEffect(() => {
const handleStorageChange = (event: StorageEvent) => {
if (event.key === 'language') {
const newLanguage = event.newValue ? JSON.parse(event.newValue) : 'korean';
setLanguage(newLanguage);
}
};

window.addEventListener('storage', handleStorageChange);
return () => window.removeEventListener('storage', handleStorageChange);
}, []);

const updateLanguage = (newLanguage: string) => {
userLocalStorage.setLanguage(newLanguage);
setLanguage(newLanguage);
};

return (
<LanguageContext.Provider value={{ language, setLanguage: updateLanguage }}>{children}</LanguageContext.Provider>
);
};

export const useLanguage = () => {
const context = useContext(LanguageContext);
if (!context) {
throw new Error('useLanguage must be used within a LanguageProvider');
}
return context;
};
37 changes: 37 additions & 0 deletions src/components/providers/User.provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { ReactNode } from 'react';
import { createContext, useContext, useEffect, useState } from 'react';

import { UserData } from '@/types';
import { userLocalStorage } from '@/utils/storage';

type UserContextType = {
user: UserData | undefined;
setUser: React.Dispatch<React.SetStateAction<UserData | undefined>>;
};

export const UserContext = createContext<UserContextType | undefined>(undefined);

export const UserProvider = ({ children }: { children: ReactNode }) => {
const [user, setUser] = useState<UserData | undefined>(() => userLocalStorage.getUser());

useEffect(() => {
const changeUser = () => {
const updatedUser = userLocalStorage.getUser();
setUser(updatedUser);
};

window.addEventListener('storage', changeUser);

return () => window.removeEventListener('storage', changeUser);
}, []);

return <UserContext.Provider value={{ user, setUser }}>{children}</UserContext.Provider>;
};

export const useUser = () => {
const context = useContext(UserContext);
if (!context) {
throw new Error('useUser must be used within a UserProvider');
}
return context;
};
14 changes: 10 additions & 4 deletions src/components/providers/index.provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@ import ModalsProvider from './Modals.provider';
import Modals from '../common/Modal/Modals';
import { QueryClientProvider } from '@tanstack/react-query';
import { queryClient } from '@/apis/instance';
import { UserProvider } from './User.provider';
import { LanguageProvider } from './Language.provider';

export default function AppProviders({ children }: { children: ReactNode }) {
return (
<GlobalStylesProvider>
<QueryClientProvider client={queryClient}>
<ModalsProvider>
{children}
<Modals />
</ModalsProvider>
<LanguageProvider>
<ModalsProvider>
<UserProvider>
{children}
<Modals />
</UserProvider>
</ModalsProvider>
</LanguageProvider>
</QueryClientProvider>
</GlobalStylesProvider>
);
Expand Down
17 changes: 13 additions & 4 deletions src/features/layout/Header/components/LanguageFilter.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import theme from '@/assets/theme';
import { Select, Icon, List } from '@/components/common';
import useGlobalSelect from '@/components/common/Select/hooks/useGlobalSelect';
import { useLanguage } from '@/components/providers/Language.provider';
import { responsiveStyle } from '@/utils/responsive';

type LanguageOptionType = {
value: string;
text: string;
};

const triggerStyle = {
minWidth: '80px',
fontSize: '16px',
Expand Down Expand Up @@ -30,18 +35,22 @@ const languageOptions = [
];

export default function LanguageFilter() {
const { selectedOption, handleSelect } = useGlobalSelect(languageOptions[0]);
const { language, setLanguage } = useLanguage();

const changeLanguage = (option: LanguageOptionType) => {
setLanguage(option.value);
};

return (
<Select.Root>
<Select.Trigger icon={<Icon.Arrow.DownBlue />} css={triggerStyle}>
{selectedOption.text}
{languageOptions.find((opt) => opt.value === language)?.text}
</Select.Trigger>
<Select.Content>
<List
items={languageOptions}
renderItem={(option) => (
<Select.Option key={option.value} value={option.value} onClick={() => handleSelect(option)}>
<Select.Option key={option.value} value={option.value} onClick={() => changeLanguage(option)}>
{option.text}
</Select.Option>
)}
Expand Down
Loading

0 comments on commit b6e44a3

Please sign in to comment.