diff --git a/package-lock.json b/package-lock.json
index 857a003..04ce88e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,9 +11,11 @@
"@reduxjs/toolkit": "^1.9.5",
"axios": "^1.5.0",
"classnames": "^2.3.2",
+ "dayjs": "^1.11.10",
"js-cookie": "^3.0.5",
"prop-types": "^15.8.1",
"react": "^18.2.0",
+ "react-calendar": "^4.6.0",
"react-dom": "^18.2.0",
"react-icons": "^4.10.1",
"react-paginate": "^8.2.0",
@@ -2744,6 +2746,19 @@
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
"dev": true
},
+ "node_modules/@types/lodash": {
+ "version": "4.14.199",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.199.tgz",
+ "integrity": "sha512-Vrjz5N5Ia4SEzWWgIVwnHNEnb1UE1XMkvY5DGXrAeOGE9imk0hgTHh5GyDjLDJi9OTCn9oo9dXH1uToK1VRfrg=="
+ },
+ "node_modules/@types/lodash.memoize": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/@types/lodash.memoize/-/lodash.memoize-4.1.7.tgz",
+ "integrity": "sha512-lGN7WeO4vO6sICVpf041Q7BX/9k1Y24Zo3FY0aUezr1QlKznpjzsDk3T3wvH8ofYzoK0QupN9TWcFAFZlyPwQQ==",
+ "dependencies": {
+ "@types/lodash": "*"
+ }
+ },
"node_modules/@types/node": {
"version": "14.18.33",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.33.tgz",
@@ -3152,6 +3167,14 @@
"vite": "^4.2.0"
}
},
+ "node_modules/@wojtekmaj/date-utils": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@wojtekmaj/date-utils/-/date-utils-1.5.0.tgz",
+ "integrity": "sha512-0mq88lCND6QiffnSDWp+TbOxzJSwy2V/3XN+HwWZ7S2n19QAgR5dy5hRVhlECXvQIq2r+VcblBu+S9V+yMcxXw==",
+ "funding": {
+ "url": "https://github.com/wojtekmaj/date-utils?sponsor=1"
+ }
+ },
"node_modules/abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
@@ -3701,6 +3724,14 @@
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
"integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
},
+ "node_modules/clsx": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz",
+ "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/code-block-writer": {
"version": "10.1.1",
"resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-10.1.1.tgz",
@@ -3843,6 +3874,11 @@
"integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==",
"dev": true
},
+ "node_modules/dayjs": {
+ "version": "1.11.10",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz",
+ "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ=="
+ },
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -5251,6 +5287,18 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/get-user-locale": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/get-user-locale/-/get-user-locale-2.3.0.tgz",
+ "integrity": "sha512-I3rQvAUwu2nauRD9YyQBSXVFJZixNouwA+eZld51Sn4Pn0N1qFbgcgOi/nPigJPQlNY519mT95fiSPRgflQiTA==",
+ "dependencies": {
+ "@types/lodash.memoize": "^4.1.7",
+ "lodash.memoize": "^4.1.1"
+ },
+ "funding": {
+ "url": "https://github.com/wojtekmaj/get-user-locale?sponsor=1"
+ }
+ },
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -5920,6 +5968,11 @@
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
},
+ "node_modules/lodash.memoize": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
+ "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag=="
+ },
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@@ -6577,6 +6630,31 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-calendar": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/react-calendar/-/react-calendar-4.6.0.tgz",
+ "integrity": "sha512-GJ6ZipKMQmlK666t+0hgmecu6WHydEnMWJjKdEkUxW6F471hiM5DkbWXkfr8wlAg9tc9feNCBhXw3SqsPOm01A==",
+ "dependencies": {
+ "@wojtekmaj/date-utils": "^1.1.3",
+ "clsx": "^2.0.0",
+ "get-user-locale": "^2.2.1",
+ "prop-types": "^15.6.0",
+ "tiny-warning": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/wojtekmaj/react-calendar?sponsor=1"
+ },
+ "peerDependencies": {
+ "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/react-dom": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
@@ -7334,6 +7412,11 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/tiny-warning": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
+ "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
+ },
"node_modules/to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
@@ -9639,6 +9722,19 @@
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
"dev": true
},
+ "@types/lodash": {
+ "version": "4.14.199",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.199.tgz",
+ "integrity": "sha512-Vrjz5N5Ia4SEzWWgIVwnHNEnb1UE1XMkvY5DGXrAeOGE9imk0hgTHh5GyDjLDJi9OTCn9oo9dXH1uToK1VRfrg=="
+ },
+ "@types/lodash.memoize": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/@types/lodash.memoize/-/lodash.memoize-4.1.7.tgz",
+ "integrity": "sha512-lGN7WeO4vO6sICVpf041Q7BX/9k1Y24Zo3FY0aUezr1QlKznpjzsDk3T3wvH8ofYzoK0QupN9TWcFAFZlyPwQQ==",
+ "requires": {
+ "@types/lodash": "*"
+ }
+ },
"@types/node": {
"version": "14.18.33",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.33.tgz",
@@ -10016,6 +10112,11 @@
"react-refresh": "^0.14.0"
}
},
+ "@wojtekmaj/date-utils": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@wojtekmaj/date-utils/-/date-utils-1.5.0.tgz",
+ "integrity": "sha512-0mq88lCND6QiffnSDWp+TbOxzJSwy2V/3XN+HwWZ7S2n19QAgR5dy5hRVhlECXvQIq2r+VcblBu+S9V+yMcxXw=="
+ },
"abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
@@ -10415,6 +10516,11 @@
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
"integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
},
+ "clsx": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz",
+ "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q=="
+ },
"code-block-writer": {
"version": "10.1.1",
"resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-10.1.1.tgz",
@@ -10532,6 +10638,11 @@
"integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==",
"dev": true
},
+ "dayjs": {
+ "version": "1.11.10",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz",
+ "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ=="
+ },
"debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -11487,6 +11598,15 @@
"get-intrinsic": "^1.1.1"
}
},
+ "get-user-locale": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/get-user-locale/-/get-user-locale-2.3.0.tgz",
+ "integrity": "sha512-I3rQvAUwu2nauRD9YyQBSXVFJZixNouwA+eZld51Sn4Pn0N1qFbgcgOi/nPigJPQlNY519mT95fiSPRgflQiTA==",
+ "requires": {
+ "@types/lodash.memoize": "^4.1.7",
+ "lodash.memoize": "^4.1.1"
+ }
+ },
"glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -11964,6 +12084,11 @@
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
},
+ "lodash.memoize": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
+ "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag=="
+ },
"lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@@ -12415,6 +12540,18 @@
"loose-envify": "^1.1.0"
}
},
+ "react-calendar": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/react-calendar/-/react-calendar-4.6.0.tgz",
+ "integrity": "sha512-GJ6ZipKMQmlK666t+0hgmecu6WHydEnMWJjKdEkUxW6F471hiM5DkbWXkfr8wlAg9tc9feNCBhXw3SqsPOm01A==",
+ "requires": {
+ "@wojtekmaj/date-utils": "^1.1.3",
+ "clsx": "^2.0.0",
+ "get-user-locale": "^2.2.1",
+ "prop-types": "^15.6.0",
+ "tiny-warning": "^1.0.0"
+ }
+ },
"react-dom": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
@@ -12941,6 +13078,11 @@
"convert-hrtime": "^3.0.0"
}
},
+ "tiny-warning": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
+ "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
+ },
"to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
diff --git a/package.json b/package.json
index 85623eb..b40f480 100644
--- a/package.json
+++ b/package.json
@@ -14,9 +14,11 @@
"@reduxjs/toolkit": "^1.9.5",
"axios": "^1.5.0",
"classnames": "^2.3.2",
+ "dayjs": "^1.11.10",
"js-cookie": "^3.0.5",
"prop-types": "^15.8.1",
"react": "^18.2.0",
+ "react-calendar": "^4.6.0",
"react-dom": "^18.2.0",
"react-icons": "^4.10.1",
"react-paginate": "^8.2.0",
diff --git a/src/App.jsx b/src/App.jsx
index a054aad..b70176c 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -4,6 +4,7 @@ import SignupPage from "./pages/SignupPage.jsx";
import MyUserPage from './pages/User/MyUserPage.jsx';
import DevelopPage from './pages/DevelopPage.jsx';
import UserUntactReservePage from './pages/User/UserUntactReservePage.jsx';
+import UserReservePage from './pages/User/UserReservePage.jsx';
import styled from "styled-components";
import "./App.scss";
@@ -22,6 +23,7 @@ function App() {
} />
}/>
} />
+ } />
diff --git a/src/assets/icons/iconmajor.png b/src/assets/icons/iconmajor.png
new file mode 100644
index 0000000..3d09d30
Binary files /dev/null and b/src/assets/icons/iconmajor.png differ
diff --git a/src/assets/icons/iconx.png b/src/assets/icons/iconx.png
new file mode 100644
index 0000000..b694cb1
Binary files /dev/null and b/src/assets/icons/iconx.png differ
diff --git a/src/components/Button/BackButton.jsx b/src/components/Button/BackButton.jsx
index f03b392..54f2627 100644
--- a/src/components/Button/BackButton.jsx
+++ b/src/components/Button/BackButton.jsx
@@ -12,7 +12,7 @@ const BackButtonContainer = styled.button`
align-items: center;
justify-content: center;
padding: 0 15px;
- margin-top:60px;
+ margin-top:40px;
margin-bottom: 10px;
margin-left: -580px;
`;
diff --git a/src/components/Calender/Calender.jsx b/src/components/Calender/Calender.jsx
new file mode 100644
index 0000000..b77bca9
--- /dev/null
+++ b/src/components/Calender/Calender.jsx
@@ -0,0 +1,88 @@
+import { useState } from "react";
+import styled from "styled-components";
+import dayjs from 'dayjs';
+import CalenderItem from "../Calender/CalenderItem.jsx";
+import IconLeft from "../../assets/icons/Page-left.png";
+import IconRight from "../../assets/icons/Page-right.png";
+
+const SelectorContainer = styled.div`
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-bottom: 10px;
+`;
+
+const Icon = styled.img`
+ cursor: pointer;
+ margin: 0 10px;
+`;
+
+const MonthLabel = styled.span`
+ font-size: 1.2rem;
+`;
+
+const Container = styled.div`
+ width: 100%;
+ display: grid;
+ grid-template-columns: repeat(7, 50px);
+ grid-gap: 6px;
+ grid-template-rows: repeat(6, 40px);
+ justify-content: center;
+ align-items: center;
+`;
+
+const Calender = () => {
+ const [selectedMonth, setSelectedMonth] = useState(8);
+ const [selectedDate, setSelectedDate] = useState(null);
+
+ const prevMonth = () => {
+ if (selectedMonth > 8) setSelectedMonth(prev => prev - 1);
+ };
+
+ const nextMonth = () => {
+ if (selectedMonth < 11) setSelectedMonth(prev => prev + 1);
+ };
+
+ const daysInCurrentMonth = dayjs([2023, selectedMonth]).daysInMonth();
+ const startDayOfWeek = dayjs([2023, selectedMonth, 1]).day();
+
+ let days = [];
+ for (let i = 1; i <= daysInCurrentMonth; i++) {
+ days.push(i);
+ }
+
+ const paddingDays = startDayOfWeek;
+ for (let i = 0; i < paddingDays; i++) {
+ days.unshift(null);
+ }
+
+ while (days.length < 35) {
+ days.push(null);
+ }
+
+ return (
+ <>
+
+
+ {`2023.${selectedMonth < 9 ? `${selectedMonth }` : selectedMonth }`}
+
+
+
+ {days.map((day, idx) => {
+ let type = day ? "currentMonth" : "paddingDay";
+ return (
+
+ );
+ })}
+
+ >
+ );
+};
+
+export default Calender;
diff --git a/src/components/Calender/CalenderItem.jsx b/src/components/Calender/CalenderItem.jsx
new file mode 100644
index 0000000..c5755f0
--- /dev/null
+++ b/src/components/Calender/CalenderItem.jsx
@@ -0,0 +1,50 @@
+import styled from "styled-components";
+import PropTypes from "prop-types";
+
+const Container = styled.div`
+ width: 50px;
+ height: 40px;
+ aspect-ratio: 1;
+ border-radius: 8px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ background-color: ${({ isSelected }) => (isSelected ? "#f3f1ff" : "transparent")};
+
+ &:hover {
+ background-color: #f3f1ff;
+ }
+ &.prevMonth,
+ &.nextMonth {
+ color: rgba(21, 21, 21, 0.3);
+ }
+`;
+
+const Text = styled.p`
+ font-size: 16px;
+ font-weight: 300;
+`;
+
+const CalenderItem = ({ date, type, isSelected, onSelectDate }) => {
+ const handleDateClick = () => {
+ onSelectDate(date);
+ };
+
+ return (
+
+ {date}
+
+ );
+};
+
+CalenderItem.propTypes = {
+ date: PropTypes.number,
+ type: PropTypes.string.isRequired,
+ isSelected: PropTypes.bool.isRequired,
+ onSelectDate: PropTypes.func.isRequired,
+};
+
+export default CalenderItem;
+
diff --git a/src/components/Input/InputTextLong.jsx b/src/components/Input/InputTextLong.jsx
new file mode 100644
index 0000000..9f713bf
--- /dev/null
+++ b/src/components/Input/InputTextLong.jsx
@@ -0,0 +1,34 @@
+import styled from 'styled-components';
+
+const InputContainer = styled.div`
+ display: flex;
+ flex-direction: column;
+ width: 320px;
+ margin-top:10px;
+`;
+
+
+const Input = styled.input`
+ width: 500px;
+ height: 100px;
+ border-radius: 10px;
+ background-color: #FAFAFA;
+ border: 1px solid #BBBBBB;
+ font-family: 'Spoqa Han Sans Neo', 'sans-serif';
+ padding-left: 0px;
+
+ &:focus {
+ outline: none;
+ }
+`;
+
+function InputTextLong() {
+ return (
+
+
+
+ );
+}
+
+export { Input };
+export default InputTextLong;
\ No newline at end of file
diff --git a/src/components/UntactReserve/UntactReserveModal.jsx b/src/components/UntactReserve/UntactReserveModal.jsx
new file mode 100644
index 0000000..401a70b
--- /dev/null
+++ b/src/components/UntactReserve/UntactReserveModal.jsx
@@ -0,0 +1,151 @@
+import styled from 'styled-components';
+import PropTypes from 'prop-types';
+import XButton from '../../assets/icons/iconx.png';
+import Calendar from '../Calender/Calender.jsx';
+import { useState } from 'react';
+import InputTextLong from '../Input/InputTextLong';
+
+const Overlay = styled.div`
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100vw;
+ height: 100vh;
+ background-color: rgba(0, 0, 0, 0.4);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+`;
+
+const ModalContainer = styled.div`
+ width: 600px;
+ height: 700px;
+ background-color: #FFFFFF;
+ border-radius: 10px;
+ padding: 20px;
+ box-shadow: 0 0 15px rgba(0, 0, 0, 0.2);
+ position: relative;
+`;
+
+const Title = styled.h1`
+ font-size: 28px;
+ font-weight: bold;
+ color: #333;
+ display: inline-block;
+`;
+
+const CloseIcon = styled.img`
+ position: absolute;
+ right: 20px;
+ top: 20px;
+ cursor: pointer;
+ margin-top:10px;
+`;
+
+const Divider = styled.hr`
+ width: 100%;
+ height: 1px;
+ background-color: #d9d9d9;
+ border: none;
+ margin-top: 10px;
+ margin-bottom: 20px;
+`;
+
+const DateText = styled.p`
+ font-size: 16px;
+ margin-top: -10px;
+`;
+
+const ReasonText = styled.p`
+ font-size: 16px;
+ margin-top: 10px;
+`;
+
+const TimeButton = styled.button`
+ width: 90px;
+ height: 40px;
+ border-radius: 10px;
+ border: ${({ status }) => (status === 'selected' ? '1px solid #0064FF' : '1px solid #E8E8E8')};
+ background-color: ${({ status }) => (status === 'available' ? '#FAFAFA' : status === 'selected' ? '#F3F1FF' : '#888888')};
+ color: ${({ status }) => (status === 'closed' ? '#444444' : '#000000')};
+ cursor: ${({ status }) => (status === 'closed' ? 'not-allowed' : 'pointer')};
+ margin-right: 5px;
+
+ &:last-child {
+ margin-right: 0;
+ }
+`;
+
+const TimeButtonContainer = styled.div`
+ display: flex;
+ margin-top: 20px;
+ margin-left:8px;
+ gap:5px;
+`;
+
+const Button = styled.button`
+ width: 140px;
+ height: 30px;
+ background-color: #3592FF;
+ font-weight: 300;
+ color: #FEFDFD;
+ font-size: 14px;
+ border: none;
+ border-radius: 5px;
+ cursor: pointer;
+ margin-top:20px;
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+`;
+
+
+export const UntactReserveModal = ({ onClose }) => {
+ const [selectedTime, setSelectedTime] = useState(null);
+
+ const times = [
+ { label: "18:00", status: "available" },
+ { label: "18:30", status: "available" },
+ { label: "19:00", status: "available" },
+ { label: "19:30", status: "closed" },
+ { label: "20:00", status: "closed" },
+ ];
+
+ const handleTimeSelect = (time) => {
+ setSelectedTime(time);
+ };
+
+ return (
+
+ e.stopPropagation()}>
+ 예약 정보 작성
+
+
+ 날짜 선택 *
+
+ 시간 선택 *
+
+ {times.map(time => (
+ time.status === 'available' && handleTimeSelect(time.label)}
+ disabled={time.status === 'closed'}
+ >
+ {time.label}
+
+ ))}
+
+ 진료 희망 사유 *
+
+
+
+
+ );
+}
+
+UntactReserveModal.propTypes = {
+ onClose: PropTypes.func.isRequired,
+};
+
+export default UntactReserveModal;
diff --git a/src/components/UserDashBoard/UserDoReserve.jsx b/src/components/UserDashBoard/UserDoReserve.jsx
new file mode 100644
index 0000000..aa681ce
--- /dev/null
+++ b/src/components/UserDashBoard/UserDoReserve.jsx
@@ -0,0 +1,74 @@
+import { useState, useEffect } from "react";
+import styled from "styled-components";
+import { UserSelectCard } from'./UserSelectCard';
+import { userLogin } from "../../librarys/login-api";
+
+const Container = styled.div`
+ width: 800px;
+ height: 600px;
+ margin: 0 auto;
+ padding: 20px;
+ border: 1px solid #0064ff;
+ border-radius: 10px;
+ background-color: #ffffff;
+ font-family: "Spoqa Han Sans Neo", "sans-serif";
+ position: relative;
+`;
+
+const CardWrapper = styled.div`
+ display: flex;
+ justify-content: center;
+ margin-top: 20px;
+ gap: 30px;
+`;
+
+const Title = styled.h1`
+ font-size: 28px;
+ font-weight: bold;
+ color: #333;
+ display: inline-block;
+`;
+
+const Divider = styled.hr`
+ width: 100%;
+ height: 1px;
+ background-color: #d9d9d9;
+ border: none;
+ margin-top: 10px;
+ margin-bottom: 20px;
+`;
+
+const Info = styled.p`
+ margin-top: 20px;
+ font-size: 18px;
+ font-weight: 300;
+ color: #000000;
+`
+
+const UserDoReserve = () => {
+ const [loginData, setLoginData] = useState(null);
+
+ useEffect(() => {
+ const fetchLoginData = async () => {
+ const data = await userLogin('HL0001', '123456');
+ setLoginData(data);
+ };
+ fetchLoginData();
+ }, []);
+
+ if (!loginData) return null;
+
+ return (
+
+ 비대면 진료 예약
+
+ 진료를 희망하는 의료진을 선택해주세요.
+
+
+
+
+
+ );
+};
+
+export default UserDoReserve;
\ No newline at end of file
diff --git a/src/components/UserDashBoard/UserSelectCard.jsx b/src/components/UserDashBoard/UserSelectCard.jsx
new file mode 100644
index 0000000..cd192e2
--- /dev/null
+++ b/src/components/UserDashBoard/UserSelectCard.jsx
@@ -0,0 +1,133 @@
+import { useState } from 'react';
+import PropTypes from 'prop-types';
+import styled from 'styled-components';
+import Icondoctor from "../../assets/icons/icondoctor.png";
+import Iconmajor from "../../assets/icons/iconmajor.png";
+import Iconhospital from "../../assets/icons/iconhospital.png";
+import DoctorImage from "../../assets/images/user/Odoctor.png";
+import TherapistImage from "../../assets/images/user/Otherapist.png";
+import { UntactReserveModal } from "../UntactReserve/UntactReserveModal";
+
+
+const Card = styled.div`
+ width: 280px;
+ border: 1px solid #e1e1e1;
+ padding: 20px;
+ border-radius: 10px;
+ box-shadow: 0 4px 8px rgba(0,0,0,0.1);
+ text-align: center;
+ margin: 20px;
+ font-family: 'Spoqa Han Sans Neo', 'sans-serif';
+ background-color: #ffffff;
+`;
+
+const Title = styled.h1`
+ font-size: 14px;
+ margin-bottom: 15px;
+`;
+
+const Separator = styled.hr`
+ width: 100%;
+ height: 2px;
+ background-color: #D9D9D9;
+ margin-bottom: 15px;
+ margin-top:10px;
+`;
+
+const ImageContainer = styled.div`
+ width: 96px;
+ height: 96px;
+ border-radius: 50%;
+ margin: 0 auto;
+ margin-bottom: 20px;
+ overflow: hidden;
+`;
+
+const Avatar = styled.img`
+ width: 100%;
+ height: 100%;
+`;
+
+const UserName = styled.span`
+ font-size: 30px;
+ font-weight: bold;
+ font-family: 'Spoqa Han Sans Neo', 'sans-serif';
+`;
+
+
+const Info = styled.div`
+ font-family: 'Spoqa Han Sans Neo', 'sans-serif';
+ font-size: 12px;
+ margin-bottom: 10px;
+ text-align: left;
+`;
+
+const Button = styled.button`
+ width: 150px;
+ height: 24px;
+ border-radius: 10px;
+ background-color: #3592FF;
+ color: #FEFDFD;
+ cursor: pointer;
+ border: none;
+ align-self: center;
+ font-size: 12px;
+`;
+
+const Icon = styled.img`
+ height: 12px;
+ width: 12px;
+ margin-right: 5px;
+ vertical-align: middle;
+`;
+
+const MidSection = styled.div`
+ background-color: rgba(0, 100, 255, 0.03);
+`;
+
+
+export const UserSelectCard = ({ userType, userData }) => {
+ const [isModalOpen, setIsModalOpen] = useState(false);
+
+ const toggleModal = () => {
+ setIsModalOpen(!isModalOpen);
+ };
+
+ if (!userData) return null;
+
+ const imageUrl = userType === "admin1" ? DoctorImage : TherapistImage;
+ const title = userType === "admin1" ? "담당 전문의 프로필" : "담당 재활치료사 프로필";
+
+ return (
+ <>
+
+ {title}
+
+
+
+
+
+ {userData.name}
+
+
+ {userType === "admin1" ? "전문의" : "재활치료사"}
+ {userData.workplace}
+ {userData.major}
+
+
+
+ {isModalOpen && }
+ >
+ );
+}
+
+UserSelectCard.propTypes = {
+ userType: PropTypes.string.isRequired,
+ userData: PropTypes.shape({
+ name: PropTypes.string,
+ workplace: PropTypes.string,
+ major: PropTypes.string,
+ }).isRequired,
+};
+
+export default UserSelectCard;
\ No newline at end of file
diff --git a/src/librarys/context.js b/src/librarys/context.js
new file mode 100644
index 0000000..3ddd569
--- /dev/null
+++ b/src/librarys/context.js
@@ -0,0 +1,4 @@
+import { createContext } from "react";
+
+export const StateContext = createContext();
+export const DispatchContext = createContext();
\ No newline at end of file
diff --git a/src/librarys/login-api.js b/src/librarys/login-api.js
index 845d23a..9a9868f 100644
--- a/src/librarys/login-api.js
+++ b/src/librarys/login-api.js
@@ -1,87 +1,102 @@
export async function userLogin(id, password) {
const accounts = [
- {
- // User: 환자
- type: "user",
- id: "HL0001",
- password: "123456",
- name: "오소현",
- admin: false,
- assignedDoctor: "김정원", // 배정된 전문의
- assignedTherapist: "오민혁", // 배정된 재활치료사
- recentVisitDate: "2023.09.01", // 최근 외래 진료일
- nextReservationDate: "2023.09.11" // 다음 외래 예약일
- },
- // Admin1: 전문의
- {
- type: "admin1",
- id: "doctor",
- password: "123456",
- name: "김정원",
- admin: true,
- },
- // Admin2: 재활치료사
- {
- type: "admin2",
- id: "therapist",
- password: "123456",
- name: "오민혁",
- admin: true,
- },
+ {
+ // User: 환자
+ type: "user",
+ id: "HL0001",
+ password: "123456",
+ name: "오소현",
+ admin: false,
+ assignedDoctor: "김정원", // 배정된 전문의
+ assignedTherapist: "오민혁", // 배정된 재활치료사
+ recentVisitDate: "2023.09.01", // 최근 외래 진료일
+ nextReservationDate: "2023.09.11", // 다음 외래 예약일
+ },
+ // Admin1: 전문의
+ {
+ type: "admin1",
+ id: "doctor",
+ password: "123456",
+ name: "김정원",
+ major: "재활의학과",
+ workplace: "한림대학교 춘천성심병원",
+ admin: true,
+ },
+ // Admin2: 재활치료사
+ {
+ type: "admin2",
+ id: "therapist",
+ password: "123456",
+ name: "오민혁",
+ major: "팔 재활",
+ workplace: "한림대학교 춘천성심병원 재활의료센터",
+ admin: true,
+ },
];
const account = accounts.find((item) => item.id === id);
+
if (!account || account.password !== password) {
- return null;
+ return null;
}
- // 계정 유형에 따른 다른 반환 값
- switch (account.type) {
- case "user":
- return {
- email: account.id,
- name: account.name,
- access_token: "user_token1",
- refresh_token: "user_token2",
- admin: account.admin,
- assignedDoctor: account.assignedDoctor,
- assignedTherapist: account.assignedTherapist,
- recentVisitDate: account.recentVisitDate,
- nextReservationDate: account.nextReservationDate
- };
- case "admin1":
- return {
- email: account.id,
- name: account.name,
- access_token: "admin1_token1",
- refresh_token: "admin1_token2",
- admin: account.admin,
- };
- case "admin2":
- return {
- email: account.id,
- name: account.name,
- access_token: "admin2_token1",
- refresh_token: "admin2_token2",
- admin: account.admin,
- };
- default:
- return null;
+
+ if (account.type === "user") {
+ const doctor = accounts.find(item => item.name === account.assignedDoctor && item.type === "admin1");
+ const therapist = accounts.find(item => item.name === account.assignedTherapist && item.type === "admin2");
+
+ return {
+ user: {
+ email: account.id,
+ name: account.name,
+ access_token: "user_token1",
+ refresh_token: "user_token2",
+ admin: account.admin,
+ assignedDoctor: account.assignedDoctor,
+ assignedTherapist: account.assignedTherapist,
+ recentVisitDate: account.recentVisitDate,
+ nextReservationDate: account.nextReservationDate,
+ },
+ doctor,
+ therapist,
+ };
+ } else if (account.type === "admin1") {
+ return {
+ email: account.id,
+ name: account.name,
+ major: account.major,
+ workplace: account.workplace,
+ access_token: "admin1_token1",
+ refresh_token: "admin1_token2",
+ admin: account.admin,
+ };
+ } else if (account.type === "admin2") {
+ return {
+ email: account.id,
+ name: account.name,
+ major: account.major,
+ workplace: account.workplace,
+ access_token: "admin2_token1",
+ refresh_token: "admin2_token2",
+ admin: account.admin,
+ };
}
+
+ return null;
}
// 임시로 환자에게 할당된 과제 데이터를 추가
const userExercises = {
- "HL0001": [
- { id: "1", name: "팔 운동 1", accuracy: "80%", judgement: "합격" },
- { id: "2", name: "팔 운동 2", accuracy: "80%", judgement: "합격" },
- { id: "3", name: "팔 운동 3", accuracy: "40%", judgement: "불합격" },
- { id: "4", name: "팔 운동 4", accuracy: "0%", judgement: "미수강" },
- { id: "5", name: "팔 운동 5", accuracy: "0%", judgement: "미수강" },
- { id: "6", name: "팔 운동 6", accuracy: "0%", judgement: "미수강" },
- { id: "7", name: "팔 운동 7", accuracy: "0%", judgement: "미수강" },
- { id: "8", name: "팔 운동 8", accuracy: "0%", judgement: "미수강" }
- ]
+ HL0001: [
+ { id: "1", name: "팔 운동 1", accuracy: "80%", judgement: "합격" },
+ { id: "2", name: "팔 운동 2", accuracy: "80%", judgement: "합격" },
+ { id: "3", name: "팔 운동 3", accuracy: "40%", judgement: "불합격" },
+ { id: "4", name: "팔 운동 4", accuracy: "0%", judgement: "미수강" },
+ { id: "5", name: "팔 운동 5", accuracy: "0%", judgement: "미수강" },
+ { id: "6", name: "팔 운동 6", accuracy: "0%", judgement: "미수강" },
+ { id: "7", name: "팔 운동 7", accuracy: "0%", judgement: "미수강" },
+ { id: "8", name: "팔 운동 8", accuracy: "0%", judgement: "미수강" },
+ ],
};
// 환자의 ID를 입력받아 해당 환자에게 할당된 과제 데이터를 반환하는 함수
@@ -91,18 +106,20 @@ export function getUserExercises(userId) {
// 기존 login-api.js 파일에 비대면 진료 기록 추가
const userUntactRecords = {
- "HL0001": [
+ HL0001: [
{
date: "2023.08.31",
doctorName: "김정원 전문의",
- record: "환자는 3일 전부터 갑자기 시작된 기침과 가래를 주소로 내원하였음. 체온 측정 결과 38.2도로 발열 증상도 있음. 오늘 진료를 통해 급성 기관지염으로 진단하고, 관련 약물 처방하였음. 1주 후 재진을 권장함."
+ record:
+ "환자는 3일 전부터 갑자기 시작된 기침과 가래를 주소로 내원하였음. 체온 측정 결과 38.2도로 발열 증상도 있음. 오늘 진료를 통해 급성 기관지염으로 진단하고, 관련 약물 처방하였음. 1주 후 재진을 권장함.",
},
{
date: "2023.09.22",
doctorName: "오민혁 재활치료사",
- record: "환자는 3일 전부터 갑자기 시작된 기침과 가래를 주소로 내원하였음. 체온 측정 결과 38.2도로 발열 증상도 있음. 오늘 진료를 통해 급성 기관지염으로 진단하고, 관련 약물 처방하였음. 1주 후 재진을 권장함."
- }
- ]
+ record:
+ "환자는 3일 전부터 갑자기 시작된 기침과 가래를 주소로 내원하였음. 체온 측정 결과 38.2도로 발열 증상도 있음. 오늘 진료를 통해 급성 기관지염으로 진단하고, 관련 약물 처방하였음. 1주 후 재진을 권장함.",
+ },
+ ],
};
// 비대면 진료 기록을 반환하는 함수
diff --git a/src/pages/User/UserReservePage.jsx b/src/pages/User/UserReservePage.jsx
new file mode 100644
index 0000000..393c74a
--- /dev/null
+++ b/src/pages/User/UserReservePage.jsx
@@ -0,0 +1,34 @@
+import styled from 'styled-components';
+import Header from '../../components/Header/Header';
+import BackButton from "../../components/Button/BackButton";
+import UserDoReserve from '../../components/UserDashBoard/UserDoReserve';
+
+
+const PageContainer = styled.div`
+ display: flex;
+ flex-direction: column;
+ height: 100vh;
+`;
+
+const CenteredContainer = styled.div`
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ flex: 1;
+`;
+
+
+const UserReservePage = () => {
+ return (
+
+
+
+
+
+
+
+ );
+}
+
+export default UserReservePage;
\ No newline at end of file