Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CHEC-1009] Customer account page #149

Merged
merged 7 commits into from Nov 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions components/common/Header.js
Expand Up @@ -144,6 +144,11 @@ class Header extends Component {
{ customer && customer.firstname && (<span className="mr-2 font-weight-regular">
Hi, { customer.firstname }!
</span>) }
<Link href="/account">
<a className="font-color-black mx-2">
My account
</a>
</Link>
<button
className="bg-transparent mr-2 font-color-black font-weight-semibold"
type="button"
Expand Down
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -20,6 +20,7 @@
"dotenv": "^8.2.0",
"jwt-decode": "^3.1.2",
"netlify-plugin-onboarding": "^1.0.0",
"moment": "^2.29.1",
"next": "9.5.5",
"next-redux-wrapper": "^6.0.0",
"node-sass": "^4.13.0",
Expand Down
264 changes: 264 additions & 0 deletions pages/account/[id].js
@@ -0,0 +1,264 @@
import React, { useState, useEffect } from 'react';
import { useRouter } from 'next/router';
import commerce from '../../lib/commerce';
import Head from 'next/head';
import Link from 'next/link';
import Root from '../../components/common/Root';
import Footer from '../../components/common/Footer';
import moment from 'moment';
import { useSelector } from 'react-redux'

export default function SingleOrderPage() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rest of this repository uses components, it feels weird to me to move away from that in one place. Can you please refactor this so it uses components instead?

const router = useRouter();
const { id } = router.query;
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const customer = useSelector(state => state.customer)

useEffect(() => {
const fetchOrderById = async (id) => {
try {
const order = await commerce.customer.getOrder(id, customer.id);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to store these in Redux instead, so they'll persist between pages and avoid refetching?


setLoading(false);
setData(order.data);
} catch (err) {
setLoading(false);
setError(err?.message);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this operator supported enough to use yet? If our transpiler supports it then all good, otherwise it's not supported in IE which is a good reason not to use it in this repository

}
};

fetchOrderById(id);
}, [id]);

/**
* Create order date if available
*/
const OrderDate = ({ date: data }) => {
if (!data) {
return null;
}

const date = moment.unix(data);

if (!date.isValid) {
return null
}
return (
<small><strong>Ordered placed on:</strong> { moment(date).format('MMM Do Y') }</small>
)
};

/**
* Create thumbnail if available
*/
const ImageThumb = ({ image: data }) => {
if (!data.media) {
return null;
}

return (
<img className="img-thumbnail h-72 mr-4" alt={data.product_name} src={data.media.source} />
)
};

/**
* Create the billing card
*/
const BillingAddress = ({ address: data }) => {
if (data.length === 0) {
return null;
}

return (
<div>
<h5>Billing address</h5>
<div className="card p-2 mb-4">
<div>
<div><strong>{ data.name }</strong></div>
<div>{ data.street }</div>
<div>{ data.town_city}{(data.town_city && data.county_state) ? ',':'' } { data.county_state }</div>
<div>{ data.country}{(data.town_city && data.county_state) ? ',':'' } { data.postal_zip_code }</div>
</div>
</div>
</div>
)
};

/**
* Create the shipping card
*/
const ShippingAddress = ({ address: data }) => {
if (data.length === 0) {
return null;
}

return (
<div>
<h5>Shipping address</h5>
<div className="card p-2">
<div>
<div><strong>{ data.name }</strong></div>
<div>{ data.street }</div>
<div>{ data.town_city}{(data.town_city && data.county_state) ? ',':'' } { data.county_state }</div>
<div>{ data.country}{(data.town_city && data.county_state) ? ',':'' } { data.postal_zip_code }</div>
</div>
</div>
</div>
)
};

/**
* Create error/loading page
*/
const TemplatePage = ({ page: data }) => {
return (
<Root>
<Head>
<title>commerce</title>
</Head>
<div className="py-5 my-5 text-center">
<h4 className="mt-4">{ data.message }</h4>
</div>
<Footer />
</Root>
)
};

/**
* Render a page if an error occured
*/
if (error) {
return <TemplatePage page={ {message: 'Sorry something went wrong.'} } />
}

/**
* Render loading state
*/
if (loading) {
return <TemplatePage page={ {message: 'Loading'} } />
}

/**
* Render a page if no order found
*/
if (!data) {
return <TemplatePage page={ {message: 'Sorry we cannot find an order witht that number, if you think this is in error please contact us!'} } />
}

/**
* If no errors, return the order page.
*/
return (
<Root>
<Head>
<title>{ data.customer_reference } | commerce</title>
</Head>
<div className="account-container">
<div className="custom-container py-5 my-4 my-sm-5">
<div className="row mt-4">
<div className="col-12">
{/* Breadcrumbs */}
<div className="d-flex pb-4 breadcrumb-container">
<Link href="/account">
<div className="font-size-caption text-decoration-underline cursor-pointer">
Account
</div>
</Link>
<img src="/icon/arrow-right.svg" className="w-16 mx-1" alt="Arrow icon"/>
<div className="font-size-caption font-weight-bold cursor-pointer">
{ data.customer_reference }
</div>
</div>
</div>
</div>
<div className="row mt-5 pt-5">
<div className="col-12">
<h2 className="font-size-header mb-4 pt-5 text-center">
Order: #{ data.customer_reference }
</h2>
{alert}
</div>
</div>
<div className="row mt-5 pt-5">
<div className="col-12 col-md-8 col-lg-8">
<div className="d-flex flex-row justify-content-between">
<h5>Items</h5>
<OrderDate date={data.created}/>
</div>
<table className="table table-bordered">
<thead>
<tr>
<th>Product</th>
<th>Price</th>
<th>Quantity</th>
<th>Total</th>
</tr>
</thead>
<tbody>
{ data.order.line_items.map((item) => {
return (
<tr key={ item.id }>
<td>
<ImageThumb image={item}/>
{ item.product_name }
</td>
<td>{ item.price.formatted_with_symbol }</td>
<td>{ item.quantity }</td>
<td>{ item.line_total.formatted_with_symbol }</td>
</tr>
)
})}
</tbody>
<tfoot>
<tr>
<td colSpan="3" className="border-right-0">
Subtotal
</td>
<td className="border-left-0 text-right">
{ data.order.subtotal.formatted_with_symbol}
</td>
</tr>
<tr>
<td colSpan="3" className="border-right-0">
Shipping
</td>
<td className="border-left-0 text-right">
{ data.order.shipping.price.formatted_with_symbol}
</td>
</tr>
<tr>
<td colSpan="3" className="border-right-0">
Tax
</td>
<td className="border-left-0 text-right">
{ data.order.tax.amount.formatted_with_symbol}
</td>
</tr>
<tr>
<td colSpan="3" className="border-right-0">
<strong>
Total
</strong>
</td>
<td className="border-left-0 text-right">
<strong>
{ data.order.total.formatted_with_symbol}
</strong>
</td>
</tr>
</tfoot>
</table>
</div>
<div className="col-12 col-md-4 col-lg-4 row-content">
<BillingAddress address={data.billing} />
<ShippingAddress address={data.shipping} />
</div>
</div>
</div>
</div>
<Footer />
</Root>
);
}