frontend/src/pages/Orders.tsx (136 lines of code) (raw):

// Copyright 2022 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. import { AiOutlineReload } from 'react-icons/ai'; import PulseLoader from 'react-spinners/PulseLoader'; import ClipLoader from 'react-spinners/ClipLoader'; import { useState } from 'react'; import { getImageUrl, getName, getPrice, totalPrice } from '../data'; import { useGetOrderAsync } from '../hooks/apis'; import { useCustomer } from '../context/CustomerContext'; import { useIdentityToken } from '../context/IdentityTokenContext'; import { useOrders, useOrdersDispatch } from '../context/OrdersContext'; import InvalidTokenError from '../components/InvalidTokenError'; const Orders = () => { const orders = useOrders(); const dispatch = useOrdersDispatch(); const [loadings, setLoadings] = useState(Array(orders.length).fill(false)); const { customerId } = useCustomer(); const { identityToken } = useIdentityToken(); const { isLoading, isError, isUnauthorizedError, getOrderAsync } = useGetOrderAsync(); const isCompleted = (isAsync: boolean, status: string) => isAsync && status === 'pending'; const handleClickUpdateOrder = async ( orderId: string, id: number, ): Promise<void> => { try { setLoadings( loadings.map((loading, currentId) => { if (currentId === id) { return true; } return false; }), ); const order = await getOrderAsync( { customer_id: customerId, order_id: orderId }, identityToken, ); dispatch({ type: 'update', payload: { order_id: orderId, status: order.status, }, }); } catch (e) { console.error(e); } finally { setLoadings(Array(orders.length).fill(false)); } }; return ( <div className="m-auto max-w-5xl p-2"> {isUnauthorizedError && <InvalidTokenError />} {isError && ( <div className="my-6 border-2 border-red-500 p-3 text-center text-red-500"> Unknown error occured. </div> )} {orders.length !== 0 && ( <p className="my-6 text-center text-xl text-slate-500"> {orders.length} orders. </p> )} {orders.map((order, id) => ( <div key={order.order_id} className="mb-4 rounded-md border"> <div className="flex items-center gap-6 bg-slate-100 p-4"> <div> <p className="text-md text-gray-600">Order ID</p> <p className="text-lg text-gray-600">{order.order_id}</p> </div> <div> <p className="text-md text-gray-600">Status</p> {loadings[id] ? ( <p className="text-lg text-gray-600"> <PulseLoader color="#64748b" size={10} /> </p> ) : ( <p className="text-lg text-gray-600">{order.status}</p> )} </div> <div> <p className="text-md text-gray-600">Total</p> <p className="text-lg text-gray-600"> $ {totalPrice(order.cartItems)} </p> </div> <div className="mx-auto" /> {isCompleted(order.isAsync, order.status) && ( <button onClick={() => handleClickUpdateOrder(order.order_id, id)} type="button" disabled={isLoading} > {isLoading && loadings[id] ? ( <ClipLoader size="32px" color="#64748b" /> ) : ( <AiOutlineReload className="h-8 w-8 align-middle text-slate-500" /> )} </button> )} </div> {order.cartItems.map((cartItem, itemId) => ( <div key={cartItem.id} className={`m-2 flex h-24 p-2 ${ itemId !== order.cartItems.length - 1 ? 'border-b-2' : '' }`} > <img className="mr-3 bg-slate-50" src={getImageUrl(cartItem.id)} alt="" /> <div className="mr-3 flex flex-col justify-around"> <p className="text-lg text-gray-600">{getName(cartItem.id)}</p> <div className="flex gap-4"> <p className="text-xl text-gray-800"> $ {getPrice(cartItem.id)} </p> <p className="text-xl text-gray-800">×</p> <p className="text-xl text-gray-600">{cartItem.quantity}</p> </div> </div> </div> ))} </div> ))} {orders.length === 0 && ( <p className="pt-10 text-center text-xl text-slate-500"> Currently no orders! </p> )} </div> ); }; export default Orders;