diff --git a/src/frontend/package.json b/src/frontend/package.json index f27c6f1..cb646f8 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -32,7 +32,8 @@ "@types/react-redux": "^7.1.9", "@types/react-router-dom": "^5.1.6", "@types/styled-components": "^5.1.4", - "jest-environment-jsdom-sixteen": "^1.0.3" + "jest-environment-jsdom-sixteen": "^1.0.3", + "prettier": "^2.2.1" }, "scripts": { "start": "craco start", diff --git a/src/frontend/src/component/main/NoteList.tsx b/src/frontend/src/component/main/NoteList.tsx new file mode 100644 index 0000000..8f7e3f0 --- /dev/null +++ b/src/frontend/src/component/main/NoteList.tsx @@ -0,0 +1,87 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import { Button, List, Popover, Typography } from 'antd'; +import { MoreOutlined } from '@ant-design/icons'; +import moment from 'moment'; +import styled from 'styled-components'; +import * as Color from '../../constants/colors'; +import * as Type from '../../types'; +import routes from '../../routes'; +import StockTransaction from './StockTransaction'; + +interface NoteListProps { + loading: boolean; + notes: Array; + onClickUpdateNoteButton: (noteId: number) => () => void; + onDeleteNote: (noteId: number) => () => void; + filterStockTransactions: (note: Type.Note) => Array>; +} + +const Container = styled.div` + background-color: ${Color.WHITE}; + box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.1); + padding: 20px; +`; + +const Title = styled(Typography.Title)` + color: ${Color.BLUE_2}; + margin-bottom: 30px !important; +`; + +const MoreButton = styled(MoreOutlined)` + font-size: 20px; +`; + +const SmallButton = styled(Button)` + padding: 4px 5px; +`; + +const StockTransactionButton = styled(Button)` + margin-right: 10px; +`; + +const NoteList: React.VFC = ({ + loading, + notes, + onClickUpdateNoteButton, + onDeleteNote, + filterStockTransactions, +}) => { + return ( + + 투자노트 목록 + ( + + {note.title}} + description={moment(note.investmentDate).format('YYYY년 MM월 DD일')} + /> + + + + 수정 + + + 삭제 + + + } + > + + + + )} + /> + + ); +}; + +export default NoteList; diff --git a/src/frontend/src/component/main/StockTransaction.tsx b/src/frontend/src/component/main/StockTransaction.tsx new file mode 100644 index 0000000..d2d0b5e --- /dev/null +++ b/src/frontend/src/component/main/StockTransaction.tsx @@ -0,0 +1,89 @@ +import React, { useCallback, useState } from 'react'; +import styled from 'styled-components'; +import { Button, Modal, Table } from 'antd'; +import * as TransactionType from '../../constants/transactionType'; +import * as Type from '../../types'; +import { addCommaToNumber } from '../../lib/number'; + +interface StockTransactionProps { + stockTransactions: Array; +} + +interface StockTransactionModalProps { + isModalVisible: boolean; + stockTransactions: Array; +} + +const StockTransactionButton = styled(Button)` + margin-right: 10px; +`; + +const columns = [ + { + title: '종목명', + dataIndex: 'companyName', + key: 'companyName', + }, + { + title: '거래량(주)', + dataIndex: 'quantity', + key: 'quantity', + }, + { + title: '1주당 가격(원)', + dataIndex: 'tradedPrice', + key: 'tradedPrice', + }, + { + title: '총 거래가(원)', + dataIndex: 'totalPrice', + key: 'totalPrice', + }, +]; + +const StockTransaction: React.VFC = ({ stockTransactions }) => { + const transactionTypes: Array = [TransactionType.BUY, TransactionType.SELL]; + + return ( +
+ {transactionTypes.map((type) => { + const filteredStockTransactions = stockTransactions.filter((s) => s.transactionType === type); + + if (filteredStockTransactions.length === 0) { + return null; + } + return ( + <> + + Modal.info({ + title: type === TransactionType.BUY ? '매수내역' : '매도내역', + content: ( + ({ + key: idx, + companyName: s.stockDetail.companyName, + quantity: addCommaToNumber(s.quantity), + tradedPrice: addCommaToNumber(s.tradedPrice), + totalPrice: addCommaToNumber(s.quantity * s.tradedPrice), + }))} + /> + ), + onOk() {}, + width: 900, + }) + } + > + {type === TransactionType.BUY ? '매수 ' : '매도 '} + {filteredStockTransactions.length}건 + + + ); + })} + + ); +}; + +export default StockTransaction; diff --git a/src/frontend/src/container/note/NoteListContainer.tsx b/src/frontend/src/container/note/NoteListContainer.tsx index c6aceca..51b6c3d 100644 --- a/src/frontend/src/container/note/NoteListContainer.tsx +++ b/src/frontend/src/container/note/NoteListContainer.tsx @@ -2,26 +2,55 @@ import React, { useCallback, useEffect } from 'react'; import { useAppDispatch, useNoteAction, useNoteState } from '../../hooks'; import history from '../../lib/history'; import routes from '../../routes'; -import NoteList from '../../component/note/NoteList'; +import * as Type from '../../types'; +import * as TransactionType from '../../constants/transactionType'; +import NoteList from '../../component/main/NoteList'; const NoteListContainer = () => { const dispatch = useAppDispatch(); - const { notes } = useNoteState(); - const noteActions = useNoteAction(); + const { notes, loading } = useNoteState(); + const noteAction = useNoteAction(); - const getMyNotes = useCallback(() => { - dispatch(noteActions.getNotesRequest({ page: 0, size: 10 })); - }, [dispatch, noteActions]); + const onClickUpdateNoteButton = useCallback( + (noteId: number) => () => { + history.push(routes.note.update(noteId)); + }, + [] + ); - const onClickMoreInfoButton = useCallback((noteId: number) => { - history.push(routes.note.detail(noteId)); + const onDeleteNote = useCallback( + (noteId: number) => () => { + if (window.confirm('정말 삭제하시겠습니까?')) { + dispatch(noteAction.deleteNoteRequest(noteId)); + } + }, + [dispatch, noteAction] + ); + + const filterStockTransactions = useCallback((note: Type.Note) => { + const buyType: Array = []; + const sellType: Array = []; + + note.stockTransactions.forEach((s) => + s.transactionType === TransactionType.BUY ? buyType.push(s) : sellType.push(s) + ); + + return [buyType, sellType]; }, []); useEffect(() => { - getMyNotes(); - }, [getMyNotes]); + dispatch(noteAction.getNotesRequest({ page: 0, size: 10 })); + }, [dispatch, noteAction]); - return ; + return ( + + ); }; export default NoteListContainer; diff --git a/src/frontend/yarn.lock b/src/frontend/yarn.lock index 961abf2..0e0da5a 100644 --- a/src/frontend/yarn.lock +++ b/src/frontend/yarn.lock @@ -9100,6 +9100,11 @@ prepend-http@^1.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= +prettier@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" + integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== + pretty-bytes@^5.1.0: version "5.3.0" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.3.0.tgz#f2849e27db79fb4d6cfe24764fc4134f165989f2"