Private
Public Access
1
0

Add all files

This commit is contained in:
root
2025-11-30 11:57:22 +01:00
parent 2ee51cac4f
commit 044271db3d
1476 changed files with 142544 additions and 0 deletions

View File

@@ -0,0 +1,265 @@
<div class="mx-auto container mt-8 rounded-2xl">
<div class="flex flex-col gap-6 w-full col-span-3 md:col-span-1 sticky top-8 md:top-20">
<div class="flex flex-col gap-6 w-full col-span-3">
<div
class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 border border-neutral/50 rounded-2xl shadow-lg overflow-hidden">
<div class="p-6 lg:p-8">
<div class="flex items-start justify-between mb-6">
<div class="flex items-center gap-2">
<div class="size-2 bg-green-500 rounded-full animate-pulse"></div>
<span class="text-xs font-medium text-green-600 uppercase tracking-wide">Available</span>
</div>
</div>
<div class="flex flex-col sm:flex-row items-center gap-6">
@if ($product->image)
@endif
<div class="flex-grow text-center sm:text-left">
<h1 class="text-3xl lg:text-4xl font-bold text-color-base mb-3 leading-tight">
{{ $product->name }}
</h1>
<div>
<article class=" text-color-muted text-sm leading-relaxed">
{!! $product->description !!}
</article>
</div>
</div>
</div>
</div>
</div>
@if ($product->availablePlans()->count() > 1)
<div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 border border-neutral/50 rounded-2xl shadow-lg overflow-hidden animate-fade-in-up"
style="animation-delay: 0.1s;">
<div class="p-6 lg:p-8">
<div class="flex items-center gap-4 mb-6">
<div class="bg-primary/10 text-primary p-3 rounded-full shadow-md">
<x-ri-price-tag-3-fill class="size-6" />
</div>
<div>
<h2 class="text-xl font-bold text-color-base">
{{ __('Select Your Plan') }}
</h2>
<p class="text-color-muted text-sm">Choose the perfect plan for your needs</p>
</div>
</div>
<div class="w-full">
<label for="plan_id" class="block text-sm font-medium text-color-base mb-2">
{{ __('Choose a plan') }}
</label>
<select wire:model.live="plan_id" id="plan_id" name="plan_id"
class="w-full text-color-base bg-background-secondary border border-neutral/50 focus:border-primary focus:ring-primary focus:ring-2 focus:ring-primary/20 rounded-xl px-4 py-3 appearance-none [&>option]:bg-background-secondary [&>option]:text-color-base dark:[&>option]:bg-gray-800 dark:[&>option]:text-gray-100">
@foreach ($product->availablePlans() as $availablePlan)
<option value="{{ $availablePlan->id }}">
{{ $availablePlan->name }} - {{ $availablePlan->price() }}
@if ($availablePlan->price()->has_setup_fee)
+ {{ $availablePlan->price()->formatted->setup_fee }} {{ __('product.setup_fee') }}
@endif
</option>
@endforeach
</select>
</div>
</div>
</div>
@endif
@if($product->configOptions->count() > 0 || count($this->getCheckoutConfig()) > 0)
<div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 border border-neutral/50 rounded-2xl shadow-lg overflow-hidden animate-fade-in-up"
style="animation-delay: 0.2s;">
<div class="p-6 lg:p-8">
<div class="flex items-center gap-4 mb-6">
<div class="bg-primary/10 text-primary p-3 rounded-full shadow-md">
<x-ri-settings-3-fill class="size-6" />
</div>
<div>
<h2 class="text-xl font-bold text-color-base">
{{ __('Configure Your Product') }}
</h2>
<p class="text-color-muted text-sm">Customize options to fit your requirements</p>
</div>
</div>
<div class="space-y-6">
{{-- Product Config Options --}}
@foreach ($product->configOptions as $configOption)
@php
$showPriceTag = $configOption->children->filter(fn ($value) => !$value->price(billing_period:
$plan->billing_period, billing_unit: $plan->billing_unit)->is_free)->count() > 0;
@endphp
<h2 class="font-bold">{{ $configOption->name }} Selection</h2>
<div class="bg-background-tertiary/30 rounded-xl p-4 border border-neutral/30">
<x-form.configoption :config="$configOption" :name="'configOptions.' . $configOption->id"
:showPriceTag="$showPriceTag" :plan="$plan">
@if ($configOption->type == 'select')
@foreach ($configOption->children as $configOptionValue)
<option value="{{ $configOptionValue->id }}"
class="bg-background-secondary text-color-base dark:bg-gray-800 dark:text-gray-100">
{{ $configOptionValue->name }}
{{ ($showPriceTag && $configOptionValue->price(billing_period:
$plan->billing_period, billing_unit: $plan->billing_unit)->available) ? ' - ' .
$configOptionValue->price(billing_period: $plan->billing_period, billing_unit:
$plan->billing_unit) : '' }}
</option>
@endforeach
@elseif($configOption->type == 'radio')
<div class="space-y-3">
@foreach ($configOption->children as $configOptionValue)
<div class="flex items-center gap-2">
<input type="radio" id="{{ $configOptionValue->id }}"
name="{{ $configOption->id }}"
wire:model.live="configOptions.{{ $configOption->id }}"
value="{{ $configOptionValue->id }}"
class="form-radio text-primary focus:ring-primary focus:ring-2 focus:ring-primary/20 size-4" />
<label for="{{ $configOptionValue->id }}" class="text-color-base">
{{ $configOptionValue->name }}
{{ ($showPriceTag && $configOptionValue->price(billing_period:
$plan->billing_period, billing_unit: $plan->billing_unit)->available) ? ' -
' . $configOptionValue->price(billing_period: $plan->billing_period,
billing_unit: $plan->billing_unit) : '' }}
</label>
</div>
@endforeach
</div>
@endif
</x-form.configoption>
</div>
@endforeach
@foreach ($this->getCheckoutConfig() as $configOption)
@php $configOption = (object) $configOption; @endphp
<div class="bg-background-tertiary/30 rounded-xl p-4 border border-neutral/30">
<x-form.configoption :config="$configOption"
:name="'checkoutConfig.' . $configOption->name">
@if ($configOption->type == 'select')
@foreach ($configOption->options as $configOptionValue => $configOptionValueName)
<option value="{{ $configOptionValue }}"
class="bg-background-secondary text-color-base dark:bg-gray-800 dark:text-gray-100">
{{ $configOptionValueName }}
</option>
@endforeach
@elseif($configOption->type == 'radio')
<div class="space-y-3">
@foreach ($configOption->options as $configOptionValue => $configOptionValueName)
<div class="flex items-center gap-2">
<input type="radio" id="{{ $configOptionValue }}"
name="{{ $configOption->name }}"
wire:model.live="checkoutConfig.{{ $configOption->name }}"
value="{{ $configOptionValue }}"
class="form-radio text-primary focus:ring-primary focus:ring-2 focus:ring-primary/20 size-4" />
<label for="{{ $configOptionValue }}" class="text-color-base">
{{ $configOptionValueName }}
</label>
</div>
@endforeach
</div>
@endif
</x-form.configoption>
</div>
@endforeach
</div>
</div>
</div>
@endif
</div>
<div class="flex flex-col gap-6 w-full col-span-3 md:col-span-1 sticky top-8 md:top-20 mt-6">
<div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 border border-neutral/50 rounded-2xl shadow-lg overflow-hidden animate-fade-in-up"
style="animation-delay: 0.3s;">
<div class="p-6 lg:p-8">
<div class="flex items-center gap-4 mb-6 pb-4 border-b border-neutral/50">
<div class="bg-primary/10 text-primary p-3 rounded-full shadow-md">
<x-ri-shopping-cart-2-fill class="size-6" />
</div>
<div>
<h2 class="text-xl font-bold text-color-base">
{{ __('product.order_summary') }}
</h2>
<p class="text-color-muted text-xs">Review your order details</p>
</div>
</div>
<div class="space-y-3 mb-6">
<div
class="flex-col items-start items-center p-4 bg-gradient-to-r from-primary/10 to-primary/5 rounded-xl">
<div class="flex items-center gap-2">
<x-ri-money-dollar-circle-fill class="size-5 text-primary" />
<h4 class="text-lg font-semibold text-color-base">{{ __('product.total_today') }}
</h4>
</div>
<span class="text-2xl font-bold text-primary">{{ $total }}</span>
</div>
@if ($total->setup_fee && $plan->type == 'recurring')
<div
class="flex justify-between items-center p-3 bg-background-tertiary/30 rounded-2xl border border-neutral/30">
<div class="flex items-center gap-2">
<x-ri-time-fill class="size-4 text-color-muted" />
<h4 class="text-sm text-color-muted">
{{ __('product.then_after_x', ['time' => $plan->billing_period . ' ' .
trans_choice(__('services.billing_cycles.' . $plan->billing_unit),
$plan->billing_period)]) }}
</h4>
</div>
<span class="text-base font-medium text-color-base">
{{ $total->format($total->price - $total->setup_fee) }}
</span>
</div>
<div
class="flex justify-between items-center p-3 bg-background-tertiary/30 rounded-2xl border border-neutral/30">
<div class="flex items-center gap-2">
<x-ri-price-tag-3-fill class="size-4 text-color-muted" />
<h4 class="text-sm text-color-muted">
Others starting at
</h4>
</div>
<span class="text-base font-medium text-color-base">
<s>{{ $total->format(($total->price - $total->setup_fee) * 3) }}</s>
</span>
</div>
@endif
</div>
@if (($product->stock > 0 || !$product->stock) && $product->price()->available)
<div class="space-y-3">
<x-button.primary wire:click="checkout" wire:loading.attr="disabled"
class="group/btn w-full inline-flex items-center justify-center gap-3 bg-primary hover:bg-primary/90 text-white px-6 py-4 rounded-xl font-semibold text-lg transition-all duration-300 hover:shadow-lg hover:shadow-primary/30 disabled:opacity-50 disabled:cursor-not-allowed">
<span wire:loading.remove>{{ __('product.checkout') }}</span>
<span wire:loading>Processing...</span>
<x-ri-arrow-right-fill
class="size-5 transform transition-transform duration-300 group-hover/btn:translate-x-1"
wire:loading.remove />
<x-ri-loader-4-fill class="size-5 animate-spin" wire:loading />
</x-button.primary>
<div class="flex items-center justify-center gap-2 text-xs text-color-muted">
<x-ri-shield-check-fill class="size-4 text-green-500" />
<span>Secure checkout powered by encryption</span>
</div>
</div>
@else
<div
class="p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-xl">
<div class="flex items-center gap-2 text-red-600 dark:text-red-400">
<x-ri-error-warning-fill class="size-5" />
<span class="font-medium">Currently Unavailable</span>
</div>
<p class="text-sm text-red-500 dark:text-red-300 mt-1">
This product is currently out of stock or not available for purchase.
</p>
</div>
@endif
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,271 @@
<div class="mx-auto container mt-8 mb-12 p-6 sm:px-6 lg:px-8">
@if ($category->image && theme('show_category_image_banner', true))
<div class="mb-8 rounded-2xl overflow-hidden shadow-xl">
<div class="relative h-48 md:h-60 lg:h-72">
<img src="{{ Storage::url($category->image) }}"
alt="{{ $category->name }}"
class="w-full h-full object-cover object-center">
<div class="absolute inset-0 bg-gradient-to-t from-primary/30 via-primary/15 to-primary/10"></div>
<div class="absolute bottom-0 left-0 right-0 p-6 md:p-8">
<div class="flex items-center gap-3 mb-3">
<div class="bg-primary/30 backdrop-blur-sm text-primary p-2.5 rounded-full shadow-lg">
<x-ri-folder-fill class="size-5" />
</div>
</div>
<h1 class="text-3xl md:text-4xl lg:text-5xl font-bold text-white text-shadow-lg mb-2 drop-shadow-2xl">
{{ $category->name }}
</h1>
@if ($category->description)
<div class="hidden md:block text-white md:text-lg text-shadow-lg max-w-3xl drop-shadow-lg ">
{!! Str::limit($category->description, 215, '...') !!}
</div>
@endif
</div>
</div>
</div>
@endif
<div class="grid md:grid-cols-4 gap-8 lg:gap-12">
<div class="flex flex-col gap-6 col-span-3 md:col-span-1">
@if (!$category->image || !theme('show_category_image_banner', true))
<div class="group bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 border border-neutral/50 rounded-2xl shadow-lg hover:shadow-xl transform transition-all duration-300 overflow-hidden">
<div class="p-6">
<div class="flex items-center gap-3 mb-4">
<div class="bg-primary/10 text-primary p-3 rounded-full shadow-md">
<x-ri-folder-fill class="size-6" />
</div>
</div>
<h1 class="text-3xl font-bold text-color-base mb-3 group-hover:text-primary transition-colors duration-300">
{{ $category->name }}
</h1>
@if ($category->description)
<article class="prose dark:prose-invert text-color-muted leading-relaxed">
{!! $category->description !!}
</article>
@endif
</div>
</div>
@endif
<div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 border border-neutral/50 rounded-2xl shadow-lg overflow-hidden">
<div class="p-6 pb-4 border-b border-neutral/50">
<span class="group/nav text-color-base transition-all duration-300 border-l-4 border-transparent font-bold text-primary">Product Catagories</span>
</div>
<nav class="flex flex-col py-2">
@foreach ($categories as $ccategory)
<a href="{{ route('category.show', ['category' => $ccategory->slug]) }}" wire:navigate
class="group/nav px-6 py-3 text-color-base hover:bg-primary/10 hover:text-primary transition-all duration-300 border-l-4 border-transparent hover:border-primary {{ $category->id == $ccategory->id ? 'font-bold text-primary bg-primary/10 border-primary' : '' }}"
aria-current="{{ $category->id == $ccategory->id ? 'page' : 'false' }}">
<div class="flex items-center justify-between">
<span>{{ $ccategory->name }}</span>
<x-ri-arrow-right-fill class="size-4 transform transition-transform duration-300 group-hover/nav:translate-x-1 opacity-0 group-hover/nav:opacity-100" />
</div>
</a>
@endforeach
</nav>
</div>
</div>
<div class="flex flex-col gap-8 col-span-3">
<!-- Child Categories -->
@if (count($childCategories) >= 1)
<div>
<div class="flex items-center justify-between mb-6">
<div class="hidden md:flex items-center gap-2 text-color-muted">
<x-ri-stack-fill class="size-4" />
<span class="text-sm">{{ count($childCategories) }} {{ Str::plural('category', count($childCategories)) }}</span>
</div>
</div>
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-6">
@foreach ($childCategories as $index => $childCategory)
@php
$hasImage = !empty($childCategory->image);
@endphp
<div class="group relative bg-gradient-to-br from-background-secondary via-background-secondary/90 to-background-secondary/70 border border-neutral/50 rounded-2xl shadow-xl hover:shadow-2xl transform hover:-translate-y-3 transition-all duration-500 overflow-hidden animate-fade-in-up"
style="animation-delay: {{ $index * 0.1 }}s;">
@if ($hasImage)
<div class="relative overflow-hidden">
<img src="{{ Storage::url($childCategory->image) }}" alt="{{ $childCategory->name }}"
class="w-full h-56 object-cover">
<div class="absolute inset-0 bg-gradient-to-t from-black/20 to-transparent"></div>
</div>
@endif
<div class="p-6">
<h3 class="text-xl font-bold text-color-base mb-4 group-hover:text-primary transition-colors duration-300 line-clamp-2">
{{ $childCategory->name }}
</h3>
@if(theme('show_category_description', true) && $childCategory->description)
<article class="prose dark:prose-invert text-color-muted text-sm mb-4 leading-relaxed">
{!! $childCategory->description !!}
</article>
@endif
<div class="flex items-center justify-between">
<a href="{{ route('category.show', ['category' => $childCategory->slug]) }}" wire:navigate
class="group/btn inline-flex items-center justify-center gap-2 bg-primary hover:bg-primary/90 text-white px-4 py-3 rounded-2xl font-medium transition-all duration-300 hover:shadow-lg hover:scale-105">
{{ __('general.view') }}
<x-ri-arrow-right-fill class="size-4 transform transition-transform duration-300 group-hover/btn:translate-x-1" />
</a>
</div>
</div>
<div class="absolute inset-0 bg-gradient-to-br from-primary/5 to-primary/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300 pointer-events-none"></div>
</div>
@endforeach
</div>
</div>
@endif
<!-- Products -->
<div>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
@foreach ($products as $index => $product)
@php
// Check if product is available
$isAvailable = ($product->stock !== 0) && $product->price()->available;
@endphp
<div class="group relative bg-gradient-to-br from-background-secondary via-background-secondary/90 to-background-secondary/70 border border-neutral/50 rounded-3xl shadow-xl hover:shadow-2xl transform hover:-translate-y-3 transition-all duration-500 overflow-hidden animate-fade-in-up"
style="animation-delay: {{ $index * 0.1 }}s;">
<div class="absolute top-4 right-4 z-10">
<div class="flex items-center gap-2 bg-background-secondary/70 backdrop-blur-sm rounded-full px-3 py-1.5 shadow-lg">
<div class="w-2 h-2 {{ $isAvailable ? 'bg-green-500' : 'bg-red-500' }} rounded-full animate-pulse"></div>
<span class="text-xs font-bold {{ $isAvailable ? 'text-green-600' : 'text-red-600' }} uppercase">
{{ $isAvailable ? 'Available' : 'Out of Stock' }}
</span>
</div>
</div>
@if(theme('small_images', false))
<div class="p-6">
<div class="flex items-start gap-4 mb-4">
@if ($product->image)
<div class="flex-shrink-0 relative">
<img src="{{ Storage::url($product->image) }}" alt="{{ $product->name }}"
class="w-20 h-20 object-cover rounded-2xl {{ !$isAvailable ? 'filter grayscale opacity-60' : '' }}">
@if (!$isAvailable)
<div class="absolute inset-0 bg-red-500/20 rounded-2xl"></div>
@endif
</div>
@endif
<div class="flex-1">
<h3 class="text-xl font-bold text-color-base mb-2 group-hover:text-primary transition-colors duration-300 line-clamp-2">
{{ $product->name }}
</h3>
<p class="text-2xl font-bold {{ $isAvailable ? 'text-primary' : 'text-color-muted line-through' }} mb-2">
{{ $product->price() }}
</p>
@if($product->stock !== null)
<div class="text-xs text-color-muted">
{{ $product->stock }} in stock
</div>
@endif
</div>
</div>
<div class="mt-4">
@if ($isAvailable && theme('direct_checkout', false))
<a href="{{ route('products.checkout', ['category' => $category, 'product' => $product->slug]) }}" wire:navigate
class="group/btn w-full inline-flex items-center justify-center gap-2 bg-primary hover:bg-primary/90 text-white px-4 py-3 rounded-2xl font-medium transition-all duration-300 hover:shadow-lg hover:scale-105">
{{ __('product.add_to_cart') }}
<x-ri-shopping-cart-fill class="size-4 transform transition-transform duration-300 group-hover/btn:scale-110" />
</a>
@elseif ($isAvailable)
<a href="{{ route('products.show', ['category' => $product->category, 'product' => $product->slug]) }}" wire:navigate
class="group/btn w-full inline-flex items-center justify-center gap-2 bg-background-tertiary hover:bg-background-tertiary/80 border border-neutral/50 text-color-base px-4 py-3 rounded-2xl font-medium transition-all duration-300 hover:shadow-lg">
{{ __('general.view') }}
<x-ri-arrow-right-fill class="size-4 transform transition-transform duration-300 group-hover/btn:translate-x-1" />
</a>
@else
<button disabled
class="w-full inline-flex items-center justify-center gap-2 bg-red-100 dark:bg-red-900/30 border border-red-500 text-red-600 dark:text-red-400 px-4 py-3 rounded-2xl font-medium cursor-not-allowed opacity-75">
{{ __('product.out_of_stock', ['product' => $product->name]) }}
<x-ri-close-circle-fill class="size-4" />
</button>
@endif
</div>
</div>
@else
@if ($product->image)
<div class="relative overflow-hidden">
<img src="{{ Storage::url($product->image) }}" alt="{{ $product->name }}"
class="w-full h-56 object-cover {{ !$isAvailable ? 'filter grayscale opacity-60' : '' }}">
@if (!$isAvailable)
<div class="absolute inset-0 bg-red-500/20"></div>
@endif
<div class="absolute inset-0 bg-gradient-to-t from-black/20 to-transparent"></div>
</div>
@endif
<div class="p-6">
<h3 class="text-xl font-bold text-color-base mb-4 group-hover:text-primary transition-colors duration-300 line-clamp-2">
{{ $product->name }}
</h3>
@if(theme('direct_checkout', false) && $product->description)
<article class="prose dark:prose-invert text-color-muted text-sm mb-4 leading-relaxed">
{!! $product->description !!}
</article>
@endif
<div class="flex items-center justify-between mb-6">
<p class="text-2xl font-bold {{ $isAvailable ? 'text-primary' : 'text-color-muted line-through' }}">
{{ $product->price() }}
</p>
@if($product->stock !== null)
<div class="text-xs text-color-muted font-medium">
{{ $product->stock }} in stock
</div>
@endif
</div>
@if ($isAvailable && theme('direct_checkout', false))
<a href="{{ route('products.checkout', ['category' => $category, 'product' => $product->slug]) }}" wire:navigate
class="group/btn w-full inline-flex items-center justify-center gap-2 bg-primary hover:bg-primary/90 text-white px-4 py-3 rounded-2xl font-medium transition-all duration-300 hover:shadow-lg hover:scale-105">
{{ __('product.add_to_cart') }}
<x-ri-shopping-cart-fill class="size-4 transform transition-transform duration-300 group-hover/btn:scale-110" />
</a>
@elseif ($isAvailable)
<a href="{{ route('products.show', ['category' => $product->category, 'product' => $product->slug]) }}" wire:navigate
class="group/btn w-full inline-flex items-center justify-center gap-2 bg-background-tertiary hover:bg-background-tertiary/80 border border-neutral/50 text-color-base px-4 py-3 rounded-2xl font-medium transition-all duration-300 hover:shadow-lg">
{{ __('general.view') }}
<x-ri-arrow-right-fill class="size-4 transform transition-transform duration-300 group-hover/btn:translate-x-1" />
</a>
@else
<button disabled
class="w-full inline-flex items-center justify-center gap-2 bg-red-100 dark:bg-red-900/30 border border-red-500 text-red-600 dark:text-red-400 px-4 py-3 rounded-2xl font-medium cursor-not-allowed opacity-75">
{{ __('product.out_of_stock', ['product' => $product->name]) }}
<x-ri-close-circle-fill class="size-4" />
</button>
@endif
</div>
@endif
<div class="absolute inset-0 bg-gradient-to-br {{ $isAvailable ? 'from-primary/5 to-primary/10' : 'from-red-500/5 to-red-500/10' }} opacity-0 group-hover:opacity-100 transition-opacity duration-300 pointer-events-none"></div>
</div>
@endforeach
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,42 @@
<div class="container mx-auto px-4 py-8 sm:px-6 lg:px-8">
<div class="bg-background-secondary rounded-xl shadow-2xl p-6 lg:p-10 border border-neutral
@if ($product->image) grid grid-cols-1 md:grid-cols-2 @endif gap-8 lg:gap-12 items-start">
@if ($product->image)
<div class="relative overflow-hidden rounded-2xl shadow-lg aspect-w-4 aspect-h-3 mb-6 md:mb-0">
<img src="{{ Storage::url($product->image) }}" alt="{{ $product->name }}"
class="w-full h-full object-cover object-center rounded-2xl transition-transform duration-300 hover:scale-105">
</div>
@endif
<div class="flex flex-col gap-8">
@if ($product->stock === 0)
<span class="text-xs font-semibold px-3 py-1 rounded-full bg-red-800 text-red-100 w-fit">
{{ __('product.out_of_stock', ['product' => $product->name]) }}
</span>
@elseif($product->stock > 0)
<span class="text-xs font-semibold px-3 py-1 rounded-full bg-green-800 text-green-100 w-fit">
{{ __('product.in_stock') }}
</span>
@endif
<div>
<h1 class="text-3xl font-extrabold text-color-base mb-2 leading-tight">{{ $product->name }}</h1>
<p class="text-xl font-bold text-primary">{{ $product->price() }}</p>
</div>
@if ($product->description)
<div class="border-t border-neutral pt-4">
<h2 class="text-lg font-bold text-color-base mb-2">{{ __('Description') }}</h2>
<article class="text-color-muted leading-relaxed">
{!! $product->description !!}
</article>
</div>
@endif
@if ($product->stock !== 0 && $product->price()->available)
<div class="mt-4">
<a href="{{ route('products.checkout', ['category' => $category, 'product' => $product->slug]) }}"
wire:navigate>
<x-button.primary class="w-full sm:w-auto">{{ __('product.add_to_cart') }}</x-button.primary>
</a>
</div>
@endif
</div>
</div>
</div>

