学生向けプログラミング入門 | 無料

学生向けにプログラミングを無料で解説。Java、C++、Ruby、PHP、データベース、Ruby on Rails, Python, Django

Django3.2 | 28 | QRオーダーシステムの構築 | QRコード表示

[27 | メニューアイテム削除] << [ホーム] >> [29 | QRコードプリント]

「qrcode.react」をインストールします。


コマンド
npm i qrcode.react@1.0.1 -s


「qrmenu_react/src/components」フォルダに「QRCodeModal.js」ファイルを新規作成します。


新規作成 【QRMenu/qrmenu_react/src/components/QRCodeModal.js】

import React from 'react';
import { Modal, Container, Row, Col } from 'react-bootstrap';
import QRCodeReact from 'qrcode.react';

const QRCodeModal = ({ show, onHide, place }) => (
    <Modal show={show} onHide={onHide} size="lg" centered>
        <Modal.Body className="text-center pt-4">
            <Container>
                <h3>客席 QRコード</h3>
                <div className="mt-4 mb-4">
                    <h5 className="mb-0 mr-2">
                        全席数: <b>{place.number_of_tables}</b>
                    </h5>
                </div>
                <Row>
                    {Array.from({ length: place.number_of_tables }, (_, i) => i + 1).map(
                        (table) => (
                            <Col key={table} lg={4} md={6} className="mb-4">
                                <QRCodeReact
                                    value={`${window.location.origin}/menu/${place.id}/${table}`}
                                    size={200}
                                />
                            </Col>
                        )    
                    )}
                </Row>
            </Container>
        </Modal.Body>
    </Modal>
)

export default QRCodeModal;



「src/pages/Place.js」ファイルを編集します。


記述編集 【/Users/heyjude/Desktop/QRMenu/qrmenu_react/src/pages/Place.js】

import { IoMdArrowBack } from 'react-icons/io';
import { AiOutlineDelete, AiOutlineQrcode, AioutlineQrcode } from 'react-icons/ai';
import { Row, Col, Button, Modal } from 'react-bootstrap';
import { useParams, useHistory } from 'react-router-dom';
import React, { useEffect, useState, useContext } from 'react';
import styled from 'styled-components';

import { fetchPlace, removePlace, removeCategory, removeMenuItem } from '../apis';
import AuthContext from '../contexts/AuthContext';
import MainLayout from '../layouts/MainLayout';
import MenuItemForm from '../containers/MenuItemForm';
import MenuItem from '../components/MenuItem';
import QRCodeModal from '../components/QRCodeModal';

const Panel = styled.div`
    background-color: white;
    padding: 20px;
    border-radius: 5px;
    box-shadow: 1px 1px 10px rgba(0,0,0,0.05);
`;


const Place = () => {
    const [place, setPlace] = useState({});
    const [menuItemFormShow, setMenuItemFormShow] = useState(false);
    const [selectedItem, setSelectedItem] = useState(null);
    const [qrCode, setQrCode] = useState(false);

    const showModal = () => setMenuItemFormShow(true);
    const hideModal = () => setMenuItemFormShow(false);

    const showQRModal = () => setQrCode(true);
    const hideQRModal = () => setQrCode(false);

    const auth = useContext(AuthContext);
    const params = useParams();
    const history = useHistory();

    const onBack = () => history.push("/places");

    const onFetchPlace = async () => {
        const json = await fetchPlace(params.id, auth.token);
        if(json) {
            setPlace(json);
        }
    };

    const onRemovePlace = () => {
        const c = window.confirm("本当に削除してもよろしいですか?")
        if (c) {
            removePlace(params.id, auth.token).then(onBack);
        }
    };

    const onRemoveCategory = (id) => {
        const c = window.confirm("本当に削除してもよろしいですか?")
        if (c) {
            removeCategory(id, auth.token).then(onFetchPlace);
        }
    };

    const onRemoveMenuItem = (id) => {
        const c = window.confirm("本当に削除してもよろしいですか?")
        if (c) {
            removeMenuItem(id, auth.token).then(onFetchPlace);
        }
    };

    useEffect(() => {
        onFetchPlace();
    }, []);

    return (
        <MainLayout>
            <Row>
                <Col lg={12}>
                    <div className="mb-4">
                        <div className="d-flex justify-content-between align-items-center mb-4">
                            <Button variant="link" onClick={onBack}>
                                <IoMdArrowBack size={25} color="black" />
                            </Button>
                            <h3 className="mb-0 ml-2 mr-2">{place.name}</h3>

                            <Button variant="link" onClick={onRemovePlace}>
                                <AiOutlineDelete size={25} color="red" />

                            </Button>
                        </div>
                        <Button variant="link" onClick={showQRModal}>
                            <AiOutlineQrcode size={25} />
                        </Button>
                    </div>
                </Col>
                <Col md={4}>
                    <Panel>
                        <MenuItemForm place={place} onDone={onFetchPlace} />
                    </Panel>
                </Col>

                <Col md={8}>
                    {place?.categories?.map((category) => (
                        <div key={category.id} className="mb-5">
                            <div className="d-flex align-items-center mb4">
                            <h4 className="mb-0 mr-2">
                                <b>{category.name}</b>
                            </h4>
                            <Button variant="link" onClick={() => onRemoveCategory(category.id)}>
                                <AiOutlineDelete size={25} color="red" />
                            </Button>
                            </div>
                            <br/>

                            {category.menu_items.map((item) => (
                                <MenuItem 
                                key={item.id} 
                                item={item}
                                onEdit={() => {
                                    setSelectedItem(item);
                                    showModal()
                                }}
                                onRemove={() => onRemoveMenuItem(item.id)} 
                            />
                            ))}

                        </div>
                    ))}
                </Col>


            </Row>

            <Modal show={menuItemFormShow} onHide={hideModal} centerd>
               <Modal.Body>
                <h4 className="text-center">メニューアイテム</h4>
                <MenuItemForm
                    place={place}
                    onDone={() =>{
                        onFetchPlace(); 
                        hideModal()
                    }}
                    item={selectedItem}
                />
                </Modal.Body>
            </Modal>

            <QRCodeModal show={qrCode} onHide={hideQRModal} place={place} centered />

        </MainLayout>
    )
};

