orders
Former-commit-id: df0cca4571f50a90d7da755bab48e06c4051bce7
This commit is contained in:
@@ -7,11 +7,13 @@ const AUTH0CONFIG = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let auth0 = null;
|
let auth0 = null;
|
||||||
|
let ready = false;
|
||||||
|
|
||||||
async function CheckRedirect() {
|
async function CheckRedirect() {
|
||||||
const isAuthenticated = await auth0.isAuthenticated();
|
const isAuthenticated = await auth0.isAuthenticated();
|
||||||
if (isAuthenticated) {
|
if (isAuthenticated) {
|
||||||
localStorage.setItem('loggedIn', true);
|
localStorage.setItem('loggedIn', true);
|
||||||
|
ready = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,6 +25,7 @@ async function CheckRedirect() {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
window.alert(e.message || 'authentication error, sorry');
|
window.alert(e.message || 'authentication error, sorry');
|
||||||
localStorage.setItem('loggedIn', false);
|
localStorage.setItem('loggedIn', false);
|
||||||
|
ready = false;
|
||||||
Signout();
|
Signout();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,9 +35,7 @@ async function CheckRedirect() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function InitAuth0() {
|
export async function InitAuth0() {
|
||||||
// localStorage.setItem('loggedIn', false);
|
ready = false;
|
||||||
// localStorage.setItem('user', 'Guest');
|
|
||||||
// localStorage.setItem('admin', false);
|
|
||||||
|
|
||||||
auth0 = await window.createAuth0Client({
|
auth0 = await window.createAuth0Client({
|
||||||
domain: AUTH0CONFIG.domain,
|
domain: AUTH0CONFIG.domain,
|
||||||
@@ -50,6 +51,7 @@ export async function InitAuth0() {
|
|||||||
localStorage.setItem('user', user.given_name || user.nickname);
|
localStorage.setItem('user', user.given_name || user.nickname);
|
||||||
NotifyNavbar('login', user);
|
NotifyNavbar('login', user);
|
||||||
localStorage.setItem('loggedIn', true);
|
localStorage.setItem('loggedIn', true);
|
||||||
|
ready = true;
|
||||||
|
|
||||||
// tell the server about the logon, so that it can make the proper
|
// tell the server about the logon, so that it can make the proper
|
||||||
// entry in the database, if there is for example an address
|
// entry in the database, if there is for example an address
|
||||||
@@ -69,6 +71,11 @@ export async function InitAuth0() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function GetToken() {
|
export async function GetToken() {
|
||||||
|
/* eslint-disable-next-line */
|
||||||
|
while (!ready) {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 100));
|
||||||
|
}
|
||||||
|
|
||||||
const token = await auth0.getTokenSilently();
|
const token = await auth0.getTokenSilently();
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
@@ -90,6 +97,7 @@ export async function LoginSignup() {
|
|||||||
|
|
||||||
export async function Signout() {
|
export async function Signout() {
|
||||||
localStorage.setItem('loggedIn', false);
|
localStorage.setItem('loggedIn', false);
|
||||||
|
ready = false;
|
||||||
localStorage.setItem('user', 'Guest');
|
localStorage.setItem('user', 'Guest');
|
||||||
localStorage.setItem('admin', false);
|
localStorage.setItem('admin', false);
|
||||||
await auth0.logout({
|
await auth0.logout({
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
<script type="module" src="/components/search.mjs"></script>
|
<script type="module" src="/components/search.mjs"></script>
|
||||||
<script type="module" src="/components/basket.mjs"></script>
|
<script type="module" src="/components/basket.mjs"></script>
|
||||||
<script type="module" src="/components/basket-popout.mjs"></script>
|
<script type="module" src="/components/basket-popout.mjs"></script>
|
||||||
<script type="module" src="/components/immutable-basket-list.mjs"></script>
|
<script type="module" src="/components/immutable-list.mjs"></script>
|
||||||
<script type="module" src="/components/accessability-popout.mjs"></script>
|
<script type="module" src="/components/accessability-popout.mjs"></script>
|
||||||
<script type="module" src="/components/notificationbar.mjs"></script>
|
<script type="module" src="/components/notificationbar.mjs"></script>
|
||||||
<script type="module" src="/components/tag.mjs"></script>
|
<script type="module" src="/components/tag.mjs"></script>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
<script type="module" src="/components/navbar.mjs"></script>
|
<script type="module" src="/components/navbar.mjs"></script>
|
||||||
<script type="module" src="/components/search.mjs"></script>
|
<script type="module" src="/components/search.mjs"></script>
|
||||||
<script type="module" src="/components/basket-popout.mjs"></script>
|
<script type="module" src="/components/basket-popout.mjs"></script>
|
||||||
<script type="module" src="/components/immutable-basket-list.mjs"></script>
|
<script type="module" src="/components/immutable-list.mjs"></script>
|
||||||
<script type="module" src="/components/checkout.mjs"></script>
|
<script type="module" src="/components/checkout.mjs"></script>
|
||||||
<script type="module" src="/components/accessability-popout.mjs"></script>
|
<script type="module" src="/components/accessability-popout.mjs"></script>
|
||||||
<script type="module" src="/components/notificationbar.mjs"></script>
|
<script type="module" src="/components/notificationbar.mjs"></script>
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ class BasketPopout extends Component {
|
|||||||
{this.state.total} Items
|
{this.state.total} Items
|
||||||
</div>
|
</div>
|
||||||
<div class="popup-content">
|
<div class="popup-content">
|
||||||
<immutable-basket-list-component h="400px" class="basket-list"></immutable-basket-list-component>
|
<immutable-list-component source="basket" h="400px" class="basket-list"></immutable-list-component>
|
||||||
</div>
|
</div>
|
||||||
<div class="popup-footer">
|
<div class="popup-footer">
|
||||||
<span class="popup-footer-total">Subtotal: £${parseFloat(this.state.subtotal).toFixed(2)}</span>
|
<span class="popup-footer-total">Subtotal: £${parseFloat(this.state.subtotal).toFixed(2)}</span>
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ class Checkout extends Component {
|
|||||||
<div class="checkout-body-right">
|
<div class="checkout-body-right">
|
||||||
<div class="checkout-summary-title section-title">Your Order <a href="/basket"><span class="edit-basket">edit basket</span><a> </div>
|
<div class="checkout-summary-title section-title">Your Order <a href="/basket"><span class="edit-basket">edit basket</span><a> </div>
|
||||||
<div class="checkout-summary">
|
<div class="checkout-summary">
|
||||||
<immutable-basket-list-component h="300px"></immutable-basket-list-component>
|
<immutable-list-component source="basket" h="300px"></immutable-list-component>
|
||||||
|
|
||||||
<div class="checkout-summary-prices">
|
<div class="checkout-summary-prices">
|
||||||
<div class="checkout-summary-prices-row">
|
<div class="checkout-summary-prices-row">
|
||||||
@@ -363,10 +363,10 @@ class Checkout extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// clear basket
|
// clear basket
|
||||||
await Basket.ClearBasket();
|
Basket.ClearBasket();
|
||||||
|
|
||||||
// redirect to receipt
|
// redirect to receipt
|
||||||
window.location.href = `/order/${req.data.receipt_id}`;
|
window.location.href = `/orders/order?id=${req.data.receipt_id}`;
|
||||||
// we're done !
|
// we're done !
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ export class Component extends HTMLElement {
|
|||||||
Update() { }
|
Update() { }
|
||||||
Render() { Component.__WARN('Render'); }
|
Render() { Component.__WARN('Render'); }
|
||||||
OnRender() { }
|
OnRender() { }
|
||||||
|
OnUnMount() { }
|
||||||
static __IDENTIFY() { Component.__WARN('identify'); }
|
static __IDENTIFY() { Component.__WARN('identify'); }
|
||||||
|
|
||||||
async connectedCallback() {
|
async connectedCallback() {
|
||||||
@@ -60,6 +61,7 @@ export class Component extends HTMLElement {
|
|||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
this.root.innerHTML = '';
|
this.root.innerHTML = '';
|
||||||
|
this.OnUnMount();
|
||||||
}
|
}
|
||||||
|
|
||||||
watchAttributeChange(callback) {
|
watchAttributeChange(callback) {
|
||||||
|
|||||||
233
client/public/components/css/order.css
Normal file
233
client/public/components/css/order.css
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
.order-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-top: 20px;
|
||||||
|
font-size: 2em;
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.monospace {
|
||||||
|
font-family: monospace;
|
||||||
|
background-color: rgb(209, 209, 209);
|
||||||
|
font-size: 0.9em;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-header-subtitle {
|
||||||
|
font-size: 0.6em;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-body {
|
||||||
|
margin-top: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-body-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
order: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-breakdown-table {
|
||||||
|
margin-top: 20px;
|
||||||
|
width: 100%;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-breakdown-table-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 30%;
|
||||||
|
height: 7em;
|
||||||
|
border: #ccc 1px solid;
|
||||||
|
/* background-color: #FAFBFB; */
|
||||||
|
padding: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
box-shadow: 0 0 5px rgba(0,0,0,0.75);
|
||||||
|
clip-path: inset(0px -5px 0px 0px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-breakdown-table-header {
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #444;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-breakdown-table-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-breakdown-table-row-value {
|
||||||
|
font-size: 1em;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-price-table-row {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 1em;
|
||||||
|
color: #444;
|
||||||
|
font-weight: lighter;
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (pointer:none), (pointer:coarse), screen and (max-width: 900px) {
|
||||||
|
.order-breakdown-table {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-breakdown-table-item {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* loosely based on https://codepen.io/luisar/pen/JjoOZav */
|
||||||
|
|
||||||
|
.order-body-item-table {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-body-item-left {
|
||||||
|
width: 49%;
|
||||||
|
order: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-body-item-right {
|
||||||
|
width: 49%;
|
||||||
|
order: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-body-header {
|
||||||
|
margin-top: 20px;
|
||||||
|
font-size: 1.3em;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-track-step {
|
||||||
|
display: flex;
|
||||||
|
height: 7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-track-step:last-child {
|
||||||
|
overflow: hidden;
|
||||||
|
height: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-track-step:last-child .order-track-status div:last-of-type {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-track-status {
|
||||||
|
margin-right: 1.5rem;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-track-step-icon {
|
||||||
|
display: block;
|
||||||
|
width: 1.8em;
|
||||||
|
height: 1.8em;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #F2CA52;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-track-step-line {
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 4px;
|
||||||
|
height: 7rem;
|
||||||
|
background: #F2CA52;
|
||||||
|
}
|
||||||
|
|
||||||
|
.completed {
|
||||||
|
background: #0EAD69;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-body-status-title {
|
||||||
|
transform: translateY(-0.6em);
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.when {
|
||||||
|
font-size: 1em;
|
||||||
|
color: #444;
|
||||||
|
font-weight: lighter;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-track {
|
||||||
|
margin-top: 20px;
|
||||||
|
transition: all .3s height 0.3s;
|
||||||
|
transform-origin: top center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.orders-list-body {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.orders-list-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-top: 30px;
|
||||||
|
width: 100%;
|
||||||
|
order: 0;
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-list-item-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-list-item-header-subtitle {
|
||||||
|
font-weight: lighter;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-list-item-body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
order: 0;
|
||||||
|
}
|
||||||
@@ -1,26 +1,25 @@
|
|||||||
import { RegisterComponent, Component } from './components.mjs';
|
import { RegisterComponent, Component } from './components.mjs';
|
||||||
import { GetBasketTotalPrice } from '../basket.mjs';
|
|
||||||
import * as LocalStorageListener from '../localstorage-listener.mjs';
|
import * as LocalStorageListener from '../localstorage-listener.mjs';
|
||||||
|
|
||||||
class ImmutableBasketList extends Component {
|
// This was changed to be generic from the original: ImmutableBasketList
|
||||||
static __IDENTIFY() { return 'immutable-basket-list'; }
|
// so it acts now on a local storage "source" instead of just basket
|
||||||
|
// works i guess /shrug
|
||||||
|
class ImmutableList extends Component {
|
||||||
|
static __IDENTIFY() { return 'immutable-list'; }
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super(ImmutableBasketList);
|
super(ImmutableList);
|
||||||
}
|
}
|
||||||
|
|
||||||
async OnLocalBasketUpdate() {
|
OnLocalStorageListener() {
|
||||||
const basket = localStorage.getItem('basket');
|
const itemsList = localStorage.getItem(this.state.source);
|
||||||
|
|
||||||
if (basket) {
|
if (itemsList) {
|
||||||
try {
|
try {
|
||||||
const basketJSON = JSON.parse(basket);
|
const itemsJson = JSON.parse(itemsList);
|
||||||
const subtotal = await GetBasketTotalPrice();
|
|
||||||
this.setState({
|
this.setState({
|
||||||
...this.getState,
|
...this.getState,
|
||||||
items: basketJSON.items,
|
items: itemsJson.items,
|
||||||
total: basketJSON.total,
|
|
||||||
subtotal,
|
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
@@ -29,26 +28,17 @@ class ImmutableBasketList extends Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
...this.getState,
|
...this.getState,
|
||||||
items: {},
|
items: {},
|
||||||
total: 0,
|
|
||||||
subtotal: 0,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
OnMount() {
|
OnMount() {
|
||||||
LocalStorageListener.ListenOnKey('basket', () => {
|
LocalStorageListener.ListenOnKey(this.state.source, () => {
|
||||||
this.OnLocalBasketUpdate(Object.bind(this));
|
this.OnLocalStorageListener(Object.bind(this));
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setState({
|
this.OnLocalStorageListener(Object.bind(this));
|
||||||
...this.getState,
|
|
||||||
items: {},
|
|
||||||
total: 0,
|
|
||||||
subtotal: 0,
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
this.OnLocalBasketUpdate(Object.bind(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Render() {
|
Render() {
|
||||||
@@ -115,4 +105,4 @@ class ImmutableBasketList extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RegisterComponent(ImmutableBasketList);
|
RegisterComponent(ImmutableList);
|
||||||
@@ -46,22 +46,29 @@ class NavBar extends Component {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (localStorage.admin === 'true' || localStorage.admin === true) {
|
|
||||||
this.root.querySelector('.stock-mode').style.display = 'flex';
|
|
||||||
} else {
|
|
||||||
this.root.querySelector('.stock-mode').style.display = 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
const account = this.root.querySelector('.account-item');
|
const account = this.root.querySelector('.account-item');
|
||||||
|
|
||||||
// doing this with proper dom manipulation wasn't working
|
if (localStorage.admin === 'true' || localStorage.admin === true) {
|
||||||
account.innerHTML = `
|
this.root.querySelector('.stock-mode').style.display = 'flex';
|
||||||
<a class="nav-link" href="#">${localStorage.user}▾</a>
|
account.innerHTML = `
|
||||||
<ul class="sub-nav" >
|
<a class="nav-link" href="#">${localStorage.user}▾</a>
|
||||||
<li><a class="sub-nav-link" href="#">My Orders</a></li>
|
<ul class="sub-nav" >
|
||||||
<li><a class="sub-nav-link logout-button" href="#">Log Out</a></li>
|
<li><a class="sub-nav-link" href="/orders">My Orders</a></li>
|
||||||
</ul>
|
<li><a class="sub-nav-link" href="">Add or Remove Stock</a></li>
|
||||||
`;
|
<li><a class="sub-nav-link" href="">Review Open Orders</a></li>
|
||||||
|
<li><a class="sub-nav-link logout-button" href="#">Log Out</a></li>
|
||||||
|
</ul>
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
this.root.querySelector('.stock-mode').style.display = 'none';
|
||||||
|
account.innerHTML = `
|
||||||
|
<a class="nav-link" href="#">${localStorage.user}▾</a>
|
||||||
|
<ul class="sub-nav" >
|
||||||
|
<li><a class="sub-nav-link" href="/orders">My Orders</a></li>
|
||||||
|
<li><a class="sub-nav-link logout-button" href="#">Log Out</a></li>
|
||||||
|
</ul>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
const logoutButton = account.querySelector('.logout-button');
|
const logoutButton = account.querySelector('.logout-button');
|
||||||
logoutButton.addEventListener('click', () => {
|
logoutButton.addEventListener('click', () => {
|
||||||
|
|||||||
60
client/public/components/order-list.mjs
Normal file
60
client/public/components/order-list.mjs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { RegisterComponent, Component, SideLoad } from './components.mjs';
|
||||||
|
import * as Auth from '../auth.mjs';
|
||||||
|
|
||||||
|
class OrderList extends Component {
|
||||||
|
static __IDENTIFY() { return 'order-list'; }
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(OrderList);
|
||||||
|
}
|
||||||
|
|
||||||
|
async OnMount() {
|
||||||
|
const options = {
|
||||||
|
method: 'GET',
|
||||||
|
headers: { Authorization: `Bearer ${await Auth.GetToken()}` },
|
||||||
|
};
|
||||||
|
|
||||||
|
const res = await fetch('/api/auth/orders', options).then(res => res.json());
|
||||||
|
|
||||||
|
console.log(res);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
...this.getState,
|
||||||
|
orders: res.data,
|
||||||
|
}, false);
|
||||||
|
console.log(this.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
Render() {
|
||||||
|
return {
|
||||||
|
template: /* html */`
|
||||||
|
<div class="order-header">
|
||||||
|
<span class="order-header-title">Your Orders</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="orders-list-body">
|
||||||
|
${this.state.orders.map(order => /* html */`
|
||||||
|
<div class="orders-list-item">
|
||||||
|
<a href="/orders/order?id=${order.id}"><div class="order-list-item">
|
||||||
|
<div class="order-list-item-header">
|
||||||
|
<span class="order-list-item-header-title">Order #${order.id}</span>
|
||||||
|
<span class="order-list-item-header-subtitle">Placed on ${new Date(order.date_placed).toDateString()}</span>
|
||||||
|
</div>
|
||||||
|
<div class="order-list-item-body">
|
||||||
|
<span class="order-list-item-body-item-title">Paid: £${parseFloat(order.subtotal_paid).toFixed(2)}</span>
|
||||||
|
<span class="order-list-item-body-item-title">Shipped? ${order.shipped ? 'Yes' : 'No'}</span>
|
||||||
|
</div>
|
||||||
|
</div></a>
|
||||||
|
</div>
|
||||||
|
`).join('')}
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
style: SideLoad('/components/css/order.css'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
OnRender() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RegisterComponent(OrderList);
|
||||||
129
client/public/components/order.mjs
Normal file
129
client/public/components/order.mjs
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
import { RegisterComponent, Component, SideLoad } from './components.mjs';
|
||||||
|
|
||||||
|
class Order extends Component {
|
||||||
|
static __IDENTIFY() { return 'order'; }
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(Order);
|
||||||
|
}
|
||||||
|
|
||||||
|
async OnMount() {
|
||||||
|
// get order id from search param
|
||||||
|
const query = new URLSearchParams(window.location.search);
|
||||||
|
const id = query.get('id');
|
||||||
|
|
||||||
|
const res = (await fetch(`/api/order/${id}`).then(res => res.json())).data;
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
...this.getState,
|
||||||
|
id,
|
||||||
|
...res,
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
console.log(this.state);
|
||||||
|
localStorage.setItem('viewing-order', JSON.stringify({ items: res.items }));
|
||||||
|
}
|
||||||
|
|
||||||
|
Render() {
|
||||||
|
return {
|
||||||
|
template: /* html */`
|
||||||
|
<div class="order-header">
|
||||||
|
<span class="order-header-title">Thank You For Your Order!</span>
|
||||||
|
<span class="order-header-subtitle">Your order number is <span class="monospace">{this.state.id}</span></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="order-body">
|
||||||
|
<div class="order-body-item">
|
||||||
|
<span class="order-body-date-placed">Placed on ${new Date(this.state.date_placed).toDateString()} at ${new Date(this.state.date_placed).toLocaleTimeString()}</span>
|
||||||
|
</div>
|
||||||
|
<div class="order-body-item">
|
||||||
|
<div class="order-breakdown-table">
|
||||||
|
<div class="order-breakdown-table-item">
|
||||||
|
<div class="order-breakdown-table-header">Address</div>
|
||||||
|
<div class="order-breakdown-table-row">
|
||||||
|
<span class="order-breakdown-table-row-value">John Doe</span>
|
||||||
|
<span class="order-breakdown-table-row-value">123 Example Av,</span>
|
||||||
|
<span class="order-breakdown-table-row-value">Portsmouth,</span>
|
||||||
|
<span class="order-breakdown-table-row-value">PO1 1EA</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="order-breakdown-table-item">
|
||||||
|
<div class="order-breakdown-table-header">Payment Card</div>
|
||||||
|
<div class="order-breakdown-table-row">
|
||||||
|
<span class="order-breakdown-table-row-value">**** **** **** 1111</span>
|
||||||
|
<span class="order-breakdown-table-row-value">Expires: 01/20</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="order-breakdown-table-item">
|
||||||
|
<div class="order-price-table">
|
||||||
|
<div class="order-breakdown-table-header">Payment Breakdown</div>
|
||||||
|
<div class="order-price-table-row">
|
||||||
|
<span class="order-price-table-row-title">Discount Applied</span>
|
||||||
|
<span class="order-price-table-row-price">£${parseFloat(this.state.discount).toFixed(2)}</span>
|
||||||
|
</div>
|
||||||
|
<div class="order-price-table-row">
|
||||||
|
<span class="order-price-table-row-title">Total Paid</span>
|
||||||
|
<span class="order-price-table-row-price">£${parseFloat(this.state.subtotal_paid).toFixed(2)}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="order-body-item">
|
||||||
|
<div class="order-body-item-table">
|
||||||
|
<div class="order-body-item-left">
|
||||||
|
<div class="order-body-header">Order Status</div>
|
||||||
|
<div class="order-track">
|
||||||
|
<div class="order-track-step">
|
||||||
|
<span class="order-track-status">
|
||||||
|
<div class="order-track-step-icon completed"></div>
|
||||||
|
<div class="order-track-step-line completed"></div>
|
||||||
|
</span>
|
||||||
|
<div class="order-track-text">
|
||||||
|
<span class="order-body-status-title">Ordered</span>
|
||||||
|
<span class="when">${new Date(this.state.date_placed).toDateString()}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="order-track-step">
|
||||||
|
<span class="order-track-status">
|
||||||
|
<div class="order-track-step-icon"></div>
|
||||||
|
<div class="order-track-step-line"></div>
|
||||||
|
</span>
|
||||||
|
<div class="order-track-text">
|
||||||
|
<span class="order-body-status-title">Posted</span>
|
||||||
|
<span class="when"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="order-track-step">
|
||||||
|
<span class="order-track-status">
|
||||||
|
<div class="order-track-step-icon"></div>
|
||||||
|
<div class="order-track-step-line"></div>
|
||||||
|
</span>
|
||||||
|
<div class="order-track-text">
|
||||||
|
<span class="order-body-status-title">Delivered</span>
|
||||||
|
<span class="when"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="order-body-item-right">
|
||||||
|
<div class="order-body-header">Your LegoLog Order</div>
|
||||||
|
<immutable-list-component source="viewing-order" h="400px" class="order-list"></immutable-list-component>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
style: SideLoad('/components/css/order.css'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
OnRender() {
|
||||||
|
// todo: add order tracking, the data is already there
|
||||||
|
}
|
||||||
|
|
||||||
|
OnUnMount() {
|
||||||
|
localStorage.removeItem('viewing-order');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RegisterComponent(Order);
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
<script type="module" src="/components/navbar.mjs"></script>
|
<script type="module" src="/components/navbar.mjs"></script>
|
||||||
<script type="module" src="/components/search.mjs"></script>
|
<script type="module" src="/components/search.mjs"></script>
|
||||||
<script type="module" src="/components/basket-popout.mjs"></script>
|
<script type="module" src="/components/basket-popout.mjs"></script>
|
||||||
<script type="module" src="/components/immutable-basket-list.mjs"></script>
|
<script type="module" src="/components/immutable-list.mjs"></script>
|
||||||
<script type="module" src="/components/accessability-popout.mjs"></script>
|
<script type="module" src="/components/accessability-popout.mjs"></script>
|
||||||
<script type="module" src="/components/notificationbar.mjs"></script>
|
<script type="module" src="/components/notificationbar.mjs"></script>
|
||||||
<script type="module" src="/components/tag.mjs"></script>
|
<script type="module" src="/components/tag.mjs"></script>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
<script type="module" src="/components/search.mjs"></script>
|
<script type="module" src="/components/search.mjs"></script>
|
||||||
<script type="module" src="/components/basket.mjs"></script>
|
<script type="module" src="/components/basket.mjs"></script>
|
||||||
<script type="module" src="/components/basket-popout.mjs"></script>
|
<script type="module" src="/components/basket-popout.mjs"></script>
|
||||||
<script type="module" src="/components/immutable-basket-list.mjs"></script>
|
<script type="module" src="/components/immutable-list.mjs"></script>
|
||||||
<script type="module" src="/components/accessability-popout.mjs"></script>
|
<script type="module" src="/components/accessability-popout.mjs"></script>
|
||||||
<script type="module" src="/components/notificationbar.mjs"></script>
|
<script type="module" src="/components/notificationbar.mjs"></script>
|
||||||
<script type="module" src="/components/storefront.mjs"></script>
|
<script type="module" src="/components/storefront.mjs"></script>
|
||||||
|
|||||||
37
client/public/orders/index.html
Normal file
37
client/public/orders/index.html
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>LegoLog Basket</title>
|
||||||
|
<meta name="viewport">
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/res/favicon.svg">
|
||||||
|
<link rel="stylesheet" type="text/css" href="/global.css">
|
||||||
|
|
||||||
|
<!-- Fonts -->
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Londrina+Solid&display=swap" rel="stylesheet">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Josefin+Sans&display=swap" rel="stylesheet">
|
||||||
|
|
||||||
|
<!-- Auth0 - a library for authentication -->
|
||||||
|
<script src="https://cdn.auth0.com/js/auth0-spa-js/1.13/auth0-spa-js.production.js"></script>
|
||||||
|
|
||||||
|
<!-- Components used on this page - they must be included to work -->
|
||||||
|
<script type="module" src="/components/components.mjs"></script>
|
||||||
|
<script type="module" src="/components/navbar.mjs"></script>
|
||||||
|
<script type="module" src="/components/search.mjs"></script>
|
||||||
|
<script type="module" src="/components/order-list.mjs"></script>
|
||||||
|
<script type="module" src="/components/basket-popout.mjs"></script>
|
||||||
|
<script type="module" src="/components/immutable-list.mjs"></script>
|
||||||
|
<script type="module" src="/components/accessability-popout.mjs"></script>
|
||||||
|
<script type="module" src="/components/notificationbar.mjs"></script>
|
||||||
|
<script type="module" src="/components/tag.mjs"></script>
|
||||||
|
<script type="module" src="/components/super-compact-listing.mjs"></script>
|
||||||
|
|
||||||
|
<script type="module" src="/index.mjs"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<notificationbar-component></notificationbar-component>
|
||||||
|
<navbar-component></navbar-component>
|
||||||
|
<limited-margin>
|
||||||
|
<order-list-component></order-list-component>
|
||||||
|
</limited-margin>
|
||||||
|
</body>
|
||||||
37
client/public/orders/order/index.html
Normal file
37
client/public/orders/order/index.html
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>LegoLog Basket</title>
|
||||||
|
<meta name="viewport">
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/res/favicon.svg">
|
||||||
|
<link rel="stylesheet" type="text/css" href="/global.css">
|
||||||
|
|
||||||
|
<!-- Fonts -->
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Londrina+Solid&display=swap" rel="stylesheet">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Josefin+Sans&display=swap" rel="stylesheet">
|
||||||
|
|
||||||
|
<!-- Auth0 - a library for authentication -->
|
||||||
|
<script src="https://cdn.auth0.com/js/auth0-spa-js/1.13/auth0-spa-js.production.js"></script>
|
||||||
|
|
||||||
|
<!-- Components used on this page - they must be included to work -->
|
||||||
|
<script type="module" src="/components/components.mjs"></script>
|
||||||
|
<script type="module" src="/components/navbar.mjs"></script>
|
||||||
|
<script type="module" src="/components/search.mjs"></script>
|
||||||
|
<script type="module" src="/components/order.mjs"></script>
|
||||||
|
<script type="module" src="/components/basket-popout.mjs"></script>
|
||||||
|
<script type="module" src="/components/immutable-list.mjs"></script>
|
||||||
|
<script type="module" src="/components/accessability-popout.mjs"></script>
|
||||||
|
<script type="module" src="/components/notificationbar.mjs"></script>
|
||||||
|
<script type="module" src="/components/tag.mjs"></script>
|
||||||
|
<script type="module" src="/components/super-compact-listing.mjs"></script>
|
||||||
|
|
||||||
|
<script type="module" src="/index.mjs"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<notificationbar-component></notificationbar-component>
|
||||||
|
<navbar-component></navbar-component>
|
||||||
|
<limited-margin>
|
||||||
|
<order-component></order-component>
|
||||||
|
</limited-margin>
|
||||||
|
</body>
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
<script type="module" src="/components/navbar.mjs"></script>
|
<script type="module" src="/components/navbar.mjs"></script>
|
||||||
<script type="module" src="/components/search.mjs"></script>
|
<script type="module" src="/components/search.mjs"></script>
|
||||||
<script type="module" src="/components/basket-popout.mjs"></script>
|
<script type="module" src="/components/basket-popout.mjs"></script>
|
||||||
<script type="module" src="/components/immutable-basket-list.mjs"></script>
|
<script type="module" src="/components/immutable-list.mjs"></script>
|
||||||
<script type="module" src="/components/accessability-popout.mjs"></script>
|
<script type="module" src="/components/accessability-popout.mjs"></script>
|
||||||
<script type="module" src="/components/notificationbar.mjs"></script>
|
<script type="module" src="/components/notificationbar.mjs"></script>
|
||||||
<script type="module" src="/components/storefront.mjs"></script>
|
<script type="module" src="/components/storefront.mjs"></script>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
<script type="module" src="/components/search.mjs"></script>
|
<script type="module" src="/components/search.mjs"></script>
|
||||||
<script type="module" src="/components/basket.mjs"></script>
|
<script type="module" src="/components/basket.mjs"></script>
|
||||||
<script type="module" src="/components/basket-popout.mjs"></script>
|
<script type="module" src="/components/basket-popout.mjs"></script>
|
||||||
<script type="module" src="/components/immutable-basket-list.mjs"></script>
|
<script type="module" src="/components/immutable-list.mjs"></script>
|
||||||
<script type="module" src="/components/accessability-popout.mjs"></script>
|
<script type="module" src="/components/accessability-popout.mjs"></script>
|
||||||
<script type="module" src="/components/notificationbar.mjs"></script>
|
<script type="module" src="/components/notificationbar.mjs"></script>
|
||||||
<script type="module" src="/components/storefront.mjs"></script>
|
<script type="module" src="/components/storefront.mjs"></script>
|
||||||
|
|||||||
@@ -90,15 +90,29 @@ CREATE TABLE IF NOT EXISTS users (
|
|||||||
date_updated TIMESTAMP WITHOUT TIME ZONE NOT NULL
|
date_updated TIMESTAMP WITHOUT TIME ZONE NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS offer_code (
|
||||||
|
code TEXT NOT NULL PRIMARY KEY,
|
||||||
|
discount DECIMAL NOT NULL, -- percentage or fixed amount
|
||||||
|
discount_type INT NOT NULL, -- 0 = percentage, 1 = fixed amount
|
||||||
|
min_order_value DECIMAL NOT NULL,
|
||||||
|
type TEXT NOT NULL, -- set or brick
|
||||||
|
expiry_date TIMESTAMP WITHOUT TIME ZONE NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS order_log (
|
CREATE TABLE IF NOT EXISTS order_log (
|
||||||
id VARCHAR (50) NOT NULL PRIMARY KEY,
|
id VARCHAR (50) NOT NULL PRIMARY KEY,
|
||||||
user_id VARCHAR (50), -- null if guest
|
user_id VARCHAR (50), -- null if guest
|
||||||
offer_code SERIAL,
|
|
||||||
subtotal_paid DECIMAL NOT NULL,
|
subtotal_paid DECIMAL NOT NULL,
|
||||||
|
offer_code TEXT,
|
||||||
discount DECIMAL,
|
discount DECIMAL,
|
||||||
date_placed TIMESTAMP WITHOUT TIME ZONE NOT NULL,
|
date_placed TIMESTAMP WITHOUT TIME ZONE NOT NULL,
|
||||||
|
shipped BOOLEAN NOT NULL,
|
||||||
|
date_shipped TIMESTAMP WITHOUT TIME ZONE,
|
||||||
|
recieved BOOLEAN NOT NULL,
|
||||||
|
date_recieved TIMESTAMP WITHOUT TIME ZONE,
|
||||||
FOREIGN KEY ( user_id ) REFERENCES users( id ),
|
FOREIGN KEY ( user_id ) REFERENCES users( id ),
|
||||||
FOREIGN KEY ( offer_code ) REFERENCES offer_code( id )
|
FOREIGN KEY ( offer_code ) REFERENCES offer_code( code )
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS order_item (
|
CREATE TABLE IF NOT EXISTS order_item (
|
||||||
@@ -108,19 +122,8 @@ CREATE TABLE IF NOT EXISTS order_item (
|
|||||||
brick_colour INT,
|
brick_colour INT,
|
||||||
set_id VARCHAR (50),
|
set_id VARCHAR (50),
|
||||||
amount INT NOT NULL,
|
amount INT NOT NULL,
|
||||||
price_paid DECIMAL NOT NULL,
|
|
||||||
FOREIGN KEY ( order_id ) REFERENCES order_log( id ),
|
FOREIGN KEY ( order_id ) REFERENCES order_log( id ),
|
||||||
FOREIGN KEY ( brick_id ) REFERENCES lego_brick( id ),
|
FOREIGN KEY ( brick_id ) REFERENCES lego_brick( id ),
|
||||||
FOREIGN KEY ( brick_colour ) REFERENCES lego_brick_colour( id ),
|
FOREIGN KEY ( brick_colour ) REFERENCES lego_brick_colour( id ),
|
||||||
FOREIGN KEY ( set_id ) REFERENCES lego_set( id )
|
FOREIGN KEY ( set_id ) REFERENCES lego_set( id )
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS offer_code (
|
|
||||||
id SERIAL NOT NULL PRIMARY KEY,
|
|
||||||
code TEXT NOT NULL,
|
|
||||||
discount DECIMAL NOT NULL, -- percentage or fixed amount
|
|
||||||
discount_type INT NOT NULL, -- 0 = percentage, 1 = fixed amount
|
|
||||||
min_order_value DECIMAL NOT NULL,
|
|
||||||
type TEXT NOT NULL, -- set or brick
|
|
||||||
expiry_date TIMESTAMP WITHOUT TIME ZONE NOT NULL
|
|
||||||
);
|
|
||||||
|
|||||||
@@ -1,14 +1,154 @@
|
|||||||
const Database = require('../database/database.js');
|
const Database = require('../database/database.js');
|
||||||
const Logger = require('../logger.js');
|
const Logger = require('../logger.js');
|
||||||
|
const Crypto = require('crypto');
|
||||||
|
|
||||||
// C
|
// C
|
||||||
|
|
||||||
|
async function NewOrder(userId, total, items, discountId = null, discountApplied = null) {
|
||||||
|
// generate unique hex order id xxxx-xxxx-xxxx-xxxx "somewhat securely"
|
||||||
|
const orderId = (Crypto.randomBytes(2).toString('hex') + '-' +
|
||||||
|
Crypto.randomBytes(2).toString('hex') + '-' +
|
||||||
|
Crypto.randomBytes(2).toString('hex') + '-' +
|
||||||
|
Crypto.randomBytes(2).toString('hex')).toUpperCase();
|
||||||
|
|
||||||
|
// user_id and discount_id are optional and can be null
|
||||||
|
await Database.Query('BEGIN TRANSACTION;');
|
||||||
|
const dbres = await Database.Query(`
|
||||||
|
INSERT INTO order_log (id, user_id, subtotal_paid, offer_code, discount, date_placed, shipped, recieved)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, NOW(), FALSE, FALSE)
|
||||||
|
`, [orderId, userId, total, discountId, discountApplied]).catch(() => {
|
||||||
|
return {
|
||||||
|
error: 'Database error',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
let dbresOrderItemError = false;
|
||||||
|
|
||||||
|
// this worked first time, it MUST be wrong
|
||||||
|
for (const [key, item] of Object.entries(items)) {
|
||||||
|
const brickId = item.type === 'brick' ? key.split('~')[0] : null;
|
||||||
|
const brickModifier = key.split('~')[1] || null;
|
||||||
|
const setId = item.type === 'set' ? key : null;
|
||||||
|
|
||||||
|
await Database.Query(`
|
||||||
|
INSERT INTO order_item (order_id, brick_id, brick_colour, set_id, amount)
|
||||||
|
VALUES ($1, $2, $3, $4, $5)
|
||||||
|
`, [orderId, brickId, brickModifier, setId, item.quantity]).catch(() => {
|
||||||
|
dbresOrderItemError = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dbres.error || dbresOrderItemError) {
|
||||||
|
Database.Query('ROLLBACK TRANSACTION;');
|
||||||
|
Logger.Error('Something went wrong inserting an order into the database');
|
||||||
|
return {
|
||||||
|
error: 'Database error',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Database.Query('COMMIT TRANSACTION;');
|
||||||
|
|
||||||
|
return orderId;
|
||||||
|
}
|
||||||
|
|
||||||
// R
|
// R
|
||||||
|
|
||||||
|
async function GetOrderById(orderid) {
|
||||||
|
await Database.Query('BEGIN TRANSACTION;');
|
||||||
|
const dbres = await Database.Query(`
|
||||||
|
SELECT order_log.id, order_log.user_id, subtotal_paid, offer_code,
|
||||||
|
discount, date_placed, shipped, date_shipped, recieved,
|
||||||
|
date_recieved, item.brick_id, item.brick_colour, item.set_id, item.amount
|
||||||
|
FROM order_log
|
||||||
|
LEFT JOIN order_item AS item ON item.order_id = order_log.id
|
||||||
|
WHERE order_log.id = $1
|
||||||
|
`, [orderid]).catch(() => {
|
||||||
|
return {
|
||||||
|
error: 'Database error',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
if (dbres.error) {
|
||||||
|
Database.Query('ROLLBACK TRANSACTION;');
|
||||||
|
Logger.Error(dbres.error);
|
||||||
|
return {
|
||||||
|
error: 'Database error',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Database.Query('COMMIT TRANSACTION;');
|
||||||
|
|
||||||
|
const result = dbres.rows;
|
||||||
|
|
||||||
|
const items = {};
|
||||||
|
for (const item of result) {
|
||||||
|
if (item.brick_id) {
|
||||||
|
items[`${item.brick_id}~${item.brick_colour}`] = {
|
||||||
|
type: 'brick',
|
||||||
|
quantity: item.amount,
|
||||||
|
};
|
||||||
|
} else if (item.set_id) {
|
||||||
|
items[item.set_id] = {
|
||||||
|
type: 'set',
|
||||||
|
quantity: item.amount,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: result[0].id,
|
||||||
|
user_id: result[0].user_id,
|
||||||
|
subtotal_paid: result[0].subtotal_paid,
|
||||||
|
offer_code: result[0].offer_code,
|
||||||
|
discount: result[0].discount,
|
||||||
|
date_placed: result[0].date_placed,
|
||||||
|
shipped: result[0].shipped,
|
||||||
|
date_shipped: result[0].date_shipped,
|
||||||
|
recieved: result[0].recieved,
|
||||||
|
date_recieved: result[0].date_recieved,
|
||||||
|
items,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function GetOrdersByUser(userId) {
|
||||||
|
await Database.Query('BEGIN TRANSACTION;');
|
||||||
|
const dbres = await Database.Query(`
|
||||||
|
SELECT order_log.id, order_log.user_id, subtotal_paid,
|
||||||
|
discount, date_placed, shipped
|
||||||
|
FROM order_log
|
||||||
|
WHERE order_log.user_id = $1
|
||||||
|
`, [userId]).catch(() => {
|
||||||
|
return {
|
||||||
|
error: 'Database error',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
if (dbres.error) {
|
||||||
|
Database.Query('ROLLBACK TRANSACTION;');
|
||||||
|
Logger.Error(dbres.error);
|
||||||
|
return {
|
||||||
|
error: 'Database error',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Database.Query('COMMIT TRANSACTION;');
|
||||||
|
|
||||||
|
const result = dbres.rows;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// U
|
// U
|
||||||
|
|
||||||
|
async function OrderShipped(orderid) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async function OrderRecieved(orderid) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// D
|
// D
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
NewOrder,
|
||||||
|
GetOrderById,
|
||||||
|
GetOrdersByUser,
|
||||||
|
OrderShipped,
|
||||||
|
OrderRecieved,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -25,11 +25,11 @@ function Init() {
|
|||||||
Server.App.post('/api/basket/price/', Helpers.CalculateBasketPrice);
|
Server.App.post('/api/basket/price/', Helpers.CalculateBasketPrice);
|
||||||
Server.App.get('/api/discount/', Helpers.DiscountCode);
|
Server.App.get('/api/discount/', Helpers.DiscountCode);
|
||||||
Server.App.post('/api/order/', Order.ProcessNew);
|
Server.App.post('/api/order/', Order.ProcessNew);
|
||||||
Server.App.get('/api/order:id');
|
Server.App.get('/api/order/:id', Order.GetOrder);
|
||||||
|
|
||||||
Server.App.get('/api/auth/login/', Auth0.JWTMiddleware, Auth0.Login);
|
Server.App.get('/api/auth/login/', Auth0.JWTMiddleware, Auth0.Login);
|
||||||
Server.App.post('/api/auth/order/', Auth0.JWTMiddleware, Order.ProcessNew);
|
Server.App.post('/api/auth/order/', Auth0.JWTMiddleware, Order.ProcessNew);
|
||||||
Server.App.get('/api/auth/orders/');
|
Server.App.get('/api/auth/orders/', Auth0.JWTMiddleware, Order.GetOrders);
|
||||||
|
|
||||||
Logger.Module('API', 'API Routes Initialized');
|
Logger.Module('API', 'API Routes Initialized');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,10 @@ const ControllerMaster = require('../controllers/controller-master.js');
|
|||||||
const MiscController = require('../controllers/misc-controller.js');
|
const MiscController = require('../controllers/misc-controller.js');
|
||||||
const BrickController = require('../controllers/brick-controller.js');
|
const BrickController = require('../controllers/brick-controller.js');
|
||||||
const SetController = require('../controllers/set-controller.js');
|
const SetController = require('../controllers/set-controller.js');
|
||||||
|
const OrderController = require('../controllers/order-controller.js');
|
||||||
const AuthRouter = require('./auth0-router.js');
|
const AuthRouter = require('./auth0-router.js');
|
||||||
|
|
||||||
async function ProcessNew(req, res) {
|
async function ProcessNew(req, res) {
|
||||||
console.log(req.body);
|
|
||||||
|
|
||||||
// as it's optional auth, 0 is guest
|
// as it's optional auth, 0 is guest
|
||||||
let userID = null;
|
let userID = null;
|
||||||
if (req.auth) {
|
if (req.auth) {
|
||||||
@@ -16,8 +15,6 @@ async function ProcessNew(req, res) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(userID);
|
|
||||||
|
|
||||||
// validate the request
|
// validate the request
|
||||||
if (!req.body.basket) {
|
if (!req.body.basket) {
|
||||||
return res.send({
|
return res.send({
|
||||||
@@ -26,7 +23,7 @@ async function ProcessNew(req, res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const basket = req.body.basket;
|
const basket = req.body.basket;
|
||||||
const discountCode = req.body.discountCode || '';
|
const discountCode = req.body.discountCode || null;
|
||||||
|
|
||||||
// validate the basket
|
// validate the basket
|
||||||
|
|
||||||
@@ -114,11 +111,13 @@ async function ProcessNew(req, res) {
|
|||||||
// now we need to calculate the discount (if applicable)
|
// now we need to calculate the discount (if applicable)
|
||||||
// again, this could do with some consolidation
|
// again, this could do with some consolidation
|
||||||
|
|
||||||
let discount = 0;
|
let discount = {
|
||||||
if (discountCode !== '') {
|
discount: 0,
|
||||||
|
};
|
||||||
|
if (discountCode !== null) {
|
||||||
const sanatisedCode = ControllerMaster.SanatiseQuery(req.query.code);
|
const sanatisedCode = ControllerMaster.SanatiseQuery(req.query.code);
|
||||||
|
|
||||||
const discount = await MiscController.GetDiscount(sanatisedCode);
|
discount = await MiscController.GetDiscount(sanatisedCode);
|
||||||
|
|
||||||
if (discount.error) {
|
if (discount.error) {
|
||||||
return res.send({
|
return res.send({
|
||||||
@@ -133,9 +132,64 @@ async function ProcessNew(req, res) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const total = basketSubtotal - discount.discount;
|
||||||
|
|
||||||
|
const order = await OrderController.NewOrder(userID, total, basket, discountCode, discount.discount);
|
||||||
|
|
||||||
|
if (order.error) {
|
||||||
|
return res.send({
|
||||||
|
error: order.error,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.send({
|
||||||
|
data: {
|
||||||
|
receipt_id: order,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function GetOrder(req, res) {
|
||||||
|
const orderId = req.params.id;
|
||||||
|
|
||||||
|
if (!orderId) {
|
||||||
|
return res.send({
|
||||||
|
error: 'No order id in request',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const order = await OrderController.GetOrderById(orderId);
|
||||||
|
|
||||||
|
if (order.error) {
|
||||||
|
return res.send({
|
||||||
|
error: order.error,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.send({
|
||||||
|
data: order,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function GetOrders(req, res) {
|
||||||
|
const user = await AuthRouter.Auth0GetUser(req);
|
||||||
|
const userId = user.sub.split('|')[1];
|
||||||
|
|
||||||
|
const orders = await OrderController.GetOrdersByUser(userId);
|
||||||
|
|
||||||
|
if (orders.error) {
|
||||||
|
return res.send({
|
||||||
|
error: orders.error,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.send({
|
||||||
|
data: orders,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
ProcessNew,
|
ProcessNew,
|
||||||
|
GetOrder,
|
||||||
|
GetOrders,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user