Skip to content

Commit

Permalink
feat/#24 Make calendar for owner
Browse files Browse the repository at this point in the history
  • Loading branch information
ShinKwang2 committed Jun 28, 2024
1 parent c28d51c commit 2cbcaeb
Show file tree
Hide file tree
Showing 14 changed files with 388 additions and 14 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@
"axios": "^1.7.2",
"clsx": "^2.1.1",
"react": "^18.3.1",
"react-calendar": "^5.0.0",
"react-dom": "^18.3.1",
"react-icons": "^5.2.1",
"react-router-dom": "^6.24.0"
},
"devDependencies": {
"@types/react": "^18.3.3",
"@types/react-calendar": "^4.1.0",
"@types/react-dom": "^18.3.0",
"@types/react-router-dom": "^5.3.3",
"@typescript-eslint/eslint-plugin": "^7.13.1",
Expand Down
2 changes: 1 addition & 1 deletion src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
width: 390px;
min-height: 100vh;
/* background-color: #f9f9fb; */
background-color: #f7f7f7;
background-color: white;
}
4 changes: 4 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { Route, Routes } from 'react-router-dom';
import './App.css';
import Test from './pages/test';
import UiTest from './pages/UiTest';
import OwnerCalendarPage from './pages/OwnerMainPage';

function App() {
return (
<>
<Routes>
<Route path='/test' element={<Test />} />
<Route path='/ui' element={<UiTest />} />
<Route path='/owner-calendar' element={<OwnerCalendarPage />} />
</Routes>
</>
);
Expand Down
4 changes: 0 additions & 4 deletions src/components/ui/Calendar.tsx

This file was deleted.

42 changes: 42 additions & 0 deletions src/components/ui/CalendarCustom.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
.react-calendar {
border: none !important;
width: 100% !important;
height: 70% !important;
display: flex;
flex-direction: column;
justify-content: space-around;
}

.react-calendar__navigation {
margin-top: 8px;
}

/* 요일 타일 스타일링 */
.react-calendar__month-view__weekdays {
@apply bg-hanaLightGreen text-center p-1 font-semibold rounded-md;
}

/* 요일 타일 텍스트 스타일링 */
.react-calendar__month-view__weekdays__weekday {
@apply text-sm text-white;
}

/* 날짜 타일 기본 스타일링 */
.react-calendar__tile {
@apply text-center p-2 m-1 hover:bg-blue-100 flex flex-col border border-b-2;
border-bottom: 2px solid #ccc !important;
}

/* 오늘 날짜 타일 스타일링 */
/* .react-calendar__tile--now {
@apply !bg-blue-300 !text-white;
} */

/* 선택된 날짜 타일 스타일링 .react-calendar__tile--active {
@apply !bg-yellow-100 !text-white;
} */

/* 선택할 수 없는 날짜 타일 스타일링 */
.react-calendar__tile--disabled {
@apply text-gray-400;
}
155 changes: 155 additions & 0 deletions src/components/ui/CalendarCustom.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import { useState } from 'react';
import './CalendarCustom.css';
import Calendar, { OnArgs } from 'react-calendar';
import 'react-calendar/dist/Calendar.css';
import CalendarMark from './CalendarMark';

const mockData = [
{
workPlaceId: 1,
workPlaceName: '롯데리아',
workPlaceColor: '01',
days: [
{
attendanceId: 2453,
workPlaceEmployeeId: 10,
employeeName: '이신광',
attendanceType: 'real', // expect, real
payPerHour: 10000,
startTime: '2024-06-26T10:30:00',
endTime: '2024-06-26T14:30:00',
},
{
attendanceId: 2460,
workPlaceEmployeeId: 10,
employeeName: '이신광',
attendanceType: 'expect', // expect, real
payPerHour: 10000,
startTime: '2024-06-30T09:30:00',
endTime: '2024-06-30T18:30:00',
},
{
attendanceId: 2461,
workPlaceEmployeeId: 12,
employeeName: '이서하',
attendanceType: 'expect', // expect, real
payPerHour: 12000,
startTime: '2024-06-30T09:30:00',
endTime: '2024-06-30T18:30:00',
},
{
attendanceId: 2991,
workPlaceEmployeeId: 12,
employeeName: '이서하',
attendanceType: 'real', // expect, real
payPerHour: 9910,
startTime: '2024-06-28T09:30:00',
endTime: '2024-06-28T18:30:00',
},
],
},
{
workPlaceId: 2,
workPalceName: '버거킹',
workPlaceColor: '02',
days: [
{
attendanceId: 1999,
workPlaceEmployeeId: 15,
employeeName: '정연주',
attendanceType: 'real', // expect, real
payPerHour: 10000,
startTime: '2024-06-26T10:30:00',
endTime: '2024-06-26T14:30:00',
},
{
attendanceId: 2464,
workPlaceEmployeeId: 18,
employeeName: '최은진',
attendanceType: 'expect', // expect, real
payPerHour: 12000,
startTime: '2024-06-29T09:30:00',
endTime: '2024-06-29T18:30:00',
},
{
attendanceId: 2479,
workPlaceEmployeeId: 18,
employeeName: '최은진',
attendanceType: 'expect', // expect, real
payPerHour: 12000,
startTime: '2024-06-30T09:30:00',
endTime: '2024-06-30T18:30:00',
},
],
},
];

type ValuePiece = Date | null;
type Value = ValuePiece | [ValuePiece, ValuePiece];

const CalendarCustom = () => {
const [value, setValue] = useState<Value>(new Date());
// const [value, setValue] = useState<Date>(new Date());
const [data, setData] = useState(mockData);

const onChangeCurrentDate = (args: OnArgs) => {
const { action, activeStartDate, value, view } = args;
console.log('🚀 activeStartDate:', activeStartDate);
console.log('🚀 view:', view);
console.log('🚀 value:', value);
console.log('🚀 action:', action);
setValue(activeStartDate!);
};

const getEventsForDate = (date: Date) => {
const events = [];
data.forEach((workPlace) => {
workPlace.days.forEach((day) => {
if (new Date(day.startTime).toDateString() === date.toDateString()) {
events.push({
...day,
workPlaceName: workPlace.workPalceName,
workPlaceColor: workPlace.workPlaceColor,
});
}
});
});
return events;
};

// 날짜 타일에 맞춤 콘텐츠를 추가한다.
const tileContent = ({ date, view }: { date: Date; view: string }) => {
if (view === 'month') {
const events = getEventsForDate(date);
return (
<div className='h-full w-full'>
{events.map((event) => (
<CalendarMark key={event.attendanceId} {...event} />
// <div
// key={event.attendanceId}
// className={`text-xs p-1 rounded-md mb-1 bg-color-${event.workPlaceColor}`}
// title={`${event.employeeName} - ${event.workPlaceName}`}
// >
// {event.employeeName} - {event.workPlaceName}
// </div>
))}
</div>
);
}
};

return (
<div className='rounded-mdw-ful'>
<Calendar
locale='en'
className={'w-full'}
onChange={setValue}
onActiveStartDateChange={onChangeCurrentDate}
value={value}
tileClassName={['h-28']}
tileContent={tileContent}
/>
</div>
);
};
export default CalendarCustom;
30 changes: 30 additions & 0 deletions src/components/ui/CalendarMark.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import calculateDailyWage from '../../utils/get-DailyPay';
import { getColor } from '../../utils/get-color';

type CalendarMarkProps = {
attendanceId: number;
workPlaceEmployeeId: number;
employeeName: string;
attendanceType: string;
payPerHour: number;
startTime: string;
endTime: string;
workPlaceName: string;
workPlaceColor: string;
};

const CalendarMark = (props: CalendarMarkProps) => {
const { employeeName, workPlaceColor, payPerHour, startTime, endTime } =
props;

const total = calculateDailyWage(startTime, endTime, payPerHour);

return (
<div
className={`px-1 rounded-lg text-nowrap text-xs ${getColor(workPlaceColor)}`}
style={{ minWidth: 'fit-content' }}
>{`${total}`}</div>
);
};

export default CalendarMark;
18 changes: 12 additions & 6 deletions src/components/ui/ToolBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,19 @@ const mockData = ['나의 사업장', '캘린더'];

type ToolBarProps = {
options?: string[];
selected: number;
onClickSelected: (idx: number) => void;
};

const ToolBar = ({ options = mockData }: ToolBarProps) => {
const [selected, setSelected] = useState(0);
const onClickSelect = (idx: number) => {
setSelected(idx);
};
const ToolBar = ({
options = mockData,
selected,
onClickSelected,
}: ToolBarProps) => {
// const [selected, setSelected] = useState(0);
// const onClickSelect = (idx: number) => {
// setSelected(idx);
// };

return (
<>
Expand All @@ -20,7 +26,7 @@ const ToolBar = ({ options = mockData }: ToolBarProps) => {
<button
key={option}
className={`bg-white px-8 py-3 ${idx == selected ? 'border-b-2 border-hanaLightGreen' : ''}`}
onClick={() => onClickSelect(idx)}
onClick={() => onClickSelected(idx)}
disabled={selected == idx}
>
<ToolBarDetail title={option} isSelected={idx == selected} />
Expand Down
8 changes: 8 additions & 0 deletions src/index.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
@tailwind base;
@tailwind components;

@layer utilities {
.text-2xs {
font-size: 0.5rem; /* 8px */
line-height: 0.75rem; /* 12px */
}
}

@tailwind utilities;

:root {
Expand Down
26 changes: 26 additions & 0 deletions src/pages/OwnerMainPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useState } from 'react';
import Nav from '../components/Nav';
import CalendarCustom from '../components/ui/CalendarCustom';
import ToolBar from '../components/ui/ToolBar';

const ownerOptions = ['나의 사업장', '캘린더'];

const OwnerMainPage = () => {
const [selected, setSelected] = useState(0);
const onClickSelected = (idx: number) => {
setSelected(idx);
};

return (
<div>
<Nav title='사장님 ON'></Nav>
<ToolBar
options={ownerOptions}
selected={selected}
onClickSelected={onClickSelected}
/>
{selected == 1 && <CalendarCustom />}
</div>
);
};
export default OwnerMainPage;
16 changes: 16 additions & 0 deletions src/types/calendar.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export type CalendarDayType = {
attendanceId: number;
workPlaceEmployeeId: number;
employeeName: string;
attendanceType: string;
payPerHour: number;
startTime: string;
endTime: string;
};

export type CalendarSalaryType = {
workPlace: number;
workPlaceName: string;
workPlaceColor: string;
day: CalendarDayType[];
};
28 changes: 28 additions & 0 deletions src/utils/get-DailyPay.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const calculateDailyWage = (
startTime: string,
endTime: string,
payPerHour: number
) => {
const start = new Date(startTime);
const end = new Date(endTime);

// 초 부분 단위 0으로 설정하여 무효화
start.setSeconds(0, 0);
end.setSeconds(0, 0);

// 근무 시간을 밀리초 단위로 계산한다.
const millisecondsWorked = end.getTime() - start.getTime();

// 밀리초를 분 단위로 변환한다.
const minutesWorked = millisecondsWorked / 1000 / 60;

// 분당 시급 계산
const payPerMinute = payPerHour / 60;

// 일당을 계산한다.
const dailyWage = minutesWorked * payPerMinute;

return dailyWage.toLocaleString();
};

export default calculateDailyWage;
Loading

0 comments on commit 2cbcaeb

Please sign in to comment.