Order details view
parent
5b1af7ba52
commit
4c4efac975
@ -0,0 +1,137 @@
|
||||
import React, {useMemo} from 'react'
|
||||
import {DateTime} from 'luxon'
|
||||
|
||||
import styles from './style.module.css'
|
||||
|
||||
export default function OrderSummary({order, isAdmin}) {
|
||||
const items = useMemo(() => coalesceItems(order), [order])
|
||||
const totalShipping = useMemo(() => sumShippingPrice(order), [order])
|
||||
|
||||
return (
|
||||
<>
|
||||
<section className={styles.summaryRow}>
|
||||
{items.length && (
|
||||
<div className={styles.summaryRowSection}>
|
||||
<h3>Contents:</h3>
|
||||
<section>
|
||||
<ul>
|
||||
{items.map(({count, item}) => (
|
||||
<li key={item.uuid}>{count}x - {item.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{order.address && (
|
||||
<div className={styles.summaryRowSection}>
|
||||
<h3>Address:</h3>
|
||||
<section>
|
||||
<pre>
|
||||
{order.address.name || order.address.company}<br/>
|
||||
{order.address.street1}<br/>
|
||||
{order.address.street2 && (<>{order.address.street2}<br/></>)}
|
||||
{order.address.city}, {order.address.state}, {order.address.zip}<br/>
|
||||
{order.address.country !== 'US' && order.address.country}
|
||||
</pre>
|
||||
</section>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={styles.summaryRowSection}>
|
||||
<h3>Shipping:</h3>
|
||||
<section>
|
||||
<ShippingStatus totalShipping={totalShipping} delivery={order.delivery} isAdmin={isAdmin}/>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
function coalesceItems(order) {
|
||||
const {transactions} = order
|
||||
|
||||
let items = {}
|
||||
|
||||
for(const trans of transactions)
|
||||
for(const {count, item} of trans.cart.items) {
|
||||
if(!items[item.uuid])
|
||||
items[item.uuid] = {
|
||||
count, item
|
||||
}
|
||||
else
|
||||
items[item.uuid].count += count
|
||||
}
|
||||
|
||||
return Object.values(items)
|
||||
}
|
||||
|
||||
function sumShippingPrice(order) {
|
||||
return order.transactions.map(trans => trans.shipping_price).reduce((a,b) => (b+a), 0)
|
||||
}
|
||||
|
||||
const formatMoney = money => {
|
||||
if (money === undefined || money === null) return null;
|
||||
|
||||
return '$' + (money / 100).toFixed(2)
|
||||
}
|
||||
|
||||
function formatTime(time){
|
||||
return DateTime.fromISO(time).setZone('local').toFormat('LLLL dd')
|
||||
}
|
||||
|
||||
function ShippingStatus({totalShipping, delivery, isAdmin}) {
|
||||
if(!delivery)
|
||||
return (
|
||||
<>
|
||||
{isAdmin && (<p>{formatMoney(totalShipping)} charged to customer</p>)}
|
||||
<p>Not yet shipped</p>
|
||||
</>
|
||||
)
|
||||
|
||||
switch (delivery.type) {
|
||||
case 'easypost':
|
||||
case 'hand_shipped':
|
||||
return (
|
||||
<>
|
||||
{isAdmin && (
|
||||
<p>
|
||||
{formatMoney(totalShipping)} charged to customer<br/>
|
||||
{delivery.easypost && (<>{formatMoney(parseFloat(delivery.easypost.selected_rate.rate) * 100)} paid to EasyPost</>)}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<p>
|
||||
Shipped
|
||||
{isAdmin && delivery.type === 'hand_shipped' && ' by hand '}
|
||||
{' '} on {formatTime(delivery.date_shipped)}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Tracking number:<br/>
|
||||
<a href={`https://tools.usps.com/go/TrackConfirmAction?tLabels=${delivery.tracking_number}`}>{delivery.tracking_number}</a>
|
||||
</p>
|
||||
</>
|
||||
)
|
||||
|
||||
case 'hand_delivered':
|
||||
return (
|
||||
<>
|
||||
<p>Delivered {isAdmin && ' in person '} on {formatTime(delivery.date_delivered)}</p>
|
||||
{isAdmin && (
|
||||
<>
|
||||
<h4>Note:</h4>
|
||||
<pre>
|
||||
{delivery.description}
|
||||
</pre>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
|
||||
default:
|
||||
return <p>Error displaying shipment information</p>
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
.summaryRow {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.summaryRowSection {
|
||||
margin: 8px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.summaryRowSection > h3, .summaryRowSection h4 {
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.summaryRowSection > section pre {
|
||||
margin: 0;
|
||||
padding: 16px;
|
||||
background: white;
|
||||
display: inline-block;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
|
||||
white-space: normal;
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
import {DateTime} from 'luxon'
|
||||
|
||||
import redirect from '~/utils/redirectGetInitialProps'
|
||||
import OrderSummary from '~/components/orderSummary'
|
||||
|
||||
OrderDetails.getInitialProps = async function({ctx, user}) {
|
||||
const {axios, query: {orderNum}} = ctx;
|
||||
|
||||
if(!user)
|
||||
return redirect(ctx, 302, '/login')
|
||||
|
||||
if(!user.email_confirmed)
|
||||
return redirect(ctx, 302, '/account/email/confirm')
|
||||
|
||||
const {data: orders} = await axios.get(`/api/orders`)
|
||||
|
||||
const order = orders.find(({number}) => number === parseInt(orderNum, 10))
|
||||
return {order}
|
||||
}
|
||||
|
||||
export default function OrderDetails({order}) {
|
||||
const lastTransaction = getLastTransaction(order)
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>Order #{order.number}: Details</h2>
|
||||
<h3>Purchased {DateTime.fromISO(lastTransaction.completion_time).toFormat('LLLL dd, h:mm a')}</h3>
|
||||
<OrderSummary order={order} isAdmin={false} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function getLastTransaction(order){
|
||||
return order.transactions.sort(sortTransactions)[0]
|
||||
}
|
||||
|
||||
function sortTransactions({completion_time: a}, {completion_time: b}){
|
||||
const timeA = DateTime.fromISO(a)
|
||||
const timeB = DateTime.fromISO(b)
|
||||
|
||||
return timeB.diff(timeA).as('seconds')
|
||||
}
|
Loading…
Reference in New Issue