diff --git a/package.json b/package.json index 31bfeaf..2ffdb85 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "money": "^0.2.0", "netlify-auth-js": "^0.5.2", "react": "^15.4.1", + "react-date-range": "^0.9.4", "react-dom": "^15.4.1", "react-gravatar": "^2.6.1", "semantic-ui-css": "^2.2.4", diff --git a/src/Components/Order/Address.js b/src/Components/Order/Address.js index 8f24b30..8e21a7f 100644 --- a/src/Components/Order/Address.js +++ b/src/Components/Order/Address.js @@ -11,7 +11,7 @@ export default function({title, address, href, onLink}: args) { {address &&
- {address.first_name} {address.last_name}
+ {address.name}
{address.company && {address.company}
} {address.address1}
{address.address2 && {address.address2}
} diff --git a/src/Components/Order/AddressEditor.js b/src/Components/Order/AddressEditor.js index adaaee2..bbbb679 100644 --- a/src/Components/Order/AddressEditor.js +++ b/src/Components/Order/AddressEditor.js @@ -48,8 +48,7 @@ export default class AddressEditor extends Component { return addr &&
- - + diff --git a/src/Components/Order/index.js b/src/Components/Order/index.js index e6b8f36..2063feb 100644 --- a/src/Components/Order/index.js +++ b/src/Components/Order/index.js @@ -9,6 +9,8 @@ import Address from './Address'; import AddressEditor from './AddressEditor'; import './styles.css'; +import { requiresShipping } from '../../helpers' + function formatId(id: string) { return id.split("-").pop(); } @@ -116,16 +118,15 @@ export default class OrderView extends Component { handleReceipt = (e: SyntheticEvent) => { e.preventDefault(); - const {commerce, config} = this.props; const {order} = this.state; if (!order) { return; } - - const openWindow = window.open("about:blank", "Receipt"); - console.log(config.receiptTemplate) - commerce.orderReceipt(order.id, config.receiptTemplate).then((data) => { - openWindow.document.body.innerHTML = data.data; - }); - + let user = localStorage.getItem('netlify.auth.user') + try { + user = JSON.parse(user) + window.open(`https://smashingmagazine.com/receipts/?id=${order.id}&jwt=${user.jwt_token}`, "Receipt"); + } catch (e) { + console.log(e) + } } render() { @@ -135,7 +136,7 @@ export default class OrderView extends Component { const item = params.item ? EditableItems[params.item] : null; return @@ -145,7 +146,7 @@ export default class OrderView extends Component {
- Order Details + Order #{order && order.invoice_number} {params.id} @@ -178,14 +179,17 @@ export default class OrderView extends Component { /> - -
- + + + {requiresShipping(order) && ( +
+ )} + } @@ -262,8 +266,8 @@ export default class OrderView extends Component {
Billing Status - {order && order.payment_state} - {order && order.payment_state === 'paid' && receipt} + {order && order.payment_state.toUpperCase()} + {order && order.payment_state === 'paid' &&

Show receipt

}
@@ -282,23 +286,26 @@ export default class OrderView extends Component { } - -
- Shipping Status - Shipping to {order && order.shipping_address && order.shipping_address.country} -
+ {false && requiresShipping(order) && ( + +
+ Shipping Status + Shipping to {order && order.shipping_address && order.shipping_address.country} +
+ + + + + + {order && newFullfilementState && newFullfilementState !== order.fulfillment_state && } + +
+ )} -
- - - - {order && newFullfilementState && newFullfilementState !== order.fulfillment_state && } -
-
diff --git a/src/Components/Orders/index.js b/src/Components/Orders/index.js index 6d9e1ec..f83c341 100644 --- a/src/Components/Orders/index.js +++ b/src/Components/Orders/index.js @@ -2,7 +2,7 @@ import type {Commerce, Pagination, Order, Address} from '../../Types'; import _ from 'lodash'; import React, {Component} from 'react'; -import {Button, Checkbox, Grid, Dimmer, Dropdown, Loader, Table, Input, Select} from 'semantic-ui-react'; +import {Button, Checkbox, Grid, Dimmer, Dropdown, Loader, Table, Icon, Header, Input, Select, Modal} from 'semantic-ui-react'; import Layout from '../Layout'; import PaginationView, {pageFromURL} from '../Pagination'; import ErrorMessage from '../Messages/Error'; @@ -10,6 +10,10 @@ import distanceInWordsToNow from 'date-fns/distance_in_words_to_now'; import countries from '../../data/countries.json'; import 'csvexport/dist/Export.min'; import './Orders.css'; +import { defaultRanges, Calendar, DateRange } from 'react-date-range' +import moment from 'moment' + +import { requiresShipping, formatDateInterval } from '../../helpers' const STORED_FIELDS_KEY = 'commerce.admin.orderFields'; @@ -32,11 +36,20 @@ function formatLineItems(order: Order, csv: boolean) { ) } +function formatName(order: Order, csv: boolean) { + + return _.get(order, 'billing_address.name') || _.get(order, 'shipping_address.name') +} + function formatLineItemTypes(order: Order, csv: boolean) { let types = []; (order.line_items || []).map(item => !types.includes(item.type) && types.push(item.type)) + if (csv) { + return types.map(t => `${t}`).join(', ').replace(/,\s*$/, '') + } + return types.map(t =>
{t}
) } @@ -80,9 +93,10 @@ function formatDateField(field: 'created_at' | 'updated_at') { } const fields = { - ID: {}, + ID: { fn: (order) => order.invoice_number }, Email: {sort: "email"}, Items: {fn: formatLineItems}, + Name: {fn: formatName}, Type: {fn: formatLineItemTypes}, "Shipping Address": {fn: formatAddress("shipping_address")}, "Shipping Country": {fn: (order) => order.shipping_address.country}, @@ -90,7 +104,7 @@ const fields = { "Billing Country": {fn: (order) => order.billing_address.country}, Taxes: {sort: "taxes", fn: formatPriceField("taxes")}, Subtotal: {sort: "subtotal", fn: formatPriceField("subtotal")}, - "Shipping State": {fn: (order) => order.fulfillment_state}, + "Shipping State": {fn: (order) => requiresShipping(order) ? order.fulfillment_state : null}, "Payment State": {}, Total: {sort: "total", fn: formatPriceField("total")}, "Created At": {sort: "created_at", fn: formatDateField("created_at")}, @@ -98,15 +112,16 @@ const fields = { }; const enabledFields = { - ID: false, + ID: true, Items: true, Type: true, + Name: true, Email: true, "Shipping Address": false, "Shipping Country": false, "Billing Address": false, "Billing Country": false, - "Shipping State": true, + //"Shipping State": false, "Payment State": true, Taxes: false, Subtotal: false, @@ -141,7 +156,6 @@ class OrderDetail extends Component { render() { const {order, enabledFields} = this.props; - return @@ -190,7 +204,7 @@ export default class Orders extends Component { error: null, filters: [], page: pageFromURL(), - enabledFields: storedFields ? JSON.parse(storedFields) : Object.assign({}, enabledFields), + enabledFields: Object.assign({}, enabledFields, storedFields ? JSON.parse(storedFields) : {}), tax: false, shippingCountries: null, orders: null, @@ -267,7 +281,7 @@ export default class Orders extends Component { const addr = formatField(field, order); console.log(addr, field, order) addressFields.forEach((field) => { - formattedOrder[`${match[1]} ${field}`] = addr[field]; + formattedOrder[`${match[1]} ${field}`] = (field === 'zip') ? `="${addr[field]}"` : addr[field]; }) } else { formattedOrder[field] = fields[field].fn ? fields[field].fn(order, true) : formatField(field, order); @@ -344,15 +358,23 @@ export default class Orders extends Component { }); } - orderQuery(page: ?number) { - const query: Object = { + orderQuery(page: ?number, per_page: ?number) { + const { startDate, endDate } = this.state + let query: Object = { user_id: "all", - per_page: PER_PAGE, - page: page || this.state.page + per_page: per_page || PER_PAGE, + page: page || this.state.page, + payment_state: 'paid', }; this.state.filters.forEach((filter) => { query[filter] = OrdersFilters[filter](this.state); }); + + if (startDate && endDate) { + query.from = startDate.unix() + query.to = endDate.unix() + } + return query; } @@ -362,7 +384,7 @@ export default class Orders extends Component { return Promise.resolve(selected); } - return this.props.commerce.orderHistory(this.orderQuery(page || 1)) + return this.props.commerce.orderHistory(this.orderQuery(page || 1, 1000)) .then(({orders, pagination}) => ( pagination.last === pagination.current ? orders : this.downloadAll(pagination.next).then(o => orders.concat(o)) )); @@ -403,21 +425,57 @@ export default class Orders extends Component { handleReceipts = (e: SyntheticEvent) => { e.preventDefault(); - const {commerce} = this.props; const {orders} = this.state; - const openWindow = window.open("about:blank", "Receipts"); + const selected = (orders || []).filter((o) => o.selected && o.payment_state === 'paid') + let user = localStorage.getItem('netlify.auth.user') - Promise.all((orders || []).filter((o) => o.selected && o.payment_state === 'paid').map((order) => commerce.orderReceipt(order.id))) - .then((receipts) => { - openWindow.document.body.innerHTML = receipts.map((data) => data.data).join("
"); - }); + console.log(selected.length) + user = JSON.parse(user) + selected.forEach((order, i) => { + setTimeout(() => window.open(`https://smashingmagazine.com/receipts/?id=${order.id}&jwt=${user.jwt_token}`, `Receipt ${i + 1}`), i * 100) + }) } + handleDatePicker = (isReset, isApply) => this.setState({ + openDatePicker: !this.state.openDatePicker, + startDate: !isReset ? this.state.startDate : null, + endDate: !isReset ? this.state.endDate : null, + }, () => (isReset || isApply) && this.loadOrders()) + render() { const {onLink} = this.props; - const {loading, allSelected, downloading, error, orders, pagination, tax, enabledFields, searchScope, selection} = this.state; + const {loading, startDate, endDate, allSelected, openDatePicker, downloading, error, orders, pagination, tax, enabledFields, searchScope, selection} = this.state; + const interval = formatDateInterval(startDate, endDate) + return + +
+ + this.setState({ startDate, endDate })} + onChange={ ({ startDate, endDate }) => this.setState({ startDate, endDate })} + theme={{ + DateRange: { width: 850 }, + Calendar : { width: 350 }, + PredefinedRanges : { marginLeft: 10, marginTop: 10 } + }} + /> + + + + + + + @@ -440,6 +498,12 @@ export default class Orders extends Component { + + + + diff --git a/src/helpers/index.js b/src/helpers/index.js new file mode 100644 index 0000000..8b8e8ea --- /dev/null +++ b/src/helpers/index.js @@ -0,0 +1,33 @@ +import get from 'lodash/get' +import isArray from 'lodash/isArray' +import moment from 'moment' + +const requiresShipping = order => { + const items = isArray(order) ? order : (get(order, 'line_items') || []) + return !!items.filter(i => i.type === 'Book')[0] +} + +export { requiresShipping } + + +const formatDateInterval = (startDate, endDate) => { + let interval = null + if (startDate && endDate) { + interval = startDate.format('DD/MM/YY') + '–' + endDate.format('DD/MM/YY') + + if (startDate.isSame(endDate, 'year')) { + interval = startDate.format('MMM DD') + '–' + endDate.format('MMM DD, YYYY') + if (startDate.isSame(endDate, 'month')) { + interval = startDate.format('MMM DD') + '–' + endDate.format('DD, YYYY') + if (startDate.isSame(endDate, 'day')) { + interval = startDate.format('MMM DD, YYYY') + } + } + if (endDate.isSame(moment(), 'year')) interval = interval.replace(endDate.format(', YYYY'), '') + } + } + + return interval +} + +export { formatDateInterval } diff --git a/yarn.lock b/yarn.lock index b263ac9..50c8130 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1104,7 +1104,7 @@ clap@^1.0.9: dependencies: chalk "^1.1.3" -classnames@^2.1.5: +classnames@^2.1.5, classnames@^2.2.1: version "2.2.5" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d" @@ -2117,6 +2117,18 @@ fb-watchman@^1.8.0, fb-watchman@^1.9.0: dependencies: bser "1.0.2" +fbjs@^0.8.16: + version "0.8.16" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db" + dependencies: + core-js "^1.0.0" + isomorphic-fetch "^2.1.1" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^0.7.9" + fbjs@^0.8.9: version "0.8.12" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.12.tgz#10b5d92f76d45575fd63a217d4ea02bea2f8ed04" @@ -3351,7 +3363,7 @@ longest@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" dependencies: @@ -3498,6 +3510,10 @@ minimist@^1.1.1, minimist@^1.2.0: dependencies: minimist "0.0.8" +moment@^2.10.6: + version "2.21.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.21.0.tgz#2a114b51d2a6ec9e6d83cf803f838a878d8a023a" + money@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/money/-/money-0.2.0.tgz#7eada2df1009df935fa18d4fb1d620ddacce02c2" @@ -3685,7 +3701,7 @@ oauth-sign@~0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" -object-assign@4.1.1, object-assign@^4.0.1, object-assign@^4.1.0: +object-assign@4.1.1, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -4198,6 +4214,14 @@ promise@7.1.1, promise@^7.1.1: dependencies: asap "~2.0.3" +prop-types@^15.5.10: + version "15.6.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca" + dependencies: + fbjs "^0.8.16" + loose-envify "^1.3.1" + object-assign "^4.1.1" + prop-types@^15.5.7, prop-types@~15.5.7: version "15.5.8" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.8.tgz#6b7b2e141083be38c8595aa51fc55775c7199394" @@ -4274,6 +4298,14 @@ rc@^1.1.7: minimist "^1.2.0" strip-json-comments "~2.0.1" +react-date-range@^0.9.4: + version "0.9.4" + resolved "https://registry.yarnpkg.com/react-date-range/-/react-date-range-0.9.4.tgz#d97515545a28edc717b674e169a50a215d69ec15" + dependencies: + classnames "^2.2.1" + moment "^2.10.6" + prop-types "^15.5.10" + react-dev-utils@^0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-0.5.2.tgz#50d0b962d3a94b6c2e8f2011ed6468e4124bc410"