Patch: Receipt page (#7)

* OrderSection receipts list of lineItems instead of always getting cart

* Hide express checkout behind feature flag

* Add torn receipt paper css look to order list

* Re-use OrderSection on receipt page

* Linting

* Reduced max font from 130->120%, now only applies scaling on div.main

Reducing relative font size for the largest screen width.
Scaling only applies to main container, not header and footer.

* Minor header size changes

* Set max-width to login input elements on desktop

* Prettier doesn't liek shorthand props
This commit is contained in:
2023-03-28 18:35:14 +02:00
committed by GitHub
parent 8bb65dfc2e
commit 63a1107427
12 changed files with 92 additions and 97 deletions

View File

@@ -14,7 +14,7 @@
<footer> <footer>
<section> <section>
<h2>Personvern og vilkår</h2> <h1>Personvern og vilkår</h1>
<ul> <ul>
<li><LinkArrow /><a href="/terms-and-conditions">Betingelser og vilkår</a></li> <li><LinkArrow /><a href="/terms-and-conditions">Betingelser og vilkår</a></li>
<li><LinkArrow /><a href="/privacy-policy">Personvernerklæring</a></li> <li><LinkArrow /><a href="/privacy-policy">Personvernerklæring</a></li>
@@ -31,7 +31,7 @@
</section> </section>
<section> <section>
<h2>Kontakt</h2> <h1>Kontakt</h1>
<ul> <ul>
<li>Epost:&nbsp;<a class="link" href="mailto:post@planetposen.no">post@planetposen.no</a></li> <li>Epost:&nbsp;<a class="link" href="mailto:post@planetposen.no">post@planetposen.no</a></li>
@@ -41,6 +41,7 @@
Kode:&nbsp;<a Kode:&nbsp;<a
class="link" class="link"
target="_blank" target="_blank"
rel="noreferrer"
href="https://github.com/search?q=user%3Akevinmidboe+sort%3Aupdated+planetposen&type=repositories" href="https://github.com/search?q=user%3Akevinmidboe+sort%3Aupdated+planetposen&type=repositories"
>github.com</a >github.com</a
> >
@@ -60,10 +61,6 @@
display: flex; display: flex;
justify-content: space-around; justify-content: space-around;
h2 {
font-size: 2.5rem;
}
section { section {
width: 30%; width: 30%;
padding: 2rem; padding: 2rem;
@@ -97,7 +94,7 @@
flex-direction: column; flex-direction: column;
padding: 3rem 0.5rem; padding: 3rem 0.5rem;
h2 { h1 {
font-size: 1.8rem; font-size: 1.8rem;
} }

View File

@@ -7,7 +7,7 @@
import CheckoutButton from '$lib/components/Button.svelte'; import CheckoutButton from '$lib/components/Button.svelte';
import StripeCard from '$lib/components/StripeCard.svelte'; import StripeCard from '$lib/components/StripeCard.svelte';
import ErrorStack from '$lib/components/ErrorStack.svelte'; import ErrorStack from '$lib/components/ErrorStack.svelte';
import { cart } from '$lib/cartStore'; import { cart, subTotal } from '$lib/cartStore';
import stripeApi from '$lib/stripe/index'; import stripeApi from '$lib/stripe/index';
import { OrderSubmitUnsuccessfullError } from '$lib/errors/OrderErrors'; import { OrderSubmitUnsuccessfullError } from '$lib/errors/OrderErrors';
import Loading from '$lib/components/loading/index.svelte'; import Loading from '$lib/components/loading/index.svelte';
@@ -28,6 +28,7 @@
let card: StripeCardElement; let card: StripeCardElement;
let form: HTMLFormElement; let form: HTMLFormElement;
let errors: string[] = []; let errors: string[] = [];
let showExpressCheckout = false;
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
let resolvePaymentPromise: (value: any) => void; let resolvePaymentPromise: (value: any) => void;
@@ -147,6 +148,7 @@
<form class="checkout" bind:this="{form}" on:submit|preventDefault="{postOrder}"> <form class="checkout" bind:this="{form}" on:submit|preventDefault="{postOrder}">
<div class="main"> <div class="main">
{#if showExpressCheckout}
<section class="express-checkout" style="display: block;"> <section class="express-checkout" style="display: block;">
<h2>Hurtigkasse</h2> <h2>Hurtigkasse</h2>
@@ -154,6 +156,7 @@
<p style="margin: 0 0 -0.5rem 0.5rem; text-align: left; color: rgba(0,0,0,0.5);">eller</p> <p style="margin: 0 0 -0.5rem 0.5rem; text-align: left; color: rgba(0,0,0,0.5);">eller</p>
</section> </section>
{/if}
<section id="delivery"> <section id="delivery">
<h2>Leveringsaddresse</h2> <h2>Leveringsaddresse</h2>
@@ -178,7 +181,7 @@
<aside class="sidebar"> <aside class="sidebar">
<section id="order"> <section id="order">
<h2>Din ordre</h2> <h2>Din ordre</h2>
<OrderSection /> <OrderSection lineItems="{$cart}" subTotal="{$subTotal}" />
</section> </section>
</aside> </aside>
</form> </form>

View File

@@ -1,6 +1,5 @@
<script lang="ts"> <script lang="ts">
import Vipps from '$lib/icons/Vipps.svelte'; import Vipps from '$lib/icons/Vipps.svelte';
import ShopPay from '$lib/icons/ShopPay.svelte';
import ApplePay from '$lib/icons/ApplePay.svelte'; import ApplePay from '$lib/icons/ApplePay.svelte';
import PayPal from '$lib/icons/PayPal.svelte'; import PayPal from '$lib/icons/PayPal.svelte';
import GooglePay from '$lib/icons/GooglePay.svelte'; import GooglePay from '$lib/icons/GooglePay.svelte';

View File

@@ -1,16 +1,9 @@
<script lang="ts"> <script lang="ts">
import OrderTotalSection from './OrderTotalSection.svelte'; import OrderTotalSection from './OrderTotalSection.svelte';
import QuantitySelect from '$lib/components/QuantitySelect.svelte'; import type ICart from '$lib/interfaces/ICart';
import { cart, subTotal } from '$lib/cartStore'; export let lineItems: ICart[];
import { decrementProductInCart, incrementProductInCart } from '$lib/websocketCart'; export let subTotal: number;
const shippingPrice = 75;
$: totalPrice = $subTotal + shippingPrice;
function lineItemClass(id: number) {
return `lineitem-${id}`;
}
</script> </script>
<div class="order-summary"> <div class="order-summary">
@@ -26,7 +19,7 @@
</thead> </thead>
<tbody data-order-summary-section="line-items"> <tbody data-order-summary-section="line-items">
{#each $cart as cartItem} {#each lineItems as lineItem}
<tr <tr
class="product" class="product"
data-product-id="6718367989809" data-product-id="6718367989809"
@@ -40,18 +33,18 @@
<img <img
alt="Black Googly Eye Puff Print Logo Tee - XS" alt="Black Googly Eye Puff Print Logo Tee - XS"
class="product-thumbnail__image" class="product-thumbnail__image"
src="{cartItem.image}" src="{lineItem.image}"
data-src="//cdn.shopify.com/s/files/1/0023/3789/8540/products/20220718_A24_GooglyEye_Tee_Black_15991x1gray_small.jpg?v=1659020903" data-src="//cdn.shopify.com/s/files/1/0023/3789/8540/products/20220718_A24_GooglyEye_Tee_Black_15991x1gray_small.jpg?v=1659020903"
/> />
</div> </div>
<span class="product-thumbnail__quantity" aria-hidden="true">{cartItem.quantity}</span <span class="product-thumbnail__quantity" aria-hidden="true">{lineItem.quantity}</span
> >
</div> </div>
</td> </td>
<th class="product__description" scope="row"> <th class="product__description" scope="row">
<span class="product__description__name order-summary__emphasis">{cartItem.name}</span> <span class="product__description__name order-summary__emphasis">{lineItem.name}</span>
<span class="product__description__variant order-summary__small-text" <span class="product__description__variant order-summary__small-text"
>{cartItem.size}</span >{lineItem.size}</span
> >
</th> </th>
<td class="product__quantity"> <td class="product__quantity">
@@ -59,7 +52,7 @@
</td> </td>
<td class="product__price"> <td class="product__price">
<p class="order-summary__emphasis skeleton-while-loading"> <p class="order-summary__emphasis skeleton-while-loading">
NOK {cartItem.quantity * cartItem.price} NOK {lineItem.quantity * lineItem.price}
</p> </p>
</td> </td>
</tr> </tr>
@@ -68,7 +61,7 @@
</tbody> </tbody>
</table> </table>
<OrderTotalSection /> <OrderTotalSection subTotal="{subTotal}" />
</div> </div>
<style lang="scss" module="scoped"> <style lang="scss" module="scoped">

View File

@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { subTotal } from '$lib/cartStore'; export let subTotal: number;
</script> </script>
<div class="total"> <div class="total">
@@ -19,7 +19,7 @@
class="order-summary__emphasis skeleton-while-loading" class="order-summary__emphasis skeleton-while-loading"
data-checkout-subtotal-price-target="4000" data-checkout-subtotal-price-target="4000"
> >
Nok {$subTotal} Nok {subTotal}
</span> </span>
</td> </td>
</tr> </tr>
@@ -58,7 +58,7 @@
<td class="price payment-due" data-presentment-currency="NOK"> <td class="price payment-due" data-presentment-currency="NOK">
<span class="payment-due__currency remove-while-loading">Nok</span> <span class="payment-due__currency remove-while-loading">Nok</span>
<span class="price skeleton-while-loading--lg" data-checkout-payment-due-target="4000"> <span class="price skeleton-while-loading--lg" data-checkout-payment-due-target="4000">
{$subTotal + 75} {subTotal + 75}
</span> </span>
</td> </td>
</tr> </tr>

View File

@@ -62,13 +62,14 @@
<style lang="scss" module="scoped"> <style lang="scss" module="scoped">
@import '../../styles/media-queries.scss'; @import '../../styles/media-queries.scss';
@include desktop {
section { section {
margin: 20% 2rem; max-width: 600px;
width: 60%; margin: auto;
}
}
@include desktop {
margin: 20% auto;
}
}
.signin-button { .signin-button {
margin-top: 2rem; margin-top: 2rem;
} }

View File

@@ -6,7 +6,7 @@ export const load: PageServerLoad = async ({ fetch, params }) => {
const { id } = params; const { id } = params;
const res = await fetch(`/api/v1/order/${id}`); const res = await fetch(`/api/v1/order/${id}`);
const orderResponse = await res.json(); const orderResponse: IOrderDTO = await res.json();
if (orderResponse?.success == false || orderResponse?.order === undefined) { if (orderResponse?.success == false || orderResponse?.order === undefined) {
console.log('throwing error', orderResponse); console.log('throwing error', orderResponse);

View File

@@ -1,5 +1,5 @@
import { redirect } from '@sveltejs/kit'; import { redirect } from '@sveltejs/kit';
import type { Actions, PageServerLoad } from './$types'; import type { Actions } from './$types';
export const actions: Actions = { export const actions: Actions = {
default: async ({ request }) => { default: async ({ request }) => {
@@ -10,8 +10,6 @@ export const actions: Actions = {
const receiptUrl = `/receipt/${orderId}?email=${email}`; const receiptUrl = `/receipt/${orderId}?email=${email}`;
throw redirect(303, receiptUrl); throw redirect(303, receiptUrl);
return { success: false };
} }
}; };

View File

@@ -2,17 +2,11 @@
import { page } from '$app/stores'; import { page } from '$app/stores';
import CircleCheckmark from '$lib/components/loading/CircleCheckmark.svelte'; import CircleCheckmark from '$lib/components/loading/CircleCheckmark.svelte';
import CircleError from '$lib/components/loading/CircleError.svelte'; import CircleError from '$lib/components/loading/CircleError.svelte';
import OrderSection from '../../checkout/OrderSection.svelte';
import type { PageServerData } from './$types'; import type { IOrder } from '$lib/interfaces/IOrder';
import type { ILineItem, IOrder } from '$lib/interfaces/IOrder';
import CircleWarning from '$lib/components/loading/CircleWarning.svelte'; import CircleWarning from '$lib/components/loading/CircleWarning.svelte';
function subTotal(lineItems: Array<ILineItem> = []) {
let total = 0;
lineItems.forEach((lineItem) => (total = total + lineItem.price * lineItem.quantity));
return total;
}
let id: string; let id: string;
let email: string; let email: string;
let order: IOrder; let order: IOrder;
@@ -23,6 +17,8 @@
email = data.email || (data?.order?.customer?.email as string); email = data.email || (data?.order?.customer?.email as string);
order = data.order as IOrder; order = data.order as IOrder;
} }
$: subTotal = Math.round((order?.payment?.amount || 1) / 100);
</script> </script>
<section class="order-confirmation"> <section class="order-confirmation">
@@ -49,21 +45,9 @@
</div> </div>
<div class="order-receipt"> <div class="order-receipt">
{#each order?.lineItems as lineItem} <div class="receipt-box">
<p> <OrderSection lineItems="{order?.lineItems}" subTotal="{subTotal}" } />
<code>{lineItem.name} x{lineItem.quantity}</code> </div>
<code>NOK {lineItem.price * lineItem.quantity}</code>
</p>
{/each}
<p>
<code>Shipping</code>
<code>NOK 75</code>
</p>
<p>
<code>Total</code>
<code>NOK {subTotal(order?.lineItems)}</code>
</p>
</div> </div>
</section> </section>
@@ -75,30 +59,52 @@
} }
.order-receipt { .order-receipt {
background-color: #f7f7f7; --receipt_color: #f7f7f7;
max-width: 500px; --tearOffHeight: 8px;
background-color: var(--receipt_color);
max-width: 800px;
width: calc(100% - 4rem); width: calc(100% - 4rem);
padding: 2rem;
font-family: monospace; font-family: monospace;
position: relative;
p { /* Paper background effect */
margin: 0.8rem 0; .receipt-box {
display: flex; height: auto;
justify-content: space-between; overflow: hidden;
border-bottom: 1px solid lightgrey; padding: 1rem;
box-shadow: 0 3px 5px rgba(0, 0, 0, 0.05);
&:last-of-type { &::after {
padding-top: 1.5rem; content: '';
border-width: 2px; height: var(--tearOffHeight);
} position: absolute;
} left: 0;
right: 0;
code { bottom: calc(var(--tearOffHeight) * -1);
opacity: 0.4; background-color: var(--receipt_color);
font-size: 1rem; clip-path: polygon(
0% 0%,
&:first-of-type { 5% 100%,
font-weight: 600; 10% 0%,
15% 100%,
20% 0%,
25% 100%,
30% 0%,
35% 100%,
40% 0%,
45% 100%,
50% 0%,
55% 100%,
60% 0%,
65% 100%,
70% 0%,
75% 100%,
80% 0%,
85% 100%,
90% 0%,
95% 100%,
100% 0%
);
} }
} }
} }

View File

@@ -4,7 +4,6 @@
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import CircleLoading from '$lib/components/loading/CircleLoading.svelte'; import CircleLoading from '$lib/components/loading/CircleLoading.svelte';
import { buildApiUrl } from '$lib/utils/apiUrl'; import { buildApiUrl } from '$lib/utils/apiUrl';
import type { PageServerData } from './$types';
const { data } = $page; const { data } = $page;
const id = data?.id as string; const id = data?.id as string;
@@ -22,7 +21,7 @@
goto(url); goto(url);
} }
function checkOrder() { async function checkOrder() {
const url = buildApiUrl(`/api/v1/order/${id}`); const url = buildApiUrl(`/api/v1/order/${id}`);
return fetch(url) return fetch(url)
.then((resp) => resp.json()) .then((resp) => resp.json())

View File

@@ -1,5 +1,4 @@
<script lang="ts"> <script lang="ts">
import ProductTile from '$lib/components/ProductTile.svelte';
import ProductVariationSelect from '$lib/components/ProductVariationSelect.svelte'; import ProductVariationSelect from '$lib/components/ProductVariationSelect.svelte';
import QuantitySelect from '$lib/components/QuantitySelect.svelte'; import QuantitySelect from '$lib/components/QuantitySelect.svelte';
import SizesSection from './SizesSection.svelte'; import SizesSection from './SizesSection.svelte';
@@ -98,7 +97,7 @@
.details { .details {
.name { .name {
font-size: 2rem; font-size: 2em;
} }
.description { .description {

View File

@@ -14,7 +14,7 @@
color: var(--color-text); color: var(--color-text);
} }
html { body .app main {
font-size: 100%; font-size: 100%;
} }
@@ -56,11 +56,11 @@ a.link {
} }
h1 { h1 {
font-size: 2rem; font-size: 2em;
} }
h2 { h2 {
font-size: 1rem; font-size: 1em;
} }
.no-scroll { .no-scroll {
@@ -104,12 +104,12 @@ button:focus:not(:focus-visible) {
} }
@media screen and (min-width: 1500px) { @media screen and (min-width: 1500px) {
html { body .app main {
font-size: 110%; font-size: 110%;
} }
} }
@media screen and (min-width: 2000px) { @media screen and (min-width: 2000px) {
html { body .app main {
font-size: 130%; font-size: 125%;
} }
} }