From bc447343a2b236f1d2e9562ebc55de971cdffed9 Mon Sep 17 00:00:00 2001 From: chobani Date: Sun, 14 Jan 2024 14:13:01 +0100 Subject: [PATCH 1/6] Add. Chart Design --- src/components/common/CustomCalendar.tsx | 42 ++++-------------------- src/pages/statistics/ChartContainer.tsx | 1 + 2 files changed, 7 insertions(+), 36 deletions(-) diff --git a/src/components/common/CustomCalendar.tsx b/src/components/common/CustomCalendar.tsx index f35b36f..74612cc 100755 --- a/src/components/common/CustomCalendar.tsx +++ b/src/components/common/CustomCalendar.tsx @@ -3,6 +3,7 @@ import Spacer from '@/components/common/Spacer'; import { useStatisticView } from '@/hooks/statisticView'; import { currentPeriod, currentSelectedDate } from '@/store/common'; import { PeriodTypeForView, currentPeriodForView } from '@/store/statistics'; +import { theme } from '@/styles'; import { Box, Button, Container, Grid, Paper, Typography } from '@mui/material'; import { DatePicker } from '@mui/x-date-pickers'; import dayjs, { Dayjs } from 'dayjs'; @@ -62,9 +63,11 @@ export const CustomCalendar = (pickerProps: any) => { ? 'pink.200' : ''; let color = ''; + const calendarPalette = theme.palette.calendar; switch (currentDay) { case 0: - color = isCurrentMonth ? 'calendar.currSun' : 'calendar.outSun'; + // color = isCurrentMonth ? 'calendar.currSun' : 'calendar.outSun'; + color = isCurrentMonth ? calendarPalette!.currSun : calendarPalette!.outSun; break; case 6: color = isCurrentMonth ? 'calendar.currSat' : 'calendar.outSat'; @@ -128,43 +131,10 @@ export const CustomCalendar = (pickerProps: any) => { * @returns */ const renderToolbar = () => { - const monthSelector = 'BY_MONTH'; - const date = selectedDate[periodType]; const month = date.month() + 1; const year = date.year(); - // const handlePrevClickEvent2 = () => { - // if (periodType === monthSelector) { - // const prevYear = selectedDate[monthSelector].subtract(1, 'year'); - // setSelectedDate({ - // ...selectedDate, - // [monthSelector]: prevYear, - // }); - // } else { - // const prev: HTMLElement = prevBtn.current!; - // prev.querySelector('button')!.click(); - // } - // }; - - // const handleNexClickEvent2 = () => { - // if (periodType === monthSelector) { - // const nextYear = selectedDate[monthSelector].add(1, 'year'); - // setSelectedDate({ - // ...selectedDate, - // [monthSelector]: nextYear, - // }); - // } else { - // const next: HTMLElement = nextBtn.current!; - // next.querySelector('button')!.click(); - // } - // }; - - // const handleSwitchClickEvent2 = () => { - // setSwitchView(() => { - // return switchView === 'BY_MONTH' ? 'BY_YEAR' : 'BY_MONTH'; - // }); - // }; return ( { display: 'flex', flexDirection: 'row', flexWrap: 'wrap', - width: 'inherit', - height: '20vh', + height: '265px', + width: '300px', padding: 0, m: 1, }} diff --git a/src/pages/statistics/ChartContainer.tsx b/src/pages/statistics/ChartContainer.tsx index c4ca2ad..f5931ed 100644 --- a/src/pages/statistics/ChartContainer.tsx +++ b/src/pages/statistics/ChartContainer.tsx @@ -471,6 +471,7 @@ const ChartContainer = () => { ), ); setCategoryDetailData(statisticsResult.filter((data) => data.timeSum > 0)); + setIsFirstPage(true); }, [statisticsResult, periodType]); return ( From 029babdaa4c6668ca0ec53145bc096803c724562 Mon Sep 17 00:00:00 2001 From: choba Date: Mon, 15 Jan 2024 12:16:05 +0100 Subject: [PATCH 2/6] Add. refactoring - Popup Message --- src/App.tsx | 2 + src/components/common/PopupMessage.tsx | 121 ++++++++ src/constants/message.ts | 2 + src/constants/sample.ts | 3 +- src/constants/type.ts | 4 + src/pages/category/EditCategoryPage.tsx | 59 +++- src/pages/record/EditRecordPage.tsx | 382 +++++++++++++----------- src/pages/record/RecordPage.tsx | 20 +- src/store/common.ts | 72 ++++- src/store/popupMessage.ts | 6 + src/store/record.ts | 11 +- src/store/resetPW.ts | 8 +- src/styles/index.ts | 8 +- 13 files changed, 481 insertions(+), 217 deletions(-) create mode 100644 src/components/common/PopupMessage.tsx create mode 100644 src/constants/type.ts create mode 100644 src/store/popupMessage.ts diff --git a/src/App.tsx b/src/App.tsx index e0722e7..1ca251f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -21,6 +21,7 @@ import { theme } from '@/styles'; import ResetPWMode from './pages/auth/resetPW/ResetPWMode'; import ResetPWStep from './pages/auth/resetPW/ResetPWSteps'; import AccountSetting from './pages/settings/AccountSetting'; +import PopupMessage from './components/common/PopupMessage'; function App() { return ( @@ -48,6 +49,7 @@ function App() { + diff --git a/src/components/common/PopupMessage.tsx b/src/components/common/PopupMessage.tsx new file mode 100644 index 0000000..b2c5bd8 --- /dev/null +++ b/src/components/common/PopupMessage.tsx @@ -0,0 +1,121 @@ +/* eslint-disable import/order */ +import { Icon, Typography, Popover, Paper } from '@mui/material'; + +import Spacer from '@/components/common/Spacer'; +import { useRecoilState, useRecoilValue } from 'recoil'; +import { theme } from '@/styles'; +import { currentPopupMessageType, popupMessageText } from '@/store/common'; +import { useEffect, useState } from 'react'; +import { popupShowState } from '@/store/popupMessage'; +import { DEFAULT_POPUP_MSG_TIMEOUT } from '@/constants/sample'; + +interface PopoverVirtualElement { + nodeType: 1; + getBoundingClientRect: () => DOMRect; +} + +const selection = window.getSelection(); + +const PopupMessage = () => { + + const popupText = useRecoilValue(popupMessageText); + const popupMessageType = useRecoilValue(currentPopupMessageType); + + const [isPopupShow, setIsPopupShow] = useRecoilState(popupShowState); + const [anchorEl, setAnchorEl] = useState(null); + + const { background, icon } = theme.palette.popup; + + useEffect(() => { + + setTimeout(() => { + console.log(isPopupShow) + console.log(popupText) + console.log(popupMessageType) + setIsPopupShow(false); + }, DEFAULT_POPUP_MSG_TIMEOUT); + }, []); + + return ( + { + setIsPopupShow(false); + }} + anchorOrigin={{ + // vertical: 'bottom', + // horizontal: 'center', + vertical: 'top', + horizontal: 'center', + }} + transformOrigin={{ + // vertical: 'center', + // horizontal: 'center', + vertical: 'top', + horizontal: 'center', + + }} + > + setIsPopupShow(false)} + sx={{ + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + margin: '0 5vw', + width: '90vw', + height: '8vh', + borderRadius: '15px', + backgroundColor: background, + }} + > + <> + + + + + + + + + + + + + + + + {popupText} + + + + + ); +}; + +export default PopupMessage; diff --git a/src/constants/message.ts b/src/constants/message.ts index 936a98b..5e7c336 100644 --- a/src/constants/message.ts +++ b/src/constants/message.ts @@ -9,4 +9,6 @@ export const buttonText = { next: '다음', confirm: '확인', delete: '삭제하기', + cancel: '취소', + complete: '완료', }; \ No newline at end of file diff --git a/src/constants/sample.ts b/src/constants/sample.ts index 26ce4c9..c357fe3 100644 --- a/src/constants/sample.ts +++ b/src/constants/sample.ts @@ -1 +1,2 @@ -export const DEFAULT_TIME_INTERVAL = 30; \ No newline at end of file +export const DEFAULT_TIME_INTERVAL = 30; +export const DEFAULT_POPUP_MSG_TIMEOUT = 3000; // 3 sec \ No newline at end of file diff --git a/src/constants/type.ts b/src/constants/type.ts new file mode 100644 index 0000000..792a91e --- /dev/null +++ b/src/constants/type.ts @@ -0,0 +1,4 @@ +export const actionType = { + resetPW: 'RESETPW', + signUP: 'SIGNUP', +}; \ No newline at end of file diff --git a/src/pages/category/EditCategoryPage.tsx b/src/pages/category/EditCategoryPage.tsx index 3d6b1dd..7517067 100644 --- a/src/pages/category/EditCategoryPage.tsx +++ b/src/pages/category/EditCategoryPage.tsx @@ -2,7 +2,7 @@ import { Box, Container } from '@mui/system'; import { TextField, Typography } from '@mui/material'; import { useEffect, useState } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; -import { useRecoilState, useRecoilValue } from 'recoil'; +import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; import { addSubCategory, @@ -19,14 +19,23 @@ import { import { MainCategoryProps, SubCategoryProps } from './CategoryPage'; import Wrapper from '../auth/common/Wrapper'; import BottomButton from '@/components/common/BottomButton'; -import { stepButtonProps } from '@/store/common'; +import { + currentPopupMessageType, + popupMessageText, + stepButtonProps, +} from '@/store/common'; import { buttonText } from '@/constants/message'; +import { popupShowState } from '@/store/popupMessage'; const EditCategoryPage = () => { const selectedMainCategory = useRecoilValue(selectedMainCategoryState); const selectedSubCategory = useRecoilValue(selectedSubCategoryState); const [nextButtonProps, setNextButtonProps] = useRecoilState(stepButtonProps); + const setIsPopupShow = useSetRecoilState(popupShowState); + const setPopupText = useSetRecoilState(popupMessageText); + const setPopupMessageType = useSetRecoilState(currentPopupMessageType); + const [inputName, setInputName] = useState(selectedSubCategory.name); const [isInputNameEmpty, setIsInputNameEmpty] = useState(true); const [isInputNameError, setIsInputNameError] = useState(false); @@ -43,25 +52,30 @@ const EditCategoryPage = () => { * handle save event */ const handleSave = async () => { - if (isInputNameEmpty || selectedIcon.iconFile.filename === '') { + const iconFile = selectedIcon.iconFile; + if (isInputNameEmpty || !iconFile.filename) { return true; } + let response; + let popupText: string = ''; if (mode === 'add') { response = await addSubCategory({ mainCategoryId: selectedMainCategory.categoryId, color: selectedMainCategory.color, highlightColor: selectedMainCategory.highlightColor, name: inputName, - iconId: selectedIcon.iconFile.id, + iconId: iconFile.id, }); + popupText = '카테고리가 추가되었습니다.'; } else { + popupText = '카테고리가 수정되었습니다.'; const categoryId = selectedSubCategory.categoryId; if (categoryId) { - const iconName = selectedIcon.iconFile.filename; + const iconName = iconFile.filename; const name = inputName; if ( - iconName === selectedSubCategory.iconFile.filename && + iconName === iconFile.filename && name === selectedSubCategory.name ) { navigation(-1); @@ -69,32 +83,43 @@ const EditCategoryPage = () => { } response = await updateSubCategory({ categoryId, - iconId: selectedIcon.iconFile.id, + iconId: iconFile.id, name, }); } } + if (response?.status === 'SUCCESS') { navigation(-1); } else { - alert('Error'); + popupText = '카테고리 설정에 실패했습니다.'; } + + setPopupMessageType('CATEGORY'); + setIsPopupShow(() => true); + setPopupText(popupText); }; /** * handle delete event */ const handleDelete = async () => { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + let popupText; const response = await deleteCategory(selectedSubCategory.categoryId!); if (response.status === 'SUCCESS') { - alert('Successfully deleted'); + popupText = '카테고리 삭제에 성공했습니다.'; navigation(-1); } else { - alert('Error'); + popupText = '카테고리 삭제에 실패했습니다.'; } + setPopupMessageType('CATEGORY'); + setIsPopupShow(() => true); + setPopupText(popupText); }; + /** + * get category data + */ const getCategoryData = async () => { const response = await getCategories(); if (response.result) { @@ -102,6 +127,10 @@ const EditCategoryPage = () => { } }; + /** + * get category icons + * @returns + */ const getCategoryIcon = () => { if (categories) { const categoryId = selectedIcon?.categoryId; @@ -148,7 +177,7 @@ const EditCategoryPage = () => { isShowNextButton={true} prevButtonIcon={ - 뒤로 + {buttonText.prev} } nextButtonIcon={ @@ -158,7 +187,7 @@ const EditCategoryPage = () => { fontSize: '13px', }} > - 완료 + {buttonText.complete} } onClickNextButton={handleSave} @@ -255,8 +284,8 @@ const EditCategoryPage = () => { {mode === 'edit' && ( )} diff --git a/src/pages/record/EditRecordPage.tsx b/src/pages/record/EditRecordPage.tsx index 91c3070..598126a 100644 --- a/src/pages/record/EditRecordPage.tsx +++ b/src/pages/record/EditRecordPage.tsx @@ -33,7 +33,13 @@ import { } from '@/store/record'; import Spacer from '@/components/common/Spacer'; import BottomButton from '@/components/common/BottomButton'; -import { FormType, currentFormType, stepButtonProps } from '@/store/common'; +import { + FormType, + currentFormType, + currentPopupMessageType, + popupMessageText, + stepButtonProps, +} from '@/store/common'; import dayjs, { Dayjs } from 'dayjs'; import { TimePicker } from 'mui_pickers_6'; import { AdapterDayjs } from 'mui_pickers_6/AdapterDayjs'; @@ -45,6 +51,7 @@ import FlexBox from '@/components/common/FlexBox'; import Wrapper from '../auth/common/Wrapper'; import { theme } from '@/styles'; import { buttonText } from '@/constants/message'; +import { popupShowState } from '@/store/popupMessage'; export interface MainCategoryProps { categoryId: number; @@ -106,12 +113,17 @@ const StyledChip = styled(Chip)(({ theme, props }) => { const formatDate = (date: any): string => dayjs(date).format('YYYY-MM-DDTHH:mm:ss'); +const pink200 = theme.palette.pink![200]; + const EditRecordPage = (): ReactElement => { const navigate = useNavigate(); const currentDay = new Date().getDay(); const setStepType = useSetRecoilState(currentFormType); const setNextButtonProps = useSetRecoilState(stepButtonProps); + const setPopupMessageType = useSetRecoilState(currentPopupMessageType); + const setPopupText = useSetRecoilState(popupMessageText); + const setIsPopupShow = useSetRecoilState(popupShowState); const [categories, setCategories] = useState([]); @@ -119,7 +131,7 @@ const EditRecordPage = (): ReactElement => { // 수정중인 이벤트 const [selectedEvent, setSelectedEvent] = - useRecoilState(currentSelectedEvent); + useRecoilState(currentSelectedEvent); const [selectedDays, setSelectedDays] = useRecoilState(selectedDaysState); const [selectedCategoryIdx, setSelectedCategoryIdx] = useState(0); @@ -343,7 +355,7 @@ const EditRecordPage = (): ReactElement => { m: '15px 0 0 0', }} > - 취소 + {buttonText.cancel} { finishedAt, content, }); - const result = response.result; - return result; + return response.result; } catch (err) { console.error(err); throw new Error(`${err}`); @@ -477,6 +488,7 @@ const EditRecordPage = (): ReactElement => { const currentSelectedDay = dayjs(selectedEvent.start).day(); const firstSelectedDay = selectedDays[0]; + let result; for (const dayIndex of selectedDays) { const diff = dayIndex - firstSelectedDay; const startedAtOfDay = formatDate(new Date(addDays(start, diff))); @@ -484,11 +496,20 @@ const EditRecordPage = (): ReactElement => { // update currently being edited event if (recordType === 'UPDATE' && currentSelectedDay === dayIndex) { - await updateEventRecord(startedAtOfDay, finishedAtOfDay); + result = await updateEventRecord(startedAtOfDay, finishedAtOfDay); } else { - await createEventRecord(startedAtOfDay, finishedAtOfDay); + result = await createEventRecord(startedAtOfDay, finishedAtOfDay); } } + + if (result) { + setIsPopupShow(() => true); + setPopupText( + recordType === 'UPDATE' + ? '기록 수정에 성공했습니다.' + : '기록 등록에 성공했습니다.', + ); + } setSelectedDays([]); navigate('/record'); } @@ -499,12 +520,15 @@ const EditRecordPage = (): ReactElement => { const deleteEventReord = async () => { const eventId = selectedEvent.id; const result = await deleteRecord(eventId); + let popupText = ''; if (result.status === 'SUCCESS') { navigate('/record'); + popupText = '기록 삭제에 성공했습니다.'; } else { - console.error('FAILED TO DELETE'); - // TODO: MODAL + popupText = '기록 삭제에 실패했습니다.'; } + setIsPopupShow(() => true); + setPopupText(popupText); }; /** @@ -525,7 +549,23 @@ const EditRecordPage = (): ReactElement => { } }; - const pink200 = theme.palette.pink![200]; + useEffect(() => { + if (recordType === 'CREATE') { + // setSelectedSubCategoryIdx(0); + } + }, [selectedCategoryIdx]); + + // 선택한 메인카테고리, sub카테고리 recoil에 저장 + useEffect(() => { + if (recordType === 'CREATE') { + // setSelectedSubCategoryIdx(0); + } + setRecoilCategoryValue(selectedCategoryIdx); + }, [selectedCategoryIdx, setRecoilCategoryValue, recoilCategoryValue]); + + useEffect(() => { + setRecoilSubCategoryValue(selectedSubCategoryIdx); + }, [selectedSubCategoryIdx, setRecoilSubCategoryValue, recoilCategoryValue]); useEffect(() => { // set initially selected day @@ -540,6 +580,8 @@ const EditRecordPage = (): ReactElement => { // set form type setStepType(() => 'RECORD'); + setPopupMessageType('RECORD'); + // set button props setNextButtonProps(() => { return { @@ -552,24 +594,6 @@ const EditRecordPage = (): ReactElement => { setContent(selectedEvent.content || ''); }, []); - useEffect(() => { - if (recordType === 'CREATE') { - // setSelectedSubCategoryIdx(0); - } - }, [selectedCategoryIdx]); - - // 선택한 메인카테고리, sub카테고리 recoil에 저장 - useEffect(() => { - if (recordType === 'CREATE') { - // setSelectedSubCategoryIdx(0); - } - setRecoilCategoryValue(selectedCategoryIdx); - }, [selectedCategoryIdx, setRecoilCategoryValue, recoilCategoryValue]); - - useEffect(() => { - setRecoilSubCategoryValue(selectedSubCategoryIdx); - }, [selectedSubCategoryIdx, setRecoilSubCategoryValue, recoilCategoryValue]); - return ( { /> } > - - {/* Selected Days */} + + {/* Selected Days */} + - - - {['일', '월', '화', '수', '목', '금', '토'].map( - (day, dayIndex) => { - const isSelected = selectedDays.includes(dayIndex); - const isCurrentDate = currentDay === dayIndex; - return ( - handleDayChipClick(dayIndex)} - color={ - dayIndex === 0 - ? 'calendar.currSun' - : dayIndex === 6 - ? 'calendar.currDay' - : '' - } - /> - ); - }, - )} - - - - {/* Time Range */} - + - {formatRangeTimeElem('start')} - - - ~ - - - {formatRangeTimeElem('end')} - - - - - - - + {['일', '월', '화', '수', '목', '금', '토'].map((day, dayIndex) => { + const isSelected = selectedDays.includes(dayIndex); + const isCurrentDate = currentDay === dayIndex; + return ( + handleDayChipClick(dayIndex)} + color={ + dayIndex === 0 + ? 'calendar.currSun' + : dayIndex === 6 + ? 'calendar.currDay' + : '' + } + /> + ); + })} + + - {/* Main Categories */} - - {categories.length && - categories.map((category: MainCategoryProps, idx) => { - const isSelected = idx === selectedCategoryIdx; - return ( - { - setSelectedSubCategoryIdx(() => 0); - setSelectedCategoryIdx(idx); - }} - props={{ - isSelected, - backgroundColor: category.highlightColor, - }} - /> - ); - })} - + {/* Time Range */} + + {formatRangeTimeElem('start')} + + + ~ + + + {formatRangeTimeElem('end')} + - + - {/* Sub Categories */} - + + + + {/* Main Categories */} + + {categories.length && + categories.map((category: MainCategoryProps, idx) => { + const isSelected = idx === selectedCategoryIdx; + return ( + { + setSelectedSubCategoryIdx(() => 0); + setSelectedCategoryIdx(idx); + }} + props={{ + isSelected, + backgroundColor: category.highlightColor, + }} + /> + ); + })} + + + + + {/* Sub Categories */} + + {categories[selectedCategoryIdx]?.subCategories.map( + (sub: SubCategoryProps) => { + const subSelected = selectedSubCategoryIdx === sub.categoryId; + return ( + + setSelectedSubCategoryIdx(() => sub.categoryId) + } + /> + ); + }, + )} + + + + + + setContent(e.target.value)} + /> + {recordType === 'UPDATE' && ( { }, }); - setModalInfo({ - open: true, - title: `${userInfo.nickname}님 환영합니다!`, - msg: '두던에서 나만의 시간 기록을 남겨보세요.\n사용 전 모드를 선택 해주세요 :)', - optionList: UserModeList.map((userMode) => { - return { id: userMode.id, type: userMode.type, name: userMode.name }; - }), - isShowConfirmBtn: true, - }); + // setModalInfo({ + // open: true, + // title: `${userInfo.nickname}님 환영합니다!`, + // msg: '두던에서 나만의 시간 기록을 남겨보세요.\n사용 전 모드를 선택 해주세요 :)', + // optionList: UserModeList.map((userMode) => { + // return { id: userMode.id, type: userMode.type, name: userMode.name }; + // }), + // isShowConfirmBtn: true, + // }); }, []); return ( diff --git a/src/store/common.ts b/src/store/common.ts index 24d9127..77b4a63 100644 --- a/src/store/common.ts +++ b/src/store/common.ts @@ -5,14 +5,13 @@ import { signUpStep, } from './signUp'; import { - resetPWNextButtonState, + resetPWCompletePopupMessage, resetPWNextButtonState, resetPWStep, - resetPWStepInstruction, + resetPWStepInstruction } from './resetPW'; import { currentPeriodForRecord, - currentSelectedDateForRecord, - recordEditNextButtonState, + currentSelectedDateForRecord, recordEditNextButtonState } from './record'; import { currentPeriodForStat, @@ -187,3 +186,68 @@ export const currentPeriod = selector({ // } // }, // }); + +/* ============ POPUP MESSAGE (PW Reset Pages / ... ) ============ */ + + +/** + * RESETPW 비밀번호 재설정 + * RECORD: Calendar for Record Page + * SETTEMPLATE 템플릿 모드 (유저 모드) 설정 + * CATEGORY 카테고리 (삭제) + */ +export type PopupMessageType = + 'RESETPW' + | 'RECORD' + | 'SETTEMPLATE' + | 'CATEGORY'; + +export const popupMessageShowStatus = atom({ + key: 'PopupMessageShowStatus', + default: true, +}); + +export const currentPopupMessageType = atom({ + key: 'CurrentPopupMessageType', + default: 'RESETPW', +}); + +export const popupMessageText = atom({ + key: 'PopupMessageText', + default: '', +}); + +export const popupMessagePosition = selector({ + key: 'PopupMessagePosition', + get: ({ get }) => { + const type = get(currentPopupMessageType); + if (type === 'RESETPW') { + return get(resetPWCompletePopupMessage); + } else if (type === 'SETTEMPLATE') { + return 'ETC MESSAGE TEXT'; + // return get(currentSelectedDateForStat); + } else { + return 'ETC MESSAGE TEXT 2222'; + } + }, +}); + +// export const popupMessageShowStatus = selector({ +// key: 'PopupMessageShowStatus', +// get: ({ get }) => { +// const type = get(currentPopupMessageType); +// if (type === 'RESETPW') { +// return get(resetPWCompletePopupShow); +// } else if (type === 'SETTEMPLATE') { +// // return get(currentSelectedDateForStat); +// } +// }, +// set: ({ set, get }, newValue) => { +// const type = get(currentPopupMessageType); +// // if (type === 'RESETPW') { +// // set(resetPWCompletePopupShow, newValue); +// // } else if (type === 'SETTEMPLATE') { +// // set(resetPWCompletePopupShow, newValue); +// // } +// }, +// }); \ No newline at end of file diff --git a/src/store/popupMessage.ts b/src/store/popupMessage.ts new file mode 100644 index 0000000..a5a6c69 --- /dev/null +++ b/src/store/popupMessage.ts @@ -0,0 +1,6 @@ +import { atom } from 'recoil'; + +export const popupShowState = atom({ + key: 'PopupShowState', + default: false, +}); \ No newline at end of file diff --git a/src/store/record.ts b/src/store/record.ts index 7b01ce2..66824b8 100644 --- a/src/store/record.ts +++ b/src/store/record.ts @@ -12,9 +12,9 @@ import { buttonText } from '@/constants/message'; // } as SelectedRangeData, // }); -export const currentSelectedEvent = atom({ +export const currentSelectedEvent = atom({ key: 'CurrentSelectedEvent', - default: {}, + default: { name: '', color: '' }, }); export const categories = atom({ @@ -98,5 +98,10 @@ export const recordEditNextButtonState = atom({ isDisabled: false, clickHandler: () => {}, text: buttonText.delete, - } + }, +}); + +export const recordEditCompletePopupMessage = atom({ + key: 'RecordEditCompletePopupMessage', + default: '기록이 완료되었습니다.', }); diff --git a/src/store/resetPW.ts b/src/store/resetPW.ts index 6ac89a2..c32183c 100644 --- a/src/store/resetPW.ts +++ b/src/store/resetPW.ts @@ -49,7 +49,6 @@ export const resetPWNextButtonState = atom({ }, }); - export const resetPWStepInstruction = atom({ key: 'ResetPWStepInstruction', default: '', @@ -62,6 +61,11 @@ export const resetPWMode = atom({ export const resetPWCompletePopupShow = atom({ key: 'resetPWCompletePopupShow', - default: false, + default: true, +}); + +export const resetPWCompletePopupMessage = atom({ + key: 'ResetPWCompletePopupMessage', + default: '비밀번호가 변경되었습니다.', }); diff --git a/src/styles/index.ts b/src/styles/index.ts index dd8745c..0cfcd96 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -10,6 +10,7 @@ declare module '@mui/material/styles' { calendar?: any; chart?: any; button?: any; + popup?: any; } interface Palette { grey?: Color; @@ -17,6 +18,7 @@ declare module '@mui/material/styles' { calendar?: any; chart?: any; button?: any; + popup?: any; } } @@ -99,7 +101,11 @@ export const theme = createTheme({ }, button: { blue: '#4F75FF', - } + }, + popup: { + background: '#D1F1E4', + icon: '#16B978', + }, }, components: { MuiPaper: { From 047128c480cbe85da8f5ad484c97c82fc791c485 Mon Sep 17 00:00:00 2001 From: choba Date: Mon, 15 Jan 2024 12:44:34 +0100 Subject: [PATCH 3/6] Edit. delete console.log & unnecessary code --- src/components/common/PopupMessage.tsx | 27 ++------------------------ 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/src/components/common/PopupMessage.tsx b/src/components/common/PopupMessage.tsx index b2c5bd8..0507213 100644 --- a/src/components/common/PopupMessage.tsx +++ b/src/components/common/PopupMessage.tsx @@ -4,37 +4,19 @@ import { Icon, Typography, Popover, Paper } from '@mui/material'; import Spacer from '@/components/common/Spacer'; import { useRecoilState, useRecoilValue } from 'recoil'; import { theme } from '@/styles'; -import { currentPopupMessageType, popupMessageText } from '@/store/common'; -import { useEffect, useState } from 'react'; +import { popupMessageText } from '@/store/common'; +import { useState } from 'react'; import { popupShowState } from '@/store/popupMessage'; -import { DEFAULT_POPUP_MSG_TIMEOUT } from '@/constants/sample'; -interface PopoverVirtualElement { - nodeType: 1; - getBoundingClientRect: () => DOMRect; -} - -const selection = window.getSelection(); const PopupMessage = () => { const popupText = useRecoilValue(popupMessageText); - const popupMessageType = useRecoilValue(currentPopupMessageType); const [isPopupShow, setIsPopupShow] = useRecoilState(popupShowState); const [anchorEl, setAnchorEl] = useState(null); const { background, icon } = theme.palette.popup; - - useEffect(() => { - - setTimeout(() => { - console.log(isPopupShow) - console.log(popupText) - console.log(popupMessageType) - setIsPopupShow(false); - }, DEFAULT_POPUP_MSG_TIMEOUT); - }, []); return ( { setIsPopupShow(false); }} anchorOrigin={{ - // vertical: 'bottom', - // horizontal: 'center', vertical: 'top', horizontal: 'center', }} transformOrigin={{ - // vertical: 'center', - // horizontal: 'center', vertical: 'top', horizontal: 'center', - }} > Date: Mon, 15 Jan 2024 13:26:15 +0100 Subject: [PATCH 4/6] Add. SignUp / ResetPW Bug --- src/hooks/signUpSteps.ts | 5 +- src/pages/auth/resetPW/ResetPWMode.tsx | 8 ++- src/pages/settings/SettingsPage.tsx | 78 +------------------------- 3 files changed, 12 insertions(+), 79 deletions(-) diff --git a/src/hooks/signUpSteps.ts b/src/hooks/signUpSteps.ts index 41f87d9..3c9cb27 100755 --- a/src/hooks/signUpSteps.ts +++ b/src/hooks/signUpSteps.ts @@ -1,17 +1,18 @@ -import { signUpStep } from '@/store/signUp'; +import { stepIndex } from '@/store/common'; import { ReactElement } from 'react'; import { useNavigate } from 'react-router-dom'; import { useRecoilState } from 'recoil'; export function useSignUpStepForm(steps: ReactElement[]) { - const [currentStepIndex, setCurrentStepIndex] = useRecoilState(signUpStep); + const [currentStepIndex, setCurrentStepIndex] = useRecoilState(stepIndex); const navigate = useNavigate(); /** * handle next button */ function handleNextButton() { + const currIdx = currentStepIndex; setCurrentStepIndex(() => (currIdx < steps.length - 1) ? (currIdx + 1) : currIdx); } diff --git a/src/pages/auth/resetPW/ResetPWMode.tsx b/src/pages/auth/resetPW/ResetPWMode.tsx index f3e615d..06ebb7e 100644 --- a/src/pages/auth/resetPW/ResetPWMode.tsx +++ b/src/pages/auth/resetPW/ResetPWMode.tsx @@ -1,12 +1,14 @@ +import { currentFormType, stepButtonProps } from '@/store/common'; import { resetPWMode, resetPWNextButtonState } from '@/store/resetPW'; import { Button, Container } from '@mui/material'; import Typography from '@mui/material/Typography'; import { useEffect } from 'react'; -import { useRecoilState } from 'recoil'; +import { useRecoilState, useSetRecoilState } from 'recoil'; const ResetPWMode = (props: any) => { - const [nextButtonProps, setNextButtonProps] = useRecoilState(resetPWNextButtonState); + const [nextButtonProps, setNextButtonProps] = useRecoilState(stepButtonProps); const [pWChgMode, setPWChgMode] = useRecoilState(resetPWMode); + const setStepType = useSetRecoilState(currentFormType); const getSelectModeButton = ( text: string, @@ -44,8 +46,10 @@ const ResetPWMode = (props: any) => { }; useEffect(() => { + setStepType('RESETPW'); setNextButtonProps({ ...nextButtonProps, + text: '다음', isDisabled: false, clickHandler: props.handleNextButton, }); diff --git a/src/pages/settings/SettingsPage.tsx b/src/pages/settings/SettingsPage.tsx index a2dda27..080bfcd 100644 --- a/src/pages/settings/SettingsPage.tsx +++ b/src/pages/settings/SettingsPage.tsx @@ -2,35 +2,26 @@ import { Box, Button, - Container, - Fade, - Icon, - List, + Container, List, ListItemButton, ListItemText, ListSubheader, - Typography, + Typography } from '@mui/material'; import React, { useEffect } from 'react'; import ChevronRightIcon from '@mui/icons-material/ChevronRight'; -import Spacer from '@/components/common/Spacer'; import ListIcon from '@/components/settings/ListIcon'; import UserAvatar from '@/components/settings/UserAvatar'; import { stepIndex } from '@/store/common'; -import { resetPWCompletePopupShow } from '@/store/resetPW'; import { useNavigate } from 'react-router-dom'; -import { useRecoilState, useSetRecoilState } from 'recoil'; +import { useSetRecoilState } from 'recoil'; import SettingWrapper from '../auth/common/Wrapper'; const SettingPage = () => { const navigation = useNavigate(); - const [completePopupShow, setCompletePopupShow] = useRecoilState( - resetPWCompletePopupShow, - ); - const setStepIndex = useSetRecoilState(stepIndex); const [open1, setOpen1] = React.useState(true); @@ -62,68 +53,6 @@ const SettingPage = () => { ); }; - const completePopupComponent = () => { - return ( - - setCompletePopupShow(false)}> - <> - - - - - - - - - - - - - - - - {'비밀번호가 변경되었습니다.'} - - - - - ); - }; - - useEffect(() => { setStepIndex(0); }); @@ -205,7 +134,6 @@ const SettingPage = () => { - {completePopupShow && completePopupComponent()} ); }; From 6d8b8063e1c5d1b8765089da3ee0de5a46356130 Mon Sep 17 00:00:00 2001 From: choba Date: Thu, 18 Jan 2024 13:11:41 +0100 Subject: [PATCH 5/6] Add. Chart Design --- src/components/common/CustomCalendar.tsx | 132 +++++++++++++++-------- src/hooks/statisticView.ts | 83 +++++++++----- src/pages/auth/resetPW/ResetPWMode.tsx | 2 +- src/pages/statistics/Period.tsx | 64 +++++------ src/store/statistics.ts | 1 + src/styles/index.ts | 2 +- 6 files changed, 176 insertions(+), 108 deletions(-) diff --git a/src/components/common/CustomCalendar.tsx b/src/components/common/CustomCalendar.tsx index 74612cc..1a2fd89 100755 --- a/src/components/common/CustomCalendar.tsx +++ b/src/components/common/CustomCalendar.tsx @@ -8,7 +8,7 @@ import { Box, Button, Container, Grid, Paper, Typography } from '@mui/material'; import { DatePicker } from '@mui/x-date-pickers'; import dayjs, { Dayjs } from 'dayjs'; import { useRef } from 'react'; -import { useRecoilState } from 'recoil'; +import { useRecoilState, useRecoilValue } from 'recoil'; export const CustomCalendar = (pickerProps: any) => { /** @@ -21,14 +21,20 @@ export const CustomCalendar = (pickerProps: any) => { */ const nextBtn = useRef(); + // previously selected date const [selectedDate, setSelectedDate] = useRecoilState(currentSelectedDate); - const [periodType, setPeriodType] = useRecoilState(currentPeriod); + + // newly selected date (temporary) + // const [tempSelectedDate, setTempSelectedDate] = + // useState(currentSelectedDate); + + const periodType = useRecoilValue(currentPeriod); const [switchView, setSwitchView] = useRecoilState(currentPeriodForView); - const { setNewDateRange } = useStatisticView(); + const { setNewDateRange, tempSelectedDate, setTempSelectedDate } = useStatisticView(); const currentYear = new Date().getFullYear(); const yearList = Array.from({ length: 12 }, (_, i) => currentYear - 11 + i); @@ -42,6 +48,7 @@ export const CustomCalendar = (pickerProps: any) => { * @returns customized calendar days */ const renderCustomCalendarDay = (elementDate: Dayjs) => { + // const date = tempSelectedDate![periodType]; const date = selectedDate![periodType]; const currentDate = elementDate.date(); const currentDay = elementDate.day(); @@ -66,14 +73,19 @@ export const CustomCalendar = (pickerProps: any) => { const calendarPalette = theme.palette.calendar; switch (currentDay) { case 0: - // color = isCurrentMonth ? 'calendar.currSun' : 'calendar.outSun'; - color = isCurrentMonth ? calendarPalette!.currSun : calendarPalette!.outSun; + color = isCurrentMonth + ? calendarPalette!.currSun + : calendarPalette!.outSun; break; case 6: - color = isCurrentMonth ? 'calendar.currSat' : 'calendar.outSat'; + color = isCurrentMonth + ? calendarPalette!.currSat + : calendarPalette!.outSat; break; default: - color = isCurrentMonth ? 'calendar.currDay' : 'calendar.outCay'; + color = isCurrentMonth + ? calendarPalette!.currDay + : calendarPalette!.outDay; break; } return ( @@ -98,7 +110,6 @@ export const CustomCalendar = (pickerProps: any) => { item style={{ position: 'absolute', - height: '85%', borderTopLeftRadius: isFirstDay ? '5px' : '0px', borderBottomLeftRadius: isFirstDay ? '5px' : '0px', borderTopRightRadius: isLastDay ? '5px' : '0px', @@ -120,12 +131,13 @@ export const CustomCalendar = (pickerProps: any) => { height: '3px', width: '100%', borderRadius: '5px', - background: isCurrentDate ? 'pink.500' : 'transparent', + background: isCurrentDate ? theme.palette.pink![500] : 'transparent', }} /> ); }; + /** * render calendar upper control tool bar * @returns @@ -133,7 +145,10 @@ export const CustomCalendar = (pickerProps: any) => { const renderToolbar = () => { const date = selectedDate[periodType]; const month = date.month() + 1; - const year = date.year(); + const year = + periodType === 'BY_YEAR' || switchView === 'BY_YEAR' + ? `${yearList[0]} ~ ${yearList[11]}` + : date.year(); return ( { flexDirection: 'row', justifyContent: periodType !== 'BY_YEAR' ? 'space-between' : 'center', alignItems: 'center', - margin: '2% 0', + p: '4% 0', }} - className={'test-class'} > {periodType !== 'BY_YEAR' && ( { { if (periodType === 'BY_MONTH') { - const prevYear = selectedDate['BY_MONTH'].subtract(1, 'year'); + const prevYear = tempSelectedDate['BY_MONTH'].subtract(1, 'year'); + // setTempSelectedDate({ + // ...tempSelectedDate, + // ['BY_MONTH']: prevYear, + // }); setSelectedDate({ ...selectedDate, ['BY_MONTH']: prevYear, @@ -193,17 +211,21 @@ export const CustomCalendar = (pickerProps: any) => { > {year} - - - {month} - + {(periodType === 'BY_DAY' || periodType === 'BY_WEEK') && ( + <> + + + {month} + + + )} {periodType !== 'BY_YEAR' && ( { { if (periodType === 'BY_MONTH') { - const nextYear = selectedDate['BY_MONTH'].add(1, 'year'); + const nextYear = tempSelectedDate['BY_MONTH'].add(1, 'year'); + // setTempSelectedDate({ + // ...tempSelectedDate, + // ['BY_MONTH']: nextYear, + // }); setSelectedDate({ ...selectedDate, ['BY_MONTH']: nextYear, @@ -249,14 +275,15 @@ export const CustomCalendar = (pickerProps: any) => { display: 'flex', flexDirection: 'row', flexWrap: 'wrap', - height: '265px', + height: '225px', width: '300px', - padding: 0, + p: '16px 8px', m: 1, }} + className="custom-calendar" > {list.map((elem, index) => { - const date = selectedDate[periodType].locale('ko'); + const date = tempSelectedDate[periodType].locale('ko'); const selected = switchView === 'BY_YEAR' || periodType === 'BY_YEAR' ? date.year() @@ -277,34 +304,53 @@ export const CustomCalendar = (pickerProps: any) => { - ); })} diff --git a/src/hooks/statisticView.ts b/src/hooks/statisticView.ts index 3f3a042..8b180ab 100755 --- a/src/hooks/statisticView.ts +++ b/src/hooks/statisticView.ts @@ -1,25 +1,48 @@ -import { currentPeriodForView, PeriodTypeForStat, currentSelectedDateForStat, currentPeriodForStat } from '@/store/statistics'; +import { currentSelectedDate } from '@/store/common'; +import { + currentPeriodForView, + PeriodTypeForStat, + currentSelectedDateForStat, + currentPeriodForStat, +} from '@/store/statistics'; import { Dayjs, ManipulateType } from 'dayjs'; import { useRecoilState } from 'recoil'; - - export function useStatisticView() { /** * selected period type state */ - const [periodType, setPeriodType] = useRecoilState( - currentPeriodForStat, + const [periodType, setPeriodType] = + useRecoilState(currentPeriodForStat); + + /** + * selected date for statistic view (all types) + */ + const [selectedDate, setSelectedDate] = useRecoilState( + // currentSelectedDateForStat, + currentSelectedDate, ); + /** + * selected date (temporary for calendar selection) for statistic view (all types) + */ + const [tempSelectedDate, setTempSelectedDate] = useRecoilState( + currentSelectedDateForStat, + ); + + const getPeriodType = (): PeriodTypeForStat => periodType; - const setNewPeriodType = (newPeriodType: PeriodTypeForStat) => setPeriodType(newPeriodType); + const setNewPeriodType = (newPeriodType: PeriodTypeForStat) => + setPeriodType(newPeriodType); + /** * get period string * @returns period string */ - const getPeriodString = (period: PeriodTypeForStat): ManipulateType | undefined => { + const getPeriodString = ( + period: PeriodTypeForStat, + ): ManipulateType | undefined => { switch (period) { case 'BY_DAY': return 'day'; @@ -35,35 +58,38 @@ export function useStatisticView() { }; /** - * month/year view state (month type only) + * month/year view state (month type only) */ - const [switchView, setSwitchView] = useRecoilState(currentPeriodForView); + const [switchView, setSwitchView] = + useRecoilState(currentPeriodForView); const getSwitchView = () => switchView; - const setNewSwitchView = () => setSwitchView(switchView === 'BY_MONTH' ? 'BY_YEAR' : 'BY_MONTH') + const setNewSwitchView = () => + setSwitchView(switchView === 'BY_MONTH' ? 'BY_YEAR' : 'BY_MONTH'); + + - /** - * selected date for statistic view (all types) - */ - const [selectedDate, setSelectedDate] = useRecoilState(currentSelectedDateForStat); const getSelectedDate = () => selectedDate; - const setNewSelectedDate = (periodType: PeriodTypeForStat, newDate: Dayjs) => { + + const setNewSelectedDate = ( + periodType: PeriodTypeForStat, + newDate: Dayjs, + ) => { setSelectedDate({ [periodType]: newDate, ...selectedDate, }); }; + /** + * get the date range format (week type only) + * @param value date + * @returns date format + */ + const getWeekPeriodInputFormat = (value: any) => + `${value.startOf('week').format('MM월 DD일 dddd')} ~ ${value + .endOf('week') + .format('MM월 DD일 dddd')}`; - -/** - * get the date range format (week type only) - * @param value date - * @returns date format - */ -const getWeekPeriodInputFormat = (value: any) => - `${value.startOf('week').format('MM월 DD일 dddd')} ~ ${value - .endOf('week') - .format('MM월 DD일 dddd')}`; /** * set data search range with newly selected date * @param newValue newly selected date @@ -73,19 +99,22 @@ const getWeekPeriodInputFormat = (value: any) => setSelectedDate({ ...selectedDate, [periodType]: newValue }); } }; + return { // getPeriodType, // setNewPeriodType, // getPeriodString, - + // getSelectedDate, // setNewSelectedDate, - // getSwitchView, // setNewSwitchView, getWeekPeriodInputFormat, setNewDateRange, + + tempSelectedDate, + setTempSelectedDate, }; } diff --git a/src/pages/auth/resetPW/ResetPWMode.tsx b/src/pages/auth/resetPW/ResetPWMode.tsx index 06ebb7e..4e6757a 100644 --- a/src/pages/auth/resetPW/ResetPWMode.tsx +++ b/src/pages/auth/resetPW/ResetPWMode.tsx @@ -1,5 +1,5 @@ import { currentFormType, stepButtonProps } from '@/store/common'; -import { resetPWMode, resetPWNextButtonState } from '@/store/resetPW'; +import { resetPWMode } from '@/store/resetPW'; import { Button, Container } from '@mui/material'; import Typography from '@mui/material/Typography'; import { useEffect } from 'react'; diff --git a/src/pages/statistics/Period.tsx b/src/pages/statistics/Period.tsx index bdd9dc8..8d316f8 100644 --- a/src/pages/statistics/Period.tsx +++ b/src/pages/statistics/Period.tsx @@ -7,20 +7,15 @@ import Chevron from '@/components/common/Chevron'; import { CustomCalendar } from '@/components/common/CustomCalendar'; import DateInput from '@/components/date/DateInput'; import { useStatisticView } from '@/hooks/statisticView'; -import { currentCalendarType, currentPeriod, currentSelectedDate, customCalendarType } from '@/store/common'; import { - statisticsResultState, - statisticsStartHour -} from '@/store/statistics'; -import { - AppBar, - Box, - Grid, - styled, - Tab, - Tabs -} from '@mui/material'; -import type { } from '@mui/material/themeCssVarsAugmentation'; + currentCalendarType, + currentPeriod, + currentSelectedDate, + customCalendarType, +} from '@/store/common'; +import { statisticsResultState, statisticsStartHour } from '@/store/statistics'; +import { AppBar, Box, Grid, styled, Tab, Tabs } from '@mui/material'; +import type {} from '@mui/material/themeCssVarsAugmentation'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; import dayjs, { ManipulateType } from 'dayjs'; @@ -113,27 +108,29 @@ const StyledTab = styled((props: StyledTabProps) => ( }); const Period = () => { - const currDate = new Date().toISOString().slice(0, 10); const startHour = useRecoilValue(statisticsStartHour); - const [selectedDate, setSelectedDate] = useRecoilState(currentSelectedDate); - const [periodType, setPeriodType] = - useRecoilState(currentPeriod); - const setCalendarType = useSetRecoilState(currentCalendarType); - + const selectedDate = useRecoilValue(currentSelectedDate); + const [periodType, setPeriodType] = useRecoilState(currentPeriod); + const setCalendarType = + useSetRecoilState(currentCalendarType); const setStatisticsResult = useSetRecoilState( statisticsResultState, ); - const { getWeekPeriodInputFormat, setNewDateRange } - = useStatisticView(); + const { getWeekPeriodInputFormat, setNewDateRange } = useStatisticView(); - const handlePeriodChange = (e: SyntheticEvent, selectedDate: PeriodTypeForStat) => { + const handlePeriodChange = ( + e: SyntheticEvent, + selectedDate: PeriodTypeForStat, + ) => { setPeriodType(selectedDate); }; - const getPeriodString = (period: PeriodTypeForStat): ManipulateType | undefined => { + const getPeriodString = ( + period: PeriodTypeForStat, + ): ManipulateType | undefined => { switch (period) { case 'BY_DAY': return 'day'; @@ -148,8 +145,6 @@ const Period = () => { } }; - - /** * get data with selected date * @param request @@ -161,11 +156,6 @@ const Period = () => { setStatisticsResult(response.result); }; - - useEffect(() => { - setCalendarType('STAT'); - }, []); - useEffect(() => { let startDate = selectedDate[periodType]; let endDate = startDate.add(1, getPeriodString(periodType)); @@ -193,6 +183,10 @@ const Period = () => { }); }, [selectedDate, periodType]); + useEffect(() => { + setCalendarType('STAT'); + }, []); + return ( {/* 날짜 선택 타입 시작 */} @@ -215,9 +209,7 @@ const Period = () => { > { - setPeriodType(selectedDate); - }} + onChange={handlePeriodChange} aria-label="statistics-category tabs" indicatorColor="primary" textColor="primary" @@ -299,9 +291,9 @@ const Period = () => { {CustomCalendar({ value: selectedDate[periodType].locale('ko'), inputFormat: 'YYYY년 MM월', - renderInput: (params: any) => ( - - ), + renderInput: (params: any) => { + return ; + }, onChange: setNewDateRange, views: ['month', 'year'], openTo: 'month', diff --git a/src/store/statistics.ts b/src/store/statistics.ts index 3a3f1e6..cae40c9 100644 --- a/src/store/statistics.ts +++ b/src/store/statistics.ts @@ -69,6 +69,7 @@ export const periodForStatListValue: IPeriodTypeForStat [] = [ { title: '한 달', id: 'BY_MONTH', subTitle: '이번달' }, { title: '일 년', id: 'BY_YEAR', subTitle: '이번해' }, ]; + export const periodForStatList = atom({ key: 'PeriodForStatList', default: periodForStatListValue, diff --git a/src/styles/index.ts b/src/styles/index.ts index 0cfcd96..75350cc 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -94,7 +94,7 @@ export const theme = createTheme({ currSat: '#3B66FF', outSat: '#3B66FF', currDay: '#4B4B4B', - outCay: '#B3B3B3', + outDay: '#B3B3B3', }, chart: { customBackground: '#FFF4F6', From c8cda95d6cfc2011a460284543a5b315a5562b80 Mon Sep 17 00:00:00 2001 From: choba Date: Tue, 23 Jan 2024 10:17:33 +0100 Subject: [PATCH 6/6] Add. chart design / function improve --- src/components/common/CustomCalendar.tsx | 204 ++++++++++++----------- src/hooks/statisticView.ts | 63 +------ src/pages/statistics/Period.tsx | 30 +--- src/store/statistics.ts | 18 +- 4 files changed, 140 insertions(+), 175 deletions(-) diff --git a/src/components/common/CustomCalendar.tsx b/src/components/common/CustomCalendar.tsx index 1a2fd89..25a3426 100755 --- a/src/components/common/CustomCalendar.tsx +++ b/src/components/common/CustomCalendar.tsx @@ -2,13 +2,26 @@ import Chevron from '@/components/common/Chevron'; import Spacer from '@/components/common/Spacer'; import { useStatisticView } from '@/hooks/statisticView'; import { currentPeriod, currentSelectedDate } from '@/store/common'; -import { PeriodTypeForView, currentPeriodForView } from '@/store/statistics'; +import { + PeriodTypeForView, + currentPeriodForView, + tempSelectedDateForStat, +} from '@/store/statistics'; import { theme } from '@/styles'; import { Box, Button, Container, Grid, Paper, Typography } from '@mui/material'; import { DatePicker } from '@mui/x-date-pickers'; -import dayjs, { Dayjs } from 'dayjs'; +import { Dayjs } from 'dayjs'; import { useRef } from 'react'; -import { useRecoilState, useRecoilValue } from 'recoil'; +import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; + +const calendarPalette = theme.palette.calendar!; +const pinkPalette = theme.palette.pink!; + +const date = new Date(); +const currentDate = date.getDate(); +const currentMonth = date.getMonth(); +const currentYear = date.getFullYear(); +const yearList = Array.from({ length: 12 }, (_, i) => currentYear - 8 + i); export const CustomCalendar = (pickerProps: any) => { /** @@ -21,41 +34,35 @@ export const CustomCalendar = (pickerProps: any) => { */ const nextBtn = useRef(); - // previously selected date - const [selectedDate, setSelectedDate] = - useRecoilState(currentSelectedDate); + const periodType = useRecoilValue(currentPeriod); + + const setSelectedDate = useSetRecoilState(currentSelectedDate); - // newly selected date (temporary) - // const [tempSelectedDate, setTempSelectedDate] = - // useState(currentSelectedDate); + // const tempSelectedDate = useRecoilValue( + // tempSelectedDateForStat, + // ); - const periodType = useRecoilValue(currentPeriod); + const [tempSelectedDate, setTempSelectedDate] = useRecoilState( + tempSelectedDateForStat, + ); const [switchView, setSwitchView] = useRecoilState(currentPeriodForView); - const { setNewDateRange, tempSelectedDate, setTempSelectedDate } = useStatisticView(); - - const currentYear = new Date().getFullYear(); - const yearList = Array.from({ length: 12 }, (_, i) => currentYear - 11 + i); + const { setNewDateRange } = useStatisticView(); /** * render custom calendar day elements (day, week type only) - * @param date - * @param selectedDates - * @param pickersDayProps - * @param value - * @returns customized calendar days + * @param elementDate + * @returns rendered calendar day */ const renderCustomCalendarDay = (elementDate: Dayjs) => { - // const date = tempSelectedDate![periodType]; - const date = selectedDate![periodType]; - const currentDate = elementDate.date(); - const currentDay = elementDate.day(); - const currentMonth = date.month(); + const date = tempSelectedDate![periodType]; - const isCurrentMonth = elementDate.month() === currentMonth; - const isCurrentDate = isCurrentMonth && currentDate === date.date(); + const elemYear = elementDate.year(); + const elemMonth = elementDate.month(); + const elemWeekday = elementDate.weekday(); + const elemDate = elementDate.date(); // for weekly calendar highlighter const start = date.startOf('week'); @@ -64,36 +71,42 @@ export const CustomCalendar = (pickerProps: any) => { const isFirstDay = elementDate.isSame(start, 'day'); const isLastDay = elementDate.isSame(end, 'day'); - const backgroundColor = + const isPrevDate = elemYear < currentYear || elemMonth < currentMonth; + + const isCurrentDate = + currentYear === elemYear && + currentMonth === elemMonth && + currentDate === elemDate; + + const isSelectedDate = (periodType === 'BY_WEEK' && dayIsBetween) || - (periodType === 'BY_DAY' && isCurrentDate) - ? 'pink.200' - : ''; + (periodType === 'BY_DAY' && + tempSelectedDate[periodType].year() === elemYear && + tempSelectedDate[periodType].month() === elemMonth && + tempSelectedDate[periodType].date() === elemDate); + + const borderRadiusLeft = + isFirstDay || periodType === 'BY_DAY' ? '5px' : '0px'; + const borderRadiusRight = + isLastDay || periodType === 'BY_DAY' ? '5px' : '0px'; + let color = ''; - const calendarPalette = theme.palette.calendar; - switch (currentDay) { + switch (elemWeekday) { case 0: - color = isCurrentMonth - ? calendarPalette!.currSun - : calendarPalette!.outSun; + color = isPrevDate ? calendarPalette.outSun : calendarPalette.currSun; break; case 6: - color = isCurrentMonth - ? calendarPalette!.currSat - : calendarPalette!.outSat; + color = isPrevDate ? calendarPalette.outSat : calendarPalette.currSat; break; default: - color = isCurrentMonth - ? calendarPalette!.currDay - : calendarPalette!.outDay; + color = isPrevDate ? calendarPalette.outDay : calendarPalette.currDay; break; } + return ( { - const dayProp = props.target.offsetParent.getAttribute('id'); - const newSelectedDate = dayjs(Number(dayProp)); - setNewDateRange(newSelectedDate); + onClick={() => { + setNewDateRange(elementDate); }} key={elementDate} sx={{ @@ -103,35 +116,34 @@ export const CustomCalendar = (pickerProps: any) => { position: 'relative', borderRadius: 0, margin: 0, - padding: 0, + padding: '0px !important', + width: '28px', + height: '28px', }} > - {currentDate} + {elemDate} @@ -143,13 +155,13 @@ export const CustomCalendar = (pickerProps: any) => { * @returns */ const renderToolbar = () => { - const date = selectedDate[periodType]; + const date = tempSelectedDate[periodType]; const month = date.month() + 1; const year = - periodType === 'BY_YEAR' || switchView === 'BY_YEAR' + periodType === 'BY_YEAR' || + (periodType === 'BY_MONTH' && switchView === 'BY_YEAR') ? `${yearList[0]} ~ ${yearList[11]}` : date.year(); - return ( { > { - if (periodType === 'BY_MONTH') { - const prevYear = tempSelectedDate['BY_MONTH'].subtract(1, 'year'); - // setTempSelectedDate({ - // ...tempSelectedDate, - // ['BY_MONTH']: prevYear, - // }); - setSelectedDate({ - ...selectedDate, - ['BY_MONTH']: prevYear, - }); - } else { + const prevPeriod = tempSelectedDate[periodType].subtract( + 1, + periodType === 'BY_MONTH' ? 'year' : 'month', + ); + if (periodType !== 'BY_MONTH') { const prev: HTMLElement = prevBtn.current!; prev.querySelector('button')!.click(); } + setNewDateRange(prevPeriod); }} direction={'left'} /> @@ -236,20 +243,15 @@ export const CustomCalendar = (pickerProps: any) => { > { - if (periodType === 'BY_MONTH') { - const nextYear = tempSelectedDate['BY_MONTH'].add(1, 'year'); - // setTempSelectedDate({ - // ...tempSelectedDate, - // ['BY_MONTH']: nextYear, - // }); - setSelectedDate({ - ...selectedDate, - ['BY_MONTH']: nextYear, - }); - } else { + const nextPeriod = tempSelectedDate[periodType].add( + 1, + periodType === 'BY_MONTH' ? 'year' : 'month', + ); + if (periodType !== 'BY_MONTH') { const next: HTMLElement = nextBtn.current!; next.querySelector('button')!.click(); } + setNewDateRange(nextPeriod); }} direction={'right'} /> @@ -262,13 +264,14 @@ export const CustomCalendar = (pickerProps: any) => { /** * render customized calendar (month, year type only) * @param props - * @returns + * @returns rendered calendar (month, year) */ const renderCustomMonthYearCalendar = () => { const list = switchView === 'BY_YEAR' || periodType === 'BY_YEAR' ? yearList : [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; + return ( { switchView === 'BY_YEAR' || periodType === 'BY_YEAR' ? date.year() : date.month() + 1; - const isCurrentDate = elem === selected; + + const isSelected = elem === selected; + const isPrevDate = + switchView === 'BY_YEAR' || periodType === 'BY_YEAR' + ? elem < currentYear + : elem < currentMonth || date.year() < currentYear; + const isCurrentDate = + switchView === 'BY_YEAR' || periodType === 'BY_YEAR' + ? elem === currentYear + : date.year() === currentYear && currentMonth + 1 === elem; + return ( { >