Admin can now view and print shipping label

main
Ashelyn Dawn 4 years ago
parent 2e2ff356c2
commit 24a6c6fee1

@ -10,7 +10,7 @@ export default function Button({outline, onClick, enabled, type, children, icon:
<div className={styles.formElementContainer + (outline ? ' ' + styles.outline : '')}>
<button style={style} className={icon && styles.buttonWithIcon} disabled={enabled === undefined ? false : !enabled} onClick={onClick} type={type}>
{icon && <span className={styles.buttonIcon}>{icon}</span>}
{children}
{children && <span className={styles.buttonText}>{children}</span>}
</button>
</div>
)

@ -165,3 +165,7 @@
width: 24px;
margin-right: 8px;
}
.buttonWithIcon > .buttonIcon:only-child {
margin-right: 0;
}

@ -1,5 +1,6 @@
import React, {useMemo} from 'react'
import {DateTime} from 'luxon'
import Link from 'next/link'
import styles from './style.module.css'
@ -41,7 +42,7 @@ export default function OrderSummary({order, isAdmin}) {
<div className={styles.summaryRowSection}>
<h3>Shipping:</h3>
<section>
<ShippingStatus totalShipping={totalShipping} delivery={order.delivery} isAdmin={isAdmin}/>
<ShippingStatus order={order} totalShipping={totalShipping} delivery={order.delivery} isAdmin={isAdmin}/>
</section>
</div>
</section>
@ -82,7 +83,7 @@ function formatTime(time){
return DateTime.fromISO(time).setZone('local').toFormat('LLLL dd')
}
function ShippingStatus({totalShipping, delivery, isAdmin}) {
function ShippingStatus({order, totalShipping, delivery, isAdmin}) {
if(!delivery)
return (
<>
@ -114,6 +115,12 @@ function ShippingStatus({totalShipping, delivery, isAdmin}) {
Tracking number:<br/>
<a href={`https://tools.usps.com/go/TrackConfirmAction?tLabels=${delivery.tracking_number}`}>{delivery.tracking_number}</a>
</p>
{isAdmin && (
<p>
<Link href={`/admin/orders/${order.uuid}/label`}><a>View/print shipping label</a></Link>
</p>
)}
</>
)

@ -0,0 +1,33 @@
import {useState, useEffect} from 'react'
const init = {defined: false}
export default function useLocalStorage(key, defaultValue) {
const [state, setState] = useState(init)
// Load from localstorage on first render or if key changes
useEffect(() => {
const stored = window.localStorage.getItem(key)
try {
if(!stored) throw new Error('No stored value')
setState(JSON.parse(stored))
} catch {
setState(defaultValue)
}
}, [key])
// Persist to localstorage when state changes
useEffect(() => {
if(state === init) {
return
}
window.localStorage.setItem(key, JSON.stringify(state))
}, [state])
if(state === init)
return [defaultValue, setState]
return [state, setState];
}

@ -0,0 +1,127 @@
import React, {useState, useRef, useEffect} from 'react'
import {DateTime} from 'luxon'
import {Button} from '~/components/form'
import styles from '~/styles/shippingLabel.module.css'
import useLocalStorage from '~/hooks/useLocalStorage'
OrderLabel.getInitialProps = async ({ctx: {axios, query: {id}}}) => {
const {data: order} = await axios.get(`/api/orders/${id}`)
return {order}
}
export default function OrderLabel({order}){
const [printCache, setPrintCache] = useLocalStorage('admin:orderlabelview', {})
const [imgLoaded, setImgLoaded] = useState(false)
const imgRef = useRef()
const currentRotation = parseInt(printCache[order.uuid]?.rotation || '0deg', 10)
// On first load, if we haven't already decided rotation for this image,
// determine based on aspect ratio
useEffect(() => {
if(printCache[order.uuid] || !imgRef.current || !imgLoaded)
return;
const {current: img} = imgRef
if(img.naturalWidth > img.naturalHeight)
setRotation(90)
}, [printCache, imgLoaded, imgRef.current])
function setRotation(deg) {
setPrintCache(printCache => {
return clearCache({
...printCache,
[order.uuid]: {
uuid: order.uuid,
rotation: deg + 'deg',
timestamp: DateTime.local().toISO()
}
})
})
}
function rotateRight() {
setRotation((currentRotation + 90) % 360)
}
function rotateLeft() {
setRotation((currentRotation - 90) % 360)
}
function print() {
printLabel(imgRef.current, currentRotation)
}
return (
<>
<h2>Print Shipping Label</h2>
<div className={styles.controls}>
<Button outline icon="rotate_left" onClick={rotateLeft}/>
<Button outline icon="print" onClick={print}/>
<Button outline icon="rotate_right" onClick={rotateRight}/>
</div>
<div className={styles.container}>
<img ref={imgRef} onLoad={() => setImgLoaded(true)} style={{transform: `rotate(${currentRotation}deg)`}} src={order.delivery.easypost.postage_label.label_url}/>
</div>
</>
)
}
// Clear out older cache entries . . . return cleared cache
function clearCache(cache) {
// Get all cache values
return Object.values(cache)
// Filter out anything older than a week
.filter(({timestamp}) => Math.abs(DateTime.fromISO(timestamp).diffNow().as('days')) < 7)
// Convert back to keyed object
.reduce((acc, obj) => ({...acc, [obj.uuid]: obj}), {})
}
function printLabel(image, rotationDegrees) {
const {naturalWidth: width, naturalHeight: height} = image
const shorterSize = width > height ? 'height' : 'width'
const longerSize = width > height ? 'width' : 'height'
const {origin, translate} = (() => {
switch(rotationDegrees) {
case 90:
case -270:
return {origin: 'left top', translate: '0%, -100%'}
case 180:
case -180:
return {origin: 'center center', translate: '0%, 0%'}
case 270:
case -90:
return {origin: 'left top', translate:'-100%, 0%'}
default:
return {origin: 'left top', translate: '0%, 0%'}
}
})()
const popup = window.open("", "Print label", "toolbar=off,width=800,height=600")
popup.document.write(`<img src="${image.getAttribute('src')}"/>`)
popup.document.write(`
<style>
html, body { width: 100%; margin: 0; padding: 0;}
img {
${shorterSize}: 4in;
${longerSize}: auto;
transform-origin: ${origin};
transform: rotate(${rotationDegrees}deg) translate(${translate});
}
</style>
`)
popup.document.close()
popup.focus()
popup.print()
popup.close()
}

@ -0,0 +1,39 @@
.label {
display: block;
margin: 0 auto;
max-width: 600px;
width: 100%;
max-height: 400px;
object-fit: contain;
}
.container {
width: 100%;
max-width: 600px;
max-height: 600px;
display: flex;
overflow: hidden;
margin: 0 auto;
}
.container > img {
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
.controls {
text-align: center;
margin-bottom: 16px;
}
.controls > div {
display: inline-block;
margin: 8px;
}
.controls > div button {
width: 42px;
height: 42px;
padding: 10px 8px 6px 8px;
}
Loading…
Cancel
Save