Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
9d83a17
docs: 구현할 기능 목록 추가
usageness Dec 12, 2021
7fc4077
feat: 페이지 상단의 탭 메뉴를 생성한다.
usageness Dec 12, 2021
6e76a85
docs: 요구사항에 화면 생성 추가
usageness Dec 12, 2021
de036a9
feat: 각 탭의 기능이 표시될 요소를 생성한다.
usageness Dec 12, 2021
2f04355
fix: 잘못 매칭된 버튼 이름 수정
usageness Dec 12, 2021
17a3c57
feat: 상품 관리 화면을 생성한다.
usageness Dec 12, 2021
bf49e20
feat: 상품명, 가격, 수량을 입력해 상품을 추가할 수 있다
usageness Dec 13, 2021
5ab09c3
refactor: dom 생성 로직 별개의 함수로 변경
usageness Dec 13, 2021
92ae713
feat: 상품 가격은 100원부터 시작하며 10원으로 나누어 떨어져야 한다.
usageness Dec 13, 2021
924cbae
feat: 사용자는 추가한 상품을 확인할 수 있다.
usageness Dec 13, 2021
95722e4
feat: 로컬 스토리지를 이용하여 작업한 정보 저장
usageness Dec 13, 2021
2066926
feat: 잔돈 충전 화면을 생성한다
usageness Dec 13, 2021
aae78e8
refactor: 동전 개수 출력 함수 분리
usageness Dec 13, 2021
66fc80e
feat: 충전하기 버튼을 눌러 보유 금액을 충전할 수 있다
usageness Dec 13, 2021
788aae5
refactor: 상품관리 요소 생성 파일 이름 변경
usageness Dec 13, 2021
215e602
feat: 자판기 보유 금액만큼 동전 무작위 생성 후 누적
usageness Dec 13, 2021
732440b
docs: 동전 개수 확인 요구사항 추가
usageness Dec 13, 2021
95acd3c
feat: 사용자가 현재 동전의 개수를 확인할 수 있도록 화면을 업데이트
usageness Dec 13, 2021
92e6301
feat: buildChargePage에 localStorage 적용
usageness Dec 13, 2021
13920f4
refactor: createTableRow 별도 함수로 분리
usageness Dec 13, 2021
314402f
feat: 상품 구매 화면을 생성한다
usageness Dec 13, 2021
b4acf2e
feat: 사용자는 금액을 여러 번 누적해서 투입 가능
usageness Dec 13, 2021
ee1346b
feat: 사용자는 반환하기 버튼을 눌러 잔돈을 반환 받을 수 있다
usageness Dec 13, 2021
2aca275
docs: 구매 관련 요구사항 추가
usageness Dec 13, 2021
e522e23
feat: 사용자는 구매하기 버튼을 눌러 상품을 구매할 수 있다
usageness Dec 13, 2021
5960f8b
fix: null 값이 될 수 있는 요소들 체크
usageness Dec 13, 2021
69900ff
feat: 잔돈 충전의 유효성 검증 로직 추가
usageness Dec 13, 2021
0f52890
fix: 잘못된 dataset 속성 수정
usageness Dec 13, 2021
6e2c6ce
refactor: 의미있는 값들을 상수로 변경
usageness Dec 13, 2021
66752c0
refactor: appendChild 대신 append 적용
usageness Dec 13, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<p align="middle" >
<img width="200px;" src="https://github.com/woowacourse/javascript-vendingmachine-precourse/blob/main/images/beverage_icon.png?raw=true"/>
</p>
<h1 align="middle">자판기</h1>

## 요구사항
> 각 탭에 따라 적절한 기능을 수행할 수 있도록 구현한다.

### 상품 관리 탭
> 사용자는 자판기가 보유하고 있는 상품을 확인하고 추가할 수 있어야한다.

- [x] 상품 관리 화면을 생성한다.
- [x] 상품명, 가격, 수량을 입력해 상품을 추가할 수 있다.
- [x] 상품 가격은 100원부터 시작하며 10원으로 나누어 떨어져야 한다.
- [x] 사용자는 추가한 상품을 확인할 수 있다.

### 잔돈 충전 탭
> 사용자는 자판기의 보유 금액을 확인하고 추가할 수 있어야한다.

