Order details view

main
Ashelyn Dawn 4 years ago
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;
}

@ -51,7 +51,7 @@ export default function AccountPage({orders}) {
{name: 'Shipping', extractor: getShippingEstimate}, {name: 'Shipping', extractor: getShippingEstimate},
{name: 'Total', extractor: getAmountPaid}, {name: 'Total', extractor: getAmountPaid},
{name: '', extractor: order => {name: '', extractor: order =>
<button className="buttonLink" onClick={() => Router.push(`/account/orders/${order.uuid}`)}>Details</button> <button className="buttonLink" onClick={() => Router.push(`/account/orders/${order.number}`)}>Details</button>
} }
]} ]}
rows={orders} rows={orders}

@ -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')
}

@ -4,6 +4,7 @@ import ActionBar from '~/components/admin/actionBar'
import Table from '~/components/table' import Table from '~/components/table'
import {DateTime} from 'luxon' import {DateTime} from 'luxon'
import {Button} from '@rmwc/button' import {Button} from '@rmwc/button'
import OrderSummary from '~/components/orderSummary'
Order.getInitialProps = async ({ctx: {axios, query: {id}}}) => { Order.getInitialProps = async ({ctx: {axios, query: {id}}}) => {
const {data: order} = await axios.get(`/api/orders/${id}`) const {data: order} = await axios.get(`/api/orders/${id}`)
@ -21,10 +22,10 @@ export default function Order({order}){
{label: 'Mark Delivered', url: `/admin/orders/${order.uuid}/ship/delivery`} {label: 'Mark Delivered', url: `/admin/orders/${order.uuid}/ship/delivery`}
]}/> ]}/>
<h2>Order for {capitalizeName(order.address.name)}</h2> <h2>Order #{order.number} for {capitalizeName(order.address.name)}</h2>
<h3>Purchased {DateTime.fromISO(lastTransaction.completion_time).toFormat('LLLL dd, h:mm a')}</h3> <h3>Purchased {DateTime.fromISO(lastTransaction.completion_time).toFormat('LLLL dd, h:mm a')}</h3>
<pre>{JSON.stringify(order, null, 2)}</pre> <OrderSummary order={order} isAdmin={true} />
</> </>
) )
} }

@ -45,7 +45,7 @@ main {
padding-bottom: 15px; padding-bottom: 15px;
} }
main > p, main > h3, main > h4, main > h5, main > h6 { main > p, main > section, main > h3, main > h4, main > h5, main > h6 {
width: calc(100% - 100px); width: calc(100% - 100px);
max-width: 800px; max-width: 800px;
margin-left: auto; margin-left: auto;

Loading…
Cancel
Save