feat: Dynamically load best-selling perfumes in jumbotron and remove hardcoded testimonials.
This commit is contained in:
parent
9bd6997359
commit
29817744b3
@ -6,7 +6,9 @@
|
||||
use App\Models\ChooseUs;
|
||||
use App\Models\Customer;
|
||||
use App\Models\Faq;
|
||||
use App\Models\Perfume;
|
||||
use App\Models\PerfumeFeature;
|
||||
use App\Models\Testimonial;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
@ -30,6 +32,8 @@ class Index extends Component
|
||||
|
||||
public $latestMembers = [];
|
||||
|
||||
public array $testimonials = [];
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->brands = Brand::orderBy('sort_order')
|
||||
@ -70,6 +74,30 @@ public function mount(): void
|
||||
->inRandomOrder()
|
||||
->take(3)
|
||||
->get();
|
||||
|
||||
$this->testimonials = Testimonial::with(['user', 'user.customer'])
|
||||
->show()
|
||||
->latest()
|
||||
->take(5)
|
||||
->get()
|
||||
->toArray();
|
||||
|
||||
$this->bestSellingPerfumes = Perfume::withSum(['items' => function ($query) {
|
||||
$query->whereHas('order', function ($q) {
|
||||
$q->paid();
|
||||
});
|
||||
}], 'quantity')
|
||||
->orderByDesc('items_sum_quantity')
|
||||
->take(2)
|
||||
->get()
|
||||
->map(function (Perfume $perfume) {
|
||||
$perfume->image = $perfume->getFirstMediaUrl('image') ? Storage::url($perfume->getFirstMediaUrl('image')) : asset('assets/images/logo.png');
|
||||
$perfume->concentration_label = $perfume->concentration->label();
|
||||
|
||||
return $perfume;
|
||||
})
|
||||
->filter(fn (Perfume $perfume) => $perfume->items_sum_quantity > 0)
|
||||
->toArray();
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
@ -88,4 +116,6 @@ public function goToVoucher(): RedirectResponse
|
||||
{
|
||||
return redirect('/voucher');
|
||||
}
|
||||
|
||||
public $bestSellingPerfumes = [];
|
||||
}
|
||||
|
||||
@ -20,26 +20,30 @@ class="border-2 border-home-background w-8 h-8 lg:w-10 lg:h-10 rounded-full over
|
||||
@endif
|
||||
|
||||
<h1 class="text-home-foreground text-4xl lg:text-6xl leading-tight text-start font-bold">
|
||||
Discover your best<br><span class="italic font-serif font-light text-home-primary">perfume</span> at today
|
||||
Temukan<br>
|
||||
<span class="italic font-serif font-light text-home-primary">
|
||||
signature scent-mu
|
||||
</span> hari ini
|
||||
</h1>
|
||||
|
||||
<p class="text-lg w-[90%] lg:w-full lg:text-lg text-home-foreground/80">
|
||||
Selecting a perfume is a personal journey. Consider your lifestyle, favorite scents, and the impression you
|
||||
want
|
||||
to make.
|
||||
Setiap parfum punya cerita. Pilih aroma yang sesuai dengan gaya hidupmu dan kesan yang ingin kamu tampilkan.
|
||||
</p>
|
||||
|
||||
<div class="flex items-center">
|
||||
<button
|
||||
<a href="{{ route('product.perfume') }}" wire:navigate
|
||||
class="bg-home-primary text-home-background py-3 px-6 rounded-full hover:shadow-lg hover:shadow-home-primary/30 transition-all font-medium">
|
||||
Explore Shop
|
||||
</button>
|
||||
Jelajahi Toko
|
||||
</a>
|
||||
<span class="w-8 h-[2px] bg-home-primary/20"></span>
|
||||
<button
|
||||
class="flex items-center justify-center w-12 h-12 border border-home-primary rounded-full hover:bg-home-background transition-colors group bg-transparent">
|
||||
<div <path stroke-linecap="round" stroke-linejoin="round" d="m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25" />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2"
|
||||
stroke="currentColor" class="w-5 h-5 text-home-primary">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
@include('livewire.home.partials.jumbotron.testimonial')
|
||||
</section>
|
||||
|
||||
@ -3,74 +3,47 @@ class="relative w-full lg:w-[65%] min-h-[600px] lg:min-h-screen hidden lg:flex f
|
||||
|
||||
<div
|
||||
class="absolute inset-0 bg-[url('https://images.unsplash.com/photo-1591925463023-1ca6b0636780?q=80&w=1632&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D')] bg-cover bg-center bg-no-repeat">
|
||||
<div class="absolute inset-0 bg-gradient-to-t from-home-background/90 via-home-background/50 to-transparent lg:hidden"></div>
|
||||
<div
|
||||
class="absolute inset-0 bg-gradient-to-t from-home-background/90 via-home-background/50 to-transparent lg:hidden">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="relative w-full px-6 lg:px-0 lg:w-[80%] grid grid-cols-1 lg:grid-cols-2 gap-6 mb-24 lg:mb-0">
|
||||
<div class="relative w-full px-6 lg:px-0 grid grid-cols-1 lg:grid-cols-2 gap-6 mb-24 lg:mb-0">
|
||||
|
||||
<article
|
||||
class="bg-white backdrop-blur-md rounded-2xl shadow-lg hover:shadow-xl transition-shadow flex flex-row items-center p-4 h-auto lg:h-[250px] group">
|
||||
@foreach ($bestSellingPerfumes as $perfume)
|
||||
<article
|
||||
class="bg-white backdrop-blur-md rounded-2xl shadow-lg hover:shadow-xl transition-shadow flex flex-row items-center p-4 h-auto lg:h-[250px] group">
|
||||
|
||||
<figure class="w-[40%] lg:w-auto h-32 lg:h-full overflow-hidden rounded-xl bg-gray-100">
|
||||
<img src="https://images.unsplash.com/photo-1693960794377-b388be4227c2?q=80&w=880&auto=format&fit=crop"
|
||||
class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
|
||||
alt="Product One">
|
||||
</figure>
|
||||
<figure class="w-[40%] lg:w-auto h-32 lg:h-full overflow-hidden rounded-xl bg-gray-100">
|
||||
<img src="{{ $perfume['image'] }}"
|
||||
class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
|
||||
alt="{{ $perfume['name'] }}">
|
||||
</figure>
|
||||
|
||||
<div class="flex-1 ml-4 lg:ml-6 flex flex-col justify-center space-y-2 lg:space-y-4">
|
||||
<div>
|
||||
<h3 class="text-lg lg:text-2xl text-black font-bold">Product One</h3>
|
||||
<p class="text-black/60 text-xs lg:text-lg">Fresh and elegant.</p>
|
||||
<div class="flex-1 ml-4 lg:ml-6 flex flex-col justify-center space-y-2 lg:space-y-4">
|
||||
<div>
|
||||
<h3 class="text-lg lg:text-2xl text-black font-bold line-clamp-1">{{ $perfume['name'] }}</h3>
|
||||
<p class="text-black/60 text-xs lg:text-lg line-clamp-2">
|
||||
{{ $perfume['concentration_label'] }}</p>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2 mt-auto">
|
||||
<button wire:click="goToProduct"
|
||||
class="flex-1 justify-center items-center border border-gray-200 py-2 px-3 bg-home-primary rounded-full text-xs lg:text-sm font-medium hover:bg-home-primary/80 transition-colors text-home-background">
|
||||
Belanja Sekarang
|
||||
</button>
|
||||
<button wire:click="goToProduct"
|
||||
class="w-8 h-8 lg:w-10 lg:h-10 bg-home-primary flex justify-center items-center rounded-full hover:bg-home-primary/80 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" class="size-4 lg:size-5 text-home-background">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2 mt-auto">
|
||||
<button
|
||||
class="flex-1 justify-center items-center border border-gray-200 py-2 px-3 bg-home-primary rounded-full text-xs lg:text-sm font-medium hover:bg-black hover:text-home-background transition-colors text-home-background">
|
||||
Shop Now
|
||||
</button>
|
||||
<button
|
||||
class="w-8 h-8 lg:w-10 lg:h-10 bg-home-primary flex justify-center items-center rounded-full hover:bg-gray-800 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="size-4 lg:size-5 text-home-background">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article
|
||||
class="bg-white backdrop-blur-md rounded-2xl shadow-lg hover:shadow-xl transition-shadow flex flex-row items-center p-4 h-auto lg:h-[250px] group">
|
||||
|
||||
<figure class="w-[40%] lg:w-auto h-32 lg:h-full overflow-hidden rounded-xl bg-gray-100">
|
||||
<img src="https://images.unsplash.com/photo-1693960794377-b388be4227c2?q=80&w=880&auto=format&fit=crop"
|
||||
class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
|
||||
alt="Product One">
|
||||
</figure>
|
||||
|
||||
<div class="flex-1 ml-4 lg:ml-6 flex flex-col justify-center space-y-2 lg:space-y-4">
|
||||
<div>
|
||||
<h3 class="text-lg lg:text-2xl text-black font-bold">Product Two</h3>
|
||||
<p class="text-black/60 text-xs lg:text-lg">Woody & classic.</p>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2 mt-auto">
|
||||
<button
|
||||
class="flex-1 justify-center items-center border border-gray-200 py-2 px-3 bg-home-primary rounded-full text-xs lg:text-sm font-medium hover:bg-black hover:text-home-background transition-colors text-home-background">
|
||||
Shop Now
|
||||
</button>
|
||||
<button
|
||||
class="w-8 h-8 lg:w-10 lg:h-10 bg-home-primary flex justify-center items-center rounded-full hover:bg-gray-800 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="size-4 lg:size-5 text-home-background">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</article>
|
||||
@endforeach
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@ -1,115 +1,85 @@
|
||||
<article
|
||||
class="relative w-full mt-auto h-[300px] lg:h-[250px] bg-home-background/40 backdrop-blur-md border border-home-secondary rounded-2xl z-30 shadow-sm overflow-hidden transition-all duration-300">
|
||||
<div class="mb-5 lg:mb-0">
|
||||
@if (!empty($testimonials))
|
||||
<article x-data="{
|
||||
current: 0,
|
||||
total: {{ count($testimonials) }},
|
||||
timer: null,
|
||||
start() {
|
||||
this.timer = setInterval(() => this.next(), 6000);
|
||||
},
|
||||
stop() {
|
||||
clearInterval(this.timer);
|
||||
},
|
||||
next() {
|
||||
this.current = (this.current + 1) % this.total;
|
||||
this.stop();
|
||||
this.start();
|
||||
},
|
||||
prev() {
|
||||
this.current = (this.current - 1 + this.total) % this.total;
|
||||
this.stop();
|
||||
this.start();
|
||||
},
|
||||
getClass(index) {
|
||||
const isCurrent = this.current === index;
|
||||
let isBefore = index < this.current;
|
||||
|
||||
if (this.current === 0 && index === this.total - 1) isBefore = true;
|
||||
if (this.current === this.total - 1 && index === 0) isBefore = false;
|
||||
|
||||
return {
|
||||
'opacity-100 z-10 translate-x-0': isCurrent,
|
||||
'opacity-0 z-0': !isCurrent,
|
||||
'-translate-x-full': !isCurrent && isBefore,
|
||||
'translate-x-full': !isCurrent && !isBefore
|
||||
};
|
||||
}
|
||||
}" x-init="start()"
|
||||
class="relative w-full mt-auto h-[300px] lg:h-[250px] bg-home-background/40 backdrop-blur-md border border-home-secondary rounded-2xl z-30 shadow-sm overflow-hidden transition-all duration-300">
|
||||
|
||||
<div id="testimonial-track" class="relative w-full h-full py-0 px-6">
|
||||
|
||||
<div
|
||||
class="testimonial-slide absolute inset-0 py-0 px-6 flex flex-col justify-center transition-all duration-500 ease-in-out opacity-100 z-10 translate-x-0">
|
||||
<div class="flex items-center gap-4 mb-3">
|
||||
<img src="https://images.unsplash.com/photo-1494790108377-be9c29b29330?auto=format&fit=crop&q=80&w=100"
|
||||
class="w-12 h-12 rounded-full object-cover ring-2 ring-home-background">
|
||||
<div>
|
||||
<h4 class="font-bold text-home-foreground text-sm lg:text-base">Sarah Jenkins</h4>
|
||||
<div class="flex text-home-primary text-xs">★★★★★</div>
|
||||
</div>
|
||||
<div class="relative w-full h-full py-0 px-6">
|
||||
@foreach ($testimonials as $index => $testimonial)
|
||||
<div class="testimonial-slide absolute inset-0 py-0 px-6 flex flex-col justify-center transition-all duration-500 ease-in-out {{ $index === 0 ? 'opacity-100 z-10 translate-x-0' : 'opacity-0 z-0 translate-x-full' }}"
|
||||
:class="getClass({{ $index }})">
|
||||
<div class="flex items-center gap-4 mb-3">
|
||||
<img src="https://ui-avatars.com/api/?name={{ urlencode($testimonial['user']['customer']['name']) }}&background=random&color=fff"
|
||||
alt="{{ $testimonial['user']['customer']['name'] }}"
|
||||
class="w-12 h-12 rounded-full object-cover ring-2 ring-home-background">
|
||||
<div>
|
||||
<h4 class="font-bold text-home-foreground text-sm lg:text-base">
|
||||
{{ $testimonial['user']['customer']['name'] }}</h4>
|
||||
<div class="flex text-home-primary text-xs">
|
||||
@for ($i = 0; $i < $testimonial['rating']; $i++)
|
||||
★
|
||||
@endfor
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-home-neutral text-base lg:text-lg italic leading-relaxed">
|
||||
"{{ $testimonial['content'] }}"
|
||||
</p>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
<p class="text-home-neutral text-base lg:text-lg italic leading-relaxed">"Aroma yang sangat elegan dan
|
||||
tahan lama. Saya sangat menyukainya untuk dipakai sehari-hari ke kantor."</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="testimonial-slide absolute inset-0 py-0 px-6 flex flex-col justify-center transition-all duration-500 ease-in-out opacity-0 z-0 translate-x-full">
|
||||
<div class="flex items-center gap-4 mb-3">
|
||||
<img src="https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?auto=format&fit=crop&q=80&w=100"
|
||||
class="w-12 h-12 rounded-full object-cover ring-2 ring-home-background">
|
||||
<div>
|
||||
<h4 class="font-bold text-home-foreground text-sm lg:text-base">Michael Chen</h4>
|
||||
<div class="flex text-home-primary text-xs">★★★★★</div>
|
||||
</div>
|
||||
<div class="absolute bottom-4 right-4 flex items-center gap-2 z-30">
|
||||
<button @click="prev()"
|
||||
class="w-10 h-10 rounded-full bg-home-background/80 backdrop-blur border border-home-background shadow-sm hover:bg-home-background flex justify-center items-center transition-colors group">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-4 h-4 text-home-primary group-hover:scale-110 transition-transform" fill="none"
|
||||
viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
<button @click="next()"
|
||||
class="w-10 h-10 rounded-full bg-home-primary shadow-md hover:bg-home-primary/80 flex justify-center items-center transition-colors group">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-4 h-4 text-home-background group-hover:scale-110 transition-transform" fill="none"
|
||||
viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<p class="text-home-neutral text-base lg:text-lg italic leading-relaxed">"Pengiriman sangat cepat dan
|
||||
packaging aman. Wanginya persis seperti deskripsi di website."</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="testimonial-slide absolute inset-0 py-0 px-6 flex flex-col justify-center transition-all duration-500 ease-in-out opacity-0 z-0 translate-x-full">
|
||||
<div class="flex items-center gap-4 mb-3">
|
||||
<img src="https://images.unsplash.com/photo-1438761681033-6461ffad8d80?auto=format&fit=crop&q=80&w=100"
|
||||
class="w-12 h-12 rounded-full object-cover ring-2 ring-home-background">
|
||||
<div>
|
||||
<h4 class="font-bold text-home-foreground text-sm lg:text-base">Jessica Tan</h4>
|
||||
<div class="flex text-home-primary text-xs">★★★★★</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-home-neutral text-base lg:text-lg italic leading-relaxed">"Varian Product Two adalah
|
||||
favorit saya! Sangat fresh dan tidak menyengat. Recommended!"</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="absolute bottom-4 right-4 flex items-center gap-2 z-30">
|
||||
<button id="prev-testi"
|
||||
class="w-10 h-10 rounded-full bg-home-background/80 backdrop-blur border border-home-background shadow-sm hover:bg-home-background flex justify-center items-center transition-colors group">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-4 h-4 text-home-primary group-hover:scale-110 transition-transform" fill="none"
|
||||
viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
<button id="next-testi"
|
||||
class="w-10 h-10 rounded-full bg-home-primary shadow-md hover:bg-home-primary/80 flex justify-center items-center transition-colors group">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-4 h-4 text-home-background group-hover:scale-110 transition-transform" fill="none"
|
||||
viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</article>
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const slides = document.querySelectorAll('.testimonial-slide');
|
||||
const prevBtn = document.getElementById('prev-testi');
|
||||
const nextBtn = document.getElementById('next-testi');
|
||||
let currentIndex = 0;
|
||||
|
||||
function updateSlides() {
|
||||
slides.forEach((slide, index) => {
|
||||
if (index === currentIndex) {
|
||||
slide.classList.remove('opacity-0', 'translate-x-full', '-translate-x-full', 'z-0');
|
||||
slide.classList.add('opacity-100', 'translate-x-0', 'z-10');
|
||||
} else {
|
||||
slide.classList.remove('opacity-100', 'translate-x-0', 'z-10');
|
||||
slide.classList.add('opacity-0', 'z-0');
|
||||
|
||||
if (index < currentIndex) {
|
||||
slide.classList.add('-translate-x-full');
|
||||
slide.classList.remove('translate-x-full');
|
||||
} else {
|
||||
slide.classList.add('translate-x-full');
|
||||
slide.classList.remove('-translate-x-full');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (nextBtn && prevBtn) {
|
||||
nextBtn.addEventListener('click', () => {
|
||||
currentIndex = (currentIndex + 1) % slides.length;
|
||||
updateSlides();
|
||||
});
|
||||
|
||||
prevBtn.addEventListener('click', () => {
|
||||
currentIndex = (currentIndex - 1 + slides.length) % slides.length;
|
||||
updateSlides();
|
||||
});
|
||||
}
|
||||
|
||||
setInterval(() => {
|
||||
currentIndex = (currentIndex + 1) % slides.length;
|
||||
updateSlides();
|
||||
}, 6000);
|
||||
});
|
||||
</script>
|
||||
</article>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user