- [x] 잔돈 충전 화면을 생성한다.
- [x] 잔돈 충전 입력 요소에 금액을 입력 후, `충전하기` 버튼을 눌러 보유 금액을 충전할 수 있다.
- [x] 자판기 보유 금액만큼의 동전이 무작위로 생성된다.
- [x] 자판기가 이미 금액을 보유하고 있다면, 추가 금액 만큼의 동전을 생성하여 누적한다.
- [x] 사용자가 현재 동전의 개수를 확인할 수 있도록 화면을 업데이트 한다.

- 단, 동전 생성은 `MissionUtils` 라이브러리의 `Random.pickNumberInList`를 사용해 구한다.
```js
Random.pickNumberInList([1, 3, 10]); // 1
```

### 상품 구매 탭
> 사용자는 자판기에서 상품을 구매하고, 잔돈을 반환 받을 수 있다.

- [x] 상품 구매 화면을 생성한다.
- [x] 사용자는 투입할 금액을 입력한 후, `투입하기` 버튼을 눌러 금액을 투입할 수 있다.
- [x] 사용자는 금액을 여러 번 누적해서 투입할 수 있다.
- [x] 사용자는 `구매하기` 버튼을 눌러 상품을 구매할 수 있다.
- [x] 사용자는 `반환하기` 버튼을 눌러 잔돈을 반환 받을 수 있다.

**반환 시 유의사항**
- 잔돈은 자판기가 가진 동전 중 최소 개수의 동전으로 반환한다.
- 지폐를 잔돈으로 반환하는 경우는 없다.
- 잔돈을 반환할 수 없는 경우 반환할 수 있는 만큼만 반환한다.

### 공통
> 상단의 탭 메뉴가 존재하며, 각 탭에 따라 적절한 기능을 수행해야 한다.

- [x] 페이지 상단의 탭 메뉴를 생성한다.
- [x] 각 탭의 기능이 표시될 요소를 생성한다.
- [x] `localStorage`를 이용하여, 새로고침하더라도 가장 최근에 작업한 정보들을 불러올 수 있도록 한다.
1 change: 1 addition & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
</head>
<body>
<div id="app"></div>
<script type="module" src="src/index.js"></script>
</body>
</html>
52 changes: 52 additions & 0 deletions src/constant/Constant.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
export const TEXT = {
CHARGE_COIN_LABEL: "자판기 동전 충전하기",
MACHINE_CHARGE_PLACEHOLDER: "자판기가 보유할 금액",
MACHINE_CHARGE_BUTTON: "충전하기",
MACHINE_CHARGE_INFO_LABEL: "보유 금액: ",
MACHINE_COIN_INFO_LABEL: "자판기가 보유한 동전",
MACHINE_COIN_CATEGORY: "동전",
MACHINE_COIN_AMOUNT: "개수",
COIN_500: "500원",
COIN_100: "100원",
COIN_50: "50원",
COIN_10: "10원",
PRODUCT_ADD_LABEL: "상품 추가하기",
PRODUCT_NAME_PLACEHOLDER: "상품명",
PRODUCT_PRICE_PLACEHOLDER: "가격",
PRODUCT_QUANTITY_PLACEHOLDER: "수량",
PRODUCT_ADD_BUTTON: "추가하기",
PRODUCT_DISPLAY_LABEL: "상품 현황",
PRODUCT_ITEM_NAME: "상품명",
PRODUCT_ITEM_PRICE: "가격",
PRODUCT_ITEM_QUANTITY: "수량",
PRODUCT_ITEM_PURCHASE: "구매",
PRODUCT_ITEM_PURCHASE_BUTTON: "구매하기",
CHARGE_TO_PURCHASE_LABEL: "금액 투입",
CHARGE_TO_PURCHASE_PLACEHOLDER: "투입할 금액",
CHARGE_TO_PURCHASE_MONEY_LABEL: "투입한 금액: ",
PURCHASABLE_PRODUCE_LABEL: "구매할 수 있는 상품 현황",
RETURN_CHANGE_LABEL: "잔돈",
RETURN_CHANGE_BUTTON: "반환하기",
TAB_TITLE: "🥤자판기🥤",
TAB_PRODUCT_ADD_BUTTON: "상품 관리",
TAB_CHARGE_BUTTON: "잔돈 충전",
TAB_PURCHASE_BUTTON: "상품 구매",
}

