From e43405bcc614a59cd53fe4793e04de3b66b85551 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Fri, 17 Apr 2026 08:33:18 +0200 Subject: [PATCH 01/47] Add empty page. --- site/content/checkout/index.md | 5 +++++ site/layouts/checkout/single.html | 15 +++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 site/content/checkout/index.md create mode 100644 site/layouts/checkout/single.html diff --git a/site/content/checkout/index.md b/site/content/checkout/index.md new file mode 100644 index 00000000..623ff0a6 --- /dev/null +++ b/site/content/checkout/index.md @@ -0,0 +1,5 @@ +--- +title: Checkout +description: Checkout +header_type: fixed-header +--- diff --git a/site/layouts/checkout/single.html b/site/layouts/checkout/single.html new file mode 100644 index 00000000..36e4a853 --- /dev/null +++ b/site/layouts/checkout/single.html @@ -0,0 +1,15 @@ +{{ define "main" }} + {{ partial "components/navbar/navbar.html" . }} +
+
+
+
+
+ {{ .Content }} +
+
+
+
+
+ {{ partial "components/go-top-button.html" . }} +{{ end }} From 2ee49557c0b8dca0fd4f7db29448f98000d0bcf3 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Fri, 17 Apr 2026 09:28:07 +0200 Subject: [PATCH 02/47] Add total section. --- site/assets/scss/main.scss | 1 + site/assets/scss/pages/_checkout.scss | 179 ++++++++++++++++++++++++++ site/content/checkout/index.md | 5 + site/layouts/checkout/single.html | 32 ++++- 4 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 site/assets/scss/pages/_checkout.scss diff --git a/site/assets/scss/main.scss b/site/assets/scss/main.scss index 3f9c416f..c0725011 100644 --- a/site/assets/scss/main.scss +++ b/site/assets/scss/main.scss @@ -51,4 +51,5 @@ @import "pages/about"; @import "pages/licenses"; @import "pages/privacy"; +@import "pages/checkout"; @import "pages/blog"; diff --git a/site/assets/scss/pages/_checkout.scss b/site/assets/scss/pages/_checkout.scss new file mode 100644 index 00000000..dcfd03a1 --- /dev/null +++ b/site/assets/scss/pages/_checkout.scss @@ -0,0 +1,179 @@ +/*! + * Copyright 2026, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +.checkout { + background: + radial-gradient(circle at top left, rgba($main-brand-color, .10), transparent 36%), + linear-gradient(180deg, #f5faff 0%, var(--body-bg-color) 220px); + + .content-with-fixed-header { + margin-top: $header-height; + + @include breakpoint(desktop) { + margin-top: $header-height; + } + } +} + +.checkout-summary { + .checkout-summary__header { + margin-bottom: 16px; + } + + .checkout-summary__title { + margin-bottom: 0; + font-size: 40px; + line-height: 1.05; + + @include breakpoint(tablet) { + font-size: 34px; + } + } + + .checkout-summary__details { + border-top: 1px solid rgba(black, .08); + padding-top: 12px; + } + + .checkout-summary__row { + display: flex; + align-items: flex-end; + gap: 14px; + padding: 12px 0; + border-bottom: 1px solid rgba(black, .08); + + @include breakpoint(md-phone) { + gap: 8px; + } + } + + .checkout-summary__row--total { + margin-top: 2px; + padding-top: 14px; + border-bottom: none; + + .checkout-summary__label { + color: $black; + font-size: 28px; + font-weight: 800; + letter-spacing: -.03em; + } + + .checkout-summary__value { + font-size: 28px; + font-weight: 800; + letter-spacing: -.03em; + } + } + + .checkout-summary__product { + padding: 4px 0 10px; + color: $main-brand-color; + font-size: 32px; + font-weight: 700; + letter-spacing: -.02em; + line-height: 1.15; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + + @include breakpoint(md-phone) { + font-size: 26px; + } + } + + .checkout-summary__label { + flex: 0 0 auto; + color: $gray-500; + font-size: 15px; + font-weight: 600; + line-height: 1.2; + } + + .checkout-summary__dots { + flex: 1 1 auto; + align-self: flex-end; + min-width: 20px; + height: 1px; + margin-bottom: .22em; + background-image: radial-gradient(circle, rgba($gray-500, .28) 1px, transparent 1.5px); + background-position: left center; + background-repeat: repeat-x; + background-size: 8px 2px; + } + + .checkout-summary__value { + flex: 0 0 auto; + color: $black; + font-size: 18px; + font-weight: 700; + line-height: 1.2; + text-align: right; + + @include breakpoint(md-phone) { + max-width: 58%; + } + } + + .checkout-summary__value--amount { + color: $black; + font-size: 20px; + font-weight: 700; + line-height: 1.2; + } + + .checkout-summary__value--total { + color: $main-brand-color; + font-size: 28px; + font-weight: 800; + letter-spacing: -.03em; + line-height: 1.1; + } + + .checkout-summary__row--total { + .checkout-summary__dots { + margin-bottom: .18em; + } + } + + @include breakpoint(md-phone) { + .checkout-summary__label { + font-size: 14px; + } + + .checkout-summary__value--amount { + font-size: 18px; + } + + .checkout-summary__row--total .checkout-summary__label, + .checkout-summary__value--total { + font-size: 24px; + } + } + + max-width: none; + margin-bottom: 24px; +} diff --git a/site/content/checkout/index.md b/site/content/checkout/index.md index 623ff0a6..1edf0e7d 100644 --- a/site/content/checkout/index.md +++ b/site/content/checkout/index.md @@ -1,5 +1,10 @@ --- title: Checkout +headline: Checkout description: Checkout header_type: fixed-header +product_name: Product name +net_price: 0.00 +vat_price: 0.00 +currency: EUR --- diff --git a/site/layouts/checkout/single.html b/site/layouts/checkout/single.html index 36e4a853..25cb4b7c 100644 --- a/site/layouts/checkout/single.html +++ b/site/layouts/checkout/single.html @@ -1,10 +1,40 @@ {{ define "main" }} + {{ $productName := .Params.product_name | default "Product name" }} + {{ $netPrice := .Params.net_price | default "0.00" }} + {{ $vatPrice := .Params.vat_price | default "0.00" }} + {{ $currency := .Params.currency | default "EUR" }} + {{ $totalPrice := add (float $netPrice) (float $vatPrice) }} {{ partial "components/navbar/navbar.html" . }} -
+
+
+
+

{{ .Params.headline | default .Title }}

+
+
+
+ {{ $productName }} +
+
+ Net price + + {{ $netPrice }} {{ $currency }} +
+
+ Tax (VAT) + + {{ $vatPrice }} {{ $currency }} +
+
+ Total + + {{ lang.FormatNumber 2 $totalPrice }} {{ $currency }} +
+
+
{{ .Content }}
From d5dd929c65a9740c5cb09657f5cd09646e83e535 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Fri, 17 Apr 2026 09:53:03 +0200 Subject: [PATCH 03/47] Fix layout. --- site/assets/scss/pages/_checkout.scss | 154 ++++++++++++++++++++------ site/content/checkout/index.md | 1 + 2 files changed, 123 insertions(+), 32 deletions(-) diff --git a/site/assets/scss/pages/_checkout.scss b/site/assets/scss/pages/_checkout.scss index dcfd03a1..aabba927 100644 --- a/site/assets/scss/pages/_checkout.scss +++ b/site/assets/scss/pages/_checkout.scss @@ -24,10 +24,26 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +.checkout-page { + overflow-x: hidden; + + .wrapper, + .main, + .footer, + #header { + overflow-x: hidden; + } +} + .checkout { background: radial-gradient(circle at top left, rgba($main-brand-color, .10), transparent 36%), linear-gradient(180deg, #f5faff 0%, var(--body-bg-color) 220px); + overflow-x: hidden; + + @include breakpoint(lg-phone) { + background: white; + } .content-with-fixed-header { margin-top: $header-height; @@ -36,11 +52,24 @@ margin-top: $header-height; } } + + .row { + margin-right: 0; + margin-left: 0; + } + + .row > [class*='col-'] { + padding-right: 0; + padding-left: 0; + } } .checkout-summary { + max-width: 100%; + min-width: 0; + .checkout-summary__header { - margin-bottom: 16px; + margin-bottom: 12px; } .checkout-summary__title { @@ -55,50 +84,54 @@ .checkout-summary__details { border-top: 1px solid rgba(black, .08); - padding-top: 12px; + padding-top: 8px; } .checkout-summary__row { - display: flex; - align-items: flex-end; - gap: 14px; - padding: 12px 0; + display: grid; + grid-template-columns: auto minmax(0, 1fr) auto; + align-items: end; + column-gap: 14px; + padding: 9px 0; border-bottom: 1px solid rgba(black, .08); + @include breakpoint(lg-phone) { + column-gap: 10px; + } + @include breakpoint(md-phone) { - gap: 8px; + column-gap: 8px; } } .checkout-summary__row--total { - margin-top: 2px; - padding-top: 14px; + margin-top: 1px; + padding-top: 10px; border-bottom: none; .checkout-summary__label { color: $black; - font-size: 28px; + font-size: 22px; font-weight: 800; - letter-spacing: -.03em; + letter-spacing: -.02em; } .checkout-summary__value { - font-size: 28px; + font-size: 22px; font-weight: 800; - letter-spacing: -.03em; + letter-spacing: -.02em; } } .checkout-summary__product { - padding: 4px 0 10px; + max-width: 100%; + padding: 2px 0 8px; color: $main-brand-color; font-size: 32px; font-weight: 700; letter-spacing: -.02em; line-height: 1.15; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; + overflow-wrap: anywhere; @include breakpoint(md-phone) { font-size: 26px; @@ -107,6 +140,7 @@ .checkout-summary__label { flex: 0 0 auto; + min-width: 0; color: $gray-500; font-size: 15px; font-weight: 600; @@ -114,8 +148,6 @@ } .checkout-summary__dots { - flex: 1 1 auto; - align-self: flex-end; min-width: 20px; height: 1px; margin-bottom: .22em; @@ -126,30 +158,29 @@ } .checkout-summary__value { - flex: 0 0 auto; + min-width: 0; + max-width: 100%; color: $black; font-size: 18px; font-weight: 700; line-height: 1.2; text-align: right; - - @include breakpoint(md-phone) { - max-width: 58%; - } + white-space: normal; + overflow-wrap: anywhere; } .checkout-summary__value--amount { color: $black; - font-size: 20px; + font-size: 17px; font-weight: 700; line-height: 1.2; } .checkout-summary__value--total { - color: $main-brand-color; - font-size: 28px; + color: $black; + font-size: 22px; font-weight: 800; - letter-spacing: -.03em; + letter-spacing: -.02em; line-height: 1.1; } @@ -159,21 +190,80 @@ } } + @include breakpoint(lg-phone) { + margin-bottom: 16px; + + .checkout-summary__title { + font-size: 32px; + } + + .checkout-summary__product { + font-size: 24px; + padding-bottom: 6px; + } + + .checkout-summary__row { + display: block; + padding: 8px 0; + } + + .checkout-summary__label { + display: block; + min-width: 0; + margin-bottom: 2px; + } + + .checkout-summary__dots { + display: none; + } + + .checkout-summary__value { + display: block; + text-align: left; + } + } + @include breakpoint(md-phone) { .checkout-summary__label { font-size: 14px; } .checkout-summary__value--amount { - font-size: 18px; + font-size: 16px; } .checkout-summary__row--total .checkout-summary__label, .checkout-summary__value--total { - font-size: 24px; + font-size: 20px; + } + } + + @include breakpoint(sm-phone) { + .checkout-summary__header { + margin-bottom: 10px; + } + + .checkout-summary__title { + font-size: 30px; + } + + .checkout-summary__product { + font-size: 22px; + } + + .checkout-summary__row { + padding: 8px 0; + } + + .checkout-summary__value--amount { + font-size: 15px; + } + + .checkout-summary__row--total .checkout-summary__label, + .checkout-summary__value--total { + font-size: 18px; } } - max-width: none; - margin-bottom: 24px; + margin-bottom: 18px; } diff --git a/site/content/checkout/index.md b/site/content/checkout/index.md index 1edf0e7d..2bfa931c 100644 --- a/site/content/checkout/index.md +++ b/site/content/checkout/index.md @@ -3,6 +3,7 @@ title: Checkout headline: Checkout description: Checkout header_type: fixed-header +body_class: checkout-page product_name: Product name net_price: 0.00 vat_price: 0.00 From 3a0759daf8de34c5420806c27e4ecc47945f2d69 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Fri, 17 Apr 2026 10:12:18 +0200 Subject: [PATCH 04/47] Add checkout fields. --- site/assets/scss/pages/_checkout.scss | 101 +++++++++++++++++++++ site/layouts/checkout/single.html | 126 ++++++++++++++++++++++++++ 2 files changed, 227 insertions(+) diff --git a/site/assets/scss/pages/_checkout.scss b/site/assets/scss/pages/_checkout.scss index aabba927..bf04b394 100644 --- a/site/assets/scss/pages/_checkout.scss +++ b/site/assets/scss/pages/_checkout.scss @@ -267,3 +267,104 @@ margin-bottom: 18px; } + +.checkout-form { + margin-top: 24px; + padding-top: 20px; + border-top: 1px solid rgba(black, .08); + + .checkout-form__title { + margin-bottom: 16px; + padding-top: 0; + font-size: 28px; + line-height: 1.15; + } + + .checkout-form__grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 16px 20px; + } + + .checkout-form__field--full { + grid-column: 1 / -1; + } + + .checkout-form__label { + display: block; + margin-bottom: 6px; + color: $gray-500; + font-size: 14px; + font-weight: 600; + line-height: 1.3; + } + + .checkout-form__input { + display: block; + width: 100%; + min-width: 0; + padding: 13px 15px; + color: $black; + font-size: 16px; + line-height: 1.3; + background-color: white; + border: 1px solid rgba(black, .12); + border-radius: $border-radius-m; + box-shadow: inset 0 1px 1px rgba(black, .02); + transition: border-color .2s ease-in-out, box-shadow .2s ease-in-out; + + &:focus { + outline: none; + border-color: rgba($main-brand-color, .55); + box-shadow: 0 0 0 3px rgba($main-brand-color, .12); + } + } + + .checkout-form__select { + appearance: none; + background-image: + linear-gradient(45deg, transparent 50%, rgba($gray-500, .9) 50%), + linear-gradient(135deg, rgba($gray-500, .9) 50%, transparent 50%); + background-position: + calc(100% - 18px) calc(50% - 2px), + calc(100% - 12px) calc(50% - 2px); + background-repeat: no-repeat; + background-size: 6px 6px, 6px 6px; + padding-right: 40px; + } + + .checkout-form__actions { + margin-top: 20px; + } + + .checkout-form__submit { + min-width: 220px; + } + + @include breakpoint(lg-phone) { + .checkout-form__title { + font-size: 24px; + } + + .checkout-form__grid { + grid-template-columns: 1fr; + gap: 14px; + } + + .checkout-form__field--full { + grid-column: auto; + } + } + + @include breakpoint(sm-phone) { + .checkout-form__input { + padding: 12px 14px; + font-size: 15px; + } + + .checkout-form__submit { + width: 100%; + min-width: 0; + } + } +} diff --git a/site/layouts/checkout/single.html b/site/layouts/checkout/single.html index 25cb4b7c..e0741759 100644 --- a/site/layouts/checkout/single.html +++ b/site/layouts/checkout/single.html @@ -35,6 +35,132 @@

{{ .Params.headline | default .Title }}

+
+

Billing details

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+
{{ .Content }} From d5c28a5430d2ee702ba9efecf9973a9b0fc2c7fc Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Fri, 17 Apr 2026 10:47:05 +0200 Subject: [PATCH 05/47] Improve fields. --- site/assets/scss/pages/_checkout.scss | 11 ++++++-- site/content/checkout/index.md | 1 + site/layouts/checkout/single.html | 40 +++++++++++++++++---------- 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/site/assets/scss/pages/_checkout.scss b/site/assets/scss/pages/_checkout.scss index bf04b394..0c0fc6c6 100644 --- a/site/assets/scss/pages/_checkout.scss +++ b/site/assets/scss/pages/_checkout.scss @@ -83,7 +83,7 @@ } .checkout-summary__details { - border-top: 1px solid rgba(black, .08); + border-top: none; padding-top: 8px; } @@ -93,7 +93,7 @@ align-items: end; column-gap: 14px; padding: 9px 0; - border-bottom: 1px solid rgba(black, .08); + border: none; @include breakpoint(lg-phone) { column-gap: 10px; @@ -107,7 +107,6 @@ .checkout-summary__row--total { margin-top: 1px; padding-top: 10px; - border-bottom: none; .checkout-summary__label { color: $black; @@ -299,6 +298,12 @@ line-height: 1.3; } + .checkout-form__label--required::after { + content: ' *'; + color: #d93025; + font-weight: 700; + } + .checkout-form__input { display: block; width: 100%; diff --git a/site/content/checkout/index.md b/site/content/checkout/index.md index 2bfa931c..a7bb0321 100644 --- a/site/content/checkout/index.md +++ b/site/content/checkout/index.md @@ -7,5 +7,6 @@ body_class: checkout-page product_name: Product name net_price: 0.00 vat_price: 0.00 +vat_rate: 0% currency: EUR --- diff --git a/site/layouts/checkout/single.html b/site/layouts/checkout/single.html index e0741759..12da557d 100644 --- a/site/layouts/checkout/single.html +++ b/site/layouts/checkout/single.html @@ -2,6 +2,7 @@ {{ $productName := .Params.product_name | default "Product name" }} {{ $netPrice := .Params.net_price | default "0.00" }} {{ $vatPrice := .Params.vat_price | default "0.00" }} + {{ $vatRate := .Params.vat_rate | default "20%" }} {{ $currency := .Params.currency | default "EUR" }} {{ $totalPrice := add (float $netPrice) (float $vatPrice) }} {{ partial "components/navbar/navbar.html" . }} @@ -19,12 +20,12 @@

{{ .Params.headline | default .Title }}

{{ $productName }}
- Net price + Subtotal {{ $netPrice }} {{ $currency }}
- Tax (VAT) + VAT ({{ $vatRate }}) {{ $vatPrice }} {{ $currency }}
@@ -39,7 +40,7 @@

{{ .Params.headline | default .Title }}

Billing details

- + Billing details inputmode="email" required>
+
+ + +
Billing details required>
- + + autocomplete="organization" + required>
- + + +
+
+ -
From 6361d19a39a20c260284421ed10739f42b9b3479 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Mon, 20 Apr 2026 08:55:13 +0200 Subject: [PATCH 08/47] Improve naming. --- site/assets/scss/pages/_checkout.scss | 10 +++++----- site/layouts/checkout/single.html | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/site/assets/scss/pages/_checkout.scss b/site/assets/scss/pages/_checkout.scss index d84981c5..f7951dc2 100644 --- a/site/assets/scss/pages/_checkout.scss +++ b/site/assets/scss/pages/_checkout.scss @@ -275,7 +275,7 @@ grid-column: 1 / -1; } - .checkout-form__field--error { + .field-error { .checkout-form__label { color: #d93025; } @@ -290,7 +290,7 @@ line-height: 1.3; } - .checkout-form__label--required::after { + .required-label::after { content: ' *'; color: #d93025; font-weight: 700; @@ -317,12 +317,12 @@ } } - .checkout-form__field--error .checkout-form__input { + .field-error .checkout-form__input { border-color: rgba(#d93025, .7); box-shadow: 0 0 0 3px rgba(#d93025, .12); } - .checkout-form__error { + .error-message { display: none; margin-top: 6px; color: #d93025; @@ -330,7 +330,7 @@ line-height: 1.35; } - .checkout-form__field--error .checkout-form__error { + .field-error .error-message { display: block; } diff --git a/site/layouts/checkout/single.html b/site/layouts/checkout/single.html index a4f9c756..e924d9c7 100644 --- a/site/layouts/checkout/single.html +++ b/site/layouts/checkout/single.html @@ -44,7 +44,7 @@

Billing details

- + Billing details autocomplete="family-name">
- + Billing details required>
- + Billing details required>
- + +
+ + + + + +
@@ -168,6 +208,27 @@

Billing details

+ {{ .Content }}
From 5de11169a89b7d3c06c0d06724866556d74d3971 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Wed, 22 Apr 2026 18:46:37 +0200 Subject: [PATCH 12/47] Add 'Thank you page'. --- site/content/checkout-completed/index.md | 7 ++++++ .../_partials/scripts/body-scripts.html | 2 ++ site/layouts/checkout-completed/single.html | 25 +++++++++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 site/content/checkout-completed/index.md create mode 100644 site/layouts/checkout-completed/single.html diff --git a/site/content/checkout-completed/index.md b/site/content/checkout-completed/index.md new file mode 100644 index 00000000..fef2636d --- /dev/null +++ b/site/content/checkout-completed/index.md @@ -0,0 +1,7 @@ +--- +title: Thank you for your purchase +headline: Thank you for your purchase +description: Your checkout has been completed successfully. +body_class: checkout-page +--- + diff --git a/site/layouts/_partials/scripts/body-scripts.html b/site/layouts/_partials/scripts/body-scripts.html index dd2c48e4..85783cf8 100644 --- a/site/layouts/_partials/scripts/body-scripts.html +++ b/site/layouts/_partials/scripts/body-scripts.html @@ -26,10 +26,12 @@ {{ $environment := hugo.Environment }} {{ $payment := site.Params.payment }} +{{ $paygate := site.Params.paygate }} {{ $params := (dict "environment" $environment "payment" $payment + "paygate" $paygate )}} {{ partial "theme/scripts/body/baseurl.html" . }} diff --git a/site/layouts/checkout-completed/single.html b/site/layouts/checkout-completed/single.html new file mode 100644 index 00000000..219a625f --- /dev/null +++ b/site/layouts/checkout-completed/single.html @@ -0,0 +1,25 @@ +{{ define "main" }} + {{ partial "components/navbar/navbar.html" . }} +
+
+
+
+
+
+ +

Thank you for your purchase

+

+ Your payment was completed successfully. We will send the order details to your email. +

+ + Back to home + +
+ {{ .Content }} +
+
+
+
+
+ {{ partial "components/go-top-button.html" . }} +{{ end }} From 50f50283b9606818b569b3e718793f3efc84bec8 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Wed, 22 Apr 2026 19:00:05 +0200 Subject: [PATCH 13/47] Improve 'Thank you page' layout. --- site/assets/scss/pages/_checkout.scss | 74 +++++++++++++++------ site/content/checkout-completed/index.md | 2 - site/layouts/checkout-completed/single.html | 17 +++-- 3 files changed, 65 insertions(+), 28 deletions(-) diff --git a/site/assets/scss/pages/_checkout.scss b/site/assets/scss/pages/_checkout.scss index 30ead962..0be19311 100644 --- a/site/assets/scss/pages/_checkout.scss +++ b/site/assets/scss/pages/_checkout.scss @@ -520,61 +520,95 @@ } .checkout-completed { + min-height: 420px; + .checkout-completed__panel { - max-width: 560px; + max-width: 620px; margin: 0 auto; - padding: 44px 0 20px; + padding: 20px 0 30px; text-align: center; } .checkout-completed__mark { - display: flex; - align-items: center; - justify-content: center; - width: 58px; - height: 58px; - margin: 0 auto 22px; - color: $main-brand-color; - font-size: 34px; - font-weight: 800; - line-height: 1; + position: relative; + width: 64px; + height: 64px; + margin: 0 auto 9px; background: rgba($main-brand-color, .1); border: 1px solid rgba($main-brand-color, .18); border-radius: 50%; + + &::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 18px; + height: 30px; + border-right: 4px solid $main-brand-color; + border-bottom: 4px solid $main-brand-color; + transform: translate(-50%, -58%) rotate(42deg); + transform-origin: center; + } } .checkout-completed__title { - margin: 0 0 12px; + margin: 0 0 14px; color: $black; - font-size: 32px; + font-size: 36px; font-weight: 800; - line-height: 1.2; + line-height: 1.15; } .checkout-completed__text { - max-width: 460px; - margin: 0 auto 28px; + max-width: 520px; + margin: 0 auto 30px; color: $gray-500; font-size: 17px; line-height: 1.55; + text-align: center; + + span { + display: block; + text-align: center; + } } .checkout-completed__link { - min-width: 156px; + min-width: 220px; + text-decoration: none; + + &:hover, + &:focus { + text-decoration: none; + } } @include breakpoint(sm-phone) { + min-height: auto; + .checkout-completed__panel { - padding-top: 30px; + padding: 14px 0 16px; + } + + .checkout-completed__mark { + width: 58px; + height: 58px; + margin-bottom: 8px; } .checkout-completed__title { - font-size: 26px; + font-size: 28px; } .checkout-completed__text { font-size: 16px; } + + .checkout-completed__link { + width: 100%; + min-width: 0; + } } } diff --git a/site/content/checkout-completed/index.md b/site/content/checkout-completed/index.md index fef2636d..9d0ba8e1 100644 --- a/site/content/checkout-completed/index.md +++ b/site/content/checkout-completed/index.md @@ -1,6 +1,4 @@ --- -title: Thank you for your purchase -headline: Thank you for your purchase description: Your checkout has been completed successfully. body_class: checkout-page --- diff --git a/site/layouts/checkout-completed/single.html b/site/layouts/checkout-completed/single.html index 219a625f..895962bc 100644 --- a/site/layouts/checkout-completed/single.html +++ b/site/layouts/checkout-completed/single.html @@ -1,19 +1,24 @@ {{ define "main" }} {{ partial "components/navbar/navbar.html" . }} + {{ partial "components/nav-hero.html" . }}
-
+
- +

Thank you for your purchase

- Your payment was completed successfully. We will send the order details to your email. + Your payment was completed successfully. + We will send the order details to your email.

- - Back to home - +
+ +
{{ .Content }}
From 40ce1c267bdc7dd336cafe32970e3500bac2c483 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Wed, 22 Apr 2026 19:11:45 +0200 Subject: [PATCH 14/47] Improve page-404. --- site/assets/scss/pages/_checkout.scss | 16 ++++++++++++++ site/layouts/404.html | 21 +++++++++++++++---- .../_partials/components/nav-hero.html | 6 ++++-- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/site/assets/scss/pages/_checkout.scss b/site/assets/scss/pages/_checkout.scss index 0be19311..c80b594e 100644 --- a/site/assets/scss/pages/_checkout.scss +++ b/site/assets/scss/pages/_checkout.scss @@ -612,6 +612,22 @@ } } +.checkout-not-found { + .checkout-not-found__mark { + display: flex; + align-items: center; + justify-content: center; + color: $main-brand-color; + font-size: 17px; + font-weight: 800; + letter-spacing: 0; + + &::after { + display: none; + } + } +} + .checkout-modal { position: fixed; inset: 0; diff --git a/site/layouts/404.html b/site/layouts/404.html index 1c3f01fc..e05f7c32 100644 --- a/site/layouts/404.html +++ b/site/layouts/404.html @@ -21,13 +21,26 @@ })(); {{ partial "components/navbar/navbar.html" . }} -
-
+ {{ partial "components/nav-hero.html" (dict "Params" (dict "hide_page_headline" true) "Title" "") }} +
+
-

404

-

The page you are looking for could not be found.

+
+ +

Page not found

+

+ The page you are looking for could not be found. + Please check the address or return to the home page. +

+
+ +
+
diff --git a/site/layouts/_partials/components/nav-hero.html b/site/layouts/_partials/components/nav-hero.html index 8d1548b7..d3590101 100644 --- a/site/layouts/_partials/components/nav-hero.html +++ b/site/layouts/_partials/components/nav-hero.html @@ -28,13 +28,15 @@
- {{ $headline := .Params.headline | default .Title }} - {{ if $headline }} + {{ if not .Params.hide_page_headline }} + {{ $headline := .Params.headline | default .Title }} + {{ if $headline }} {{ if .Params.headline_as_h1 }}

{{ $headline }}

{{ else }}

{{ $headline }}

{{ end }} + {{ end }} {{ end }}
From 45fd752b1c69deb32ed771c2deb9be669abb1409 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Wed, 22 Apr 2026 19:35:40 +0200 Subject: [PATCH 15/47] Extract submodels. --- site/assets/js/modules/forms/phone-number.js | 54 ++ site/assets/js/modules/paygate/purchases.js | 56 ++ .../pages/{checkout.js => checkout-page.js} | 64 +-- site/assets/scss/main.scss | 3 + site/assets/scss/modules/_forms.scss | 255 +++++++++ site/assets/scss/modules/_message-modal.scss | 80 +++ site/assets/scss/modules/_result-panel.scss | 81 +++ site/assets/scss/pages/_checkout.scss | 505 ++---------------- site/content/checkout/index.md | 2 +- site/layouts/404.html | 26 +- .../_partials/components/result-panel.html | 31 ++ site/layouts/checkout-completed/single.html | 23 +- site/layouts/checkout/single.html | 130 ++--- 13 files changed, 698 insertions(+), 612 deletions(-) create mode 100644 site/assets/js/modules/forms/phone-number.js create mode 100644 site/assets/js/modules/paygate/purchases.js rename site/assets/js/pages/{checkout.js => checkout-page.js} (92%) create mode 100644 site/assets/scss/modules/_forms.scss create mode 100644 site/assets/scss/modules/_message-modal.scss create mode 100644 site/assets/scss/modules/_result-panel.scss create mode 100644 site/layouts/_partials/components/result-panel.html diff --git a/site/assets/js/modules/forms/phone-number.js b/site/assets/js/modules/forms/phone-number.js new file mode 100644 index 00000000..7e5604d2 --- /dev/null +++ b/site/assets/js/modules/forms/phone-number.js @@ -0,0 +1,54 @@ +/* + * Copyright 2026, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +'use strict'; + +export function isValidPhoneNumberInput(value) { + return /^[0-9\s()-]+$/.test(value); +} + +export function sanitizePhoneNumberInput(value) { + return String(value || '').replace(/[^0-9\s()-]/g, ''); +} + +export function normalizePhoneNumber(rawCountryCode, rawNumber) { + const countryCode = String(rawCountryCode || '').replace(/\D/g, ''); + const number = String(rawNumber || '').replace(/\D/g, ''); + + if (!countryCode || !number) { + return null; + } + + const numericCountryCode = Number(countryCode); + if (!Number.isInteger(numericCountryCode) || numericCountryCode <= 0) { + return null; + } + + return { + countryCode: numericCountryCode, + number + }; +} diff --git a/site/assets/js/modules/paygate/purchases.js b/site/assets/js/modules/paygate/purchases.js new file mode 100644 index 00000000..5647363a --- /dev/null +++ b/site/assets/js/modules/paygate/purchases.js @@ -0,0 +1,56 @@ +/* + * Copyright 2026, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +'use strict'; + +export function createPurchaseClient(serverUrl) { + const baseUrl = normalizeServerUrl(serverUrl); + + return { + placeOrder(productId, handlers) { + return postJson(`${baseUrl}/purchases/place-order`, {productId}, handlers); + }, + calculateCharges(payload, handlers) { + return postJson(`${baseUrl}/purchases/calculate-charges`, payload, handlers); + }, + submitBillingInfo(payload, handlers) { + return postJson(`${baseUrl}/purchases/submit-billing-info`, payload, handlers); + } + }; +} + +export function normalizeServerUrl(url) { + return String(url || '').replace(/\/+$/, ''); +} + +function postJson(url, payload, handlers) { + return $.ajax(url, { + type: 'POST', + data: JSON.stringify(payload), + contentType: 'application/json', + ...handlers + }); +} diff --git a/site/assets/js/pages/checkout.js b/site/assets/js/pages/checkout-page.js similarity index 92% rename from site/assets/js/pages/checkout.js rename to site/assets/js/pages/checkout-page.js index b2d41aab..4e874232 100644 --- a/site/assets/js/pages/checkout.js +++ b/site/assets/js/pages/checkout-page.js @@ -27,13 +27,15 @@ 'use strict'; import * as params from '@params'; +import {isValidPhoneNumberInput, normalizePhoneNumber, sanitizePhoneNumberInput} from '../modules/forms/phone-number'; +import {createPurchaseClient} from '../modules/paygate/purchases'; $( function () { const $form = $('#checkout-form'); const $summary = $('.checkout-summary'); const $country = $('#checkout-country'); - const $phone = $('.checkout-form__phone'); + const $phone = $('.phone-field'); const $phoneCountryCode = $('#checkout-phone-country-code'); const $phoneFlag = $('#checkout-phone-flag'); const $phoneDialCode = $('#checkout-phone-dial-code'); @@ -52,7 +54,7 @@ $( return; } - const serverUrl = normalizeServerUrl(params.paygate.serverurl); + const purchaseClient = createPurchaseClient(params.paygate.serverurl); const form = $form.get(0); const requiredSelector = 'input[required], select[required], textarea[required]'; const productId = getProductId(); @@ -183,10 +185,7 @@ $( const payload = buildSubmitBillingInfoRequest(); - $.ajax(`${serverUrl}/purchases/submit-billing-info`, { - type: 'POST', - data: JSON.stringify(payload), - contentType: 'application/json', + purchaseClient.submitBillingInfo(payload, { success: response => { const redirectUrl = response.paymentLink || response.redirectUrl || response.url || response.link; @@ -222,10 +221,7 @@ $( function placeOrder() { setSummaryLoading(true); - $.ajax(`${serverUrl}/purchases/place-order`, { - type: 'POST', - data: JSON.stringify({productId}), - contentType: 'application/json', + purchaseClient.placeOrder(productId, { success: response => { if (!response.product) { showNotFoundPage(); @@ -278,10 +274,7 @@ $( vatId: vatId || null }; - $.ajax(`${serverUrl}/purchases/calculate-charges`, { - type: 'POST', - data: JSON.stringify(payload), - contentType: 'application/json', + purchaseClient.calculateCharges(payload, { success: response => { if (requestId !== chargesRequestId) { return; @@ -337,7 +330,7 @@ $( return true; } - const fieldContainer = field.closest('.checkout-form__field'); + const fieldContainer = field.closest('.form-field'); if (!fieldContainer) { return true; } @@ -362,14 +355,6 @@ $( return !message; } - function isValidPhoneNumberInput(value) { - return /^[0-9\s()-]+$/.test(value); - } - - function sanitizePhoneNumberInput(value) { - return String(value || '').replace(/[^0-9\s()-]/g, ''); - } - function showVatIdError(reason) { const message = vatIdErrorMessage(reason); @@ -377,7 +362,13 @@ $( } function updateVatIdFieldState() { - validateField($vatId.get(0)); + const vatId = ($vatId.val() || '').trim(); + + if (vatId) { + validateField($vatId.get(0)); + } else { + setFieldError($vatId.get(0), ''); + } lastChargesRequestKey = ''; } @@ -386,7 +377,7 @@ $( return; } - const fieldContainer = field.closest('.checkout-form__field'); + const fieldContainer = field.closest('.form-field'); if (!fieldContainer) { return; @@ -650,29 +641,6 @@ $( return [line1, line2].map(value => (value || '').trim()).filter(Boolean).join(', '); } - function normalizePhoneNumber(rawCountryCode, rawNumber) { - const countryCode = String(rawCountryCode || '').replace(/\D/g, ''); - const number = String(rawNumber || '').replace(/\D/g, ''); - - if (!countryCode || !number) { - return null; - } - - const numericCountryCode = Number(countryCode); - if (!Number.isInteger(numericCountryCode) || numericCountryCode <= 0) { - return null; - } - - return { - countryCode: numericCountryCode, - number - }; - } - - function normalizeServerUrl(url) { - return String(url || '').replace(/\/+$/, ''); - } - function getProductId() { const pathProductId = getProductIdFromPath(); diff --git a/site/assets/scss/main.scss b/site/assets/scss/main.scss index c0725011..786df24f 100644 --- a/site/assets/scss/main.scss +++ b/site/assets/scss/main.scss @@ -42,6 +42,9 @@ @import "modules/checkbox"; @import "modules/redirect-screen"; @import "modules/loader"; +@import "modules/forms"; +@import "modules/result-panel"; +@import "modules/message-modal"; @import "pages/landing"; @import "pages/release-notes/release-notes"; diff --git a/site/assets/scss/modules/_forms.scss b/site/assets/scss/modules/_forms.scss new file mode 100644 index 00000000..89eee998 --- /dev/null +++ b/site/assets/scss/modules/_forms.scss @@ -0,0 +1,255 @@ +.form-section { + margin-top: 24px; + padding-top: 20px; + border-top: 1px solid rgba(black, .08); +} + +.form-title { + margin-bottom: 16px; + padding-top: 0; + font-size: 28px; + line-height: 1.15; +} + +.form-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 16px 20px; +} + +.form-field-full { + grid-column: 1 / -1; +} + +.form-label { + display: block; + margin-bottom: 6px; + color: $gray-500; + font-size: 14px; + font-weight: 600; + line-height: 1.3; +} + +.field-error { + .form-label { + color: #d93025; + } + + .form-input { + border-color: rgba(#d93025, .7); + box-shadow: 0 0 0 3px rgba(#d93025, .12); + } + + .error-message { + display: block; + } +} + +.required-label::after { + content: ' *'; + color: #d93025; + font-weight: 700; +} + +.form-input { + display: block; + width: 100%; + min-width: 0; + padding: 13px 15px; + color: $black; + font-size: 16px; + line-height: 1.3; + background-color: white; + border: 1px solid rgba(black, .12); + border-radius: $border-radius-m; + box-shadow: inset 0 1px 1px rgba(black, .02); + transition: border-color .2s ease-in-out, box-shadow .2s ease-in-out; + + &:focus { + outline: none; + border-color: rgba($main-brand-color, .55); + box-shadow: 0 0 0 3px rgba($main-brand-color, .12); + } +} + +.form-select { + appearance: none; + background-image: + linear-gradient(45deg, transparent 50%, rgba($gray-500, .9) 50%), + linear-gradient(135deg, rgba($gray-500, .9) 50%, transparent 50%); + background-position: + calc(100% - 18px) calc(50% - 2px), + calc(100% - 12px) calc(50% - 2px); + background-repeat: no-repeat; + background-size: 6px 6px, 6px 6px; + padding-right: 40px; +} + +.phone-field { + position: relative; + display: flex; + align-items: center; + min-height: 47px; + padding-left: 66px; + border: 1px solid rgba(black, .12); + border-radius: $border-radius-m; + background: white; + box-shadow: inset 0 1px 1px rgba(black, .02); + transition: border-color .2s ease-in-out, box-shadow .2s ease-in-out; + + &:focus-within { + border-color: rgba($main-brand-color, .55); + box-shadow: 0 0 0 3px rgba($main-brand-color, .12); + } + + @include breakpoint(sm-phone) { + min-height: 44px; + padding-left: 64px; + } +} + +.phone-field[data-phone-country-selected='false'] { + cursor: pointer; + + .phone-number { + cursor: pointer; + } + + .phone-flag, + .phone-chevron { + display: none; + } + + .phone-country-select { + width: 100%; + } +} + +.phone-field .phone-number { + flex: 1 1 auto; + min-width: 0; + padding: 13px 15px 13px 10px; + border: 0; + border-radius: 0; + box-shadow: none; + color: $black; + font-size: 16px; + letter-spacing: 0; + background: transparent; + + &:focus { + border-color: transparent; + box-shadow: none; + } +} + +.field-error .phone-field { + border-color: rgba(#d93025, .7); + box-shadow: 0 0 0 3px rgba(#d93025, .12); +} + +.field-error .phone-field .phone-number { + border-color: transparent; + box-shadow: none; +} + +.phone-country-select { + position: absolute; + top: 0; + bottom: 0; + left: 0; + z-index: 3; + width: 66px; + height: 100%; + opacity: 0; + cursor: pointer; +} + +.phone-flag { + position: absolute; + top: 50%; + left: 16px; + z-index: 1; + font-size: 19px; + line-height: 1; + transform: translateY(-50%); +} + +.phone-chevron { + position: absolute; + top: 50%; + left: 42px; + z-index: 1; + width: 9px; + height: 9px; + border-right: 2px solid rgba($gray-500, .9); + border-bottom: 2px solid rgba($gray-500, .9); + transform: translateY(-64%) rotate(45deg); + pointer-events: none; +} + +.phone-dial-code { + flex: 0 0 auto; + color: $black; + font-size: 16px; + font-weight: 400; + line-height: 1; +} + +.error-message { + display: none; + margin-top: 6px; + color: #d93025; + font-size: 13px; + line-height: 1.35; +} + +.form-actions { + margin-top: 20px; +} + +.form-submit-button { + min-width: 220px; + text-decoration: none; + + &:hover, + &:focus { + text-decoration: none; + } +} + +@include breakpoint(lg-phone) { + .form-title { + font-size: 24px; + } + + .form-grid { + grid-template-columns: 1fr; + gap: 14px; + } + + .form-field-full { + grid-column: auto; + } +} + +@include breakpoint(sm-phone) { + .form-input { + padding: 12px 14px; + font-size: 15px; + } + + .phone-flag { + left: 15px; + font-size: 18px; + } + + .phone-chevron { + left: 40px; + } + + .form-submit-button { + width: 100%; + min-width: 0; + } +} diff --git a/site/assets/scss/modules/_message-modal.scss b/site/assets/scss/modules/_message-modal.scss new file mode 100644 index 00000000..1c07c388 --- /dev/null +++ b/site/assets/scss/modules/_message-modal.scss @@ -0,0 +1,80 @@ +.message-modal { + position: fixed; + inset: 0; + z-index: map_get($z-index, 'redirect-screen'); + display: flex; + align-items: center; + justify-content: center; + padding: 24px; + + &[hidden] { + display: none; + } +} + +.message-modal-backdrop { + position: absolute; + inset: 0; + background: rgba(black, .45); +} + +.message-modal-dialog { + position: relative; + z-index: 1; + width: 100%; + max-width: 520px; + padding: 30px 34px; + background: white; + border-radius: $border-radius-m; + box-shadow: 0 22px 60px rgba(black, .18); + overflow: hidden; +} + +.message-modal-close { + position: absolute; + top: 10px; + right: 12px; + padding: 4px 8px; + color: $gray-500; + font-size: 28px; + line-height: 1; + background: transparent; + border: 0; + cursor: pointer; +} + +.message-modal-title { + margin: 0 28px 12px 0; + padding: 0; + color: $black; + font-size: 24px; + line-height: 1.2; +} + +.message-modal-text { + margin: 0; + color: $gray-500; + font-size: 16px; + line-height: 1.55; + + a { + color: $main-brand-color; + font-weight: 700; + text-decoration: none; + + &:hover, + &:focus { + text-decoration: underline; + } + } +} + +@include breakpoint(sm-phone) { + .message-modal { + padding: 16px; + } + + .message-modal-dialog { + padding: 24px; + } +} diff --git a/site/assets/scss/modules/_result-panel.scss b/site/assets/scss/modules/_result-panel.scss new file mode 100644 index 00000000..df47685d --- /dev/null +++ b/site/assets/scss/modules/_result-panel.scss @@ -0,0 +1,81 @@ +.result-panel { + max-width: 620px; + margin: 0 auto; + padding: 20px 0 30px; + text-align: center; +} + +.result-panel-mark { + position: relative; + width: 64px; + height: 64px; + margin: 0 auto 9px; + background: rgba($main-brand-color, .1); + border: 1px solid rgba($main-brand-color, .18); + border-radius: 50%; +} + +.result-panel-mark-success::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 18px; + height: 30px; + border-right: 4px solid $main-brand-color; + border-bottom: 4px solid $main-brand-color; + transform: translate(-50%, -58%) rotate(42deg); + transform-origin: center; +} + +.result-panel-mark-code { + display: flex; + align-items: center; + justify-content: center; + color: $main-brand-color; + font-size: 17px; + font-weight: 800; + letter-spacing: 0; +} + +.result-panel-title { + margin: 0 0 14px; + color: $black; + font-size: 36px; + font-weight: 800; + line-height: 1.15; +} + +.result-panel-text { + max-width: 520px; + margin: 0 auto 30px; + color: $gray-500; + font-size: 17px; + line-height: 1.55; + text-align: center; + + span { + display: block; + text-align: center; + } +} + +@include breakpoint(sm-phone) { + .result-panel { + padding: 14px 0 16px; + } + + .result-panel-mark { + width: 58px; + height: 58px; + margin-bottom: 8px; + } + + .result-panel-title { + font-size: 28px; + } + + .result-panel-text { + font-size: 16px; + } +} diff --git a/site/assets/scss/pages/_checkout.scss b/site/assets/scss/pages/_checkout.scss index c80b594e..24b88c16 100644 --- a/site/assets/scss/pages/_checkout.scss +++ b/site/assets/scss/pages/_checkout.scss @@ -67,19 +67,20 @@ .checkout-summary { max-width: 100%; min-width: 0; + margin-bottom: 18px; &[data-loading='true'] { - .checkout-summary__row { + .checkout-summary-row { opacity: .72; } } - .checkout-summary__details { + .checkout-summary-details { border-top: none; padding-top: 8px; } - .checkout-summary__loading { + .checkout-summary-loading { margin: 2px 0 12px; color: $gray-500; font-size: 15px; @@ -87,7 +88,7 @@ line-height: 1.4; } - .checkout-summary__row { + .checkout-summary-row { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: end; @@ -104,25 +105,29 @@ } } - .checkout-summary__row--total { + .checkout-summary-row-total { margin-top: 1px; padding-top: 10px; - .checkout-summary__label { + .checkout-summary-label { color: $black; font-size: 22px; font-weight: 800; letter-spacing: -.02em; } - .checkout-summary__value { + .checkout-summary-value { font-size: 22px; font-weight: 800; letter-spacing: -.02em; } + + .checkout-summary-dots { + margin-bottom: .18em; + } } - .checkout-summary__product { + .checkout-summary-product { max-width: 100%; padding: 2px 0 8px; color: $main-brand-color; @@ -137,7 +142,7 @@ } } - .checkout-summary__product-description { + .checkout-summary-product-description { margin: -2px 0 10px; color: $gray-500; font-size: 15px; @@ -149,7 +154,7 @@ } } - .checkout-summary__label { + .checkout-summary-label { flex: 0 0 auto; min-width: 0; color: $gray-500; @@ -158,7 +163,7 @@ line-height: 1.2; } - .checkout-summary__dots { + .checkout-summary-dots { min-width: 20px; height: 1px; margin-bottom: .22em; @@ -168,7 +173,7 @@ background-size: 8px 2px; } - .checkout-summary__value { + .checkout-summary-value { min-width: 0; max-width: 100%; color: $black; @@ -180,14 +185,14 @@ overflow-wrap: anywhere; } - .checkout-summary__value--amount { + .checkout-summary-value-amount { color: $black; font-size: 17px; font-weight: 700; line-height: 1.2; } - .checkout-summary__value--total { + .checkout-summary-value-total { color: $black; font-size: 22px; font-weight: 800; @@ -195,515 +200,75 @@ line-height: 1.1; } - .checkout-summary__row--total { - .checkout-summary__dots { - margin-bottom: .18em; - } - } - @include breakpoint(lg-phone) { margin-bottom: 16px; - .checkout-summary__product { + .checkout-summary-product { font-size: 24px; padding-bottom: 6px; } - .checkout-summary__row { + .checkout-summary-row { display: block; padding: 8px 0; } - .checkout-summary__label { + .checkout-summary-label { display: block; min-width: 0; margin-bottom: 2px; } - .checkout-summary__dots { + .checkout-summary-dots { display: none; } - .checkout-summary__value { + .checkout-summary-value { display: block; text-align: left; } } @include breakpoint(md-phone) { - .checkout-summary__label { + .checkout-summary-label { font-size: 14px; } - .checkout-summary__value--amount { + .checkout-summary-value-amount { font-size: 16px; } - .checkout-summary__row--total .checkout-summary__label, - .checkout-summary__value--total { + .checkout-summary-row-total .checkout-summary-label, + .checkout-summary-value-total { font-size: 20px; } } @include breakpoint(sm-phone) { - .checkout-summary__product { + .checkout-summary-product { font-size: 22px; } - .checkout-summary__row { + .checkout-summary-row { padding: 8px 0; } - .checkout-summary__value--amount { + .checkout-summary-value-amount { font-size: 15px; } - .checkout-summary__row--total .checkout-summary__label, - .checkout-summary__value--total { + .checkout-summary-row-total .checkout-summary-label, + .checkout-summary-value-total { font-size: 18px; } } - - margin-bottom: 18px; -} - -.checkout-form { - margin-top: 24px; - padding-top: 20px; - border-top: 1px solid rgba(black, .08); - - .checkout-form__title { - margin-bottom: 16px; - padding-top: 0; - font-size: 28px; - line-height: 1.15; - } - - .checkout-form__grid { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 16px 20px; - } - - .checkout-form__field--full { - grid-column: 1 / -1; - } - - .field-error { - .checkout-form__label { - color: #d93025; - } - } - - .checkout-form__label { - display: block; - margin-bottom: 6px; - color: $gray-500; - font-size: 14px; - font-weight: 600; - line-height: 1.3; - } - - .required-label::after { - content: ' *'; - color: #d93025; - font-weight: 700; - } - - .checkout-form__input { - display: block; - width: 100%; - min-width: 0; - padding: 13px 15px; - color: $black; - font-size: 16px; - line-height: 1.3; - background-color: white; - border: 1px solid rgba(black, .12); - border-radius: $border-radius-m; - box-shadow: inset 0 1px 1px rgba(black, .02); - transition: border-color .2s ease-in-out, box-shadow .2s ease-in-out; - - &:focus { - outline: none; - border-color: rgba($main-brand-color, .55); - box-shadow: 0 0 0 3px rgba($main-brand-color, .12); - } - } - - .field-error .checkout-form__input { - border-color: rgba(#d93025, .7); - box-shadow: 0 0 0 3px rgba(#d93025, .12); - } - - .checkout-form__phone { - position: relative; - display: flex; - align-items: center; - min-height: 47px; - padding-left: 66px; - border: 1px solid rgba(black, .12); - border-radius: $border-radius-m; - background: white; - box-shadow: inset 0 1px 1px rgba(black, .02); - transition: border-color .2s ease-in-out, box-shadow .2s ease-in-out; - - &:focus-within { - border-color: rgba($main-brand-color, .55); - box-shadow: 0 0 0 3px rgba($main-brand-color, .12); - } - - @include breakpoint(sm-phone) { - min-height: 44px; - padding-left: 64px; - } - } - - .checkout-form__phone[data-phone-country-selected='false'] { - cursor: pointer; - - #checkout-phone { - cursor: pointer; - } - - .checkout-form__phone-flag, - .checkout-form__phone-chevron { - display: none; - } - - .checkout-form__phone-code { - width: 100%; - } - } - - .checkout-form__phone #checkout-phone { - flex: 1 1 auto; - min-width: 0; - padding: 13px 15px 13px 10px; - border: 0; - border-radius: 0; - box-shadow: none; - color: $black; - font-size: 16px; - letter-spacing: 0; - background: transparent; - - &:focus { - border-color: transparent; - box-shadow: none; - } - } - - .field-error .checkout-form__phone { - border-color: rgba(#d93025, .7); - box-shadow: 0 0 0 3px rgba(#d93025, .12); - } - - .field-error .checkout-form__phone #checkout-phone { - border-color: transparent; - box-shadow: none; - } - - .checkout-form__phone-code { - position: absolute; - top: 0; - bottom: 0; - left: 0; - z-index: 3; - width: 66px; - height: 100%; - opacity: 0; - cursor: pointer; - } - - .checkout-form__phone-flag { - position: absolute; - top: 50%; - left: 16px; - z-index: 1; - font-size: 19px; - line-height: 1; - transform: translateY(-50%); - } - - .checkout-form__phone-chevron { - position: absolute; - top: 50%; - left: 42px; - z-index: 1; - width: 9px; - height: 9px; - border-right: 2px solid rgba($gray-500, .9); - border-bottom: 2px solid rgba($gray-500, .9); - transform: translateY(-64%) rotate(45deg); - pointer-events: none; - } - - .checkout-form__phone-dial-code { - flex: 0 0 auto; - color: $black; - font-size: 16px; - font-weight: 400; - line-height: 1; - } - - @include breakpoint(sm-phone) { - .checkout-form__phone-flag { - left: 15px; - font-size: 18px; - } - - .checkout-form__phone-chevron { - left: 40px; - } - } - - .error-message { - display: none; - margin-top: 6px; - color: #d93025; - font-size: 13px; - line-height: 1.35; - } - - .field-error .error-message { - display: block; - } - - .checkout-form__select { - appearance: none; - background-image: - linear-gradient(45deg, transparent 50%, rgba($gray-500, .9) 50%), - linear-gradient(135deg, rgba($gray-500, .9) 50%, transparent 50%); - background-position: - calc(100% - 18px) calc(50% - 2px), - calc(100% - 12px) calc(50% - 2px); - background-repeat: no-repeat; - background-size: 6px 6px, 6px 6px; - padding-right: 40px; - } - - .checkout-form__actions { - margin-top: 20px; - } - - .checkout-form__submit { - min-width: 220px; - } - - @include breakpoint(lg-phone) { - .checkout-form__title { - font-size: 24px; - } - - .checkout-form__grid { - grid-template-columns: 1fr; - gap: 14px; - } - - .checkout-form__field--full { - grid-column: auto; - } - } - - @include breakpoint(sm-phone) { - .checkout-form__input { - padding: 12px 14px; - font-size: 15px; - } - - .checkout-form__submit { - width: 100%; - min-width: 0; - } - } } -.checkout-completed { +.checkout-completed, +.checkout-not-found { min-height: 420px; - .checkout-completed__panel { - max-width: 620px; - margin: 0 auto; - padding: 20px 0 30px; - text-align: center; - } - - .checkout-completed__mark { - position: relative; - width: 64px; - height: 64px; - margin: 0 auto 9px; - background: rgba($main-brand-color, .1); - border: 1px solid rgba($main-brand-color, .18); - border-radius: 50%; - - &::after { - content: ''; - position: absolute; - top: 50%; - left: 50%; - width: 18px; - height: 30px; - border-right: 4px solid $main-brand-color; - border-bottom: 4px solid $main-brand-color; - transform: translate(-50%, -58%) rotate(42deg); - transform-origin: center; - } - } - - .checkout-completed__title { - margin: 0 0 14px; - color: $black; - font-size: 36px; - font-weight: 800; - line-height: 1.15; - } - - .checkout-completed__text { - max-width: 520px; - margin: 0 auto 30px; - color: $gray-500; - font-size: 17px; - line-height: 1.55; - text-align: center; - - span { - display: block; - text-align: center; - } - } - - .checkout-completed__link { - min-width: 220px; - text-decoration: none; - - &:hover, - &:focus { - text-decoration: none; - } - } - @include breakpoint(sm-phone) { min-height: auto; - - .checkout-completed__panel { - padding: 14px 0 16px; - } - - .checkout-completed__mark { - width: 58px; - height: 58px; - margin-bottom: 8px; - } - - .checkout-completed__title { - font-size: 28px; - } - - .checkout-completed__text { - font-size: 16px; - } - - .checkout-completed__link { - width: 100%; - min-width: 0; - } - } -} - -.checkout-not-found { - .checkout-not-found__mark { - display: flex; - align-items: center; - justify-content: center; - color: $main-brand-color; - font-size: 17px; - font-weight: 800; - letter-spacing: 0; - - &::after { - display: none; - } - } -} - -.checkout-modal { - position: fixed; - inset: 0; - z-index: map_get($z-index, 'redirect-screen'); - display: flex; - align-items: center; - justify-content: center; - padding: 24px; - - &[hidden] { - display: none; - } - - .checkout-modal__backdrop { - position: absolute; - inset: 0; - background: rgba(black, .45); - } - - .checkout-modal__dialog { - position: relative; - z-index: 1; - width: 100%; - max-width: 520px; - padding: 30px 34px; - background: white; - border-radius: $border-radius-m; - box-shadow: 0 22px 60px rgba(black, .18); - overflow: hidden; - } - - .checkout-modal__close { - position: absolute; - top: 10px; - right: 12px; - padding: 4px 8px; - color: $gray-500; - font-size: 28px; - line-height: 1; - background: transparent; - border: 0; - cursor: pointer; - } - - .checkout-modal__title { - margin: 0 28px 12px 0; - padding: 0; - color: $black; - font-size: 24px; - line-height: 1.2; - } - - .checkout-modal__text { - margin: 0; - color: $gray-500; - font-size: 16px; - line-height: 1.55; - - a { - color: $main-brand-color; - font-weight: 700; - text-decoration: none; - - &:hover, - &:focus { - text-decoration: underline; - } - } - } - - @include breakpoint(sm-phone) { - padding: 16px; - - .checkout-modal__dialog { - padding: 24px; - } - } } diff --git a/site/content/checkout/index.md b/site/content/checkout/index.md index e304ea1a..15a6e503 100644 --- a/site/content/checkout/index.md +++ b/site/content/checkout/index.md @@ -3,5 +3,5 @@ title: Checkout headline: Checkout description: Checkout body_class: checkout-page -customjs: js/pages/checkout.js +customjs: js/pages/checkout-page.js --- diff --git a/site/layouts/404.html b/site/layouts/404.html index e05f7c32..24a08d1d 100644 --- a/site/layouts/404.html +++ b/site/layouts/404.html @@ -27,20 +27,18 @@
-
- -

Page not found

-

- The page you are looking for could not be found. - Please check the address or return to the home page. -

-
- -
-
+ {{ partial "components/result-panel.html" (dict + "title" "Page not found" + "titleTag" "h1" + "markText" "404" + "markClass" "result-panel-mark-code" + "lines" (slice + "The page you are looking for could not be found." + "Please check the address or return to the home page." + ) + "actionLabel" "Back to home" + "actionUrl" "/" + ) }}
diff --git a/site/layouts/_partials/components/result-panel.html b/site/layouts/_partials/components/result-panel.html new file mode 100644 index 00000000..36f8aa97 --- /dev/null +++ b/site/layouts/_partials/components/result-panel.html @@ -0,0 +1,31 @@ +{{ $title := .title }} +{{ $titleTag := .titleTag | default "h2" }} +{{ $markText := .markText | default "" }} +{{ $markClass := .markClass | default "result-panel-mark-success" }} +{{ $lines := .lines | default slice }} +{{ $actionLabel := .actionLabel | default "" }} +{{ $actionUrl := .actionUrl | default "/" }} + +
+ + {{ if eq $titleTag "h1" }} +

{{ $title }}

+ {{ else }} +

{{ $title }}

+ {{ end }} + {{ if $lines }} +

+ {{ range $lines }} + {{ . }} + {{ end }} +

+ {{ end }} + {{ if $actionLabel }} +
+ +
+ {{ end }} +
diff --git a/site/layouts/checkout-completed/single.html b/site/layouts/checkout-completed/single.html index 895962bc..067c759b 100644 --- a/site/layouts/checkout-completed/single.html +++ b/site/layouts/checkout-completed/single.html @@ -6,20 +6,15 @@
-
- -

Thank you for your purchase

-

- Your payment was completed successfully. - We will send the order details to your email. -

-
- -
-
+ {{ partial "components/result-panel.html" (dict + "title" "Thank you for your purchase" + "lines" (slice + "Your payment was completed successfully." + "We will send the order details to your email." + ) + "actionLabel" "Back to home" + "actionUrl" "/" + ) }} {{ .Content }}
diff --git a/site/layouts/checkout/single.html b/site/layouts/checkout/single.html index d0f5909c..1ff48260 100644 --- a/site/layouts/checkout/single.html +++ b/site/layouts/checkout/single.html @@ -9,37 +9,37 @@
-
-
Loading checkout details...
- - -
- Subtotal - - +
Loading checkout details...
+ + +
+ Subtotal + + ...
-
- + VAT - - + ...
-
- Total - - + Total + + ...
-
-

Billing details

-
-
- - +

Billing details

+
+
+ + Billing details inputmode="email" required>
-
- -
- Billing details - - - - + + + Billing details disabled>
-
- - + +
-
- - + +
-
- - + +
-
- - + +
-
- - Billing details
-
- - + +
-
- - + +
-
- - + +
-
- - + +
-
-
-