mirror of
				https://github.com/KevinMidboe/planetposen-frontend.git
				synced 2025-10-29 13:10:12 +00:00 
			
		
		
		
	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:
		| @@ -14,7 +14,7 @@ | ||||
|  | ||||
| <footer> | ||||
|   <section> | ||||
|     <h2>Personvern og vilkår</h2> | ||||
|     <h1>Personvern og vilkår</h1> | ||||
|     <ul> | ||||
|       <li><LinkArrow /><a href="/terms-and-conditions">Betingelser og vilkår</a></li> | ||||
|       <li><LinkArrow /><a href="/privacy-policy">Personvernerklæring</a></li> | ||||
| @@ -31,7 +31,7 @@ | ||||
|   </section> | ||||
|  | ||||
|   <section> | ||||
|     <h2>Kontakt</h2> | ||||
|     <h1>Kontakt</h1> | ||||
|     <ul> | ||||
|       <li>Epost: <a class="link" href="mailto:post@planetposen.no">post@planetposen.no</a></li> | ||||
|  | ||||
| @@ -41,6 +41,7 @@ | ||||
|         Kode: <a | ||||
|           class="link" | ||||
|           target="_blank" | ||||
|           rel="noreferrer" | ||||
|           href="https://github.com/search?q=user%3Akevinmidboe+sort%3Aupdated+planetposen&type=repositories" | ||||
|           >github.com</a | ||||
|         > | ||||
| @@ -60,10 +61,6 @@ | ||||
|     display: flex; | ||||
|     justify-content: space-around; | ||||
|  | ||||
|     h2 { | ||||
|       font-size: 2.5rem; | ||||
|     } | ||||
|  | ||||
|     section { | ||||
|       width: 30%; | ||||
|       padding: 2rem; | ||||
| @@ -97,7 +94,7 @@ | ||||
|       flex-direction: column; | ||||
|       padding: 3rem 0.5rem; | ||||
|  | ||||
|       h2 { | ||||
|       h1 { | ||||
|         font-size: 1.8rem; | ||||
|       } | ||||
|  | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|   import CheckoutButton from '$lib/components/Button.svelte'; | ||||
|   import StripeCard from '$lib/components/StripeCard.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 { OrderSubmitUnsuccessfullError } from '$lib/errors/OrderErrors'; | ||||
|   import Loading from '$lib/components/loading/index.svelte'; | ||||
| @@ -28,6 +28,7 @@ | ||||
|   let card: StripeCardElement; | ||||
|   let form: HTMLFormElement; | ||||
|   let errors: string[] = []; | ||||
|   let showExpressCheckout = false; | ||||
|  | ||||
|   /* eslint-disable @typescript-eslint/no-explicit-any */ | ||||
|   let resolvePaymentPromise: (value: any) => void; | ||||
| @@ -147,13 +148,15 @@ | ||||
|  | ||||
| <form class="checkout" bind:this="{form}" on:submit|preventDefault="{postOrder}"> | ||||
|   <div class="main"> | ||||
|     <section class="express-checkout" style="display: block;"> | ||||
|       <h2>Hurtigkasse</h2> | ||||
|     {#if showExpressCheckout} | ||||
|       <section class="express-checkout" style="display: block;"> | ||||
|         <h2>Hurtigkasse</h2> | ||||
|  | ||||
|       <ExpressSection /> | ||||
|         <ExpressSection /> | ||||
|  | ||||
|       <p style="margin: 0 0 -0.5rem 0.5rem; text-align: left; color: rgba(0,0,0,0.5);">eller</p> | ||||
|     </section> | ||||
|         <p style="margin: 0 0 -0.5rem 0.5rem; text-align: left; color: rgba(0,0,0,0.5);">eller</p> | ||||
|       </section> | ||||
|     {/if} | ||||
|  | ||||
|     <section id="delivery"> | ||||
|       <h2>Leveringsaddresse</h2> | ||||
| @@ -178,7 +181,7 @@ | ||||
|   <aside class="sidebar"> | ||||
|     <section id="order"> | ||||
|       <h2>Din ordre</h2> | ||||
|       <OrderSection /> | ||||
|       <OrderSection lineItems="{$cart}" subTotal="{$subTotal}" /> | ||||
|     </section> | ||||
|   </aside> | ||||
| </form> | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| <script lang="ts"> | ||||
|   import Vipps from '$lib/icons/Vipps.svelte'; | ||||
|   import ShopPay from '$lib/icons/ShopPay.svelte'; | ||||
|   import ApplePay from '$lib/icons/ApplePay.svelte'; | ||||
|   import PayPal from '$lib/icons/PayPal.svelte'; | ||||
|   import GooglePay from '$lib/icons/GooglePay.svelte'; | ||||
|   | ||||
| @@ -1,16 +1,9 @@ | ||||
| <script lang="ts"> | ||||
|   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'; | ||||
|   import { decrementProductInCart, incrementProductInCart } from '$lib/websocketCart'; | ||||
|  | ||||
|   const shippingPrice = 75; | ||||
|   $: totalPrice = $subTotal + shippingPrice; | ||||
|  | ||||
|   function lineItemClass(id: number) { | ||||
|     return `lineitem-${id}`; | ||||
|   } | ||||
|   export let lineItems: ICart[]; | ||||
|   export let subTotal: number; | ||||
| </script> | ||||
|  | ||||
| <div class="order-summary"> | ||||
| @@ -26,7 +19,7 @@ | ||||
|     </thead> | ||||
|  | ||||
|     <tbody data-order-summary-section="line-items"> | ||||
|       {#each $cart as cartItem} | ||||
|       {#each lineItems as lineItem} | ||||
|         <tr | ||||
|           class="product" | ||||
|           data-product-id="6718367989809" | ||||
| @@ -40,18 +33,18 @@ | ||||
|                 <img | ||||
|                   alt="Black Googly Eye Puff Print Logo Tee - XS" | ||||
|                   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" | ||||
|                 /> | ||||
|               </div> | ||||
|               <span class="product-thumbnail__quantity" aria-hidden="true">{cartItem.quantity}</span | ||||
|               <span class="product-thumbnail__quantity" aria-hidden="true">{lineItem.quantity}</span | ||||
|               > | ||||
|             </div> | ||||
|           </td> | ||||
|           <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" | ||||
|               >{cartItem.size}</span | ||||
|               >{lineItem.size}</span | ||||
|             > | ||||
|           </th> | ||||
|           <td class="product__quantity"> | ||||
| @@ -59,7 +52,7 @@ | ||||
|           </td> | ||||
|           <td class="product__price"> | ||||
|             <p class="order-summary__emphasis skeleton-while-loading"> | ||||
|               NOK {cartItem.quantity * cartItem.price} | ||||
|               NOK {lineItem.quantity * lineItem.price} | ||||
|             </p> | ||||
|           </td> | ||||
|         </tr> | ||||
| @@ -68,7 +61,7 @@ | ||||
|     </tbody> | ||||
|   </table> | ||||
|  | ||||
|   <OrderTotalSection /> | ||||
|   <OrderTotalSection subTotal="{subTotal}" /> | ||||
| </div> | ||||
|  | ||||
| <style lang="scss" module="scoped"> | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <script lang="ts"> | ||||
|   import { subTotal } from '$lib/cartStore'; | ||||
|   export let subTotal: number; | ||||
| </script> | ||||
|  | ||||
| <div class="total"> | ||||
| @@ -19,7 +19,7 @@ | ||||
|             class="order-summary__emphasis skeleton-while-loading" | ||||
|             data-checkout-subtotal-price-target="4000" | ||||
|           > | ||||
|             Nok {$subTotal} | ||||
|             Nok {subTotal} | ||||
|           </span> | ||||
|         </td> | ||||
|       </tr> | ||||
| @@ -58,7 +58,7 @@ | ||||
|         <td class="price payment-due" data-presentment-currency="NOK"> | ||||
|           <span class="payment-due__currency remove-while-loading">Nok</span> | ||||
|           <span class="price skeleton-while-loading--lg" data-checkout-payment-due-target="4000"> | ||||
|             {$subTotal + 75} | ||||
|             {subTotal + 75} | ||||
|           </span> | ||||
|         </td> | ||||
|       </tr> | ||||
|   | ||||
| @@ -62,13 +62,14 @@ | ||||
| <style lang="scss" module="scoped"> | ||||
|   @import '../../styles/media-queries.scss'; | ||||
|  | ||||
|   @include desktop { | ||||
|     section { | ||||
|       margin: 20% 2rem; | ||||
|       width: 60%; | ||||
|   section { | ||||
|     max-width: 600px; | ||||
|     margin: auto; | ||||
|  | ||||
|     @include desktop { | ||||
|       margin: 20% auto; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .signin-button { | ||||
|     margin-top: 2rem; | ||||
|   } | ||||
|   | ||||
| @@ -6,7 +6,7 @@ export const load: PageServerLoad = async ({ fetch, params }) => { | ||||
|   const { id } = params; | ||||
|  | ||||
|   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) { | ||||
|     console.log('throwing error', orderResponse); | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| import { redirect } from '@sveltejs/kit'; | ||||
| import type { Actions, PageServerLoad } from './$types'; | ||||
| import type { Actions } from './$types'; | ||||
|  | ||||
| export const actions: Actions = { | ||||
|   default: async ({ request }) => { | ||||
| @@ -10,8 +10,6 @@ export const actions: Actions = { | ||||
|  | ||||
|     const receiptUrl = `/receipt/${orderId}?email=${email}`; | ||||
|     throw redirect(303, receiptUrl); | ||||
|  | ||||
|     return { success: false }; | ||||
|   } | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -2,17 +2,11 @@ | ||||
|   import { page } from '$app/stores'; | ||||
|   import CircleCheckmark from '$lib/components/loading/CircleCheckmark.svelte'; | ||||
|   import CircleError from '$lib/components/loading/CircleError.svelte'; | ||||
|   import OrderSection from '../../checkout/OrderSection.svelte'; | ||||
|  | ||||
|   import type { PageServerData } from './$types'; | ||||
|   import type { ILineItem, IOrder } from '$lib/interfaces/IOrder'; | ||||
|   import type { IOrder } from '$lib/interfaces/IOrder'; | ||||
|   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 email: string; | ||||
|   let order: IOrder; | ||||
| @@ -23,6 +17,8 @@ | ||||
|     email = data.email || (data?.order?.customer?.email as string); | ||||
|     order = data.order as IOrder; | ||||
|   } | ||||
|  | ||||
|   $: subTotal = Math.round((order?.payment?.amount || 1) / 100); | ||||
| </script> | ||||
|  | ||||
| <section class="order-confirmation"> | ||||
| @@ -49,21 +45,9 @@ | ||||
|   </div> | ||||
|  | ||||
|   <div class="order-receipt"> | ||||
|     {#each order?.lineItems as lineItem} | ||||
|       <p> | ||||
|         <code>{lineItem.name} x{lineItem.quantity}</code> | ||||
|         <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 class="receipt-box"> | ||||
|       <OrderSection lineItems="{order?.lineItems}" subTotal="{subTotal}" } /> | ||||
|     </div> | ||||
|   </div> | ||||
| </section> | ||||
|  | ||||
| @@ -75,30 +59,52 @@ | ||||
|   } | ||||
|  | ||||
|   .order-receipt { | ||||
|     background-color: #f7f7f7; | ||||
|     max-width: 500px; | ||||
|     --receipt_color: #f7f7f7; | ||||
|     --tearOffHeight: 8px; | ||||
|     background-color: var(--receipt_color); | ||||
|     max-width: 800px; | ||||
|     width: calc(100% - 4rem); | ||||
|     padding: 2rem; | ||||
|     font-family: monospace; | ||||
|     position: relative; | ||||
|  | ||||
|     p { | ||||
|       margin: 0.8rem 0; | ||||
|       display: flex; | ||||
|       justify-content: space-between; | ||||
|       border-bottom: 1px solid lightgrey; | ||||
|     /* Paper background effect */ | ||||
|     .receipt-box { | ||||
|       height: auto; | ||||
|       overflow: hidden; | ||||
|       padding: 1rem; | ||||
|       box-shadow: 0 3px 5px rgba(0, 0, 0, 0.05); | ||||
|  | ||||
|       &:last-of-type { | ||||
|         padding-top: 1.5rem; | ||||
|         border-width: 2px; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     code { | ||||
|       opacity: 0.4; | ||||
|       font-size: 1rem; | ||||
|  | ||||
|       &:first-of-type { | ||||
|         font-weight: 600; | ||||
|       &::after { | ||||
|         content: ''; | ||||
|         height: var(--tearOffHeight); | ||||
|         position: absolute; | ||||
|         left: 0; | ||||
|         right: 0; | ||||
|         bottom: calc(var(--tearOffHeight) * -1); | ||||
|         background-color: var(--receipt_color); | ||||
|         clip-path: polygon( | ||||
|           0% 0%, | ||||
|           5% 100%, | ||||
|           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% | ||||
|         ); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   | ||||
| @@ -4,7 +4,6 @@ | ||||
|   import { onMount } from 'svelte'; | ||||
|   import CircleLoading from '$lib/components/loading/CircleLoading.svelte'; | ||||
|   import { buildApiUrl } from '$lib/utils/apiUrl'; | ||||
|   import type { PageServerData } from './$types'; | ||||
|  | ||||
|   const { data } = $page; | ||||
|   const id = data?.id as string; | ||||
| @@ -22,7 +21,7 @@ | ||||
|     goto(url); | ||||
|   } | ||||
|  | ||||
|   function checkOrder() { | ||||
|   async function checkOrder() { | ||||
|     const url = buildApiUrl(`/api/v1/order/${id}`); | ||||
|     return fetch(url) | ||||
|       .then((resp) => resp.json()) | ||||
|   | ||||
| @@ -1,5 +1,4 @@ | ||||
| <script lang="ts"> | ||||
|   import ProductTile from '$lib/components/ProductTile.svelte'; | ||||
|   import ProductVariationSelect from '$lib/components/ProductVariationSelect.svelte'; | ||||
|   import QuantitySelect from '$lib/components/QuantitySelect.svelte'; | ||||
|   import SizesSection from './SizesSection.svelte'; | ||||
| @@ -98,7 +97,7 @@ | ||||
|  | ||||
|     .details { | ||||
|       .name { | ||||
|         font-size: 2rem; | ||||
|         font-size: 2em; | ||||
|       } | ||||
|  | ||||
|       .description { | ||||
|   | ||||
| @@ -14,7 +14,7 @@ | ||||
|   color: var(--color-text); | ||||
| } | ||||
|  | ||||
| html { | ||||
| body .app main { | ||||
|   font-size: 100%; | ||||
| } | ||||
|  | ||||
| @@ -56,11 +56,11 @@ a.link { | ||||
| } | ||||
|  | ||||
| h1 { | ||||
|   font-size: 2rem; | ||||
|   font-size: 2em; | ||||
| } | ||||
|  | ||||
| h2 { | ||||
|   font-size: 1rem; | ||||
|   font-size: 1em; | ||||
| } | ||||
|  | ||||
| .no-scroll { | ||||
| @@ -104,12 +104,12 @@ button:focus:not(:focus-visible) { | ||||
| } | ||||
|  | ||||
| @media screen and (min-width: 1500px) { | ||||
|   html { | ||||
|   body .app main { | ||||
|     font-size: 110%; | ||||
|   } | ||||
| } | ||||
| @media screen and (min-width: 2000px) { | ||||
|   html { | ||||
|     font-size: 130%; | ||||
|   body .app main { | ||||
|     font-size: 125%; | ||||
|   } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user