Skip to content

Commit

Permalink
[럼카] 1주차 1번째 PR (#26)
Browse files Browse the repository at this point in the history
  • Loading branch information
yongseongjeon authored May 11, 2022
1 parent c225a17 commit 9bce1ba
Show file tree
Hide file tree
Showing 20 changed files with 12,245 additions and 0 deletions.
23 changes: 23 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# production
/build

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
8 changes: 8 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"singleQuote": true,
"semi": true,
"useTabs": false,
"tabWidth": 2,
"trailingComma": "all",
"printWidth": 80
}
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# fe-vm

web vending machine project
6 changes: 6 additions & 0 deletions jsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"compilerOptions": {
"baseUrl": "src"
},
"includes": ["src"]
}
11,829 changes: 11,829 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

52 changes: 52 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"name": "fe-vm",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.2.0",
"@testing-library/user-event": "^13.5.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^8.5.0",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"react-router-dom": "^6.3.0",
"react-scripts": "5.0.1",
"styled-components": "^5.3.5",
"styled-reset": "^4.3.4",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"airbnb",
"prettier"
],
"rules": {
"import/no-unresolved": "off",
"react/prop-types": "off",
"react/no-array-index-key": "off",
"no-use-before-define": "off"
},
"env": {
"browser": true
}
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
12 changes: 12 additions & 0 deletions public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>웹 자판기</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
3 changes: 3 additions & 0 deletions public/robots.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
49 changes: 49 additions & 0 deletions src/components/App.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/* eslint-disable react/jsx-no-constructed-context-values */
import Home from 'pages/Home';
import Wallet from 'pages/Wallet';
import React, { useState } from 'react';
import { BrowserRouter, Link, Route, Routes } from 'react-router-dom';
import styled from 'styled-components';
import { delay } from 'utils';

export const MoneyContext = React.createContext();

const defaultMoney = 0;
const defaultErrorMsg = '';

function App() {
const [curMoney, setMoney] = useState(defaultMoney);
const [errorMsg, setErrorMsg] = useState(defaultErrorMsg);

return (
<MoneyContext.Provider value={{ curMoney, setMoney, showErrorMsg }}>
<BrowserRouter>
<Nav>
<Link to="/">자판기</Link>
<Link to="wallet">지갑</Link>
</Nav>
<Routes>
<Route index path="/" element={<Home />} />
<Route path="wallet" element={<Wallet />} />
</Routes>
<ErrorMsg>{errorMsg}</ErrorMsg>
</BrowserRouter>
</MoneyContext.Provider>
);
async function showErrorMsg(msg) {
setErrorMsg(msg);
const DELAY_MS = 3000;
await delay(DELAY_MS);
setErrorMsg('');
}
}

export default App;

const Nav = styled.nav({
display: 'flex',
gap: '10px',
});
const ErrorMsg = styled.div({
padding: '10px',
});
41 changes: 41 additions & 0 deletions src/components/Coin.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React, { useContext, useState } from 'react';
import styled from 'styled-components';
import { MoneyContext } from 'components/App';
import MESSAGES from 'messages';

function Coin({ amount, cnt }) {
const { curMoney, setMoney, showErrorMsg } = useContext(MoneyContext);
const [moneyCnt, setMoneyCnt] = useState(cnt);
return (
<Wrap>
<Amount onClick={handleChargeMoney}>{amount}</Amount>
<Box>{moneyCnt}</Box>
</Wrap>
);
function handleChargeMoney() {
const hasCoins = moneyCnt >= 1;
if (!hasCoins) {
showErrorMsg(MESSAGES.ERROR.NOT_ENOUGH_COINS);
return;
}
setMoney(curMoney + amount);
setMoneyCnt(moneyCnt - 1);
}
}

export default Coin;

const Wrap = styled.div({
display: 'flex',
gap: '10px',
});
const Box = styled.div({
width: '100px',
height: '50px',
textAlign: 'center',
lineHeight: '50px',
border: '1px solid black',
});
const Amount = styled(Box)({
cursor: 'pointer',
});
39 changes: 39 additions & 0 deletions src/components/ControlPanel.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React, { useContext } from 'react';
import styled from 'styled-components';
import { MoneyContext } from 'components/App';

function ControlPanel() {
const { curMoney } = useContext(MoneyContext);
return (
<Wrap>
<Row>
<Money value={curMoney} />
<span></span>
</Row>
<button type="button">반환하기</button>
<EventLog />
</Wrap>
);
}