export default Place;



QRコードボタンを押すと、テーブル席のQRコードが出るようになりました。

QRコード
QRコード



管理画面でテーブルの数を増やしてみます。

テーブルの数を増やす
テーブルの数を増やす



テーブル席数のQRコードが表示されます。

テーブル席数のQRコード表示
テーブル席数のQRコード表示



客席数を変更できるよう実装します。


「src/apis.js」ファイルに記述を追加します。


記述追加 【QRMenu/qrmenu_react/src/apis.js】111行目

import { toast } from 'react-toastify';

function request(path,  {data = null, token = null, method = "GET" }) {
  return fetch(path, {
    method,
    headers: {
      Authorization: token ? `Token ${token}` : "",
      "Content-Type": "application/json",
    },
    body: method !=="GET" && method !== "DELETE" ? JSON.stringify(data): null,
  })
    .then((response) => {

      //もし成功したら
      if (response.ok) {
        if(method === "DELETE") {
          return true;
        }
        //toast.success("ログイン成功");
        return response.json();
      }
      //失敗
      return response.json().then((json) => {
          //JSONエラー
          if (response.status === 400) {
            //toast.error("氏名もしくはパスワードに間違いがあります。");
            const errors = Object.keys(json).map(
                (k) => `${(json[k].join(" "))}`
            );
            throw new Error(errors.join(" "));
          }
          throw new Error(JSON.stringify(json));
        })
        .catch((e) => {
          if (e.name === "SyntaxError") {
            throw new Error(response.statusText);
          }
          throw new Error(e);
        })
    })

    .catch((e) => {
      //全エラー
      toast(e.message, { type: "error" });
    })
}

export function signIn(username, password) {
  return request("/auth/token/login/", {
    data: {username, password},
    method: "POST",
  })
}

export function register(username, password) {
  return request("/auth/users/", {
    data: {username, password},
    method: "POST",
  })
}

export function fetchPlaces(token) {
  return request("/api/places/", {token});
}

export function addPlace(data, token) {
  return request("/api/places/", {data, token, method: "POST" });
}

export function uploadImage(image) {
  const formData = new FormData();
  formData.append("file", image);
  formData.append("upload_preset", "qrmenu_photos");

  return fetch("https://api.cloudinary.com/v1_1/dov57gocw/image/upload", {
    method: "POST",
    body: formData,
  }).then((response) => {
    return response.json();
  });
}

export function fetchPlace(id, token) {
  return request(`/api/places/${id}`, { token });
}

export function addCategory(data, token) {
  return request("/api/categories/", {data, token, method: "POST"});
}

export function addMenuItems(data, token) {
  return request("/api/menu_items/", {data, token, method: "POST"});
}

export function updateMenuItem(id, data, token) {
  return request(`/api/menu_items/${id}`, { data, token, method: "PATCH"});
}

export function removePlace(id, token) {
  return request(`/api/places/${id}`, {token, method: "DELETE"});
}

export function removeCategory(id, token) {
  return request(`/api/categories/${id}`, {token, method: "DELETE"});
}

export function removeMenuItem(id, token) {
  return request(`/api/menu_items/${id}`, {token, method: "DELETE"});
}

export function updatePlace(id, data, token) {
  return request(`/api/places/${id}`, { data, token, method: "PATCH"})
}



「src/pages/Place.js」ファイルを編集します。


記述編集 【QRMenu/qrmenu_react/src/pages/Place.js】

import { IoMdArrowBack } from 'react-icons/io';
import { AiOutlineDelete, AiOutlineQrcode, AioutlineQrcode } from 'react-icons/ai';
import { Row, Col, Button, Modal } from 'react-bootstrap';
import { useParams, useHistory } from 'react-router-dom';
import React, { useEffect, useState, useContext } from 'react';
import styled from 'styled-components';

import {
     fetchPlace, 
     removePlace, 
     removeCategory, 
     removeMenuItem, 
     updatePlace 
    } 
    from '../apis';