export const MSG = {
INVALID_CHARGE: "올바르지 않은 잔돈 값입니다.",
INVALID_PRICE: "가격을 다시 입력해주세요",
INVALID_QUANTITY: "재고가 없습니다.",
INVALID_MONEY: "잔액이 부족합니다.",
INVALID_INPUT: "잘못된 입력 값 :",
}

export const NUMBER = {
MINIMUM_CHARGE: 10,
MINIMUM_COIN_UNIT: 10,
MINIMUM_PRICE: 100,
COIN_500: 500,
COIN_100: 100,
COIN_50: 50,
COIN_10: 10
}
81 changes: 81 additions & 0 deletions src/dom/buildChargePage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import VendingMachine from "../vendingMachine/VendingMachine.js";
import createDocumentElement from "../util/createDocumentElement.js";
import createTableRow from "../util/createTableRow.js";
import validateCharge from "../util/validateCharge.js";
import { TEXT, MSG } from "../constant/Constant.js";

const vendingMachine = new VendingMachine;

function buildChargePage() {
const container = document.querySelector(".container");

vendingMachine.getFromLocalStorage();

clearContainer(container);
chargeElement(container);
coinDisplayElement(container);
updateCoin();
}

function chargeElement(container) {
const vendingMachineChargeLabel = createDocumentElement("h3", TEXT.CHARGE_COIN_LABEL);
const vendingMachineChargeInput = createDocumentElement("input", "", "vending-machine-charge-input", TEXT.MACHINE_CHARGE_PLACEHOLDER);
const vendingMachineChargeButton = createDocumentElement("button", TEXT.MACHINE_CHARGE_BUTTON, "vending-machine-charge-button");
const vendingMachineMoneyLabel = createDocumentElement("p", TEXT.MACHINE_CHARGE_INFO_LABEL);
const vendingMachineChargeAmount = createDocumentElement("span", "", "vending-machine-charge-amount");
const vendingMachineCoinDisplayLabel = createDocumentElement("h3", TEXT.MACHINE_COIN_INFO_LABEL);

vendingMachineChargeButton.onclick = () => addCharge();

container.append(vendingMachineChargeLabel, vendingMachineChargeInput, vendingMachineChargeButton, vendingMachineMoneyLabel, vendingMachineCoinDisplayLabel);
vendingMachineMoneyLabel.appendChild(vendingMachineChargeAmount);
}

function coinDisplayElement(container) {
const coinDisplayTable = document.createElement("table");
const coinDisplayTableRow = document.createElement("tr");
const coinCategory = createDocumentElement("td", TEXT.MACHINE_COIN_CATEGORY);
const coinAmount = createDocumentElement("td", TEXT.MACHINE_COIN_AMOUNT);

container.appendChild(coinDisplayTable);
coinDisplayTable.appendChild(coinDisplayTableRow);
coinDisplayTableRow.append(coinCategory, coinAmount);

createTableRow(coinDisplayTable, TEXT.COIN_500,"vending-machine-coin-500-quantity");
createTableRow(coinDisplayTable, TEXT.COIN_100,"vending-machine-coin-100-quantity");
createTableRow(coinDisplayTable, TEXT.COIN_50,"vending-machine-coin-50-quantity");
createTableRow(coinDisplayTable, TEXT.COIN_10,"vending-machine-coin-10-quantity");
}

function addCharge() {
const vendingMachineChargeInput = document.querySelector("#vending-machine-charge-input");
if(validateCharge(vendingMachineChargeInput.value)) {
vendingMachine.chargeChange(vendingMachineChargeInput.value);
}
else{
alert(MSG.INVALID_CHARGE);
}

vendingMachineChargeInput.value = "";
updateCoin();
}

