generated from boboko/starter
price function global (needs review)
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Support;
|
||||
|
||||
class Price
|
||||
{
|
||||
/**
|
||||
* Format a price the Greek way: comma as the decimal separator, dot as
|
||||
* the thousands separator, and no decimals shown unless the amount
|
||||
* actually needs them (19.00 -> "19", 19.50 -> "19,5", 19.55 -> "19,55").
|
||||
*/
|
||||
public static function format(float|int|string|null $amount, string $currency = '€'): ?string
|
||||
{
|
||||
if ($amount === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$amount = round((float) $amount, 2);
|
||||
$cents = (int) round($amount * 100);
|
||||
|
||||
$decimals = match (true) {
|
||||
$cents % 100 === 0 => 0,
|
||||
$cents % 10 === 0 => 1,
|
||||
default => 2,
|
||||
};
|
||||
|
||||
return $currency.number_format($amount, $decimals, ',', '.');
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Controller } from '@hotwired/stimulus'
|
||||
import { formatPrice } from '../utils/format-price'
|
||||
|
||||
export default class extends Controller {
|
||||
static targets = ['price', 'image', 'swatch', 'colorName']
|
||||
@@ -30,7 +31,7 @@ export default class extends Controller {
|
||||
if (!variant) return
|
||||
|
||||
if (this.hasPriceTarget && variant.price !== null) {
|
||||
this.priceTarget.textContent = '€' + parseFloat(variant.price).toFixed(2)
|
||||
this.priceTarget.textContent = formatPrice(variant.price)
|
||||
}
|
||||
|
||||
if (this.hasImageTarget && variant.image) {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// Mirrors App\Support\Price::format() — keep both in sync. Greek formatting:
|
||||
// comma as the decimal separator, dot as the thousands separator, and no
|
||||
// decimals shown unless the amount actually needs them.
|
||||
// 19.00 -> "€19", 19.50 -> "€19,5", 19.55 -> "€19,55"
|
||||
export function formatPrice(amount, currency = '€') {
|
||||
if (amount === null || amount === undefined) return null
|
||||
|
||||
const value = Math.round(parseFloat(amount) * 100) / 100
|
||||
const cents = Math.round(value * 100)
|
||||
|
||||
const decimals = cents % 100 === 0 ? 0 : (cents % 10 === 0 ? 1 : 2)
|
||||
|
||||
const formatted = value.toLocaleString('el-GR', {
|
||||
minimumFractionDigits: decimals,
|
||||
maximumFractionDigits: decimals,
|
||||
})
|
||||
|
||||
return currency + formatted
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
@props([
|
||||
'amount' => null,
|
||||
'currency' => '€',
|
||||
])
|
||||
|
||||
{{-- No wrapper element on purpose — drop this inside whatever tag/classes
|
||||
the call site already uses (span, p, etc). Formatting logic lives in
|
||||
App\Support\Price so it's identical everywhere it's used. --}}
|
||||
{{ \App\Support\Price::format($amount, $currency) }}
|
||||
@@ -28,7 +28,7 @@ class="w-full h-auto block"
|
||||
<div class="flex items-baseline justify-between gap-4">
|
||||
<a href="{{ $href }}" class="font-bold text-xl leading-snug hover:text-brand">{{ $name }}</a>
|
||||
@if($price !== null)
|
||||
<span class="font-bold text-xl shrink-0">$ {{ $price }}</span>
|
||||
<span class="font-bold text-xl shrink-0"><x-ui.price :amount="$price" /></span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -33,7 +33,7 @@ class="font-display font-semibold text-6xl lg:text-7xl leading-tight"
|
||||
aria-label="Προηγούμενο προϊόν"
|
||||
class="absolute left-4 lg:left-8 top-1/2 -translate-y-1/2 hover:opacity-60 transition-opacity"
|
||||
>
|
||||
<x-ui.icon name="arrow-left" :size="56" />
|
||||
<x-ui.icon name="arrow-left" :size="62" class="[&_path]:stroke-2" />
|
||||
</button>
|
||||
|
||||
<div class="w-full ">
|
||||
@@ -63,7 +63,7 @@ class="w-full h-full object-cover"
|
||||
<div class="flex items-baseline justify-between gap-4">
|
||||
<span class="font-display font-bold text-2xl">{{ $product['name'] }}</span>
|
||||
@if($product['price'] !== null)
|
||||
<span class="font-bold text-xl shrink-0">€{{ number_format($product['price'], 2) }}</span>
|
||||
<span class="font-bold text-xl shrink-0"><x-ui.price :amount="$product['price']" /></span>
|
||||
@endif
|
||||
</div>
|
||||
</a>
|
||||
@@ -76,7 +76,7 @@ class="w-full h-full object-cover"
|
||||
aria-label="Επόμενο προϊόν"
|
||||
class="absolute right-4 lg:right-8 top-1/2 -translate-y-1/2 hover:opacity-60 transition-opacity"
|
||||
>
|
||||
<x-ui.icon name="arrow-right" :size="56" />
|
||||
<x-ui.icon name="arrow-right" :size="62" class="[&_path]:stroke-2"/>
|
||||
</button>
|
||||
@else
|
||||
<p class="text-neutral-500">Δεν βρέθηκαν προϊόντα.</p>
|
||||
|
||||
@@ -152,7 +152,7 @@ class="absolute bottom-6 right-8 text-white text-sm"
|
||||
|
||||
@if($product->variants->first()?->prices->isNotEmpty())
|
||||
<p class="text-2xl font-bold" data-product-form-target="price">
|
||||
€{{ number_format($product->variants->first()->prices->first()->price->decimal, 2) }}
|
||||
<x-ui.price :amount="$product->variants->first()->prices->first()->price->decimal" />
|
||||
</p>
|
||||
@endif
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ class="absolute bottom-6 right-8 text-white text-sm"
|
||||
|
||||
@if($product->variants->first()?->prices->isNotEmpty())
|
||||
<p class="text-2xl font-bold" data-product-form-target="price">
|
||||
€{{ number_format($product->variants->first()->prices->first()->price->decimal, 2) }}
|
||||
<x-ui.price :amount="$product->variants->first()->prices->first()->price->decimal" />
|
||||
</p>
|
||||
@endif
|
||||
|
||||
|
||||
Reference in New Issue
Block a user