diff --git a/README.md b/README.md index 56633c23..5331e413 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,135 @@ -## N석봉 +## N석봉 - 가계부 서비스 +

+ +

-> Project16-A-Account-Book +

+ + + + + + + + + + + + +

-
- - - - - -
+## 🏠 [HOME PAGE](http://xn--n-b22fl8h.kro.kr) -## 💸 [HOME PAGE](http://xn--n-b22fl8h.kro.kr/login) +돈은 너가 쓰거라 🤑, 관리는 내가 할테니 😎 -돈은 너가 쓰거라, 관리는 내가 할테니 +## 📌 가계부 서비스 소개 +### 👩‍👩‍👧‍👦 개인 또는 공용으로 관리할 수 있는 자산관리 가계부 서비스 +> - 혼자만의 가계부를 관리할 수 있습니다. +> - 친구를 초대하여 함께 가계부를 관리할 수 있습니다. +### 📈 지출과 수입에 대한 통계 제공 +> - 그동안의 지출내역과 수입을 **분석**하여 입출금 현황을 파악할 수 있습니다. +### ✔️ 내가 보고 싶은 것들만 필터링 +> - 원하는 내역만 **필터링**해서 볼 수 있는 기능을 제공합니다. +### 📆 달력을 통한 거래내역 확인 +> - 달 별로 돈의 사용 내역을 확인할 수 있습니다! +### 📩 MMS를 입력하여 바로 거래내역에 추가 +> - 문자로 온 거래내역을 치기만 하면, 바로 거래내역에 추가할 수 있습니다. -## 📌 소개 + +## 📌 [기술 및 논의 정리 - WIKI](https://github.com/boostcamp-2020/Project16-A-Account-Book/wiki) -💰 개인 또는 공용으로 이용 할 수 있는 자산관리 서비스 입니다. +## 📌 주요 기능 -📈 입력된 데이터를 시작화하여 분석 및 파악 할 수 있는 기능을 제공합니다. +|[🔗 로그인](https://github.com/boostcamp-2020/Project16-A-Account-Book/wiki/%EC%A3%BC%EC%9A%94-%EA%B8%B0%EB%8A%A5-%EC%86%8C%EA%B0%9C#-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%ED%8E%98%EC%9D%B4%EC%A7%80%EA%B8%B0%EB%8A%A5-%EC%86%8C%EA%B0%9C)|[🔗 가계부리스트](https://github.com/boostcamp-2020/Project16-A-Account-Book/wiki/%EC%A3%BC%EC%9A%94-%EA%B8%B0%EB%8A%A5-%EC%86%8C%EA%B0%9C#-%EA%B3%B5%EC%9C%A0-%EA%B0%80%EA%B3%84%EB%B6%80-%ED%8E%98%EC%9D%B4%EC%A7%80)|[🔗 메인페이지](https://github.com/boostcamp-2020/Project16-A-Account-Book/wiki/%EC%A3%BC%EC%9A%94-%EA%B8%B0%EB%8A%A5-%EC%86%8C%EA%B0%9C#-%EB%A9%94%EC%9D%B8-%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%86%8C%EA%B0%9C)|[🔗 달력페이지](https://github.com/boostcamp-2020/Project16-A-Account-Book/wiki/%EC%A3%BC%EC%9A%94-%EA%B8%B0%EB%8A%A5-%EC%86%8C%EA%B0%9C#-%EB%8B%AC%EB%A0%A5-%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%86%8C%EA%B0%9C)| +|:--:|:--:|:--:|:--:| +|![](https://i.imgur.com/Vh06k4p.png)|![](https://i.imgur.com/lfBYG9C.png)|![](https://i.imgur.com/fp18GM8.png)|![](https://i.imgur.com/QqL7sad.png)| + +|[🔗 통계페이지](https://github.com/boostcamp-2020/Project16-A-Account-Book/wiki/%EC%A3%BC%EC%9A%94-%EA%B8%B0%EB%8A%A5-%EC%86%8C%EA%B0%9C#-%ED%86%B5%EA%B3%84-%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%86%8C%EA%B0%9C)|[🔗 채팅페이지](https://github.com/boostcamp-2020/Project16-A-Account-Book/wiki/%EC%A3%BC%EC%9A%94-%EA%B8%B0%EB%8A%A5-%EC%86%8C%EA%B0%9C#-%EC%B1%84%ED%8C%85-%ED%8E%98%EC%9D%B4%EC%A7%80%EA%B8%B0%EB%8A%A5-%EC%86%8C%EA%B0%9C)|[🔗 태그페이지](https://github.com/boostcamp-2020/Project16-A-Account-Book/wiki/%EC%A3%BC%EC%9A%94-%EA%B8%B0%EB%8A%A5-%EC%86%8C%EA%B0%9C#-%ED%83%9C%EA%B7%B8-%ED%8E%98%EC%9D%B4%EC%A7%80)|[🔗 알람 모달](https://github.com/boostcamp-2020/Project16-A-Account-Book/wiki/%EC%A3%BC%EC%9A%94-%EA%B8%B0%EB%8A%A5-%EC%86%8C%EA%B0%9C#-%EC%95%8C%EB%9E%8C-%EB%AA%A8%EB%8B%AC)| +|:--:|:--:|:--:|:--:| +|![](https://i.imgur.com/RDIQb3Q.png)|![](https://i.imgur.com/rhwIsA1.png)|![](https://i.imgur.com/36NIY7b.png)|| + +## 📌서비스 흐름 +![](https://i.imgur.com/w2UftIk.png) + + +## ⚙️ 프로젝트 구동 방법 + +우선 Repository clone한 후, Project16-A-Account-Book 폴더에 들어간다. + +**1. 몽고디비 설치 후 데이터베이스 생성** +**2. 백엔드** +- 백엔드 환경 변수 설정 + - be 폴더 바로 밑에 .env 파일 생성 + - .env-template 안에 있는 내용 작성 + - .env 예시 + - mongodb cloud의 경우 DB_PORT는 작성하지 않기 + ``` + DB_USER=[데이터베이스 유저 이름 (ex. test)] + + DB_PASSWORD=[데이터베이스 password (ex. 123123)] + + DB_HOST=[데이터베이스 호스트 이름 (ex. cluster0.3v1lt.mongodb.net)] + + DB_DATABASE=[데이터베이스 이름 (ex. account?retryWrites=true&w=majority)] + + DB_PORT=[데이터베이스 포트 (ex. 27017)] + + JWT_SECRET=[JWT secret key (ex. ajsdFAG430tu04qkn) ] + + GITHUB_ID=[GitHub_OAuth_Client_Id (ex. 6df23f10bc0622c89804)] + + GITHUB_SECRET=[GitHub_OAuth_Client_Secret] + + HOST=[서버 주소 (ex. http://localhost)] + + BE_PORT=[백엔드엔드 서버 포트 (ex. 4000)] + + FE_PORT=[프론트엔드 서버 포트 (ex. 3000)] + + EXPIRES_IN=[JWT 토큰 만료 시간 (ex. 24h)] + ``` + +- 실행 + + ```jsx + cd be + yarn + yarn dev + ``` + or + ```jsx + cd be + npm install + npm run dev + ``` + +**3. 프론트엔드** +- 프론트엔드 환경 변수 설정 + - fe 폴더 바로 밑에 .env.development 파일 생성 + - .env_sample에 있는 내용 작성 + - .env 예시 + ``` + REACT_APP_API_URL=[서버 주소 (ex. http://localhost)] + + REACT_APP_API_PORT=[프론트엔드 서버 포트 (ex. 4000)] + ``` + +- 실행 + ```jsx + cd fe + yarn + yarn start + ``` + or + ```jsx + cd fe + npm install + npm run start + ``` ## 📌 팀원소개 @@ -27,6 +138,4 @@ | img | img | img | img | | [dbstjrwnekd](https://github.com/dbstjrwnekd) | [yejineee](https://github.com/yejineee) | [pkiop](https://github.com/pkiop) | [rolled-potatoes](https://github.com/rolled-potatoes) | -## 📌 기술 스택 -
diff --git a/be/package.json b/be/package.json index e1df9b41..e2c81722 100644 --- a/be/package.json +++ b/be/package.json @@ -1,6 +1,6 @@ { "scripts": { - "dev": "cd src && nodemon --exec ts-node -r tsconfig-paths/register app.ts", + "dev": "cd src && NODE_ENV=development nodemon --exec ts-node -r tsconfig-paths/register app.ts", "seed": "cd src && NODE_ENV=SEED ts-node -r tsconfig-paths/register seed.ts", "unseed": "cd src && NODE_ENV=UNSEED ts-node -r tsconfig-paths/register seed.ts", "seed-dev": "npm run seed && npm run dev", diff --git a/be/src/.env-template b/be/src/.env-template index deb03f71..c76643b1 100644 --- a/be/src/.env-template +++ b/be/src/.env-template @@ -8,5 +8,4 @@ GITHUB_ID= GITHUB_SECRET= HOST= BE_PORT= -FE_PORT= EXPIRES_IN= \ No newline at end of file diff --git a/be/src/app.ts b/be/src/app.ts index f0eb06e9..e08346bd 100644 --- a/be/src/app.ts +++ b/be/src/app.ts @@ -17,8 +17,8 @@ app.use(async (ctx, next) => { ctx.body = err; } }); -app.use(cors(corsOptions)); +app.use(cors(corsOptions)); app.use(bodyParser()); app.use(Router.routes()); app.use(Router.allowedMethods()); diff --git a/be/src/config/index.ts b/be/src/config/index.ts index 9f963bd4..2fa2d5f6 100644 --- a/be/src/config/index.ts +++ b/be/src/config/index.ts @@ -34,12 +34,9 @@ export const getDbUri = () => { return dbConfig.port ? localUri : srvUri; }; -export const getHostUrl = () => { - return `${hostConfig.url}:${hostConfig.backPort}`; -}; - export const getFrontUrl = () => { - return `${hostConfig.url}:${hostConfig.frontPort}`; + const port = process.env.NODE_ENV === 'development' ? ':3000' : ''; + return `${hostConfig.url}${port}`; }; export const corsOptions = { diff --git a/be/src/controllers/user/index.ts b/be/src/controllers/user/index.ts index 47c4fbee..fec804af 100644 --- a/be/src/controllers/user/index.ts +++ b/be/src/controllers/user/index.ts @@ -18,6 +18,7 @@ export const getUserByAccessToken = async (ctx: Context) => { ctx.status = 200; ctx.body = ctx.request.body.user; }; + export const getInvitation = async (ctx: Context) => { const { user } = ctx.request.body; const accounts = await userService.getInvitation(user); @@ -26,8 +27,16 @@ export const getInvitation = async (ctx: Context) => { title: account.title, ownerName: account.ownerName, host: user.invitations[idx].host, + imageUrl: account.imageUrl, })); ctx.body = invitations; }; +export const deleteInvitation = async (ctx: Context) => { + const { user } = ctx.request.body; + const { accountObjId } = ctx.params; + await userService.denyInvitation(user, accountObjId); + ctx.status = 204; + ctx.res.end(); +}; export default titleByAccountId; diff --git a/be/src/routers/api/user/account.ts b/be/src/routers/api/user/account.ts index efe0b925..f5354ede 100644 --- a/be/src/routers/api/user/account.ts +++ b/be/src/routers/api/user/account.ts @@ -1,7 +1,9 @@ import Router from 'koa-router'; -import { getInvitation } from 'controllers/user'; +import { getInvitation, deleteInvitation } from 'controllers/user'; const router = new Router(); router.get('/', getInvitation); +router.delete('/:accountObjId', deleteInvitation); + export default router; diff --git a/be/src/services/user/index.ts b/be/src/services/user/index.ts index c3e42969..58b7a6f9 100644 --- a/be/src/services/user/index.ts +++ b/be/src/services/user/index.ts @@ -1,5 +1,5 @@ import { UserModel, IUserDocument } from 'models/user'; -import { AccountModel, IAccountDocument } from 'models/account'; +import { AccountModel } from 'models/account'; export const titleByAccountId = async (accountId: String) => { const account = await AccountModel.findOne( @@ -14,14 +14,28 @@ export const getUserList = async () => { const allUserList = await UserModel.find({}).exec(); return allUserList; }; + export const getInvitation = async (user: IUserDocument) => { const account: any = user.invitations?.map((invitation) => AccountModel.findById(invitation.accounts).select( - 'title _id ownerName accountProfile', + 'title _id ownerName imageUrl', ), ); const results = await Promise.all(account); return results; }; +export const denyInvitation = async ( + user: IUserDocument, + accountObjId: string, +) => { + const prevInvitaionLength = user.invitations?.length; + // eslint-disable-next-line no-param-reassign + user.invitations = user.invitations?.filter( + (invitation) => String(invitation.accounts) !== accountObjId, + ); + if (user.invitations?.length === prevInvitaionLength) + return Promise.resolve(); + return user.save(); +}; export default titleByAccountId; diff --git a/fe/package.json b/fe/package.json index 5ae95d73..e5245038 100644 --- a/fe/package.json +++ b/fe/package.json @@ -64,6 +64,7 @@ "last 1 safari version" ] }, + "proxy":"http://localhost:4000", "devDependencies": { "@babel/core": "^7.12.3", "@storybook/addon-actions": "^6.0.28", diff --git a/fe/public/favicon.ico b/fe/public/favicon.ico new file mode 100644 index 00000000..50a6caad Binary files /dev/null and b/fe/public/favicon.ico differ diff --git a/fe/public/index.html b/fe/public/index.html index 1c0b14f7..6e98ad39 100644 --- a/fe/public/index.html +++ b/fe/public/index.html @@ -2,6 +2,7 @@ + diff --git a/fe/src/apis/axios.ts b/fe/src/apis/axios.ts index 655c4fbb..58bf25ed 100644 --- a/fe/src/apis/axios.ts +++ b/fe/src/apis/axios.ts @@ -2,7 +2,6 @@ import axios from 'axios'; const instance = axios.create({ withCredentials: true, - baseURL: `${process.env.REACT_APP_API_URL}:${process.env.REACT_APP_API_PORT}`, }); instance.interceptors.response.use( diff --git a/fe/src/components/atoms/DatePicker/index.stories.tsx b/fe/src/components/atoms/DatePicker/index.stories.tsx new file mode 100644 index 00000000..ac600360 --- /dev/null +++ b/fe/src/components/atoms/DatePicker/index.stories.tsx @@ -0,0 +1,21 @@ +import React, { useState } from 'react'; + +import GlobalThemeProvider from 'styles/GlobalThemeProvider'; +import DatePicker from '.'; + +export default { + title: 'atoms/DatePicker', + component: DatePicker, +}; + +export const datePicker = () => { + const [date, setDates] = useState(null); + const onChnage = (d: Date) => { + setDates(d); + }; + return ( + + + + ); +}; diff --git a/fe/src/components/molecules/DatePicker/index.tsx b/fe/src/components/atoms/DatePicker/index.tsx similarity index 64% rename from fe/src/components/molecules/DatePicker/index.tsx rename to fe/src/components/atoms/DatePicker/index.tsx index 6a5de441..94e97b43 100644 --- a/fe/src/components/molecules/DatePicker/index.tsx +++ b/fe/src/components/atoms/DatePicker/index.tsx @@ -5,35 +5,39 @@ import ReactDatePicker, { } from 'react-datepicker'; import 'react-datepicker/dist/react-datepicker.css'; import ko from 'date-fns/locale/ko'; -import DateRange from '../DateRange'; import * as S from './style'; registerLocale('ko', ko); setDefaultLocale('ko'); export interface IDatePicker { - dates: { - startDate: Date | null; - endDate: Date | null; - }; + date: Date | null; + name?: string; onChange: any; + disabled: boolean; } export interface IButton { onClick?: any; } +export interface ICustomInput { + value?: any; + onClick?: any; +} -const DatePicker = ({ dates, onChange }: IDatePicker) => { +const DatePicker = ({ + date, + onChange, + name = 'startDate', + disabled, +}: IDatePicker) => { return ( - onChange(d, name)} className="my-react-picker" locale={ko} + disabled={disabled} + dateFormat="yy.MM.dd" popperPlacement="auto" popperModifiers={{ preventOverflow: { enabled: true } }} /> diff --git a/fe/src/components/molecules/DatePicker/style.ts b/fe/src/components/atoms/DatePicker/style.ts similarity index 83% rename from fe/src/components/molecules/DatePicker/style.ts rename to fe/src/components/atoms/DatePicker/style.ts index 97dfa8c2..b774bfde 100644 --- a/fe/src/components/molecules/DatePicker/style.ts +++ b/fe/src/components/atoms/DatePicker/style.ts @@ -15,6 +15,11 @@ export const Container = styled.div` .react-datepicker__month-container { font-family: 'Bmeuljiro'; } + .my-react-picker { + background-color: ${({ theme }) => theme.color.white}; + border: none; + outline: none; + } `; export const DecsContainer = styled.div` diff --git a/fe/src/components/molecules/DatePicker/index.stories.tsx b/fe/src/components/molecules/DatePicker/index.stories.tsx deleted file mode 100644 index e6c50b27..00000000 --- a/fe/src/components/molecules/DatePicker/index.stories.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React, { useState } from 'react'; - -import GlobalThemeProvider from 'styles/GlobalThemeProvider'; -import DatePicker from '.'; - -export default { - title: 'molecules/DatePicker', - component: DatePicker, -}; -export interface IDatePicker { - dates: { - startDate: Date; - endDate: Date | null; - }; - onChange: any; -} -export const datePicker = () => { - const [dates, setDates] = useState<{ - startDate: Date; - endDate: Date | null; - }>({ - startDate: new Date(), - endDate: new Date(), - }); - const onChnage = (date: [Date, Date]) => { - setDates({ startDate: date[0], endDate: date[1] }); - }; - return ( - - - - ); -}; diff --git a/fe/src/components/molecules/DateRange/index.stories.tsx b/fe/src/components/molecules/DateRange/index.stories.tsx index 064a6ba0..b513d834 100644 --- a/fe/src/components/molecules/DateRange/index.stories.tsx +++ b/fe/src/components/molecules/DateRange/index.stories.tsx @@ -12,9 +12,10 @@ export const dateRange = () => { startDate: new Date(), endDate: new Date(), }; + const onchange = () => {}; return ( - + ); }; diff --git a/fe/src/components/molecules/DateRange/index.tsx b/fe/src/components/molecules/DateRange/index.tsx index 47aa1449..ae49be05 100644 --- a/fe/src/components/molecules/DateRange/index.tsx +++ b/fe/src/components/molecules/DateRange/index.tsx @@ -1,30 +1,39 @@ import React from 'react'; -import 'react-datepicker/dist/react-datepicker.css'; -import dateUtils from 'utils/date'; +import DatePicker from 'components/atoms/DatePicker'; import * as S from './style'; export interface IDateRange { dates: { - startDate: Date | string | null; - endDate: Date | string | null; + startDate: Date | null; + endDate: Date | null; }; + onChange?: any; + disabled?: boolean; } -const DateRange = ({ dates }: IDateRange) => { +const DateRange = ({ dates, onChange, disabled = false }: IDateRange) => { return (
시작일
- {dates.startDate && - dateUtils.dateCustomFormatter(dates.startDate, 'YY.MM.DD')} +
마지막일
- {dates.endDate && - dateUtils.dateCustomFormatter(dates.endDate, 'YY.MM.DD')} +
diff --git a/fe/src/components/molecules/InvitationItem/index.stories.tsx b/fe/src/components/molecules/InvitationItem/index.stories.tsx index ac33c932..17a55a3d 100644 --- a/fe/src/components/molecules/InvitationItem/index.stories.tsx +++ b/fe/src/components/molecules/InvitationItem/index.stories.tsx @@ -19,7 +19,7 @@ export const invitationItem = () => { host={host} accountObjId={accountObjId} ownerName={owner} - accountProfile={src} + imageUrl={src} title={title} /> diff --git a/fe/src/components/molecules/InvitationItem/index.tsx b/fe/src/components/molecules/InvitationItem/index.tsx index 4ed8c689..cfafe3ef 100644 --- a/fe/src/components/molecules/InvitationItem/index.tsx +++ b/fe/src/components/molecules/InvitationItem/index.tsx @@ -12,13 +12,13 @@ const InvitationItem = ({ host, ownerName, title, - accountProfile, + imageUrl, onClick, }: Prop) => { return (
- +
diff --git a/fe/src/components/molecules/ModalContainer/style.ts b/fe/src/components/molecules/ModalContainer/style.ts index 9ae056c0..cc997d5b 100644 --- a/fe/src/components/molecules/ModalContainer/style.ts +++ b/fe/src/components/molecules/ModalContainer/style.ts @@ -24,7 +24,7 @@ const Container = styled.div` width: 100vw; height: 100vh; display: flex; - z-index: 100; + z-index: 1; justify-content: ${({ justify }) => positionConverter(justify)}; align-items: ${({ align }) => positionConverter(align)}; background: ${({ theme }) => rgba(theme.color.black, 0.5)}; diff --git a/fe/src/components/organisms/InvitationList/index.tsx b/fe/src/components/organisms/InvitationList/index.tsx index a33cf39f..f07de1ab 100644 --- a/fe/src/components/organisms/InvitationList/index.tsx +++ b/fe/src/components/organisms/InvitationList/index.tsx @@ -6,7 +6,7 @@ import Container from './style'; export interface IInvitaion { accountObjId: string; title: string; - accountProfile: string; + imageUrl: string; ownerName: string; host: string; } diff --git a/fe/src/components/organisms/MainFilterForm/SubComponents/DatePickerList.tsx b/fe/src/components/organisms/MainFilterForm/SubComponents/DatePickerList.tsx index 3d1c4e18..29872afb 100644 --- a/fe/src/components/organisms/MainFilterForm/SubComponents/DatePickerList.tsx +++ b/fe/src/components/organisms/MainFilterForm/SubComponents/DatePickerList.tsx @@ -1,13 +1,7 @@ import React from 'react'; import * as S from '../style'; -const DatePickerList = ({ - onClick, - onClickFix, -}: { - onClick: any; - onClickFix: any; -}) => { +const DatePickerList = ({ onClickFix }: { onClickFix: any }) => { const innerTexts = ['오늘', '이번주', '이번달', '올 해']; return ( @@ -18,7 +12,6 @@ const DatePickerList = ({ ); })} - 기간 설정 ); }; diff --git a/fe/src/components/organisms/MainFilterForm/SubComponents/Modal.tsx b/fe/src/components/organisms/MainFilterForm/SubComponents/Modal.tsx deleted file mode 100644 index 6b12f612..00000000 --- a/fe/src/components/organisms/MainFilterForm/SubComponents/Modal.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import DataPicker, { IDatePicker } from '../../../molecules/DatePicker'; -import * as S from '../style'; - -interface IModal extends IDatePicker { - ref: any; -} - -const Modal = ({ dates, onChange, ref }: IModal) => { - return ( - -
- -
-
- ); -}; - -export default Modal; diff --git a/fe/src/components/organisms/MainFilterForm/SubComponents/index.ts b/fe/src/components/organisms/MainFilterForm/SubComponents/index.ts index 0b298cdf..94cd83d9 100644 --- a/fe/src/components/organisms/MainFilterForm/SubComponents/index.ts +++ b/fe/src/components/organisms/MainFilterForm/SubComponents/index.ts @@ -1,4 +1,3 @@ -export { default as Modal } from './Modal'; export { default as CategoryFilterList } from './CategoryFilterList'; export { default as DatePickerList } from './DatePickerList'; export { default as TopFilter } from './TopFilter'; diff --git a/fe/src/components/organisms/MainFilterForm/index.tsx b/fe/src/components/organisms/MainFilterForm/index.tsx index ad56a6f6..51db26b7 100644 --- a/fe/src/components/organisms/MainFilterForm/index.tsx +++ b/fe/src/components/organisms/MainFilterForm/index.tsx @@ -1,6 +1,5 @@ /* eslint-disable react/jsx-curly-newline */ -import React, { useRef, useReducer, useEffect } from 'react'; -import useVisible from 'hooks/useVisible'; +import React, { useReducer, useEffect } from 'react'; import { MethodStore } from 'stores/Method'; import { CategoryStore, @@ -15,12 +14,7 @@ import DateRange from '../../molecules/DateRange'; import DropdownHeader from '../../molecules/DropdownHeader'; import Dropdown from '../../molecules/Dropdown'; import { reducer, actions } from './filterReducer'; -import { - TopFilter, - CategoryFilterList, - Modal, - DatePickerList, -} from './SubComponents'; +import { TopFilter, CategoryFilterList, DatePickerList } from './SubComponents'; const SELECT_ALL_TYPE = 'ALL'; @@ -28,15 +22,15 @@ const MainFilterForm = () => { const [state, dispatch] = useReducer(reducer, { dates: { startDate: TransactionStore.getOriginDates().startDate, - endDate: dateUtils.subTractDate(TransactionStore.getDates().endDate), + endDate: new Date( + dateUtils.subTractDate(TransactionStore.getDates().endDate), + ), }, ...TransactionStore.getFilter(), }); const { dates, categories, methods } = state; const { income, expense, unclassified } = categories; - const container = useRef(null); - const [visible, toggleVisible] = useVisible(container); const selectAll = (type: string) => { const fetchedCategories = CategoryStore.getCategories(type); const selectedCategoryIdList = @@ -90,11 +84,12 @@ const MainFilterForm = () => { } } }; - - const onChangeDate = (dateList: [Date | null, Date | null]) => { - const [startDate, endDate] = dateList; - dispatch(actions.setDates(startDate, endDate)); - if (endDate) toggleVisible(); + const onChangeDate = (date: Date, name: string) => { + const targetDates = { + ...dates, + [name]: date, + }; + dispatch(actions.setDates(targetDates.startDate, targetDates.endDate)); }; const onClickCategory = ({ type, _id }: { type: string; _id: string }) => { @@ -135,21 +130,14 @@ const MainFilterForm = () => { document.body.click(); }; return ( - + - + - @@ -162,9 +150,6 @@ const MainFilterForm = () => { title="결제수단" /> - {visible && ( - - )} 카테고리 diff --git a/fe/src/components/organisms/MonthInfoHeader/index.tsx b/fe/src/components/organisms/MonthInfoHeader/index.tsx index 5b4284b5..8b5a97c1 100644 --- a/fe/src/components/organisms/MonthInfoHeader/index.tsx +++ b/fe/src/components/organisms/MonthInfoHeader/index.tsx @@ -8,10 +8,7 @@ import DateRange from 'components/molecules/DateRange'; import { MonthInfoHeaderContainer, MonthButton } from './style'; interface MonthInfoHeaderInterface { - date?: { - startDate: string; - endDate: string; - }; + date?: any; total?: { income: number; expense: number; @@ -25,8 +22,10 @@ type Params = { const MonthInfoHeader = ({ date = { - startDate: TransactionStore.getDates().startDate, - endDate: dateUtils.subTractDate(TransactionStore.getDates().endDate), + startDate: TransactionStore.getOriginDates().startDate, + endDate: new Date( + dateUtils.subTractDate(TransactionStore.getDates().endDate), + ), }, total = TransactionStore.totalPrices, }: MonthInfoHeaderInterface) => { @@ -34,7 +33,7 @@ const MonthInfoHeader = ({ const baseUrl = `/transactions/${owner}/${title}`; return ( - +
diff --git a/fe/src/hooks/useVisible.ts b/fe/src/hooks/useVisible.ts index b6c9d5f7..2e59532f 100644 --- a/fe/src/hooks/useVisible.ts +++ b/fe/src/hooks/useVisible.ts @@ -4,11 +4,9 @@ const useVisible = (ref: RefObject): [boolean, Function] => { const [visible, setVisible] = useState(false); const outsideClickHandler = (event: any) => { - event.stopPropagation(); - event.preventDefault(); + if (event.target.classList.contains('react-datepicker__day')) return; if (!ref.current) return; if (event.target.closest('#date-picker')) return; - if (ref && !ref.current.contains(event.target)) { setVisible(false); }