function updateCoin() {
const vendingMachineCoin500Quantity = document.querySelector("#vending-machine-coin-500-quantity");
const vendingMachineCoin100Quantity = document.querySelector("#vending-machine-coin-100-quantity");
const vendingMachineCoin50Quantity = document.querySelector("#vending-machine-coin-50-quantity");
const vendingMachineCoin10Quantity = document.querySelector("#vending-machine-coin-10-quantity");

const coins = vendingMachine.getCoins();

vendingMachineCoin500Quantity.innerText = coins.coin500 + "개";
vendingMachineCoin100Quantity.innerText = coins.coin100 + "개";
vendingMachineCoin50Quantity.innerText = coins.coin50 + "개";
vendingMachineCoin10Quantity.innerText = coins.coin10 + "개";
}

function clearContainer(container) {
container.innerHTML = "";
}

export default buildChargePage;
9 changes: 9 additions & 0 deletions src/dom/buildContainer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
function buildContainer() {
const app = document.getElementById("app");
const container = document.createElement("div");

container.className = "container";
app.appendChild(container);
}

export default buildContainer;
92 changes: 92 additions & 0 deletions src/dom/buildProductPage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import ProductList from "../product/ProductList.js";
import createDocumentElement from "../util/createDocumentElement.js"
import validatePrice from "../util/validatePrice.js";
import { TEXT, MSG } from "../constant/Constant.js";

const productList = new ProductList;

function buildProductPage() {
const container = document.querySelector(".container");
productList.getFromLocalStorage();

clearContainer(container);
productAddElement(container);
productItemElement(container);
productItemRefresh();
bindButtonEvent();
}

function productAddElement(container) {
const productAddLabel = createDocumentElement("h3", TEXT.PRODUCT_ADD_LABEL);
const productNameInput = createDocumentElement("input", "", "product-name-input", TEXT.PRODUCT_NAME_PLACEHOLDER);
const productPriceInput = createDocumentElement("input", "", "product-price-input", TEXT.PRODUCT_PRICE_PLACEHOLDER);
const productQuantityInput = createDocumentElement("input", "", "product-quantity-input", TEXT.PRODUCT_QUANTITY_PLACEHOLDER);
const productAddButton = createDocumentElement("button", TEXT.PRODUCT_ADD_BUTTON, "product-add-button");
const productDisplayLabel = createDocumentElement("h3", TEXT.PRODUCT_DISPLAY_LABEL);

container.append(productAddLabel, productNameInput, productPriceInput, productQuantityInput, productAddButton, productDisplayLabel);
}

function productItemElement(container) {
const productItemTable = document.createElement("table");
const productItemTableRow = document.createElement("tr");
const productItemName = createDocumentElement("td", TEXT.PRODUCT_ITEM_NAME);
const productItemPrice = createDocumentElement("td", TEXT.PRODUCT_ITEM_PRICE);
const productItemQuantity = createDocumentElement("td", TEXT.PRODUCT_ITEM_QUANTITY);

container.appendChild(productItemTable);
productItemTable.appendChild(productItemTableRow);
productItemTableRow.append(productItemName, productItemPrice, productItemQuantity);
}

function deleteItemElement() {
const productItemTable = document.querySelector("table");
productItemTable.parentNode.removeChild(productItemTable);
}

function addItemElement(name, price, quantity) {
const productItemTable = document.querySelector("table");
const productItemTableRow = createDocumentElement("tr", "", "", "", "product-manage-item");
const productItemName = createDocumentElement("td", name, "", "", "product-manage-name");
const productItemPrice = createDocumentElement("td", price, "", "", "product-manage-price");
const productItemQuantity = createDocumentElement("td", quantity, "", "", "product-manage-quantity");

productItemTable.appendChild(productItemTableRow);
productItemTableRow.append(productItemName, productItemPrice, productItemQuantity);
}

function productItemRefresh() {
const container = document.querySelector(".container");

deleteItemElement();
productItemElement(container);
if(productList.getItem() !== null) {
productList.getItem().map((item) => {
addItemElement(item.name, item.price, item.quantity);
});
}
}

function clearContainer(container) {
container.innerHTML = "";
}

function bindButtonEvent() {
const button = document.querySelector("#product-add-button");

button.onclick = () => {
const name = document.querySelector("#product-name-input").value;
const price = document.querySelector("#product-price-input").value;
const quantity = document.querySelector("#product-quantity-input").value;

if(validatePrice(price)) {
productList.addItem(name, price, quantity);
productItemRefresh();
}
else {
alert(MSG.INVALID_PRICE);
}
}
}

export default buildProductPage;
Loading