mirror of
				https://github.com/KevinMidboe/planetposen-frontend.git
				synced 2025-10-29 13:10:12 +00:00 
			
		
		
		
	Polls for order status & displays loading while status=initiated
This is the page sent to after the payment is verified clientside at /checkout. While status is only initiated and not updated from stripe webhook we display spinner. TODO should still timeout to content message
This commit is contained in:
		| @@ -1,10 +1,33 @@ | |||||||
| import validOrderId from '$lib/utils/validOrderId'; |  | ||||||
| import type { PageServerLoad } from './$types'; | import type { PageServerLoad } from './$types'; | ||||||
|  |  | ||||||
| export const load: PageServerLoad = ({ params }) => { | export const load: PageServerLoad = async ({ fetch, params, url }) => { | ||||||
|   const { id } = params; |   const { id } = params; | ||||||
|  |   const email = url.searchParams.get('email'); | ||||||
|  |  | ||||||
|  |   let order = null; | ||||||
|  |  | ||||||
|  |   try { | ||||||
|  |     const res = await fetch(`/api/v1/order/${id}`); | ||||||
|  |     if (res?.status === 404) { | ||||||
|  |       return { | ||||||
|  |         id, | ||||||
|  |         email, | ||||||
|  |         order: null | ||||||
|  |       }; | ||||||
|  |     } | ||||||
|  |     const orderResponse = await res.json(); | ||||||
|  |  | ||||||
|  |     if (orderResponse?.order && orderResponse?.order?.lineItems?.length > 0) { | ||||||
|  |       order = orderResponse?.order; | ||||||
|  |     } | ||||||
|  |   } catch (error) { | ||||||
|  |     console.error('unable to parse order response'); | ||||||
|  |     throw error; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   return { |   return { | ||||||
|     id, |     id, | ||||||
|     isValidReceipt: validOrderId(id) |     email, | ||||||
|  |     order | ||||||
|   }; |   }; | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -1,14 +1,27 @@ | |||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
|  |   import PoolInitiatedOrder from './PoolInitiatedOrder.svelte'; | ||||||
|   import ReceiptNotFound from './ReceiptNotFound.svelte'; |   import ReceiptNotFound from './ReceiptNotFound.svelte'; | ||||||
|  |   import BadgeType from '$lib/interfaces/BadgeType'; | ||||||
|  |  | ||||||
|   import type { PageData } from './$types'; |   import type { PageData } from './$types'; | ||||||
|  |   import PageMeta from '$lib/components/PageMeta.svelte'; | ||||||
|  |  | ||||||
|   export let data: PageData; |   export let data: PageData; | ||||||
|   const isValidReceipt = data.isValidReceipt as boolean; |   let orderStatus: BadgeType = BadgeType.NOT_FOUND; | ||||||
|   const id = data.id as string; |  | ||||||
|  |   if (data?.order) { | ||||||
|  |     orderStatus = data.order?.status; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   const id = data?.id as string; | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| {#if isValidReceipt} | <PageMeta title="Kvittering" description="Søk og finn din ordre kvittering" /> | ||||||
|   <slot /> |  | ||||||
| {:else} | {#if orderStatus === BadgeType.NOT_FOUND} | ||||||
|   <ReceiptNotFound id="{id}" /> |   <ReceiptNotFound id="{id}" /> | ||||||
|  | {:else if orderStatus === BadgeType.INITIATED} | ||||||
|  |   <PoolInitiatedOrder /> | ||||||
|  | {:else} | ||||||
|  |   <slot /> | ||||||
| {/if} | {/if} | ||||||
|   | |||||||
| @@ -1,17 +1,6 @@ | |||||||
| import { redirect } from '@sveltejs/kit'; | import { redirect } from '@sveltejs/kit'; | ||||||
| import validOrderId from '$lib/utils/validOrderId'; |  | ||||||
| import type { Actions, PageServerLoad } from './$types'; | import type { Actions, PageServerLoad } from './$types'; | ||||||
|  |  | ||||||
| export const load: PageServerLoad = ({ params, url }) => { |  | ||||||
|   const { id } = params; |  | ||||||
|   const email = url.searchParams.get('email'); |  | ||||||
|  |  | ||||||
|   return { |  | ||||||
|     id, |  | ||||||
|     email |  | ||||||
|   }; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export const actions: Actions = { | export const actions: Actions = { | ||||||
|   default: async ({ request }) => { |   default: async ({ request }) => { | ||||||
|     const data = await request.formData(); |     const data = await request.formData(); | ||||||
| @@ -19,12 +8,8 @@ export const actions: Actions = { | |||||||
|     const orderId = data.get('order-id'); |     const orderId = data.get('order-id'); | ||||||
|     const email = data.get('order-email'); |     const email = data.get('order-email'); | ||||||
|  |  | ||||||
|     // TODO replace with posting form (json) to backend to check?? |     const receiptUrl = `/receipt/${orderId}?email=${email}`; | ||||||
|     // also return statusCode from the backend directly. |     throw redirect(303, receiptUrl); | ||||||
|     if (validOrderId(String(orderId)) && email) { |  | ||||||
|       const receiptUrl = `/receipt/${orderId}?email=${email}`; |  | ||||||
|       throw redirect(303, receiptUrl); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return { success: false }; |     return { success: false }; | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -1,8 +1,11 @@ | |||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
|   import CircleCheckmark from '$lib/icons/CircleCheckmark.svelte'; |   import { page } from '$app/stores'; | ||||||
|  |   import CircleCheckmark from '$lib/components/loading/CircleCheckmark.svelte'; | ||||||
|  |   import CircleError from '$lib/components/loading/CircleError.svelte'; | ||||||
|  |  | ||||||
|   import type { PageServerData } from './$types'; |   import type { PageServerData } from './$types'; | ||||||
|   import type { ILineItem, IOrder } from '$lib/interfaces/IOrder'; |   import type { ILineItem, IOrder } from '$lib/interfaces/IOrder'; | ||||||
|  |   import CircleWarning from '$lib/components/loading/CircleWarning.svelte'; | ||||||
|  |  | ||||||
|   function subTotal(lineItems: Array<ILineItem> = []) { |   function subTotal(lineItems: Array<ILineItem> = []) { | ||||||
|     let total = 0; |     let total = 0; | ||||||
| @@ -10,20 +13,32 @@ | |||||||
|     return total; |     return total; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   export let data: PageServerData; |   let id: string; | ||||||
|   const id = data.id as string; |   let email: string; | ||||||
|   const email = data.email as string; |   let order: IOrder; | ||||||
|   const order = data.order as IOrder; |  | ||||||
|  |  | ||||||
|   // export let currentRoute; |   const { data } = $page; | ||||||
|   // const id = currentRoute?.namedParams?.id; |   if (data) { | ||||||
|   // const email = currentRoute?.queryParams?.email; |     id = data.id as string; | ||||||
|  |     email = data.email || (data?.order?.customer?.email as string); | ||||||
|  |     order = data.order as IOrder; | ||||||
|  |   } | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <section class="order-confirmation"> | <section class="order-confirmation"> | ||||||
|   <CircleCheckmark /> |   {#if order.status === 'SUCCESS' || order.status === 'CONFIRMED'} | ||||||
|  |     <CircleCheckmark /> | ||||||
|  |   {:else if order.status === 'CANCELLED' || order.status === 'REJECTED'} | ||||||
|  |     <CircleError /> | ||||||
|  |   {:else} | ||||||
|  |     <CircleWarning /> | ||||||
|  |   {/if} | ||||||
|  |  | ||||||
|   <h1>Takk for din bestilling!</h1> |   {#if order.status === 'SUCCESS' || order.status === 'CONFIRMED'} | ||||||
|  |     <h1>Takk for din bestilling!</h1> | ||||||
|  |   {:else} | ||||||
|  |     <h1>Bestilling ikke gjennomført!</h1> | ||||||
|  |   {/if} | ||||||
|  |  | ||||||
|   <div class="order-description"> |   <div class="order-description"> | ||||||
|     <p> |     <p> | ||||||
| @@ -42,7 +57,7 @@ | |||||||
|     {/each} |     {/each} | ||||||
|     <p> |     <p> | ||||||
|       <code>Shipping</code> |       <code>Shipping</code> | ||||||
|       <code>NOK 79</code> |       <code>NOK 75</code> | ||||||
|     </p> |     </p> | ||||||
|  |  | ||||||
|     <p> |     <p> | ||||||
| @@ -55,6 +70,10 @@ | |||||||
| <style lang="scss"> | <style lang="scss"> | ||||||
|   @import './styles-receipt-page.scss'; |   @import './styles-receipt-page.scss'; | ||||||
|  |  | ||||||
|  |   .order-description .underline { | ||||||
|  |     text-decoration: underline; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   .order-receipt { |   .order-receipt { | ||||||
|     background-color: #f7f7f7; |     background-color: #f7f7f7; | ||||||
|     max-width: 500px; |     max-width: 500px; | ||||||
|   | |||||||
							
								
								
									
										60
									
								
								src/routes/receipt/[[id]]/PoolInitiatedOrder.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/routes/receipt/[[id]]/PoolInitiatedOrder.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | |||||||
|  | <script lang="ts"> | ||||||
|  |   import { page } from '$app/stores'; | ||||||
|  |   import { goto } from '$app/navigation'; | ||||||
|  |   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; | ||||||
|  |  | ||||||
|  |   let maxPoolTime: Date = new Date(); | ||||||
|  |   maxPoolTime.setSeconds(maxPoolTime.getSeconds() + 15); | ||||||
|  |  | ||||||
|  |   if (!id) { | ||||||
|  |     console.log('no id found after all:('); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function redirect() { | ||||||
|  |     const url = `/receipt/${id}`; | ||||||
|  |     window.location.href = url; | ||||||
|  |     goto(url); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function checkOrder() { | ||||||
|  |     const url = buildApiUrl(`/api/v1/order/${id}`); | ||||||
|  |     return fetch(url) | ||||||
|  |       .then((resp) => resp.json()) | ||||||
|  |       .then((response) => { | ||||||
|  |         response?.order?.status !== 'INITIATED' ? redirect() : null; | ||||||
|  |       }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function pool() { | ||||||
|  |     if (new Date() < maxPoolTime) { | ||||||
|  |       setTimeout(() => checkOrder().then(() => pool()), 1500); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   onMount(pool); | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <section class="order-confirmation"> | ||||||
|  |   <CircleLoading /> | ||||||
|  |  | ||||||
|  |   <h1>Bestillingen din behandles</h1> | ||||||
|  |  | ||||||
|  |   <div class="order-description"> | ||||||
|  |     <p>Vent noen sekunder mens betalingen din blir godkjent</p> | ||||||
|  |   </div> | ||||||
|  | </section> | ||||||
|  |  | ||||||
|  | <style lang="scss"> | ||||||
|  |   @import './styles-receipt-page.scss'; | ||||||
|  |  | ||||||
|  |   h1 { | ||||||
|  |     text-align: center; | ||||||
|  |   } | ||||||
|  | </style> | ||||||
| @@ -1,33 +1,16 @@ | |||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
|   import Input from '$lib/components/Input.svelte'; |   import Input from '$lib/components/Input.svelte'; | ||||||
|   import PlanetButton from '$lib/components/Button.svelte'; |   import PlanetButton from '$lib/components/Button.svelte'; | ||||||
|   import CircleError from '$lib/icons/CircleError.svelte'; |   import CircleWarning from '$lib/components/loading/CircleWarning.svelte'; | ||||||
|   import CircleWarning from '$lib/icons/CircleWarning.svelte'; |  | ||||||
|  |  | ||||||
|   const CircleComponent = Math.random() > 0.5 ? CircleWarning : CircleError; |  | ||||||
|  |  | ||||||
|   export let id: string; |   export let id: string; | ||||||
|  |  | ||||||
|   // async function handleSubmit(event) { |  | ||||||
|   //   const data = new FormData(this); |  | ||||||
|   //   console.log('formdata::', data); |  | ||||||
|  |  | ||||||
|   //   const orderId = data.get('order-id'); |  | ||||||
|   //   const orderEmail = data.get('order-email'); |  | ||||||
|  |  | ||||||
|   //   console.log('orderId:', orderId) |  | ||||||
|   //   console.log('orderEmail:', orderEmail) |  | ||||||
|  |  | ||||||
|   //   const url = `/receipt/${orderId}?email=${orderEmail}`; |  | ||||||
|   //   goto(url); |  | ||||||
|   // } |  | ||||||
|  |  | ||||||
|   let searchOrderNumber: string; |   let searchOrderNumber: string; | ||||||
|   let searchOrderEmail: string; |   let searchOrderEmail: string; | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <section class="order-confirmation"> | <section class="order-confirmation"> | ||||||
|   <svelte:component this="{CircleComponent}" /> |   <CircleWarning /> | ||||||
|  |  | ||||||
|   <h1>Fant ikke din bestilling!</h1> |   <h1>Fant ikke din bestilling!</h1> | ||||||
|  |  | ||||||
| @@ -40,7 +23,8 @@ | |||||||
|   <form class="order-search" method="POST"> |   <form class="order-search" method="POST"> | ||||||
|     <span>Du kan forsøke søke opp din ordre her:</span> |     <span>Du kan forsøke søke opp din ordre her:</span> | ||||||
|  |  | ||||||
|     <Input name="order-id" label="Ordre id (hex)" bind:value="{searchOrderNumber}" /> |     <Input name="order-id" label="Ordre id " bind:value="{searchOrderNumber}" /> | ||||||
|  |     <br /> | ||||||
|     <Input name="order-email" label="Epost adresse" bind:value="{searchOrderEmail}" /> |     <Input name="order-email" label="Epost adresse" bind:value="{searchOrderEmail}" /> | ||||||
|  |  | ||||||
|     <PlanetButton text="Søk" /> |     <PlanetButton text="Søk" /> | ||||||
| @@ -62,6 +46,10 @@ | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   .underline { | ||||||
|  |     text-decoration: underline; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   :global(.order-search button) { |   :global(.order-search button) { | ||||||
|     margin-top: 1rem; |     margin-top: 1rem; | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| @import '../../../styles/media-queries.scss'; | @import '../../../styles/media-queries.scss'; | ||||||
|  |  | ||||||
| .order-confirmation { | .order-confirmation { | ||||||
|   margin: 6rem auto 0; |   margin: 10rem auto 0; | ||||||
|   display: grid; |   display: grid; | ||||||
|   place-items: center; |   place-items: center; | ||||||
|   padding: 0 0.5rem; |   padding: 0 0.5rem; | ||||||
| @@ -10,8 +10,6 @@ | |||||||
|     margin-top: 3rem; |     margin-top: 3rem; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // @include pageMargin; |  | ||||||
|  |  | ||||||
|   @include tablet { |   @include tablet { | ||||||
|     padding: 0 1rem; |     padding: 0 1rem; | ||||||
|   } |   } | ||||||
| @@ -31,8 +29,4 @@ | |||||||
|   padding: 1rem; |   padding: 1rem; | ||||||
|   margin: 1rem 0; |   margin: 1rem 0; | ||||||
|   text-align: center; |   text-align: center; | ||||||
|  |  | ||||||
|   .underline { |  | ||||||
|     text-decoration: underline; |  | ||||||
|   } |  | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user