[32 | カートボタン] << [ホーム] >> [34 | Stripeモデルの実装]
「qrmenu_react/src/components」フォルダに「ShoppingCart.js」ファイルを新規作成します。
作成した「src/components/ShoppingCart.js」ファイルを以下のように編集します。
新規作成 【QRMenu/qrmenu_react/src/components/ShoppingCart.js】
import React, {useMemo } from 'react'; import { Card } from 'react-bootstrap'; const ShoppingCart = ({ items }) => { const totalPrice = useMemo( () => items.map((i) => i.quantity * i.price).reduce((a,b) => a+b, 0), [items] ); return ( <> <h3 className="text-center mb-4"> <b>注文内容</b> </h3> <Card> <Card.Body> {totalPrice} </Card.Body> </Card> </> ); } export default ShoppingCart;
「src/pages/Menu.js」ファイルを編集します。
記述編集 【QRMenu/qrmenu_react/src/pages/Menu.js】
import { Container, Row, Col, Button } from 'react-bootstrap'; import { IoCloseOutline } from 'react-icons/io5'; import { useParams } from 'react-router-dom'; import React, { useState, useEffect, useMemo } from 'react'; import { fetchPlace } from '../apis'; import styled from 'styled-components'; import MenuList from '../components/MenuList'; import ShoppingCart from '../components/ShoppingCart'; const OrderButton = styled(Button)` position: fixed; bottom: 20px; right: 20px; border-radius: 50%; box-shadow: 1px 1px 8px rgba(0,0,0,0.2); width: 80px; height: 80px; } `; const Menu = () => { const [ place, setPlace ] = useState({}); const [shoppingCart, setShoppingCart] = useState({}); const [showShoppingCart, setShowShoppingCart] = useState(false); const params = useParams(); const onFetchPlace = async () => { const json = await fetchPlace(params.id); console.log(json); if(json) { setPlace(json); } }; const onAddItemtoShoppingCart = (item) => { setShoppingCart({ ...shoppingCart, [item.id]:{ ...item, quantity: (shoppingCart[item.id]?.quantity || 0) + 1, } }); } const totalQuantity = useMemo( () => Object.keys(shoppingCart) .map((i) => shoppingCart[i].quantity) .reduce((a,b) => a+ b, 0), [shoppingCart] ); useEffect(() => { onFetchPlace(); }, []); return ( <Container ClassName="mt-5 mb-5"> <Row className="justify-content-center"> <Col lg={8}> {showShoppingCart ? ( <ShoppingCart items={Object.keys(shoppingCart) .map((key) => shoppingCart[key]) .filter((item) => item.quantity > 0) } /> ) : ( <MenuList place={place} shoppingCart={shoppingCart} onOrder={onAddItemtoShoppingCart} /> )} </Col> </Row> {totalQuantity ? ( <OrderButton variant="standard" onClick={() => setShowShoppingCart(!showShoppingCart)}> <div Style="font-size: 13px;">注文確定</div> {showShoppingCart ? <IoCloseOutline size={25} /> : totalQuantity} </OrderButton> ) : null} </Container> ) }; export default Menu;
注文確定ボタンを押すと、合計金額が出るようになりました。
「src/components/ShoppingCart.js」ファイルを以下のように追加編集します。
追加編集 【QRMenu/qrmenu_react/src/components/ShoppingCart.js】
import React, {useMemo } from 'react'; import { Card } from 'react-bootstrap'; const ShoppingCart = ({ items }) => { const totalPrice = useMemo( () => items.map((i) => i.quantity * i.price).reduce((a,b) => a+b, 0), [items] ); return ( <> <h3 className="text-center mb-4"> <b>注文内容</b> </h3> <Card> <Card.Body> {items.map((item) => ( <div key={item.id} className="d-flex mb-4 align-items-center"> <div className="flex-grow-1"> <p className="mb-0"> <b>{item.name}</b> </p> <span>{item.price}円</span> </div> <div className="d-flex align-items-center"> <span>{item.quantity}</span> </div> </div> ))} <hr/> <div className="d-flex justify-content-between"> <h6><b>合計金額</b></h6> <h5><b>{totalPrice.toLocaleString()}円</b></h5> </div> </Card.Body> </Card> </> ); } export default ShoppingCart;
合計金額が表示されるようになりました。
「src/components」フォルダに「OperationButton.js」ファイルを新規作成します。
作成した「OperationButton.js」ファイルを以下のように編集します。
新規作成 【QRMenu/qrmenu_react/src/components/OperationButton.js】
import { Button } from 'react-bootstrap'; import styled from 'styled-components'; const OperationButton = styled(Button)` width: 30px; height: 30px; margin: 0 10px; font-size: 20px; line-height: 18px; `; export default OperationButton;
「src/components/QRCodeModal.js」ファイルを編集します。
記述編集 【QRMenu/qrmenu_react/src/components/QRCodeModal.js】
import React from 'react'; import { Modal, Container, Row, Col, Button } from 'react-bootstrap'; import { AiFillPlusCircle, AiFillMinusCircle } from 'react-icons/ai'; import QRCode from './QRCode'; import OperationButton from './OperationButton'; const QRCodeModal = ({ show, onHide, place, onUpdatePlace }) => ( <Modal show={show} onHide={onHide} size="lg" centered> <Modal.Body className="text-center pt-4"> <Container> <h3>客席 QRコード</h3> <div className="s-flex align-items-center mt-4 mb-4"> <h5 className="mb-0 mr-2"> 全席数: <b>{place.number_of_tables}</b> </h5> <OperationButton variant="lightgray" size="sm" onClick={() => onUpdatePlace(place.number_of_tables -1)} > <AiFillMinusCircle size={25} color="red" /> </OperationButton> <OperationButton variant="lightgray" size="sm" onClick={() => onUpdatePlace(place.number_of_tables +1)} > <AiFillPlusCircle size={25} color="blue" /> </OperationButton> </div> <Row> {Array.from({ length: place.number_of_tables }, (_, i) => i + 1).map( (table) => ( <Col key={table} lg={4} md={6} className="mb-4"> <QRCode table={table} placeId={place.id} /> </Col> ) )} </Row> </Container> </Modal.Body> </Modal> ) export default QRCodeModal;
「src/components/ShoppingCart.js」ファイルを編集します。
記述編集 【Desktop/QRMenu/qrmenu_react/src/components/ShoppingCart.js】
import React, {useMemo } from 'react'; import { Card } from 'react-bootstrap'; import OperationButton from './OperationButton'; import { AiFillPlusCircle, AiFillMinusCircle } from 'react-icons/ai'; const ShoppingCart = ({ items, onAdd, onRemove }) => { const totalPrice = useMemo( () => items.map((i) => i.quantity * i.price).reduce((a,b) => a+b, 0), [items] ); return ( <> <h3 className="text-center mb-4 mt-5"> <b>注文内容</b> </h3> <Card> <Card.Body> {items.map((item) => ( <div key={item.id} className="d-flex mb-4 align-items-center"> <div className="flex-grow-1"> <p className="mb-0"> <b>{item.name}</b> </p> <span>{item.price}円</span> </div> <div className="d-flex align-items-center"> <OperationButton variant="lightgray" size="sm" onClick={() => onRemove(item)} > <AiFillMinusCircle size={25} color="red" /> </OperationButton> <span> {item.quantity}</span> <OperationButton variant="lightgray" size="sm" onClick={() => onAdd(item)} > <AiFillPlusCircle size={25} color="blue" /> </OperationButton> </div> </div> ))} <hr/> <div className="d-flex justify-content-between"> <h6><b>合計金額</b></h6> <h5><b>{totalPrice.toLocaleString()}円</b></h5> </div> </Card.Body> </Card> </> ); } export default ShoppingCart;
「src/pages/Menu.js」ファイルを編集します。
記述編集 【QRMenu/qrmenu_react/src/pages/Menu.js】
import { Container, Row, Col, Button } from 'react-bootstrap'; import { IoCloseOutline } from 'react-icons/io5'; import { useParams } from 'react-router-dom'; import React, { useState, useEffect, useMemo } from 'react'; import { fetchPlace } from '../apis'; import styled from 'styled-components'; import MenuList from '../components/MenuList'; import ShoppingCart from '../components/ShoppingCart'; const OrderButton = styled(Button)` position: fixed; bottom: 20px; right: 20px; border-radius: 50%; box-shadow: 1px 1px 8px rgba(0,0,0,0.2); width: 80px; height: 80px; } `; const Menu = () => { const [ place, setPlace ] = useState({}); const [shoppingCart, setShoppingCart] = useState({}); const [showShoppingCart, setShowShoppingCart] = useState(false); const params = useParams(); const onFetchPlace = async () => { const json = await fetchPlace(params.id); console.log(json); if(json) { setPlace(json); } }; const onAddItemtoShoppingCart = (item) => { setShoppingCart({ ...shoppingCart, [item.id]:{ ...item, quantity: (shoppingCart[item.id]?.quantity || 0) + 1, } }); } const onRemoveItemToShoppingCart = (item) => { if(totalQuantity === 1) { setShowShoppingCart(false); } setShoppingCart({ ...shoppingCart, [item.id]:{ ...item, quantity: (shoppingCart[item.id]?.quantity || 0) - 1, } }); } const totalQuantity = useMemo( () => Object.keys(shoppingCart) .map((i) => shoppingCart[i].quantity) .reduce((a,b) => a+ b, 0), [shoppingCart] ); useEffect(() => { onFetchPlace(); }, []); return ( <Container ClassName="mt-5 mb-5"> <Row className="justify-content-center"> <Col lg={8}> {showShoppingCart ? ( <ShoppingCart items={Object.keys(shoppingCart) .map((key) => shoppingCart[key]) .filter((item) => item.quantity > 0) } onAdd={onAddItemtoShoppingCart} onRemove={onRemoveItemToShoppingCart} /> ) : ( <MenuList place={place} shoppingCart={shoppingCart} onOrder={onAddItemtoShoppingCart} /> )} </Col> </Row> {totalQuantity ? ( <OrderButton variant="standard" onClick={() => setShowShoppingCart(!showShoppingCart)}> <div Style="font-size: 13px;">注文確定</div> {showShoppingCart ? <IoCloseOutline size={25} /> : totalQuantity} </OrderButton> ) : null} </Container> ) }; export default Menu;
アイテム数の増減ができるようになりました。
↓↓クリックして頂けると励みになります。
[32 | カートボタン] << [ホーム] >> [34 | Stripeモデルの実装]