export default ControlPanel;

const Wrap = styled.div({
width: '300px',
display: 'flex',
flexDirection: 'column',
padding: '20px',
gap: '20px',
});
const Row = styled.div({
display: 'flex',
});
const Money = styled.input({
width: '100%',
textAlign: 'right',
});
const EventLog = styled.div({
border: '1px solid black',
height: '100%',
overflowY: 'auto',
});
43 changes: 43 additions & 0 deletions src/components/MoneyCharge.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React, { useContext } from 'react';
import styled from 'styled-components';
import { MoneyContext } from 'components/App';
import Coin from 'components/Coin';
import COINS from 'mock/coins';

function MoneyCharge() {
const { curMoney } = useContext(MoneyContext);
return (
<Wrap>
<Coins />
<Total>
<div>{curMoney}</div>
<div></div>
</Total>
</Wrap>
);
}

function Coins() {
return COINS.map(({ AMOUNT, CNT }) => (
<Coin key={`${AMOUNT}`} amount={AMOUNT} cnt={CNT} />
));
}

const Wrap = styled.div({
display: 'flex',
flexDirection: 'column',
width: '200px',
gap: '20px',
marginTop: '20px',
});
const Total = styled.div({
width: '100%',
textAlign: 'end',
gridColumnStart: 'span 2',
display: 'flex',
border: '1px solid black',
height: '50px',
lineHeight: '50px',
});

export default MoneyCharge;
24 changes: 24 additions & 0 deletions src/components/Product.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react';
import styled from 'styled-components';

function Product({ name, price }) {
return (
<Wrap>
<Name>{name}</Name>
<Price>{price}</Price>
</Wrap>
);
}

export default Product;

const Wrap = styled.div({});
const Name = styled.div({
border: '1px solid black',
height: '50px',
textAlign: 'center',
lineHeight: '50px',
});
const Price = styled.div({
textAlign: 'center',
});
10 changes: 10 additions & 0 deletions src/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './components/App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>,
);
9 changes: 9 additions & 0 deletions src/messages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const MESSAGES = {
ERROR: {
NOT_ENOUGH_COINS:
'🚨 동전 또는 지폐의 개수가 0개 이하라서 충전에 실패했습니다. 🚨',
NOT_ENOUGH_MONEY: '🚨 돈이 부족해서 상품을 선택할 수 없습니다. 🚨',
},
};

export default MESSAGES;
17 changes: 17 additions & 0 deletions src/mock/coins.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { getRandomNumber } from 'utils';

const COINS = [
{ AMOUNT: 10, CNT: getNumber1to10() },
{ AMOUNT: 50, CNT: getNumber1to10() },
{ AMOUNT: 100, CNT: getNumber1to10() },
{ AMOUNT: 500, CNT: getNumber1to10() },
{ AMOUNT: 1000, CNT: getNumber1to10() },
{ AMOUNT: 5000, CNT: getNumber1to10() },
{ AMOUNT: 10000, CNT: getNumber1to10() },
];

function getNumber1to10() {
return getRandomNumber({ min: 0, max: 10 });
}

export default COINS;
24 changes: 24 additions & 0 deletions src/mock/products.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const PRODUCTS = [
{ name: '콜라 1', price: '500' },
{ name: '콜라 2', price: '1000' },
{ name: '콜라 3', price: '1500' },
{ name: '콜라 4', price: '2000' },
{ name: '콜라 5', price: '2500' },
{ name: '콜라 6', price: '3000' },
{ name: '콜라 7', price: '3500' },
{ name: '콜라 8', price: '4000' },
{ name: '콜라 9', price: '4500' },
{ name: '콜라 10', price: '5000' },
{ name: '콜라 11', price: '5500' },
{ name: '콜라 12', price: '6000' },
{ name: '콜라 13', price: '6500' },
{ name: '콜라 14', price: '7000' },
{ name: '콜라 15', price: '7500' },
{ name: '콜라 16', price: '8000' },
{ name: '콜라 17', price: '8500' },
{ name: '콜라 18', price: '9000' },
{ name: '콜라 19', price: '9500' },
{ name: '콜라 20', price: '10000' },
];

export default PRODUCTS;
Loading

0 comments on commit 9bce1ba

Please sign in to comment.