View File

@@ -0,0 +1,226 @@
<div class="mx-auto container mt-8 p-4 sm:p-6 lg:p-8 rounded-2xl">
<div class="grid md:grid-cols-4 gap-8 lg:gap-12 items-start">
<div class="flex flex-col gap-6 w-full col-span-3">
<div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 border border-neutral/50 rounded-2xl shadow-lg overflow-hidden">
<div class="p-6 lg:p-8">
<div class="flex items-start justify-between mb-6">
<div class="flex items-center gap-2">
<div class="size-2 bg-green-500 rounded-full animate-pulse"></div>
<span class="text-xs font-medium text-green-600 uppercase tracking-wide">Available</span>
</div>
</div>
<div class="flex flex-col sm:flex-row items-center gap-6">
@if ($product->image)
<div class="flex-shrink-0 relative overflow-hidden rounded-xl w-32 h-32 sm:w-36 sm:h-36 bg-gradient-to-br from-primary/10 to-primary/5 p-4">
<img src="{{ Storage::url($product->image) }}" alt="{{ $product->name }}" class="w-full h-full object-contain object-center">
</div>
@endif
<div class="flex-grow text-center sm:text-left">
<h1 class="text-3xl lg:text-4xl font-bold text-color-base mb-3 leading-tight">
{{ $product->name }}
</h1>
<div>
<article class="prose dark:prose-invert text-color-muted text-sm leading-relaxed">
{!! $product->description !!}
</article>
</div>
</div>
</div>
</div>
</div>
@if ($product->availablePlans()->count() > 1)
<div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 border border-neutral/50 rounded-2xl shadow-lg overflow-hidden animate-fade-in-up" style="animation-delay: 0.1s;">
<div class="p-6 lg:p-8">
<div class="flex items-center gap-4 mb-6">
<div class="bg-primary/10 text-primary p-3 rounded-full shadow-md">
<x-ri-price-tag-3-fill class="size-6" />
</div>
<div>
<h2 class="text-xl font-bold text-color-base">
{{ __('Select Your Plan') }}
</h2>
<p class="text-color-muted text-sm">Choose the perfect plan for your needs</p>
</div>
</div>
<div class="w-full">
<label for="plan_id" class="block text-sm font-medium text-color-base mb-2">
{{ __('Choose a plan') }}
</label>
<select wire:model.live="plan_id"
id="plan_id"
name="plan_id"
class="w-full text-color-base bg-background-secondary border border-neutral/50 focus:border-primary focus:ring-primary focus:ring-2 focus:ring-primary/20 rounded-xl px-4 py-3 appearance-none [&>option]:bg-background-secondary [&>option]:text-color-base dark:[&>option]:bg-gray-800 dark:[&>option]:text-gray-100">
@foreach ($product->availablePlans() as $availablePlan)
<option value="{{ $availablePlan->id }}">
{{ $availablePlan->name }} - {{ $availablePlan->price() }}
@if ($availablePlan->price()->has_setup_fee)
+ {{ $availablePlan->price()->formatted->setup_fee }} {{ __('product.setup_fee') }}
@endif
</option>
@endforeach
</select>
</div>
</div>
</div>
@endif
@if($product->configOptions->count() > 0 || count($this->getCheckoutConfig()) > 0)
<div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 border border-neutral/50 rounded-2xl shadow-lg overflow-hidden animate-fade-in-up" style="animation-delay: 0.2s;">
<div class="p-6 lg:p-8">
<div class="flex items-center gap-4 mb-6">
<div class="bg-primary/10 text-primary p-3 rounded-full shadow-md">
<x-ri-settings-3-fill class="size-6" />
</div>
<div>
<h2 class="text-xl font-bold text-color-base">
{{ __('Configure Your Product') }}
</h2>
<p class="text-color-muted text-sm">Customize options to fit your requirements</p>
</div>
</div>
<div class="space-y-6">
{{-- Product Config Options --}}
@foreach ($product->configOptions as $configOption)
@php
$showPriceTag = $configOption->children->filter(fn ($value) => !$value->price(billing_period: $plan->billing_period, billing_unit: $plan->billing_unit)->is_free)->count() > 0;
@endphp
<div class="bg-background-tertiary/30 rounded-xl p-4 border border-neutral/30">
<x-form.configoption :config="$configOption" :name="'configOptions.' . $configOption->id" :showPriceTag="$showPriceTag" :plan="$plan">
@if ($configOption->type == 'select')
@foreach ($configOption->children as $configOptionValue)
<option value="{{ $configOptionValue->id }}" class="bg-background-secondary text-color-base dark:bg-gray-800 dark:text-gray-100">
{{ $configOptionValue->name }}
{{ ($showPriceTag && $configOptionValue->price(billing_period: $plan->billing_period, billing_unit: $plan->billing_unit)->available) ? ' - ' . $configOptionValue->price(billing_period: $plan->billing_period, billing_unit: $plan->billing_unit) : '' }}
</option>
@endforeach
@elseif($configOption->type == 'radio')
<div class="space-y-3">
@foreach ($configOption->children as $configOptionValue)
<div class="flex items-center gap-2">
<input type="radio" id="{{ $configOptionValue->id }}" name="{{ $configOption->id }}"
wire:model.live="configOptions.{{ $configOption->id }}"
value="{{ $configOptionValue->id }}"
class="form-radio text-primary focus:ring-primary focus:ring-2 focus:ring-primary/20 size-4" />
<label for="{{ $configOptionValue->id }}" class="text-color-base">
{{ $configOptionValue->name }}
{{ ($showPriceTag && $configOptionValue->price(billing_period: $plan->billing_period, billing_unit: $plan->billing_unit)->available) ? ' - ' . $configOptionValue->price(billing_period: $plan->billing_period, billing_unit: $plan->billing_unit) : '' }}
</label>
</div>
@endforeach
</div>
@endif
</x-form.configoption>
</div>
@endforeach
@foreach ($this->getCheckoutConfig() as $configOption)
@php $configOption = (object) $configOption; @endphp
<div class="bg-background-tertiary/30 rounded-xl p-4 border border-neutral/30">
<x-form.configoption :config="$configOption" :name="'checkoutConfig.' . $configOption->name">
@if ($configOption->type == 'select')
@foreach ($configOption->options as $configOptionValue => $configOptionValueName)
<option value="{{ $configOptionValue }}" class="bg-background-secondary text-color-base dark:bg-gray-800 dark:text-gray-100">
{{ $configOptionValueName }}
</option>
@endforeach
@elseif($configOption->type == 'radio')
<div class="space-y-3">
@foreach ($configOption->options as $configOptionValue => $configOptionValueName)
<div class="flex items-center gap-2">
<input type="radio" id="{{ $configOptionValue }}" name="{{ $configOption->name }}"
wire:model.live="checkoutConfig.{{ $configOption->name }}"
value="{{ $configOptionValue }}"
class="form-radio text-primary focus:ring-primary focus:ring-2 focus:ring-primary/20 size-4" />
<label for="{{ $configOptionValue }}" class="text-color-base">
{{ $configOptionValueName }}
</label>
</div>
@endforeach
</div>
@endif
</x-form.configoption>
</div>
@endforeach
</div>
</div>
</div>
@endif
</div>
<div class="flex flex-col gap-6 w-full col-span-3 md:col-span-1 sticky top-8 md:top-20">
<div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 border border-neutral/50 rounded-2xl shadow-lg overflow-hidden animate-fade-in-up" style="animation-delay: 0.3s;">
<div class="p-6 lg:p-8">
<div class="flex items-center gap-4 mb-6 pb-4 border-b border-neutral/50">
<div class="bg-primary/10 text-primary p-3 rounded-full shadow-md">
<x-ri-shopping-cart-2-fill class="size-6" />
</div>
<div>
<h2 class="text-xl font-bold text-color-base">
{{ __('product.order_summary') }}
</h2>
<p class="text-color-muted text-xs">Review your order details</p>
</div>
</div>
<div class="space-y-3 mb-6">
<div class="flex-col items-start items-center p-4 bg-gradient-to-r from-primary/10 to-primary/5 rounded-xl">
<div class="flex items-center gap-2">
<x-ri-money-dollar-circle-fill class="size-5 text-primary" />
<h4 class="text-lg font-semibold text-color-base">{{ __('product.total_today') }}</h4>
</div>
<span class="text-2xl font-bold text-primary">{{ $total }}</span>
</div>
@if ($total->setup_fee && $plan->type == 'recurring')
<div class="flex justify-between items-center p-3 bg-background-tertiary/30 rounded-2xl border border-neutral/30">
<div class="flex items-center gap-2">
<x-ri-time-fill class="size-4 text-color-muted" />
<h4 class="text-sm text-color-muted">
{{ __('product.then_after_x', ['time' => $plan->billing_period . ' ' . trans_choice(__('services.billing_cycles.' . $plan->billing_unit), $plan->billing_period)]) }}
</h4>
</div>
<span class="text-base font-medium text-color-base">
{{ $total->format($total->price - $total->setup_fee) }}
</span>
</div>
@endif
</div>
@if (($product->stock > 0 || !$product->stock) && $product->price()->available)
<div class="space-y-3">
<x-button.primary wire:click="checkout" wire:loading.attr="disabled"
class="group/btn w-full inline-flex items-center justify-center gap-3 bg-primary hover:bg-primary/90 text-white px-6 py-4 rounded-xl font-semibold text-lg transition-all duration-300 hover:shadow-lg hover:shadow-primary/30 disabled:opacity-50 disabled:cursor-not-allowed">
<span wire:loading.remove>{{ __('product.checkout') }}</span>
<span wire:loading>Processing...</span>
<x-ri-arrow-right-fill class="size-5 transform transition-transform duration-300 group-hover/btn:translate-x-1" wire:loading.remove />
<x-ri-loader-4-fill class="size-5 animate-spin" wire:loading />
</x-button.primary>
<div class="flex items-center justify-center gap-2 text-xs text-color-muted">
<x-ri-shield-check-fill class="size-4 text-green-500" />
<span>Secure checkout powered by encryption</span>
</div>
</div>
@else
<div class="p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-xl">
<div class="flex items-center gap-2 text-red-600 dark:text-red-400">
<x-ri-error-warning-fill class="size-5" />
<span class="font-medium">Currently Unavailable</span>
</div>
<p class="text-sm text-red-500 dark:text-red-300 mt-1">
This product is currently out of stock or not available for purchase.
</p>
</div>
@endif
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,321 @@
<div class="mx-auto container mt-8 rounded-2xl">
<div class="flex flex-col gap-6 w-full col-span-3 md:col-span-1 sticky top-8 md:top-20">
<div class="flex flex-col gap-6 w-full col-span-3">
<div
class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 border border-neutral/50 rounded-2xl shadow-lg overflow-hidden">
<div class="p-6 lg:p-8">
<div class="flex items-start justify-between mb-6">
<div class="flex items-center gap-2">
<div class="size-2 bg-green-500 rounded-full animate-pulse"></div>
<span class="text-xs font-medium text-green-600 uppercase tracking-wide">Available</span>
</div>
</div>
<div class="flex flex-col sm:flex-row items-center gap-6">
<div class="flex-grow text-center sm:text-left">
<h1 class="text-3xl lg:text-4xl font-bold text-color-base mb-3 leading-tight">
{{ $product->name }}
</h1>
<div>
<article class=" text-color-muted text-sm leading-relaxed">
{!! $product->description !!}
</article>
</div>
</div>
</div>
</div>
</div>
@if ($product->availablePlans()->count() > 1)
<div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 border border-neutral/50 rounded-2xl shadow-lg overflow-hidden animate-fade-in-up"
style="animation-delay: 0.1s;">
<div class="p-6 lg:p-8">
<div class="flex items-center gap-4 mb-6">
<div class="bg-primary/10 text-primary p-3 rounded-full shadow-md">
<x-ri-price-tag-3-fill class="size-6" />
</div>
<div>
<h2 class="text-xl font-bold text-color-base">
{{ __('Select Your Plan') }}
</h2>
<p class="text-color-muted text-sm">Choose the perfect plan for your needs</p>
</div>
</div>
<div class="w-full">
<label for="plan_id" class="block text-sm font-medium text-color-base mb-2">
{{ __('Choose a plan') }}
</label>
<select wire:model.live="plan_id" id="plan_id" name="plan_id"
class="w-full text-color-base bg-background-secondary border border-neutral/50 focus:border-primary focus:ring-primary focus:ring-2 focus:ring-primary/20 rounded-xl px-4 py-3 appearance-none [&>option]:bg-background-secondary [&>option]:text-color-base dark:[&>option]:bg-gray-800 dark:[&>option]:text-gray-100">
@foreach ($product->availablePlans() as $availablePlan)
<option value="{{ $availablePlan->id }}">
{{ $availablePlan->name }} - {{ $availablePlan->price() }}
@if ($availablePlan->price()->has_setup_fee)
+ {{ $availablePlan->price()->formatted->setup_fee }} {{ __('product.setup_fee') }}
@endif
</option>
@endforeach
</select>
</div>
</div>
</div>
@endif
@if($product->configOptions->count() > 0 || count($this->getCheckoutConfig()) > 0)
<div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 border border-neutral/50 rounded-2xl shadow-lg overflow-hidden animate-fade-in-up"
style="animation-delay: 0.2s;">
<div class="p-6 lg:p-8">
<div class="flex items-center gap-4 mb-6">
<div class="bg-primary/10 text-primary p-3 rounded-full shadow-md">
<x-ri-settings-3-fill class="size-6" />
</div>
<div>
<h2 class="text-xl font-bold text-color-base">
{{ __('Configure Your Product') }}
</h2>
<p class="text-color-muted text-sm">Customize options to fit your requirements</p>
</div>
</div>
<div class="space-y-6">
{{-- Product Config Options --}}
@foreach ($product->configOptions as $configOption)
@php
$showPriceTag = $configOption->children->filter(fn ($value) => !$value->price(billing_period:
$plan->billing_period, billing_unit: $plan->billing_unit)->is_free)->count() > 0;
@endphp
<div class="bg-background-tertiary/30 rounded-xl p-4 border border-neutral/30">
<x-form.configoption :config="$configOption" :name="'configOptions.' . $configOption->id"
:showPriceTag="$showPriceTag" :plan="$plan">
@if ($configOption->type == 'select')
@foreach ($configOption->children as $configOptionValue)
<option value="{{ $configOptionValue->id }}"
class="bg-background-secondary text-color-base dark:bg-gray-800 dark:text-gray-100">
{{ $configOptionValue->name }}
{{ ($showPriceTag && $configOptionValue->price(billing_period:
$plan->billing_period, billing_unit: $plan->billing_unit)->available) ? ' - ' .
$configOptionValue->price(billing_period: $plan->billing_period, billing_unit:
$plan->billing_unit) : '' }}
</option>
@endforeach
@elseif($configOption->type == 'radio')
<div class="space-y-3">
@foreach ($configOption->children as $configOptionValue)
<div class="flex items-center gap-2">
<input type="radio" id="{{ $configOptionValue->id }}"
name="{{ $configOption->id }}"
wire:model.live="configOptions.{{ $configOption->id }}"
value="{{ $configOptionValue->id }}"
class=" text-primary focus:ring-primary focus:ring-2 focus:ring-primary/20 size-4 focus:text-blue-600" />
<label for="{{ $configOptionValue->id }}" class="text-color-base">
{{ $configOptionValue->name }}
{{ ($showPriceTag && $configOptionValue->price(billing_period:
$plan->billing_period, billing_unit: $plan->billing_unit)->available) ? ' -
' . $configOptionValue->price(billing_period: $plan->billing_period,
billing_unit: $plan->billing_unit) : '' }}
</label>
</div>
@endforeach
</div>
@endif
</x-form.configoption>
</div>
@endforeach
@foreach ($this->getCheckoutConfig() as $configOption)
@php $configOption = (object) $configOption; @endphp
<div class="bg-background-tertiary/30 rounded-xl p-4 border border-neutral/30">
<x-form.configoption :config="$configOption"
:name="'checkoutConfig.' . $configOption->name">
@if ($configOption->type == 'select')
@foreach ($configOption->options as $configOptionValue => $configOptionValueName)
<option value="{{ $configOptionValue }}"
class="bg-background-secondary text-color-base dark:bg-gray-800 dark:text-gray-100">
{{ $configOptionValueName }}
</option>
@endforeach
@elseif($configOption->type == 'radio')
<div class="space-y-3">
@foreach ($configOption->options as $configOptionValue => $configOptionValueName)
<div class="flex items-center gap-2">
<input type="radio" id="{{ $configOptionValue }}"
name="{{ $configOption->name }}"
wire:model.live="checkoutConfig.{{ $configOption->name }}"
value="{{ $configOptionValue }}"
class="form-radio text-primary focus:ring-primary focus:ring-2 focus:ring-primary/20 size-4" />
<label for="{{ $configOptionValue }}" class="text-color-base">
{{ $configOptionValueName }}
</label>
</div>
@endforeach
</div>
@endif
</x-form.configoption>
</div>
@endforeach
</div>
</div>
</div>
@endif
<div class="flex flex-col gap-6 w-full col-span-3 md:col-span-1 sticky top-8 md:top-20 mt-6">
<div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 border border-neutral/50 rounded-2xl shadow-lg overflow-hidden animate-fade-in-up"
style="animation-delay: 0.3s;">
<div class="p-6 lg:p-8">
<div class="flex items-center gap-4 mb-6 pb-4 border-b border-neutral/50">
<div class="bg-primary/10 text-primary p-3 rounded-full shadow-md">
<x-ri-shopping-cart-2-fill class="size-6" />
</div>
<div>
<h2 class="text-xl font-bold text-color-base">
{{ __('product.order_summary') }}
</h2>
<p class="text-color-muted text-xs">Review your order details</p>
</div>
</div>
<div class="space-y-3 mb-6">
<div
class="flex-col items-start items-center p-4 bg-gradient-to-r from-primary/10 to-primary/5 rounded-xl">
<div class="flex items-center gap-2">
<x-ri-money-dollar-circle-fill class="size-5 text-primary" />
<h4 class="text-lg font-semibold text-color-base">{{ __('product.total_today') }}
</h4>
</div>
<span class="text-2xl font-bold text-primary">{{ $total }}</span>
</div>
@if ($total->setup_fee && $plan->type == 'recurring')
<div
class="flex justify-between items-center p-3 bg-background-tertiary/30 rounded-2xl border border-neutral/30">
<div class="flex items-center gap-2">
<x-ri-time-fill class="size-4 text-color-muted" />
<h4 class="text-sm text-color-muted">
{{ __('product.then_after_x', ['time' => $plan->billing_period . ' ' .
trans_choice(__('services.billing_cycles.' . $plan->billing_unit),
$plan->billing_period)]) }} (Monthly Charge)
</h4>
</div>
<span class="text-base font-medium text-color-base">
{{ $total->format($total->price - $total->setup_fee) }}
</span>
</div>
<div
class="flex justify-between items-center p-3 bg-background-tertiary/30 rounded-2xl border border-neutral/30">
<div class="flex items-center gap-2">
<x-ri-hard-drive-3-fill class="size-4 text-color-muted" />
<h4 class="text-sm text-color-muted">
Initial Setup Fee (Covers SSD Maintenance)
</h4>
</div>
<span class="text-base font-medium text-color-base">
{{ $total->format(($total->setup_fee)) }}
</span>
</div>
<div
class="flex justify-between items-center p-3 bg-background-tertiary/30 rounded-2xl border border-neutral/30">
<div class="flex items-center gap-2">
<x-ri-price-tag-3-fill class="size-4 text-color-muted" />
<h4 class="text-sm text-color-muted">
Competitors Start At (Monthly Charge)
</h4>
</div>
<span class="text-base font-medium text-color-base">
<s>{{ $total->format((($total->price - $total->setup_fee) +2) * 2) }}</s>
</span>
</div>
@endif
</div>
@if (
($product->stock > 0 || !$product->stock)
&& $product->price()->available
&& (
$product->id != 33
|| ($product->id == 33 && auth()->check() && !empty(auth()->user()->tfa_secret))
)
)
{{-- Checkout button --}}
<div class="space-y-3">
<x-button.primary wire:click="checkout" wire:loading.attr="disabled"
class="group/btn w-full inline-flex items-center justify-center gap-3 bg-primary hover:bg-primary/90 text-white px-6 py-4 rounded-xl font-semibold text-lg transition-all duration-300 hover:shadow-lg hover:shadow-primary/30 disabled:opacity-50 disabled:cursor-not-allowed">
<span wire:loading.remove>{{ __('product.checkout') }}</span>
<span wire:loading>Processing...</span>
<x-ri-arrow-right-fill
class="size-5 transform transition-transform duration-300 group-hover/btn:translate-x-1"
wire:loading.remove />
<x-ri-loader-4-fill class="size-5 animate-spin" wire:loading />
</x-button.primary>
<div class="flex items-center justify-center gap-2 text-xs text-color-muted">
<x-ri-shield-check-fill class="size-4 text-green-500" />
<span>Secure checkout powered by encryption</span>
</div>
</div>
@elseif (
($product->stock > 0 || !$product->stock)
&& $product->price()->available
&& $product->id == 33
&& auth()->check() && empty(auth()->user()->tfa_secret)
)
{{-- TFA Required --}}
<div class="p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-xl">
<div class="flex items-center gap-2 text-red-600 dark:text-red-400">
<x-ri-error-warning-fill class="size-5" />
<span class="font-medium">Two-Factor Authentication Required</span>
</div>
<p class="text-sm text-red-500 dark:text-red-300 mt-1">
You must enable two-factor authentication to order this product.
</p>
</div>
@elseif (
($product->stock > 0 || !$product->stock)
&& $product->price()->available
&& $product->id == 33
&& !auth()->check()
)
{{-- Login Required --}}
<div class="p-4 bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-xl">
<div class="flex items-center gap-2 text-blue-600 dark:text-blue-400">
<x-ri-information-fill class="size-5" />
<span class="font-medium">Login and Two-Factor Authentication Required</span>
</div>
<p class="text-sm text-blue-500 dark:text-blue-300 mt-1">
You must be logged in to order this product and enable two-factor authentication.
</p>
</div>
@else
{{-- Currently Unavailable --}}
<div class="p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-xl">
<div class="flex items-center gap-2 text-red-600 dark:text-red-400">
<x-ri-error-warning-fill class="size-5" />
<span class="font-medium">Currently Unavailable</span>
</div>
<p class="text-sm text-red-500 dark:text-red-300 mt-1">
This product is currently out of stock or not available for purchase.
</p>
</div>
@endif
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,261 @@
<div class="mx-auto container mt-8 mb-12 p-6 sm:px-6 lg:px-8">
@if ($category->image && theme('show_category_image_banner', true))
<div class="mb-8 rounded-2xl overflow-hidden shadow-xl">
<div class="relative h-48 md:h-60 lg:h-72">
<img src="{{ Storage::url($category->image) }}"
alt="{{ $category->name }}"
class="w-full h-full object-cover object-center">
<div class="absolute inset-0 bg-gradient-to-t from-primary/30 via-primary/15 to-primary/10"></div>
<div class="absolute bottom-0 left-0 right-0 p-6 md:p-8">
<div class="flex items-center gap-3 mb-3">
</div>
<h1 class="text-3xl md:text-4xl lg:text-5xl font-bold text-white text-shadow-lg mb-2 drop-shadow-2xl">
{{ $category->name }}
</h1>
@if ($category->description)
<div class="hidden md:block text-white md:text-lg text-shadow-lg max-w-3xl drop-shadow-lg ">
{!! Str::limit($category->description, 215, '...') !!}
</div>
@endif
</div>
</div>
</div>
@endif
<div class="grid md:grid-cols-4 gap-8 lg:gap-12">
<div class="flex flex-col gap-6 col-span-3 md:col-span-1">
@if (!$category->image || !theme('show_category_image_banner', true))
<div class="group bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 border border-neutral/50 rounded-2xl shadow-lg hover:shadow-xl transform transition-all duration-300 overflow-hidden">
<div class="p-6">
<div class="flex items-center gap-3 mb-4">
<div class="bg-primary/10 text-primary p-3 rounded-full shadow-md">
<x-ri-folder-fill class="size-6" />
</div>
</div>
<h1 class="text-3xl font-bold text-color-base mb-3 group-hover:text-primary transition-colors duration-300">
{{ $category->name }}
</h1>
@if ($category->description)
<article class="prose dark:prose-invert text-color-muted leading-relaxed">
{!! $category->description !!}
</article>
@endif
</div>
</div>
@endif
<div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 border border-neutral/50 rounded-2xl shadow-lg overflow-hidden">
<div class="p-6 pb-4 border-b border-neutral/50">
<div class="flex items-center text-primary font-bold text-lg">
Categories
</div>
</div>
<nav class="flex flex-col py-2">
@foreach ($categories as $ccategory)
<a href="{{ route('category.show', ['category' => $ccategory->slug]) }}" wire:navigate
class="group/nav px-6 py-3 text-color-base hover:bg-primary/10 hover:text-primary transition-all duration-300 border-l-4 border-transparent hover:border-primary {{ $category->id == $ccategory->id ? 'font-bold text-primary bg-primary/10 border-primary' : '' }}"
aria-current="{{ $category->id == $ccategory->id ? 'page' : 'false' }}">
<div class="flex items-center justify-between">
<span>{{ $ccategory->name }}</span>
<x-ri-arrow-right-fill class="size-4 transform transition-transform duration-300 group-hover/nav:translate-x-1 opacity-0 group-hover/nav:opacity-100" />
</div>
</a>
@endforeach
</nav>
</div>
</div>
<div class="flex flex-col gap-8 col-span-3">
<!-- Child Categories -->
@if (count($childCategories) >= 1)
<div>
<div class="flex items-center justify-between mb-6">
<div class="hidden md:flex items-center gap-2 text-color-muted">
<x-ri-stack-fill class="size-4" />
<span class="text-sm">{{ count($childCategories) }} {{ Str::plural('category', count($childCategories)) }}</span>
</div>
</div>
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-6">
@foreach ($childCategories as $index => $childCategory)
@php
$hasImage = !empty($childCategory->image);
@endphp
<div class="group relative bg-gradient-to-br from-background-secondary via-background-secondary/90 to-background-secondary/70 border border-neutral/50 rounded-2xl shadow-xl hover:shadow-2xl transform hover:-translate-y-3 transition-all duration-500 overflow-hidden animate-fade-in-up"
style="animation-delay: {{ $index * 0.1 }}s;">
@if ($hasImage)
<div class="relative overflow-hidden">
<img src="{{ Storage::url($childCategory->image) }}" alt="{{ $childCategory->name }}"
class="w-full h-56 object-cover">
<div class="absolute inset-0 bg-gradient-to-t from-black/20 to-transparent"></div>
</div>
@endif
<div class="p-6">
<h3 class="text-xl font-bold text-color-base mb-4 group-hover:text-primary transition-colors duration-300 line-clamp-2">
{{ $childCategory->name }}
</h3>
@if(theme('show_category_description', true) && $childCategory->description)
<article class="prose dark:prose-invert text-color-muted text-sm mb-4 leading-relaxed">
{!! $childCategory->description !!}
</article>
@endif
<div class="flex w-full">
<a href="{{ route('category.show', ['category' => $childCategory->slug]) }}" wire:navigate
class="group/btn flex w-full items-center justify-center gap-2 bg-primary hover:bg-primary/90 text-white px-4 py-3 rounded-2xl font-medium transition-all duration-300 hover:shadow-lg hover:scale-105">
{{ __('View Products') }}
<x-ri-arrow-right-fill class="size-4 transform transition-transform duration-300 group-hover/btn:translate-x-1" />
</a>
</div>
</div>
<div class="absolute inset-0 bg-gradient-to-br from-primary/5 to-primary/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300 pointer-events-none"></div>
</div>
@endforeach
</div>
</div>
@endif
<!-- Products -->
<div>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-2 gap-6">
@foreach ($products as $index => $product)
@php
// Check if product is available
$isAvailable = ($product->stock !== 0) && $product->price()->available;
@endphp
<div class="group relative bg-gradient-to-br from-background-secondary via-background-secondary/90 to-background-secondary/70 border border-neutral/50 rounded-3xl shadow-xl hover:shadow-2xl transform hover:-translate-y-3 transition-all duration-500 overflow-hidden animate-fade-in-up"
style="animation-delay: {{ $index * 0.1 }}s;">
@if(theme('small_images', false))
<div class="p-6">
<div class="flex items-start gap-4 mb-4">
@if ($product->image)
<div class="flex-shrink-0 relative">
<img src="{{ Storage::url($product->image) }}" alt="{{ $product->name }}"
class="w-20 h-20 object-cover">
@if (!$isAvailable)
<div class="flex-shrink-0 relative"></div>
@endif
</div>
@endif
<div class="flex-1">
<h3 class="text-xl font-bold text-color-base mb-2 group-hover:text-primary transition-colors duration-300 line-clamp-2">
{{ $product->name }}
</h3>
<p class="text-2xl font-bold {{ $isAvailable ? 'text-primary' : 'text-color-muted' }} mb-2">
{{ $product->price() }}
</p>
@if($product->stock !== null)
<div class="text-xs text-color-muted">
<!--{{ $product->stock }} in stock-->
</div>
@endif
</div>
</div>
<div class="mt-4">
@if ($isAvailable && theme('direct_checkout', false))
<a href="{{ route('products.checkout', ['category' => $category, 'product' => $product->slug]) }}" wire:navigate
class="group/btn w-full inline-flex items-center justify-center gap-2 bg-primary hover:bg-primary/90 text-white px-4 py-3 rounded-2xl font-medium transition-all duration-300 hover:shadow-lg hover:scale-105">
{{ __('product.add_to_cart') }}
<x-ri-shopping-cart-fill class="size-4 transform transition-transform duration-300 group-hover/btn:scale-110" />
</a>
@elseif ($isAvailable)
<a href="{{ route('products.show', ['category' => $product->category, 'product' => $product->slug]) }}" wire:navigate
class="group/btn w-full inline-flex items-center justify-center gap-2 bg-background-tertiary hover:bg-background-tertiary/80 border border-neutral/50 text-color-base px-4 py-3 rounded-2xl font-medium transition-all duration-300 hover:shadow-lg">
{{ __('general.view') }}
<x-ri-arrow-right-fill class="size-4 transform transition-transform duration-300 group-hover/btn:translate-x-1" />
</a>
@else
<button disabled
class="w-full inline-flex items-center justify-center gap-2 bg-red-100 dark:bg-red-900/30 border border-red-500 text-red-600 dark:text-red-400 px-4 py-3 rounded-2xl font-medium cursor-not-allowed opacity-75">
{{ __('product.out_of_stock', ['product' => $product->name]) }}
<x-ri-close-circle-fill class="size-4" />
</button>
@endif
</div>
</div>
@else
@if ($product->image)
<div class="relative overflow-hidden">
<img src="{{ Storage::url($product->image) }}" alt="{{ $product->name }}"
class="w-full h-56 object-cover {{ !$isAvailable ? 'filter grayscale opacity-60' : '' }}">
@if (!$isAvailable)
<div class="relative overflow-hidden"></div>
@endif
<div class="absolute inset-0 bg-gradient-to-t from-black/20 to-transparent"></div>
</div>
@endif
<div class="p-6">
<h3 class="text-xl font-bold text-color-base mb-4 group-hover:text-primary transition-colors duration-300 line-clamp-2">
{{ $product->name }}
</h3>
@if(theme('direct_checkout', false) && $product->description)
<article class="prose dark:prose-invert text-color-muted text-sm mb-4 leading-relaxed">
{!! $product->description !!}
</article>
@endif
<div class="flex items-center justify-between mb-6">
<p class="text-2xl font-bold {{ $isAvailable ? 'text-primary' : 'text-color-muted' }}">
{{ $product->price() }}
</p>
@if($product->stock !== null)
<div class="text-xs text-color-muted font-medium">
{{ $product->stock }} in stock
</div>
@endif
</div>
@if ($isAvailable && theme('direct_checkout', false))
<a href="{{ route('products.checkout', ['category' => $category, 'product' => $product->slug]) }}" wire:navigate
class="group/btn w-full inline-flex items-center justify-center gap-2 bg-primary hover:bg-primary/90 text-white px-4 py-3 rounded-2xl font-medium transition-all duration-300 hover:shadow-lg hover:scale-105">
{{ __('product.add_to_cart') }}
<x-ri-shopping-cart-fill class="size-4 transform transition-transform duration-300 group-hover/btn:scale-110" />
</a>
@elseif ($isAvailable)
<a href="{{ route('products.show', ['category' => $product->category, 'product' => $product->slug]) }}" wire:navigate
class="group/btn w-full inline-flex items-center justify-center gap-2 bg-background-tertiary hover:bg-background-tertiary/80 border border-neutral/50 text-color-base px-4 py-3 rounded-2xl font-medium transition-all duration-300 hover:shadow-lg">
{{ __('general.view') }}
<x-ri-arrow-right-fill class="size-4 transform transition-transform duration-300 group-hover/btn:translate-x-1" />
</a>
@else
<button disabled
class="w-full inline-flex items-center justify-center gap-2 bg-red-100 dark:bg-red-900/30 border border-red-500 text-red-600 dark:text-red-400 px-4 py-3 rounded-2xl font-medium cursor-not-allowed opacity-75">
{{ __('product.out_of_stock', ['product' => $product->name]) }}
<x-ri-close-circle-fill class="size-4" />
</button>
@endif
</div>
@endif
<div class="absolute inset-0 bg-gradient-to-br {{ $isAvailable ? 'from-primary/5 to-primary/10' : 'from-red-500/5 to-red-500/10' }} opacity-0 group-hover:opacity-100 transition-opacity duration-300 pointer-events-none"></div>
</div>
@endforeach
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,42 @@
<div class="container mx-auto px-4 py-8 sm:px-6 lg:px-8">
<div class="bg-background-secondary rounded-xl shadow-2xl p-6 lg:p-10 border border-neutral
@if ($product->image) grid grid-cols-1 md:grid-cols-2 @endif gap-8 lg:gap-12 items-start">
@if ($product->image)
<div class="relative overflow-hidden rounded-2xl shadow-lg aspect-w-4 aspect-h-3 mb-6 md:mb-0">
<img src="{{ Storage::url($product->image) }}" alt="{{ $product->name }}"
class="w-full h-full object-cover object-center rounded-2xl transition-transform duration-300 hover:scale-105">
</div>
@endif
<div class="flex flex-col gap-8">
@if ($product->stock === 0)
<span class="text-xs font-semibold px-3 py-1 rounded-full bg-red-800 text-red-100 w-fit">
{{ __('product.out_of_stock', ['product' => $product->name]) }}
</span>
@elseif($product->stock > 0)
<span class="text-xs font-semibold px-3 py-1 rounded-full bg-green-800 text-green-100 w-fit">
{{ __('product.in_stock') }}
</span>
@endif
<div>
<h1 class="text-3xl font-extrabold text-color-base mb-2 leading-tight">{{ $product->name }}</h1>
<p class="text-xl font-bold text-primary">{{ $product->price() }}</p>
</div>
@if ($product->description)
<div class="border-t border-neutral pt-4">
<h2 class="text-lg font-bold text-color-base mb-2">{{ __('Description') }}</h2>
<article class="prose dark:prose-invert text-color-muted leading-relaxed">
{!! $product->description !!}
</article>
</div>
@endif
@if ($product->stock !== 0 && $product->price()->available)
<div class="mt-4">
<a href="{{ route('products.checkout', ['category' => $category, 'product' => $product->slug]) }}"
wire:navigate>
<x-button.primary class="w-full sm:w-auto">{{ __('product.add_to_cart') }}</x-button.primary>
</a>
</div>
@endif
</div>
</div>
</div>