5 Commits

25 changed files with 1785 additions and 302 deletions

View File

@@ -1,130 +1,62 @@
--- ---
kind: pipeline kind: pipeline
type: docker type: docker
name: Build name: Lint and build project
platform: platform:
os: linux os: linux
arch: amd64 arch: amd64
steps: steps:
- name: Install dependencies - name: Lint project
image: node:21-alpine3.17 image: node:18
commands: commands:
- yarn - yarn
- name: Lint project
image: node:21-alpine3.17
commands:
- yarn lint - yarn lint
- name: Build - name: Build project
image: node:21-alpine3.17 image: node:18
commands: commands:
- yarn
- yarn build - yarn build
--- ---
kind: pipeline kind: pipeline
type: docker type: docker
name: Publish name: Compile & publish docker image
platform: platform:
os: linux os: linux
arch: amd64 arch: amd64
steps: steps:
- name: Publish to ghcr - name: Publish docker image
image: plugins/docker image: plugins/docker
settings: settings:
registry: ghcr.io registry: ghcr.io
repo: ghcr.io/kevinmidboe/${DRONE_REPO_NAME} repo: ghcr.io/kevinmidboe/planetposen-frontend
dockerfile: Dockerfile dockerfile: Dockerfile
username: username:
from_secret: GITHUB_USERNAME from_secret: GITHUB_USERNAME
password: password:
from_secret: GHCR_UPLOAD_TOKEN from_secret: GITHUB_PASSWORD
tags: tags: latest
- latest
- ${DRONE_COMMIT_SHA} depends_on:
- Lint and build project
trigger: trigger:
branch:
- main
- ci/ghcr-publish
event: event:
include: include:
- push - push
exclude: exclude:
- pull_request - pull_request
branch:
- main
depends_on:
- Build
---
kind: pipeline
type: docker
name: Deploy
platform:
os: linux
arch: amd64
steps:
- name: Prepare kubernetes environment
image: alpine/k8s:1.25.15
environment:
VAULT_TOKEN:
from_secret: VAULT_TOKEN
VAULT_HOST:
from_secret: VAULT_HOST
commands:
- mkdir -p /root/.kube
- echo "IMAGE=ghcr.io/kevinmidboe/${DRONE_REPO_NAME}:${DRONE_COMMIT_SHA}" > /root/.kube/.env
- echo "NAMESPACE=${DRONE_REPO_NAME}" >> /root/.kube/.env
- 'curl -s
-H "X-Vault-Token: $VAULT_TOKEN"
$VAULT_HOST/v1/schleppe/data/kazan/_infra
| jq -r ".data.data.KUBE_CONFIG" > /root/.kube/config'
- 'curl -s
-H "X-Vault-Token: $VAULT_TOKEN"
$VAULT_HOST/v1/schleppe/data/kazan/_infra
| jq -cr ".data.data | .[\"ghcr-login-secret\"] | @base64" > /root/.kube/dockerconfig.json'
- echo "DOCKER_CONFIG=$(cat /root/.kube/dockerconfig.json)" >> /root/.kube/.env
- sed -i '/^$/!s/^/export /' /root/.kube/.env
volumes:
- name: kube-config
path: /root/.kube
- name: Deploy to kubernetes
image: alpine/k8s:1.25.15
commands:
- source /root/.kube/.env > /dev/null 2>&1
- cat .kubernetes/*.yml
| envsubst
| kubectl --kubeconfig=/root/.kube/config apply -f -
volumes:
- name: kube-config
path: /root/.kube
trigger:
event:
include:
- push
exclude:
- pull_request
branch:
- main
depends_on:
- Build
- Publish
volumes:
- name: kube-config
temp: {}
--- ---
kind: signature kind: signature
hmac: 1e803c7610cc5d3b586af3f10228a4a3477d877538813dee6c366c952771e3e0 hmac: 2578e80da0b7719a6d85be93b4a86803159ba7f320707607df9b579979c66e39
... ...

2
.gitignore vendored
View File

@@ -9,4 +9,4 @@ node_modules
.vercel .vercel
.output .output
build build
yarn.lock

View File

@@ -1,7 +0,0 @@
---
apiVersion: v1
kind: Namespace
metadata:
name: planet
labels:
name: planet

View File

@@ -1,41 +0,0 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: planet-frontend
name: planet-frontend
namespace: planet
spec:
progressDeadlineSeconds: 600
replicas: 2
revisionHistoryLimit: 10
selector:
matchLabels:
app: planet-frontend
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
app: planet-frontend
spec:
containers:
- image: ${IMAGE}
imagePullPolicy: Always
name: planet-frontend
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
imagePullSecrets:
- name: ghcr-login-secret
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30

View File

@@ -1,19 +0,0 @@
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: planet-frontend-ingress
namespace: planet
spec:
ingressClassName: traefik
rules:
- host: planet.kazan.schleppe.cloud
http:
paths:
- backend:
service:
name: planet-frontend-service
port:
number: 80
path: /
pathType: Prefix

View File

@@ -1,18 +0,0 @@
---
apiVersion: v1
kind: Service
metadata:
labels:
app: planet-frontend
name: planet-frontend-service
namespace: planet
spec:
selector:
app: planet-frontend
type: ClusterIP
ports:
- name: http
port: 80
targetPort: 3000
sessionAffinity: None

View File

@@ -1,18 +1,28 @@
# Build the project # - - Build image
FROM node:18-alpine AS builder FROM node:18-alpine AS BUILD_IMAGE
# Alpine package dependencies & emove cached files to reduce size
RUN apk update && apk add python3 make g++ && rm -rf /var/cache/apk/*
WORKDIR /app WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn --froze-lockfile
COPY . . COPY . .
RUN yarn
# Build application
RUN yarn build RUN yarn build
# - - Run image
FROM node:18-alpine FROM node:18-alpine
WORKDIR /app WORKDIR /app
COPY --from=builder /app/build build/ COPY package.json ./
COPY --from=builder /app/node_modules node_modules/ COPY --from=BUILD_IMAGE /app/build ./build
COPY package.json . COPY --from=BUILD_IMAGE /app/node_modules ./node_modules
EXPOSE 3000 EXPOSE 3000
ENV NODE_ENV=production CMD ["node", "build/index.js"]
CMD [ "node", "build" ]

View File

@@ -12,7 +12,7 @@
"format": "prettier --plugin-search-dir . --write src" "format": "prettier --plugin-search-dir . --write src"
}, },
"devDependencies": { "devDependencies": {
"@sveltejs/adapter-node": "^4.0.1", "@sveltejs/adapter-node": "^1.2.3",
"@sveltejs/adapter-static": "1.0.0", "@sveltejs/adapter-static": "1.0.0",
"@sveltejs/kit": "1.0.1", "@sveltejs/kit": "1.0.1",
"@types/cookie": "0.5.1", "@types/cookie": "0.5.1",

View File

@@ -1,13 +1,11 @@
import { env } from '$env/dynamic/private';
import type { HandleFetch } from '@sveltejs/kit'; import type { HandleFetch } from '@sveltejs/kit';
export const handleFetch: HandleFetch = async ({ request, fetch }) => { export const handleFetch: HandleFetch = async ({ request, fetch }) => {
const { origin } = new URL(request.url); const { origin } = new URL(request.url);
const host = env?.API_HOST || 'http://localhost:30010';
if (request.url.startsWith(`${origin}/api`)) { if (request.url.startsWith(`${origin}/api`)) {
// clone the original request, but change the URL // clone the original request, but change the URL
request = new Request(request.url.replace(origin, host), request); request = new Request(request.url.replace(origin, 'http://localhost:30010'), request);
} }
return fetch(request); return fetch(request);

View File

@@ -10,20 +10,19 @@
const textImages: Array<IFrontTextImage> = [ const textImages: Array<IFrontTextImage> = [
{ {
title: 'Our story', title: 'Vårt oppdrag',
text: 'I started making fabric gift bags as a way to combat the wastefulness of traditional paper packaging. As a lifelong crafter and DIY enthusiast, I wanted to create a product that was both beautiful and sustainable. After experimenting with different materials and designs, I landed on the perfect formula for a reusable fabric gift bag that was both eco-friendly and functional.', text: 'The new fabulous museum at Kistefos, designed by world renowned architect Bjarke Ingels Group, BIG, opened Wednesday September 18th, 2019. The building has been named the top architectural museum project in the world to open in 2019, by both the Daily Telegraph and Bloomberg.',
image: 'https://storage.googleapis.com/planetposen-images/front-kf-1.jpg' image: 'https://storage.googleapis.com/planetposen-images/front-kf-1.jpg'
}, },
{ {
title: 'Eco-Friendly Materials', title: 'Paper waste and the planet',
text: 'Our bags are made from organic cotton or recycled fabric, both of which are sustainably sourced and responsibly produced. By choosing to use our bags instead of disposable paper or plastic packaging, you are making a positive impact on the environment and reducing your carbon footprint.', text: "As the 50th artwork to be included in the park, a site-specific new commission by French artist Pierre Huyghe (b. 1962, Paris) was opened on the 12th of June. The vast permanent work will be the artist's largest site-specific work to date and the most ambitious to ever be conceived for Kistefos.",
imageRight: true, imageRight: true,
// image: 'https://storage.googleapis.com/planetposen-images/front-kf-2.jpg' image: 'https://storage.googleapis.com/planetposen-images/front-kf-2.jpg'
image: 'https://storage.googleapis.com/planetposen-images/bags_backyard-upscaled-2.jpeg'
}, },
{ {
title: 'Gift Ideas', title: 'Our goal',
text: "Whether you're looking for a birthday present, a wedding gift, or a holiday surprise, our fabric gift bags are the perfect way to add a personal touch to any occasion. Use our bags to wrap a variety of gifts, from jewelry and accessories to small electronics and gadgets. Not sure where to start? Check out our gallery for inspiration!", text: 'The scenic sculpture park has an impressive collection of works by internationally renowned contemporary artists including Anish Kapoor, Jeppe Hein, Tony Cragg, Olafur Eliasson, Fernando Bottero and Elmgreen & Dragset. The sculpture park focus is sight specific and international contemporary works of art and is available all year.',
image: 'https://storage.googleapis.com/planetposen-images/front-kf-3.jpg' image: 'https://storage.googleapis.com/planetposen-images/front-kf-3.jpg'
}, },
{ {
@@ -33,8 +32,8 @@
image: 'https://storage.googleapis.com/planetposen-images/front-bee-1.jpg' image: 'https://storage.googleapis.com/planetposen-images/front-bee-1.jpg'
}, },
{ {
title: 'Gift Ideas', title: 'Sculpture park of international standing',
text: '', text: 'The scenic sculpture park has an impressive collection of works by internationally renowned contemporary artists including Anish Kapoor, Jeppe Hein, Tony Cragg, Olafur Eliasson, Fernando Bottero and Elmgreen & Dragset. The sculpture park focus is sight specific and international contemporary works of art and is available all year.',
imageRight: false, imageRight: false,
image: 'https://storage.googleapis.com/planetposen-images/front-bee-2.jpg' image: 'https://storage.googleapis.com/planetposen-images/front-bee-2.jpg'
} }
@@ -43,12 +42,12 @@
const textTitle: Array<IFrontText> = [ const textTitle: Array<IFrontText> = [
{ {
title: 'Katy Vandekerckhove:', title: 'Katy Vandekerckhove:',
text: 'Give a gift that keeps on giving - our fabric gift bags are reusable, eco-friendly, and stylish.', text: 'Kistefos was really a jewel on earth with high level art in fantastic surroundings',
color: '#27615d' color: '#27615d'
}, },
{ {
title: 'Katy Vandekerckhove:', title: 'Katy Vandekerckhove:',
text: 'Wrap it up in style and sustainability with planetposen fabric gift bags.', text: 'Kistefos was really a jewel on earth with high level art in fantastic surroundings',
color: 'orange' color: 'orange'
} }
]; ];
@@ -105,10 +104,7 @@
<FrontText data="{textTitle[0]}" /> <FrontText data="{textTitle[0]}" />
<FrontTextImage data="{textImages[3]}" /> <FrontTextImage data="{textImages[3]}" />
<!--
<FrontTextImage data="{textImages[4]}" /> <FrontTextImage data="{textImages[4]}" />
-->
<FrontText data="{textTitle[1]}" /> <FrontText data="{textTitle[1]}" />
</section> </section>

View File

@@ -14,7 +14,7 @@
<footer> <footer>
<section> <section>
<h1>Personvern og vilkår</h1> <h2>Personvern og vilkår</h2>
<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>
<h1>Kontakt</h1> <h2>Kontakt</h2>
<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,7 +41,6 @@
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
> >
@@ -61,6 +60,10 @@
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;
@@ -94,7 +97,7 @@
flex-direction: column; flex-direction: column;
padding: 3rem 0.5rem; padding: 3rem 0.5rem;
h1 { h2 {
font-size: 1.8rem; font-size: 1.8rem;
} }

View File

@@ -1 +0,0 @@
export const GET = () => new Response('ok');

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, subTotal } from '$lib/cartStore'; import { cart } 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,7 +28,6 @@
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;
@@ -148,15 +147,13 @@
<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>
<ExpressSection /> <ExpressSection />
<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>
@@ -181,7 +178,7 @@
<aside class="sidebar"> <aside class="sidebar">
<section id="order"> <section id="order">
<h2>Din ordre</h2> <h2>Din ordre</h2>
<OrderSection lineItems="{$cart}" subTotal="{$subTotal}" /> <OrderSection />
</section> </section>
</aside> </aside>
</form> </form>

View File

@@ -1,5 +1,6 @@
<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,9 +1,16 @@
<script lang="ts"> <script lang="ts">
import OrderTotalSection from './OrderTotalSection.svelte'; import OrderTotalSection from './OrderTotalSection.svelte';
import type ICart from '$lib/interfaces/ICart'; import QuantitySelect from '$lib/components/QuantitySelect.svelte';
export let lineItems: ICart[]; import { cart, subTotal } from '$lib/cartStore';
export let subTotal: number; import { decrementProductInCart, incrementProductInCart } from '$lib/websocketCart';
const shippingPrice = 75;
$: totalPrice = $subTotal + shippingPrice;
function lineItemClass(id: number) {
return `lineitem-${id}`;
}
</script> </script>
<div class="order-summary"> <div class="order-summary">
@@ -19,7 +26,7 @@
</thead> </thead>
<tbody data-order-summary-section="line-items"> <tbody data-order-summary-section="line-items">
{#each lineItems as lineItem} {#each $cart as cartItem}
<tr <tr
class="product" class="product"
data-product-id="6718367989809" data-product-id="6718367989809"
@@ -33,18 +40,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="{lineItem.image}" src="{cartItem.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">{lineItem.quantity}</span <span class="product-thumbnail__quantity" aria-hidden="true">{cartItem.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">{lineItem.name}</span> <span class="product__description__name order-summary__emphasis">{cartItem.name}</span>
<span class="product__description__variant order-summary__small-text" <span class="product__description__variant order-summary__small-text"
>{lineItem.size}</span >{cartItem.size}</span
> >
</th> </th>
<td class="product__quantity"> <td class="product__quantity">
@@ -52,7 +59,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 {lineItem.quantity * lineItem.price} NOK {cartItem.quantity * cartItem.price}
</p> </p>
</td> </td>
</tr> </tr>
@@ -61,7 +68,7 @@
</tbody> </tbody>
</table> </table>
<OrderTotalSection subTotal="{subTotal}" /> <OrderTotalSection />
</div> </div>
<style lang="scss" module="scoped"> <style lang="scss" module="scoped">

View File

@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
export let subTotal: number; import { subTotal } from '$lib/cartStore';
</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,14 +62,13 @@
<style lang="scss" module="scoped"> <style lang="scss" module="scoped">
@import '../../styles/media-queries.scss'; @import '../../styles/media-queries.scss';
section { @include desktop {
max-width: 600px; section {
margin: auto; margin: 20% 2rem;
width: 60%;
@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: IOrderDTO = await res.json(); const orderResponse = 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 } from './$types'; import type { Actions, PageServerLoad } from './$types';
export const actions: Actions = { export const actions: Actions = {
default: async ({ request }) => { default: async ({ request }) => {
@@ -10,6 +10,8 @@ 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,11 +2,17 @@
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 { IOrder } from '$lib/interfaces/IOrder'; import type { PageServerData } from './$types';
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;
@@ -17,8 +23,6 @@
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">
@@ -45,9 +49,21 @@
</div> </div>
<div class="order-receipt"> <div class="order-receipt">
<div class="receipt-box"> {#each order?.lineItems as lineItem}
<OrderSection lineItems="{order?.lineItems}" subTotal="{subTotal}" } /> <p>
</div> <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> </div>
</section> </section>
@@ -59,52 +75,30 @@
} }
.order-receipt { .order-receipt {
--receipt_color: #f7f7f7; background-color: #f7f7f7;
--tearOffHeight: 8px; max-width: 500px;
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;
/* Paper background effect */ p {
.receipt-box { margin: 0.8rem 0;
height: auto; display: flex;
overflow: hidden; justify-content: space-between;
padding: 1rem; border-bottom: 1px solid lightgrey;
box-shadow: 0 3px 5px rgba(0, 0, 0, 0.05);
&::after { &:last-of-type {
content: ''; padding-top: 1.5rem;
height: var(--tearOffHeight); border-width: 2px;
position: absolute; }
left: 0; }
right: 0;
bottom: calc(var(--tearOffHeight) * -1); code {
background-color: var(--receipt_color); opacity: 0.4;
clip-path: polygon( font-size: 1rem;
0% 0%,
5% 100%, &:first-of-type {
10% 0%, font-weight: 600;
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,6 +4,7 @@
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;
@@ -21,7 +22,7 @@
goto(url); goto(url);
} }
async function checkOrder() { 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,4 +1,5 @@
<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';
@@ -97,7 +98,7 @@
.details { .details {
.name { .name {
font-size: 2em; font-size: 2rem;
} }
.description { .description {

View File

@@ -14,7 +14,7 @@
color: var(--color-text); color: var(--color-text);
} }
body .app main { html {
font-size: 100%; font-size: 100%;
} }
@@ -56,11 +56,11 @@ a.link {
} }
h1 { h1 {
font-size: 2em; font-size: 2rem;
} }
h2 { h2 {
font-size: 1em; font-size: 1rem;
} }
.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) {
body .app main { html {
font-size: 110%; font-size: 110%;
} }
} }
@media screen and (min-width: 2000px) { @media screen and (min-width: 2000px) {
body .app main { html {
font-size: 125%; font-size: 130%;
} }
} }

View File

@@ -3,16 +3,19 @@ import preprocess from 'svelte-preprocess';
/** @type {import('@sveltejs/kit').Config} */ /** @type {import('@sveltejs/kit').Config} */
const config = { const config = {
// Consult https://github.com/sveltejs/svelte-preprocess // Consult https://github.com/sveltejs/svelte-preprocess
// for more information about preprocessors // for more information about preprocessors
preprocess: preprocess(), preprocess: preprocess(),
kit: { kit: {
adapter: adapter(), adapter: adapter({
csrf: { out: 'build',
checkOrigin: false preprocess: false
} }),
} csrf: {
checkOrigin: false
}
}
}; };
export default config; export default config;

1625
yarn.lock Normal file

File diff suppressed because it is too large Load Diff