diff --git a/fe/src/App.tsx b/fe/src/App.tsx
index 2445291b4..4354be113 100644
--- a/fe/src/App.tsx
+++ b/fe/src/App.tsx
@@ -4,13 +4,22 @@ import { theme as MuiGlobalTheme } from 'styles/GlobalStyles';
import { theme as ScGlobalTheme } from 'styles/theme';
import Router from 'Router';
+import { PersonnalProvider } from 'contexts/PersonnelContext';
+import { PriceProvider } from 'contexts/PriceContext';
+import { PeriodPriovider } from 'contexts/periodContext';
function App() {
return (
-
+
+
+
+
+
+
+
);
diff --git a/fe/src/components/Calendar.tsx b/fe/src/components/Calendar.tsx
new file mode 100644
index 000000000..409eda84b
--- /dev/null
+++ b/fe/src/components/Calendar.tsx
@@ -0,0 +1,92 @@
+import { usePeriodDispatch } from 'contexts/periodContext';
+import { ReactComponent as LeftIcon } from 'images/FE_숙소예약서비스/Property 1=chevron-left.svg';
+import { ReactComponent as RightIcon } from 'images/FE_숙소예약서비스/Property 1=chevron-right.svg';
+import styled from 'styled-components';
+
+type CalendarProps = {
+ month: number;
+};
+
+function Calendar({ month }: CalendarProps) {
+ const days: Array = ['일', '월', '화', '수', '목', '금', '토'];
+ const daysComp = days.map((day) => {
+ return {day};
+ });
+ const { setCheckIn, setCheckOut, setMonth } = usePeriodDispatch();
+
+ function increaseMonth() {
+ if (month < 10) setMonth(month + 2);
+ }
+ function decreaseMonth() {
+ if (month > 0) setMonth(month - 2);
+ }
+ return (
+ <>
+
+ {
+ decreaseMonth();
+ }}
+ />
+ {month + 1}월
+ {month + 2}월
+ {
+ increaseMonth();
+ }}
+ />
+
+
+ {daysComp}
+ 다음달
+
+ >
+ );
+}
+
+const SlideBtnWrap = styled.div`
+ width: 100%;
+ height: 24px;
+ display: flex;
+ justify-content: space-between;
+ position: relative;
+ margin-bottom: 24px;
+`;
+const PrevBtn = styled(LeftIcon)`
+ position: absolute;
+`;
+const ThisMonthTitle = styled.div`
+ width: 336px;
+ display: flex;
+ justify-content: center;
+ ${({ theme }) => theme.fontStyles.bold16px};
+`;
+const NextMonthTitle = styled(ThisMonthTitle)``;
+const NextBtn = styled(RightIcon)`
+ position: absolute;
+ right: 0;
+ cursor: pointer;
+`;
+const CalendarWrap = styled.div`
+ width: 100%;
+ display: flex;
+ justify-content: space-between;
+`;
+const ThisMonth = styled.div`
+ width: 336px;
+ height: 336px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+`;
+const Day = styled.div`
+ width: 48px;
+ height: 24px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ ${({ theme }) => theme.fontStyles.normal12px}
+ color : ${({ theme }) => theme.colors.grey3}
+`;
+const NextMonth = styled(ThisMonth)``;
+export default Calendar;
diff --git a/fe/src/components/Header/MiniSearchBar/index.tsx b/fe/src/components/Header/MiniSearchBar/index.tsx
index 25b5932e1..bea884f85 100644
--- a/fe/src/components/Header/MiniSearchBar/index.tsx
+++ b/fe/src/components/Header/MiniSearchBar/index.tsx
@@ -2,18 +2,24 @@ import styled from 'styled-components';
import { Divider } from '@mui/material';
import { StyledSearchIcon } from 'components/Header/SearchBar/searchBar.styled';
+import { usePersonnelState } from 'contexts/PersonnelContext';
+import { usePriceState } from 'contexts/PriceContext';
+
type MyProps = {
changeSearchBar: () => void;
};
function MiniSearchBar({ changeSearchBar }: MyProps) {
+ const { counterText } = usePersonnelState();
+ const { rangeText } = usePriceState();
+
return (
일정 입력
- ₩100,000~1,000,000
+ {rangeText}
- 인원 입력
+ {counterText}
diff --git a/fe/src/components/Header/SearchBar/Period.tsx b/fe/src/components/Header/SearchBar/Period.tsx
index e9518478c..239b258b3 100644
--- a/fe/src/components/Header/SearchBar/Period.tsx
+++ b/fe/src/components/Header/SearchBar/Period.tsx
@@ -1,36 +1,57 @@
import { Divider } from '@mui/material';
-import {
- CommonContainer,
- CommonButton,
- Label,
- InputState,
- StyledCrossIcon,
-} from 'components/Header/SearchBar/searchBar.styled';
+import { CommonContainer } from 'components/Header/SearchBar/searchBar.styled';
+import { ClickModal } from '.';
+import InputButton from './common/InputButton';
+import ResetButton from './common/ResetButton';
-function Period() {
+function Period({ clickModal, isClicked, focusModal }: ClickModal) {
return (
<>
-
-
-
- 날짜 입력
-
-
-
-
-
-
-
- 날짜 입력
-
-
-
+
+ {focusModal === '' && }
+
>
);
}
+function CheckIn({ clickModal, isClicked, focusModal }: ClickModal) {
+ const FILTER_ID = 'CHECK_IN';
+ const BUTTON_INFO = {
+ id: FILTER_ID,
+ title: '체크인',
+ inputText: '날짜 입력',
+ ariaLabel: '체크인 날짜 입력 버튼',
+ };
+ const RESET_BUTTON_ARIA_LABEL = '날짜 입력 취소 버튼';
+
+ return (
+
+
+ {isClicked && focusModal === FILTER_ID && }
+
+ );
+}
+
+function CheckOut({ clickModal, isClicked, focusModal }: ClickModal) {
+ const FILTER_ID = 'CHECK_OUT';
+ const BUTTON_INFO = {
+ id: FILTER_ID,
+ title: '체크아웃',
+ inputText: '날짜 입력',
+ ariaLabel: '체크아웃 날짜 입력 버튼',
+ };
+ const RESET_BUTTON_ARIA_LABEL = '날짜 입력 취소 버튼';
+
+ return (
+
+
+ {isClicked && focusModal === FILTER_ID && }
+
+ );
+}
+
export default Period;
diff --git a/fe/src/components/Header/SearchBar/Personnel.tsx b/fe/src/components/Header/SearchBar/Personnel.tsx
index 565725a18..3335f7bca 100644
--- a/fe/src/components/Header/SearchBar/Personnel.tsx
+++ b/fe/src/components/Header/SearchBar/Personnel.tsx
@@ -1,29 +1,31 @@
-import styled from 'styled-components';
-import {
- CommonButton,
- Label,
- InputState,
- StyledCrossIcon,
-} from 'components/Header/SearchBar/searchBar.styled';
+import { usePersonnelState } from 'contexts/PersonnelContext';
+
+import { ClickModal } from '.';
+import ResetButton from './common/ResetButton';
+import InputButton from './common/InputButton';
+
+function Personnel({ clickModal, isClicked, focusModal }: ClickModal) {
+ const { counterText } = usePersonnelState();
+
+ const FILTER_ID = 'PERSONNEL';
+ const BUTTON_INFO = {
+ id: FILTER_ID,
+ title: '인원',
+ inputText: counterText,
+ ariaLabel: '게스트 추가 버튼',
+ };
+ const RESET_BUTTON_ARIA_LABEL = '게스트 추가 취소 버튼';
-function Personnel() {
return (
<>
-
-
- 게스트 2명, 유아 2명
-
-
+
+ {isClicked && focusModal === FILTER_ID && }
>
);
}
-const PersonnelInputState = styled(InputState)`
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-`;
-
export default Personnel;
diff --git a/fe/src/components/Header/SearchBar/Price.tsx b/fe/src/components/Header/SearchBar/Price.tsx
index 469580e48..8e0c01f43 100644
--- a/fe/src/components/Header/SearchBar/Price.tsx
+++ b/fe/src/components/Header/SearchBar/Price.tsx
@@ -1,21 +1,27 @@
-import {
- CommonContainer,
- CommonButton,
- Label,
- InputState,
- StyledCrossIcon,
-} from 'components/Header/SearchBar/searchBar.styled';
+import { usePriceState } from 'contexts/PriceContext';
+
+import { CommonContainer } from 'components/Header/SearchBar/searchBar.styled';
+import ResetButton from './common/ResetButton';
+import InputButton from './common/InputButton';
+
+import { ClickModal } from '.';
+
+function Price({ clickModal, isClicked, focusModal }: ClickModal) {
+ const { rangeText } = usePriceState();
+
+ const FILTER_ID = 'PRICE';
+ const BUTTON_INFO = {
+ id: FILTER_ID,
+ title: '요금',
+ inputText: rangeText,
+ ariaLabel: '요금 입력 버튼',
+ };
+ const RESET_BUTTON_ARIA_LABEL = '요금 입력 취소 버튼';
-function Price() {
return (
-
-
-
- ₩100,000~1,000,000
-
-
+
+
+ {isClicked && focusModal === FILTER_ID && }
);
}
diff --git a/fe/src/components/Header/SearchBar/SearchButton.tsx b/fe/src/components/Header/SearchBar/SearchButton.tsx
index e9a628b15..882a46cf2 100644
--- a/fe/src/components/Header/SearchBar/SearchButton.tsx
+++ b/fe/src/components/Header/SearchBar/SearchButton.tsx
@@ -2,12 +2,16 @@ import {
SearchButton as SearchButtonContainer,
StyledSearchIcon,
} from 'components/Header/SearchBar/searchBar.styled';
+import { ModalContext } from 'contexts/ModalContext';
+import { useContext } from 'react';
function SearchButton() {
+ const { focusModal } = useContext(ModalContext);
+
return (
-
+
- 검색
+ {focusModal !== '' && 검색}
);
}
diff --git a/fe/src/components/Header/SearchBar/common/InputButton.tsx b/fe/src/components/Header/SearchBar/common/InputButton.tsx
new file mode 100644
index 000000000..a04b90102
--- /dev/null
+++ b/fe/src/components/Header/SearchBar/common/InputButton.tsx
@@ -0,0 +1,38 @@
+import { CommonButton, Label, SelectedOption } from 'components/Header/SearchBar/searchBar.styled';
+
+type ButtonInfo = {
+ id: string | undefined;
+ title: string;
+ inputText: string;
+ ariaLabel: string;
+};
+
+type Style = {
+ [key: string]: number | string;
+};
+
+type InputButtonType = {
+ clickModal: (e: React.MouseEvent) => void;
+ buttonInfo: ButtonInfo;
+ styleOptions?: Style;
+};
+
+function InputButton({ clickModal, buttonInfo, styleOptions }: InputButtonType) {
+ return (
+
+
+ {buttonInfo.inputText}
+
+ );
+}
+
+InputButton.defaultProps = {
+ styleOptions: undefined,
+};
+
+export default InputButton;
diff --git a/fe/src/components/Header/SearchBar/common/ResetButton.tsx b/fe/src/components/Header/SearchBar/common/ResetButton.tsx
new file mode 100644
index 000000000..694021614
--- /dev/null
+++ b/fe/src/components/Header/SearchBar/common/ResetButton.tsx
@@ -0,0 +1,15 @@
+import { StyledCrossIcon } from 'components/Header/SearchBar/searchBar.styled';
+
+type Aria = {
+ ariaLabel: string;
+};
+
+function ResetButton({ ariaLabel }: Aria) {
+ return (
+
+ );
+}
+
+export default ResetButton;
diff --git a/fe/src/components/Header/SearchBar/index.tsx b/fe/src/components/Header/SearchBar/index.tsx
index 6e3b49a4a..6fa979af0 100644
--- a/fe/src/components/Header/SearchBar/index.tsx
+++ b/fe/src/components/Header/SearchBar/index.tsx
@@ -1,3 +1,5 @@
+import { useModal } from 'hooks/useModal';
+
import { Divider } from '@mui/material';
import {
SearchBarWrap,
@@ -11,27 +13,58 @@ import Price from 'components/Header/SearchBar/Price';
import SearchButton from 'components/Header/SearchBar/SearchButton';
import PeriodModal from 'components/Modals/PeriodModal';
-import PriceModal from 'components/Modals/PriceModal';
import PersonnelModal from 'components/Modals/PersonnelModal';
+import PriceModal from 'components/Modals/PriceModal';
+
+export type ClickModal = {
+ clickModal: (e: React.MouseEvent) => void;
+ isClicked?: boolean;
+ focusModal?: string;
+ id?: string;
+};
+
+type Focus = {
+ focus: string;
+};
function SearchBar() {
+ const { focusModal, clickModalFocus, isClicked } = useModal();
+
+ const clickModal = (e: React.MouseEvent) => {
+ if (!e.currentTarget.dataset.id) return;
+ const datasetID: string = e.currentTarget.dataset.id;
+ clickModalFocus?.(datasetID);
+ };
+
return (
-
-
-
-
-
-
-
+
+
+ {focusModal === '' && }
+
+ {focusModal === '' && }
+
+
-
-
-
+ {focusModal !== '' && }
);
}
+function FocusModal({ focus }: Focus) {
+ switch (focus) {
+ case 'CHECK_IN':
+ case 'CHECK_OUT':
+ return ;
+ case 'PERSONNEL':
+ return ;
+ case 'PRICE':
+ return ;
+ default:
+ throw Error();
+ }
+}
+
export default SearchBar;
diff --git a/fe/src/components/Header/SearchBar/searchBar.styled.js b/fe/src/components/Header/SearchBar/searchBar.styled.ts
similarity index 65%
rename from fe/src/components/Header/SearchBar/searchBar.styled.js
rename to fe/src/components/Header/SearchBar/searchBar.styled.ts
index e23b6cca1..2478e6e4a 100644
--- a/fe/src/components/Header/SearchBar/searchBar.styled.js
+++ b/fe/src/components/Header/SearchBar/searchBar.styled.ts
@@ -1,22 +1,40 @@
-import styled from 'styled-components';
+import styled, { css } from 'styled-components';
import { ReactComponent as SearchIcon } from 'images/FE_숙소예약서비스/Property 1=search.svg';
import { ReactComponent as CrossIcon } from 'images/FE_숙소예약서비스/Property 1=x-circle.svg';
+type SearchBarContainerType = {
+ isClicked: boolean;
+};
+
export const SearchBarContainer = styled.div`
position: relative;
`;
-export const SearchBarWrap = styled.div`
+export const SearchBarWrap = styled.div`
display: flex;
align-items: center;
width: 916px;
height: 76px;
border: 1px solid ${({ theme }) => theme.colors.grey4};
border-radius: 999px;
- background: ${({ theme }) => theme.colors.white};
+
+ background: ${({ isClicked }) =>
+ isClicked
+ ? css`
+ ${({ theme }) => theme.colors.grey5};
+ `
+ : css`
+ ${({ theme }) => theme.colors.white}
+ `};
`;
-export const CommonContainer = styled.div`
+interface ContainerType {
+ isClicked?: boolean;
+ focusModal?: string;
+ id: string;
+}
+
+export const CommonContainer = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
@@ -29,11 +47,13 @@ export const CommonContainer = styled.div`
background: ${({ theme }) => theme.colors.grey6};
}
- /* props로 넘겨받도록 수정 */
- &.focus {
- background: ${({ theme }) => theme.colors.white};
- box-shadow: 0px 0px 13px 2px rgba(51, 51, 51, 0.29);
- }
+ ${({ focusModal, id, isClicked }) =>
+ focusModal === id &&
+ isClicked &&
+ css`
+ background: ${({ theme }) => theme.colors.white};
+ box-shadow: 0px 0px 13px 2px rgba(51, 51, 51, 0.29);
+ `};
`;
export const CommonButton = styled.button`
@@ -49,14 +69,18 @@ export const Label = styled.span`
${({ theme }) => theme.fontStyles.bold12px};
`;
-export const InputState = styled.span`
+export const SelectedOption = styled.span`
width: 100%;
text-align: left;
color: ${({ theme }) => theme.colors.grey2};
${({ theme }) => theme.fontStyles.normal16px};
+
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
`;
-export const SearchButton = styled.button`
+export const SearchButton = styled.a`
display: flex;
align-items: center;
justify-content: center;
diff --git a/fe/src/components/Header/SearchWrapper.jsx b/fe/src/components/Header/SearchWrapper.tsx
similarity index 52%
rename from fe/src/components/Header/SearchWrapper.jsx
rename to fe/src/components/Header/SearchWrapper.tsx
index 48fee3d85..f5adcc07e 100644
--- a/fe/src/components/Header/SearchWrapper.jsx
+++ b/fe/src/components/Header/SearchWrapper.tsx
@@ -1,8 +1,17 @@
+import { Dispatch } from 'react';
import styled from 'styled-components';
+import Menu from './Menu';
import MiniSearchBar from './MiniSearchBar';
import SearchBar from './SearchBar';
-function SearchWrapper({ changeSearchBar, miniFocus, setMiniFocus }) {
+type SearchWrapperType = {
+ changeSearchBar: () => void;
+ miniFocus: boolean;
+ setMiniFocus: Dispatch>;
+ path: string;
+};
+
+function SearchWrapper({ changeSearchBar, miniFocus, setMiniFocus, path }: SearchWrapperType) {
function mouseLeave() {
if (!miniFocus) {
setMiniFocus(true);
@@ -11,7 +20,14 @@ function SearchWrapper({ changeSearchBar, miniFocus, setMiniFocus }) {
return (
mouseLeave()}>
- {miniFocus ? : }
+ {path === '/' || !miniFocus ? (
+ <>
+
+
+ >
+ ) : (
+
+ )}
);
}
diff --git a/fe/src/components/Header/UserMenu.tsx b/fe/src/components/Header/UserMenu.tsx
new file mode 100644
index 000000000..2fdbccd10
--- /dev/null
+++ b/fe/src/components/Header/UserMenu.tsx
@@ -0,0 +1,43 @@
+import styled from 'styled-components';
+import { Avatar } from '@mui/material';
+import { ReactComponent as HamburgerIcon } from 'images/FE_숙소예약서비스/Property 1=menu.svg';
+
+function UserMenu() {
+ return (
+
+
+
+
+ );
+}
+
+const UserWrapper = styled.button`
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 76px;
+ height: 40px;
+ border-radius: 999px;
+ border: 1px solid ${({ theme }) => theme.colors.grey4};
+ background: ${({ theme }) => theme.colors.white};
+`;
+
+const Hamburger = styled(HamburgerIcon)`
+ width: 16px;
+ height: 16px;
+ margin: 0 8px 0 10px;
+
+ path {
+ stroke: ${({ theme }) => theme.colors.black};
+ }
+`;
+
+const CustomAvatar = styled(Avatar)`
+ && {
+ width: 32px;
+ height: 32px;
+ background: ${({ theme }) => theme.colors.grey3};
+ }
+`;
+
+export default UserMenu;
diff --git a/fe/src/components/Header/index.tsx b/fe/src/components/Header/index.tsx
index a4b962f6f..f4e804f25 100644
--- a/fe/src/components/Header/index.tsx
+++ b/fe/src/components/Header/index.tsx
@@ -2,13 +2,13 @@ import { useState } from 'react';
import { useLocation } from 'react-router-dom';
import styled, { css } from 'styled-components';
-import { Avatar, Link } from '@mui/material';
-import { ReactComponent as HamburgerIcon } from 'images/FE_숙소예약서비스/Property 1=menu.svg';
+import { Link } from '@mui/material';
import { ReactComponent as LogoImg } from 'images/logo.svg';
-import Menu from 'components/Header/Menu';
-import SearchBar from 'components/Header/SearchBar/index';
-import SearchWrapper, { SearchWrap } from './SearchWrapper';
+import SearchWrapper from './SearchWrapper';
+import UserMenu from './UserMenu';
+
+// type Router = '/' | '/result';
type Position = {
position: string;
@@ -20,27 +20,20 @@ function Header() {
const changeSearchBar = () => setMiniFocus((focus) => !focus);
+ const path = location.pathname;
+
return (
-
+
- {location.pathname === '/' ? (
-
-
-
-
- ) : (
-
- )}
-
-
-
-
+
+
);
}
@@ -68,32 +61,4 @@ const HeaderWrap = styled.header`
z-index: 1;
`;
-const UserWrapper = styled.button`
- display: flex;
- justify-content: center;
- align-items: center;
- width: 76px;
- height: 40px;
- border-radius: 999px;
- border: 1px solid ${({ theme }) => theme.colors.grey4};
- background: ${({ theme }) => theme.colors.white};
-`;
-
-const Hamburger = styled(HamburgerIcon)`
- width: 16px;
- height: 16px;
- margin: 0 8px 0 10px;
-
- path {
- stroke: ${({ theme }) => theme.colors.black};
- }
-`;
-
-const CustomAvatar = styled(Avatar)`
- && {
- width: 32px;
- height: 32px;
- background: ${({ theme }) => theme.colors.grey3};
- }
-`;
export default Header;
diff --git a/fe/src/components/Modals/PeriodModal.tsx b/fe/src/components/Modals/PeriodModal.tsx
index d71311638..c62e64fe4 100644
--- a/fe/src/components/Modals/PeriodModal.tsx
+++ b/fe/src/components/Modals/PeriodModal.tsx
@@ -1,13 +1,24 @@
+import { usePeriodState } from 'contexts/periodContext';
+import Calendar from 'components/Calendar';
import styled from 'styled-components';
import { ModalWrap } from './styled';
function PeriodModal() {
- return 기간모달;
+ const state = usePeriodState();
+ const thisMonth: number = state.month;
+ return (
+
+
+
+ );
}
const PeriodModalWrap = styled(ModalWrap)`
width: 916px;
height: 512px;
- display: none;
+ /* display: none; */
+ padding: 64px 88px;
+ flex-direction: column;
`;
+
export default PeriodModal;
diff --git a/fe/src/components/Modals/PersonnelModal.tsx b/fe/src/components/Modals/PersonnelModal.tsx
index a8aa6955b..a1118217f 100644
--- a/fe/src/components/Modals/PersonnelModal.tsx
+++ b/fe/src/components/Modals/PersonnelModal.tsx
@@ -4,18 +4,25 @@ import PersonnelModalWrap from './PersonnelModalWrap';
import { ModalWrap } from './styled';
+export type PersonnselInfo = {
+ id: number;
+ title: string;
+ info: string;
+ desc: string;
+};
+
function PersonnelModal() {
const PERSONNEL_INFO = [
- { id: 1, title: '성인', info: '만 13세 이상' },
- { id: 2, title: '어린이', info: '만 2~12세' },
- { id: 3, title: '유아', info: '만 2세 미만' },
+ { id: 1, title: '성인', info: '만 13세 이상', desc: 'adult' },
+ { id: 2, title: '어린이', info: '만 2~12세', desc: 'child' },
+ { id: 3, title: '유아', info: '만 2세 미만', desc: 'toddler' },
];
- const PersonnelInfo = PERSONNEL_INFO.map((info, index) => {
+ const PersonnelInfo = PERSONNEL_INFO.map((info: PersonnselInfo, index: number) => {
return (
<>
- {index !== PERSONNEL_INFO.length - 1 && }
+ {index !== PERSONNEL_INFO.length - 1 && }
>
);
});
@@ -29,7 +36,6 @@ const PersonnelModalContainer = styled(ModalWrap)`
right: 0;
padding: 64px;
display: flex;
- display: none;
flex-direction: column;
`;
diff --git a/fe/src/components/Modals/PersonnelModalWrap.tsx b/fe/src/components/Modals/PersonnelModalWrap.tsx
index 581608190..f8d56d13a 100644
--- a/fe/src/components/Modals/PersonnelModalWrap.tsx
+++ b/fe/src/components/Modals/PersonnelModalWrap.tsx
@@ -1,8 +1,17 @@
+import { usePersonnelState } from 'contexts/PersonnelContext';
+
import styled from 'styled-components';
import { ReactComponent as PlusIcon } from 'images/FE_숙소예약서비스/Property 1=plus-circle.svg';
import { ReactComponent as MinusIcon } from 'images/FE_숙소예약서비스/Property 1=minus-circle.svg';
+import { PersonnselInfo } from './PersonnelModal';
+
+type InfoProps = {
+ info: PersonnselInfo;
+};
+
+function PersonnelModalWrap({ info }: InfoProps) {
+ const { counter } = usePersonnelState();
-function PersonnelModalWrap({ info }: any) {
return (
@@ -11,7 +20,7 @@ function PersonnelModalWrap({ info }: any) {
- 0
+ {counter[info.desc]}
diff --git a/fe/src/components/Modals/PriceModal.tsx b/fe/src/components/Modals/PriceModal.tsx
index 20013e253..b26421f39 100644
--- a/fe/src/components/Modals/PriceModal.tsx
+++ b/fe/src/components/Modals/PriceModal.tsx
@@ -21,7 +21,6 @@ function PriceModal() {
const PriceModalWrap = styled(ModalWrap)`
width: 493px;
height: 364px;
- display: none;
right: 200px;
padding: 52px 64px;
justify-content: left;
diff --git a/fe/src/contexts/ModalContext.tsx b/fe/src/contexts/ModalContext.tsx
new file mode 100644
index 000000000..8669a5756
--- /dev/null
+++ b/fe/src/contexts/ModalContext.tsx
@@ -0,0 +1,27 @@
+import { createContext, Dispatch, ReactNode, useState } from 'react';
+
+type ModalInfo = {
+ focusModal: string;
+ setFocusModal?: Dispatch>;
+ isClicked: boolean;
+ setClicked?: Dispatch>;
+};
+
+export const ModalContext = createContext({
+ focusModal: '',
+ isClicked: false,
+});
+
+export function ModalProvider({ children }: { children: ReactNode }) {
+ const [focusModal, setFocusModal] = useState('');
+ const [isClicked, setClicked] = useState(false);
+
+ return (
+
+ {children}
+
+ );
+}
diff --git a/fe/src/contexts/PersonnelContext.tsx b/fe/src/contexts/PersonnelContext.tsx
new file mode 100644
index 000000000..0aba0c118
--- /dev/null
+++ b/fe/src/contexts/PersonnelContext.tsx
@@ -0,0 +1,62 @@
+import { createContext, Dispatch, ReactNode, useContext, useReducer } from 'react';
+
+type Counter = {
+ [key: string]: number;
+ adult: number;
+ child: number;
+ toddler: number;
+};
+
+type State = {
+ counter: Counter;
+ counterText: string;
+};
+
+type Action = { type: 'INCREASE_COUNT'; age: string } | { type: 'DECREASE_COUNT'; age: string };
+
+type PersonnelDispatch = Dispatch;
+
+const PersonnelStateContext = createContext(null);
+const PersonnelDispatchContext = createContext(null);
+
+const initState: State = {
+ counter: { adult: 0, child: 0, toddler: 0 },
+ counterText: '인원 선택',
+};
+
+export function PersonnalProvider({ children }: { children: ReactNode }) {
+ const [state, dispatch] = useReducer(personnalReducer, initState);
+
+ return (
+
+
+ {children}
+
+
+ );
+}
+
+function personnalReducer(state: State, action: Action) {
+ switch (action.type) {
+ case 'INCREASE_COUNT':
+ return state;
+ case 'DECREASE_COUNT':
+ return state;
+ default:
+ throw new Error();
+ }
+}
+
+export function usePersonnelState() {
+ const state = useContext(PersonnelStateContext);
+ if (!state) throw new Error();
+
+ return { counter: state.counter, counterText: state.counterText };
+}
+
+export function usePersonnelDispatch() {
+ const dispatch = useContext(PersonnelDispatchContext);
+ if (!dispatch) throw new Error();
+
+ return dispatch;
+}
diff --git a/fe/src/contexts/PriceContext.tsx b/fe/src/contexts/PriceContext.tsx
new file mode 100644
index 000000000..e2512e709
--- /dev/null
+++ b/fe/src/contexts/PriceContext.tsx
@@ -0,0 +1,54 @@
+import { createContext, Dispatch, ReactNode, useContext, useReducer } from 'react';
+
+type range = {
+ minPrice: number;
+ maxPrice: number;
+};
+
+type State = {
+ range: range;
+ rangeText: string;
+};
+
+type Action = { type: 'SET_RANGE'; age: string };
+
+type PriceDispatch = Dispatch;
+
+const PriceStateContext = createContext(null);
+const PriceDispatchContext = createContext(null);
+
+const initState: State = {
+ range: { minPrice: 0, maxPrice: 0 },
+ rangeText: '금액대 설정',
+};
+
+export function PriceProvider({ children }: { children: ReactNode }) {
+ const [state, dispatch] = useReducer(priceReducer, initState);
+
+ return (
+
+ {children}
+
+ );
+}
+
+function priceReducer(state: State, action: Action): State {
+ switch (action.type) {
+ default:
+ throw new Error();
+ }
+}
+
+export function usePriceState() {
+ const state = useContext(PriceStateContext);
+ if (!state) throw new Error();
+
+ return { range: state.range, rangeText: state.rangeText };
+}
+
+export function usePriceDispatch() {
+ const dispatch = useContext(PriceDispatchContext);
+ if (!dispatch) throw new Error();
+
+ return dispatch;
+}
diff --git a/fe/src/contexts/periodContext.tsx b/fe/src/contexts/periodContext.tsx
new file mode 100644
index 000000000..44529e051
--- /dev/null
+++ b/fe/src/contexts/periodContext.tsx
@@ -0,0 +1,77 @@
+import { createContext, Dispatch, ReactNode, useContext, useReducer } from 'react';
+
+type Period = {
+ month: number;
+ checkIn: string;
+ checkOut: string;
+};
+
+type Action =
+ | { type: 'SET_CHECK_IN'; action: string }
+ | { type: 'SET_CHECK_OUT'; action: string }
+ | { type: 'SET_MONTH'; month: number };
+
+type PeriodDispatch = Dispatch;
+const date = new Date();
+const monthData: number = date.getMonth();
+const PeriodStateContext = createContext(null);
+const PeriodDispatchContext = createContext(null);
+const initialState: Period = {
+ month: monthData,
+ checkIn: date.toString(),
+ checkOut: date.toString(),
+};
+
+export function PeriodPriovider({ children }: { children: ReactNode }) {
+ const [state, dispatch] = useReducer(reducer, initialState);
+
+ return (
+
+ {children}
+
+ );
+}
+
+function reducer(state: Period, action: Action): Period {
+ const date: Date = new Date();
+ switch (action.type) {
+ case 'SET_CHECK_IN':
+ console.log('체크인');
+ return {
+ ...state,
+ checkIn: '체크인',
+ checkOut: '',
+ };
+ case 'SET_CHECK_OUT':
+ console.log(state);
+ return {
+ ...state,
+ checkIn: '',
+ checkOut: '체크아웃',
+ };
+ case 'SET_MONTH':
+ return {
+ ...state,
+ month: action.month,
+ };
+ default:
+ throw new Error();
+ }
+}
+
+export function usePeriodState() {
+ const state = useContext(PeriodStateContext);
+ if (!state) throw new Error();
+
+ return { checkIn: state.checkIn, checkOut: state.checkOut, month: state.month };
+}
+
+export function usePeriodDispatch() {
+ const dispatch = useContext(PeriodDispatchContext);
+ if (!dispatch) throw new Error();
+
+ const setCheckIn = (action: string) => dispatch({ type: 'SET_CHECK_IN', action });
+ const setCheckOut = (action: string) => dispatch({ type: 'SET_CHECK_OUT', action });
+ const setMonth = (month: number) => dispatch({ type: 'SET_MONTH', month });
+ return { setCheckIn, setCheckOut, setMonth };
+}
diff --git a/fe/src/hooks/useModal.tsx b/fe/src/hooks/useModal.tsx
new file mode 100644
index 000000000..3de2810b8
--- /dev/null
+++ b/fe/src/hooks/useModal.tsx
@@ -0,0 +1,22 @@
+import { useContext } from 'react';
+import { ModalContext } from 'contexts/ModalContext';
+
+export function useModal() {
+ const { focusModal, setFocusModal, isClicked, setClicked } = useContext(ModalContext);
+
+ const clickModalFocus = (id: string) => {
+ if (focusModal === id) {
+ setFocusModal?.('');
+ setClicked?.(false);
+ } else {
+ setFocusModal?.(id);
+ setClicked?.(true);
+ }
+ };
+
+ const closeModal = () => {
+ setFocusModal?.('');
+ };
+
+ return { focusModal, setFocusModal, isClicked, setClicked, clickModalFocus, closeModal };
+}
diff --git a/fe/src/pages/Layout.jsx b/fe/src/pages/Layout.jsx
index 076f8ad7a..64c553235 100644
--- a/fe/src/pages/Layout.jsx
+++ b/fe/src/pages/Layout.jsx
@@ -1,13 +1,16 @@
import { Outlet } from 'react-router-dom';
import Header from 'components/Header/index';
import styled from 'styled-components';
+import { ModalProvider } from 'contexts/ModalContext';
function Layout() {
return (
-
-
-
-
+
+
+
+
+
+
);
}