mirror of
https://github.com/KevinMidboe/planetposen-frontend.git
synced 2025-10-29 13:10:12 +00:00
Compare commits
4 Commits
ci/ghcr-pu
...
b56be97f86
| Author | SHA1 | Date | |
|---|---|---|---|
| b56be97f86 | |||
| 6d2550f2f3 | |||
| 71e053297e | |||
| 63a1107427 |
113
.drone.yml
113
.drone.yml
@@ -1,53 +1,130 @@
|
|||||||
---
|
---
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
type: docker
|
type: docker
|
||||||
name: Lint and build project
|
name: Build
|
||||||
|
|
||||||
platform:
|
platform:
|
||||||
os: linux
|
os: linux
|
||||||
arch: amd64
|
arch: amd64
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Build project
|
- name: Install dependencies
|
||||||
image: node:18
|
image: node:21-alpine3.17
|
||||||
commands:
|
commands:
|
||||||
- yarn
|
- yarn
|
||||||
- yarn build
|
|
||||||
|
|
||||||
- name: Lint project
|
- name: Lint project
|
||||||
image: node:18
|
image: node:21-alpine3.17
|
||||||
commands:
|
commands:
|
||||||
- yarn
|
|
||||||
- yarn lint
|
- yarn lint
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
image: node:21-alpine3.17
|
||||||
|
commands:
|
||||||
|
- yarn build
|
||||||
|
|
||||||
---
|
---
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
type: docker
|
type: docker
|
||||||
name: Compile docker image
|
name: Publish
|
||||||
|
|
||||||
platform:
|
platform:
|
||||||
os: linux
|
os: linux
|
||||||
arch: amd64
|
arch: amd64
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Build
|
- name: Publish to ghcr
|
||||||
image: node:18
|
image: plugins/docker
|
||||||
commands:
|
settings:
|
||||||
- yarn
|
registry: ghcr.io
|
||||||
- yarn build
|
repo: ghcr.io/kevinmidboe/${DRONE_REPO_NAME}
|
||||||
|
dockerfile: Dockerfile
|
||||||
depends_on:
|
username:
|
||||||
- Lint and build project
|
from_secret: GITHUB_USERNAME
|
||||||
|
password:
|
||||||
|
from_secret: GHCR_UPLOAD_TOKEN
|
||||||
|
tags:
|
||||||
|
- latest
|
||||||
|
- ${DRONE_COMMIT_SHA}
|
||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
branch:
|
|
||||||
- main
|
|
||||||
event:
|
event:
|
||||||
|
include:
|
||||||
|
- 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: 84765f19d995d66f1d3409c4eddd1f68d1f2d297d65cd9e2612e6bb13e8ecb94
|
hmac: 1e803c7610cc5d3b586af3f10228a4a3477d877538813dee6c366c952771e3e0
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|||||||
7
.kubernetes/0-Namespace.yml
Normal file
7
.kubernetes/0-Namespace.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: planet
|
||||||
|
labels:
|
||||||
|
name: planet
|
||||||
41
.kubernetes/deployment.yml
Normal file
41
.kubernetes/deployment.yml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
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
|
||||||
|
|
||||||
19
.kubernetes/ingress.yml
Normal file
19
.kubernetes/ingress.yml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
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
|
||||||
18
.kubernetes/service.yml
Normal file
18
.kubernetes/service.yml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
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
|
||||||
|
|
||||||
18
Dockerfile
Normal file
18
Dockerfile
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Build the project
|
||||||
|
FROM node:18-alpine AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY . .
|
||||||
|
RUN yarn
|
||||||
|
RUN yarn build
|
||||||
|
|
||||||
|
FROM node:18-alpine
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY --from=builder /app/build build/
|
||||||
|
COPY --from=builder /app/node_modules node_modules/
|
||||||
|
COPY package.json .
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
|
||||||
|
CMD [ "node", "build" ]
|
||||||
@@ -12,6 +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-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",
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
|
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, 'http://localhost:30010'), request);
|
request = new Request(request.url.replace(origin, host), request);
|
||||||
}
|
}
|
||||||
|
|
||||||
return fetch(request);
|
return fetch(request);
|
||||||
|
|||||||
@@ -10,19 +10,20 @@
|
|||||||
|
|
||||||
const textImages: Array<IFrontTextImage> = [
|
const textImages: Array<IFrontTextImage> = [
|
||||||
{
|
{
|
||||||
title: 'Vårt oppdrag',
|
title: 'Our story',
|
||||||
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.',
|
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.',
|
||||||
image: 'https://storage.googleapis.com/planetposen-images/front-kf-1.jpg'
|
image: 'https://storage.googleapis.com/planetposen-images/front-kf-1.jpg'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Paper waste and the planet',
|
title: 'Eco-Friendly Materials',
|
||||||
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.",
|
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.',
|
||||||
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: 'Our goal',
|
title: 'Gift Ideas',
|
||||||
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.',
|
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!",
|
||||||
image: 'https://storage.googleapis.com/planetposen-images/front-kf-3.jpg'
|
image: 'https://storage.googleapis.com/planetposen-images/front-kf-3.jpg'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -32,8 +33,8 @@
|
|||||||
image: 'https://storage.googleapis.com/planetposen-images/front-bee-1.jpg'
|
image: 'https://storage.googleapis.com/planetposen-images/front-bee-1.jpg'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Sculpture park of international standing',
|
title: 'Gift Ideas',
|
||||||
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.',
|
text: '',
|
||||||
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'
|
||||||
}
|
}
|
||||||
@@ -42,12 +43,12 @@
|
|||||||
const textTitle: Array<IFrontText> = [
|
const textTitle: Array<IFrontText> = [
|
||||||
{
|
{
|
||||||
title: 'Katy Vandekerckhove:',
|
title: 'Katy Vandekerckhove:',
|
||||||
text: 'Kistefos was really a jewel on earth with high level art in fantastic surroundings',
|
text: 'Give a gift that keeps on giving - our fabric gift bags are reusable, eco-friendly, and stylish.',
|
||||||
color: '#27615d'
|
color: '#27615d'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Katy Vandekerckhove:',
|
title: 'Katy Vandekerckhove:',
|
||||||
text: 'Kistefos was really a jewel on earth with high level art in fantastic surroundings',
|
text: 'Wrap it up in style and sustainability with planetposen fabric gift bags.',
|
||||||
color: 'orange'
|
color: 'orange'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -104,7 +105,10 @@
|
|||||||
<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>
|
||||||
|
|||||||
@@ -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: <a class="link" href="mailto:post@planetposen.no">post@planetposen.no</a></li>
|
<li>Epost: <a class="link" href="mailto:post@planetposen.no">post@planetposen.no</a></li>
|
||||||
|
|
||||||
@@ -41,6 +41,7 @@
|
|||||||
Kode: <a
|
Kode: <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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1
src/routes/_health/+server.ts
Normal file
1
src/routes/_health/+server.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export const GET = () => new Response('ok');
|
||||||
@@ -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,13 +148,15 @@
|
|||||||
|
|
||||||
<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">
|
||||||
<section class="express-checkout" style="display: block;">
|
{#if showExpressCheckout}
|
||||||
<h2>Hurtigkasse</h2>
|
<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>
|
<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>
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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 {
|
max-width: 600px;
|
||||||
margin: 20% 2rem;
|
margin: auto;
|
||||||
width: 60%;
|
|
||||||
|
@include desktop {
|
||||||
|
margin: 20% auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.signin-button {
|
.signin-button {
|
||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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 };
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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%
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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())
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import adapter from '@sveltejs/adapter-node';
|
||||||
import preprocess from 'svelte-preprocess';
|
import preprocess from 'svelte-preprocess';
|
||||||
|
|
||||||
/** @type {import('@sveltejs/kit').Config} */
|
/** @type {import('@sveltejs/kit').Config} */
|
||||||
@@ -7,6 +8,7 @@ const config = {
|
|||||||
preprocess: preprocess(),
|
preprocess: preprocess(),
|
||||||
|
|
||||||
kit: {
|
kit: {
|
||||||
|
adapter: adapter(),
|
||||||
csrf: {
|
csrf: {
|
||||||
checkOrigin: false
|
checkOrigin: false
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user