Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 8 additions & 14 deletions docs/tags.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,6 @@
# Tags

<div class="column-nav">

- [currency](#currency)
- [customer:orders](#customer-orders)
- [customer:order](#customer-order)
- [id](#id)
- [product:single](#product-single)
- [product:configurable](#product-configurable)
- [product:price](#product-price)
- [product:options](#product-options)
- [store:countries](#store-countries)
- [wishlist:contains](#test)

</div>
[TOC]


## Introduction
Expand Down Expand Up @@ -55,6 +42,13 @@ Attach it to a variable to make it reusable:
</script>
```

## siteCurrency
Returns the current currency that is being used by the site. This will either be the `default_currency` defined in `gaia.php` or the `currency` attribute set on the site if you're on a multisite setup.

```twig
{{ gaia:site_currency }}
```


## product:single
Returns `true` if the current product is a single product that has no variants, or `false` if it does.
Expand Down
8 changes: 6 additions & 2 deletions js/dist/gaia.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,12 @@ window.gaia = {
});
},

formatPrice: (price, currency) => {
return price.toLocaleString("en-GB", { style: "currency", currency: "GBP" });
formatPrice: (price, locale, currency) => {
// Since the code comes from Statamic it should be in POSIX format, we
// will attempt to convert to a valid BCP 47 locale.
locale = locale.replace('_', '-');

return price.toLocaleString(locale, { style: "currency", currency: currency });
},

getCSRF: () => {
Expand Down
11 changes: 11 additions & 0 deletions src/Tags/Gaia.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,17 @@ public function id()
return Str::lower(Str::random(10));
}

/**
* Retrieves the current site's currency.
*
* @return string The currency code for the current site.
* If not set, returns the default currency from configuration.
*/
public function siteCurrency(): string
{
return Site::current()->attribute('currency') ?? config('gaia.default_currency');
}

public function livewireStart(): string
{
return <<<'JS'
Expand Down
78 changes: 58 additions & 20 deletions stubs/resources/views/shop/product/default/_price.antlers.html
Original file line number Diff line number Diff line change
@@ -1,23 +1,61 @@
<div class="flex flex-col">
{{ gaia:product:price }}
{{ if {gaia:product:single} }}
{{ if is_discounted }}
<span class="block mt-2 text-3xl font-medium text-red-600">Now {{ gaia:currency :price="min_discounted_price" }}</span>
<div class="flex items-center mt-2">
<span class="text-sm text-slate-500">Was {{ gaia:currency :price="min_regular_price" }}</span>
{{ if (discount_percentage <= '5') }}
<span class="p-1 ml-2 text-sm text-red-600 rounded-xs">(-{{ gaia:currency :price="discount_amount" }})</span>
{{ else }}
<span class="p-1 ml-2 text-sm font-medium text-red-600 rounded-xs">(-{{ discount_percentage | floor }}%)</span>
{{ /if }}
</div>
{{ /else }}
<span class="block mt-2 text-3xl" >{{ gaia:currency :price="min_price" }}</span>
<div class="flex flex-col" x-data="productPrice()">
<span x-show="! show">
{{ gaia:product:price }}
{{ if {gaia:product:single} }}
{{ if is_discounted }}
<span class="block mt-2 text-3xl font-medium text-red-600">Now {{ gaia:currency :price="min_discounted_price" }}</span>
<div class="flex items-center mt-2">
<span class="text-sm text-slate-500">Was {{ gaia:currency :price="min_regular_price" }}</span>
{{ if (discount_percentage <= '5') }}
<span class="p-1 ml-2 text-sm text-red-600 rounded-xs">(-{{ gaia:currency :price="discount_amount" }})</span>
{{ else }}
<span class="p-1 ml-2 text-sm font-medium text-red-600 rounded-xs">(-{{ discount_percentage | floor }}%)</span>
{{ /if }}
</div>
{{ /else }}
<span class="block mt-2 text-3xl" >{{ gaia:currency :price="min_price" }}</span>
{{ /if }}
{{ /if }}

{{ if {gaia:product:configurable} }}
<span class="block mt-2 text-3xl">From {{ gaia:currency :price="min_price" }}</span>
{{ /if }}
{{ /if }}
{{ /gaia:product:price }}
</span>

{{ if {gaia:product:configurable} }}
<span class="block mt-2 text-3xl">From {{ gaia:currency :price="min_price" }}</span>
{{ /if }}
{{ /gaia:product:price }}
{{ if {gaia:product:configurable} }}
<span x-show="show" x-cloak>
<template x-if="$store.product.priceTable.isDiscounted">
<div>
<span class="block mt-2 text-3xl font-medium text-red-600">Now <span x-text="$store.product.priceTable.formattedMinPrice"></span></span>
<div class="flex items-center mt-2">
<span class="text-sm text-slate-500">Was <span x-text="$store.product.priceTable.formattedMaxPrice"></span></span>
<span class="p-1 ml-2 text-sm font-medium text-red-600 rounded-xs">
(-<span x-text="$store.product.priceTable.discountPercentage"></span>%)
</span>
</div>
</div>
</template>

<template x-if="(! $store.product.priceTable.isDiscounted) && ($store.product.areAllOptionsSelected)">
<span class="block mt-2 text-3xl" x-text="$store.product.priceTable.formattedMinPrice"></span>
</template>

<span x-show="! $store.product.areAllOptionsSelected" class="block mt-2 text-3xl">From <span x-text="$store.product.priceTable.formattedMinPrice"></span>
</span>
{{ /if }}
</div>

<script>
function productPrice() {
return {
show: false,
init() {
// Once the product options are changed then show the new prices
this.$watch('$store.product.selectedOptions', value => {
this.show = true;
});
}
}
}
</script>
100 changes: 80 additions & 20 deletions stubs/resources/views/shop/product/default/index.antlers.html
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ <h1 class="mb-4 text-4xl font-medium leading-tight tracking-tighter lg:text-5xl"
<script type="module">
Alpine.store('product', {
title: '{{ title }}',
price: '{{ gaia:product:price }}{{min_price}}{{ /gaia:product:price}}',
variants: JSON.parse(` {{ { collection:variants :product_slug:is="slug" :site="site" } | pluck_with_keys('option1', 'option2', 'option3', 'inventory_quantity', 'slug', 'price', 'image') | to_json }} `),
currentVariants: JSON.parse(` {{ { collection:variants :product_slug:is="slug" :site="site" } | pluck_with_keys('option1', 'option2', 'option3', 'inventory_quantity', 'slug', 'price', 'image') | to_json }} `),
priceTable: {},
variants: JSON.parse(` {{ { collection:variants :product_slug:is="slug" :site="site" } | pluck_with_keys('option1', 'option2', 'option3', 'inventory_quantity', 'slug', 'price', 'compare_at_price', 'image') | to_json }} `),
currentVariants: JSON.parse(` {{ { collection:variants :product_slug:is="slug" :site="site" } | pluck_with_keys('option1', 'option2', 'option3', 'inventory_quantity', 'slug', 'price', 'compare_at_price', 'image') | to_json }} `),
currentProduct: null,
selectedOptions: null,
stockStatus: 'inStock',
Expand Down Expand Up @@ -91,39 +91,95 @@ <h1 class="mb-4 text-4xl font-medium leading-tight tracking-tighter lg:text-5xl"
...variant,
isSaleable: variant.inventory_quantity > 0,
inStock: variant.inventory_quantity > 0,
formattedPrice: gaia.formatPrice(variant.price),
productId: '{{ product_id }}'
}
},

updateDisplayedPrice() {
if (this.variants.length === 1) {
this.price = this.currentProduct.formattedPrice;
return;
const formatPrice = (price) => {
if (price === null || price === undefined) {
return false;
}

return gaia.formatPrice(parseFloat(price), '{{ site:locale }}', '{{ gaia:site_currency }}');
}

function calculateMinMaxPrices(variants) {
return variants.reduce((acc, variant) => {
const calculateMinMaxPrices = () => {
return this.currentVariants.reduce((acc, variant) => {
return {
smallestPrice: Math.min(acc.smallestPrice, variant.price),
largestPrice: Math.max(acc.largestPrice, variant.price)
smallestPrice: Math.min(acc.smallestPrice, variant.price),
largestPrice: Math.max(acc.largestPrice, variant.price)
};
}, { smallestPrice: Infinity, largestPrice: -Infinity });
}

const { smallestPrice, largestPrice } = calculateMinMaxPrices(this.currentVariants);
const minPrice = () => {
if (this.areAllOptionsSelected) {
return this.currentProduct.compare_at_price || this.currentProduct.price;
}

if (smallestPrice === Infinity || largestPrice === Infinity) {
let { smallestPrice, largestPrice } = calculateMinMaxPrices(this.variants);
this.price = `${gaia.formatPrice(smallestPrice)} - ${gaia.formatPrice(largestPrice)}`;
return;
const { smallestPrice, largestPrice } = calculateMinMaxPrices();

return smallestPrice;
}

if (smallestPrice === largestPrice) {
this.price = gaia.formatPrice(largestPrice);
return;
const maxPrice = () => {
if (this.areAllOptionsSelected) {
return this.currentProduct.price;
}

const { smallestPrice, largestPrice } = calculateMinMaxPrices();

return largestPrice;
}

this.price = `${gaia.formatPrice(smallestPrice)} - ${gaia.formatPrice(largestPrice)}`;
const compareAtPrice = () => {
// If the product doesn't exist yet, return false
if (! this.currentProduct) {
return false;
}

return this.currentProduct.compare_at_price
}

const discountAmount = () => {
// If the product doesn't exist yet, return false
if (! this.currentProduct) {
return false;
}

if (! this.currentProduct.compare_at_price) {
return false;
}

return this.currentProduct.price - this.currentProduct.compare_at_price;
}

const discountPercentage = () => {
// If the product doesn't exist yet, return false
if (! this.currentProduct) {
return false;
}

// If the product doesn't have a compare at price, it's
// not discounted so return false
if (! this.currentProduct.compare_at_price) {
return false;
}

return Math.floor((discountAmount() / this.currentProduct.price) * 100);
}

this.priceTable = {
minPrice: minPrice(),
formattedMinPrice: formatPrice(minPrice()),
maxPrice: maxPrice(),
formattedMaxPrice: formatPrice(maxPrice()),
isDiscounted: discountAmount() ? true : false,
discountPercentage: discountPercentage(),
discountAmount: discountAmount(),
formattedDiscountAmount: formatPrice(discountAmount()),
}
},
updateCurrentVariants() {
if (!this.selectedOptions) {
Expand Down Expand Up @@ -197,6 +253,10 @@ <h1 class="mb-4 text-4xl font-medium leading-tight tracking-tighter lg:text-5xl"
}
},

get areAllOptionsSelected() {
return this.selectedOptions && Object.keys(this.selectedOptions).length === 3;
},

// Product form
loading: false,
disabled: false,
Expand Down