diff --git a/.eslintrc.js b/.eslintrc.js
index c8df6075..03ee7431 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -1,4 +1,4 @@
module.exports = {
root: true,
- extends: ["custom"],
+ extends: ['custom'],
};
diff --git "a/.github/ISSUE_TEMPLATE/\342\231\273\357\270\217-\353\246\254\355\214\251\355\206\240\353\247\201-\354\232\224\354\262\255.md" "b/.github/ISSUE_TEMPLATE/\342\231\273\357\270\217-\353\246\254\355\214\251\355\206\240\353\247\201-\354\232\224\354\262\255.md"
index 49fc6fd4..936d4a86 100644
--- "a/.github/ISSUE_TEMPLATE/\342\231\273\357\270\217-\353\246\254\355\214\251\355\206\240\353\247\201-\354\232\224\354\262\255.md"
+++ "b/.github/ISSUE_TEMPLATE/\342\231\273\357\270\217-\353\246\254\355\214\251\355\206\240\353\247\201-\354\232\224\354\262\255.md"
@@ -1,23 +1,26 @@
---
-name: '♻️ 리팩토링 요청'
+name: "♻️ 리팩토링 요청"
about: 변경 혹은 개선해야 되는 문제를 작성해 주세요
title: 'refactor: '
labels: ''
assignees: ''
+
---
-## 개선해야 되는 코드 혹은 기능에 대해서 적어주세요
+## 개선해야 되는 코드 혹은 기능에 대해서 적어주세요
+
+
-개선해야 될 코드에 대한 명확하고 간단한 설명
## 원하는 개선 방향
+
+
-개선해야 되는 간단한 이유 혹은 개선 후 장점에 대해 적어주세요
## 생각 중인 기능 추가 방안
+
-해결책으로 간단하게 생각한 개선 방법에 대해 적어주세요
-## ETC
-스크린샷이나 기능 등 추가 자료를 기술해 주세요
+## ETC
+
diff --git "a/.github/ISSUE_TEMPLATE/\342\234\250-\352\270\260\353\212\245-\354\266\224\352\260\200-\354\232\224\354\262\255.md" "b/.github/ISSUE_TEMPLATE/\342\234\250-\352\270\260\353\212\245-\354\266\224\352\260\200-\354\232\224\354\262\255.md"
index d6783961..3e6aee3d 100644
--- "a/.github/ISSUE_TEMPLATE/\342\234\250-\352\270\260\353\212\245-\354\266\224\352\260\200-\354\232\224\354\262\255.md"
+++ "b/.github/ISSUE_TEMPLATE/\342\234\250-\352\270\260\353\212\245-\354\266\224\352\260\200-\354\232\224\354\262\255.md"
@@ -1,26 +1,27 @@
---
-name: '✨ 기능 추가 요청'
+name: "✨ 기능 추가 요청"
about: 구현하려는 새로운 기능을 요청
-title: 'feat: '
+title: 'feat: '
labels: ''
assignees: ''
+
---
-## 추가하려는 기능이 어떠한 문제 혹은 기능과 연관되어 있나요?
+## 추가하려는 기능이 어떠한 문제 혹은 기능과 연관되어 있나요?
+
-문제가 무엇인지에 대한 명확하고 간결한 설명을 적어주세요
-## 원하는 기능 추가
-추가하려는 기능을 명확하고 간결하게 설명해주세요
+## 원하는 기능 추가
+
- [ ] todo
- [ ] todo
## 생각 중인 기능 추가 방안
+
-해결책으로 간단하게 생각한 기능의 방향 혹은 컴포넌트를 설명해주세요
-## ETC
-스크린샷이나 기능 등 추가 자료를 기술해 주세요
+## ETC
+
diff --git "a/.github/ISSUE_TEMPLATE/\360\237\220\233-\353\262\204\352\267\270-\353\246\254\355\217\254\355\212\270.md" "b/.github/ISSUE_TEMPLATE/\360\237\220\233-\353\262\204\352\267\270-\353\246\254\355\217\254\355\212\270.md"
index 561796c4..7a361c09 100644
--- "a/.github/ISSUE_TEMPLATE/\360\237\220\233-\353\262\204\352\267\270-\353\246\254\355\217\254\355\212\270.md"
+++ "b/.github/ISSUE_TEMPLATE/\360\237\220\233-\353\262\204\352\267\270-\353\246\254\355\217\254\355\212\270.md"
@@ -4,17 +4,20 @@ about: 버그를 고쳐주세요
title: 'bug: '
labels: ''
assignees: ''
+
---
## 버그 설명
+
-발생되는 문제에 대해 간단하게 설명해 주세요
-## 버그 발생 단계
+## 버그 발생 단계
+
diff --git a/apps/shelter/package.json b/apps/shelter/package.json
index d75db056..059c5543 100644
--- a/apps/shelter/package.json
+++ b/apps/shelter/package.json
@@ -6,7 +6,7 @@
"scripts": {
"build": "tsc && vite build",
"dev": "vite",
- "lint": "eslint \"src/**/*.ts\"",
+ "lint": "eslint \"src/**/*.{ts,tsx}\"",
"preview": "vite preview"
},
"dependencies": {
@@ -24,7 +24,7 @@
"react-error-boundary": "^4.0.11",
"react-hook-form": "^7.47.0",
"react-router-dom": "^6.17.0",
- "ui": "workspace:*",
+ "shared": "workspace:*",
"zod": "^3.22.4",
"zustand": "^4.4.4"
},
diff --git a/apps/shelter/src/App.tsx b/apps/shelter/src/App.tsx
index 88653cfd..e222a5e1 100644
--- a/apps/shelter/src/App.tsx
+++ b/apps/shelter/src/App.tsx
@@ -1,12 +1,22 @@
import { ChakraProvider } from '@chakra-ui/react';
-import { CustomButton, Header } from 'ui';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
+import { RouterProvider } from 'react-router-dom';
+import Fonts from 'shared/fonts';
+import theme from 'shared/theme';
+
+import { router } from '@/routes';
+
+const queryClient = new QueryClient();
export default function App() {
return (
-
-
- 보호소 어플리케이션
-
-
+
+
+
+
+
+
+
);
}
diff --git a/apps/shelter/src/apis/auth.ts b/apps/shelter/src/apis/auth.ts
new file mode 100644
index 00000000..1e4fc97a
--- /dev/null
+++ b/apps/shelter/src/apis/auth.ts
@@ -0,0 +1,26 @@
+import axiosInstance from 'shared/apis/axiosInstance';
+import type {
+ checkDuplicatedEmailRequestData,
+ checkDuplicatedEmailResponseData,
+ SigninRequestData,
+ SigninResponseData,
+} from 'shared/types/apis/auth';
+
+import { SignupRequestData } from '@/types/apis/auth';
+
+export const signinShelter = async (data: SigninRequestData) =>
+ await axiosInstance.post(
+ '/auth/shelters/login',
+ data,
+ );
+
+export const signupShelter = async (data: SignupRequestData) =>
+ await axiosInstance.post('/shelters', data);
+
+export const checkDuplicatedShelterEmail = async (email: string) =>
+ await axiosInstance.post<
+ checkDuplicatedEmailResponseData,
+ checkDuplicatedEmailRequestData
+ >('/shelters/email', {
+ email,
+ });
diff --git a/apps/shelter/src/apis/recruitment.ts b/apps/shelter/src/apis/recruitment.ts
new file mode 100644
index 00000000..a5983917
--- /dev/null
+++ b/apps/shelter/src/apis/recruitment.ts
@@ -0,0 +1,140 @@
+import axiosInstance from 'shared/apis/axiosInstance';
+
+type PageInfo = {
+ totalElements: number;
+ hasNext: boolean;
+};
+
+type Recruitment = {
+ recruitmentId: number;
+ recruitmentTitle: string;
+ recruitmentStartTime: string;
+ recruitmentEndTime: string;
+ recruitmentDeadline: string;
+ recruitmentIsClosed: boolean;
+ recruitmentApplicantCount: number;
+ recruitmentCapacity: number;
+};
+
+type RecruitSearchParams = {
+ keyword: string;
+ startDate: string;
+ endDate: string;
+ isClosed: boolean;
+ content: boolean;
+ title: boolean;
+ pageSize: number;
+ pageNumber: number;
+};
+
+type PostRecruitmentParams = {
+ title: string;
+ startTime: string;
+ endTime: string;
+ deadline: string;
+ capacity: number;
+ content: string;
+ imageUrls: string[];
+};
+
+type AttendanceStatus = {
+ applicantId: number;
+ attendance: boolean;
+};
+
+type Gender = 'MALE' | 'FEMALE';
+type RecruitementStatus = 'PENDING' | 'REFUSED' | 'APPROVED';
+
+export const getShelterRecruitments = async (
+ recruitSearchParams: Partial,
+) =>
+ axiosInstance.get<
+ {
+ pageInfo: PageInfo;
+ recruitments: Recruitment[];
+ },
+ RecruitSearchParams
+ >('/shelters/recruitments', {
+ params: recruitSearchParams,
+ });
+
+export const createShelterRecruitment = (
+ recruitmentParams: PostRecruitmentParams,
+) =>
+ axiosInstance.post(
+ `/shelters/recruitments`,
+ recruitmentParams,
+ );
+
+export const updateShelterRecruitment = (
+ recruitmentId: number,
+ recruitmentParams: PostRecruitmentParams,
+) =>
+ axiosInstance.patch(
+ `/shelters/recruitments/${recruitmentId}`,
+ recruitmentParams,
+ );
+
+export const deleteShelterRecruitment = (recruitmentId: number) =>
+ axiosInstance.delete(
+ `/shelters/recruitments/${recruitmentId}`,
+ );
+
+export const closeShelterRecruitment = (recruitmentId: number) =>
+ axiosInstance.patch(
+ `/shelters/recruitments/${recruitmentId}/close`,
+ );
+
+export const getShelterRecruitmentApplicants = (recruitmentId: number) =>
+ axiosInstance.get<{
+ applicants: {
+ applicantId: number;
+ volunteerId: number;
+ volunteerName: string;
+ volunteerBirthDate: string;
+ volunteerGender: Gender;
+ completedVolunteerCount: number;
+ volunteerTemperature: number;
+ applicantStatus: RecruitementStatus;
+ }[];
+ recruitmentCapacity: number;
+ }>(`/shelters/recruitments/${recruitmentId}/applicants`);
+
+export const updateShelterRecruitmentApplicant = (
+ recruitmentId: number,
+ applicantId: number,
+) =>
+ axiosInstance.patch<
+ unknown,
+ {
+ status: RecruitementStatus;
+ }
+ >(`/shelters/recruitments/${recruitmentId}/applicants/${applicantId}`);
+
+export const getShelterApprovedRecruitmentApplicants = (
+ recruitmentId: number,
+) =>
+ axiosInstance.get<{
+ applicants: {
+ volunteerId: number;
+ applicantId: number;
+ volunteerName: string;
+ volunteerBirthDate: string;
+ volunteerGender: Gender;
+ volunteerPhoneNumber: string;
+ volunteerAttendance: boolean;
+ }[];
+ }>(`/shelters/recruitments/${recruitmentId}/approval`);
+
+export const updatShelterApplicantsApproval = (
+ recruitmentId: number,
+ applicants: AttendanceStatus[],
+) =>
+ axiosInstance.patch<
+ unknown,
+ {
+ applicants: AttendanceStatus[];
+ }
+ >(`/shelters/recruitments/${recruitmentId}/approval`, {
+ applicants,
+ });
diff --git a/apps/shelter/src/apis/shelter.ts b/apps/shelter/src/apis/shelter.ts
new file mode 100644
index 00000000..87faad9f
--- /dev/null
+++ b/apps/shelter/src/apis/shelter.ts
@@ -0,0 +1,53 @@
+import axiosInstance from 'shared/apis/axiosInstance';
+
+import { ShelterInfo } from '@/types/apis/shetler';
+
+type PasswordUpdateParams = {
+ newPassword: string;
+ oldPassword: string;
+};
+
+type PageParams = {
+ pageSize: number;
+ pageNumber: number;
+};
+
+export const getShelterInfoAPI = () =>
+ axiosInstance.get('/shelters/me');
+
+export const updateShelterInfo = (shelterInfo: ShelterInfo) =>
+ axiosInstance.patch('/shelters/me', shelterInfo);
+
+export const updatePassword = (passwordUpdateParams: PasswordUpdateParams) =>
+ axiosInstance.patch(
+ '/shelters/me/password',
+ passwordUpdateParams,
+ );
+
+export const updateAddressStatusAPI = (isOpenedAddress: boolean) =>
+ axiosInstance.patch<
+ unknown,
+ {
+ isOpenedAddress: boolean;
+ }
+ >('/shelters/me/address/status', { isOpenedAddress });
+
+export const getShelterReviewList = (pageParams: PageParams) =>
+ axiosInstance.get<{
+ pageInfo: {
+ totalElements: number;
+ hasNext: boolean;
+ };
+ reviews: {
+ reviewId: number;
+ reviewCreatedAt: string;
+ reviewContent: string;
+ reviewImageUrls: string[];
+ volunteerName: string;
+ volunteerTemperature: number;
+ volunteerReviewCount: number;
+ volunteerImageUrl: string;
+ }[];
+ }>(`/shelters/me/reviews`, {
+ params: pageParams,
+ });
diff --git a/apps/shelter/src/constants/path.ts b/apps/shelter/src/constants/path.ts
new file mode 100644
index 00000000..d6396874
--- /dev/null
+++ b/apps/shelter/src/constants/path.ts
@@ -0,0 +1,40 @@
+const PATH = {
+ VOLUNTEERS: {
+ INDEX: 'volunteers',
+ DETAIL: ':id',
+ PROFILE: 'profile',
+ SEARCH: 'search',
+ WRITE: 'write',
+ UPDATE: 'write/:id',
+ },
+ ANIMALS: {
+ INDEX: 'animals',
+ DETAIL: ':id',
+ SEARCH: 'search',
+ WRITE: 'write',
+ UPDATE: 'write/:id',
+ },
+ CHATTINGS: {
+ INDEX: 'chattings',
+ ROOM: 'chattings/:id',
+ },
+ MYPAGE: {
+ INDEX: 'mypage',
+ REVIEWS: 'reviews',
+ },
+ SETTINGS: {
+ INDEX: 'settings',
+ ACCOUNT: 'account',
+ PASSWORD: 'password',
+ },
+ MANAGE: {
+ INDEX: 'manage',
+ ATTENDANCE: 'attendance/:id',
+ APPLY: 'apply/:id',
+ },
+ NOTIFICATIONS: 'notifications',
+ SIGNUP: 'signup',
+ SIGNIN: 'signin',
+};
+
+export default PATH;
diff --git a/apps/shelter/src/hooks/useIntersection.tsx b/apps/shelter/src/hooks/useIntersection.tsx
new file mode 100644
index 00000000..be9c2f1a
--- /dev/null
+++ b/apps/shelter/src/hooks/useIntersection.tsx
@@ -0,0 +1,36 @@
+import { useCallback, useEffect, useRef } from 'react';
+
+type IntersectHandler = (
+ entry: IntersectionObserverEntry,
+ observer: IntersectionObserver,
+) => void;
+
+const useIntersect = (
+ onIntersect: IntersectHandler,
+ options?: IntersectionObserverInit,
+) => {
+ const ref = useRef(null);
+ const callback = useCallback(
+ (entries: IntersectionObserverEntry[], observer: IntersectionObserver) => {
+ entries.forEach((entry) => {
+ if (entry.isIntersecting) {
+ onIntersect(entry, observer);
+ }
+ });
+ },
+ [onIntersect],
+ );
+
+ useEffect(() => {
+ if (!ref.current) {
+ return;
+ }
+ const observer = new IntersectionObserver(callback, options);
+ observer.observe(ref.current);
+ return () => observer.disconnect();
+ }, [ref, options, callback]);
+
+ return ref;
+};
+
+export default useIntersect;
diff --git a/apps/shelter/src/main.tsx b/apps/shelter/src/main.tsx
index 23ceb8cf..f6444594 100644
--- a/apps/shelter/src/main.tsx
+++ b/apps/shelter/src/main.tsx
@@ -1,5 +1,3 @@
-import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
-import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import React from 'react';
import ReactDOM from 'react-dom/client';
@@ -17,15 +15,10 @@ async function deferRender() {
return worker.start();
}
-const queryClient = new QueryClient();
-
deferRender().then(() => {
ReactDOM.createRoot(document.getElementById('root')!).render(
-
-
-
-
+
,
);
});
diff --git a/apps/shelter/src/mocks/browser.ts b/apps/shelter/src/mocks/browser.ts
index c95883bc..7f1ed5cd 100644
--- a/apps/shelter/src/mocks/browser.ts
+++ b/apps/shelter/src/mocks/browser.ts
@@ -1,5 +1,11 @@
import { setupWorker } from 'msw/browser';
-import { handlers } from './handlers';
+import { handlers as authHandlers } from './handlers/auth';
+import { handlers as recruitmentHandler } from './handlers/recruitment';
+import { handlers as shelterHandlers } from './handlers/shelter';
-export const worker = setupWorker(...handlers);
+export const worker = setupWorker(
+ ...authHandlers,
+ ...shelterHandlers,
+ ...recruitmentHandler,
+);
diff --git a/apps/shelter/src/mocks/handlers.ts b/apps/shelter/src/mocks/handlers.ts
deleted file mode 100644
index b091270d..00000000
--- a/apps/shelter/src/mocks/handlers.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import { delay, http, HttpResponse } from 'msw';
-
-const ALL_POST = [
- {
- userId: 1,
- id: 1,
- title: '모킹 포스트 1',
- body: '모킹된 포스트에요. MSW를 이용했어요.',
- },
- {
- userId: 1,
- id: 2,
- title: '모킹 포스트 2',
- body: '모킹된 포스트에요. MSW를 이용했어요.',
- },
- {
- userId: 1,
- id: 3,
- title: '모킹 포스트 3',
- body: '모킹된 포스트에요. MSW를 이용했어요.',
- },
- {
- userId: 1,
- id: 4,
- title: '모킹 포스트 4',
- body: '모킹된 포스트에요. MSW를 이용했어요.',
- },
- {
- userId: 1,
- id: 5,
- title: '모킹 포스트 5',
- body: '모킹된 포스트에요. MSW를 이용했어요.',
- },
-];
-
-export const handlers = [
- http.get('/example/posts', async () => {
- await delay(200);
- return HttpResponse.json(ALL_POST);
- }),
-];
diff --git a/apps/shelter/src/mocks/handlers/auth.ts b/apps/shelter/src/mocks/handlers/auth.ts
new file mode 100644
index 00000000..f7a041d0
--- /dev/null
+++ b/apps/shelter/src/mocks/handlers/auth.ts
@@ -0,0 +1,78 @@
+import { delay, http, HttpResponse } from 'msw';
+
+export const handlers = [
+ http.post('/auth/shelters/login', async () => {
+ await delay(200);
+ return HttpResponse.json(
+ {
+ accessToken: 'accessToken',
+ userId: 1,
+ role: 'ROLE_SEHLTER',
+ },
+ { status: 200 },
+ );
+ }),
+ http.post('/auth/shelters/login', async () => {
+ await delay(200);
+ return HttpResponse.json(
+ {
+ errorCode: 'AF002',
+ message: '이메일/비밀번호가 올바르지 않습니다',
+ },
+ { status: 400 },
+ );
+ }),
+ http.post('/auth/shelters/login', async () => {
+ await delay(200);
+ return HttpResponse.json(
+ {
+ errorCode: 'AF001',
+ message: '잘못된 입력값입니다',
+ },
+ { status: 400 },
+ );
+ }),
+ http.post('/shelters', async () => {
+ await delay(200);
+ return HttpResponse.json({}, { status: 400 });
+ }),
+ http.post('/shelters', async () => {
+ await delay(200);
+ return HttpResponse.json(
+ {
+ errorCode: 'AF002',
+ message: '{입력값}은 1자 이상, 20자 이하여야 합니다.',
+ },
+ { status: 400 },
+ );
+ }),
+ http.post('/shelters', async () => {
+ await delay(200);
+ return HttpResponse.json(
+ {
+ errorCode: 'AF001',
+ message: '요청값이 입력되지 않았습니다.. {}',
+ },
+ { status: 400 },
+ );
+ }),
+ http.post('/shelters/email', async () => {
+ await delay(200);
+ return HttpResponse.json(
+ {
+ isDuplicated: false,
+ },
+ { status: 200 },
+ );
+ }),
+ http.post('/shelters/email', async () => {
+ await delay(200);
+ return HttpResponse.json(
+ {
+ errorCode: 'AF001',
+ message: '잘못된 입력값입니다',
+ },
+ { status: 400 },
+ );
+ }),
+];
diff --git a/apps/shelter/src/mocks/handlers/recruitment.ts b/apps/shelter/src/mocks/handlers/recruitment.ts
new file mode 100644
index 00000000..98219ebe
--- /dev/null
+++ b/apps/shelter/src/mocks/handlers/recruitment.ts
@@ -0,0 +1,33 @@
+import { delay, http, HttpResponse } from 'msw';
+
+const DUMMY_RECRUITMENT = {
+ recruitmentId: 1,
+ recruitmentTitle: '봉사자를 모집합니다',
+ recruitmentStartTime: '2021-11-08T11:44:30.327959',
+ recruitmentEndTime: '2021-11-08T11:44:30.327959',
+ recruitmentDeadline: '2023-11-20T11:44:30.327959',
+ recruitmentIsClosed: false,
+ recruitmentApplicantCount: 15,
+ recruitmentCapacity: 15,
+};
+
+const DUMMY_RECRUITMENT_LIST = Array.from(
+ { length: 4 },
+ () => DUMMY_RECRUITMENT,
+);
+
+export const handlers = [
+ http.get('/shelters/recruitments', async () => {
+ await delay(1000);
+ return HttpResponse.json(
+ {
+ pageInfo: {
+ totalElements: 100,
+ hasNext: true,
+ },
+ recruitments: DUMMY_RECRUITMENT_LIST,
+ },
+ { status: 200 },
+ );
+ }),
+];
diff --git a/apps/shelter/src/mocks/handlers/shelter.ts b/apps/shelter/src/mocks/handlers/shelter.ts
new file mode 100644
index 00000000..d7ee40cf
--- /dev/null
+++ b/apps/shelter/src/mocks/handlers/shelter.ts
@@ -0,0 +1,52 @@
+import { delay, http, HttpResponse } from 'msw';
+
+const DUMMY_IMAGE = 'https://source.unsplash.com/random';
+const DUMMY_IMAGE_LIST = Array.from({ length: 4 }, () => DUMMY_IMAGE);
+const DUMMY_REVIEW = {
+ reviewId: 32,
+ reviewCreatedAt: '2023-03-16T18:00',
+ reviewContent: '시설이 너무 깨끗하고 강아지도...',
+ reviewImageUrls: DUMMY_IMAGE_LIST,
+ volunteerName: '강혜린',
+ volunteerTemperature: 44,
+ volunteerReviewCount: 4,
+ volunteerImageUrl: DUMMY_IMAGE,
+};
+const DUMMY_REVIEW_LIST = Array.from({ length: 4 }, () => DUMMY_REVIEW);
+
+export const handlers = [
+ http.get('/shelters/me', async () => {
+ await delay(200);
+ return HttpResponse.json(
+ {
+ shelterId: 1,
+ shelterEmail: 'Shelter1234@gmail.com',
+ shelterName: '양천구 보호소',
+ imageUrl: null,
+ shelterAddress: '서울특별시 양천구',
+ shelterAddressDetail: '서울특별시 양천구 신월동 동자빌딩',
+ shelterPhoneNumber: '010-1234-5678',
+ shelterSparePhoneNumber: '02-345-6780',
+ shelterIsOpenedAddress: true,
+ },
+ { status: 200 },
+ );
+ }),
+ http.patch('/shelters/me/address/status', async () => {
+ await delay(200);
+ return HttpResponse.json({ status: 200 });
+ }),
+ http.get('/shelters/me/reviews', async () => {
+ await delay(200);
+ return HttpResponse.json(
+ {
+ pageInfo: {
+ totalElements: 100,
+ hasNext: true,
+ },
+ reviews: DUMMY_REVIEW_LIST,
+ },
+ { status: 200 },
+ );
+ }),
+];
diff --git a/apps/shelter/src/pages/animals/detail/index.tsx b/apps/shelter/src/pages/animals/detail/index.tsx
new file mode 100644
index 00000000..e36382be
--- /dev/null
+++ b/apps/shelter/src/pages/animals/detail/index.tsx
@@ -0,0 +1,21 @@
+import { useEffect } from 'react';
+import useDetailHeaderStore from 'shared/store/detailHeaderStore';
+
+const handleDeletePost = (postId: number) => {
+ // TODO: AnimalPost delete API 호출
+ console.log('[Delete Animal] postId:', postId);
+};
+
+export default function AnimalsDetailPage() {
+ const setOnDelete = useDetailHeaderStore((state) => state.setOnDelete);
+
+ useEffect(() => {
+ setOnDelete(handleDeletePost);
+
+ return () => {
+ setOnDelete(() => {});
+ };
+ }, [setOnDelete]);
+
+ return AnimalsDetailPage
;
+}
diff --git a/apps/shelter/src/pages/animals/index.tsx b/apps/shelter/src/pages/animals/index.tsx
new file mode 100644
index 00000000..c11ddc43
--- /dev/null
+++ b/apps/shelter/src/pages/animals/index.tsx
@@ -0,0 +1,3 @@
+export default function AnimalsPage() {
+ return AnimalsPage
;
+}
diff --git a/apps/shelter/src/pages/animals/search/index.tsx b/apps/shelter/src/pages/animals/search/index.tsx
new file mode 100644
index 00000000..5bbff8b6
--- /dev/null
+++ b/apps/shelter/src/pages/animals/search/index.tsx
@@ -0,0 +1,21 @@
+import { useEffect } from 'react';
+import useSearchHeaderStore from 'shared/store/searchHeaderStore';
+
+const handleSearchkeyword = (keyword: string) => {
+ // TODO: AnimalList 검색 API 호출
+ console.log('[Search Animal] - keyword:', keyword);
+};
+
+export default function AnimalsSearchPage() {
+ const setOnSearch = useSearchHeaderStore((state) => state.setOnSearch);
+
+ useEffect(() => {
+ setOnSearch(handleSearchkeyword);
+
+ return () => {
+ setOnSearch(() => {});
+ };
+ }, [setOnSearch]);
+
+ return AnimalsSearchPage
;
+}
diff --git a/apps/shelter/src/pages/animals/update/index.tsx b/apps/shelter/src/pages/animals/update/index.tsx
new file mode 100644
index 00000000..3a9b93fc
--- /dev/null
+++ b/apps/shelter/src/pages/animals/update/index.tsx
@@ -0,0 +1,3 @@
+export default function AnimalsUpdatePage() {
+ return AnimalsUpdatePage
;
+}
diff --git a/apps/shelter/src/pages/animals/write/index.tsx b/apps/shelter/src/pages/animals/write/index.tsx
new file mode 100644
index 00000000..a081d6a5
--- /dev/null
+++ b/apps/shelter/src/pages/animals/write/index.tsx
@@ -0,0 +1,3 @@
+export default function AnimalsWritePage() {
+ return AnimalsWritePage
;
+}
diff --git a/apps/shelter/src/pages/chattings/index.tsx b/apps/shelter/src/pages/chattings/index.tsx
new file mode 100644
index 00000000..a48ead0b
--- /dev/null
+++ b/apps/shelter/src/pages/chattings/index.tsx
@@ -0,0 +1,3 @@
+export default function ChattingsPage() {
+ return ChattingsPage
;
+}
diff --git a/apps/shelter/src/pages/chattings/room/index.tsx b/apps/shelter/src/pages/chattings/room/index.tsx
new file mode 100644
index 00000000..55fc00e1
--- /dev/null
+++ b/apps/shelter/src/pages/chattings/room/index.tsx
@@ -0,0 +1,3 @@
+export default function ChattingsRoomPage() {
+ return ChattingsRoomPage
;
+}
diff --git a/apps/shelter/src/pages/manage/apply/index.tsx b/apps/shelter/src/pages/manage/apply/index.tsx
new file mode 100644
index 00000000..ad1f19d6
--- /dev/null
+++ b/apps/shelter/src/pages/manage/apply/index.tsx
@@ -0,0 +1,3 @@
+export default function ManageApplyPage() {
+ return ManageApplyPage
;
+}
diff --git a/apps/shelter/src/pages/manage/attendance/index.tsx b/apps/shelter/src/pages/manage/attendance/index.tsx
new file mode 100644
index 00000000..31edc9f3
--- /dev/null
+++ b/apps/shelter/src/pages/manage/attendance/index.tsx
@@ -0,0 +1,3 @@
+export default function ManageAttendancePage() {
+ return ManageAttendancePage
;
+}
diff --git a/apps/shelter/src/pages/my/_hooks/useMyPage.ts b/apps/shelter/src/pages/my/_hooks/useMyPage.ts
new file mode 100644
index 00000000..2d781c66
--- /dev/null
+++ b/apps/shelter/src/pages/my/_hooks/useMyPage.ts
@@ -0,0 +1,66 @@
+import { useQuery } from '@tanstack/react-query';
+import { useState } from 'react';
+
+import { getShelterInfoAPI, updateAddressStatusAPI } from '@/apis/shelter';
+import { ShelterInfo } from '@/types/apis/shetler';
+
+type ShelterProfile = {
+ shelterName: string;
+ email: string;
+ phoneNumber: string;
+ sparePhoneNumber: string;
+ shelterAddress: string;
+ isAddressPublic: boolean;
+};
+
+const createProfile = (response: ShelterInfo): ShelterProfile => {
+ const {
+ shelterName,
+ shelterEmail,
+ shelterPhoneNumber,
+ shelterSparePhoneNumber,
+ shelterAddressDetail,
+ shelterIsOpenedAddress,
+ } = response;
+ return {
+ shelterName: shelterName,
+ email: shelterEmail,
+ phoneNumber: shelterPhoneNumber,
+ sparePhoneNumber: shelterSparePhoneNumber,
+ shelterAddress: shelterAddressDetail,
+ isAddressPublic: shelterIsOpenedAddress,
+ };
+};
+
+export const useMyPage = () => {
+ const [isAddressPublic, setIsAddressPublic] = useState(false);
+
+ const updateAddressStatus = async () => {
+ try {
+ await updateAddressStatusAPI(!isAddressPublic);
+ setIsAddressPublic(!isAddressPublic);
+ } catch (error) {
+ console.error(error);
+ }
+ };
+
+ const { data } = useQuery({
+ queryKey: ['shelterProfile'],
+ queryFn: async () => {
+ const response = (await getShelterInfoAPI()).data;
+ setIsAddressPublic(response.shelterIsOpenedAddress);
+
+ return createProfile(response);
+ },
+ initialData: {
+ shelterName: '',
+ email: '',
+ phoneNumber: '',
+ sparePhoneNumber: '',
+ shelterAddress: '',
+ isAddressPublic: false,
+ },
+ });
+
+ return { shelterProfile: data, isAddressPublic, updateAddressStatus };
+};
diff --git a/apps/shelter/src/pages/my/index.tsx b/apps/shelter/src/pages/my/index.tsx
new file mode 100644
index 00000000..7648b4d1
--- /dev/null
+++ b/apps/shelter/src/pages/my/index.tsx
@@ -0,0 +1,62 @@
+import { Box, Divider, Switch, VStack } from '@chakra-ui/react';
+import { useNavigate } from 'react-router-dom';
+import InfoItem from 'shared/components/InfoItem';
+import InfoList from 'shared/components/InfoList';
+import InfoTextItem from 'shared/components/InfoTextItem';
+import ProfileInfo from 'shared/components/ProfileInfo';
+import SettingGroup from 'shared/components/SettingGroup';
+
+import { useMyPage } from '@/pages/my/_hooks/useMyPage';
+
+export default function MyPage() {
+ const navigate = useNavigate();
+ const { shelterProfile, isAddressPublic, updateAddressStatus } = useMyPage();
+
+ const { shelterName, email, phoneNumber, sparePhoneNumber, shelterAddress } =
+ shelterProfile;
+
+ const goShelterReview = () => navigate('/mypage/reviews');
+ const goSettingsAccount = () => navigate('/settings/account');
+ const goSettingsPassword = () => navigate('/settings/password');
+ const logout = () => {
+ // TODO: 로그아웃
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/shelter/src/pages/my/reviews/VolunteerProfile.tsx b/apps/shelter/src/pages/my/reviews/VolunteerProfile.tsx
new file mode 100644
index 00000000..9701bbcd
--- /dev/null
+++ b/apps/shelter/src/pages/my/reviews/VolunteerProfile.tsx
@@ -0,0 +1,41 @@
+import { Avatar, Box, HStack, Image, Text } from '@chakra-ui/react';
+import NextIcon from 'shared/assets/icon_review_next.svg';
+import InfoSubtext from 'shared/components/InfoSubtext';
+import Label from 'shared/components/Label';
+
+type VolunteerProfileprops = {
+ volunteerName: string;
+ volunteerTempature: number;
+ volunteerReviewCount: number;
+ volunteerImageUrl: string;
+ reviewCreatedAt: string;
+};
+
+export default function VolunteerProfile({
+ volunteerName,
+ volunteerTempature,
+ volunteerReviewCount,
+ volunteerImageUrl,
+ reviewCreatedAt,
+}: VolunteerProfileprops) {
+ return (
+
+
+
+
+
+ {volunteerName}
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/shelter/src/pages/my/reviews/hooks/useFetchShelterReviews.tsx b/apps/shelter/src/pages/my/reviews/hooks/useFetchShelterReviews.tsx
new file mode 100644
index 00000000..48bb56dd
--- /dev/null
+++ b/apps/shelter/src/pages/my/reviews/hooks/useFetchShelterReviews.tsx
@@ -0,0 +1,14 @@
+import { useSuspenseInfiniteQuery } from '@tanstack/react-query';
+
+import { getShelterReviewList } from '@/apis/shelter';
+
+export default function useFetchShelterReviews(pageSize: number) {
+ return useSuspenseInfiniteQuery({
+ queryKey: ['reviews'],
+ queryFn: ({ pageParam }) =>
+ getShelterReviewList({ pageNumber: pageParam, pageSize }),
+ initialPageParam: 0,
+ getNextPageParam: ({ data: { pageInfo } }, _, lastPageParam) =>
+ pageInfo.hasNext ? lastPageParam + 1 : null,
+ });
+}
diff --git a/apps/shelter/src/pages/my/reviews/index.tsx b/apps/shelter/src/pages/my/reviews/index.tsx
new file mode 100644
index 00000000..9f0f0167
--- /dev/null
+++ b/apps/shelter/src/pages/my/reviews/index.tsx
@@ -0,0 +1,77 @@
+import { Box, Heading, VStack } from '@chakra-ui/react';
+import { Suspense } from 'react';
+import ReviewItem from 'shared/components/ReviewItem';
+import { createFormattedTime } from 'shared/utils/date';
+
+import useIntersect from '@/hooks/useIntersection';
+
+import useFetchShelterReviews from './hooks/useFetchShelterReviews';
+import VolunteerProfile from './VolunteerProfile';
+
+const PAGE_SIZE = 10;
+
+function Reviews() {
+ //TODO 봉사자 옆에 화살표 버튼 클릭시 봉사자 프로필 페이지로 가는 기능추가
+
+ const {
+ data: { pages },
+ hasNextPage,
+ isFetchingNextPage,
+ fetchNextPage,
+ } = useFetchShelterReviews(PAGE_SIZE);
+
+ const totalReviews = pages[0].data.pageInfo.totalElements;
+ const reviews = pages.flatMap(({ data }) => data.reviews);
+ const ref = useIntersect(async (entry, observer) => {
+ observer.unobserve(entry.target);
+ if (hasNextPage && !isFetchingNextPage) {
+ fetchNextPage();
+ }
+ });
+
+ return (
+
+
+ 봉사자들이 작성한 봉사후기{` ${totalReviews}개`}
+
+
+
+ {reviews.map((review) => (
+
+
+
+ ))}
+
+
+
+ );
+}
+
+export default function MyReviewsPage() {
+ return (
+ 글목록 로딩중...
}>
+
+
+ );
+}
diff --git a/apps/shelter/src/pages/notfound/index.tsx b/apps/shelter/src/pages/notfound/index.tsx
new file mode 100644
index 00000000..5ba55266
--- /dev/null
+++ b/apps/shelter/src/pages/notfound/index.tsx
@@ -0,0 +1,3 @@
+export default function NotFoundPage() {
+ return NotFoundPage
;
+}
diff --git a/apps/shelter/src/pages/notifications/index.tsx b/apps/shelter/src/pages/notifications/index.tsx
new file mode 100644
index 00000000..b7e24e5d
--- /dev/null
+++ b/apps/shelter/src/pages/notifications/index.tsx
@@ -0,0 +1,3 @@
+export default function NotificationsPage() {
+ return NotificationsPage
;
+}
diff --git a/apps/shelter/src/pages/settings/account/index.tsx b/apps/shelter/src/pages/settings/account/index.tsx
new file mode 100644
index 00000000..92db107c
--- /dev/null
+++ b/apps/shelter/src/pages/settings/account/index.tsx
@@ -0,0 +1,3 @@
+export default function SettingsAccountPage() {
+ return SettingsAccountPage
;
+}
diff --git a/apps/shelter/src/pages/settings/password/index.tsx b/apps/shelter/src/pages/settings/password/index.tsx
new file mode 100644
index 00000000..1d504fa2
--- /dev/null
+++ b/apps/shelter/src/pages/settings/password/index.tsx
@@ -0,0 +1,3 @@
+export default function SettingsPasswordPage() {
+ return SettingsPasswordPage
;
+}
diff --git a/apps/shelter/src/pages/signin/index.tsx b/apps/shelter/src/pages/signin/index.tsx
new file mode 100644
index 00000000..22355677
--- /dev/null
+++ b/apps/shelter/src/pages/signin/index.tsx
@@ -0,0 +1,175 @@
+import {
+ Box,
+ Button,
+ Center,
+ FormControl,
+ FormErrorMessage,
+ FormLabel,
+ Icon,
+ Image,
+ Input,
+ InputGroup,
+ InputRightElement,
+ useToast,
+ VStack,
+} from '@chakra-ui/react';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { useMutation } from '@tanstack/react-query';
+import type { AxiosResponse } from 'axios';
+import { AxiosError } from 'axios';
+import { useEffect } from 'react';
+import { useForm } from 'react-hook-form';
+import { useNavigate } from 'react-router-dom';
+import AnimalfriendsLogo from 'shared/assets/image-anifriends-logo.png';
+import IoEyeOff from 'shared/assets/IoEyeOff';
+import IoEyeSharp from 'shared/assets/IoEyeSharp';
+import useToggle from 'shared/hooks/useToggle';
+import type {
+ SigninRequestData,
+ SigninResponseData,
+} from 'shared/types/apis/auth';
+import { ErrorResponseData } from 'shared/types/apis/error';
+import * as z from 'zod';
+
+import { signinShelter } from '@/apis/auth';
+import PATH from '@/constants/path';
+
+type Schema = z.infer;
+
+const schema = z.object({
+ email: z
+ .string()
+ .min(1, '이메일이 입려되지 않았습니다')
+ .email('유효하지 않은 이메일입니다'),
+ password: z.string().min(1, '비밀번호가 입력되지 않았습니다'),
+});
+
+export default function SigninPage() {
+ const navigate = useNavigate();
+ const toast = useToast();
+ const [isShow, toggleInputShow] = useToggle();
+ const {
+ register,
+ handleSubmit,
+ formState: { errors },
+ setFocus,
+ } = useForm({
+ resolver: zodResolver(schema),
+ });
+ const { mutate } = useMutation<
+ AxiosResponse,
+ AxiosError,
+ SigninRequestData
+ >({
+ mutationFn: (data) => signinShelter(data),
+ onSuccess: () => {
+ navigate(`/${PATH.VOLUNTEERS.INDEX}`);
+ },
+ onError: (error) => {
+ toast({
+ position: 'top',
+ description: error.response?.data.message,
+ status: 'error',
+ duration: 1500,
+ });
+
+ setFocus('email');
+ },
+ });
+
+ const goSignupPage = () => {
+ navigate(`/${PATH.SIGNUP}`);
+ };
+
+ const onSubmit = async (data: Schema) => {
+ mutate(data);
+ };
+
+ useEffect(() => setFocus('email'), [setFocus]);
+
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/apps/shelter/src/pages/signup/index.tsx b/apps/shelter/src/pages/signup/index.tsx
new file mode 100644
index 00000000..bf6be532
--- /dev/null
+++ b/apps/shelter/src/pages/signup/index.tsx
@@ -0,0 +1,300 @@
+import {
+ Box,
+ Button,
+ Center,
+ FormControl,
+ FormErrorMessage,
+ FormHelperText,
+ FormLabel,
+ HStack,
+ Icon,
+ Image,
+ Input,
+ InputGroup,
+ InputRightAddon,
+ InputRightElement,
+ Switch,
+ VStack,
+} from '@chakra-ui/react';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { Controller, useForm } from 'react-hook-form';
+import AnimalfriendsLogo from 'shared/assets/image-anifriends-logo.png';
+import IoEyeOff from 'shared/assets/IoEyeOff';
+import IoEyeSharp from 'shared/assets/IoEyeSharp';
+import useToggle from 'shared/hooks/useToggle';
+import * as z from 'zod';
+
+type Schema = z.infer;
+
+const schema = z
+ .object({
+ email: z
+ .string()
+ .min(1, '이메일은 필수 정보입니다')
+ .email('유효하지 않은 이메일입니다'),
+ password: z
+ .string()
+ .regex(
+ /^(?=.*[!@#$%^&*()\-_=+[\]\\|{};:'",<.>/?]+)(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}$/,
+ '비밀번호는 필수 정보입니다(8자 이상)',
+ ),
+ passwordConfirm: z.string().min(1, '비밀번호 확인 정보는 필수입니다'),
+ name: z.string().min(1, '보호소 이름 정보는 필수입니다'),
+ address: z.string().min(1, '보호소 주소 정보는 필수입니다'),
+ addressDetail: z.string().min(1, '보호소 상세주소 정보는 필수입니다'),
+ isOpendedAddress: z.boolean(),
+ phoneNumber: z
+ .string()
+ .min(1, '보호소 전화번호 정보는 필수입니다')
+ .refine(
+ (val) => !Number.isNaN(Number(val)),
+ '전화번호 형식은 숫자입니다',
+ ),
+ sparePhoneNumber: z
+ .string()
+ .refine(
+ (val) => !Number.isNaN(Number(val)) || val === '',
+ '전화번호 형식은 숫자입니다',
+ ),
+ })
+ .refine(({ password, passwordConfirm }) => password === passwordConfirm, {
+ message: '비밀번호가 일치하지 않습니다',
+ path: ['passwordConfirm'],
+ });
+
+export default function SignupPage() {
+ const [isPasswordShow, togglePasswordShow] = useToggle();
+ const [isPasswordConfirmShow, togglePasswordConfirmShow] = useToggle();
+ const {
+ register,
+ handleSubmit,
+ formState: { errors },
+ control,
+ } = useForm({
+ defaultValues: {
+ isOpendedAddress: false,
+ },
+ resolver: zodResolver(schema),
+ });
+
+ const onSubmit = (data: Schema) => {
+ console.log(data);
+ };
+
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/apps/shelter/src/pages/volunteers/_components/RecruitDateText.tsx b/apps/shelter/src/pages/volunteers/_components/RecruitDateText.tsx
new file mode 100644
index 00000000..2a2f686a
--- /dev/null
+++ b/apps/shelter/src/pages/volunteers/_components/RecruitDateText.tsx
@@ -0,0 +1,15 @@
+import { Text } from '@chakra-ui/react';
+
+type RecruitDateTextProps = {
+ title: string;
+ date: string;
+ time: string;
+};
+
+export default function RecruitDateText({
+ title,
+ date,
+ time,
+}: RecruitDateTextProps) {
+ return {`${title} | ${date} • ${time}`};
+}
diff --git a/apps/shelter/src/pages/volunteers/_components/RecruitItem.tsx b/apps/shelter/src/pages/volunteers/_components/RecruitItem.tsx
new file mode 100644
index 00000000..e13fe287
--- /dev/null
+++ b/apps/shelter/src/pages/volunteers/_components/RecruitItem.tsx
@@ -0,0 +1,233 @@
+import {
+ Box,
+ Button,
+ Flex,
+ HStack,
+ Image,
+ Menu,
+ MenuButton,
+ MenuItem,
+ MenuList,
+ Spacer,
+ Text,
+ VStack,
+} from '@chakra-ui/react';
+import MenuIcon from 'shared/assets/icon_menu.svg';
+import ApplicantStatus from 'shared/components/ApplicantStatus';
+import Label from 'shared/components/Label';
+import LabelText from 'shared/components/LabelText';
+import { createFormattedTime, getDDay } from 'shared/utils/date';
+
+import RecruitDateText from './RecruitDateText';
+
+type Recruitment = {
+ recruitmentId: number;
+ recruitmentTitle: string;
+ recruitmentStartTime: string;
+ recruitmentEndTime: string;
+ recruitmentDeadline: string;
+ recruitmentIsClosed: boolean;
+ recruitmentApplicantCount: number;
+ recruitmentCapacity: number;
+};
+
+type RecruitItemProps = {
+ showMenuButton?: boolean;
+ onUpdate?: VoidFunction;
+ onDelete?: VoidFunction;
+ onClickManageApplyButton: VoidFunction;
+ onClickManageAttendanceButton: VoidFunction;
+ onClickCloseRecruitButton: VoidFunction;
+} & Recruitment;
+
+export default function RecruitItem({
+ showMenuButton = true,
+ onUpdate = () => {},
+ onDelete = () => {},
+ recruitmentId,
+ recruitmentTitle,
+ recruitmentStartTime,
+ recruitmentEndTime,
+ recruitmentDeadline,
+ recruitmentIsClosed,
+ recruitmentApplicantCount,
+ recruitmentCapacity,
+ onClickCloseRecruitButton,
+ onClickManageApplyButton,
+ onClickManageAttendanceButton,
+}: RecruitItemProps) {
+ return (
+
+
+
+ {recruitmentIsClosed ? (
+
+ ) : (
+
+ )}
+
+ {recruitmentTitle}
+
+
+
+
+
+
+
+
+
+
+ {recruitmentIsClosed ? (
+
+ ) : (
+
+ )}
+
+ {showMenuButton && }
+
+ );
+}
+
+function RecruitingButtons({
+ onClickManageApplyButton,
+ onClickCloseRecruitButton,
+}: Pick<
+ RecruitItemProps,
+ 'onClickCloseRecruitButton' | 'onClickManageApplyButton'
+>) {
+ return (
+
+
+
+
+ );
+}
+
+function AttendanceManagementButton({
+ onClickManageAttendanceButton,
+}: Pick) {
+ return (
+
+ );
+}
+
+function CustomMenu({
+ onUpdate,
+ onDelete,
+}: {
+ onUpdate: VoidFunction;
+ onDelete: VoidFunction;
+}) {
+ return (
+
+ );
+}
diff --git a/apps/shelter/src/pages/volunteers/detail/AlertModal.tsx b/apps/shelter/src/pages/volunteers/detail/AlertModal.tsx
new file mode 100644
index 00000000..a28edc70
--- /dev/null
+++ b/apps/shelter/src/pages/volunteers/detail/AlertModal.tsx
@@ -0,0 +1,52 @@
+import {
+ Button,
+ Modal,
+ ModalBody,
+ ModalCloseButton,
+ ModalContent,
+ ModalFooter,
+ ModalHeader,
+ ModalOverlay,
+ ModalProps,
+} from '@chakra-ui/react';
+
+type AlertModalProps = {
+ modalTitle: string;
+ modalContent: string;
+ onClick?: VoidFunction;
+} & Omit;
+
+export default function AlertModal({
+ modalTitle,
+ modalContent,
+ isOpen,
+ onClose,
+ onClick,
+}: AlertModalProps) {
+ return (
+
+
+
+
+ {modalTitle}
+
+
+ {modalContent}
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/shelter/src/pages/volunteers/detail/index.tsx b/apps/shelter/src/pages/volunteers/detail/index.tsx
new file mode 100644
index 00000000..da7a2ced
--- /dev/null
+++ b/apps/shelter/src/pages/volunteers/detail/index.tsx
@@ -0,0 +1,138 @@
+import {
+ Box,
+ Button,
+ Divider,
+ HStack,
+ Text,
+ useDisclosure,
+ VStack,
+} from '@chakra-ui/react';
+import { useEffect, useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import ImageCarousel from 'shared/components/ImageCarousel';
+import InfoTextList from 'shared/components/InfoTextList';
+import { LabelProps } from 'shared/components/Label';
+import LabelText from 'shared/components/LabelText';
+import useDetailHeaderStore from 'shared/store/detailHeaderStore';
+
+import AlertModal from './AlertModal';
+
+const handleDeletePost = (postId: number) => {
+ // TODO: VolunteerPost delete API 호출
+ console.log('[Delete Volunteer] postId:', postId);
+};
+
+export default function VolunteersDetailPage() {
+ const setOnDelete = useDetailHeaderStore((state) => state.setOnDelete);
+ const postId = 5;
+ useEffect(() => {
+ setOnDelete(handleDeletePost);
+
+ return () => {
+ setOnDelete(() => {});
+ };
+ }, [setOnDelete]);
+
+ const navigate = useNavigate();
+
+ const { isOpen, onOpen, onClose } = useDisclosure();
+ const [label, setLabel] = useState({
+ labelTitle: '모집 중',
+ type: 'GREEN',
+ });
+ const [isClosed, setIsClosed] = useState(false);
+
+ const goManageApply = () => navigate(`/manage/apply/${postId}`);
+ const goManageAttendance = () => navigate(`/manage/attendance/${postId}`);
+ const onVolunteerDeadline = () => {
+ onClose();
+ //TODO label type gray로 변경
+ setLabel({ labelTitle: '모집 마감', type: 'ORANGE' });
+ setIsClosed(!isClosed);
+ };
+
+ return (
+
+
+
+
+
+ 강아지 목욕 봉사자를 모집합니다!
+
+
+ 작성일 | 2023.10.23(수정됨)
+
+
+
+
+
+
+
+ 강아지 봉사자를 모집합니다~~~강아지 봉사자를 모집합니다~~~강아지
+ 봉사자를 모집합니다~~~강아지 봉사자를 모집합니다~~~강아지 봉사자를
+ 모집합니다~~~
+
+
+ {isClosed ? (
+
+ ) : (
+ <>
+
+
+ >
+ )}
+
+
+
+ );
+}
diff --git a/apps/shelter/src/pages/volunteers/hooks/useFetchVolunteers.tsx b/apps/shelter/src/pages/volunteers/hooks/useFetchVolunteers.tsx
new file mode 100644
index 00000000..225641cd
--- /dev/null
+++ b/apps/shelter/src/pages/volunteers/hooks/useFetchVolunteers.tsx
@@ -0,0 +1,14 @@
+import { useSuspenseInfiniteQuery } from '@tanstack/react-query';
+
+import { getShelterRecruitments } from '@/apis/recruitment';
+
+export default function useFetchVolunteers(pageSize: number) {
+ return useSuspenseInfiniteQuery({
+ queryKey: ['recruitments'],
+ queryFn: ({ pageParam }) =>
+ getShelterRecruitments({ pageNumber: pageParam, pageSize }),
+ initialPageParam: 0,
+ getNextPageParam: ({ data: { pageInfo } }, _, lastPageParam) =>
+ pageInfo.hasNext ? lastPageParam + 1 : null,
+ });
+}
diff --git a/apps/shelter/src/pages/volunteers/index.tsx b/apps/shelter/src/pages/volunteers/index.tsx
new file mode 100644
index 00000000..c6fbe012
--- /dev/null
+++ b/apps/shelter/src/pages/volunteers/index.tsx
@@ -0,0 +1,72 @@
+import { Suspense } from 'react';
+import { useNavigate } from 'react-router-dom';
+
+import useIntersect from '@/hooks/useIntersection';
+
+import RecruitItem from './_components/RecruitItem';
+import useFetchVolunteers from './hooks/useFetchVolunteers';
+
+const PAGE_SIZE = 10;
+
+function Recruitments() {
+ const navigate = useNavigate();
+
+ const goToManageApplyPage = (postId: number) => {
+ navigate(`/manage/apply/${postId}`);
+ };
+ const goToManageAttendancePage = (postId: number) => {
+ navigate(`/manage/attendance/${postId}`);
+ };
+ const goToUpdatePage = (postId: number) => {
+ navigate(`/volunteers/write/${postId}`);
+ };
+
+ //TODO 삭제 버튼 눌렀을 때 기능 추가
+
+ //TODO recruit id 받아서 마감
+ const closeRecruit = () => {};
+
+ const {
+ data: { pages },
+ hasNextPage,
+ isFetchingNextPage,
+ fetchNextPage,
+ } = useFetchVolunteers(PAGE_SIZE);
+
+ const recruitments = pages.flatMap(({ data }) => data.recruitments);
+
+ const ref = useIntersect(async (entry, observer) => {
+ observer.unobserve(entry.target);
+ if (hasNextPage && !isFetchingNextPage) {
+ fetchNextPage();
+ }
+ });
+
+ return (
+ <>
+ {recruitments.map((recruitment) => (
+
+ goToManageApplyPage(recruitment.recruitmentId)
+ }
+ onClickManageAttendanceButton={() =>
+ goToManageAttendancePage(recruitment.recruitmentId)
+ }
+ onClickCloseRecruitButton={closeRecruit}
+ onUpdate={() => goToUpdatePage(recruitment.recruitmentId)}
+ />
+ ))}
+
+ >
+ );
+}
+
+export default function VolunteersPage() {
+ return (
+ 글목록 로딩중...}>
+
+
+ );
+}
diff --git a/apps/shelter/src/pages/volunteers/profile/index.tsx b/apps/shelter/src/pages/volunteers/profile/index.tsx
new file mode 100644
index 00000000..dd8105c0
--- /dev/null
+++ b/apps/shelter/src/pages/volunteers/profile/index.tsx
@@ -0,0 +1,3 @@
+export default function VolunteersProfilePage() {
+ return VolunteersProfilePage
;
+}
diff --git a/apps/shelter/src/pages/volunteers/search/_components/FilterGroup.tsx b/apps/shelter/src/pages/volunteers/search/_components/FilterGroup.tsx
new file mode 100644
index 00000000..505db8ef
--- /dev/null
+++ b/apps/shelter/src/pages/volunteers/search/_components/FilterGroup.tsx
@@ -0,0 +1,30 @@
+import { Flex } from '@chakra-ui/react';
+import { ReactNode } from 'react';
+
+type FilterGroupProps = {
+ children: ReactNode;
+};
+
+export default function FilterGroup({ children }: FilterGroupProps) {
+ return (
+
+
+ {children}
+
+
+ );
+}
diff --git a/apps/shelter/src/pages/volunteers/search/_components/FilterSelect.tsx b/apps/shelter/src/pages/volunteers/search/_components/FilterSelect.tsx
new file mode 100644
index 00000000..db497d10
--- /dev/null
+++ b/apps/shelter/src/pages/volunteers/search/_components/FilterSelect.tsx
@@ -0,0 +1,9 @@
+import { Select, SelectProps } from '@chakra-ui/react';
+
+export default function FilterSelect({ children, ...props }: SelectProps) {
+ return (
+
+ );
+}
diff --git a/apps/shelter/src/pages/volunteers/search/_constants/filter.ts b/apps/shelter/src/pages/volunteers/search/_constants/filter.ts
new file mode 100644
index 00000000..be339dcf
--- /dev/null
+++ b/apps/shelter/src/pages/volunteers/search/_constants/filter.ts
@@ -0,0 +1,16 @@
+export const PERIOD = {
+ WITHIN_ONE_DAY: 'previousDay',
+ WITHIN_ONE_WEEK: 'previousWeek',
+ WITHIN_ONE_MONTH: 'previousMonth',
+ CUSTOM_PERIOD: 'customPeriod',
+} as const;
+
+export const RECRUITMENT_STATUS = {
+ IS_OPEN: 'isOpen',
+ IS_CLOSED: 'isClosed',
+} as const;
+
+export const CATEGORY = {
+ TITLE: 'title',
+ CONTENT: 'content',
+} as const;
diff --git a/apps/shelter/src/pages/volunteers/search/_hooks/useSearchFilter.ts b/apps/shelter/src/pages/volunteers/search/_hooks/useSearchFilter.ts
new file mode 100644
index 00000000..43658a7e
--- /dev/null
+++ b/apps/shelter/src/pages/volunteers/search/_hooks/useSearchFilter.ts
@@ -0,0 +1,62 @@
+import { useEffect, useState } from 'react';
+import { useSearchParams } from 'react-router-dom';
+import useSearchHeaderStore from 'shared/store/searchHeaderStore';
+
+const parseSearchParams = (searchParams: URLSearchParams) => {
+ const params: Record = {};
+ for (const [key, value] of searchParams) {
+ params[key] = value;
+ }
+ return params;
+};
+
+export const useSearchFilter = (
+ onSearch: (filter: SearchFilter) => void,
+): [SearchFilter, (filter: SearchFilter) => void] => {
+ const setKeyword = useSearchHeaderStore((state) => state.setKeyword);
+
+ const [filter, setFilter] = useState({} as SearchFilter);
+ const [searchParams, setSearchParams] = useSearchParams();
+
+ useEffect(() => {
+ if (Object.keys(filter).length === 0) {
+ return;
+ }
+
+ onSearch(filter);
+ }, [filter, onSearch]);
+
+ useEffect(() => {
+ if (searchParams.size === 0) {
+ return;
+ }
+
+ const params = parseSearchParams(searchParams);
+
+ if (params.keyword) {
+ setKeyword(params.keyword);
+ }
+
+ setFilter(params as SearchFilter);
+ }, [searchParams, setKeyword]);
+
+ const createSearchParams = (filter: SearchFilter) => {
+ const keys = Object.keys(filter);
+
+ return keys.reduce(
+ (params, key) => {
+ return filter[key as keyof SearchFilter]
+ ? { ...params, [key]: String(filter[key as keyof SearchFilter]) }
+ : params;
+ },
+ {} as Record,
+ );
+ };
+
+ const setFilterValue = (filter: SearchFilter) => {
+ setSearchParams(createSearchParams(filter), { replace: true });
+ setFilter(filter);
+ };
+
+ return [filter, setFilterValue];
+};
diff --git a/apps/shelter/src/pages/volunteers/search/_hooks/useVolunteerSearch.ts b/apps/shelter/src/pages/volunteers/search/_hooks/useVolunteerSearch.ts
new file mode 100644
index 00000000..a36503f9
--- /dev/null
+++ b/apps/shelter/src/pages/volunteers/search/_hooks/useVolunteerSearch.ts
@@ -0,0 +1,55 @@
+import { useEffect, useState } from 'react';
+import useSearchHeaderStore from 'shared/store/searchHeaderStore';
+
+import { useVolunteerSearchFilter } from '@/pages/volunteers/search/_hooks/useVolunteerSearchFilter';
+
+const DUMMY_RECRUITMENT = {
+ recruitmentId: 1,
+ recruitmentTitle: '봉사자를 모집합니다',
+ recruitmentStartTime: '2021-11-08T11:44:30.327959',
+ recruitmentEndTime: '2021-11-08T11:44:30.327959',
+ recruitmentDeadline: '2021-11-08T11:44:30.327959',
+ recruitmentIsClosed: false,
+ recruitmentApplicantCount: 15,
+ recruitmentCapacity: 15,
+};
+
+export const useVolunteerSearch = () => {
+ const [recruitmentList, setRecruitmentList] = useState([]);
+
+ useEffect(() => {
+ setRecruitmentList(Array.from({ length: 10 }, () => DUMMY_RECRUITMENT));
+ }, []);
+
+ const searchAPI = async () => {
+ // TODO: API call
+ };
+
+ const {
+ isSearched,
+ setKeywordFilter,
+ volunteerSearchFilter,
+ setVolunteerSearchFilter,
+ } = useVolunteerSearchFilter(searchAPI);
+
+ const [setOnSearch, setKeyword] = useSearchHeaderStore((state) => [
+ state.setOnSearch,
+ state.setKeyword,
+ ]);
+
+ useEffect(() => {
+ setOnSearch(setKeywordFilter);
+
+ return () => {
+ setKeyword('');
+ setOnSearch(() => {});
+ };
+ }, []);
+
+ return {
+ isSearched,
+ recruitmentList,
+ volunteerSearchFilter,
+ setVolunteerSearchFilter,
+ };
+};
diff --git a/apps/shelter/src/pages/volunteers/search/_hooks/useVolunteerSearchFilter.ts b/apps/shelter/src/pages/volunteers/search/_hooks/useVolunteerSearchFilter.ts
new file mode 100644
index 00000000..0132c3b7
--- /dev/null
+++ b/apps/shelter/src/pages/volunteers/search/_hooks/useVolunteerSearchFilter.ts
@@ -0,0 +1,89 @@
+import { ChangeEvent, useEffect, useState } from 'react';
+
+import { useSearchFilter } from '@/pages/volunteers/search/_hooks/useSearchFilter';
+import {
+ Category,
+ RecruitmentStatus,
+ SearchFilter,
+ VolunteerSearchFilter,
+} from '@/pages/volunteers/search/_types/filter';
+import {
+ createCategorySearchFilter,
+ createPeriodSearchFilter,
+ createRecruitmentStatusSearchFilter,
+ createVolunteerSearchFilter,
+} from '@/pages/volunteers/search/_utils/createFilter';
+
+export const useVolunteerSearchFilter = (searchAPI: () => void) => {
+ const search = (filter: SearchFilter) => {
+ console.log('filter', filter);
+ // TODO: filter 를 통해 request 객체 가공하기
+ // request = createSearchRequest(filter);
+ // searchAPI(request);
+ searchAPI();
+ };
+
+ const [searchFilter, setSearchFilter] = useSearchFilter(search);
+
+ const [volunteerSearchFilter, setVolunteerSearchFilter] =
+ useState({} as VolunteerSearchFilter);
+
+ useEffect(() => {
+ const newVolunteerSearchFilter = createVolunteerSearchFilter(searchFilter);
+
+ setVolunteerSearchFilter({
+ ...volunteerSearchFilter,
+ ...newVolunteerSearchFilter,
+ });
+ }, [searchFilter]);
+
+ const setPeriod = (event: ChangeEvent) => {
+ const { value } = event.target;
+
+ setVolunteerSearchFilter({
+ ...volunteerSearchFilter,
+ period: value,
+ });
+
+ const newFilter = createPeriodSearchFilter(value);
+
+ setSearchFilter({ ...searchFilter, ...newFilter });
+ };
+
+ const setRecruitmentStatus = (event: ChangeEvent) => {
+ const { value } = event.target;
+
+ setVolunteerSearchFilter({
+ ...volunteerSearchFilter,
+ recruitmentStatus: value as RecruitmentStatus,
+ });
+
+ const newFilter = createRecruitmentStatusSearchFilter(value);
+
+ setSearchFilter({ ...searchFilter, ...newFilter });
+ };
+
+ const setCategory = (event: ChangeEvent) => {
+ const { value } = event.target;
+
+ setVolunteerSearchFilter({
+ ...volunteerSearchFilter,
+ category: value as Category,
+ });
+
+ const newFilter = createCategorySearchFilter(value);
+
+ setSearchFilter({ ...searchFilter, ...newFilter });
+ };
+
+ const setKeywordFilter = (keyword: string) => {
+ setSearchFilter({ ...searchFilter, keyword });
+ };
+
+ return {
+ isSearched: Boolean(searchFilter.keyword),
+ setKeywordFilter,
+ volunteerSearchFilter,
+ setVolunteerSearchFilter: { setPeriod, setRecruitmentStatus, setCategory },
+ };
+};
diff --git a/apps/shelter/src/pages/volunteers/search/_types/filter.ts b/apps/shelter/src/pages/volunteers/search/_types/filter.ts
new file mode 100644
index 00000000..cb0d0896
--- /dev/null
+++ b/apps/shelter/src/pages/volunteers/search/_types/filter.ts
@@ -0,0 +1,24 @@
+import {
+ CATEGORY,
+ RECRUITMENT_STATUS,
+} from '@/pages/volunteers/search/_constants/filter';
+
+export type SearchFilter = Partial<{
+ keyword: string;
+ startDate: string;
+ endDate: string;
+ isClosed: string;
+ content: string;
+ title: string;
+}>;
+
+export type RecruitmentStatus =
+ (typeof RECRUITMENT_STATUS)[keyof typeof RECRUITMENT_STATUS];
+
+export type Category = (typeof CATEGORY)[keyof typeof CATEGORY];
+
+export type VolunteerSearchFilter = Partial<{
+ period: string;
+ recruitmentStatus: RecruitmentStatus;
+ category: Category;
+}>;
diff --git a/apps/shelter/src/pages/volunteers/search/_utils/createFilter.ts b/apps/shelter/src/pages/volunteers/search/_utils/createFilter.ts
new file mode 100644
index 00000000..0de29e3f
--- /dev/null
+++ b/apps/shelter/src/pages/volunteers/search/_utils/createFilter.ts
@@ -0,0 +1,121 @@
+import { MILISECONDS } from 'shared/constants/date';
+import { createFormattedTime, isSameDay } from 'shared/utils/date';
+
+import {
+ CATEGORY,
+ PERIOD,
+ RECRUITMENT_STATUS,
+} from '@/pages/volunteers/search/_constants/filter';
+import {
+ SearchFilter,
+ VolunteerSearchFilter,
+} from '@/pages/volunteers/search/_types/filter';
+
+const createPeriod = (startDate?: string, endDate?: string) => {
+ if (!startDate) {
+ return endDate;
+ }
+ if (!endDate) {
+ return startDate;
+ }
+
+ const start = new Date(startDate);
+ const end = new Date(endDate);
+ const today = new Date();
+
+ if (isSameDay(start, today)) {
+ const diff = (end.getTime() - start.getTime()) / 1000;
+
+ if (diff === MILISECONDS.DAY) {
+ return PERIOD.WITHIN_ONE_DAY;
+ }
+ if (diff === MILISECONDS.DAY * 7) {
+ return PERIOD.WITHIN_ONE_WEEK;
+ }
+ if (diff === MILISECONDS.MONTH) {
+ return PERIOD.WITHIN_ONE_MONTH;
+ }
+ }
+
+ return `${startDate}~${endDate}`;
+};
+
+export const createVolunteerSearchFilter = (
+ filter: SearchFilter,
+): VolunteerSearchFilter => {
+ const { startDate, endDate, isClosed, title, content } = filter;
+
+ const volunteerSearchFilter: VolunteerSearchFilter = {};
+
+ if (startDate || endDate) {
+ const period = createPeriod(startDate, endDate);
+ volunteerSearchFilter.period = period;
+ }
+
+ if (isClosed) {
+ volunteerSearchFilter.recruitmentStatus =
+ isClosed === String(true) ? 'isClosed' : 'isOpen';
+ }
+
+ if (title) {
+ volunteerSearchFilter.category = 'title';
+ }
+
+ if (content) {
+ volunteerSearchFilter.category = 'content';
+ }
+
+ return volunteerSearchFilter;
+};
+
+export const createPeriodSearchFilter = (value: string): SearchFilter => {
+ if (Object.values(PERIOD).every((period) => period !== value)) {
+ return { startDate: undefined, endDate: undefined };
+ }
+
+ const startDate = new Date();
+ const endDate = new Date();
+
+ if (value === PERIOD.WITHIN_ONE_DAY) {
+ endDate.setDate(startDate.getDate() + 1);
+ }
+
+ if (value === PERIOD.WITHIN_ONE_WEEK) {
+ endDate.setDate(startDate.getDate() + 7);
+ }
+
+ if (value === PERIOD.WITHIN_ONE_MONTH) {
+ endDate.setMonth(startDate.getMonth() + 1);
+ }
+
+ return {
+ startDate: createFormattedTime(startDate, 'YYYY-MM-DD'),
+ endDate: createFormattedTime(endDate, 'YYYY-MM-DD'),
+ };
+};
+
+export const createRecruitmentStatusSearchFilter = (
+ value: string,
+): SearchFilter => {
+ if (value === RECRUITMENT_STATUS.IS_CLOSED) {
+ return { isClosed: String(true) };
+ }
+
+ if (value === RECRUITMENT_STATUS.IS_OPEN) {
+ return { isClosed: String(false) };
+ }
+
+ return { isClosed: undefined };
+};
+
+export const createCategorySearchFilter = (value: string): SearchFilter => {
+ if (value === CATEGORY.TITLE) {
+ return { title: String(true), content: undefined };
+ }
+
+ if (value === CATEGORY.CONTENT) {
+ return { title: undefined, content: String(true) };
+ }
+
+ return { title: undefined, content: undefined };
+};
diff --git a/apps/shelter/src/pages/volunteers/search/index.tsx b/apps/shelter/src/pages/volunteers/search/index.tsx
new file mode 100644
index 00000000..01b93c8c
--- /dev/null
+++ b/apps/shelter/src/pages/volunteers/search/index.tsx
@@ -0,0 +1,67 @@
+import { Box } from '@chakra-ui/react';
+
+import RecruitItem from '@/pages/volunteers/_components/RecruitItem';
+import FilterGroup from '@/pages/volunteers/search/_components/FilterGroup';
+import FilterSelect from '@/pages/volunteers/search/_components/FilterSelect';
+import {
+ CATEGORY,
+ PERIOD,
+ RECRUITMENT_STATUS,
+} from '@/pages/volunteers/search/_constants/filter';
+import { useVolunteerSearch } from '@/pages/volunteers/search/_hooks/useVolunteerSearch';
+
+export default function VolunteersSearchPage() {
+ const {
+ isSearched,
+ recruitmentList,
+ volunteerSearchFilter,
+ setVolunteerSearchFilter,
+ } = useVolunteerSearch();
+
+ const { period, recruitmentStatus, category } = volunteerSearchFilter;
+ const { setPeriod, setRecruitmentStatus, setCategory } =
+ setVolunteerSearchFilter;
+
+ if (!isSearched) {
+ return null;
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {recruitmentList?.map((recruitmentItem: any) => (
+
+ ))}
+
+ );
+}
diff --git a/apps/shelter/src/pages/volunteers/update/index.tsx b/apps/shelter/src/pages/volunteers/update/index.tsx
new file mode 100644
index 00000000..d005acc1
--- /dev/null
+++ b/apps/shelter/src/pages/volunteers/update/index.tsx
@@ -0,0 +1,3 @@
+export default function VolunteersUpdatePage() {
+ return VolunteersUpdatePage
;
+}
diff --git a/apps/shelter/src/pages/volunteers/write/index.tsx b/apps/shelter/src/pages/volunteers/write/index.tsx
new file mode 100644
index 00000000..66925bc1
--- /dev/null
+++ b/apps/shelter/src/pages/volunteers/write/index.tsx
@@ -0,0 +1,3 @@
+export default function VolunteersWritePage() {
+ return VolunteersWritePage
;
+}
diff --git a/apps/shelter/src/routes/index.tsx b/apps/shelter/src/routes/index.tsx
new file mode 100644
index 00000000..1537e753
--- /dev/null
+++ b/apps/shelter/src/routes/index.tsx
@@ -0,0 +1,179 @@
+import { createBrowserRouter, RouterProviderProps } from 'react-router-dom';
+import APP_TYPE from 'shared/constants/appType';
+import PAGE_TYPE from 'shared/constants/pageType';
+import Layout from 'shared/layout';
+
+import PATH from '@/constants/path';
+import AnimalsPage from '@/pages/animals';
+import AnimalsDetailPage from '@/pages/animals/detail';
+import AnimalsSearchPage from '@/pages/animals/search';
+import AnimalsUpdatePage from '@/pages/animals/update';
+import AnimalsWritePage from '@/pages/animals/write';
+import ChattingsPage from '@/pages/chattings';
+import ChattingsRoomPage from '@/pages/chattings/room';
+import ManageApplyPage from '@/pages/manage/apply';
+import ManageAttendancePage from '@/pages/manage/attendance';
+import MyPage from '@/pages/my';
+import MyReviewsPage from '@/pages/my/reviews';
+import NotFoundPage from '@/pages/notfound';
+import NotificationsPage from '@/pages/notifications';
+import SettingsAccountPage from '@/pages/settings/account';
+import SettingsPasswordPage from '@/pages/settings/password';
+import SigninPage from '@/pages/signin';
+import SignupPage from '@/pages/signup';
+import VolunteersPage from '@/pages/volunteers';
+import VolunteersDetailPage from '@/pages/volunteers/detail';
+import VolunteersProfilePage from '@/pages/volunteers/profile';
+import VolunteersSearchPage from '@/pages/volunteers/search';
+import VolunteersUpdatePage from '@/pages/volunteers/update';
+import VolunteersWritePage from '@/pages/volunteers/write';
+
+export const router: RouterProviderProps['router'] = createBrowserRouter([
+ {
+ path: '/',
+ element: ,
+ errorElement: ,
+ children: [
+ {
+ path: PATH.VOLUNTEERS.INDEX,
+ children: [
+ {
+ id: PAGE_TYPE.VOLUNTEERS,
+ index: true,
+ element: ,
+ },
+ {
+ id: PAGE_TYPE.VOLUNTEERS_DETAIL,
+ path: PATH.VOLUNTEERS.DETAIL,
+ element: ,
+ },
+ {
+ id: PAGE_TYPE.VOLUNTEERS_PROFILE,
+ path: PATH.VOLUNTEERS.PROFILE,
+ element: ,
+ },
+ {
+ id: PAGE_TYPE.VOLUNTEERS_SEARCH,
+ path: PATH.VOLUNTEERS.SEARCH,
+ element: ,
+ },
+ {
+ id: PAGE_TYPE.VOLUNTEERS_WRITE,
+ path: PATH.VOLUNTEERS.WRITE,
+ element: ,
+ },
+ {
+ id: PAGE_TYPE.VOLUNTEERS_UPDATE,
+ path: PATH.VOLUNTEERS.UPDATE,
+ element: ,
+ },
+ ],
+ },
+ {
+ path: PATH.ANIMALS.INDEX,
+ children: [
+ {
+ id: PAGE_TYPE.ANIMALS,
+ index: true,
+ element: ,
+ },
+ {
+ id: PAGE_TYPE.ANIMALS_DETAIL,
+ path: PATH.ANIMALS.DETAIL,
+ element: ,
+ },
+ {
+ id: PAGE_TYPE.ANIMALS_SEARCH,
+ path: PATH.ANIMALS.SEARCH,
+ element: ,
+ },
+ {
+ id: PAGE_TYPE.ANIMALS_WRITE,
+ path: PATH.ANIMALS.WRITE,
+ element: ,
+ },
+ {
+ id: PAGE_TYPE.ANIMALS_UPDATE,
+ path: PATH.ANIMALS.UPDATE,
+ element: ,
+ },
+ ],
+ },
+ {
+ path: PATH.CHATTINGS.INDEX,
+ children: [
+ {
+ id: PAGE_TYPE.CHATTINGS,
+ index: true,
+ element: ,
+ },
+ {
+ id: PAGE_TYPE.CHATTINGS_ROOM,
+ path: PATH.CHATTINGS.ROOM,
+ element: ,
+ },
+ ],
+ },
+ {
+ path: PATH.MYPAGE.INDEX,
+ children: [
+ {
+ id: PAGE_TYPE.MYPAGE,
+ index: true,
+ element: ,
+ },
+ {
+ id: PAGE_TYPE.MYPAGE_REVIEWS,
+ path: PATH.MYPAGE.REVIEWS,
+ element: ,
+ },
+ ],
+ },
+ {
+ path: PATH.SETTINGS.INDEX,
+ children: [
+ {
+ id: PAGE_TYPE.SETTINGS_ACCOUNT,
+ path: PATH.SETTINGS.ACCOUNT,
+ element: ,
+ },
+ {
+ id: PAGE_TYPE.SETTINGS_PASSWORD,
+ path: PATH.SETTINGS.PASSWORD,
+ element: ,
+ },
+ ],
+ },
+ {
+ path: PATH.MANAGE.INDEX,
+ children: [
+ {
+ id: PAGE_TYPE.MANAGE_ATTENDANCE,
+ path: PATH.MANAGE.ATTENDANCE,
+ element: ,
+ },
+ {
+ id: PAGE_TYPE.MANAGE_APPLY,
+ path: PATH.MANAGE.APPLY,
+ element: ,
+ },
+ ],
+ },
+ {
+ id: PAGE_TYPE.NOTIFICATIONS,
+ path: PATH.NOTIFICATIONS,
+ element: ,
+ },
+ {
+ id: PAGE_TYPE.SIGNUP,
+ path: PATH.SIGNUP,
+ element: ,
+ },
+ {
+ id: PAGE_TYPE.SIGNIN,
+ path: PATH.SIGNIN,
+ element: ,
+ },
+ ],
+ },
+]);
diff --git a/apps/shelter/src/types/apis/auth.ts b/apps/shelter/src/types/apis/auth.ts
new file mode 100644
index 00000000..80d03996
--- /dev/null
+++ b/apps/shelter/src/types/apis/auth.ts
@@ -0,0 +1,10 @@
+import { SigninRequestData } from 'shared/types/apis/auth';
+
+export type SignupRequestData = SigninRequestData & {
+ name: string;
+ address: string;
+ addressDetail: string;
+ phoneNumber: string;
+ sparePhoneNumber: string;
+ isOpenedAddress: boolean;
+};
diff --git a/apps/shelter/src/types/apis/shetler.ts b/apps/shelter/src/types/apis/shetler.ts
new file mode 100644
index 00000000..bf14ca0a
--- /dev/null
+++ b/apps/shelter/src/types/apis/shetler.ts
@@ -0,0 +1,11 @@
+export type ShelterInfo = {
+ shelterId: number;
+ shelterEmail: string;
+ shelterName: string;
+ shelterImageUrl: string;
+ shelterAddress: string;
+ shelterAddressDetail: string;
+ shelterPhoneNumber: string;
+ shelterSparePhoneNumber: string;
+ shelterIsOpenedAddress: boolean;
+};
diff --git a/apps/shelter/vercel.json b/apps/shelter/vercel.json
new file mode 100644
index 00000000..3a48e56b
--- /dev/null
+++ b/apps/shelter/vercel.json
@@ -0,0 +1,3 @@
+{
+ "rewrites": [{ "source": "/(.*)", "destination": "/" }]
+}
diff --git a/apps/volunteer/package.json b/apps/volunteer/package.json
index a13091cf..c506a9a6 100644
--- a/apps/volunteer/package.json
+++ b/apps/volunteer/package.json
@@ -6,7 +6,7 @@
"scripts": {
"build": "tsc && vite build",
"dev": "vite",
- "lint": "eslint \"src/**/*.ts\"",
+ "lint": "eslint \"src/**/*.{ts,tsx}\"",
"preview": "vite preview"
},
"dependencies": {
@@ -24,7 +24,7 @@
"react-error-boundary": "^4.0.11",
"react-hook-form": "^7.47.0",
"react-router-dom": "^6.17.0",
- "ui": "workspace:*",
+ "shared": "workspace:*",
"zod": "^3.22.4",
"zustand": "^4.4.4"
},
@@ -35,8 +35,12 @@
"@vitejs/plugin-react-swc": "^3.4.0",
"eslint": "^8.45.0",
"eslint-config-custom": "workspace:*",
+ "msw": "^2.0.1",
"tsconfig": "workspace:*",
"typescript": "^5.0.2",
"vite": "^4.4.5"
+ },
+ "msw": {
+ "workerDirectory": "public"
}
-}
+}
\ No newline at end of file
diff --git a/apps/volunteer/public/mockServiceWorker.js b/apps/volunteer/public/mockServiceWorker.js
new file mode 100644
index 00000000..3c94cff7
--- /dev/null
+++ b/apps/volunteer/public/mockServiceWorker.js
@@ -0,0 +1,292 @@
+/* eslint-disable */
+/* tslint:disable */
+
+/**
+ * Mock Service Worker (2.0.5).
+ * @see https://github.com/mswjs/msw
+ * - Please do NOT modify this file.
+ * - Please do NOT serve this file on production.
+ */
+
+const INTEGRITY_CHECKSUM = '0877fcdc026242810f5bfde0d7178db4'
+const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
+const activeClientIds = new Set()
+
+self.addEventListener('install', function () {
+ self.skipWaiting()
+})
+
+self.addEventListener('activate', function (event) {
+ event.waitUntil(self.clients.claim())
+})
+
+self.addEventListener('message', async function (event) {
+ const clientId = event.source.id
+
+ if (!clientId || !self.clients) {
+ return
+ }
+
+ const client = await self.clients.get(clientId)
+
+ if (!client) {
+ return
+ }
+
+ const allClients = await self.clients.matchAll({
+ type: 'window',
+ })
+
+ switch (event.data) {
+ case 'KEEPALIVE_REQUEST': {
+ sendToClient(client, {
+ type: 'KEEPALIVE_RESPONSE',
+ })
+ break
+ }
+
+ case 'INTEGRITY_CHECK_REQUEST': {
+ sendToClient(client, {
+ type: 'INTEGRITY_CHECK_RESPONSE',
+ payload: INTEGRITY_CHECKSUM,
+ })
+ break
+ }
+
+ case 'MOCK_ACTIVATE': {
+ activeClientIds.add(clientId)
+
+ sendToClient(client, {
+ type: 'MOCKING_ENABLED',
+ payload: true,
+ })
+ break
+ }
+
+ case 'MOCK_DEACTIVATE': {
+ activeClientIds.delete(clientId)
+ break
+ }
+
+ case 'CLIENT_CLOSED': {
+ activeClientIds.delete(clientId)
+
+ const remainingClients = allClients.filter((client) => {
+ return client.id !== clientId
+ })
+
+ // Unregister itself when there are no more clients
+ if (remainingClients.length === 0) {
+ self.registration.unregister()
+ }
+
+ break
+ }
+ }
+})
+
+self.addEventListener('fetch', function (event) {
+ const { request } = event
+
+ // Bypass navigation requests.
+ if (request.mode === 'navigate') {
+ return
+ }
+
+ // Opening the DevTools triggers the "only-if-cached" request
+ // that cannot be handled by the worker. Bypass such requests.
+ if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') {
+ return
+ }
+
+ // Bypass all requests when there are no active clients.
+ // Prevents the self-unregistered worked from handling requests
+ // after it's been deleted (still remains active until the next reload).
+ if (activeClientIds.size === 0) {
+ return
+ }
+
+ // Generate unique request ID.
+ const requestId = crypto.randomUUID()
+ event.respondWith(handleRequest(event, requestId))
+})
+
+async function handleRequest(event, requestId) {
+ const client = await resolveMainClient(event)
+ const response = await getResponse(event, client, requestId)
+
+ // Send back the response clone for the "response:*" life-cycle events.
+ // Ensure MSW is active and ready to handle the message, otherwise
+ // this message will pend indefinitely.
+ if (client && activeClientIds.has(client.id)) {
+ ;(async function () {
+ const responseClone = response.clone()
+ // When performing original requests, response body will
+ // always be a ReadableStream, even for 204 responses.
+ // But when creating a new Response instance on the client,
+ // the body for a 204 response must be null.
+ const responseBody = response.status === 204 ? null : responseClone.body
+
+ sendToClient(
+ client,
+ {
+ type: 'RESPONSE',
+ payload: {
+ requestId,
+ isMockedResponse: IS_MOCKED_RESPONSE in response,
+ type: responseClone.type,
+ status: responseClone.status,
+ statusText: responseClone.statusText,
+ body: responseBody,
+ headers: Object.fromEntries(responseClone.headers.entries()),
+ },
+ },
+ [responseBody],
+ )
+ })()
+ }
+
+ return response
+}
+
+// Resolve the main client for the given event.
+// Client that issues a request doesn't necessarily equal the client
+// that registered the worker. It's with the latter the worker should
+// communicate with during the response resolving phase.
+async function resolveMainClient(event) {
+ const client = await self.clients.get(event.clientId)
+
+ if (client?.frameType === 'top-level') {
+ return client
+ }
+
+ const allClients = await self.clients.matchAll({
+ type: 'window',
+ })
+
+ return allClients
+ .filter((client) => {
+ // Get only those clients that are currently visible.
+ return client.visibilityState === 'visible'
+ })
+ .find((client) => {
+ // Find the client ID that's recorded in the
+ // set of clients that have registered the worker.
+ return activeClientIds.has(client.id)
+ })
+}
+
+async function getResponse(event, client, requestId) {
+ const { request } = event
+
+ // Clone the request because it might've been already used
+ // (i.e. its body has been read and sent to the client).
+ const requestClone = request.clone()
+
+ function passthrough() {
+ const headers = Object.fromEntries(requestClone.headers.entries())
+
+ // Remove internal MSW request header so the passthrough request
+ // complies with any potential CORS preflight checks on the server.
+ // Some servers forbid unknown request headers.
+ delete headers['x-msw-intention']
+
+ return fetch(requestClone, { headers })
+ }
+
+ // Bypass mocking when the client is not active.
+ if (!client) {
+ return passthrough()
+ }
+
+ // Bypass initial page load requests (i.e. static assets).
+ // The absence of the immediate/parent client in the map of the active clients
+ // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet
+ // and is not ready to handle requests.
+ if (!activeClientIds.has(client.id)) {
+ return passthrough()
+ }
+
+ // Bypass requests with the explicit bypass header.
+ // Such requests can be issued by "ctx.fetch()".
+ const mswIntention = request.headers.get('x-msw-intention')
+ if (['bypass', 'passthrough'].includes(mswIntention)) {
+ return passthrough()
+ }
+
+ // Notify the client that a request has been intercepted.
+ const requestBuffer = await request.arrayBuffer()
+ const clientMessage = await sendToClient(
+ client,
+ {
+ type: 'REQUEST',
+ payload: {
+ id: requestId,
+ url: request.url,
+ mode: request.mode,
+ method: request.method,
+ headers: Object.fromEntries(request.headers.entries()),
+ cache: request.cache,
+ credentials: request.credentials,
+ destination: request.destination,
+ integrity: request.integrity,
+ redirect: request.redirect,
+ referrer: request.referrer,
+ referrerPolicy: request.referrerPolicy,
+ body: requestBuffer,
+ keepalive: request.keepalive,
+ },
+ },
+ [requestBuffer],
+ )
+
+ switch (clientMessage.type) {
+ case 'MOCK_RESPONSE': {
+ return respondWithMock(clientMessage.data)
+ }
+
+ case 'MOCK_NOT_FOUND': {
+ return passthrough()
+ }
+ }
+
+ return passthrough()
+}
+
+function sendToClient(client, message, transferrables = []) {
+ return new Promise((resolve, reject) => {
+ const channel = new MessageChannel()
+
+ channel.port1.onmessage = (event) => {
+ if (event.data && event.data.error) {
+ return reject(event.data.error)
+ }
+
+ resolve(event.data)
+ }
+
+ client.postMessage(
+ message,
+ [channel.port2].concat(transferrables.filter(Boolean)),
+ )
+ })
+}
+
+async function respondWithMock(response) {
+ // Setting response status code to 0 is a no-op.
+ // However, when responding with a "Response.error()", the produced Response
+ // instance will have status code set to 0. Since it's not possible to create
+ // a Response instance with status code 0, handle that use-case separately.
+ if (response.status === 0) {
+ return Response.error()
+ }
+
+ const mockedResponse = new Response(response.body, response)
+
+ Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, {
+ value: true,
+ enumerable: true,
+ })
+
+ return mockedResponse
+}
diff --git a/apps/volunteer/src/App.tsx b/apps/volunteer/src/App.tsx
index 775be3fd..e1198d0a 100644
--- a/apps/volunteer/src/App.tsx
+++ b/apps/volunteer/src/App.tsx
@@ -1,12 +1,22 @@
import { ChakraProvider } from '@chakra-ui/react';
-import { CustomButton, Header } from 'ui';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
+import { RouterProvider } from 'react-router-dom';
+import Fonts from 'shared/fonts';
+import theme from 'shared/theme';
+
+import { router } from '@/routes';
+
+const queryClient = new QueryClient();
export default function App() {
return (
-
-
- 봉사자 어플리케이션
-
-
+
+
+
+
+
+
+
);
}
diff --git a/apps/volunteer/src/apis/animal.ts b/apps/volunteer/src/apis/animal.ts
new file mode 100644
index 00000000..218c55ad
--- /dev/null
+++ b/apps/volunteer/src/apis/animal.ts
@@ -0,0 +1,55 @@
+import axiosInstance from 'shared/apis/axiosInstance';
+
+type PageInfo = {
+ totalElements: number;
+ hasNext: boolean;
+};
+
+type Animal = {
+ animalId: number;
+ animalName: string;
+ shelterName: string;
+ shelterAddress: string;
+ animalImage: string;
+};
+
+export const searchVolunteerAnimals = () =>
+ axiosInstance.get<
+ {
+ pageInfo: PageInfo;
+ animals: Animal[];
+ },
+ {
+ type: string;
+ gender: string;
+ isNeutered: boolean;
+ active: string;
+ size: string;
+ age: string;
+ pageNumber: number;
+ pageSize: number;
+ }
+ >('/volunteers/animals');
+
+type Shelter = {
+ shelterId: number;
+ name: string;
+ imageUrl: string;
+ email: string;
+ address: string;
+};
+
+export const getVolunteerAnimalDetail = (animalId: number) => {
+ return axiosInstance.get<{
+ name: string;
+ birthDate: string;
+ breed: string;
+ gender: string;
+ isNeutered: boolean;
+ active: string;
+ weight: number;
+ information: string;
+ animalImageUrls: string[];
+ shelter: Shelter;
+ }>(`/volunteers/animals/${animalId}`);
+};
diff --git a/apps/volunteer/src/apis/auth.ts b/apps/volunteer/src/apis/auth.ts
new file mode 100644
index 00000000..d1c91e9f
--- /dev/null
+++ b/apps/volunteer/src/apis/auth.ts
@@ -0,0 +1,26 @@
+import axiosInstance from 'shared/apis/axiosInstance';
+import type {
+ checkDuplicatedEmailRequestData,
+ checkDuplicatedEmailResponseData,
+ SigninRequestData,
+ SigninResponseData,
+} from 'shared/types/apis/auth';
+
+import { SignupRequestData } from '@/types/apis/auth';
+
+export const signinVolunteer = async (data: SigninRequestData) =>
+ await axiosInstance.post(
+ '/auth/volunteers/login',
+ data,
+ );
+
+export const signupVolunteer = async (data: SignupRequestData) =>
+ await axiosInstance.post('/volunteers', data);
+
+export const checkDuplicatedVolunteerEmail = async (email: string) =>
+ await axiosInstance.post<
+ checkDuplicatedEmailResponseData,
+ checkDuplicatedEmailRequestData
+ >('/volunteers/email', {
+ email,
+ });
diff --git a/apps/volunteer/src/apis/recruitment.ts b/apps/volunteer/src/apis/recruitment.ts
new file mode 100644
index 00000000..41afd77f
--- /dev/null
+++ b/apps/volunteer/src/apis/recruitment.ts
@@ -0,0 +1,46 @@
+import axiosInstance from 'shared/apis/axiosInstance';
+
+export const ApplyRecruitments = (recruitmentId: string) =>
+ axiosInstance.post(`/recruitments/${recruitmentId}/apply`);
+
+type PageInfo = {
+ totalElements: number;
+ hasNext: boolean;
+};
+
+type Recruitment = {
+ recruitmentId: number;
+ recruitmentTitle: string;
+ recruitmentStartTime: string;
+ recruitmentEndTime: string;
+ recruitmentIsClosed: boolean;
+ recruitmentApplicantCount: number;
+ recruitmentCapacity: number;
+ shelterName: string;
+ shelterImageUrl: string;
+};
+
+type RecruitSearchParams = {
+ keyword: string;
+ startDate: string;
+ endDate: string;
+ isClosed: boolean;
+ content: boolean;
+ title: boolean;
+ shelterName: boolean;
+ pageNumber: number;
+ pageSize: number;
+};
+
+export const getRecruitments = (
+ recruitSearchParams: Partial,
+) =>
+ axiosInstance.get<
+ {
+ pageInfo: PageInfo;
+ recruitments: Recruitment[];
+ },
+ RecruitSearchParams
+ >('/recruitments', {
+ params: recruitSearchParams,
+ });
diff --git a/apps/volunteer/src/apis/review.ts b/apps/volunteer/src/apis/review.ts
new file mode 100644
index 00000000..5be72188
--- /dev/null
+++ b/apps/volunteer/src/apis/review.ts
@@ -0,0 +1,76 @@
+import axiosInstance from 'shared/apis/axiosInstance';
+
+type NewReviewParams = {
+ applicantId: number;
+ content: string;
+ imageUrls: string[];
+};
+
+type UpdatedReviewParams = {
+ content: string;
+ imageUrls: string[];
+};
+
+export const getVolunteerReviewDetail = (reviewId: number) =>
+ axiosInstance.get<{
+ reviewId: number;
+ content: string;
+ imageUrls: string[];
+ }>(`/reviews/${reviewId}`);
+
+export const createVolunteerNewReview = (newReviewParams: NewReviewParams) =>
+ axiosInstance.post(
+ '/volunteers/reviews',
+ newReviewParams,
+ );
+
+export const updateVolunteerReview = (
+ reviewId: string,
+ updatedReviewParams: UpdatedReviewParams,
+) =>
+ axiosInstance.patch(
+ `/volunteers/reviews/${reviewId}`,
+ updatedReviewParams,
+ );
+
+export const deleteVolunteerReview = (reviewId: string) =>
+ axiosInstance.delete(`/volunteers/reviews/${reviewId}`);
+
+type ReviewOnShelterParams = {
+ pageNumber: number;
+ pageSize: number;
+};
+
+type Review = {
+ reviewId: number;
+ volunteerEmail: string;
+ volunteerTemperature: number;
+ reviewCreatedAt: string;
+ reviewComtent: string;
+ reviewImageUrls: string[];
+};
+
+type PageInfo = {
+ totalElements: number;
+ hasNext: boolean;
+};
+
+type ReviewOnShelterResponse = {
+ pageInfo: PageInfo;
+ reviews: Review[];
+};
+
+export const getVolunteerReviewsOnShelter = (
+ shelterId: number,
+ pageNumber: number,
+ pageSize: number,
+) =>
+ axiosInstance.get(
+ `/shelters/${shelterId}/reviews`,
+ {
+ params: {
+ pageNumber,
+ pageSize,
+ },
+ },
+ );
diff --git a/apps/volunteer/src/apis/shelter.ts b/apps/volunteer/src/apis/shelter.ts
new file mode 100644
index 00000000..7ca40791
--- /dev/null
+++ b/apps/volunteer/src/apis/shelter.ts
@@ -0,0 +1,21 @@
+import axiosInstance from 'shared/apis/axiosInstance';
+
+export const getSimpleShelterProfile = (shelterId: number) =>
+ axiosInstance.get<{
+ shelterName: string;
+ shelterImageUrl: string;
+ shelterAddress: string;
+ shelterEmail: string;
+ }>(`/shelters/${shelterId}/profile/simple`);
+
+export const getShelterProfileDetail = (shelterId: number) =>
+ axiosInstance.get<{
+ shelterId: number;
+ shelterName: string;
+ shelterEmail: string;
+ shelterImageUrl: string;
+ shelterAddress: string;
+ shelterAddressDetail: string;
+ shelterPhoneNumber: string;
+ shelterSparePhoneNumber: string;
+ }>(`/shelters/${shelterId}/profile`);
diff --git a/apps/volunteer/src/apis/volunteer.ts b/apps/volunteer/src/apis/volunteer.ts
new file mode 100644
index 00000000..51f38d75
--- /dev/null
+++ b/apps/volunteer/src/apis/volunteer.ts
@@ -0,0 +1,66 @@
+import axiosInstance from 'shared/apis/axiosInstance';
+
+type MyInfoResponse = {
+ volunteerId: string;
+ volunteerEmail: string;
+ volunteerName: string;
+ volunteerBirthDate: string;
+ volunteerPhoneNumber: string;
+ volunteerTemperture: number;
+ volunteerCount: number;
+ volunteerImageUrl: string;
+ volunteerGender: 'FEMAIL' | 'MALE';
+};
+
+export const getMyVolunteerInfo = () =>
+ axiosInstance.get('/volunteers/me');
+
+type PasswordUpdateParams = {
+ newPassword: string;
+ oldPassword: string;
+};
+
+export const updateVolunteerPassword = (
+ passwordUpdateParams: PasswordUpdateParams,
+) => {
+ return axiosInstance.patch(
+ '/volunteers/me/password',
+ passwordUpdateParams,
+ );
+};
+
+type UpdateUserInfoParams = {
+ name: string;
+ gender: string;
+ birthData: string;
+ phoneNumber: string;
+ imageUrl: string;
+};
+
+export const updateVolunteerUserInfo = (
+ updateUserInfoParams: UpdateUserInfoParams,
+) =>
+ axiosInstance.patch(
+ '/volunteers/me',
+ updateUserInfoParams,
+ );
+
+type Applicant = {
+ recruitmentId: number;
+ recruitmentTitle: string;
+ recruitmentStartTime: string;
+ shelterName: string;
+ applicantId: number;
+ applicantStatus: string;
+ applicantIsWritedReview: boolean;
+};
+
+type ApplicantsResponse = {
+ applicants: Applicant[];
+};
+
+//봉사자가 신청한 봉사 리스트 조회
+export const getVolunteerApplicantList = () =>
+ axiosInstance.get('/volunteers/applicants');
+
+//TODO 봉사자가 작성한 후기 리스트 조회
diff --git a/apps/volunteer/src/constants/gender.ts b/apps/volunteer/src/constants/gender.ts
new file mode 100644
index 00000000..bc768cfb
--- /dev/null
+++ b/apps/volunteer/src/constants/gender.ts
@@ -0,0 +1,9 @@
+export const PERAON_GENDER_ENG = {
+ FEMALE: 'FEMALE',
+ MALE: 'MALE',
+} as const;
+
+export const PERAON_GENDER_KOR = {
+ FEMALE: '여성',
+ MALE: '남성',
+} as const;
diff --git a/apps/volunteer/src/constants/path.ts b/apps/volunteer/src/constants/path.ts
new file mode 100644
index 00000000..02c58b0d
--- /dev/null
+++ b/apps/volunteer/src/constants/path.ts
@@ -0,0 +1,35 @@
+const PATH = {
+ VOLUNTEERS: {
+ INDEX: 'volunteers',
+ DETAIL: ':id',
+ SEARCH: 'search',
+ },
+ ANIMALS: {
+ INDEX: 'animals',
+ DETAIL: ':id',
+ },
+ CHATTINGS: {
+ INDEX: 'chattings',
+ ROOM: 'chattings/:id',
+ },
+ MYPAGE: {
+ INDEX: 'mypage',
+ REVIEWS: 'mypage/reviews',
+ },
+ SETTINGS: {
+ INDEX: 'settings',
+ ACCOUNT: 'account',
+ PASSWORD: 'password',
+ },
+ SHELTERS: {
+ INDEX: 'shelters',
+ PROFILE: 'profile/:id',
+ REVIEWS_WRITE: 'reviews/write',
+ REVIEWS_UPDATE: 'reviews/write/:id',
+ },
+ NOTIFICATIONS: 'notifications',
+ SIGNUP: 'signup',
+ SIGNIN: 'signin',
+};
+
+export default PATH;
diff --git a/apps/volunteer/src/main.tsx b/apps/volunteer/src/main.tsx
index b2677542..d7ba7b9a 100644
--- a/apps/volunteer/src/main.tsx
+++ b/apps/volunteer/src/main.tsx
@@ -3,8 +3,22 @@ import ReactDOM from 'react-dom/client';
import App from './App.tsx';
-ReactDOM.createRoot(document.getElementById('root')!).render(
-
-
- ,
-);
+async function deferRender() {
+ if (import.meta.env.MODE !== 'development') {
+ return;
+ }
+
+ const { worker } = await import('./mocks/browser');
+
+ // `worker.start()` returns a Promise that resolves
+ // once the Service Worker is up and ready to intercept requests.
+ return worker.start();
+}
+
+deferRender().then(() => {
+ ReactDOM.createRoot(document.getElementById('root')!).render(
+
+
+ ,
+ );
+});
diff --git a/apps/volunteer/src/mocks/browser.ts b/apps/volunteer/src/mocks/browser.ts
new file mode 100644
index 00000000..d69b3505
--- /dev/null
+++ b/apps/volunteer/src/mocks/browser.ts
@@ -0,0 +1,5 @@
+import { setupWorker } from 'msw/browser';
+
+import { handlers } from './handlers/auth';
+
+export const worker = setupWorker(...handlers);
diff --git a/apps/volunteer/src/mocks/handlers/auth.ts b/apps/volunteer/src/mocks/handlers/auth.ts
new file mode 100644
index 00000000..e04c4df5
--- /dev/null
+++ b/apps/volunteer/src/mocks/handlers/auth.ts
@@ -0,0 +1,68 @@
+import { delay, http, HttpResponse } from 'msw';
+
+export const handlers = [
+ http.post('/auth/volunteers/login', async () => {
+ await delay(200);
+ return HttpResponse.json(
+ {
+ accessToken: 'accessToken',
+ userId: 1,
+ role: 'ROLE_VOLUNTEER',
+ },
+ { status: 200 },
+ );
+ }),
+ http.post('/auth/volunteers/login', async () => {
+ await delay(200);
+ return HttpResponse.json(
+ {
+ errorCode: 'AF002',
+ message: '이메일/비밀번호가 올바르지 않습니다',
+ },
+ { status: 400 },
+ );
+ }),
+ http.post('/auth/volunteers/login', async () => {
+ await delay(200);
+ return HttpResponse.json(
+ {
+ errorCode: 'AF001',
+ message: '잘못된 입력값입니다',
+ },
+ { status: 400 },
+ );
+ }),
+ http.post('/volunteers', async () => {
+ await delay(200);
+ return HttpResponse.json({}, { status: 200 });
+ }),
+ http.post('/volunteers', async () => {
+ await delay(200);
+ return HttpResponse.json(
+ {
+ errorCode: 'AF001',
+ message: '{입력값}이 잘못되었습니다.',
+ },
+ { status: 400 },
+ );
+ }),
+ http.post('/volunteers/email', async () => {
+ await delay(200);
+ return HttpResponse.json(
+ {
+ isDuplicated: false,
+ },
+ { status: 200 },
+ );
+ }),
+ http.post('/volunteers/email', async () => {
+ await delay(200);
+ return HttpResponse.json(
+ {
+ errorCode: 'AF001',
+ message: '잘못된 입력값입니다',
+ },
+ { status: 400 },
+ );
+ }),
+];
diff --git a/apps/volunteer/src/pages/animals/detail/index.tsx b/apps/volunteer/src/pages/animals/detail/index.tsx
new file mode 100644
index 00000000..49cf55b5
--- /dev/null
+++ b/apps/volunteer/src/pages/animals/detail/index.tsx
@@ -0,0 +1,3 @@
+export default function AnimalsDetailPage() {
+ return AnimalsDetailPage
;
+}
diff --git a/apps/volunteer/src/pages/animals/index.tsx b/apps/volunteer/src/pages/animals/index.tsx
new file mode 100644
index 00000000..c11ddc43
--- /dev/null
+++ b/apps/volunteer/src/pages/animals/index.tsx
@@ -0,0 +1,3 @@
+export default function AnimalsPage() {
+ return AnimalsPage
;
+}
diff --git a/apps/volunteer/src/pages/chattings/index.tsx b/apps/volunteer/src/pages/chattings/index.tsx
new file mode 100644
index 00000000..a48ead0b
--- /dev/null
+++ b/apps/volunteer/src/pages/chattings/index.tsx
@@ -0,0 +1,3 @@
+export default function ChattingsPage() {
+ return ChattingsPage
;
+}
diff --git a/apps/volunteer/src/pages/chattings/room/index.tsx b/apps/volunteer/src/pages/chattings/room/index.tsx
new file mode 100644
index 00000000..55fc00e1
--- /dev/null
+++ b/apps/volunteer/src/pages/chattings/room/index.tsx
@@ -0,0 +1,3 @@
+export default function ChattingsRoomPage() {
+ return ChattingsRoomPage
;
+}
diff --git a/apps/volunteer/src/pages/my/index.tsx b/apps/volunteer/src/pages/my/index.tsx
new file mode 100644
index 00000000..1d167116
--- /dev/null
+++ b/apps/volunteer/src/pages/my/index.tsx
@@ -0,0 +1,3 @@
+export default function MyPage() {
+ return MyPage
;
+}
diff --git a/apps/volunteer/src/pages/notfound/index.tsx b/apps/volunteer/src/pages/notfound/index.tsx
new file mode 100644
index 00000000..5ba55266
--- /dev/null
+++ b/apps/volunteer/src/pages/notfound/index.tsx
@@ -0,0 +1,3 @@
+export default function NotFoundPage() {
+ return NotFoundPage
;
+}
diff --git a/apps/volunteer/src/pages/notifications/index.tsx b/apps/volunteer/src/pages/notifications/index.tsx
new file mode 100644
index 00000000..b7e24e5d
--- /dev/null
+++ b/apps/volunteer/src/pages/notifications/index.tsx
@@ -0,0 +1,3 @@
+export default function NotificationsPage() {
+ return NotificationsPage
;
+}
diff --git a/apps/volunteer/src/pages/settings/account/index.tsx b/apps/volunteer/src/pages/settings/account/index.tsx
new file mode 100644
index 00000000..92db107c
--- /dev/null
+++ b/apps/volunteer/src/pages/settings/account/index.tsx
@@ -0,0 +1,3 @@
+export default function SettingsAccountPage() {
+ return SettingsAccountPage
;
+}
diff --git a/apps/volunteer/src/pages/settings/index.tsx b/apps/volunteer/src/pages/settings/index.tsx
new file mode 100644
index 00000000..7dbf8d14
--- /dev/null
+++ b/apps/volunteer/src/pages/settings/index.tsx
@@ -0,0 +1,3 @@
+export default function SettingsPage() {
+ return SettingsPage
;
+}
diff --git a/apps/volunteer/src/pages/settings/password/index.tsx b/apps/volunteer/src/pages/settings/password/index.tsx
new file mode 100644
index 00000000..1d504fa2
--- /dev/null
+++ b/apps/volunteer/src/pages/settings/password/index.tsx
@@ -0,0 +1,3 @@
+export default function SettingsPasswordPage() {
+ return SettingsPasswordPage
;
+}
diff --git a/apps/volunteer/src/pages/shelters/profile/index.tsx b/apps/volunteer/src/pages/shelters/profile/index.tsx
new file mode 100644
index 00000000..81a181b4
--- /dev/null
+++ b/apps/volunteer/src/pages/shelters/profile/index.tsx
@@ -0,0 +1,3 @@
+export default function SheltersProfilePage() {
+ return SheltersProfilePage
;
+}
diff --git a/apps/volunteer/src/pages/shelters/reviews/update/index.tsx b/apps/volunteer/src/pages/shelters/reviews/update/index.tsx
new file mode 100644
index 00000000..8c8515e9
--- /dev/null
+++ b/apps/volunteer/src/pages/shelters/reviews/update/index.tsx
@@ -0,0 +1,3 @@
+export default function SheltersReviewsUpdatePage() {
+ return SheltersReviewsUpdatePage
;
+}
diff --git a/apps/volunteer/src/pages/shelters/reviews/write/index.tsx b/apps/volunteer/src/pages/shelters/reviews/write/index.tsx
new file mode 100644
index 00000000..f7e01d55
--- /dev/null
+++ b/apps/volunteer/src/pages/shelters/reviews/write/index.tsx
@@ -0,0 +1,3 @@
+export default function SheltersReviewsWritePage() {
+ return SheltersReviewsWritePage
;
+}
diff --git a/apps/volunteer/src/pages/signin/index.tsx b/apps/volunteer/src/pages/signin/index.tsx
new file mode 100644
index 00000000..62ee2682
--- /dev/null
+++ b/apps/volunteer/src/pages/signin/index.tsx
@@ -0,0 +1,193 @@
+import {
+ Box,
+ Button,
+ Center,
+ FormControl,
+ FormErrorMessage,
+ FormLabel,
+ Icon,
+ Image,
+ Input,
+ InputGroup,
+ InputRightElement,
+ useToast,
+ VStack,
+} from '@chakra-ui/react';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { useMutation } from '@tanstack/react-query';
+import type { AxiosResponse } from 'axios';
+import { AxiosError } from 'axios';
+import { useEffect } from 'react';
+import { useForm } from 'react-hook-form';
+import { useNavigate } from 'react-router-dom';
+import AnimalfriendsLogo from 'shared/assets/image-anifriends-logo.png';
+import IoEyeOff from 'shared/assets/IoEyeOff';
+import IoEyeSharp from 'shared/assets/IoEyeSharp';
+import useToggle from 'shared/hooks/useToggle';
+import type {
+ SigninRequestData,
+ SigninResponseData,
+} from 'shared/types/apis/auth';
+import { ErrorResponseData } from 'shared/types/apis/error';
+import * as z from 'zod';
+
+import { signinVolunteer } from '@/apis/auth';
+import PATH from '@/constants/path';
+
+type Schema = z.infer;
+
+const schema = z.object({
+ email: z
+ .string()
+ .min(1, '이메일이 입려되지 않았습니다')
+ .email('유효하지 않은 이메일입니다'),
+ password: z.string().min(1, '비밀번호가 입력되지 않았습니다'),
+});
+
+export default function SigninPage() {
+ const navigate = useNavigate();
+ const toast = useToast();
+ const [isShow, toggleInputShow] = useToggle();
+ const {
+ register,
+ handleSubmit,
+ formState: { errors },
+ setFocus,
+ } = useForm({
+ resolver: zodResolver(schema),
+ });
+ const { mutate } = useMutation<
+ AxiosResponse,
+ AxiosError,
+ SigninRequestData
+ >({
+ mutationFn: (data) => signinVolunteer(data),
+ onSuccess: () => {
+ navigate(`/${PATH.VOLUNTEERS.INDEX}`);
+ },
+ onError: (error) => {
+ toast({
+ position: 'top',
+ description: error.response?.data.message,
+ status: 'error',
+ duration: 1500,
+ });
+
+ setFocus('email');
+ },
+ });
+
+ const goSignupPage = () => {
+ navigate(`/${PATH.SIGNUP}`);
+ };
+
+ const goVolunteersPage = () => {
+ navigate(`/${PATH.VOLUNTEERS.INDEX}`);
+ };
+
+ const onSubmit = async (data: Schema) => {
+ mutate(data);
+ };
+
+ useEffect(() => setFocus('email'), [setFocus]);
+
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/apps/volunteer/src/pages/signup/index.tsx b/apps/volunteer/src/pages/signup/index.tsx
new file mode 100644
index 00000000..63058bbd
--- /dev/null
+++ b/apps/volunteer/src/pages/signup/index.tsx
@@ -0,0 +1,263 @@
+import {
+ Box,
+ Button,
+ Center,
+ FormControl,
+ FormErrorMessage,
+ FormHelperText,
+ FormLabel,
+ Icon,
+ Image,
+ Input,
+ InputGroup,
+ InputRightAddon,
+ InputRightElement,
+ VStack,
+} from '@chakra-ui/react';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { Controller, useForm } from 'react-hook-form';
+import AnimalfriendsLogo from 'shared/assets/image-anifriends-logo.png';
+import IoEyeOff from 'shared/assets/IoEyeOff';
+import IoEyeSharp from 'shared/assets/IoEyeSharp';
+import RadioGroup from 'shared/components/RadioGroup';
+import useToggle from 'shared/hooks/useToggle';
+import * as z from 'zod';
+
+import { PersonGenderEng, PersonGenderKor } from '@/types/gender';
+
+type Schema = z.infer;
+
+const schema = z
+ .object({
+ email: z
+ .string()
+ .min(1, '이메일은 필수 정보입니다')
+ .email('유효하지 않은 이메일입니다'),
+ password: z
+ .string()
+ .regex(
+ /^(?=.*[!@#$%^&*()\-_=+[\]\\|{};:'",<.>/?]+)(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}$/,
+ '비밀번호는 필수 정보입니다(8자 이상)',
+ ),
+ passwordConfirm: z.string().min(1, '비밀번호 확인 정보는 필수입니다'),
+ name: z.string().min(1, '이름 정보는 필수입니다'),
+ date: z
+ .string()
+ .min(1, '생년월일 정보는 필수입니다')
+ .refine(
+ (val) => new Date(val) < new Date(),
+ `${new Date()
+ .toLocaleDateString('ko-KR')
+ .split('')
+ .filter((v) => v !== ' ')
+ .join('')} 이전으로 선택해주세요`,
+ ),
+ phoneNumber: z
+ .string()
+ .min(1, '전화번호 정보는 필수입니다')
+ .refine(
+ (val) => !Number.isNaN(Number(val)),
+ '전화번호 형식은 숫자입니다',
+ ),
+ gender: z.enum(['FEMALE', 'MALE']),
+ })
+ .refine(({ password, passwordConfirm }) => password === passwordConfirm, {
+ message: '비밀번호가 일치하지 않습니다',
+ path: ['passwordConfirm'],
+ });
+
+export default function SignupPage() {
+ const [isPasswordShow, togglePasswordShow] = useToggle();
+ const [isPasswordConfirmShow, togglePasswordConfirmShow] = useToggle();
+ const {
+ register,
+ handleSubmit,
+ formState: { errors },
+ control,
+ } = useForm({
+ resolver: zodResolver(schema),
+ });
+
+ const onSubmit = (data: Schema) => {
+ console.log(data);
+ };
+
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/apps/volunteer/src/pages/volunteers/VolunteerRecruitItem.tsx b/apps/volunteer/src/pages/volunteers/VolunteerRecruitItem.tsx
new file mode 100644
index 00000000..caf34eed
--- /dev/null
+++ b/apps/volunteer/src/pages/volunteers/VolunteerRecruitItem.tsx
@@ -0,0 +1,40 @@
+import {
+ AspectRatio,
+ Box,
+ HStack,
+ Image,
+ Text,
+ VStack,
+} from '@chakra-ui/react';
+import ApplicantStatus from 'shared/components/ApplicantStatus';
+import InfoSubText from 'shared/components/InfoSubtext';
+import LabelText from 'shared/components/LabelText';
+
+const DUMMY_IMAGE = 'https://source.unsplash.com/random';
+
+export default function VolunteerRecruitItem() {
+ return (
+
+
+
+
+
+
+
+
+
+ 봉사자 모집합니다!!
+
+
+ 양천구 보건소
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/volunteer/src/pages/volunteers/detail/index.tsx b/apps/volunteer/src/pages/volunteers/detail/index.tsx
new file mode 100644
index 00000000..eba1c072
--- /dev/null
+++ b/apps/volunteer/src/pages/volunteers/detail/index.tsx
@@ -0,0 +1,3 @@
+export default function VolunteersDetailPage() {
+ return VolunteersDetailPage
;
+}
diff --git a/apps/volunteer/src/pages/volunteers/index.tsx b/apps/volunteer/src/pages/volunteers/index.tsx
new file mode 100644
index 00000000..c7293a18
--- /dev/null
+++ b/apps/volunteer/src/pages/volunteers/index.tsx
@@ -0,0 +1,3 @@
+export default function VolunteersPage() {
+ return VolunteersPage
;
+}
diff --git a/apps/volunteer/src/pages/volunteers/search/index.tsx b/apps/volunteer/src/pages/volunteers/search/index.tsx
new file mode 100644
index 00000000..b35ca75a
--- /dev/null
+++ b/apps/volunteer/src/pages/volunteers/search/index.tsx
@@ -0,0 +1,21 @@
+import { useEffect } from 'react';
+import useSearchHeaderStore from 'shared/store/searchHeaderStore';
+
+const handleSearchkeyword = (keyword: string) => {
+ // TODO: VolunteerList 검색 API 호출
+ console.log('[Search Volunteer] keyword:', keyword);
+};
+
+export default function VolunteersSearchPage() {
+ const setOnSearch = useSearchHeaderStore((state) => state.setOnSearch);
+
+ useEffect(() => {
+ setOnSearch(handleSearchkeyword);
+
+ return () => {
+ setOnSearch(() => {});
+ };
+ }, [setOnSearch]);
+
+ return VolunteersSearchPage
;
+}
diff --git a/apps/volunteer/src/routes/index.tsx b/apps/volunteer/src/routes/index.tsx
new file mode 100644
index 00000000..ed39944a
--- /dev/null
+++ b/apps/volunteer/src/routes/index.tsx
@@ -0,0 +1,141 @@
+import { createBrowserRouter, RouterProviderProps } from 'react-router-dom';
+import APP_TYPE from 'shared/constants/appType';
+import PAGE_TYPE from 'shared/constants/pageType';
+import Layout from 'shared/layout';
+
+import PATH from '@/constants/path';
+import AnimalsPage from '@/pages/animals';
+import AnimalsDetailPage from '@/pages/animals/detail';
+import ChattingsPage from '@/pages/chattings';
+import ChattingsRoomPage from '@/pages/chattings/room';
+import MyPage from '@/pages/my';
+import NotFoundPage from '@/pages/notfound';
+import NotificationsPage from '@/pages/notifications';
+import SettingsPage from '@/pages/settings';
+import SettingsAccountPage from '@/pages/settings/account';
+import SettingsPasswordPage from '@/pages/settings/password';
+import SheltersProfilePage from '@/pages/shelters/profile';
+import SheltersReviewsUpdatePage from '@/pages/shelters/reviews/update';
+import SheltersReviewsWritePage from '@/pages/shelters/reviews/write';
+import SigninPage from '@/pages/signin';
+import SignupPage from '@/pages/signup';
+import VolunteersPage from '@/pages/volunteers';
+import VolunteersDetailPage from '@/pages/volunteers/detail';
+import VolunteersSearchPage from '@/pages/volunteers/search';
+
+export const router: RouterProviderProps['router'] = createBrowserRouter([
+ {
+ path: '/',
+ element: ,
+ errorElement: ,
+ children: [
+ {
+ path: PATH.VOLUNTEERS.INDEX,
+ children: [
+ {
+ id: PAGE_TYPE.VOLUNTEERS,
+ index: true,
+ element: ,
+ },
+ {
+ id: PAGE_TYPE.VOLUNTEERS_DETAIL,
+ path: PATH.VOLUNTEERS.DETAIL,
+ element: ,
+ },
+ {
+ id: PAGE_TYPE.VOLUNTEERS_SEARCH,
+ path: PATH.VOLUNTEERS.SEARCH,
+ element: ,
+ },
+ ],
+ },
+ {
+ path: PATH.ANIMALS.INDEX,
+ children: [
+ {
+ id: PAGE_TYPE.ANIMALS,
+ index: true,
+ element: ,
+ },
+ {
+ id: PAGE_TYPE.ANIMALS_DETAIL,
+ path: PATH.ANIMALS.DETAIL,
+ element: ,
+ },
+ ],
+ },
+ {
+ path: PATH.CHATTINGS.INDEX,
+ children: [
+ {
+ id: PAGE_TYPE.CHATTINGS,
+ index: true,
+ element: ,
+ },
+ {
+ id: PAGE_TYPE.CHATTINGS_ROOM,
+ path: PATH.CHATTINGS.ROOM,
+ element: ,
+ },
+ ],
+ },
+ {
+ id: PAGE_TYPE.MYPAGE,
+ path: PATH.MYPAGE.INDEX,
+ element: ,
+ },
+ {
+ id: PAGE_TYPE.SETTINGS,
+ path: PATH.SETTINGS.INDEX,
+ element: ,
+ children: [
+ {
+ id: PAGE_TYPE.SETTINGS_ACCOUNT,
+ path: PATH.SETTINGS.ACCOUNT,
+ element: ,
+ },
+ {
+ id: PAGE_TYPE.SETTINGS_PASSWORD,
+ path: PATH.SETTINGS.PASSWORD,
+ element: ,
+ },
+ ],
+ },
+ {
+ path: PATH.SHELTERS.INDEX,
+ children: [
+ {
+ id: PAGE_TYPE.SHELTERS_PROFILE,
+ path: PATH.SHELTERS.PROFILE,
+ element: ,
+ },
+ {
+ id: PAGE_TYPE.SHELTERS_REVIEWS_WRITE,
+ path: PATH.SHELTERS.REVIEWS_WRITE,
+ element: ,
+ },
+ {
+ id: PAGE_TYPE.SHELTERS_REVIEWS_UPDATE,
+ path: PATH.SHELTERS.REVIEWS_UPDATE,
+ element: ,
+ },
+ ],
+ },
+ {
+ id: PAGE_TYPE.NOTIFICATIONS,
+ path: PATH.NOTIFICATIONS,
+ element: ,
+ },
+ {
+ id: PAGE_TYPE.SIGNUP,
+ path: PATH.SIGNUP,
+ element: ,
+ },
+ {
+ id: PAGE_TYPE.SIGNIN,
+ path: PATH.SIGNIN,
+ element: ,
+ },
+ ],
+ },
+]);
diff --git a/apps/volunteer/src/types/apis/auth.ts b/apps/volunteer/src/types/apis/auth.ts
new file mode 100644
index 00000000..a67d7e76
--- /dev/null
+++ b/apps/volunteer/src/types/apis/auth.ts
@@ -0,0 +1,10 @@
+import { SigninRequestData } from 'shared/types/apis/auth';
+
+import { PersonGenderEng } from '../gender';
+
+export type SignupRequestData = SigninRequestData & {
+ name: string;
+ birthDate: string;
+ phoneNumber: string;
+ gender: PersonGenderEng;
+};
diff --git a/apps/volunteer/src/types/gender.ts b/apps/volunteer/src/types/gender.ts
new file mode 100644
index 00000000..87f035c2
--- /dev/null
+++ b/apps/volunteer/src/types/gender.ts
@@ -0,0 +1,7 @@
+import { PERAON_GENDER_ENG, PERAON_GENDER_KOR } from '@/constants/gender';
+
+export type PersonGenderEng =
+ (typeof PERAON_GENDER_ENG)[keyof typeof PERAON_GENDER_ENG];
+
+export type PersonGenderKor =
+ (typeof PERAON_GENDER_KOR)[keyof typeof PERAON_GENDER_KOR];
diff --git a/apps/volunteer/vercel.json b/apps/volunteer/vercel.json
new file mode 100644
index 00000000..3a48e56b
--- /dev/null
+++ b/apps/volunteer/vercel.json
@@ -0,0 +1,3 @@
+{
+ "rewrites": [{ "source": "/(.*)", "destination": "/" }]
+}
diff --git a/commitlint.config.js b/commitlint.config.js
index b888378b..0a107507 100644
--- a/commitlint.config.js
+++ b/commitlint.config.js
@@ -1,6 +1,7 @@
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
+ 'subject-case': [0],
'type-enum': [
2,
'always',
diff --git a/cz-config.js b/cz-config.js
index 72fa801a..64b3c047 100644
--- a/cz-config.js
+++ b/cz-config.js
@@ -35,7 +35,7 @@ module.exports = {
],
scopes: [
{ name: 'common' },
- { name: 'ui' },
+ { name: 'shared' },
{ name: 'volunteer' },
{ name: 'shelter' },
],
diff --git a/package.json b/package.json
index 74a857c6..c89f9e53 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"private": true,
- "packageManager": "pnpm@8.6.10",
+ "packageManager": "pnpm@8.10.0",
"scripts": {
"build": "turbo run build",
"build:shelter": "turbo run build --filter=shelter",
@@ -27,12 +27,12 @@
"@commitlint/config-conventional": "^18.1.0",
"commitizen": "^4.3.0",
"cz-customizable": "^7.0.0",
- "eslint": "^7.32.0",
+ "eslint": "^8.45.0",
"eslint-config-custom": "workspace:*",
"husky": "^8.0.0",
"lint-staged": "^15.0.2",
"packlint": "^0.2.4",
- "prettier": "^2.5.1",
+ "prettier": "3.0.3",
"turbo": "latest"
}
}
diff --git a/packages/ui/.eslintrc.cjs b/packages/shared/.eslintrc.cjs
similarity index 100%
rename from packages/ui/.eslintrc.cjs
rename to packages/shared/.eslintrc.cjs
diff --git a/packages/ui/.gitignore b/packages/shared/.gitignore
similarity index 100%
rename from packages/ui/.gitignore
rename to packages/shared/.gitignore
diff --git a/packages/shared/apis/axiosInstance.ts b/packages/shared/apis/axiosInstance.ts
new file mode 100644
index 00000000..bc770885
--- /dev/null
+++ b/packages/shared/apis/axiosInstance.ts
@@ -0,0 +1,71 @@
+import axios, {
+ AxiosInstance,
+ AxiosRequestConfig,
+ AxiosResponse,
+ InternalAxiosRequestConfig,
+} from 'axios';
+
+import { BASE_URL } from '../constants/baseURL';
+import {
+ onErrorRequest,
+ onErrorResponse,
+ onRequest,
+ onResponse,
+} from './axiosInterceptor';
+
+type interceptors = {
+ onRequest: (config: InternalAxiosRequestConfig) => InternalAxiosRequestConfig;
+ onErrorRequest: (error: Error) => void | Promise;
+ onResponse: (response: AxiosResponse) => AxiosResponse;
+ onErrorResponse: (error: Error) => void | Promise;
+};
+
+class AxiosService {
+ private static instance: AxiosService;
+
+ private constructor(private axiosInstance: AxiosInstance) {}
+
+ public static getInstance({
+ onRequest,
+ onErrorRequest,
+ onResponse,
+ onErrorResponse,
+ }: interceptors): AxiosService {
+ if (!AxiosService.instance) {
+ const axiosInstance = axios.create({ baseURL: BASE_URL });
+ axiosInstance.interceptors.request.use(onRequest, onErrorRequest);
+ axiosInstance.interceptors.response.use(onResponse, onErrorResponse);
+ this.instance = new AxiosService(axiosInstance);
+ }
+ return this.instance;
+ }
+
+ get(
+ url: string,
+ config?: AxiosRequestConfig,
+ ) {
+ return this.axiosInstance.get(url, config);
+ }
+
+ post(url: string, data?: Request) {
+ return this.axiosInstance.post(url, data);
+ }
+
+ delete(
+ url: string,
+ config?: AxiosRequestConfig,
+ ) {
+ return this.axiosInstance.delete(url, config);
+ }
+
+ patch(url: string, data?: Request) {
+ return this.axiosInstance.patch(url, data);
+ }
+}
+
+export default AxiosService.getInstance({
+ onRequest,
+ onErrorRequest,
+ onResponse,
+ onErrorResponse,
+});
diff --git a/packages/shared/apis/axiosInterceptor.ts b/packages/shared/apis/axiosInterceptor.ts
new file mode 100644
index 00000000..ce4e3ef7
--- /dev/null
+++ b/packages/shared/apis/axiosInterceptor.ts
@@ -0,0 +1,18 @@
+import { AxiosResponse, InternalAxiosRequestConfig } from 'axios';
+
+export const onRequest = (config: InternalAxiosRequestConfig) => config;
+
+export const onErrorRequest = (error: Error) => {
+ return Promise.reject(error);
+};
+export const onResponse = (response: AxiosResponse) => response;
+export const onErrorResponse = (error: Error) => {
+ return Promise.reject(error);
+};
+
+export default {
+ onRequest,
+ onErrorRequest,
+ onResponse,
+ onErrorResponse,
+};
diff --git a/packages/shared/apis/common/Image.ts b/packages/shared/apis/common/Image.ts
new file mode 100644
index 00000000..fdf8ff4f
--- /dev/null
+++ b/packages/shared/apis/common/Image.ts
@@ -0,0 +1,11 @@
+import axiosInstance from 'apis/axiosInstance';
+
+export const uploadImage = (images: File[]) =>
+ axiosInstance.post<
+ {
+ imageUrls: string[];
+ },
+ {
+ images: File[];
+ }
+ >('/images', { images });
diff --git a/packages/shared/apis/common/Recruitments.ts b/packages/shared/apis/common/Recruitments.ts
new file mode 100644
index 00000000..7f8205da
--- /dev/null
+++ b/packages/shared/apis/common/Recruitments.ts
@@ -0,0 +1,77 @@
+import axiosInstance from '../axiosInstance';
+
+type RecruitmentDetailResponse = {
+ recruitmentTitle: string;
+ recruitmentApplicantCount: number;
+ recruitmentCapacity: number;
+ recruitmentContent: string;
+ recruitmentStartTime: string;
+ recruitmentEndTime: string;
+ recruitmentIsClosed: boolean;
+ recruitmentDeadline: string;
+ recruitmentCreatedAt: string;
+ recruitmentUpdatedAt: string;
+ recruitmentImageUrls: string[];
+};
+
+export const getRecruitmentDetail = (recruitmentId: number) =>
+ axiosInstance.get(
+ `/recruitments/${recruitmentId}`,
+ );
+
+// 봉사자가 완료한 봉사 모집글 리스트 조회
+export const getCompletedVolunteers = (
+ volunteerId: number,
+ pageNumber: number,
+ pageSize: number,
+) =>
+ axiosInstance.get<
+ {
+ pageInfo: {
+ totalElements: number;
+ hasNext: boolean;
+ };
+ recruitments: {
+ recruitmentId: number;
+ recruitmentTitle: string;
+ recruitmentStartTime: string;
+ shelterName: string;
+ }[];
+ },
+ { pageNumber: number; pageSize: number }
+ >(`/volunteers/${volunteerId}/recruitments/completed`, {
+ params: {
+ pageNumber,
+ pageSize,
+ },
+ });
+
+//보호소가 생성한 봉사 모집글 리스트 조회
+export const getShelterRecruitment = (
+ shelterId: number,
+ pageNumber: number,
+ pageSize: number,
+) =>
+ axiosInstance.get<
+ {
+ pageInfo: {
+ totalElements: number;
+ hasNext: boolean;
+ };
+ recruitments: {
+ recruitmentId: number;
+ recruitmentTitle: string;
+ recruitmentStartTime: string;
+ shelterName: string;
+ recruitmentDeadline: string;
+ recruitmentCapacity: number;
+ recruitmentApplicantCount: number;
+ }[];
+ },
+ { pageNumber: number; pageSize: number }
+ >(`/shelters/${shelterId}/recruitments`, {
+ params: {
+ pageNumber,
+ pageSize,
+ },
+ });
diff --git a/packages/shared/apis/common/Review.ts b/packages/shared/apis/common/Review.ts
new file mode 100644
index 00000000..211b85a6
--- /dev/null
+++ b/packages/shared/apis/common/Review.ts
@@ -0,0 +1,31 @@
+import axiosInstance from 'apis/axiosInstance';
+
+export const getVolunteerReviews = (
+ volunteerId: number,
+ pageNumber: number,
+ pageSize: number,
+) =>
+ axiosInstance.get<
+ {
+ pageInfo: {
+ totalElements: number;
+ hasNext: boolean;
+ };
+ reviews: {
+ reviewId: number;
+ shelterName: string;
+ reviewCreatedAt: string;
+ reviewContent: string;
+ reviewImageUrls: string[];
+ }[];
+ },
+ {
+ pageNumber: number;
+ pageSize: number;
+ }
+ >(`/volunteers/${volunteerId}/reviews`, {
+ params: {
+ pageNumber,
+ pageSize,
+ },
+ });
diff --git a/packages/shared/apis/common/Volunteer.ts b/packages/shared/apis/common/Volunteer.ts
new file mode 100644
index 00000000..7fbc4510
--- /dev/null
+++ b/packages/shared/apis/common/Volunteer.ts
@@ -0,0 +1,10 @@
+import axiosInstance from 'apis/axiosInstance';
+
+export const getVolunteerProfileInfo = (recruitmentId: number) =>
+ axiosInstance.get<{
+ volunteerEmail: string;
+ volunteerName: string;
+ volunteerTemperate: number;
+ volunteerImageUrl: string;
+ volunteerPhoneNumber: string;
+ }>(`/recruitments/${recruitmentId}`);
diff --git a/packages/shared/assets/IoEyeOff.tsx b/packages/shared/assets/IoEyeOff.tsx
new file mode 100644
index 00000000..7df1f095
--- /dev/null
+++ b/packages/shared/assets/IoEyeOff.tsx
@@ -0,0 +1,23 @@
+import { ComponentProps } from 'react';
+
+export default function IoEyeOff({ ...props }: ComponentProps<'svg'>) {
+ return (
+
+ );
+}
diff --git a/packages/shared/assets/IoEyeSharp.tsx b/packages/shared/assets/IoEyeSharp.tsx
new file mode 100644
index 00000000..132a5194
--- /dev/null
+++ b/packages/shared/assets/IoEyeSharp.tsx
@@ -0,0 +1,23 @@
+import { ComponentProps } from 'react';
+
+export default function IoEyeSharp({ ...props }: ComponentProps<'svg'>) {
+ return (
+
+ );
+}
diff --git a/packages/shared/assets/bottomNavBar/icon_animals_selected.svg b/packages/shared/assets/bottomNavBar/icon_animals_selected.svg
new file mode 100644
index 00000000..ea981bc8
--- /dev/null
+++ b/packages/shared/assets/bottomNavBar/icon_animals_selected.svg
@@ -0,0 +1,5 @@
+
diff --git a/packages/shared/assets/bottomNavBar/icon_animals_unselected.svg b/packages/shared/assets/bottomNavBar/icon_animals_unselected.svg
new file mode 100644
index 00000000..8d76a0e8
--- /dev/null
+++ b/packages/shared/assets/bottomNavBar/icon_animals_unselected.svg
@@ -0,0 +1,3 @@
+
diff --git a/packages/shared/assets/bottomNavBar/icon_chattings_selected.svg b/packages/shared/assets/bottomNavBar/icon_chattings_selected.svg
new file mode 100644
index 00000000..365ece97
--- /dev/null
+++ b/packages/shared/assets/bottomNavBar/icon_chattings_selected.svg
@@ -0,0 +1,4 @@
+
diff --git a/packages/shared/assets/bottomNavBar/icon_chattings_unselected.svg b/packages/shared/assets/bottomNavBar/icon_chattings_unselected.svg
new file mode 100644
index 00000000..3d4338b4
--- /dev/null
+++ b/packages/shared/assets/bottomNavBar/icon_chattings_unselected.svg
@@ -0,0 +1,3 @@
+
diff --git a/packages/shared/assets/bottomNavBar/icon_mypage_selected.svg b/packages/shared/assets/bottomNavBar/icon_mypage_selected.svg
new file mode 100644
index 00000000..bfca5350
--- /dev/null
+++ b/packages/shared/assets/bottomNavBar/icon_mypage_selected.svg
@@ -0,0 +1,4 @@
+
diff --git a/packages/shared/assets/bottomNavBar/icon_mypage_unselected.svg b/packages/shared/assets/bottomNavBar/icon_mypage_unselected.svg
new file mode 100644
index 00000000..3c98ae3a
--- /dev/null
+++ b/packages/shared/assets/bottomNavBar/icon_mypage_unselected.svg
@@ -0,0 +1,3 @@
+
diff --git a/packages/shared/assets/bottomNavBar/icon_volunteers_selected.svg b/packages/shared/assets/bottomNavBar/icon_volunteers_selected.svg
new file mode 100644
index 00000000..e889f519
--- /dev/null
+++ b/packages/shared/assets/bottomNavBar/icon_volunteers_selected.svg
@@ -0,0 +1,3 @@
+
diff --git a/packages/shared/assets/bottomNavBar/icon_volunteers_unselected.svg b/packages/shared/assets/bottomNavBar/icon_volunteers_unselected.svg
new file mode 100644
index 00000000..a1681789
--- /dev/null
+++ b/packages/shared/assets/bottomNavBar/icon_volunteers_unselected.svg
@@ -0,0 +1,3 @@
+
diff --git a/packages/shared/assets/icon-IoEyeOff.svg b/packages/shared/assets/icon-IoEyeOff.svg
new file mode 100644
index 00000000..1df7a242
--- /dev/null
+++ b/packages/shared/assets/icon-IoEyeOff.svg
@@ -0,0 +1,4 @@
+
diff --git a/packages/shared/assets/icon-IoEyeSharp.svg b/packages/shared/assets/icon-IoEyeSharp.svg
new file mode 100644
index 00000000..62c791b7
--- /dev/null
+++ b/packages/shared/assets/icon-IoEyeSharp.svg
@@ -0,0 +1,4 @@
+
diff --git a/packages/shared/assets/icon_BiX.svg b/packages/shared/assets/icon_BiX.svg
new file mode 100644
index 00000000..a6595bb4
--- /dev/null
+++ b/packages/shared/assets/icon_BiX.svg
@@ -0,0 +1,3 @@
+
diff --git a/packages/shared/assets/icon_IoCamera.svg b/packages/shared/assets/icon_IoCamera.svg
new file mode 100644
index 00000000..8f7078e4
--- /dev/null
+++ b/packages/shared/assets/icon_IoCamera.svg
@@ -0,0 +1,4 @@
+
diff --git a/packages/shared/assets/icon_applicant.svg b/packages/shared/assets/icon_applicant.svg
new file mode 100644
index 00000000..52a832de
--- /dev/null
+++ b/packages/shared/assets/icon_applicant.svg
@@ -0,0 +1,10 @@
+
diff --git a/packages/shared/assets/icon_back.svg b/packages/shared/assets/icon_back.svg
new file mode 100644
index 00000000..a03e8cc6
--- /dev/null
+++ b/packages/shared/assets/icon_back.svg
@@ -0,0 +1,3 @@
+
diff --git a/packages/shared/assets/icon_menu.svg b/packages/shared/assets/icon_menu.svg
new file mode 100644
index 00000000..429a0ff8
--- /dev/null
+++ b/packages/shared/assets/icon_menu.svg
@@ -0,0 +1,3 @@
+
diff --git a/packages/shared/assets/icon_next.svg b/packages/shared/assets/icon_next.svg
new file mode 100644
index 00000000..083d251d
--- /dev/null
+++ b/packages/shared/assets/icon_next.svg
@@ -0,0 +1,3 @@
+
diff --git a/packages/shared/assets/icon_notifications.svg b/packages/shared/assets/icon_notifications.svg
new file mode 100644
index 00000000..20120955
--- /dev/null
+++ b/packages/shared/assets/icon_notifications.svg
@@ -0,0 +1,3 @@
+
diff --git a/packages/shared/assets/icon_review_next.svg b/packages/shared/assets/icon_review_next.svg
new file mode 100644
index 00000000..c0d965d9
--- /dev/null
+++ b/packages/shared/assets/icon_review_next.svg
@@ -0,0 +1,3 @@
+
diff --git a/packages/shared/assets/icon_search.svg b/packages/shared/assets/icon_search.svg
new file mode 100644
index 00000000..f42582ae
--- /dev/null
+++ b/packages/shared/assets/icon_search.svg
@@ -0,0 +1,3 @@
+
diff --git a/packages/shared/assets/icon_settings.svg b/packages/shared/assets/icon_settings.svg
new file mode 100644
index 00000000..46033f3b
--- /dev/null
+++ b/packages/shared/assets/icon_settings.svg
@@ -0,0 +1,3 @@
+
diff --git a/packages/shared/assets/image-anifriends-logo.png b/packages/shared/assets/image-anifriends-logo.png
new file mode 100644
index 00000000..a9c3932b
Binary files /dev/null and b/packages/shared/assets/image-anifriends-logo.png differ
diff --git a/packages/shared/components/ApplicantStatus.tsx b/packages/shared/components/ApplicantStatus.tsx
new file mode 100644
index 00000000..12a82961
--- /dev/null
+++ b/packages/shared/components/ApplicantStatus.tsx
@@ -0,0 +1,24 @@
+import { Flex, Image, Text } from '@chakra-ui/react';
+
+import ApplicantIcon from '../assets/icon_applicant.svg';
+
+type ApplicantStatusProps = {
+ size?: number;
+ numerator: number;
+ denominator: number;
+};
+
+export default function ApplicantStatus({
+ size = 5,
+ numerator,
+ denominator,
+}: ApplicantStatusProps) {
+ return (
+
+
+
+ {`${numerator} / ${denominator}`}
+
+
+ );
+}
diff --git a/packages/shared/components/EditPhotoItem.tsx b/packages/shared/components/EditPhotoItem.tsx
new file mode 100644
index 00000000..f49c0829
--- /dev/null
+++ b/packages/shared/components/EditPhotoItem.tsx
@@ -0,0 +1,45 @@
+import type { ImageProps } from '@chakra-ui/react';
+import { Box, Image } from '@chakra-ui/react';
+import { MouseEvent } from 'react';
+
+import BiX from '../assets/icon_BiX.svg';
+
+type UploadedPhotoItemProps = {
+ photoId: ImageProps['id'];
+ photoSrc: ImageProps['src'];
+ onDeletePhoto: (event: MouseEvent) => void;
+};
+
+export default function EditPhotoItem({
+ photoId,
+ photoSrc,
+ onDeletePhoto,
+}: UploadedPhotoItemProps) {
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/packages/shared/components/EditPhotoList.tsx b/packages/shared/components/EditPhotoList.tsx
new file mode 100644
index 00000000..ea6cfa87
--- /dev/null
+++ b/packages/shared/components/EditPhotoList.tsx
@@ -0,0 +1,140 @@
+import type { UseToastOptions } from '@chakra-ui/react';
+import {
+ Box,
+ Flex,
+ HStack,
+ Image,
+ Input,
+ Text,
+ useToast,
+} from '@chakra-ui/react';
+import type { ChangeEvent, Dispatch, MouseEvent, SetStateAction } from 'react';
+
+import IoIosCamera from '../assets/icon_IoCamera.svg';
+import EditPhotoItem from './EditPhotoItem';
+
+type EditPhotoListProps = {
+ urls: string[];
+ setUrls: Dispatch>;
+};
+
+type UploadPhotoItemProps = {
+ urlsCount: number;
+ onUploadPhoto: (event: ChangeEvent) => void;
+};
+
+export default function EditPhotoList({ urls, setUrls }: EditPhotoListProps) {
+ const toast = useToast();
+
+ const afterUploadToast = (
+ description: string,
+ status: UseToastOptions['status'],
+ ) => {
+ toast({
+ description,
+ position: 'top',
+ status,
+ duration: 1500,
+ isClosable: true,
+ });
+ };
+
+ const deletePhoto = (event: MouseEvent) => {
+ const { id } = event.currentTarget;
+ const newUrls = urls.filter((url) => url !== id);
+
+ setUrls(newUrls);
+ };
+
+ const uploadPhoto = async (event: ChangeEvent) => {
+ if (urls.length === 5) {
+ afterUploadToast('사진을 더이상 추가할 수 없습니다', 'error');
+
+ return;
+ }
+
+ const formData = new FormData();
+ const { files } = event.currentTarget;
+ const uploadPhotoCount = (files?.length ?? 0) + urls.length;
+
+ if (uploadPhotoCount > 5) {
+ afterUploadToast(`${5 - urls.length}개 더 등록이 가능합니다`, 'error');
+
+ return;
+ }
+
+ if (files) {
+ Array.from(files).forEach((file) => {
+ formData.append('images', file);
+ });
+ }
+
+ // 아래는 api 함수로 받아왔다는 이미지들을 url 형식들로 받아왔다는 가정하에 진행하는 로직입니다
+
+ if (files) {
+ const imageUrls = Array.from(files).map((file) =>
+ URL.createObjectURL(file),
+ );
+
+ setUrls((prevUrls) => [...imageUrls, ...prevUrls]);
+ }
+ };
+
+ return (
+
+
+ {urls.map((url, index) => (
+
+ ))}
+
+ );
+}
+
+function UploadPhotoItem({ urlsCount, onUploadPhoto }: UploadPhotoItemProps) {
+ return (
+
+
+
+
+
+
+
+ {urlsCount}
+
+ /
+ 5
+
+
+
+
+ );
+}
diff --git a/packages/shared/components/ImageCarousel.tsx b/packages/shared/components/ImageCarousel.tsx
new file mode 100644
index 00000000..dc49ff8d
--- /dev/null
+++ b/packages/shared/components/ImageCarousel.tsx
@@ -0,0 +1,135 @@
+import { Box, chakra, Flex, Image, shouldForwardProp } from '@chakra-ui/react';
+import type { PanInfo } from 'framer-motion';
+import {
+ AnimatePresence,
+ isValidMotionProp,
+ motion,
+ wrap,
+} from 'framer-motion';
+import { useState } from 'react';
+
+import BackIcon from '../assets/icon_back.svg';
+
+type ImageCarouselProps = {
+ imageUrls: string[];
+};
+
+const variants = {
+ enter: (direction: number) => {
+ return {
+ x: direction > 0 ? 500 : -500,
+ opacity: 0,
+ };
+ },
+ center: {
+ x: 0,
+ opacity: 1,
+ },
+ exit: (direction: number) => {
+ return {
+ x: direction < 0 ? 500 : -500,
+ opacity: 0,
+ };
+ },
+};
+
+const swipeConfidenceThreshold = 10000;
+
+const swipePower = (offset: number, velocity: number) =>
+ Math.abs(offset) * velocity;
+
+export default function ImageCarousel({ imageUrls }: ImageCarouselProps) {
+ const [[page, direction], setPage] = useState([0, 0]);
+
+ const imageUrlIndex = wrap(0, imageUrls.length, page);
+
+ const paginate = (newDirection: number) =>
+ setPage([page + newDirection, newDirection]);
+
+ const dragImage = (
+ // eslint-disable-next-line
+ // @ts-ignore
+ event: DragEvent | TouchEvent | PointerEvent,
+ { offset, velocity }: PanInfo,
+ ) => {
+ const swipe = swipePower(offset.x, velocity.x);
+
+ if (swipe < -swipeConfidenceThreshold) {
+ paginate(1);
+ } else if (swipe > swipeConfidenceThreshold) {
+ paginate(-1);
+ }
+ };
+
+ const goBackPage = () => paginate(-1);
+
+ const goNextPage = () => paginate(1);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+const MotionImage = chakra(motion.img, {
+ shouldForwardProp: (prop) =>
+ isValidMotionProp(prop) || shouldForwardProp(prop),
+});
diff --git a/packages/shared/components/InfoItem.tsx b/packages/shared/components/InfoItem.tsx
new file mode 100644
index 00000000..68864206
--- /dev/null
+++ b/packages/shared/components/InfoItem.tsx
@@ -0,0 +1,33 @@
+import type { TextProps } from '@chakra-ui/react';
+import { Flex, Text } from '@chakra-ui/react';
+import { ReactElement } from 'react';
+
+export type InfoItemStylesProps = {
+ titleTextStyles?: TextProps;
+};
+
+export type InfoItemProps = {
+ title: string;
+ children: ReactElement;
+};
+
+export default function InfoItem({
+ title,
+ titleTextStyles,
+ children,
+}: InfoItemProps & InfoItemStylesProps) {
+ return (
+
+
+ {title}
+
+ {children}
+
+ );
+}
diff --git a/packages/shared/components/InfoList.tsx b/packages/shared/components/InfoList.tsx
new file mode 100644
index 00000000..07213df8
--- /dev/null
+++ b/packages/shared/components/InfoList.tsx
@@ -0,0 +1,17 @@
+import { Flex, FlexProps } from '@chakra-ui/react';
+
+export default function InfoList({ children, ...props }: FlexProps) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/packages/shared/components/InfoSubtext.tsx b/packages/shared/components/InfoSubtext.tsx
new file mode 100644
index 00000000..96f68888
--- /dev/null
+++ b/packages/shared/components/InfoSubtext.tsx
@@ -0,0 +1,19 @@
+import { HStack, Text } from '@chakra-ui/react';
+
+type InfoSubtext = {
+ title: string;
+ content: string;
+};
+
+export default function InfoSubtext({ title, content }: InfoSubtext) {
+ return (
+
+
+ {title}
+
+
+ {content}
+
+
+ );
+}
diff --git a/packages/shared/components/InfoTextItem.tsx b/packages/shared/components/InfoTextItem.tsx
new file mode 100644
index 00000000..5d8b32bb
--- /dev/null
+++ b/packages/shared/components/InfoTextItem.tsx
@@ -0,0 +1,33 @@
+import type { TextProps } from '@chakra-ui/react';
+import { Text } from '@chakra-ui/react';
+
+import InfoItem, { InfoItemStylesProps } from './InfoItem';
+
+export type InfoTextItemStylesProps = {
+ contentTextStyles?: TextProps;
+} & InfoItemStylesProps;
+
+export type InfoTextItemProps = {
+ title: string;
+ content: string;
+};
+
+export default function InfoTextItem({
+ content,
+ contentTextStyles,
+ ...props
+}: InfoTextItemProps & InfoTextItemStylesProps) {
+ return (
+
+
+ {content}
+
+
+ );
+}
diff --git a/packages/shared/components/InfoTextList.tsx b/packages/shared/components/InfoTextList.tsx
new file mode 100644
index 00000000..1a658883
--- /dev/null
+++ b/packages/shared/components/InfoTextList.tsx
@@ -0,0 +1,22 @@
+import { FlexProps } from '@chakra-ui/react';
+
+import InfoList from './InfoList';
+import type { InfoTextItemProps } from './InfoTextItem';
+import InfoTextItem from './InfoTextItem';
+
+type InfoTextListProps = {
+ infoTextItems: InfoTextItemProps[];
+} & Omit;
+
+export default function InfoTextList({
+ infoTextItems,
+ ...props
+}: InfoTextListProps) {
+ return (
+
+ {infoTextItems.map((infoTextItemProps, index) => (
+
+ ))}
+
+ );
+}
diff --git a/packages/shared/components/Label.tsx b/packages/shared/components/Label.tsx
new file mode 100644
index 00000000..d6ced9d3
--- /dev/null
+++ b/packages/shared/components/Label.tsx
@@ -0,0 +1,32 @@
+import { Badge } from '@chakra-ui/react';
+
+const LABEL_BACKGROUND_COLOR = {
+ GREEN: 'green.300',
+ ORANGE: 'orange.400',
+ YELLOW: 'yellow.300',
+ RED: 'red.400',
+ GRAY: 'gray.400',
+} as const;
+
+export type LabelProps = {
+ labelTitle: string;
+ type?: keyof typeof LABEL_BACKGROUND_COLOR;
+};
+
+export default function Label({ labelTitle, type = 'GREEN' }: LabelProps) {
+ return (
+
+ {labelTitle}
+
+ );
+}
diff --git a/packages/shared/components/LabelText.tsx b/packages/shared/components/LabelText.tsx
new file mode 100644
index 00000000..764407e4
--- /dev/null
+++ b/packages/shared/components/LabelText.tsx
@@ -0,0 +1,17 @@
+import { HStack, Text } from '@chakra-ui/react';
+
+import type { LabelProps } from './Label';
+import Label from './Label';
+
+type LabelTextProps = { content: string } & LabelProps;
+
+export default function LabelText({ labelTitle, content }: LabelTextProps) {
+ return (
+
+
+
+ {content}
+
+
+ );
+}
diff --git a/packages/shared/components/OptionMenu.tsx b/packages/shared/components/OptionMenu.tsx
new file mode 100644
index 00000000..6eb80748
--- /dev/null
+++ b/packages/shared/components/OptionMenu.tsx
@@ -0,0 +1,26 @@
+import {
+ Image,
+ Menu,
+ MenuButton,
+ MenuButtonProps,
+ MenuItem,
+ MenuList,
+} from '@chakra-ui/react';
+import { ReactElement } from 'react';
+
+import MenuIcon from '../assets/icon_menu.svg';
+
+type OptionMenuProps = {
+ children: ReactElement | ReactElement[];
+} & Omit;
+
+export default function OptionMenu({ children, ...props }: OptionMenuProps) {
+ return (
+
+ );
+}
diff --git a/packages/shared/components/ProfileInfo.tsx b/packages/shared/components/ProfileInfo.tsx
new file mode 100644
index 00000000..0f692143
--- /dev/null
+++ b/packages/shared/components/ProfileInfo.tsx
@@ -0,0 +1,35 @@
+import { Avatar, HStack, Text, VStack } from '@chakra-ui/react';
+
+type ProfileInfoProps = {
+ infoImage?: string;
+ infoTitle: string;
+ infoTexts?: string[];
+ children?: React.ReactNode;
+};
+
+export default function ProfileInfo({
+ infoImage,
+ infoTitle,
+ infoTexts,
+ children,
+}: ProfileInfoProps) {
+ return (
+
+
+
+
+
+ {infoTitle}
+
+ {children}
+
+ {infoTexts &&
+ infoTexts.map((infoText, index) => (
+
+ {infoText}
+
+ ))}
+
+
+ );
+}
diff --git a/packages/shared/components/RadioGroup.tsx b/packages/shared/components/RadioGroup.tsx
new file mode 100644
index 00000000..30a132b0
--- /dev/null
+++ b/packages/shared/components/RadioGroup.tsx
@@ -0,0 +1,52 @@
+import type {
+ RadioGroupProps as ChakraRadioGroupProps,
+ RadioProps,
+ StackProps,
+} from '@chakra-ui/react';
+import {
+ HStack,
+ Radio,
+ RadioGroup as ChakraRadioGroup,
+} from '@chakra-ui/react';
+
+type Radio = {
+ value: Value;
+ text: Text;
+};
+
+type RadioGroupProps = Omit & {
+ value: Value;
+ onChange: (nextValue: Value) => void;
+ defaultValue?: Value;
+ radios: Radio[];
+ hStackProps?: StackProps;
+ radioProps?: RadioProps;
+};
+
+export default function RadioGroup({
+ value,
+ onChange,
+ defaultValue,
+ radios,
+ hStackProps,
+ radioProps,
+ ...chakraRadioGropRestprops
+}: RadioGroupProps) {
+ return (
+
+
+ {radios.map(({ value, text }: Radio) => (
+
+ {text}
+
+ ))}
+
+
+ );
+}
diff --git a/packages/shared/components/ReviewItem.tsx b/packages/shared/components/ReviewItem.tsx
new file mode 100644
index 00000000..3a661a82
--- /dev/null
+++ b/packages/shared/components/ReviewItem.tsx
@@ -0,0 +1,97 @@
+import {
+ Card,
+ HStack,
+ Image,
+ Menu,
+ MenuButton,
+ MenuItem,
+ MenuList,
+ Text,
+} from '@chakra-ui/react';
+import React from 'react';
+
+import MenuIcon from '../assets/icon_menu.svg';
+
+type ReviewItemProps = {
+ children: React.ReactNode;
+ content: string;
+ images?: string[];
+ showMenuButton?: boolean;
+ onUpdate?: VoidFunction;
+ onDelete?: VoidFunction;
+};
+
+function CustomMenu({
+ onUpdate,
+ onDelete,
+}: Pick) {
+ return (
+
+ );
+}
+
+export default function ReviewItem({
+ children,
+ content,
+ images,
+ showMenuButton = false,
+ onUpdate,
+ onDelete,
+}: ReviewItemProps) {
+ return (
+
+ {children}
+ {showMenuButton && }
+
+ {content}
+
+
+ {images?.map((src, index) => (
+
+ ))}
+
+
+ );
+}
diff --git a/packages/shared/components/SettingGroup.tsx b/packages/shared/components/SettingGroup.tsx
new file mode 100644
index 00000000..6ae14b16
--- /dev/null
+++ b/packages/shared/components/SettingGroup.tsx
@@ -0,0 +1,25 @@
+import { Box, Text } from '@chakra-ui/react';
+
+import type { SettingItemProps } from './SettingItem';
+import SettingItem from './SettingItem';
+
+type SettingProps = {
+ groupTitle: string;
+ settingItems: SettingItemProps[];
+};
+
+export default function SettingGroup({
+ groupTitle,
+ settingItems,
+}: SettingProps) {
+ return (
+
+
+ {groupTitle}
+
+ {settingItems.map((item, index) => (
+
+ ))}
+
+ );
+}
diff --git a/packages/shared/components/SettingItem.tsx b/packages/shared/components/SettingItem.tsx
new file mode 100644
index 00000000..908ae860
--- /dev/null
+++ b/packages/shared/components/SettingItem.tsx
@@ -0,0 +1,26 @@
+import { Box, Flex, Image, Text } from '@chakra-ui/react';
+
+import Next from '../assets/icon_next.svg';
+
+export type SettingItemProps = {
+ itemTitle: string;
+ onClick: VoidFunction;
+};
+export default function SettingItem({ itemTitle, onClick }: SettingItemProps) {
+ return (
+
+ {itemTitle}
+
+
+
+
+ );
+}
diff --git a/packages/shared/constants/appType.ts b/packages/shared/constants/appType.ts
new file mode 100644
index 00000000..11710c6c
--- /dev/null
+++ b/packages/shared/constants/appType.ts
@@ -0,0 +1,6 @@
+const APP_TYPE = {
+ SHELTER_APP: 'SHELTER_APP',
+ VOLUNTEER_APP: 'VOLUNTEER_APP',
+} as const;
+
+export default APP_TYPE;
diff --git a/packages/shared/constants/baseURL.ts b/packages/shared/constants/baseURL.ts
new file mode 100644
index 00000000..16f714e9
--- /dev/null
+++ b/packages/shared/constants/baseURL.ts
@@ -0,0 +1 @@
+export const BASE_URL = '/';
diff --git a/packages/shared/constants/date.ts b/packages/shared/constants/date.ts
new file mode 100644
index 00000000..c72baf0f
--- /dev/null
+++ b/packages/shared/constants/date.ts
@@ -0,0 +1,7 @@
+export const MILISECONDS = {
+ YEAR: 60 * 60 * 24 * 365,
+ MONTH: 60 * 60 * 24 * 30,
+ DAY: 60 * 60 * 24,
+ HOUR: 60 * 60,
+ MINUITE: 60,
+};
diff --git a/packages/shared/constants/headerTitle.ts b/packages/shared/constants/headerTitle.ts
new file mode 100644
index 00000000..da89b7bf
--- /dev/null
+++ b/packages/shared/constants/headerTitle.ts
@@ -0,0 +1,36 @@
+import { PageType } from '../types/page';
+
+type HeaderTitle = {
+ [key in PageType]: string;
+};
+
+const headerTitle: HeaderTitle = {
+ VOLUNTEERS: '봉사자 모집',
+ VOLUNTEERS_DETAIL: '봉사자 모집 상세',
+ VOLUNTEERS_PROFILE: '봉사자 프로필',
+ VOLUNTEERS_SEARCH: '봉사자 모집글 검색',
+ VOLUNTEERS_WRITE: '봉사자 모집글 작성',
+ VOLUNTEERS_UPDATE: '봉사자 모집글 수정',
+ ANIMALS: '유기보호 동물',
+ ANIMALS_DETAIL: '유기보호 동물 상세',
+ ANIMALS_SEARCH: '유기보호 동물 검색',
+ ANIMALS_WRITE: '유기보호 동물 작성',
+ ANIMALS_UPDATE: '유기보호 동물 수정',
+ CHATTINGS: '채팅',
+ CHATTINGS_ROOM: '채팅방',
+ MYPAGE: '마이페이지',
+ MYPAGE_REVIEWS: '봉사 후기',
+ SETTINGS: '설정',
+ SETTINGS_ACCOUNT: '계정 정보 수정',
+ SETTINGS_PASSWORD: '비밀 번호 수정',
+ MANAGE_ATTENDANCE: '봉사자 출석 관리',
+ MANAGE_APPLY: '봉사자 신청 현황',
+ NOTIFICATIONS: '알림',
+ SHELTERS_PROFILE: '보호소 프로필',
+ SHELTERS_REVIEWS_WRITE: '봉사 후기 작성',
+ SHELTERS_REVIEWS_UPDATE: '봉사 후기 수정',
+ SIGNUP: '회원가입',
+ SIGNIN: '로그인',
+} as const;
+
+export default headerTitle;
diff --git a/packages/shared/constants/headerType.ts b/packages/shared/constants/headerType.ts
new file mode 100644
index 00000000..11010d76
--- /dev/null
+++ b/packages/shared/constants/headerType.ts
@@ -0,0 +1,7 @@
+const HEADER_TYPE = {
+ DEFAULT: 'DEFAULT',
+ DETAIL: 'DETAIL',
+ SEARCH: 'SEARCH',
+} as const;
+
+export default HEADER_TYPE;
diff --git a/packages/shared/constants/pageType.ts b/packages/shared/constants/pageType.ts
new file mode 100644
index 00000000..0bd02019
--- /dev/null
+++ b/packages/shared/constants/pageType.ts
@@ -0,0 +1,30 @@
+const PAGE_TYPE = {
+ VOLUNTEERS: 'VOLUNTEERS',
+ VOLUNTEERS_DETAIL: 'VOLUNTEERS_DETAIL',
+ VOLUNTEERS_PROFILE: 'VOLUNTEERS_PROFILE',
+ VOLUNTEERS_SEARCH: 'VOLUNTEERS_SEARCH',
+ VOLUNTEERS_WRITE: 'VOLUNTEERS_WRITE',
+ VOLUNTEERS_UPDATE: 'VOLUNTEERS_UPDATE',
+ ANIMALS: 'ANIMALS',
+ ANIMALS_DETAIL: 'ANIMALS_DETAIL',
+ ANIMALS_SEARCH: 'ANIMALS_SEARCH',
+ ANIMALS_WRITE: 'ANIMALS_WRITE',
+ ANIMALS_UPDATE: 'ANIMALS_UPDATE',
+ CHATTINGS: 'CHATTINGS',
+ CHATTINGS_ROOM: 'CHATTINGS_ROOM',
+ MYPAGE: 'MYPAGE',
+ MYPAGE_REVIEWS: 'MYPAGE_REVIEWS',
+ SETTINGS: 'SETTINGS',
+ SETTINGS_ACCOUNT: 'SETTINGS_ACCOUNT',
+ SETTINGS_PASSWORD: 'SETTINGS_PASSWORD',
+ MANAGE_ATTENDANCE: 'MANAGE_ATTENDANCE',
+ MANAGE_APPLY: 'MANAGE_APPLY',
+ NOTIFICATIONS: 'NOTIFICATIONS',
+ SHELTERS_PROFILE: 'SHELTERS_PROFILE',
+ SHELTERS_REVIEWS_WRITE: 'SHELTERS_REVIEWS_WRITE',
+ SHELTERS_REVIEWS_UPDATE: 'SHELTERS_REVIEWS_UPDATE',
+ SIGNUP: 'SIGNUP',
+ SIGNIN: 'SIGNIN',
+} as const;
+
+export default PAGE_TYPE;
diff --git a/packages/shared/fonts/IBMPlexSans-Regular.woff b/packages/shared/fonts/IBMPlexSans-Regular.woff
new file mode 100644
index 00000000..dc4ae673
Binary files /dev/null and b/packages/shared/fonts/IBMPlexSans-Regular.woff differ
diff --git a/packages/shared/fonts/index.tsx b/packages/shared/fonts/index.tsx
new file mode 100644
index 00000000..db5c928e
--- /dev/null
+++ b/packages/shared/fonts/index.tsx
@@ -0,0 +1,27 @@
+import { Global } from '@emotion/react';
+
+export default function Fonts() {
+ return (
+
+ );
+}
diff --git a/packages/shared/hooks/usePageType.ts b/packages/shared/hooks/usePageType.ts
new file mode 100644
index 00000000..065b9ea3
--- /dev/null
+++ b/packages/shared/hooks/usePageType.ts
@@ -0,0 +1,18 @@
+import { useEffect, useState } from 'react';
+import { useMatches } from 'react-router-dom';
+
+import { PageType } from '../types/page';
+
+export const usePageType = () => {
+ const [pageType, setPageType] = useState();
+
+ const match = useMatches().at(-1);
+
+ useEffect(() => {
+ const page = match?.id;
+
+ setPageType(page as PageType);
+ }, [match]);
+
+ return { pageType };
+};
diff --git a/packages/shared/hooks/useRadioGroup.ts b/packages/shared/hooks/useRadioGroup.ts
new file mode 100644
index 00000000..3ef1cf2a
--- /dev/null
+++ b/packages/shared/hooks/useRadioGroup.ts
@@ -0,0 +1,13 @@
+import { useState } from 'react';
+
+const useRadioGroup = (
+ initialValue: Value,
+): [Value, (nextValue: Value) => void] => {
+ const [value, setValue] = useState(initialValue);
+
+ const changeValue = (nextValue: Value) => setValue(nextValue);
+
+ return [value, changeValue];
+};
+
+export default useRadioGroup;
diff --git a/packages/shared/hooks/useToggle.ts b/packages/shared/hooks/useToggle.ts
new file mode 100644
index 00000000..87ac3b4c
--- /dev/null
+++ b/packages/shared/hooks/useToggle.ts
@@ -0,0 +1,13 @@
+import { useBoolean } from '@chakra-ui/react';
+
+const useToggle = (initialState = false): [boolean, () => void] => {
+ const [isTrue, setIsTrue] = useBoolean(initialState);
+
+ const toggle = () => {
+ setIsTrue.toggle();
+ };
+
+ return [isTrue, toggle];
+};
+
+export default useToggle;
diff --git a/packages/ui/index.tsx b/packages/shared/index.tsx
similarity index 100%
rename from packages/ui/index.tsx
rename to packages/shared/index.tsx
diff --git a/packages/shared/layout/BottomNavBar/NavBarButton.tsx b/packages/shared/layout/BottomNavBar/NavBarButton.tsx
new file mode 100644
index 00000000..5cf0c77c
--- /dev/null
+++ b/packages/shared/layout/BottomNavBar/NavBarButton.tsx
@@ -0,0 +1,29 @@
+import { Box, Image } from '@chakra-ui/react';
+
+export type NavBarButtonProps = {
+ onClick: VoidFunction;
+ selected: boolean;
+ buttonImageSrc: [string, string];
+ buttonText: string;
+};
+
+export default function NavBarButton({
+ onClick,
+ selected,
+ buttonImageSrc,
+ buttonText,
+}: NavBarButtonProps) {
+ const [unselectedButton, selectedButton] = buttonImageSrc;
+
+ return (
+
+
+ {buttonText}
+
+ );
+}
diff --git a/packages/shared/layout/BottomNavBar/index.tsx b/packages/shared/layout/BottomNavBar/index.tsx
new file mode 100644
index 00000000..2e9b0f28
--- /dev/null
+++ b/packages/shared/layout/BottomNavBar/index.tsx
@@ -0,0 +1,71 @@
+import { Flex } from '@chakra-ui/react';
+import { useNavigate } from 'react-router-dom';
+
+import AnimalsSelectedIcon from '../../assets/bottomNavBar/icon_animals_selected.svg';
+import AnimalsUnselectedIcon from '../../assets/bottomNavBar/icon_animals_unselected.svg';
+import ChattingsSelectedIcon from '../../assets/bottomNavBar/icon_chattings_selected.svg';
+import ChattingsUnselectedIcon from '../../assets/bottomNavBar/icon_chattings_unselected.svg';
+import MyPageSeletedIcon from '../../assets/bottomNavBar/icon_mypage_selected.svg';
+import MyPageUnselectedIcon from '../../assets/bottomNavBar/icon_mypage_unselected.svg';
+import VolunteersSelectedIcon from '../../assets/bottomNavBar/icon_volunteers_selected.svg';
+import VolunteersUnselectedIcon from '../../assets/bottomNavBar/icon_volunteers_unselected.svg';
+import PAGE_TYPE from '../../constants/pageType';
+import { usePageType } from '../../hooks/usePageType';
+import NavBarButton from './NavBarButton';
+import { useBottomNavBar } from './useBottomNavBar';
+
+export default function BottomNavBar() {
+ const { pageType } = usePageType();
+ const { isBottomNavBarVisible } = useBottomNavBar(pageType);
+
+ const navigate = useNavigate();
+
+ const goVolunteers = () => navigate('/volunteers');
+ const goAnimals = () => navigate('/animals');
+ const goChattings = () => navigate('/chattings');
+ const goMyPage = () => navigate('/mypage');
+
+ return (
+ isBottomNavBarVisible && (
+
+
+
+
+
+
+ )
+ );
+}
diff --git a/packages/shared/layout/BottomNavBar/useBottomNavBar.ts b/packages/shared/layout/BottomNavBar/useBottomNavBar.ts
new file mode 100644
index 00000000..32c07ec6
--- /dev/null
+++ b/packages/shared/layout/BottomNavBar/useBottomNavBar.ts
@@ -0,0 +1,23 @@
+import { useEffect, useState } from 'react';
+
+import PAGE_TYPE from '../../constants/pageType';
+import { PageType } from '../../types/page';
+
+export const useBottomNavBar = (pageType?: PageType) => {
+ const [isBottomNavBarVisible, setIsBottomNavBarVisible] = useState(false);
+
+ useEffect(() => {
+ if (
+ pageType === PAGE_TYPE.VOLUNTEERS ||
+ pageType === PAGE_TYPE.ANIMALS ||
+ pageType === PAGE_TYPE.CHATTINGS ||
+ pageType === PAGE_TYPE.MYPAGE
+ ) {
+ return setIsBottomNavBarVisible(true);
+ }
+
+ return setIsBottomNavBarVisible(false);
+ }, [pageType]);
+
+ return { isBottomNavBarVisible };
+};
diff --git a/packages/shared/layout/Header/DefaultHeader/headerIconState.ts b/packages/shared/layout/Header/DefaultHeader/headerIconState.ts
new file mode 100644
index 00000000..7a296f32
--- /dev/null
+++ b/packages/shared/layout/Header/DefaultHeader/headerIconState.ts
@@ -0,0 +1,85 @@
+import { AppType } from '../../../types/app';
+import { DefaultHeaderIconVisibility } from './useDefaultHeader';
+
+type DefaultHeaderIconState = {
+ [key: string]: {
+ [key in AppType]: DefaultHeaderIconVisibility;
+ };
+};
+
+const defaultHeaderIconState: DefaultHeaderIconState = {
+ VOLUNTEERS: {
+ SHELTER_APP: {
+ searchIcon: true,
+ settingsIcon: false,
+ notificationsIcon: true,
+ },
+ VOLUNTEER_APP: {
+ searchIcon: true,
+ settingsIcon: false,
+ notificationsIcon: true,
+ },
+ },
+ ANIMALS: {
+ SHELTER_APP: {
+ searchIcon: true,
+ settingsIcon: false,
+ notificationsIcon: true,
+ },
+ VOLUNTEER_APP: {
+ searchIcon: false,
+ settingsIcon: false,
+ notificationsIcon: true,
+ },
+ },
+ CHATTINGS: {
+ SHELTER_APP: {
+ searchIcon: false,
+ settingsIcon: false,
+ notificationsIcon: true,
+ },
+ VOLUNTEER_APP: {
+ searchIcon: false,
+ settingsIcon: false,
+ notificationsIcon: true,
+ },
+ },
+ MYPAGE: {
+ SHELTER_APP: {
+ searchIcon: false,
+ settingsIcon: false,
+ notificationsIcon: true,
+ },
+ VOLUNTEER_APP: {
+ searchIcon: false,
+ settingsIcon: true,
+ notificationsIcon: true,
+ },
+ },
+ SIGNUP: {
+ SHELTER_APP: {
+ searchIcon: false,
+ settingsIcon: false,
+ notificationsIcon: false,
+ },
+ VOLUNTEER_APP: {
+ searchIcon: false,
+ settingsIcon: false,
+ notificationsIcon: false,
+ },
+ },
+ SIGNIN: {
+ SHELTER_APP: {
+ searchIcon: false,
+ settingsIcon: false,
+ notificationsIcon: false,
+ },
+ VOLUNTEER_APP: {
+ searchIcon: false,
+ settingsIcon: false,
+ notificationsIcon: false,
+ },
+ },
+} as const;
+
+export default defaultHeaderIconState;
diff --git a/packages/shared/layout/Header/DefaultHeader/index.tsx b/packages/shared/layout/Header/DefaultHeader/index.tsx
new file mode 100644
index 00000000..b602115b
--- /dev/null
+++ b/packages/shared/layout/Header/DefaultHeader/index.tsx
@@ -0,0 +1,57 @@
+import { Box, ButtonGroup, Flex, Image, Text } from '@chakra-ui/react';
+import { useLocation, useNavigate } from 'react-router-dom';
+
+import NotificationsIcon from '../../../assets/icon_notifications.svg';
+import SearchIcon from '../../../assets/icon_search.svg';
+import SettingsIcon from '../../../assets/icon_settings.svg';
+import { HeaderProps } from '../index';
+import { useDefaultHeader } from './useDefaultHeader';
+
+export default function DefaultHeader({ appType }: HeaderProps) {
+ const navigate = useNavigate();
+ const { pathname } = useLocation();
+ const { title, iconVisibility } = useDefaultHeader(appType);
+
+ const { searchIcon, settingsIcon, notificationsIcon } = iconVisibility;
+
+ const goSearch = () => navigate(`${pathname}/search`);
+ const goSettings = () => navigate('/settings');
+ const goNotifications = () => navigate('/notifications');
+
+ return (
+
+
+ {title}
+
+
+ {searchIcon && (
+
+
+
+ )}
+ {settingsIcon && (
+
+
+
+ )}
+ {notificationsIcon && (
+
+
+
+ )}
+
+
+ );
+}
diff --git a/packages/shared/layout/Header/DefaultHeader/useDefaultHeader.ts b/packages/shared/layout/Header/DefaultHeader/useDefaultHeader.ts
new file mode 100644
index 00000000..de7a25d7
--- /dev/null
+++ b/packages/shared/layout/Header/DefaultHeader/useDefaultHeader.ts
@@ -0,0 +1,37 @@
+import { useEffect, useState } from 'react';
+
+import { usePageType } from '../../../hooks/usePageType';
+import { AppType } from '../../../types/app';
+import { getHeaderTitle } from '../utils';
+import defaultHeaderState from './headerIconState';
+
+export type DefaultHeaderIconVisibility = {
+ searchIcon: boolean;
+ settingsIcon: boolean;
+ notificationsIcon: boolean;
+};
+
+export const useDefaultHeader = (appType: AppType) => {
+ const { pageType } = usePageType();
+ const [title, setTitle] = useState('');
+ const [iconVisibility, setIconVisibility] =
+ useState({
+ searchIcon: false,
+ settingsIcon: false,
+ notificationsIcon: false,
+ });
+
+ useEffect(() => {
+ if (pageType) {
+ setTitle(getHeaderTitle(pageType));
+
+ const iconState = defaultHeaderState[pageType];
+
+ if (iconState) {
+ setIconVisibility(iconState[appType]);
+ }
+ }
+ }, [appType, pageType]);
+
+ return { title, iconVisibility };
+};
diff --git a/packages/shared/layout/Header/DetailHeader/index.tsx b/packages/shared/layout/Header/DetailHeader/index.tsx
new file mode 100644
index 00000000..3f1a25b5
--- /dev/null
+++ b/packages/shared/layout/Header/DetailHeader/index.tsx
@@ -0,0 +1,61 @@
+import { Box, Flex, Image, MenuItem, Text } from '@chakra-ui/react';
+import { useLocation, useNavigate } from 'react-router-dom';
+
+import BackIcon from '../../../assets/icon_back.svg';
+import OptionMenu from '../../../components/OptionMenu';
+import useDetailHeaderStore from '../../../store/detailHeaderStore';
+import { HeaderProps } from '../index';
+import { useDetailHeader } from './useDetailHeader';
+
+export default function DetailHeader({ appType }: HeaderProps) {
+ const navigate = useNavigate();
+ const { pathname } = useLocation();
+ const { title, iconVisibility } = useDetailHeader(appType);
+ const onDelete = useDetailHeaderStore((state) => state.onDelete);
+
+ const { menuIcon } = iconVisibility;
+
+ const goBack = () => navigate(-1);
+
+ const handleUpdate = () => {
+ const [, path, id] = pathname.split('/');
+
+ navigate(`${path}/write/${id}`);
+ };
+
+ const handleDelete = () => {
+ const [, , id] = pathname.split('/');
+
+ onDelete(Number(id));
+ };
+
+ return (
+
+
+
+
+
+ {title}
+
+ {menuIcon && (
+
+
+
+
+ )}
+
+ );
+}
diff --git a/packages/shared/layout/Header/DetailHeader/useDetailHeader.ts b/packages/shared/layout/Header/DetailHeader/useDetailHeader.ts
new file mode 100644
index 00000000..64eff87b
--- /dev/null
+++ b/packages/shared/layout/Header/DetailHeader/useDetailHeader.ts
@@ -0,0 +1,36 @@
+import { useEffect, useState } from 'react';
+
+import APP_TYPE from '../../../constants/appType';
+import PAGE_TYPE from '../../../constants/pageType';
+import { usePageType } from '../../../hooks/usePageType';
+import { AppType } from '../../../types/app';
+import { getHeaderTitle } from '../utils';
+
+export type DetailHeaderIconVisibility = {
+ menuIcon: boolean;
+};
+
+export const useDetailHeader = (appType: AppType) => {
+ const { pageType } = usePageType();
+ const [title, setTitle] = useState('');
+ const [iconVisibility, setIconVisibility] =
+ useState({ menuIcon: false });
+
+ useEffect(() => {
+ if (pageType) {
+ setTitle(getHeaderTitle(pageType));
+
+ if (
+ appType === APP_TYPE.SHELTER_APP &&
+ (pageType === PAGE_TYPE.VOLUNTEERS_DETAIL ||
+ pageType === PAGE_TYPE.ANIMALS_DETAIL)
+ ) {
+ setIconVisibility({ menuIcon: true });
+ } else {
+ setIconVisibility({ menuIcon: false });
+ }
+ }
+ }, [appType, pageType]);
+
+ return { title, iconVisibility };
+};
diff --git a/packages/shared/layout/Header/SearchHeader/index.tsx b/packages/shared/layout/Header/SearchHeader/index.tsx
new file mode 100644
index 00000000..e91b1978
--- /dev/null
+++ b/packages/shared/layout/Header/SearchHeader/index.tsx
@@ -0,0 +1,86 @@
+import { Box, Flex, FormControl, Image, Input } from '@chakra-ui/react';
+import { ChangeEvent, FormEvent, useEffect, useRef } from 'react';
+import { useNavigate } from 'react-router-dom';
+
+import BackIcon from '../../../assets/icon_back.svg';
+import useSearchHeaderStore from '../../../store/searchHeaderStore';
+
+export default function SearchHeader() {
+ const navigate = useNavigate();
+ const [keyword, setKeyword, onSearch] = useSearchHeaderStore((state) => [
+ state.keyword,
+ state.setKeyword,
+ state.onSearch,
+ ]);
+
+ const inputRef = useRef(null);
+
+ useEffect(() => {
+ if (inputRef.current) {
+ inputRef.current.focus();
+ }
+ }, []);
+
+ const goBack = () => navigate(-1);
+
+ const handleChangeKeyword = (event: ChangeEvent) => {
+ const { value } = event.target;
+ setKeyword(value);
+ };
+
+ const handleSubmit = (event: FormEvent) => {
+ event.preventDefault();
+ if (!keyword) {
+ return;
+ }
+
+ onSearch(keyword);
+
+ if (inputRef.current) {
+ inputRef.current.blur();
+ }
+ };
+
+ return (
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/packages/shared/layout/Header/index.tsx b/packages/shared/layout/Header/index.tsx
new file mode 100644
index 00000000..5f4c9f5d
--- /dev/null
+++ b/packages/shared/layout/Header/index.tsx
@@ -0,0 +1,27 @@
+import { usePageType } from '../../hooks/usePageType';
+import { AppType } from '../../types/app';
+import DefaultHeader from './DefaultHeader';
+import DetailHeader from './DetailHeader';
+import SearchHeader from './SearchHeader';
+import { useHeader } from './useHeader';
+
+const Headers = {
+ DEFAULT: (props: HeaderProps) => ,
+ DETAIL: (props: HeaderProps) => ,
+ SEARCH: () => ,
+};
+
+export type HeaderProps = {
+ appType: AppType;
+};
+
+export default function Header({ appType }: HeaderProps) {
+ const { pageType } = usePageType();
+ const { headerType } = useHeader();
+
+ if (!pageType) {
+ return null;
+ }
+
+ return Headers[headerType]({ appType });
+}
diff --git a/packages/shared/layout/Header/useHeader.ts b/packages/shared/layout/Header/useHeader.ts
new file mode 100644
index 00000000..6a283b0d
--- /dev/null
+++ b/packages/shared/layout/Header/useHeader.ts
@@ -0,0 +1,19 @@
+import { useEffect, useState } from 'react';
+
+import HEADER_TYPE from '../../constants/headerType';
+import { usePageType } from '../../hooks/usePageType';
+import { HeaderType } from '../../types/header';
+import { getHeaderType } from './utils';
+
+export const useHeader = () => {
+ const { pageType } = usePageType();
+ const [headerType, setHeaderType] = useState(HEADER_TYPE.DEFAULT);
+
+ useEffect(() => {
+ if (pageType) {
+ setHeaderType(getHeaderType(pageType));
+ }
+ }, [pageType]);
+
+ return { headerType };
+};
diff --git a/packages/shared/layout/Header/utils.ts b/packages/shared/layout/Header/utils.ts
new file mode 100644
index 00000000..f599537e
--- /dev/null
+++ b/packages/shared/layout/Header/utils.ts
@@ -0,0 +1,28 @@
+import headerTitle from '../../constants/headerTitle';
+import HEADER_TYPE from '../../constants/headerType';
+import PAGE_TYPE from '../../constants/pageType';
+import { HeaderType } from '../../types/header';
+import { PageType } from '../../types/page';
+
+export const getHeaderType = (pageType: PageType): HeaderType => {
+ if (
+ pageType === PAGE_TYPE.VOLUNTEERS ||
+ pageType === PAGE_TYPE.ANIMALS ||
+ pageType === PAGE_TYPE.CHATTINGS ||
+ pageType === PAGE_TYPE.MYPAGE
+ ) {
+ return HEADER_TYPE.DEFAULT;
+ }
+
+ if (
+ pageType === PAGE_TYPE.VOLUNTEERS_SEARCH ||
+ pageType === PAGE_TYPE.ANIMALS_SEARCH
+ ) {
+ return HEADER_TYPE.SEARCH;
+ }
+
+ return HEADER_TYPE.DETAIL;
+};
+
+export const getHeaderTitle = (pageType: PageType): string =>
+ headerTitle[pageType];
diff --git a/packages/shared/layout/index.tsx b/packages/shared/layout/index.tsx
new file mode 100644
index 00000000..02d7a7f5
--- /dev/null
+++ b/packages/shared/layout/index.tsx
@@ -0,0 +1,22 @@
+import { Box, Container } from '@chakra-ui/react';
+import { Outlet } from 'react-router-dom';
+
+import { AppType } from '../types/app';
+import BottomNavBar from './BottomNavBar';
+import Header from './Header';
+
+type LayoutProps = {
+ appType: AppType;
+};
+
+export default function Layout({ appType }: LayoutProps) {
+ return (
+
+
+
+
+
+
+
+ );
+}
diff --git a/packages/ui/package.json b/packages/shared/package.json
similarity index 80%
rename from packages/ui/package.json
rename to packages/shared/package.json
index 833d471d..488ccc79 100644
--- a/packages/ui/package.json
+++ b/packages/shared/package.json
@@ -1,5 +1,5 @@
{
- "name": "ui",
+ "name": "shared",
"version": "0.0.0",
"license": "MIT",
"main": "./index.tsx",
@@ -11,14 +11,16 @@
"@chakra-ui/react": "^2.8.1",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
+ "axios": "^1.6.0",
"framer-motion": "^10.16.4",
"react": "^18.2.0",
- "react-dom": "^18.2.0"
+ "react-dom": "^18.2.0",
+ "react-router-dom": "^6.17.0",
+ "zustand": "^4.4.4"
},
"devDependencies": {
"@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7",
- "@vitejs/plugin-react-swc": "^3.4.0",
"eslint": "^8.45.0",
"eslint-config-custom": "workspace:*",
"tsconfig": "workspace:*",
diff --git a/packages/shared/store/detailHeaderStore.ts b/packages/shared/store/detailHeaderStore.ts
new file mode 100644
index 00000000..6caad5c9
--- /dev/null
+++ b/packages/shared/store/detailHeaderStore.ts
@@ -0,0 +1,19 @@
+import { create } from 'zustand';
+
+type DeleteFunction = (id: number) => void;
+interface DetailHeaderState {
+ onDelete: DeleteFunction;
+}
+
+interface DetailHeaderActions {
+ setOnDelete: (onDelete: DeleteFunction) => void;
+}
+
+const useDetailHeaderStore = create(
+ (set) => ({
+ onDelete: () => {},
+ setOnDelete: (onDelete: DeleteFunction) => set(() => ({ onDelete })),
+ }),
+);
+
+export default useDetailHeaderStore;
diff --git a/packages/shared/store/searchHeaderStore.ts b/packages/shared/store/searchHeaderStore.ts
new file mode 100644
index 00000000..38e94ec4
--- /dev/null
+++ b/packages/shared/store/searchHeaderStore.ts
@@ -0,0 +1,24 @@
+import { create } from 'zustand';
+
+type SearchFunction = (keyword: string) => void;
+
+interface SearchHeaderState {
+ keyword: string;
+ onSearch: SearchFunction;
+}
+
+interface SearchHeaderActions {
+ setKeyword: (keyword: string) => void;
+ setOnSearch: (onSearch: SearchFunction) => void;
+}
+
+const useSearchHeaderStore = create(
+ (set) => ({
+ keyword: '',
+ onSearch: () => {},
+ setKeyword: (keyword: string) => set(() => ({ keyword })),
+ setOnSearch: (onSearch: SearchFunction) => set(() => ({ onSearch })),
+ }),
+);
+
+export default useSearchHeaderStore;
diff --git a/packages/shared/svg.d.ts b/packages/shared/svg.d.ts
new file mode 100644
index 00000000..cdb2b1a9
--- /dev/null
+++ b/packages/shared/svg.d.ts
@@ -0,0 +1,4 @@
+declare module '*.svg' {
+ const content: string;
+ export default content;
+}
diff --git a/packages/shared/theme/index.ts b/packages/shared/theme/index.ts
new file mode 100644
index 00000000..7a98714f
--- /dev/null
+++ b/packages/shared/theme/index.ts
@@ -0,0 +1,17 @@
+import { extendTheme } from '@chakra-ui/react';
+
+const theme = extendTheme({
+ fonts: {
+ heading: `'IBMPlexSans-Heading'`,
+ body: `'IBMPlexSans-Body'`,
+ },
+ styles: {
+ global: {
+ body: {
+ 'overscroll-behavior': 'none',
+ },
+ },
+ },
+});
+
+export default theme;
diff --git a/packages/ui/tsconfig.json b/packages/shared/tsconfig.json
similarity index 60%
rename from packages/ui/tsconfig.json
rename to packages/shared/tsconfig.json
index ad816cf5..2719d08e 100644
--- a/packages/ui/tsconfig.json
+++ b/packages/shared/tsconfig.json
@@ -1,11 +1,15 @@
{
"extends": "tsconfig/base.json",
- "include": ["."],
+ "include": [".", "svg.d.ts"],
"exclude": ["dist", "build", "node_modules"],
"compilerOptions": {
"jsx": "react-jsx",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"module": "ESNext",
- "target": "es6"
+ "target": "es6",
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["../../packages/shared/*"]
+ }
}
}
diff --git a/packages/shared/types/apis/auth.ts b/packages/shared/types/apis/auth.ts
new file mode 100644
index 00000000..71c60fc2
--- /dev/null
+++ b/packages/shared/types/apis/auth.ts
@@ -0,0 +1,17 @@
+export type checkDuplicatedEmailRequestData = {
+ email: string;
+};
+
+export type SigninRequestData = checkDuplicatedEmailRequestData & {
+ password: string;
+};
+
+export type SigninResponseData = {
+ accessToken: string;
+ useId: number;
+ role: string;
+};
+
+export type checkDuplicatedEmailResponseData = {
+ isDuplicated: boolean;
+};
diff --git a/packages/shared/types/apis/error.ts b/packages/shared/types/apis/error.ts
new file mode 100644
index 00000000..46ec5774
--- /dev/null
+++ b/packages/shared/types/apis/error.ts
@@ -0,0 +1,4 @@
+export type ErrorResponseData = {
+ errorCode: string;
+ message: string;
+};
diff --git a/packages/shared/types/app.ts b/packages/shared/types/app.ts
new file mode 100644
index 00000000..82b07294
--- /dev/null
+++ b/packages/shared/types/app.ts
@@ -0,0 +1,3 @@
+import APP_TYPE from '../constants/appType';
+
+export type AppType = keyof typeof APP_TYPE;
diff --git a/packages/shared/types/header.ts b/packages/shared/types/header.ts
new file mode 100644
index 00000000..3ded0871
--- /dev/null
+++ b/packages/shared/types/header.ts
@@ -0,0 +1,3 @@
+import HEADER_TYPE from '../constants/headerType';
+
+export type HeaderType = keyof typeof HEADER_TYPE;
diff --git a/packages/shared/types/page.ts b/packages/shared/types/page.ts
new file mode 100644
index 00000000..e5affb64
--- /dev/null
+++ b/packages/shared/types/page.ts
@@ -0,0 +1,3 @@
+import PAGE_TYPE from '../constants/pageType';
+
+export type PageType = keyof typeof PAGE_TYPE;
diff --git a/packages/shared/utils/date.ts b/packages/shared/utils/date.ts
new file mode 100644
index 00000000..cd24fe5e
--- /dev/null
+++ b/packages/shared/utils/date.ts
@@ -0,0 +1,36 @@
+import { MILISECONDS } from '../constants/date';
+
+export const createFormattedTime = (
+ date: Date,
+ format = 'YYYY.MM.DD',
+): string => {
+ const formatReplacements: Record = {
+ YYYY: String(date.getFullYear()),
+ YY: String(date.getFullYear()).substring(2, 4),
+ MM: String(date.getMonth() + 1).padStart(2, '0'),
+ DD: String(date.getDate()).padStart(2, '0'),
+ hh: String(date.getHours()).padStart(2, '0'),
+ mm: String(date.getMinutes()).padStart(2, '0'),
+ };
+
+ const formattedTime = format.replace(
+ /YYYY|YY|MM|DD|hh|mm/g,
+ (match) => formatReplacements[match],
+ );
+
+ return formattedTime;
+};
+
+export const isSameDay = (a: Date, b: Date): boolean => {
+ const diff = (a.getTime() - b.getTime()) / 1000;
+
+ return Math.trunc(diff / MILISECONDS.DAY) === 0;
+};
+
+export function getDDay(deadLine: string) {
+ const deadLineDate = new Date(deadLine).getTime();
+ const currentDate = new Date().getTime();
+ const diffDate = deadLineDate - currentDate;
+
+ return Math.floor(diffDate / (1000 * MILISECONDS.DAY)).toString();
+}
diff --git a/packages/ui/components/CustomButton.tsx b/packages/ui/components/CustomButton.tsx
deleted file mode 100644
index 1748297e..00000000
--- a/packages/ui/components/CustomButton.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import { Button } from '@chakra-ui/react';
-
-export function CustomButton() {
- return ;
-}
diff --git a/packages/ui/components/Header.tsx b/packages/ui/components/Header.tsx
deleted file mode 100644
index 80f3e150..00000000
--- a/packages/ui/components/Header.tsx
+++ /dev/null
@@ -1,3 +0,0 @@
-export function Header() {
- return ;
-}
diff --git a/packlint.config.mjs b/packlint.config.mjs
index 03280a48..67202395 100644
--- a/packlint.config.mjs
+++ b/packlint.config.mjs
@@ -1,3 +1,7 @@
export default {
- files: ['./package.json', './packages/*/package.json', './apps/*/package.json'],
+ files: [
+ './package.json',
+ './packages/*/package.json',
+ './apps/*/package.json',
+ ],
};
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index c3342c43..4f1d6e2f 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -10,19 +10,19 @@ importers:
devDependencies:
'@commitlint/cli':
specifier: ^18.2.0
- version: 18.2.0(typescript@5.0.2)
+ version: 18.2.0(typescript@5.2.2)
'@commitlint/config-conventional':
specifier: ^18.1.0
version: 18.1.0
commitizen:
specifier: ^4.3.0
- version: 4.3.0(typescript@5.0.2)
+ version: 4.3.0(typescript@5.2.2)
cz-customizable:
specifier: ^7.0.0
version: 7.0.0
eslint:
- specifier: ^7.32.0
- version: 7.32.0
+ specifier: ^8.45.0
+ version: 8.53.0
eslint-config-custom:
specifier: workspace:*
version: link:packages/eslint-config-custom
@@ -36,8 +36,8 @@ importers:
specifier: ^0.2.4
version: 0.2.4(typanion@3.14.0)
prettier:
- specifier: ^2.5.1
- version: 2.8.0
+ specifier: 3.0.3
+ version: 3.0.3
turbo:
specifier: latest
version: 1.10.16
@@ -46,13 +46,13 @@ importers:
dependencies:
'@chakra-ui/react':
specifier: ^2.8.1
- version: 2.8.1(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.15)(framer-motion@10.16.4)(react-dom@18.2.0)(react@18.2.0)
+ version: 2.8.1(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.33)(framer-motion@10.16.4)(react-dom@18.2.0)(react@18.2.0)
'@emotion/react':
specifier: ^11.11.1
- version: 11.11.1(@types/react@18.2.15)(react@18.2.0)
+ version: 11.11.1(@types/react@18.2.33)(react@18.2.0)
'@emotion/styled':
specifier: ^11.11.0
- version: 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.15)(react@18.2.0)
+ version: 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.33)(react@18.2.0)
'@hookform/resolvers':
specifier: ^3.3.2
version: 3.3.2(react-hook-form@7.47.0)
@@ -85,59 +85,59 @@ importers:
version: 7.47.0(react@18.2.0)
react-router-dom:
specifier: ^6.17.0
- version: 6.17.0(react-dom@18.2.0)(react@18.2.0)
- ui:
+ version: 6.18.0(react-dom@18.2.0)(react@18.2.0)
+ shared:
specifier: workspace:*
- version: link:../../packages/ui
+ version: link:../../packages/shared
zod:
specifier: ^3.22.4
version: 3.22.4
zustand:
specifier: ^4.4.4
- version: 4.4.4(@types/react@18.2.15)(react@18.2.0)
+ version: 4.4.5(@types/react@18.2.33)(react@18.2.0)
devDependencies:
'@types/node':
specifier: ^20.8.10
version: 20.8.10
'@types/react':
specifier: ^18.2.15
- version: 18.2.15
+ version: 18.2.33
'@types/react-dom':
specifier: ^18.2.7
- version: 18.2.7
+ version: 18.2.14
'@vitejs/plugin-react-swc':
specifier: ^3.4.0
- version: 3.4.0(vite@4.4.5)
+ version: 3.4.1(vite@4.5.0)
eslint:
specifier: ^8.45.0
- version: 8.45.0
+ version: 8.52.0
eslint-config-custom:
specifier: workspace:*
version: link:../../packages/eslint-config-custom
msw:
specifier: ^2.0.1
- version: 2.0.1(typescript@5.0.2)
+ version: 2.0.1(typescript@5.2.2)
tsconfig:
specifier: workspace:*
version: link:../../packages/tsconfig
typescript:
specifier: ^5.0.2
- version: 5.0.2
+ version: 5.2.2
vite:
specifier: ^4.4.5
- version: 4.4.5(@types/node@20.8.10)
+ version: 4.5.0(@types/node@20.8.10)
apps/volunteer:
dependencies:
'@chakra-ui/react':
specifier: ^2.8.1
- version: 2.8.1(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.15)(framer-motion@10.16.4)(react-dom@18.2.0)(react@18.2.0)
+ version: 2.8.1(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.33)(framer-motion@10.16.4)(react-dom@18.2.0)(react@18.2.0)
'@emotion/react':
specifier: ^11.11.1
- version: 11.11.1(@types/react@18.2.15)(react@18.2.0)
+ version: 11.11.1(@types/react@18.2.33)(react@18.2.0)
'@emotion/styled':
specifier: ^11.11.0
- version: 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.15)(react@18.2.0)
+ version: 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.33)(react@18.2.0)
'@hookform/resolvers':
specifier: ^3.3.2
version: 3.3.2(react-hook-form@7.47.0)
@@ -170,99 +170,99 @@ importers:
version: 7.47.0(react@18.2.0)
react-router-dom:
specifier: ^6.17.0
- version: 6.17.0(react-dom@18.2.0)(react@18.2.0)
- ui:
+ version: 6.18.0(react-dom@18.2.0)(react@18.2.0)
+ shared:
specifier: workspace:*
- version: link:../../packages/ui
+ version: link:../../packages/shared
zod:
specifier: ^3.22.4
version: 3.22.4
zustand:
specifier: ^4.4.4
- version: 4.4.4(@types/react@18.2.15)(react@18.2.0)
+ version: 4.4.5(@types/react@18.2.33)(react@18.2.0)
devDependencies:
'@types/node':
specifier: ^20.8.10
version: 20.8.10
'@types/react':
specifier: ^18.2.15
- version: 18.2.15
+ version: 18.2.33
'@types/react-dom':
specifier: ^18.2.7
- version: 18.2.7
+ version: 18.2.14
'@vitejs/plugin-react-swc':
specifier: ^3.4.0
- version: 3.4.0(vite@4.4.5)
+ version: 3.4.1(vite@4.5.0)
eslint:
specifier: ^8.45.0
- version: 8.45.0
+ version: 8.52.0
eslint-config-custom:
specifier: workspace:*
version: link:../../packages/eslint-config-custom
+ msw:
+ specifier: ^2.0.1
+ version: 2.0.1(typescript@5.2.2)
tsconfig:
specifier: workspace:*
version: link:../../packages/tsconfig
typescript:
specifier: ^5.0.2
- version: 5.0.2
+ version: 5.2.2
vite:
specifier: ^4.4.5
- version: 4.4.5(@types/node@20.8.10)
+ version: 4.5.0(@types/node@20.8.10)
packages/eslint-config-custom:
dependencies:
'@typescript-eslint/eslint-plugin':
specifier: ^5.30.7
- version: 5.44.0(@typescript-eslint/parser@5.44.0)(eslint@8.45.0)(typescript@5.0.2)
+ version: 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.53.0)(typescript@5.2.2)
'@typescript-eslint/parser':
specifier: ^5.30.7
- version: 5.44.0(eslint@8.45.0)(typescript@5.0.2)
+ version: 5.62.0(eslint@8.53.0)(typescript@5.2.2)
eslint-config-prettier:
specifier: ^8.5.0
- version: 8.5.0(eslint@8.45.0)
+ version: 8.10.0(eslint@8.53.0)
eslint-plugin-import:
specifier: ^2.29.0
- version: 2.29.0(@typescript-eslint/parser@5.44.0)(eslint@8.45.0)
+ version: 2.29.0(@typescript-eslint/parser@5.62.0)(eslint@8.53.0)
eslint-plugin-prettier:
specifier: ^5.0.1
- version: 5.0.1(eslint-config-prettier@8.5.0)(eslint@8.45.0)(prettier@3.0.3)
+ version: 5.0.1(eslint-config-prettier@8.10.0)(eslint@8.53.0)(prettier@3.0.3)
eslint-plugin-react:
specifier: ^7.33.2
- version: 7.33.2(eslint@8.45.0)
+ version: 7.33.2(eslint@8.53.0)
eslint-plugin-react-hooks:
specifier: ^4.6.0
- version: 4.6.0(eslint@8.45.0)
+ version: 4.6.0(eslint@8.53.0)
eslint-plugin-react-refresh:
specifier: ^0.4.3
- version: 0.4.3(eslint@8.45.0)
+ version: 0.4.4(eslint@8.53.0)
eslint-plugin-simple-import-sort:
specifier: ^10.0.0
- version: 10.0.0(eslint@8.45.0)
+ version: 10.0.0(eslint@8.53.0)
eslint-plugin-unused-imports:
specifier: ^3.0.0
- version: 3.0.0(@typescript-eslint/eslint-plugin@5.44.0)(eslint@8.45.0)
- devDependencies:
- '@vitejs/plugin-react-swc':
- specifier: ^3.4.0
- version: 3.4.0(vite@4.4.5)
-
- packages/tsconfig:
+ version: 3.0.0(@typescript-eslint/eslint-plugin@5.62.0)(eslint@8.53.0)
devDependencies:
'@vitejs/plugin-react-swc':
specifier: ^3.4.0
- version: 3.4.0(vite@4.4.5)
+ version: 3.4.1(vite@4.5.0)
- packages/ui:
+ packages/shared:
dependencies:
'@chakra-ui/react':
specifier: ^2.8.1
- version: 2.8.1(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.15)(framer-motion@10.16.4)(react-dom@18.2.0)(react@18.2.0)
+ version: 2.8.1(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.33)(framer-motion@10.16.4)(react-dom@18.2.0)(react@18.2.0)
'@emotion/react':
specifier: ^11.11.1
- version: 11.11.1(@types/react@18.2.15)(react@18.2.0)
+ version: 11.11.1(@types/react@18.2.33)(react@18.2.0)
'@emotion/styled':
specifier: ^11.11.0
- version: 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.15)(react@18.2.0)
+ version: 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.33)(react@18.2.0)
+ axios:
+ specifier: ^1.6.0
+ version: 1.6.0
framer-motion:
specifier: ^10.16.4
version: 10.16.4(react-dom@18.2.0)(react@18.2.0)
@@ -272,19 +272,22 @@ importers:
react-dom:
specifier: ^18.2.0
version: 18.2.0(react@18.2.0)
+ react-router-dom:
+ specifier: ^6.17.0
+ version: 6.18.0(react-dom@18.2.0)(react@18.2.0)
+ zustand:
+ specifier: ^4.4.4
+ version: 4.4.5(@types/react@18.2.33)(react@18.2.0)
devDependencies:
'@types/react':
specifier: ^18.2.15
- version: 18.2.15
+ version: 18.2.33
'@types/react-dom':
specifier: ^18.2.7
- version: 18.2.7
- '@vitejs/plugin-react-swc':
- specifier: ^3.4.0
- version: 3.4.0(vite@4.4.5)
+ version: 18.2.14
eslint:
specifier: ^8.45.0
- version: 8.45.0
+ version: 8.53.0
eslint-config-custom:
specifier: workspace:*
version: link:../eslint-config-custom
@@ -293,7 +296,13 @@ importers:
version: link:../tsconfig
typescript:
specifier: ^5.0.2
- version: 5.0.2
+ version: 5.2.2
+
+ packages/tsconfig:
+ devDependencies:
+ '@vitejs/plugin-react-swc':
+ specifier: ^3.4.0
+ version: 3.4.1(vite@4.5.0)
packages:
@@ -301,12 +310,6 @@ packages:
resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==}
engines: {node: '>=0.10.0'}
- /@babel/code-frame@7.12.11:
- resolution: {integrity: sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==}
- dependencies:
- '@babel/highlight': 7.18.6
- dev: true
-
/@babel/code-frame@7.22.13:
resolution: {integrity: sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==}
engines: {node: '>=6.9.0'}
@@ -330,15 +333,6 @@ packages:
resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==}
engines: {node: '>=6.9.0'}
- /@babel/highlight@7.18.6:
- resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==}
- engines: {node: '>=6.9.0'}
- dependencies:
- '@babel/helper-validator-identifier': 7.22.20
- chalk: 2.4.2
- js-tokens: 4.0.0
- dev: true
-
/@babel/highlight@7.22.20:
resolution: {integrity: sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==}
engines: {node: '>=6.9.0'}
@@ -554,7 +548,7 @@ packages:
'@emotion/react': '>=10.0.35'
react: '>=18'
dependencies:
- '@emotion/react': 11.11.1(@types/react@18.2.15)(react@18.2.0)
+ '@emotion/react': 11.11.1(@types/react@18.2.33)(react@18.2.0)
react: 18.2.0
dev: false
@@ -595,14 +589,14 @@ packages:
resolution: {integrity: sha512-IGM/yGUHS+8TOQrZGpAKOJl/xGBrmRYJrmbHfUE7zrG3PpQyXvbLDP1M+RggkCFVgHlJi2wpYIf0QtQlU0XZfw==}
dev: false
- /@chakra-ui/focus-lock@2.1.0(@types/react@18.2.15)(react@18.2.0):
+ /@chakra-ui/focus-lock@2.1.0(@types/react@18.2.33)(react@18.2.0):
resolution: {integrity: sha512-EmGx4PhWGjm4dpjRqM4Aa+rCWBxP+Rq8Uc/nAVnD4YVqkEhBkrPTpui2lnjsuxqNaZ24fIAZ10cF1hlpemte/w==}
peerDependencies:
react: '>=18'
dependencies:
'@chakra-ui/dom-utils': 2.1.0
react: 18.2.0
- react-focus-lock: 2.9.6(@types/react@18.2.15)(react@18.2.0)
+ react-focus-lock: 2.9.6(@types/react@18.2.33)(react@18.2.0)
transitivePeerDependencies:
- '@types/react'
dev: false
@@ -740,7 +734,7 @@ packages:
react: 18.2.0
dev: false
- /@chakra-ui/modal@2.3.1(@chakra-ui/system@2.6.1)(@types/react@18.2.15)(framer-motion@10.16.4)(react-dom@18.2.0)(react@18.2.0):
+ /@chakra-ui/modal@2.3.1(@chakra-ui/system@2.6.1)(@types/react@18.2.33)(framer-motion@10.16.4)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-TQv1ZaiJMZN+rR9DK0snx/OPwmtaGH1HbZtlYt4W4s6CzyK541fxLRTjIXfEzIGpvNW+b6VFuFjbcR78p4DEoQ==}
peerDependencies:
'@chakra-ui/system': '>=2.0.0'
@@ -749,7 +743,7 @@ packages:
react-dom: '>=18'
dependencies:
'@chakra-ui/close-button': 2.1.1(@chakra-ui/system@2.6.1)(react@18.2.0)
- '@chakra-ui/focus-lock': 2.1.0(@types/react@18.2.15)(react@18.2.0)
+ '@chakra-ui/focus-lock': 2.1.0(@types/react@18.2.33)(react@18.2.0)
'@chakra-ui/portal': 2.1.0(react-dom@18.2.0)(react@18.2.0)
'@chakra-ui/react-context': 2.1.0(react@18.2.0)
'@chakra-ui/react-types': 2.0.7(react@18.2.0)
@@ -761,7 +755,7 @@ packages:
framer-motion: 10.16.4(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
- react-remove-scroll: 2.5.7(@types/react@18.2.15)(react@18.2.0)
+ react-remove-scroll: 2.5.7(@types/react@18.2.33)(react@18.2.0)
transitivePeerDependencies:
- '@types/react'
dev: false
@@ -882,8 +876,8 @@ packages:
'@chakra-ui/react-env': 3.1.0(react@18.2.0)
'@chakra-ui/system': 2.6.1(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0)
'@chakra-ui/utils': 2.0.15
- '@emotion/react': 11.11.1(@types/react@18.2.15)(react@18.2.0)
- '@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.15)(react@18.2.0)
+ '@emotion/react': 11.11.1(@types/react@18.2.33)(react@18.2.0)
+ '@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.33)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
@@ -1099,7 +1093,7 @@ packages:
react: 18.2.0
dev: false
- /@chakra-ui/react@2.8.1(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.15)(framer-motion@10.16.4)(react-dom@18.2.0)(react@18.2.0):
+ /@chakra-ui/react@2.8.1(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.33)(framer-motion@10.16.4)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-UL9Rtj4DovP3+oVbI06gsdfyJJb+wmS2RYnGNXjW9tsjCyXxjlBw9TAUj0jyOfWe0+zd/4juL8+J+QCwmdhptg==}
peerDependencies:
'@emotion/react': ^11.0.0
@@ -1120,7 +1114,7 @@ packages:
'@chakra-ui/counter': 2.1.0(react@18.2.0)
'@chakra-ui/css-reset': 2.3.0(@emotion/react@11.11.1)(react@18.2.0)
'@chakra-ui/editable': 3.1.0(@chakra-ui/system@2.6.1)(react@18.2.0)
- '@chakra-ui/focus-lock': 2.1.0(@types/react@18.2.15)(react@18.2.0)
+ '@chakra-ui/focus-lock': 2.1.0(@types/react@18.2.33)(react@18.2.0)
'@chakra-ui/form-control': 2.1.1(@chakra-ui/system@2.6.1)(react@18.2.0)
'@chakra-ui/hooks': 2.2.1(react@18.2.0)
'@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.1)(react@18.2.0)
@@ -1130,7 +1124,7 @@ packages:
'@chakra-ui/live-region': 2.1.0(react@18.2.0)
'@chakra-ui/media-query': 3.3.0(@chakra-ui/system@2.6.1)(react@18.2.0)
'@chakra-ui/menu': 2.2.1(@chakra-ui/system@2.6.1)(framer-motion@10.16.4)(react@18.2.0)
- '@chakra-ui/modal': 2.3.1(@chakra-ui/system@2.6.1)(@types/react@18.2.15)(framer-motion@10.16.4)(react-dom@18.2.0)(react@18.2.0)
+ '@chakra-ui/modal': 2.3.1(@chakra-ui/system@2.6.1)(@types/react@18.2.33)(framer-motion@10.16.4)(react-dom@18.2.0)(react@18.2.0)
'@chakra-ui/number-input': 2.1.1(@chakra-ui/system@2.6.1)(react@18.2.0)
'@chakra-ui/pin-input': 2.1.0(@chakra-ui/system@2.6.1)(react@18.2.0)
'@chakra-ui/popover': 2.2.1(@chakra-ui/system@2.6.1)(framer-motion@10.16.4)(react@18.2.0)
@@ -1161,8 +1155,8 @@ packages:
'@chakra-ui/transition': 2.1.0(framer-motion@10.16.4)(react@18.2.0)
'@chakra-ui/utils': 2.0.15
'@chakra-ui/visually-hidden': 2.2.0(@chakra-ui/system@2.6.1)(react@18.2.0)
- '@emotion/react': 11.11.1(@types/react@18.2.15)(react@18.2.0)
- '@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.15)(react@18.2.0)
+ '@emotion/react': 11.11.1(@types/react@18.2.33)(react@18.2.0)
+ '@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.33)(react@18.2.0)
framer-motion: 10.16.4(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
@@ -1301,8 +1295,8 @@ packages:
'@chakra-ui/styled-system': 2.9.1
'@chakra-ui/theme-utils': 2.0.20
'@chakra-ui/utils': 2.0.15
- '@emotion/react': 11.11.1(@types/react@18.2.15)(react@18.2.0)
- '@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.15)(react@18.2.0)
+ '@emotion/react': 11.11.1(@types/react@18.2.33)(react@18.2.0)
+ '@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.33)(react@18.2.0)
react: 18.2.0
react-fast-compare: 3.2.2
dev: false
@@ -1468,14 +1462,14 @@ packages:
react: 18.2.0
dev: false
- /@commitlint/cli@18.2.0(typescript@5.0.2):
+ /@commitlint/cli@18.2.0(typescript@5.2.2):
resolution: {integrity: sha512-F/DCG791kMFmWg5eIdogakuGeg4OiI2kD430ed1a1Hh3epvrJdeIAgcGADAMIOmF+m0S1+VlIYUKG2dvQQ1Izw==}
engines: {node: '>=v18'}
hasBin: true
dependencies:
'@commitlint/format': 18.1.0
'@commitlint/lint': 18.1.0
- '@commitlint/load': 18.2.0(typescript@5.0.2)
+ '@commitlint/load': 18.2.0(typescript@5.2.2)
'@commitlint/read': 18.1.0
'@commitlint/types': 18.1.0
execa: 5.1.1
@@ -1499,7 +1493,7 @@ packages:
engines: {node: '>=v18'}
dependencies:
'@commitlint/types': 18.1.0
- ajv: 8.11.2
+ ajv: 8.12.0
dev: true
/@commitlint/ensure@18.1.0:
@@ -1545,7 +1539,7 @@ packages:
'@commitlint/types': 18.1.0
dev: true
- /@commitlint/load@18.2.0(typescript@5.0.2):
+ /@commitlint/load@18.2.0(typescript@5.2.2):
resolution: {integrity: sha512-xjX3d3CRlOALwImhOsmLYZh14/+gW/KxsY7+bPKrzmGuFailf9K7ckhB071oYZVJdACnpY4hDYiosFyOC+MpAA==}
engines: {node: '>=v18'}
dependencies:
@@ -1553,10 +1547,10 @@ packages:
'@commitlint/execute-rule': 18.1.0
'@commitlint/resolve-extends': 18.1.0
'@commitlint/types': 18.1.0
- '@types/node': 18.18.7
+ '@types/node': 18.18.8
chalk: 4.1.2
- cosmiconfig: 8.3.6(typescript@5.0.2)
- cosmiconfig-typescript-loader: 5.0.0(@types/node@18.18.7)(cosmiconfig@8.3.6)(typescript@5.0.2)
+ cosmiconfig: 8.3.6(typescript@5.2.2)
+ cosmiconfig-typescript-loader: 5.0.0(@types/node@18.18.8)(cosmiconfig@8.3.6)(typescript@5.2.2)
lodash.isplainobject: 4.0.6
lodash.merge: 4.6.2
lodash.uniq: 4.5.0
@@ -1686,7 +1680,7 @@ packages:
resolution: {integrity: sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==}
dev: false
- /@emotion/react@11.11.1(@types/react@18.2.15)(react@18.2.0):
+ /@emotion/react@11.11.1(@types/react@18.2.33)(react@18.2.0):
resolution: {integrity: sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA==}
peerDependencies:
'@types/react': '*'
@@ -1702,7 +1696,7 @@ packages:
'@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0)
'@emotion/utils': 1.2.1
'@emotion/weak-memoize': 0.3.1
- '@types/react': 18.2.15
+ '@types/react': 18.2.33
hoist-non-react-statics: 3.3.2
react: 18.2.0
dev: false
@@ -1721,7 +1715,7 @@ packages:
resolution: {integrity: sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==}
dev: false
- /@emotion/styled@11.11.0(@emotion/react@11.11.1)(@types/react@18.2.15)(react@18.2.0):
+ /@emotion/styled@11.11.0(@emotion/react@11.11.1)(@types/react@18.2.33)(react@18.2.0):
resolution: {integrity: sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==}
peerDependencies:
'@emotion/react': ^11.0.0-rc.0
@@ -1734,11 +1728,11 @@ packages:
'@babel/runtime': 7.23.2
'@emotion/babel-plugin': 11.11.0
'@emotion/is-prop-valid': 1.2.1
- '@emotion/react': 11.11.1(@types/react@18.2.15)(react@18.2.0)
+ '@emotion/react': 11.11.1(@types/react@18.2.33)(react@18.2.0)
'@emotion/serialize': 1.1.2
'@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0)
'@emotion/utils': 1.2.1
- '@types/react': 18.2.15
+ '@types/react': 18.2.33
react: 18.2.0
dev: false
@@ -1960,45 +1954,55 @@ packages:
dev: true
optional: true
- /@eslint-community/eslint-utils@4.4.0(eslint@8.45.0):
+ /@eslint-community/eslint-utils@4.4.0(eslint@8.52.0):
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
dependencies:
- eslint: 8.45.0
+ eslint: 8.52.0
+ eslint-visitor-keys: 3.4.3
+ dev: true
+
+ /@eslint-community/eslint-utils@4.4.0(eslint@8.53.0):
+ resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+ dependencies:
+ eslint: 8.53.0
eslint-visitor-keys: 3.4.3
/@eslint-community/regexpp@4.10.0:
resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==}
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
- /@eslint/eslintrc@0.4.3:
- resolution: {integrity: sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==}
- engines: {node: ^10.12.0 || >=12.0.0}
+ /@eslint/eslintrc@2.1.2:
+ resolution: {integrity: sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
ajv: 6.12.6
debug: 4.3.4
- espree: 7.3.1
+ espree: 9.6.1
globals: 13.23.0
- ignore: 4.0.6
+ ignore: 5.2.4
import-fresh: 3.3.0
- js-yaml: 3.14.1
+ js-yaml: 4.1.0
minimatch: 3.1.2
strip-json-comments: 3.1.1
transitivePeerDependencies:
- supports-color
dev: true
- /@eslint/eslintrc@2.1.2:
- resolution: {integrity: sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==}
+ /@eslint/eslintrc@2.1.3:
+ resolution: {integrity: sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
ajv: 6.12.6
debug: 4.3.4
espree: 9.6.1
globals: 13.23.0
- ignore: 5.2.0
+ ignore: 5.2.4
import-fresh: 3.3.0
js-yaml: 4.1.0
minimatch: 3.1.2
@@ -2006,8 +2010,13 @@ packages:
transitivePeerDependencies:
- supports-color
- /@eslint/js@8.44.0:
- resolution: {integrity: sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==}
+ /@eslint/js@8.52.0:
+ resolution: {integrity: sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ dev: true
+
+ /@eslint/js@8.53.0:
+ resolution: {integrity: sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
/@hookform/resolvers@3.3.2(react-hook-form@7.47.0):
@@ -2028,25 +2037,10 @@ packages:
transitivePeerDependencies:
- supports-color
- /@humanwhocodes/config-array@0.5.0:
- resolution: {integrity: sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==}
- engines: {node: '>=10.10.0'}
- dependencies:
- '@humanwhocodes/object-schema': 1.2.1
- debug: 4.3.4
- minimatch: 3.1.2
- transitivePeerDependencies:
- - supports-color
- dev: true
-
/@humanwhocodes/module-importer@1.0.1:
resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
engines: {node: '>=12.22'}
- /@humanwhocodes/object-schema@1.2.1:
- resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
- dev: true
-
/@humanwhocodes/object-schema@2.0.1:
resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==}
@@ -2094,7 +2088,7 @@ packages:
engines: {node: '>= 8'}
dependencies:
'@nodelib/fs.scandir': 2.1.5
- fastq: 1.13.0
+ fastq: 1.15.0
/@open-draft/deferred-promise@2.2.0:
resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==}
@@ -2162,8 +2156,8 @@ packages:
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
dev: false
- /@remix-run/router@1.10.0:
- resolution: {integrity: sha512-Lm+fYpMfZoEucJ7cMxgt4dYt8jLfbpwRCzAjm9UgSLOkmlqo9gupxt6YX3DY0Fk155NT9l17d/ydi+964uS9Lw==}
+ /@remix-run/router@1.11.0:
+ resolution: {integrity: sha512-BHdhcWgeiudl91HvVa2wxqZjSHbheSgIiDvxrF1VjFzBzpTtuDPkOdOi3Iqvc08kXtFkLjhbS+ML9aM8mJS+wQ==}
engines: {node: '>=14.0.0'}
dev: false
@@ -2336,8 +2330,8 @@ packages:
resolution: {integrity: sha512-/NCbMABw2uacuyE16Iwka1EzREDD50/W2ggRBad0y1WHBvAkvR9OEINxModVY7D428gXBe0igeVX7bUc9GaslQ==}
dev: true
- /@types/json-schema@7.0.11:
- resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
+ /@types/json-schema@7.0.14:
+ resolution: {integrity: sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==}
dev: false
/@types/json5@0.0.29:
@@ -2358,8 +2352,8 @@ packages:
resolution: {integrity: sha512-Kfe/D3hxHTusnPNRbycJE1N77WHDsdS4AjUYIzlDzhDrS47NrwuL3YW4VITxwR7KCVpzwgy4Rbj829KSSQmwXQ==}
dev: true
- /@types/node@18.18.7:
- resolution: {integrity: sha512-bw+lEsxis6eqJYW8Ql6+yTqkE6RuFtsQPSe5JxXbqYRFQEER5aJA9a5UH9igqDWm3X4iLHIKOHlnAXLM4mi7uQ==}
+ /@types/node@18.18.8:
+ resolution: {integrity: sha512-OLGBaaK5V3VRBS1bAkMVP2/W9B+H8meUfl866OrMNQqt7wDgdpWPp5o6gmIc9pB+lIQHSq4ZL8ypeH1vPxcPaQ==}
dependencies:
undici-types: 5.26.5
dev: true
@@ -2386,14 +2380,14 @@ packages:
ts-toolbelt: 6.15.5
dev: true
- /@types/react-dom@18.2.7:
- resolution: {integrity: sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==}
+ /@types/react-dom@18.2.14:
+ resolution: {integrity: sha512-V835xgdSVmyQmI1KLV2BEIUgqEuinxp9O4G6g3FqO/SqLac049E53aysv0oEFD2kHfejeKU+ZqL2bcFWj9gLAQ==}
dependencies:
- '@types/react': 18.2.15
+ '@types/react': 18.2.33
dev: true
- /@types/react@18.2.15:
- resolution: {integrity: sha512-oEjE7TQt1fFTFSbf8kkNuc798ahTUzn3Le67/PWjE8MAfYAD/qB7O8hSTcromLFqHCt9bcdOg5GXMokzTjJ5SA==}
+ /@types/react@18.2.33:
+ resolution: {integrity: sha512-v+I7S+hu3PIBoVkKGpSYYpiBT1ijqEzWpzQD62/jm4K74hPpSP7FF9BnKG6+fg2+62weJYkkBWDJlZt5JO/9hg==}
dependencies:
'@types/prop-types': 15.7.9
'@types/scheduler': 0.16.5
@@ -2402,16 +2396,16 @@ packages:
/@types/scheduler@0.16.5:
resolution: {integrity: sha512-s/FPdYRmZR8SjLWGMCuax7r3qCWQw9QKHzXVukAuuIJkXkDRwp+Pu5LMIVFi0Fxbav35WURicYr8u1QsoybnQw==}
- /@types/semver@7.3.13:
- resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==}
+ /@types/semver@7.5.4:
+ resolution: {integrity: sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==}
dev: false
/@types/statuses@2.0.3:
resolution: {integrity: sha512-NwCYScf83RIwCyi5/9cXocrJB//xrqMh5PMw3mYTSFGaI3DuVjBLfO/PCk7QVAC3Da8b9NjxNmTO9Aj9T3rl/Q==}
dev: true
- /@typescript-eslint/eslint-plugin@5.44.0(@typescript-eslint/parser@5.44.0)(eslint@8.45.0)(typescript@5.0.2):
- resolution: {integrity: sha512-j5ULd7FmmekcyWeArx+i8x7sdRHzAtXTkmDPthE4amxZOWKFK7bomoJ4r7PJ8K7PoMzD16U8MmuZFAonr1ERvw==}
+ /@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.53.0)(typescript@5.2.2):
+ resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
'@typescript-eslint/parser': ^5.0.0
@@ -2421,24 +2415,25 @@ packages:
typescript:
optional: true
dependencies:
- '@typescript-eslint/parser': 5.44.0(eslint@8.45.0)(typescript@5.0.2)
- '@typescript-eslint/scope-manager': 5.44.0
- '@typescript-eslint/type-utils': 5.44.0(eslint@8.45.0)(typescript@5.0.2)
- '@typescript-eslint/utils': 5.44.0(eslint@8.45.0)(typescript@5.0.2)
+ '@eslint-community/regexpp': 4.10.0
+ '@typescript-eslint/parser': 5.62.0(eslint@8.53.0)(typescript@5.2.2)
+ '@typescript-eslint/scope-manager': 5.62.0
+ '@typescript-eslint/type-utils': 5.62.0(eslint@8.53.0)(typescript@5.2.2)
+ '@typescript-eslint/utils': 5.62.0(eslint@8.53.0)(typescript@5.2.2)
debug: 4.3.4
- eslint: 8.45.0
- ignore: 5.2.0
+ eslint: 8.53.0
+ graphemer: 1.4.0
+ ignore: 5.2.4
natural-compare-lite: 1.4.0
- regexpp: 3.2.0
- semver: 7.3.8
- tsutils: 3.21.0(typescript@5.0.2)
- typescript: 5.0.2
+ semver: 7.5.4
+ tsutils: 3.21.0(typescript@5.2.2)
+ typescript: 5.2.2
transitivePeerDependencies:
- supports-color
dev: false
- /@typescript-eslint/parser@5.44.0(eslint@8.45.0)(typescript@5.0.2):
- resolution: {integrity: sha512-H7LCqbZnKqkkgQHaKLGC6KUjt3pjJDx8ETDqmwncyb6PuoigYajyAwBGz08VU/l86dZWZgI4zm5k2VaKqayYyA==}
+ /@typescript-eslint/parser@5.62.0(eslint@8.53.0)(typescript@5.2.2):
+ resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
@@ -2447,26 +2442,26 @@ packages:
typescript:
optional: true
dependencies:
- '@typescript-eslint/scope-manager': 5.44.0
- '@typescript-eslint/types': 5.44.0
- '@typescript-eslint/typescript-estree': 5.44.0(typescript@5.0.2)
+ '@typescript-eslint/scope-manager': 5.62.0
+ '@typescript-eslint/types': 5.62.0
+ '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.2.2)
debug: 4.3.4
- eslint: 8.45.0
- typescript: 5.0.2
+ eslint: 8.53.0
+ typescript: 5.2.2
transitivePeerDependencies:
- supports-color
dev: false
- /@typescript-eslint/scope-manager@5.44.0:
- resolution: {integrity: sha512-2pKml57KusI0LAhgLKae9kwWeITZ7IsZs77YxyNyIVOwQ1kToyXRaJLl+uDEXzMN5hnobKUOo2gKntK9H1YL8g==}
+ /@typescript-eslint/scope-manager@5.62.0:
+ resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
- '@typescript-eslint/types': 5.44.0
- '@typescript-eslint/visitor-keys': 5.44.0
+ '@typescript-eslint/types': 5.62.0
+ '@typescript-eslint/visitor-keys': 5.62.0
dev: false
- /@typescript-eslint/type-utils@5.44.0(eslint@8.45.0)(typescript@5.0.2):
- resolution: {integrity: sha512-A1u0Yo5wZxkXPQ7/noGkRhV4J9opcymcr31XQtOzcc5nO/IHN2E2TPMECKWYpM3e6olWEM63fq/BaL1wEYnt/w==}
+ /@typescript-eslint/type-utils@5.62.0(eslint@8.53.0)(typescript@5.2.2):
+ resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: '*'
@@ -2475,23 +2470,23 @@ packages:
typescript:
optional: true
dependencies:
- '@typescript-eslint/typescript-estree': 5.44.0(typescript@5.0.2)
- '@typescript-eslint/utils': 5.44.0(eslint@8.45.0)(typescript@5.0.2)
+ '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.2.2)
+ '@typescript-eslint/utils': 5.62.0(eslint@8.53.0)(typescript@5.2.2)
debug: 4.3.4
- eslint: 8.45.0
- tsutils: 3.21.0(typescript@5.0.2)
- typescript: 5.0.2
+ eslint: 8.53.0
+ tsutils: 3.21.0(typescript@5.2.2)
+ typescript: 5.2.2
transitivePeerDependencies:
- supports-color
dev: false
- /@typescript-eslint/types@5.44.0:
- resolution: {integrity: sha512-Tp+zDnHmGk4qKR1l+Y1rBvpjpm5tGXX339eAlRBDg+kgZkz9Bw+pqi4dyseOZMsGuSH69fYfPJCBKBrbPCxYFQ==}
+ /@typescript-eslint/types@5.62.0:
+ resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: false
- /@typescript-eslint/typescript-estree@5.44.0(typescript@5.0.2):
- resolution: {integrity: sha512-M6Jr+RM7M5zeRj2maSfsZK2660HKAJawv4Ud0xT+yauyvgrsHu276VtXlKDFnEmhG+nVEd0fYZNXGoAgxwDWJw==}
+ /@typescript-eslint/typescript-estree@5.62.0(typescript@5.2.2):
+ resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
typescript: '*'
@@ -2499,53 +2494,56 @@ packages:
typescript:
optional: true
dependencies:
- '@typescript-eslint/types': 5.44.0
- '@typescript-eslint/visitor-keys': 5.44.0
+ '@typescript-eslint/types': 5.62.0
+ '@typescript-eslint/visitor-keys': 5.62.0
debug: 4.3.4
globby: 11.1.0
is-glob: 4.0.3
semver: 7.5.4
- tsutils: 3.21.0(typescript@5.0.2)
- typescript: 5.0.2
+ tsutils: 3.21.0(typescript@5.2.2)
+ typescript: 5.2.2
transitivePeerDependencies:
- supports-color
dev: false
- /@typescript-eslint/utils@5.44.0(eslint@8.45.0)(typescript@5.0.2):
- resolution: {integrity: sha512-fMzA8LLQ189gaBjS0MZszw5HBdZgVwxVFShCO3QN+ws3GlPkcy9YuS3U4wkT6su0w+Byjq3mS3uamy9HE4Yfjw==}
+ /@typescript-eslint/utils@5.62.0(eslint@8.53.0)(typescript@5.2.2):
+ resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
dependencies:
- '@types/json-schema': 7.0.11
- '@types/semver': 7.3.13
- '@typescript-eslint/scope-manager': 5.44.0
- '@typescript-eslint/types': 5.44.0
- '@typescript-eslint/typescript-estree': 5.44.0(typescript@5.0.2)
- eslint: 8.45.0
+ '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0)
+ '@types/json-schema': 7.0.14
+ '@types/semver': 7.5.4
+ '@typescript-eslint/scope-manager': 5.62.0
+ '@typescript-eslint/types': 5.62.0
+ '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.2.2)
+ eslint: 8.53.0
eslint-scope: 5.1.1
- eslint-utils: 3.0.0(eslint@8.45.0)
semver: 7.5.4
transitivePeerDependencies:
- supports-color
- typescript
dev: false
- /@typescript-eslint/visitor-keys@5.44.0:
- resolution: {integrity: sha512-a48tLG8/4m62gPFbJ27FxwCOqPKxsb8KC3HkmYoq2As/4YyjQl1jDbRr1s63+g4FS/iIehjmN3L5UjmKva1HzQ==}
+ /@typescript-eslint/visitor-keys@5.62.0:
+ resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
- '@typescript-eslint/types': 5.44.0
+ '@typescript-eslint/types': 5.62.0
eslint-visitor-keys: 3.4.3
dev: false
- /@vitejs/plugin-react-swc@3.4.0(vite@4.4.5):
- resolution: {integrity: sha512-m7UaA4Uvz82N/0EOVpZL4XsFIakRqrFKeSNxa1FBLSXGvWrWRBwmZb4qxk+ZIVAZcW3c3dn5YosomDgx62XWcQ==}
+ /@ungap/structured-clone@1.2.0:
+ resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
+
+ /@vitejs/plugin-react-swc@3.4.1(vite@4.5.0):
+ resolution: {integrity: sha512-7YQOQcVV5x1luD8nkbCDdyYygFvn1hjqJk68UvNAzY2QG4o4N5EwAhLLFNOcd1HrdMwDl0VElP8VutoWf9IvJg==}
peerDependencies:
vite: ^4
dependencies:
'@swc/core': 1.3.95
- vite: 4.4.5(@types/node@20.8.10)
+ vite: 4.5.0(@types/node@20.8.10)
transitivePeerDependencies:
- '@swc/helpers'
dev: true
@@ -2572,29 +2570,15 @@ packages:
through: 2.3.8
dev: true
- /acorn-jsx@5.3.2(acorn@7.4.1):
+ /acorn-jsx@5.3.2(acorn@8.11.2):
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
dependencies:
- acorn: 7.4.1
- dev: true
+ acorn: 8.11.2
- /acorn-jsx@5.3.2(acorn@8.10.0):
- resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
- peerDependencies:
- acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
- dependencies:
- acorn: 8.10.0
-
- /acorn@7.4.1:
- resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==}
- engines: {node: '>=0.4.0'}
- hasBin: true
- dev: true
-
- /acorn@8.10.0:
- resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==}
+ /acorn@8.11.2:
+ resolution: {integrity: sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==}
engines: {node: '>=0.4.0'}
hasBin: true
@@ -2606,8 +2590,8 @@ packages:
json-schema-traverse: 0.4.1
uri-js: 4.4.1
- /ajv@8.11.2:
- resolution: {integrity: sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==}
+ /ajv@8.12.0:
+ resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==}
dependencies:
fast-deep-equal: 3.1.3
json-schema-traverse: 1.0.0
@@ -2615,11 +2599,6 @@ packages:
uri-js: 4.4.1
dev: true
- /ansi-colors@4.1.3:
- resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
- engines: {node: '>=6'}
- dev: true
-
/ansi-escapes@3.2.0:
resolution: {integrity: sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==}
engines: {node: '>=4'}
@@ -2683,12 +2662,6 @@ packages:
picomatch: 2.3.1
dev: true
- /argparse@1.0.10:
- resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
- dependencies:
- sprintf-js: 1.0.3
- dev: true
-
/argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
@@ -2785,11 +2758,6 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
- /astral-regex@2.0.0:
- resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==}
- engines: {node: '>=8'}
- dev: true
-
/asynciterator.prototype@1.0.0:
resolution: {integrity: sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==}
dependencies:
@@ -2954,7 +2922,7 @@ packages:
normalize-path: 3.0.0
readdirp: 3.6.0
optionalDependencies:
- fsevents: 2.3.2
+ fsevents: 2.3.3
dev: true
/cli-cursor@2.1.0:
@@ -3059,13 +3027,13 @@ packages:
engines: {node: '>=16'}
dev: true
- /commitizen@4.3.0(typescript@5.0.2):
+ /commitizen@4.3.0(typescript@5.2.2):
resolution: {integrity: sha512-H0iNtClNEhT0fotHvGV3E9tDejDeS04sN1veIebsKYGMuGscFaswRoYJKmT3eW85eIJAs0F28bG2+a/9wCOfPw==}
engines: {node: '>= 12'}
hasBin: true
dependencies:
cachedir: 2.3.0
- cz-conventional-changelog: 3.3.0(typescript@5.0.2)
+ cz-conventional-changelog: 3.3.0(typescript@5.2.2)
dedent: 0.7.0
detect-indent: 6.1.0
find-node-modules: 2.1.3
@@ -3140,7 +3108,7 @@ packages:
toggle-selection: 1.0.6
dev: false
- /cosmiconfig-typescript-loader@5.0.0(@types/node@18.18.7)(cosmiconfig@8.3.6)(typescript@5.0.2):
+ /cosmiconfig-typescript-loader@5.0.0(@types/node@18.18.8)(cosmiconfig@8.3.6)(typescript@5.2.2):
resolution: {integrity: sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==}
engines: {node: '>=v16'}
peerDependencies:
@@ -3148,10 +3116,10 @@ packages:
cosmiconfig: '>=8.2'
typescript: '>=4'
dependencies:
- '@types/node': 18.18.7
- cosmiconfig: 8.3.6(typescript@5.0.2)
- jiti: 1.20.0
- typescript: 5.0.2
+ '@types/node': 18.18.8
+ cosmiconfig: 8.3.6(typescript@5.2.2)
+ jiti: 1.21.0
+ typescript: 5.2.2
dev: true
/cosmiconfig@7.1.0:
@@ -3164,7 +3132,7 @@ packages:
path-type: 4.0.0
yaml: 1.10.2
- /cosmiconfig@8.3.6(typescript@5.0.2):
+ /cosmiconfig@8.3.6(typescript@5.2.2):
resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==}
engines: {node: '>=14'}
peerDependencies:
@@ -3177,7 +3145,7 @@ packages:
js-yaml: 4.1.0
parse-json: 5.2.0
path-type: 4.0.0
- typescript: 5.0.2
+ typescript: 5.2.2
dev: true
/cross-spawn@7.0.3:
@@ -3197,18 +3165,18 @@ packages:
/csstype@3.1.2:
resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==}
- /cz-conventional-changelog@3.3.0(typescript@5.0.2):
+ /cz-conventional-changelog@3.3.0(typescript@5.2.2):
resolution: {integrity: sha512-U466fIzU5U22eES5lTNiNbZ+d8dfcHcssH4o7QsdWaCcRs/feIPCxKYSWkYBNs5mny7MvEfwpTLWjvbm94hecw==}
engines: {node: '>= 10'}
dependencies:
chalk: 2.4.2
- commitizen: 4.3.0(typescript@5.0.2)
+ commitizen: 4.3.0(typescript@5.2.2)
conventional-commit-types: 3.0.0
lodash.map: 4.6.0
longest: 2.0.1
- word-wrap: 1.2.3
+ word-wrap: 1.2.5
optionalDependencies:
- '@commitlint/load': 18.2.0(typescript@5.0.2)
+ '@commitlint/load': 18.2.0(typescript@5.2.2)
transitivePeerDependencies:
- typescript
dev: true
@@ -3222,7 +3190,7 @@ packages:
inquirer: 6.5.2
lodash: 4.17.21
temp: 0.9.4
- word-wrap: 1.2.3
+ word-wrap: 1.2.5
dev: true
/dargs@7.0.0:
@@ -3238,7 +3206,7 @@ packages:
supports-color:
optional: true
dependencies:
- ms: 2.1.2
+ ms: 2.1.3
dev: false
/debug@4.3.4:
@@ -3381,13 +3349,6 @@ packages:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
dev: true
- /enquirer@2.3.6:
- resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==}
- engines: {node: '>=8.6'}
- dependencies:
- ansi-colors: 4.1.3
- dev: true
-
/error-ex@1.3.2:
resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
dependencies:
@@ -3446,7 +3407,7 @@ packages:
define-properties: 1.2.1
es-abstract: 1.22.3
es-set-tostringtag: 2.0.2
- function-bind: 1.1.1
+ function-bind: 1.1.2
get-intrinsic: 1.2.2
globalthis: 1.0.3
has-property-descriptors: 1.0.1
@@ -3524,13 +3485,13 @@ packages:
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
engines: {node: '>=10'}
- /eslint-config-prettier@8.5.0(eslint@8.45.0):
- resolution: {integrity: sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==}
+ /eslint-config-prettier@8.10.0(eslint@8.53.0):
+ resolution: {integrity: sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==}
hasBin: true
peerDependencies:
eslint: '>=7.0.0'
dependencies:
- eslint: 8.45.0
+ eslint: 8.53.0
dev: false
/eslint-import-resolver-node@0.3.9:
@@ -3543,7 +3504,7 @@ packages:
- supports-color
dev: false
- /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.44.0)(eslint-import-resolver-node@0.3.9)(eslint@8.45.0):
+ /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint@8.53.0):
resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==}
engines: {node: '>=4'}
peerDependencies:
@@ -3564,15 +3525,15 @@ packages:
eslint-import-resolver-webpack:
optional: true
dependencies:
- '@typescript-eslint/parser': 5.44.0(eslint@8.45.0)(typescript@5.0.2)
+ '@typescript-eslint/parser': 5.62.0(eslint@8.53.0)(typescript@5.2.2)
debug: 3.2.7
- eslint: 8.45.0
+ eslint: 8.53.0
eslint-import-resolver-node: 0.3.9
transitivePeerDependencies:
- supports-color
dev: false
- /eslint-plugin-import@2.29.0(@typescript-eslint/parser@5.44.0)(eslint@8.45.0):
+ /eslint-plugin-import@2.29.0(@typescript-eslint/parser@5.62.0)(eslint@8.53.0):
resolution: {integrity: sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==}
engines: {node: '>=4'}
peerDependencies:
@@ -3582,16 +3543,16 @@ packages:
'@typescript-eslint/parser':
optional: true
dependencies:
- '@typescript-eslint/parser': 5.44.0(eslint@8.45.0)(typescript@5.0.2)
+ '@typescript-eslint/parser': 5.62.0(eslint@8.53.0)(typescript@5.2.2)
array-includes: 3.1.7
array.prototype.findlastindex: 1.2.3
array.prototype.flat: 1.3.2
array.prototype.flatmap: 1.3.2
debug: 3.2.7
doctrine: 2.1.0
- eslint: 8.45.0
+ eslint: 8.53.0
eslint-import-resolver-node: 0.3.9
- eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.44.0)(eslint-import-resolver-node@0.3.9)(eslint@8.45.0)
+ eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint@8.53.0)
hasown: 2.0.0
is-core-module: 2.13.1
is-glob: 4.0.3
@@ -3607,7 +3568,7 @@ packages:
- supports-color
dev: false
- /eslint-plugin-prettier@5.0.1(eslint-config-prettier@8.5.0)(eslint@8.45.0)(prettier@3.0.3):
+ /eslint-plugin-prettier@5.0.1(eslint-config-prettier@8.10.0)(eslint@8.53.0)(prettier@3.0.3):
resolution: {integrity: sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==}
engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies:
@@ -3621,31 +3582,31 @@ packages:
eslint-config-prettier:
optional: true
dependencies:
- eslint: 8.45.0
- eslint-config-prettier: 8.5.0(eslint@8.45.0)
+ eslint: 8.53.0
+ eslint-config-prettier: 8.10.0(eslint@8.53.0)
prettier: 3.0.3
prettier-linter-helpers: 1.0.0
synckit: 0.8.5
dev: false
- /eslint-plugin-react-hooks@4.6.0(eslint@8.45.0):
+ /eslint-plugin-react-hooks@4.6.0(eslint@8.53.0):
resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==}
engines: {node: '>=10'}
peerDependencies:
eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0
dependencies:
- eslint: 8.45.0
+ eslint: 8.53.0
dev: false
- /eslint-plugin-react-refresh@0.4.3(eslint@8.45.0):
- resolution: {integrity: sha512-Hh0wv8bUNY877+sI0BlCUlsS0TYYQqvzEwJsJJPM2WF4RnTStSnSR3zdJYa2nPOJgg3UghXi54lVyMSmpCalzA==}
+ /eslint-plugin-react-refresh@0.4.4(eslint@8.53.0):
+ resolution: {integrity: sha512-eD83+65e8YPVg6603Om2iCIwcQJf/y7++MWm4tACtEswFLYMwxwVWAfwN+e19f5Ad/FOyyNg9Dfi5lXhH3Y3rA==}
peerDependencies:
eslint: '>=7'
dependencies:
- eslint: 8.45.0
+ eslint: 8.53.0
dev: false
- /eslint-plugin-react@7.33.2(eslint@8.45.0):
+ /eslint-plugin-react@7.33.2(eslint@8.53.0):
resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==}
engines: {node: '>=4'}
peerDependencies:
@@ -3656,7 +3617,7 @@ packages:
array.prototype.tosorted: 1.1.2
doctrine: 2.1.0
es-iterator-helpers: 1.0.15
- eslint: 8.45.0
+ eslint: 8.53.0
estraverse: 5.3.0
jsx-ast-utils: 3.3.5
minimatch: 3.1.2
@@ -3670,15 +3631,15 @@ packages:
string.prototype.matchall: 4.0.10
dev: false
- /eslint-plugin-simple-import-sort@10.0.0(eslint@8.45.0):
+ /eslint-plugin-simple-import-sort@10.0.0(eslint@8.53.0):
resolution: {integrity: sha512-AeTvO9UCMSNzIHRkg8S6c3RPy5YEwKWSQPx3DYghLedo2ZQxowPFLGDN1AZ2evfg6r6mjBSZSLxLFsWSu3acsw==}
peerDependencies:
eslint: '>=5.0.0'
dependencies:
- eslint: 8.45.0
+ eslint: 8.53.0
dev: false
- /eslint-plugin-unused-imports@3.0.0(@typescript-eslint/eslint-plugin@5.44.0)(eslint@8.45.0):
+ /eslint-plugin-unused-imports@3.0.0(@typescript-eslint/eslint-plugin@5.62.0)(eslint@8.53.0):
resolution: {integrity: sha512-sduiswLJfZHeeBJ+MQaG+xYzSWdRXoSw61DpU13mzWumCkR0ufD0HmO4kdNokjrkluMHpj/7PJeN35pgbhW3kw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -3688,8 +3649,8 @@ packages:
'@typescript-eslint/eslint-plugin':
optional: true
dependencies:
- '@typescript-eslint/eslint-plugin': 5.44.0(@typescript-eslint/parser@5.44.0)(eslint@8.45.0)(typescript@5.0.2)
- eslint: 8.45.0
+ '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.53.0)(typescript@5.2.2)
+ eslint: 8.53.0
eslint-rule-composer: 0.3.0
dev: false
@@ -3704,6 +3665,7 @@ packages:
dependencies:
esrecurse: 4.3.0
estraverse: 4.3.0
+ dev: false
/eslint-scope@7.2.2:
resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
@@ -3712,96 +3674,70 @@ packages:
esrecurse: 4.3.0
estraverse: 5.3.0
- /eslint-utils@2.1.0:
- resolution: {integrity: sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==}
- engines: {node: '>=6'}
- dependencies:
- eslint-visitor-keys: 1.3.0
- dev: true
-
- /eslint-utils@3.0.0(eslint@8.45.0):
- resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==}
- engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0}
- peerDependencies:
- eslint: '>=5'
- dependencies:
- eslint: 8.45.0
- eslint-visitor-keys: 2.1.0
- dev: false
-
- /eslint-visitor-keys@1.3.0:
- resolution: {integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==}
- engines: {node: '>=4'}
- dev: true
-
- /eslint-visitor-keys@2.1.0:
- resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==}
- engines: {node: '>=10'}
-
/eslint-visitor-keys@3.4.3:
resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
- /eslint@7.32.0:
- resolution: {integrity: sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==}
- engines: {node: ^10.12.0 || >=12.0.0}
+ /eslint@8.52.0:
+ resolution: {integrity: sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ hasBin: true
dependencies:
- '@babel/code-frame': 7.12.11
- '@eslint/eslintrc': 0.4.3
- '@humanwhocodes/config-array': 0.5.0
+ '@eslint-community/eslint-utils': 4.4.0(eslint@8.52.0)
+ '@eslint-community/regexpp': 4.10.0
+ '@eslint/eslintrc': 2.1.2
+ '@eslint/js': 8.52.0
+ '@humanwhocodes/config-array': 0.11.13
+ '@humanwhocodes/module-importer': 1.0.1
+ '@nodelib/fs.walk': 1.2.8
+ '@ungap/structured-clone': 1.2.0
ajv: 6.12.6
chalk: 4.1.2
cross-spawn: 7.0.3
debug: 4.3.4
doctrine: 3.0.0
- enquirer: 2.3.6
escape-string-regexp: 4.0.0
- eslint-scope: 5.1.1
- eslint-utils: 2.1.0
- eslint-visitor-keys: 2.1.0
- espree: 7.3.1
- esquery: 1.4.0
+ eslint-scope: 7.2.2
+ eslint-visitor-keys: 3.4.3
+ espree: 9.6.1
+ esquery: 1.5.0
esutils: 2.0.3
fast-deep-equal: 3.1.3
file-entry-cache: 6.0.1
- functional-red-black-tree: 1.0.1
- glob-parent: 5.1.2
- globals: 13.18.0
- ignore: 4.0.6
- import-fresh: 3.3.0
+ find-up: 5.0.0
+ glob-parent: 6.0.2
+ globals: 13.23.0
+ graphemer: 1.4.0
+ ignore: 5.2.4
imurmurhash: 0.1.4
is-glob: 4.0.3
- js-yaml: 3.14.1
+ is-path-inside: 3.0.3
+ js-yaml: 4.1.0
json-stable-stringify-without-jsonify: 1.0.1
levn: 0.4.1
lodash.merge: 4.6.2
minimatch: 3.1.2
natural-compare: 1.4.0
- optionator: 0.9.1
- progress: 2.0.3
- regexpp: 3.2.0
- semver: 7.3.8
+ optionator: 0.9.3
strip-ansi: 6.0.1
- strip-json-comments: 3.1.1
- table: 6.8.1
text-table: 0.2.0
- v8-compile-cache: 2.3.0
transitivePeerDependencies:
- supports-color
dev: true
- /eslint@8.45.0:
- resolution: {integrity: sha512-pd8KSxiQpdYRfYa9Wufvdoct3ZPQQuVuU5O6scNgMuOMYuxvH0IGaYK0wUFjo4UYYQQCUndlXiMbnxopwvvTiw==}
+ /eslint@8.53.0:
+ resolution: {integrity: sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
hasBin: true
dependencies:
- '@eslint-community/eslint-utils': 4.4.0(eslint@8.45.0)
+ '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0)
'@eslint-community/regexpp': 4.10.0
- '@eslint/eslintrc': 2.1.2
- '@eslint/js': 8.44.0
+ '@eslint/eslintrc': 2.1.3
+ '@eslint/js': 8.53.0
'@humanwhocodes/config-array': 0.11.13
'@humanwhocodes/module-importer': 1.0.1
'@nodelib/fs.walk': 1.2.8
+ '@ungap/structured-clone': 1.2.0
ajv: 6.12.6
chalk: 4.1.2
cross-spawn: 7.0.3
@@ -3819,7 +3755,7 @@ packages:
glob-parent: 6.0.2
globals: 13.23.0
graphemer: 1.4.0
- ignore: 5.2.0
+ ignore: 5.2.4
imurmurhash: 0.1.4
is-glob: 4.0.3
is-path-inside: 3.0.3
@@ -3835,35 +3771,14 @@ packages:
transitivePeerDependencies:
- supports-color
- /espree@7.3.1:
- resolution: {integrity: sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==}
- engines: {node: ^10.12.0 || >=12.0.0}
- dependencies:
- acorn: 7.4.1
- acorn-jsx: 5.3.2(acorn@7.4.1)
- eslint-visitor-keys: 1.3.0
- dev: true
-
/espree@9.6.1:
resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
- acorn: 8.10.0
- acorn-jsx: 5.3.2(acorn@8.10.0)
+ acorn: 8.11.2
+ acorn-jsx: 5.3.2(acorn@8.11.2)
eslint-visitor-keys: 3.4.3
- /esprima@4.0.1:
- resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
- engines: {node: '>=4'}
- dev: true
-
- /esquery@1.4.0:
- resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==}
- engines: {node: '>=0.10'}
- dependencies:
- estraverse: 5.3.0
- dev: true
-
/esquery@1.5.0:
resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==}
engines: {node: '>=0.10'}
@@ -3879,6 +3794,7 @@ packages:
/estraverse@4.3.0:
resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==}
engines: {node: '>=4.0'}
+ dev: false
/estraverse@5.3.0:
resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
@@ -3975,8 +3891,8 @@ packages:
/fast-levenshtein@2.0.6:
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
- /fastq@1.13.0:
- resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==}
+ /fastq@1.15.0:
+ resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==}
dependencies:
reusify: 1.0.4
@@ -3998,7 +3914,7 @@ packages:
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
engines: {node: ^10.12.0 || >=12.0.0}
dependencies:
- flat-cache: 3.0.4
+ flat-cache: 3.1.1
/fill-range@7.0.1:
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
@@ -4056,15 +3972,16 @@ packages:
resolve-dir: 1.0.1
dev: true
- /flat-cache@3.0.4:
- resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==}
- engines: {node: ^10.12.0 || >=12.0.0}
+ /flat-cache@3.1.1:
+ resolution: {integrity: sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==}
+ engines: {node: '>=12.0.0'}
dependencies:
- flatted: 3.2.7
+ flatted: 3.2.9
+ keyv: 4.5.4
rimraf: 3.0.2
- /flatted@3.2.7:
- resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==}
+ /flatted@3.2.9:
+ resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==}
/focus-lock@1.0.0:
resolution: {integrity: sha512-a8Ge6cdKh9za/GZR/qtigTAk7SrGore56EFcoMshClsh7FLk1zwszc/ltuMfKhx56qeuyL/jWQ4J4axou0iJ9w==}
@@ -4161,18 +4078,14 @@ packages:
/fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
- /fsevents@2.3.2:
- resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+ /fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
requiresBuild: true
dev: true
optional: true
- /function-bind@1.1.1:
- resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
- dev: false
-
/function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
@@ -4186,10 +4099,6 @@ packages:
functions-have-names: 1.2.3
dev: false
- /functional-red-black-tree@1.0.1:
- resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==}
- dev: true
-
/functions-have-names@1.2.3:
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
dev: false
@@ -4291,13 +4200,6 @@ packages:
which: 1.3.1
dev: true
- /globals@13.18.0:
- resolution: {integrity: sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==}
- engines: {node: '>=8'}
- dependencies:
- type-fest: 0.20.2
- dev: true
-
/globals@13.23.0:
resolution: {integrity: sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==}
engines: {node: '>=8'}
@@ -4318,7 +4220,7 @@ packages:
array-union: 2.1.0
dir-glob: 3.0.1
fast-glob: 3.3.1
- ignore: 5.2.0
+ ignore: 5.2.4
merge2: 1.4.1
slash: 3.0.0
dev: false
@@ -4446,13 +4348,8 @@ packages:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
dev: true
- /ignore@4.0.6:
- resolution: {integrity: sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==}
- engines: {node: '>= 4'}
- dev: true
-
- /ignore@5.2.0:
- resolution: {integrity: sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==}
+ /ignore@5.2.4:
+ resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
engines: {node: '>= 4'}
/import-fresh@3.3.0:
@@ -4524,6 +4421,27 @@ packages:
wrap-ansi: 7.0.0
dev: true
+ /inquirer@8.2.6:
+ resolution: {integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==}
+ engines: {node: '>=12.0.0'}
+ dependencies:
+ ansi-escapes: 4.3.2
+ chalk: 4.1.2
+ cli-cursor: 3.1.0
+ cli-width: 3.0.0
+ external-editor: 3.1.0
+ figures: 3.2.0
+ lodash: 4.17.21
+ mute-stream: 0.0.8
+ ora: 5.4.1
+ run-async: 2.4.1
+ rxjs: 7.8.1
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+ through: 2.3.8
+ wrap-ansi: 6.2.0
+ dev: true
+
/internal-slot@1.0.6:
resolution: {integrity: sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==}
engines: {node: '>= 0.4'}
@@ -4805,8 +4723,8 @@ packages:
set-function-name: 2.0.1
dev: false
- /jiti@1.20.0:
- resolution: {integrity: sha512-3TV69ZbrvV6U5DfQimop50jE9Dl6J8O1ja1dvBbMba/sZ3YBEQqJ2VZRoQPVnhlzjNtU1vaXRZVrVjU4qtm8yA==}
+ /jiti@1.21.0:
+ resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==}
hasBin: true
dev: true
@@ -4818,19 +4736,15 @@ packages:
/js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
- /js-yaml@3.14.1:
- resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
- dependencies:
- argparse: 1.0.10
- esprima: 4.0.1
- dev: true
-
/js-yaml@4.1.0:
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
hasBin: true
dependencies:
argparse: 2.0.1
+ /json-buffer@3.0.1:
+ resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
+
/json-parse-even-better-errors@2.3.1:
resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
@@ -4874,6 +4788,11 @@ packages:
object.values: 1.1.7
dev: false
+ /keyv@4.5.4:
+ resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+ dependencies:
+ json-buffer: 3.0.1
+
/kind-of@6.0.3:
resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
engines: {node: '>=0.10.0'}
@@ -4979,10 +4898,6 @@ packages:
resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==}
dev: true
- /lodash.truncate@4.4.2:
- resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==}
- dev: true
-
/lodash.uniq@4.5.0:
resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==}
dev: true
@@ -5143,7 +5058,11 @@ packages:
/ms@2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
- /msw@2.0.1(typescript@5.0.2):
+ /ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+ dev: false
+
+ /msw@2.0.1(typescript@5.2.2):
resolution: {integrity: sha512-eGcoo5anJmorEkGMQ48dLFx53EJlDL00GjeJtl2dRGm6WwPpzt2fYDZ9ueJ53zMNTtrNtjH9RqbDa+nz7e9Dew==}
engines: {node: '>=18'}
hasBin: true
@@ -5168,7 +5087,7 @@ packages:
formdata-node: 4.4.1
graphql: 16.8.1
headers-polyfill: 4.0.2
- inquirer: 8.2.5
+ inquirer: 8.2.6
is-node-process: 1.2.0
js-levenshtein: 1.1.6
node-fetch: 2.7.0
@@ -5176,7 +5095,7 @@ packages:
path-to-regexp: 6.2.1
strict-event-emitter: 0.5.1
type-fest: 2.19.0
- typescript: 5.0.2
+ typescript: 5.2.2
yargs: 17.7.2
transitivePeerDependencies:
- encoding
@@ -5357,18 +5276,6 @@ packages:
is-wsl: 2.2.0
dev: false
- /optionator@0.9.1:
- resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==}
- engines: {node: '>= 0.8.0'}
- dependencies:
- deep-is: 0.1.4
- fast-levenshtein: 2.0.6
- levn: 0.4.1
- prelude-ls: 1.2.1
- type-check: 0.4.0
- word-wrap: 1.2.3
- dev: true
-
/optionator@0.9.3:
resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==}
engines: {node: '>= 0.8.0'}
@@ -5552,21 +5459,10 @@ packages:
fast-diff: 1.3.0
dev: false
- /prettier@2.8.0:
- resolution: {integrity: sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA==}
- engines: {node: '>=10.13.0'}
- dev: true
-
/prettier@3.0.3:
resolution: {integrity: sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==}
engines: {node: '>=14'}
hasBin: true
- dev: false
-
- /progress@2.0.3:
- resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
- engines: {node: '>=0.4.0'}
- dev: true
/prop-types@15.8.1:
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
@@ -5580,8 +5476,8 @@ packages:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
dev: false
- /punycode@2.1.1:
- resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==}
+ /punycode@2.3.1:
+ resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
/queue-microtask@1.2.3:
@@ -5628,7 +5524,7 @@ packages:
resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==}
dev: false
- /react-focus-lock@2.9.6(@types/react@18.2.15)(react@18.2.0):
+ /react-focus-lock@2.9.6(@types/react@18.2.33)(react@18.2.0):
resolution: {integrity: sha512-B7gYnCjHNrNYwY2juS71dHbf0+UpXXojt02svxybj8N5bxceAkzPChKEncHuratjUHkIFNCn06k2qj1DRlzTug==}
peerDependencies:
'@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -5638,13 +5534,13 @@ packages:
optional: true
dependencies:
'@babel/runtime': 7.23.2
- '@types/react': 18.2.15
+ '@types/react': 18.2.33
focus-lock: 1.0.0
prop-types: 15.8.1
react: 18.2.0
react-clientside-effect: 1.2.6(react@18.2.0)
- use-callback-ref: 1.3.0(@types/react@18.2.15)(react@18.2.0)
- use-sidecar: 1.1.2(@types/react@18.2.15)(react@18.2.0)
+ use-callback-ref: 1.3.0(@types/react@18.2.33)(react@18.2.0)
+ use-sidecar: 1.1.2(@types/react@18.2.33)(react@18.2.0)
dev: false
/react-hook-form@7.47.0(react@18.2.0):
@@ -5660,7 +5556,7 @@ packages:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
dev: false
- /react-remove-scroll-bar@2.3.4(@types/react@18.2.15)(react@18.2.0):
+ /react-remove-scroll-bar@2.3.4(@types/react@18.2.33)(react@18.2.0):
resolution: {integrity: sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==}
engines: {node: '>=10'}
peerDependencies:
@@ -5670,13 +5566,13 @@ packages:
'@types/react':
optional: true
dependencies:
- '@types/react': 18.2.15
+ '@types/react': 18.2.33
react: 18.2.0
- react-style-singleton: 2.2.1(@types/react@18.2.15)(react@18.2.0)
+ react-style-singleton: 2.2.1(@types/react@18.2.33)(react@18.2.0)
tslib: 2.6.2
dev: false
- /react-remove-scroll@2.5.7(@types/react@18.2.15)(react@18.2.0):
+ /react-remove-scroll@2.5.7(@types/react@18.2.33)(react@18.2.0):
resolution: {integrity: sha512-FnrTWO4L7/Bhhf3CYBNArEG/yROV0tKmTv7/3h9QCFvH6sndeFf1wPqOcbFVu5VAulS5dV1wGT3GZZ/1GawqiA==}
engines: {node: '>=10'}
peerDependencies:
@@ -5686,39 +5582,39 @@ packages:
'@types/react':
optional: true
dependencies:
- '@types/react': 18.2.15
+ '@types/react': 18.2.33
react: 18.2.0
- react-remove-scroll-bar: 2.3.4(@types/react@18.2.15)(react@18.2.0)
- react-style-singleton: 2.2.1(@types/react@18.2.15)(react@18.2.0)
+ react-remove-scroll-bar: 2.3.4(@types/react@18.2.33)(react@18.2.0)
+ react-style-singleton: 2.2.1(@types/react@18.2.33)(react@18.2.0)
tslib: 2.6.2
- use-callback-ref: 1.3.0(@types/react@18.2.15)(react@18.2.0)
- use-sidecar: 1.1.2(@types/react@18.2.15)(react@18.2.0)
+ use-callback-ref: 1.3.0(@types/react@18.2.33)(react@18.2.0)
+ use-sidecar: 1.1.2(@types/react@18.2.33)(react@18.2.0)
dev: false
- /react-router-dom@6.17.0(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-qWHkkbXQX+6li0COUUPKAUkxjNNqPJuiBd27dVwQGDNsuFBdMbrS6UZ0CLYc4CsbdLYTckn4oB4tGDuPZpPhaQ==}
+ /react-router-dom@6.18.0(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-Ubrue4+Ercc/BoDkFQfc6og5zRQ4A8YxSO3Knsne+eRbZ+IepAsK249XBH/XaFuOYOYr3L3r13CXTLvYt5JDjw==}
engines: {node: '>=14.0.0'}
peerDependencies:
react: '>=16.8'
react-dom: '>=16.8'
dependencies:
- '@remix-run/router': 1.10.0
+ '@remix-run/router': 1.11.0
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
- react-router: 6.17.0(react@18.2.0)
+ react-router: 6.18.0(react@18.2.0)
dev: false
- /react-router@6.17.0(react@18.2.0):
- resolution: {integrity: sha512-YJR3OTJzi3zhqeJYADHANCGPUu9J+6fT5GLv82UWRGSxu6oJYCKVmxUcaBQuGm9udpWmPsvpme/CdHumqgsoaA==}
+ /react-router@6.18.0(react@18.2.0):
+ resolution: {integrity: sha512-vk2y7Dsy8wI02eRRaRmOs9g2o+aE72YCx5q9VasT1N9v+lrdB79tIqrjMfByHiY5+6aYkH2rUa5X839nwWGPDg==}
engines: {node: '>=14.0.0'}
peerDependencies:
react: '>=16.8'
dependencies:
- '@remix-run/router': 1.10.0
+ '@remix-run/router': 1.11.0
react: 18.2.0
dev: false
- /react-style-singleton@2.2.1(@types/react@18.2.15)(react@18.2.0):
+ /react-style-singleton@2.2.1(@types/react@18.2.33)(react@18.2.0):
resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
engines: {node: '>=10'}
peerDependencies:
@@ -5728,7 +5624,7 @@ packages:
'@types/react':
optional: true
dependencies:
- '@types/react': 18.2.15
+ '@types/react': 18.2.33
get-nonce: 1.0.1
invariant: 2.2.4
react: 18.2.0
@@ -5810,10 +5706,6 @@ packages:
set-function-name: 2.0.1
dev: false
- /regexpp@3.2.0:
- resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==}
- engines: {node: '>=8'}
-
/require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'}
@@ -5915,7 +5807,7 @@ packages:
engines: {node: '>=14.18.0', npm: '>=8.0.0'}
hasBin: true
optionalDependencies:
- fsevents: 2.3.2
+ fsevents: 2.3.3
dev: true
/run-applescript@5.0.0:
@@ -5990,12 +5882,6 @@ packages:
hasBin: true
dev: false
- /semver@7.3.8:
- resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==}
- engines: {node: '>=10'}
- dependencies:
- lru-cache: 6.0.0
-
/semver@7.5.4:
resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==}
engines: {node: '>=10'}
@@ -6053,15 +5939,6 @@ packages:
engines: {node: '>=8'}
dev: false
- /slice-ansi@4.0.0:
- resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==}
- engines: {node: '>=10'}
- dependencies:
- ansi-styles: 4.3.0
- astral-regex: 2.0.0
- is-fullwidth-code-point: 3.0.0
- dev: true
-
/slice-ansi@5.0.0:
resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==}
engines: {node: '>=12'}
@@ -6113,10 +5990,6 @@ packages:
engines: {node: '>= 10.x'}
dev: true
- /sprintf-js@1.0.3:
- resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
- dev: true
-
/statuses@2.0.1:
resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
engines: {node: '>= 0.8'}
@@ -6286,17 +6159,6 @@ packages:
tslib: 2.6.2
dev: false
- /table@6.8.1:
- resolution: {integrity: sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==}
- engines: {node: '>=10.0.0'}
- dependencies:
- ajv: 8.11.2
- lodash.truncate: 4.4.2
- slice-ansi: 4.0.0
- string-width: 4.2.3
- strip-ansi: 6.0.1
- dev: true
-
/temp@0.9.4:
resolution: {integrity: sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==}
engines: {node: '>=6.0.0'}
@@ -6386,14 +6248,14 @@ packages:
/tslib@2.6.2:
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
- /tsutils@3.21.0(typescript@5.0.2):
+ /tsutils@3.21.0(typescript@5.2.2):
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
engines: {node: '>= 6'}
peerDependencies:
typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta'
dependencies:
tslib: 1.14.1
- typescript: 5.0.2
+ typescript: 5.2.2
dev: false
/turbo-darwin-64@1.10.16:
@@ -6538,9 +6400,9 @@ packages:
is-typed-array: 1.1.12
dev: false
- /typescript@5.0.2:
- resolution: {integrity: sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==}
- engines: {node: '>=12.20'}
+ /typescript@5.2.2:
+ resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==}
+ engines: {node: '>=14.17'}
hasBin: true
/unbox-primitive@1.0.2:
@@ -6569,9 +6431,9 @@ packages:
/uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
dependencies:
- punycode: 2.1.1
+ punycode: 2.3.1
- /use-callback-ref@1.3.0(@types/react@18.2.15)(react@18.2.0):
+ /use-callback-ref@1.3.0(@types/react@18.2.33)(react@18.2.0):
resolution: {integrity: sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==}
engines: {node: '>=10'}
peerDependencies:
@@ -6581,12 +6443,12 @@ packages:
'@types/react':
optional: true
dependencies:
- '@types/react': 18.2.15
+ '@types/react': 18.2.33
react: 18.2.0
tslib: 2.6.2
dev: false
- /use-sidecar@1.1.2(@types/react@18.2.15)(react@18.2.0):
+ /use-sidecar@1.1.2(@types/react@18.2.33)(react@18.2.0):
resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==}
engines: {node: '>=10'}
peerDependencies:
@@ -6596,7 +6458,7 @@ packages:
'@types/react':
optional: true
dependencies:
- '@types/react': 18.2.15
+ '@types/react': 18.2.33
detect-node-es: 1.1.0
react: 18.2.0
tslib: 2.6.2
@@ -6621,10 +6483,6 @@ packages:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
dev: true
- /v8-compile-cache@2.3.0:
- resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==}
- dev: true
-
/validate-npm-package-license@3.0.4:
resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
dependencies:
@@ -6632,8 +6490,8 @@ packages:
spdx-expression-parse: 3.0.1
dev: true
- /vite@4.4.5(@types/node@20.8.10):
- resolution: {integrity: sha512-4m5kEtAWHYr0O1Fu7rZp64CfO1PsRGZlD3TAB32UmQlpd7qg15VF7ROqGN5CyqN7HFuwr7ICNM2+fDWRqFEKaA==}
+ /vite@4.5.0(@types/node@20.8.10):
+ resolution: {integrity: sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==}
engines: {node: ^14.18.0 || >=16.0.0}
hasBin: true
peerDependencies:
@@ -6665,7 +6523,7 @@ packages:
postcss: 8.4.31
rollup: 3.29.4
optionalDependencies:
- fsevents: 2.3.2
+ fsevents: 2.3.3
dev: true
/wcwidth@1.0.1:
@@ -6752,11 +6610,20 @@ packages:
dependencies:
isexe: 2.0.0
- /word-wrap@1.2.3:
- resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==}
+ /word-wrap@1.2.5:
+ resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'}
dev: true
+ /wrap-ansi@6.2.0:
+ resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
+ engines: {node: '>=8'}
+ dependencies:
+ ansi-styles: 4.3.0
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+ dev: true
+
/wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'}
@@ -6834,8 +6701,8 @@ packages:
/zod@3.22.4:
resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==}
- /zustand@4.4.4(@types/react@18.2.15)(react@18.2.0):
- resolution: {integrity: sha512-5UTUIAiHMNf5+mFp7/AnzJXS7+XxktULFN0+D1sCiZWyX7ZG+AQpqs2qpYrynRij4QvoDdCD+U+bmg/cG3Ucxw==}
+ /zustand@4.4.5(@types/react@18.2.33)(react@18.2.0):
+ resolution: {integrity: sha512-jgIrBBLKncQW74PA2Lclct3gwD4aPughwGE4FqlXrv3rDqQox7JCn8rwUiFK6ygJcbBAvfTf8fF+ICg7HLD2FQ==}
engines: {node: '>=12.7.0'}
peerDependencies:
'@types/react': '>=16.8'
@@ -6849,7 +6716,7 @@ packages:
react:
optional: true
dependencies:
- '@types/react': 18.2.15
+ '@types/react': 18.2.33
react: 18.2.0
use-sync-external-store: 1.2.0(react@18.2.0)
dev: false