diff --git a/package.json b/package.json index d6e3e2e..07da1e1 100644 --- a/package.json +++ b/package.json @@ -13,12 +13,14 @@ "dependencies": { "axios": "^1.7.2", "clsx": "^2.1.1", + "date-fns": "^3.6.0", "react": "^18.3.1", "react-calendar": "^5.0.0", "react-dom": "^18.3.1", "react-icons": "^5.2.1", - "react-kakao-maps-sdk": "^1.1.27", "react-router-dom": "^6.24.0", + "swiper": "^11.1.4", + "react-kakao-maps-sdk": "^1.1.27" "react-signature-canvas": "^1.0.6" }, "devDependencies": { diff --git a/src/App.tsx b/src/App.tsx index 11052a2..a0ef5bc 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,6 +1,11 @@ import { Route, Routes } from 'react-router-dom'; import './App.css'; import Test from './pages/test'; +import UiTest from './pages/UiTest'; +import OwnerMainPage from './pages/OwnerMainPage'; +import CalendarCustom from './components/ui/CalendarCustom'; +import DateDetail from './components/calendar-owner/DateDetail'; +import AttendanceEdit from './components/calendar-owner/AttendanceEdit'; import PaymentMain from './pages/employee/PartTimeTab/PaymentMain'; import WorkTime from './pages/employee/PartTimeTab/WorkTime'; import Payment from './pages/employee/PartTimeTab/Payment'; @@ -21,6 +26,15 @@ function App() { } /> } /> + } /> + }> + } /> + } /> + } + /> + } /> }> } /> diff --git a/src/components/calendar-owner/AttendanceEdit.tsx b/src/components/calendar-owner/AttendanceEdit.tsx new file mode 100644 index 0000000..7cb6d6b --- /dev/null +++ b/src/components/calendar-owner/AttendanceEdit.tsx @@ -0,0 +1,71 @@ +import { useState } from 'react'; +import { useAttendance } from '../../contexts/Attendance-Context'; +import InputBox from '../ui/InputBox'; +import { VStack } from '../ui/Stack'; +import TimeBox from './TimeBox'; +import WorkPlaceName from '../ui/WorkPlaceName'; +import SelectOptions from './SelectOptions'; +import Select from './Select'; + +const mockData = [ + { + workPlaceId: 1, // Long + workPlaceNm: '롯데리아 자양점', + workPlaceColor: '01', // String + workPlaceEmployeeId: 1, // Long + employeeNm: '이신광', // String + }, + { + workPlaceId: 1, // Long + workPlaceNm: '롯데리아 자양점', + workPlaceColor: '01', // String + workPlaceEmployeeId: 2, // Long + employeeNm: '이서하', // String + }, + { + workPlaceId: 1, // Long + workPlaceNm: '롯데리아 자양점', + workPlaceColor: '01', + workPlaceEmployeeId: 3, // Long + employeeNm: '정연주', // String + }, + { + workPlaceId: 2, // Long + workPlaceNm: '버거킹', + workPlaceColor: '02', // String + workPlaceEmployeeId: 4, // Long + employeeNm: '고영우', // String + }, + { + workPlaceId: 2, // Long + workPlaceNm: '버거킹', + workPlaceColor: '02', // String + workPlaceEmployeeId: 5, // Long + employeeNm: '최은진', // String + }, +]; + +const AttendanceEdit = () => { + const [workEmployeeList, setWorkEmployeeList] = useState(mockData); + + const { attendance, changeAttendance } = useAttendance(); + const [employeeId, setEmployeeId] = useState(attendance?.workPlaceEmployeeId); + const [startTime, setStartTime] = useState(attendance?.startTime); + const [endTime, setEndTime] = useState(attendance?.endTime); + const [payPerHour, setPayPerHour] = useState(attendance?.payPerHour); + + console.log(attendance); + + return ( + + + { + return ( + <> +
+ {options.map((option) => { + return ( + + `bg-white px-8 py-3 ${isActive ? 'text-hanaLightGreen border-b-2 border-hanaLightGreen' : ''}` + } + key={option.idx} + to={option.url} + > + {option.title} + + ); + })} +
+ + ); +}; + +export default ToolBarLink; diff --git a/src/components/ui/WorkPlaceName.tsx b/src/components/ui/WorkPlaceName.tsx index 8ea51e8..10cd5b0 100644 --- a/src/components/ui/WorkPlaceName.tsx +++ b/src/components/ui/WorkPlaceName.tsx @@ -7,7 +7,9 @@ type WorkPlaceNameProps = { const WorkPlaceName = ({ name, colorType }: WorkPlaceNameProps) => { return ( - + {name} ); diff --git a/src/contexts/Attendance-Context.tsx b/src/contexts/Attendance-Context.tsx new file mode 100644 index 0000000..a3bb2dd --- /dev/null +++ b/src/contexts/Attendance-Context.tsx @@ -0,0 +1,48 @@ +import { + PropsWithChildren, + createContext, + useContext, + useReducer, +} from 'react'; +import { Attendance } from '../types/calendar'; + +type AttendanceContextProps = { + attendance: Attendance | undefined; + changeAttendance: (attendance: Attendance) => void; +}; + +const AttendanceContext = createContext({ + attendance: undefined, + changeAttendance: () => {}, +}); + +type ReducerAction = { type: 'CHANGE'; data: Attendance }; + +const reducer = (attendance: Attendance | undefined, action: ReducerAction) => { + switch (action.type) { + case 'CHANGE': + return { ...action.data }; + default: + return attendance; + } +}; + +export const AttendanceProvider = ({ children }: PropsWithChildren) => { + const [attendance, dispatch] = useReducer(reducer, undefined); + + const changeAttendance = (attendance: Attendance) => { + dispatch({ + type: 'CHANGE', + data: attendance, + }); + }; + + return ( + + {children} + + ); +}; + +// eslint-disable-next-line react-refresh/only-export-components +export const useAttendance = () => useContext(AttendanceContext); diff --git a/src/contexts/Calender-Data-Context.tsx b/src/contexts/Calender-Data-Context.tsx new file mode 100644 index 0000000..f05faf4 --- /dev/null +++ b/src/contexts/Calender-Data-Context.tsx @@ -0,0 +1,118 @@ +import { PropsWithChildren, createContext, useContext, useState } from 'react'; +import { CalendarSalaryType, DateWorkDetail } from '../types/calendar'; + +const mockData = [ + { + workPlaceId: 1, + workPlaceName: '롯데리아', + workPlaceColor: '01', + days: [ + { + attendanceId: 2453, + workPlaceEmployeeId: 10, + employeeName: '이신광', + attendanceType: 'real', // expect, real + payPerHour: 10000, + startTime: new Date('2024-06-26T10:30:00'), + endTime: new Date('2024-06-26T14:30:00'), + }, + { + attendanceId: 2460, + workPlaceEmployeeId: 10, + employeeName: '이신광', + attendanceType: 'expect', // expect, real + payPerHour: 10000, + startTime: new Date('2024-06-30T10:30:00'), + endTime: new Date('2024-06-30T18:30:00'), + }, + { + attendanceId: 2461, + workPlaceEmployeeId: 12, + employeeName: '이서하', + attendanceType: 'expect', // expect, real + payPerHour: 12000, + startTime: new Date('2024-06-30T08:30:00'), + endTime: new Date('2024-06-30T18:30:00'), + }, + { + attendanceId: 2991, + workPlaceEmployeeId: 12, + employeeName: '이서하', + attendanceType: 'real', // expect, real + payPerHour: 9910, + startTime: new Date('2024-06-28T09:30:00'), + endTime: new Date('2024-06-28T18:30:00'), + }, + ], + }, + { + workPlaceId: 2, + workPlaceName: '버거킹', + workPlaceColor: '02', + days: [ + { + attendanceId: 1999, + workPlaceEmployeeId: 15, + employeeName: '정연주', + attendanceType: 'real', // expect, real + payPerHour: 10000, + startTime: new Date('2024-06-26T10:30:00'), + endTime: new Date('2024-06-26T14:30:00'), + }, + { + attendanceId: 2464, + workPlaceEmployeeId: 18, + employeeName: '최은진', + attendanceType: 'expect', // expect, real + payPerHour: 12000, + startTime: new Date('2024-06-29T09:30:00'), + endTime: new Date('2024-06-29T18:30:00'), + }, + { + attendanceId: 2479, + workPlaceEmployeeId: 18, + employeeName: '최은진', + attendanceType: 'expect', // expect, real + payPerHour: 12000, + startTime: new Date('2024-06-30T09:30:00'), + endTime: new Date('2024-06-30T18:30:00'), + }, + ], + }, +]; + +type CalendarDataContextProps = { + calendarData: CalendarSalaryType[]; + getFilteredData: (date: Date) => DateWorkDetail[]; +}; + +const CalendarDataContext = createContext([]); + +export const CalendarDataProvider = ({ children }: PropsWithChildren) => { + const [calendarData, setCalendarData] = useState(mockData); + + const getFilteredData = (date: Date) => { + const filteredData: DateWorkDetail[] = []; + calendarData.forEach((data) => { + data.days.forEach((day) => { + if (new Date(day.startTime).toDateString() === date.toDateString()) { + filteredData.push({ + ...day, + workPlaceName: data.workPlaceName, + workPlaceColor: data.workPlaceColor, + }); + } + }); + }); + return filteredData; + }; + + return ( + + {children} + + ); +}; + +// eslint-disable-next-line react-refresh/only-export-components +export const useCalendarData = () => useContext(CalendarDataContext); diff --git a/src/pages/OwnerMainPage.tsx b/src/pages/OwnerMainPage.tsx index ae7289f..9f9622a 100644 --- a/src/pages/OwnerMainPage.tsx +++ b/src/pages/OwnerMainPage.tsx @@ -1,25 +1,24 @@ -import { useState } from 'react'; import Nav from '../components/Nav'; -import CalendarCustom from '../components/ui/CalendarCustom'; -import ToolBar from '../components/ui/ToolBar'; +import { Outlet } from 'react-router-dom'; +import ToolBarLink from '../components/ui/ToolBarLink'; +import { CalendarDataProvider } from '../contexts/Calender-Data-Context'; +import { AttendanceProvider } from '../contexts/Attendance-Context'; -const ownerOptions = ['나의 사업장', '캘린더']; +const ownerOptions = [ + { idx: 0, title: '나의 사업장', url: 'myList' }, + { idx: 1, title: '캘린더', url: 'calendar' }, +]; const OwnerMainPage = () => { - const [selected, setSelected] = useState(0); - const onClickSelected = (idx: number) => { - setSelected(idx); - }; - return (
- - {selected == 1 && } + + + + + +
); }; diff --git a/src/types/calendar.d.ts b/src/types/calendar.d.ts index 04333f7..0bdf2fa 100644 --- a/src/types/calendar.d.ts +++ b/src/types/calendar.d.ts @@ -4,13 +4,35 @@ export type CalendarDayType = { employeeName: string; attendanceType: string; payPerHour: number; - startTime: string; - endTime: string; + startTime: Date; + endTime: Date; }; export type CalendarSalaryType = { - workPlace: number; + workPlaceId: number; workPlaceName: string; workPlaceColor: string; - day: CalendarDayType[]; + days: CalendarDayType[]; +}; + +export type DateWorkDetail = { + workPlaceName: string; + workPlaceColor: string; + attendanceId: number; + workPlaceEmployeeId: number; + employeeName: string; + attendanceType: string; + payPerHour: number; + startTime: Date; + endTime: Date; +}; + +export type Attendance = { + attendanceId?: number; + workPlaceEmployeeId: number; + payPerHour: number; // 시급 + startTime: Date; // 시작 시간 + endTime: Date; // 끝 시간 + restStartTime: Date; + restEndTime: Date; }; diff --git a/src/utils/get-TimeString.ts b/src/utils/get-TimeString.ts new file mode 100644 index 0000000..23f8ae0 --- /dev/null +++ b/src/utils/get-TimeString.ts @@ -0,0 +1,7 @@ +export const getTimeString = (date: Date) => { + return date.toLocaleTimeString([], { + hour: '2-digit', + minute: '2-digit', + hour12: false, + }); +}; diff --git a/yarn.lock b/yarn.lock index 98705c8..67ca52c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -963,6 +963,11 @@ data-view-byte-offset@^1.0.0: es-errors "^1.3.0" is-data-view "^1.0.1" +date-fns@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-3.6.0.tgz#f20ca4fe94f8b754951b24240676e8618c0206bf" + integrity sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww== + debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: version "4.3.5" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" @@ -2837,6 +2842,11 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +swiper@^11.1.4: + version "11.1.4" + resolved "https://registry.yarnpkg.com/swiper/-/swiper-11.1.4.tgz#2f8e303e8bf9e5bc40a3885fc637ae60ff27996c" + integrity sha512-1n7kbYJB2dFEpUHRFszq7gys/ofIBrMNibwTiMvPHwneKND/t9kImnHt6CfGPScMHgI+dWMbGTycCKGMoOO1KA== + synckit@^0.8.6: version "0.8.8" resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.8.tgz#fe7fe446518e3d3d49f5e429f443cf08b6edfcd7"