import AuthContext from '../contexts/AuthContext';
import MainLayout from '../layouts/MainLayout';
import MenuItemForm from '../containers/MenuItemForm';
import MenuItem from '../components/MenuItem';
import QRCodeModal from '../components/QRCodeModal';

const Panel = styled.div`
    background-color: white;
    padding: 20px;
    border-radius: 5px;
    box-shadow: 1px 1px 10px rgba(0,0,0,0.05);
`;


const Place = () => {
    const [place, setPlace] = useState({});
    const [menuItemFormShow, setMenuItemFormShow] = useState(false);
    const [selectedItem, setSelectedItem] = useState(null);
    const [qrCode, setQrCode] = useState(false);

    const showModal = () => setMenuItemFormShow(true);
    const hideModal = () => setMenuItemFormShow(false);

    const showQRModal = () => setQrCode(true);
    const hideQRModal = () => setQrCode(false);

    const auth = useContext(AuthContext);
    const params = useParams();
    const history = useHistory();

    const onBack = () => history.push("/places");

    const onFetchPlace = async () => {
        const json = await fetchPlace(params.id, auth.token);
        if(json) {
            setPlace(json);
        }
    };

    const onRemovePlace = () => {
        const c = window.confirm("本当に削除してもよろしいですか?")
        if (c) {
            removePlace(params.id, auth.token).then(onBack);
        }
    };

    const onRemoveCategory = (id) => {
        const c = window.confirm("本当に削除してもよろしいですか?")
        if (c) {
            removeCategory(id, auth.token).then(onFetchPlace);
        }
    };

    const onRemoveMenuItem = (id) => {
        const c = window.confirm("本当に削除してもよろしいですか?")
        if (c) {
            removeMenuItem(id, auth.token).then(onFetchPlace);
        }
    };

    const onUpdatePlace = (tables) => {
        updatePlace(place.id, {number_of_tables: tables }, auth.token).then(
            (json) => {
                if (json) {
                    setPlace(json);
                }
            }
        )
    }

    useEffect(() => {
        onFetchPlace();
    }, []);

    return (
        <MainLayout>
            <Row>
                <Col lg={12}>
                    <div className="mb-4">
                        <div className="d-flex justify-content-between align-items-center mb-4">
                            <Button variant="link" onClick={onBack}>
                                <IoMdArrowBack size={25} color="black" />
                            </Button>
                            <h3 className="mb-0 ml-2 mr-2">{place.name}</h3>

                            <Button variant="link" onClick={onRemovePlace}>
                                <AiOutlineDelete size={25} color="red" />

                            </Button>
                        </div>
                        <Button variant="link" onClick={showQRModal}>
                            <AiOutlineQrcode size={25} />
                        </Button>
                    </div>
                </Col>
                <Col md={4}>
                    <Panel>
                        <MenuItemForm place={place} onDone={onFetchPlace} />
                    </Panel>
                </Col>

                <Col md={8}>
                    {place?.categories?.map((category) => (
                        <div key={category.id} className="mb-5">
                            <div className="d-flex align-items-center mb4">
                            <h4 className="mb-0 mr-2">
                                <b>{category.name}</b>
                            </h4>
                            <Button variant="link" onClick={() => onRemoveCategory(category.id)}>
                                <AiOutlineDelete size={25} color="red" />
                            </Button>
                            </div>
                            <br/>

                            {category.menu_items.map((item) => (
                                <MenuItem 
                                key={item.id} 
                                item={item}
                                onEdit={() => {
                                    setSelectedItem(item);
                                    showModal()
                                }}
                                onRemove={() => onRemoveMenuItem(item.id)} 
                            />
                            ))}

                        </div>
                    ))}
                </Col>


            </Row>

            <Modal show={menuItemFormShow} onHide={hideModal} centerd>
               <Modal.Body>
                <h4 className="text-center">メニューアイテム</h4>
                <MenuItemForm
                    place={place}
                    onDone={() =>{
                        onFetchPlace(); 
                        hideModal()
                    }}
                    item={selectedItem}
                />
                </Modal.Body>
            </Modal>

            <QRCodeModal 
                show={qrCode} 
                onHide={hideQRModal} 
                place={place} 
                centered 
                onUpdatePlace={onUpdatePlace}
            />

        </MainLayout>
    )
};

export default Place;



「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 QRCodeReact from 'qrcode.react';
import styled from 'styled-components';

const OperationButton = styled(Button)`
    width: 30px;
    height: 30px;
    margin: 0 10px;
    font-size: 20px;
    line-height: 18px;
`;

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">
                                <QRCodeReact
                                    value={`${window.location.origin}/menu/${place.id}/${table}`}
                                    size={200}
                                />
                            </Col>
                        )    
                    )}
                </Row>
            </Container>
        </Modal.Body>
    </Modal>
)

export default QRCodeModal;



+ボタンとーボタンで客席数を変えることができるようになりました。

客席数を変更
客席数を変更



↓↓クリックして頂けると励みになります。


[27 | メニューアイテム削除] << [ホーム] >> [29 | QRコードプリント]