Manually add orders
parent
bd6dd194d6
commit
f739d7a5fb
@ -0,0 +1,35 @@
|
||||
import React from 'react'
|
||||
import ActionBar from '~/components/admin/actionBar'
|
||||
import {FormController, Input, Button} from '~/components/form'
|
||||
import Router from 'next/router'
|
||||
|
||||
OrderAddress.getInitialProps = async ({ctx}) => {
|
||||
const {data: order} = await ctx.axios.get(`/api/orders/${ctx.query.uuid}`)
|
||||
return {order}
|
||||
}
|
||||
|
||||
export default function OrderAddress({order}) {
|
||||
function afterSave(order) {
|
||||
Router.push(`/admin/orders/new/${order.uuid}/payment`)
|
||||
}
|
||||
|
||||
const {address} = order;
|
||||
|
||||
return (
|
||||
<>
|
||||
<ActionBar title="Set Address" actions={[
|
||||
{label: 'Cancel', url: `/admin/orders`}
|
||||
]}/>
|
||||
<FormController url={`/api/orders/manual/${order.uuid}/address`} afterSubmit={afterSave}>
|
||||
<Input initialValue={address?.name} name="name" validate={value=>value.length > 0}/>
|
||||
<Input initialValue={address?.street1} label="Street (line 1)" name="street1" validate={value=>value.length > 0}/>
|
||||
<Input initialValue={address?.street2} label="Street (line 2)" name="street2"/>
|
||||
<Input initialValue={address?.city} label="City" name="city" validate={value=>value.length > 0}/>
|
||||
<Input initialValue={address?.state} label="State / Province" name="state" validate={value=>value.length > 0}/>
|
||||
<Input initialValue={address?.zip} label="Postal Code" name="zip" validate={value=>value.length > 0}/>
|
||||
<Input initialValue={address?.country} label="Country" name="country" validate={value=>value.length > 0}/>
|
||||
<Button type="submit">Save Address</Button>
|
||||
</FormController>
|
||||
</>
|
||||
)
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
import React, {useState} from 'react'
|
||||
import Router from 'next/router'
|
||||
import axios from 'axios'
|
||||
import ActionBar from '~/components/admin/actionBar'
|
||||
import {Button, FormController, Input} from '~/components/form'
|
||||
import OrderSummary from '~/components/orderSummary'
|
||||
|
||||
import styles from '../../../../store/checkout/style.module.css'
|
||||
|
||||
ManualOrderPayment.getInitialProps = async ({ctx}) => {
|
||||
const {data: order} = await ctx.axios.get(`/api/orders/${ctx.query.uuid}`)
|
||||
return {order}
|
||||
}
|
||||
|
||||
export default function ManualOrderPayment({order}) {
|
||||
const [recipientEmail, setRecipientEmail] = useState('')
|
||||
const [orderNote, setOrderNote] = useState('')
|
||||
|
||||
const validToSubmit = recipientEmail.length > 0 && orderNote.length > 0
|
||||
|
||||
const currentTransaction = order
|
||||
.transactions.find(transaction => (
|
||||
transaction.payment_state === 'started'
|
||||
))
|
||||
|
||||
const {item_total_price, shipping_price, tax_price, coupon_effective_discount} = currentTransaction
|
||||
const {free_shipping} = currentTransaction.coupon || {}
|
||||
const total_price =
|
||||
(item_total_price && shipping_price)
|
||||
? item_total_price + (free_shipping ? 0 : shipping_price) + tax_price - (coupon_effective_discount || 0)
|
||||
: null
|
||||
|
||||
const formatMoney = money => {
|
||||
if (money === undefined || money === null) return null;
|
||||
|
||||
return '$' + (money / 100).toFixed(2)
|
||||
}
|
||||
|
||||
async function markPaid() {
|
||||
await axios.post(`/api/orders/manual/${order.uuid}/payment`, {
|
||||
recipientEmail,
|
||||
reason: orderNote
|
||||
})
|
||||
|
||||
Router.push('/admin/orders')
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ActionBar title="Set Payment" actions={[
|
||||
{label: 'Edit address', url: `/admin/orders/new/${order.uuid}/address`},
|
||||
{label: 'Cancel', url: `/admin/orders`}
|
||||
]}/>
|
||||
|
||||
<OrderSummary order={order} />
|
||||
|
||||
<FormController>
|
||||
<Input label="Recipient email" name="email" validate={value=>value.length > 0} onChange={setRecipientEmail} />
|
||||
<Input label="Order explanation" name="explanation" validate={value=>value.length > 0} onChange={setOrderNote} />
|
||||
</FormController>
|
||||
|
||||
<div className={styles.checkoutSection}>
|
||||
<h3>Price Total:</h3>
|
||||
<div className={styles.horizContainer}>
|
||||
<div>
|
||||
<table>
|
||||
<tbody>
|
||||
{currentTransaction.cart.items.map(({uuid, count, item}) => (
|
||||
<tr key={uuid}>
|
||||
<td>{item.name} {count > 1 ? `(${count})` : ''}:</td>
|
||||
<td>{formatMoney(count * item.price_cents)}</td>
|
||||
</tr>
|
||||
))}
|
||||
{coupon_effective_discount > 0 && (
|
||||
<tr>
|
||||
<td>Coupon:</td>
|
||||
<td>- {formatMoney(coupon_effective_discount)}</td>
|
||||
</tr>
|
||||
)}
|
||||
<tr className={free_shipping ? 'strikethrough' : undefined}>
|
||||
<td>Shipping:</td>
|
||||
<td>{formatMoney(shipping_price) || '-'}</td>
|
||||
</tr>
|
||||
{tax_price && (
|
||||
<tr>
|
||||
<td>Sales tax:</td>
|
||||
<td>{formatMoney(tax_price)}</td>
|
||||
</tr>
|
||||
)}
|
||||
<tr style={{fontWeight: 'bold'}}>
|
||||
<td>Total:</td>
|
||||
<td style={{textDecoration: 'line-through'}}>{formatMoney(total_price) || '-'}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style={{opacity: .6}}>(Paid by admin)</td>
|
||||
<td style={{fontWeight: 'bold'}}>{formatMoney(0) || '-'}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className={styles.paymentButtons}>
|
||||
<Button enabled={validToSubmit} onClick={markPaid}>Complete Order</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
import React, {useReducer} from 'react'
|
||||
import Router from 'next/router'
|
||||
import axios from 'axios'
|
||||
import ActionBar from '~/components/admin/actionBar'
|
||||
import Table from '~/components/table'
|
||||
import {Button} from '~/components/form'
|
||||
|
||||
ManualOrder.getInitialProps = async ({ctx}) => {
|
||||
const {data: items} = await ctx.axios.get('/api/items?showUnpublished=true')
|
||||
return {items}
|
||||
}
|
||||
|
||||
export default function ManualOrder({items}) {
|
||||
const [itemState, dispatch] = useReducer(itemCountReducer, {})
|
||||
const totalNumber = Object.values(itemState).reduce((a,b) => (b+a), 0)
|
||||
|
||||
const add = (item) => () => dispatch({type: 'add', item: item.uuid})
|
||||
const subtract = (item) => () => dispatch({type: 'subtract', item: item.uuid})
|
||||
const remove = (item) => () => dispatch({type: 'remove', item: item.uuid})
|
||||
const clear = () => dispatch({type: 'clear'})
|
||||
|
||||
async function createOrder(){
|
||||
const items = Object.keys(itemState)
|
||||
.map(uuid => ({ uuid, count: itemState[uuid] }))
|
||||
|
||||
const {data: order} = await axios.put(`/api/orders/manual`, {items})
|
||||
|
||||
Router.push(`/admin/orders/new/${order.uuid}/address`)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ActionBar title="New Order" actions={[
|
||||
{label: 'Cancel', url: `/admin/orders`}
|
||||
]}/>
|
||||
<Table
|
||||
columns={[
|
||||
{name: 'Name', extractor: item => item.name},
|
||||
{name: 'Price', extractor: item => `$${(item.price_cents / 100).toFixed(2)}`},
|
||||
{name: 'Number in stock', extractor: item => item.number_in_stock},
|
||||
{name: 'Number in order', extractor: item => (
|
||||
<span style={{display: 'flex', flexDirection: 'row', width: 120, justifyContent: 'space-between', alignItems: 'center'}}>
|
||||
<Button enabled={itemState[item.uuid] > 0} style={{width: 30}} onClick={subtract(item)}>-</Button>
|
||||
{itemState[item.uuid] || 0}
|
||||
<Button enabled={(itemState[item.uuid] || 0) < item.number_in_stock} style={{width: 30}} onClick={add(item)}>+</Button>
|
||||
</span>
|
||||
)},
|
||||
{name: 'Actions', extractor: item => (
|
||||
<span>
|
||||
<button className="buttonLink" onClick={remove(item)}>Remove all</button>
|
||||
</span>
|
||||
)}
|
||||
]}
|
||||
// Map in an id property so the table can use array.map
|
||||
rows={items.map(item => ({id: item.uuid, ...item}))}
|
||||
/>
|
||||
<p style={{textAlign: 'center'}}>
|
||||
<button className="buttonLink" onClick={clear}>Reset order</button>
|
||||
</p>
|
||||
<p style={{maxWidth: 400, margin: '0 auto'}}>
|
||||
<Button enabled={totalNumber > 0} onClick={createOrder}>Create Order</Button>
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function itemCountReducer(state, action) {
|
||||
switch(action.type) {
|
||||
case 'add':
|
||||
if (state[action.item])
|
||||
return {...state, [action.item]: state[action.item] + 1}
|
||||
else
|
||||
return {...state, [action.item]: 1}
|
||||
case 'subtract':
|
||||
if (state[action.item])
|
||||
return {...state, [action.item]: state[action.item] - 1}
|
||||
else
|
||||
return state
|
||||
case 'remove':
|
||||
const newState = {...state}
|
||||
delete newState[action.item]
|
||||
return newState;
|
||||
case 'clear':
|
||||
return {};
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue