Skip to content

Commit

Permalink
[도비] VM 2주차 금 PR (#110)
Browse files Browse the repository at this point in the history
* PR 리뷰 반영 및 Product 스타일 개선 (#23)

* Refactor: StrictMode 활성화 및 reducer 수정
- 전위연산자를 -1로 수정
- 일부는 스프레드 연산자로 state를 복사하도록 수정
Related to: #22

* Refactor: OrderLog 컴포넌트 리턴문 개선
- if문을 리턴문 안으로 이동함
Related to: #22

* Style: Children이 없는 컴포넌트 수정

* Design: Product 컴포넌트 스타일 개선
- isAvailable이 false이면 하얀 동그라미가 되도록 수정
- prop에 대한 조건문 수정

* 반환 기능 추가 및 리렌더링 개선 (#24)

* Fix: 반환 버튼과 관련된 컴포넌트를 파일로 분리

* Feat: 반환 버튼 클릭시 투입한 돈 반환하기 구현
Related to: #13

* Style: 중복된 case문 병합

* Refactor: input에 돈 투입하는 함수를 컴포넌트와 분리

* Feat: 로그 메세지가 길어지면 스크롤이 자동으로 내려가는 기능 추가
- logMessage 함수 분리

* Feat: 전역 타이머값을 위한 TimerContext 생성
- 타이머의 남은 시간을 나타내는 UI 추가
Related to: #13

* Refactor: MoneyContext 개선 및 일부 컴포넌트에 memo 적용
- dispatch와 관련된 함수를 hook으로 분리
- dispatch 함수 내부로 logContext의 dispatch 함수를 호출하도록 수정

* Refactor: LogContext 개선
- MoneyProvider 내부의 dispatch를 사용한 함수를 hook으로 분리

* Style: OrderContainer 관련 컴포넌트에 고정된 width값 추가

* Refactor: ProductContext 개선 및 memo 적용

* Fix: 스크롤 개선
- scrollIntoView를 scrollTop으로 개선
  • Loading branch information
JiminKim-dev authored May 22, 2022
1 parent cc27e6c commit 153e80e
Show file tree
Hide file tree
Showing 18 changed files with 441 additions and 297 deletions.
15 changes: 9 additions & 6 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,26 @@ import { MoneyProvider } from 'context/MoneyContext';
import { ThemeProvider } from 'styled-components';
import { LogProvider } from 'context/LogContext';
import { ProductsProvider } from 'context/ProductContext';
import { TimerProvider } from 'context/TimerContext';

import GlobalStyle from 'theme/GlobalStyles';
import theme from 'theme/theme';
import Router from 'Router';

function App() {
return (
<LogProvider>
<TimerProvider>
<MoneyProvider>
<ProductsProvider>
<ThemeProvider theme={theme}>
<GlobalStyle />
<Router />
</ThemeProvider>
<LogProvider>
<ThemeProvider theme={theme}>
<GlobalStyle />
<Router />
</ThemeProvider>
</LogProvider>
</ProductsProvider>
</MoneyProvider>
</LogProvider>
</TimerProvider>
);
}

Expand Down
47 changes: 7 additions & 40 deletions src/components/VendingMachine/OrderContainer/Input.jsx
Original file line number Diff line number Diff line change
@@ -1,50 +1,16 @@
import { useEffect, useContext, useState } from 'react';
import { LogContext } from 'context/LogContext';
import { MoneyContext } from 'context/MoneyContext';
import { useEffect, useState } from 'react';
import { useMoneyState } from 'context/MoneyContext';

import styled from 'styled-components';
import setLocalString from 'utils/setLocalString';
import calculateTotalMoney from 'utils/calculateTotalMoney';
import insertWalletMoney from 'utils/insertMoney';

export default function UserInput() {
const { insertMoneyLog } = useContext(LogContext);
const { walletMoneyData, inputInsertMoney } = useContext(MoneyContext);
const { walletMoneyData, inputInsertMoney } = useMoneyState();
const [inputValue, setInputValue] = useState('');
const totalMoney = calculateTotalMoney(walletMoneyData);

const insertWalletMoney = num => {
const insertLog = [];

// 자동보정
let money = Math.floor(num / 10) * 10;
if (totalMoney < money) {
insertMoneyLog(walletMoneyData);
return walletMoneyData;
}

walletMoneyData.forEach(item => {
// 천원 투입시 만원이 사용되지않도록, 해당하는 금액의 수량이 없으면 리턴
if (!Math.floor(money / item.unit) || !item.amount) return;

// 15000원을 투입했는데 5000원권이 2개밖에 없다면?
if (item.unit * item.amount < money) {
const 투입가능금액 = item.unit * item.amount;
const 투입가능횟수 = Math.floor(투입가능금액 / item.unit);
insertLog.push({ ...item, amount: 투입가능횟수 });
money -= 투입가능금액;
return;
}

const 투입가능횟수 = Math.floor(money / item.unit);
insertLog.push({ ...item, amount: 투입가능횟수 });
money - item.unit < item.unit && money <= 0
? (money -= item.unit)
: (money -= item.unit * 투입가능횟수);
});

insertMoneyLog(insertLog);
return insertLog;
};

const handleChange = e => {
if (e.target.value.length > 6) return;
setInputValue(setLocalString(e.target.value.replace(/[^0-9]/g, '')));
Expand All @@ -65,7 +31,8 @@ export default function UserInput() {
}

const removeLocalString = Number(inputValue.replace(',', ''));
inputInsertMoney(insertWalletMoney(removeLocalString));
const insertLog = insertWalletMoney(walletMoneyData, removeLocalString, totalMoney);
inputInsertMoney(insertLog);
setInputValue('');
};

Expand Down
46 changes: 33 additions & 13 deletions src/components/VendingMachine/OrderContainer/Order.jsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,42 @@
import { useContext } from 'react';
import { LogContext } from 'context/LogContext';
import { MoneyContext } from 'context/MoneyContext';
import { useContext, useEffect, useRef } from 'react';

import { useMoneyState } from 'context/MoneyContext';
import { useLogState } from 'context/LogContext';
import { TimerContext } from 'context/TimerContext';

import styled from 'styled-components';
import setLocalString from 'utils/setLocalString';
import OrderLog from './OrderLog';

export default function UserOrder() {
const { machineLog } = useContext(LogContext);
const { insertMoneyData } = useContext(MoneyContext);
const { insertMoneyData } = useMoneyState();
const { machineLog } = useLogState();
const { countdown } = useContext(TimerContext);
const totalInsertMoney = insertMoneyData;

const orderLog = machineLog.map(log => {
return <OrderLog key={log.id} log={log}></OrderLog>;
return <OrderLog key={log.id} log={log} />;
});

const scrollRef = useRef();

useEffect(() => {
const { scrollHeight, clientHeight } = scrollRef.current;
scrollRef.current.scrollTop = scrollHeight - clientHeight;
});

return (
<OrderInfo>
<OrderList>{orderLog}</OrderList>
<OrderList ref={scrollRef}>{orderLog}</OrderList>
<InputCostInfo>
<span>투입금액: </span>
<span>{setLocalString(totalInsertMoney)}</span>
<InfoWrapper>
<span>투입 금액: </span>
<span>{setLocalString(totalInsertMoney)}</span>
</InfoWrapper>
<InfoWrapper>
<span>남은 시간: </span>
<span>{countdown}</span>
</InfoWrapper>
</InputCostInfo>
</OrderInfo>
);
Expand All @@ -30,7 +46,7 @@ const OrderInfo = styled.div`
display: flex;
flex-direction: column;
justify-content: space-between;
width: 100%;
width: 240px;
height: 400px;
margin-bottom: 12px;
padding: 20px;
Expand All @@ -41,16 +57,20 @@ const OrderInfo = styled.div`
`;

const InputCostInfo = styled.div`
border-top: 2px solid black;
${({ theme }) => theme.fontStyles.mediumBold};
`;

const InfoWrapper = styled.div`
display: flex;
justify-content: space-between;
padding-top: 6px;
border-top: 2px solid black;
${({ theme }) => theme.fontStyles.mediumBold};
`;

const OrderList = styled.ul`
overflow-y: auto;
margin: 12px 0;
margin-bottom: 6px;
scroll-behavior: smooth;
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
Expand Down
44 changes: 18 additions & 26 deletions src/components/VendingMachine/OrderContainer/OrderLog.jsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,28 @@
import { memo } from 'react';
import styled from 'styled-components';
import setLocalString from 'utils/setLocalString';
import logMessage from 'utils/logmessage';

export default function OrderLog({ log }) {
const message = info => {
switch (info.type) {
case 'INSERT':
return ` 투입되었습니다.`;
case 'BUY':
return `을(를) 구매하였습니다.`;
case 'DROP':
return `이(가) 배출되었습니다.`;
default:
return;
}
};

if (typeof log.value === 'object') {
return (
<Order>
<strong>{setLocalString(log.value.unit)}</strong><strong>{log.value.amount}</strong>
{message(log)}
</Order>
);
}

const OrderLog = ({ log }) => {
return (
<Order>
<strong>{log.value}</strong>
{message(log)}
{typeof log.value === 'object' ? (
<>
<strong>{setLocalString(log.value.unit)}</strong>{' '}
<strong>{log.value.amount}</strong>
{logMessage(log.type)}
</>
) : (
<>
<strong>{log.value}</strong>
{logMessage(log.type)}
</>
)}
</Order>
);
}
};

export default memo(OrderLog);

const Order = styled.li`
margin-bottom: 4px;
Expand Down
25 changes: 25 additions & 0 deletions src/components/VendingMachine/OrderContainer/Retrun.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useMoneyState } from 'context/MoneyContext';

import styled from 'styled-components';
import exchangeMoney from 'utils/exchangeMoney';

export default function ReturnCost() {
const { insertMoneyData, returnMoney } = useMoneyState();

const handleClick = () => {
const exchangeLog = exchangeMoney(insertMoneyData);
returnMoney(exchangeLog);
};

return <ReturnBtn onClick={handleClick}>반환</ReturnBtn>;
}

const ReturnBtn = styled.button`
padding: 4px 24px;
margin-bottom: 24px;
border-radius: 4px;
background: ${({ theme }) => theme.colors.gray1};
box-shadow: 0 2px 2px 0 rgba(38, 38, 135, 0.3);
color: ${({ theme }) => theme.colors.gray3};
${({ theme }) => theme.fontStyles.xSmallBold};
`;
14 changes: 3 additions & 11 deletions src/components/VendingMachine/OrderContainer/index.jsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import styled from 'styled-components';
import UserOrder from './Order';
import UserInput from './Input';
import ReturnCost from './Retrun';

export default function OrderContainer() {
return (
<Container>
<UserOrder />
<UserInput />
<ReturnCost>반환</ReturnCost>
<ReturnCost />
<PickupBox>PUSH</PickupBox>
</Container>
);
}

const Container = styled.div`
width: 280px;
padding: 20px;
border: 1px solid black;
border-left: none;
Expand All @@ -22,16 +24,6 @@ const Container = styled.div`
background: ${({ theme }) => theme.colors.green};
`;

const ReturnCost = styled.button`
padding: 4px 24px;
margin-bottom: 24px;
border-radius: 4px;
background: ${({ theme }) => theme.colors.gray1};
box-shadow: 0 2px 2px 0 rgba(38, 38, 135, 0.3);
color: ${({ theme }) => theme.colors.gray3};
${({ theme }) => theme.fontStyles.xSmallBold};
`;

const PickupBox = styled.div`
width: 100%;
padding: 28px;
Expand Down
Loading

0 comments on commit 153e80e

Please sign in to comment.