diff --git a/package.json b/package.json index 7d48988..e32988f 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "format": "prettier --write --cache ." }, "dependencies": { + "date-fns": "^2.30.0", "eslint-config-airbnb": "^19.0.4", "eslint-plugin-import": "^2.27.5", "eslint-plugin-jsx-a11y": "^6.7.1", @@ -19,9 +20,9 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-query": "^3.39.3", - "react-router-dom": "^6.14.0", + "react-router-dom": "^6.14.1", "recoil": "^0.7.7", - "styled-components": "^6.0.0", + "styled-components": "^6.0.1", "vite-plugin-svgr": "^3.2.0", "vite-tsconfig-paths": "^4.2.0" }, diff --git a/src/components/hyein/.keep b/src/components/hyein/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/src/components/hyein/Calendar.tsx b/src/components/hyein/Calendar.tsx new file mode 100644 index 0000000..cf62759 --- /dev/null +++ b/src/components/hyein/Calendar.tsx @@ -0,0 +1,29 @@ +import React, { useState } from "react"; +import CalendarHeader from "./CalendarHeader"; +import { format, subMonths, addMonths } from "date-fns"; +import Week from "./Week"; +import Days from "./Days"; + +export default function Calendar() { + const [currentMonth, setCurrentMonth] = useState(new Date()); + const [selectedDate, setSelectedDate] = useState(new Date()); + + function prevMonth() { + setCurrentMonth(subMonths(currentMonth, 1)); + } + function nextMonth() { + setCurrentMonth(addMonths(currentMonth, 1)); + } + + function onClickDate(day) { + setSelectedDate(day); + } + + return ( +
+ + + +
+ ); +} diff --git a/src/components/hyein/CalendarHeader.tsx b/src/components/hyein/CalendarHeader.tsx new file mode 100644 index 0000000..515ad71 --- /dev/null +++ b/src/components/hyein/CalendarHeader.tsx @@ -0,0 +1,48 @@ +import React from "react"; +import styled from "styled-components"; +import { format, add, subMonths } from "date-fns"; + +interface CalendarHeaderProps { + currentMonth: Date; + prevMonth: Date; + nextMonth: Date; +} + +export default function CalendarHeader({ currentMonth, prevMonth, nextMonth }: CalendarHeaderProps) { + return ( + + {"<"} + + {format(currentMonth, "yyyy")}년 {format(currentMonth, "MM")}월 + + {">"} + + ); +} + +const HeaderWrapper = styled.header` + display: flex; + justify-content: center; + align-items: center; + font-size: 1.1rem; + gap: 1.6rem; +`; + +const YearMonthWrapper = styled.span` + display: flex; + justify-content: center; + align-items: center; + font-size: 1.1rem; +`; + +const PrevMonthButton = styled.button` + display: flex; + justify-content: center; + align-items: center; +`; + +const NextMonthButton = styled.button` + display: flex; + justify-content: center; + align-items: center; +`; diff --git a/src/components/hyein/Days.tsx b/src/components/hyein/Days.tsx new file mode 100644 index 0000000..4d6fe89 --- /dev/null +++ b/src/components/hyein/Days.tsx @@ -0,0 +1,167 @@ +import React from "react"; +import styled from "styled-components"; +import { + parse, + format, + endOfMonth, + endOfWeek, + startOfMonth, + startOfWeek, + addDays, + isSameDay, + isSunday, +} from "date-fns"; + +import { SCHEDULE_DATA, COLOR_DATA } from "../../mocks/data"; + +interface DaysProps { + currentMonth: Date; + selectedDate: Date; + // onClickDate: (date: Date) => void; +} + +export default function Days({ currentMonth, selectedDate }: DaysProps) { + const monthStart = startOfMonth(currentMonth); + const monthEnd = endOfMonth(monthStart); + const startDate = startOfWeek(monthStart); + const endDate: Date = endOfWeek(monthEnd); + const scheduleData = SCHEDULE_DATA.map((item) => ({ + ...item, + date: item.date.map((data) => parse(data, "yyyy.MM.dd", new Date())), + })); + + const sortedScheduleData = scheduleData.sort((a, b) => a.time.localeCompare(b.time)); + + const rows: React.ReactNode[] = []; + let days: React.ReactNode[] = []; + let day: Date = startDate; + let formattedDate = ""; + + while (day <= endDate) { + for (let i = 0; i < 7; i++) { + formattedDate = format(day, "d"); + const sunDay = isSunday(day); + const daySchedule = sortedScheduleData.filter((item) => item.date.some((date) => isSameDay(date, day))); + + days.push( + + + {formattedDate} + + {daySchedule.map((schedule) => ( + + {schedule.student} {schedule.time.slice(0, 5)} + + ))} + , + ); + day = addDays(day, 1); + } + rows.push({days}); + days = []; + } + + return {rows}; +} + +const Wrapper = styled.section` + display: flex; + align-items: center; + justify-content: center; + margin-top: 5rem; + gap: 2rem; + flex-direction: column; +`; + +const DaysWrapper = styled.article` + display: flex; + align-items: center; + justify-content: space-between; + gap: 2rem; + width: 48rem; + cursor: pointer; +`; + +interface DayProp { + $issunday: boolean; +} + +const Day = styled.article` + display: flex; + align-items: center; + flex-direction: column; + height: 5rem; + width: 30rem; + + ${({ $issunday }) => ` + ${$issunday ? "color: #FCB3A6" : undefined} + `}; +`; + +interface DayTextProps { + $isnotvalid: boolean; + $issameday: boolean; +} + +const DayText = styled.p` + display: flex; + align-items: center; + justify-content: center; + width: 1.2rem; + height: 1.2rem; + + ${({ $isnotvalid, $issameday }) => ` + ${$isnotvalid ? "color: #CED4DA" : ""} + ${$issameday ? "color: white; background-color: #0DA98E; border-radius: 50%; " : ""} + `}; +`; + +interface ScheduleWrapper { + $student: string; +} + +const ScheduleWrapper = styled.p` + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + margin-top: 0.5rem; + + padding: 0.2rem 0; + + ${({ $student }) => { + switch ($student) { + case "지수": + return ` + background-color: #FFD9F2 + `; + break; + + case "희정": + return ` + background-color:#E3D2FA`; + break; + + case "혜인": + return ` + background-color:#D3F1C1`; + break; + + case "성경": + return ` + background-color: #CCF5ED + `; + break; + + case "은빈": + return ` + background-color:#EBDDD5 `; + break; + + default: + return undefined; + } + }} +`; diff --git a/src/components/hyein/Week.tsx b/src/components/hyein/Week.tsx new file mode 100644 index 0000000..a215c4f --- /dev/null +++ b/src/components/hyein/Week.tsx @@ -0,0 +1,26 @@ +import React from "react"; +import styled from "styled-components"; + +export default function Week() { + const DATE: string[] = ["일", "월", "화", "수", "목", "금", "토", "일"]; + + const dateList: JSX.Element[] = DATE.map((day, index) => {day}); + return {dateList}; +} + +const WeekWrapper = styled.section` + display: flex; + justify-content: center; + align-items: center; + font-size: 1rem; + font-weight: 400; + margin-top: 1.8rem; +`; + +const DayWrapper = styled.span` + display: flex; + justify-content: center; + align-items: center; + height: auto; + width: 6rem; +`; diff --git a/src/mocks/.keep b/src/mocks/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/src/mocks/data.ts b/src/mocks/data.ts new file mode 100644 index 0000000..8c236f8 --- /dev/null +++ b/src/mocks/data.ts @@ -0,0 +1,15 @@ +interface ScheduleData { + id: number; + student: string; + time: string; + date: string[]; +} +[]; + +export const SCHEDULE_DATA: ScheduleData[] = [ + { id: 1, student: "지수", time: "13:00~14:00", date: ["2023.07.06", "2023.07.13", "2023.07.20", "2023.07.27"] }, + { id: 2, student: "희정", time: "19:00~20:00", date: ["2023.07.06", "2023.07.16"] }, + { id: 3, student: "혜인", time: "10:00~12:00", date: ["2023.07.06", "2023.07.16"] }, + { id: 4, student: "은빈", time: "22:00~23:00", date: ["2023.07.03", "2023.07.27"] }, + { id: 5, student: "성경", time: "21:00~22:30", date: ["2023.07.15"] }, +]; diff --git a/src/pages/Hyein.tsx b/src/pages/Hyein.tsx index 4b97a8f..c00b324 100644 --- a/src/pages/Hyein.tsx +++ b/src/pages/Hyein.tsx @@ -1,3 +1,5 @@ +import Calendar from "../components/hyein/Calendar"; + export default function Hyein() { - return
Hyein
; + return ; } diff --git a/yarn.lock b/yarn.lock index 3c5a3f2..3705f88 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1047,7 +1047,7 @@ resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime@^7.12.5", "@babel/runtime@^7.20.7", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4": +"@babel/runtime@^7.12.5", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.5.tgz#8564dd588182ce0047d55d7a75e93921107b57ec" integrity sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA== @@ -1329,10 +1329,10 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@remix-run/router@1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.7.0.tgz#550a8d5760b78efc5d60f62b5829b0f74c1fde81" - integrity sha512-Eu1V3kz3mV0wUpVTiFHuaT8UD1gj/0VnoFHQYX35xlslQUpe8CuYoKFn9d4WZFHm3yDywz6ALZuGdnUPKrNeAw== +"@remix-run/router@1.7.1": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.7.1.tgz#fea7ac35ae4014637c130011f59428f618730498" + integrity sha512-bgVQM4ZJ2u2CM8k1ey70o1ePFXsEzYVZoWghh6WjM8p59jQ7HxzbHW4SbnWFG7V9ig9chLawQxDTZ3xzOF8MkQ== "@rollup/pluginutils@^5.0.2": version "5.0.2" @@ -2011,6 +2011,13 @@ damerau-levenshtein@^1.0.8: resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== +date-fns@^2.30.0: + version "2.30.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0" + integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw== + dependencies: + "@babel/runtime" "^7.21.0" + debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" @@ -3491,20 +3498,20 @@ react-refresh@^0.14.0: resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e" integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ== -react-router-dom@^6.14.0: - version "6.14.0" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.14.0.tgz#7ce6c3f73549e4d40216ba150253d3bf30812b33" - integrity sha512-YEwlApKwzMMMbGbhh+Q7MsloTldcwMgHxUY/1g0uA62+B1hZo2jsybCWIDCL8zvIDB1FA0pBKY9chHbZHt+2dQ== +react-router-dom@^6.14.1: + version "6.14.1" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.14.1.tgz#0ad7ba7abdf75baa61169d49f096f0494907a36f" + integrity sha512-ssF6M5UkQjHK70fgukCJyjlda0Dgono2QGwqGvuk7D+EDGHdacEN3Yke2LTMjkrpHuFwBfDFsEjGVXBDmL+bWw== dependencies: - "@remix-run/router" "1.7.0" - react-router "6.14.0" + "@remix-run/router" "1.7.1" + react-router "6.14.1" -react-router@6.14.0: - version "6.14.0" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.14.0.tgz#1c3e8e922d934d43a253fd862c72c82167c0a7f1" - integrity sha512-OD+vkrcGbvlwkspUFDgMzsu1RXwdjNh83YgG/28lBnDzgslhCgxIqoExLlxsfTpIygp7fc+Hd3esloNwzkm2xA== +react-router@6.14.1: + version "6.14.1" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.14.1.tgz#5e82bcdabf21add859dc04b1859f91066b3a5810" + integrity sha512-U4PfgvG55LdvbQjg5Y9QRWyVxIdO1LlpYT7x+tMAxd9/vmiPuJhIwdxZuIQLN/9e3O4KFDHYfR9gzGeYMasW8g== dependencies: - "@remix-run/router" "1.7.0" + "@remix-run/router" "1.7.1" react@^18.2.0: version "18.2.0" @@ -3851,10 +3858,10 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -styled-components@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-6.0.0.tgz#f42abb6bd8a9cfc21f16ddfe00513da939e054b6" - integrity sha512-OPraU50BgQ+cqiXmO9JnJiDP8N7gTB7Pzx9Yk6M+koZuzOvFf+Fd8ff6Qp57OnE65KAYO058IIWj+RWng3Cmiw== +styled-components@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-6.0.1.tgz#f4db3195e5e510ff3cd0909b1a7b103f4440cbbc" + integrity sha512-6VAlf5A9KZJOnX54becRCLnBFSfeqP+q3raTCdPDPFg4HOy7MNlnWFqAq3sHlQVDdZh5jcsDwK06vTU3NMO6yQ== dependencies: "@babel/cli" "^7.21.0" "@babel/core" "^7.21.0"