diff --git a/app/Livewire/Datatable/Catalog/CategoriesTable.php b/app/Livewire/Datatable/Catalog/CategoriesTable.php index 8f08fba..12e9d2f 100644 --- a/app/Livewire/Datatable/Catalog/CategoriesTable.php +++ b/app/Livewire/Datatable/Catalog/CategoriesTable.php @@ -5,13 +5,15 @@ use App\Models\Category; use App\Traits\Datatable\WithConfiguration; use App\Traits\Datatable\WithPrependColumn; +use App\Traits\Media\WithMediaHandler; use Illuminate\Database\Eloquent\Builder; use Rappasoft\LaravelLivewireTables\DataTableComponent; use Rappasoft\LaravelLivewireTables\Views\Column; +use Rappasoft\LaravelLivewireTables\Views\Columns\ImageColumn; class CategoriesTable extends DataTableComponent { - use WithConfiguration, WithPrependColumn; + use WithConfiguration, WithMediaHandler, WithPrependColumn; protected $model = Category::class; @@ -20,6 +22,13 @@ public function columns(): array return [ Column::make('Nama', 'name')->searchable(), + ImageColumn::make('Gambar') + ->location(fn ($row) => optional($row->getMedia('image')->first())->getUrl() ?? asset('assets/images/logo.png')) + ->attributes(fn ($row) => [ + 'style' => 'width: 50px; height: 50px;', + 'alt' => $row->name, + ]), + Column::make('Aksi') ->label(function ($row) { $actions = ''; diff --git a/app/Livewire/Forms/Studio/Catalog/CategoryForm.php b/app/Livewire/Forms/Studio/Catalog/CategoryForm.php index 6c30c57..f5a4cb8 100644 --- a/app/Livewire/Forms/Studio/Catalog/CategoryForm.php +++ b/app/Livewire/Forms/Studio/Catalog/CategoryForm.php @@ -3,6 +3,7 @@ namespace App\Livewire\Forms\Studio\Catalog; use App\Models\Category; +use App\Traits\Media\WithMediaHandler; use App\Traits\Utilities\WithSortOrder; use Illuminate\Support\Facades\DB; use Illuminate\Validation\Rule; @@ -10,7 +11,7 @@ class CategoryForm extends Form { - use WithSortOrder; + use WithMediaHandler, WithSortOrder; public ?Category $category = null; @@ -18,11 +19,14 @@ class CategoryForm extends Form public ?string $description = null; + public array $image = []; + public function rules(): array { return [ 'name' => ['required', 'string', 'max:20', Rule::unique('categories', 'name')->whereNull('deleted_at')->ignore($this->category)], 'description' => 'nullable', + 'image' => 'array', ]; } @@ -31,6 +35,7 @@ public function validationAttributes(): array return [ 'name' => 'nama', 'description' => 'deskripsi', + 'image' => 'gambar', ]; } @@ -40,6 +45,8 @@ public function setCategory(Category $category): void $this->name = $category->name; $this->description = $category->description; + + $this->image = $this->mapMediaCollection($category->getMedia('image')); } public function store(): void @@ -50,11 +57,13 @@ public function store(): void // Increment all existing categories' sort_order Category::query()->increment('sort_order'); - Category::create([ + $category = Category::create([ 'name' => $this->name, 'description' => $this->description, 'sort_order' => 1, ]); + + $this->uploadMedia($this->image, $category, 'image'); }); } @@ -62,10 +71,15 @@ public function update(): void { $this->validate(); - $this->category->update([ - 'name' => $this->name, - 'description' => $this->description, - ]); + DB::transaction(function () { + $this->category->update([ + 'name' => $this->name, + 'description' => $this->description, + ]); + + $this->syncMedia($this->image, $this->category, 'image'); + $this->uploadMedia($this->image, $this->category, 'image'); + }); } public function delete(): void diff --git a/app/Livewire/Home/Index.php b/app/Livewire/Home/Index.php index 8e5a5cf..cf6e449 100644 --- a/app/Livewire/Home/Index.php +++ b/app/Livewire/Home/Index.php @@ -3,6 +3,7 @@ namespace App\Livewire\Home; use App\Models\Brand; +use App\Models\Category; use App\Models\ChooseUs; use App\Models\Customer; use App\Models\Faq; @@ -13,12 +14,15 @@ use Illuminate\Support\Facades\Storage; use Livewire\Attributes\Layout; use Livewire\Component; +use Livewire\WithPagination; #[Layout('components.layouts.home', [ 'title' => 'Beranda', ])] class Index extends Component { + use WithPagination; + public array $bestSellingPerfumes = []; public array $brands = []; @@ -65,13 +69,7 @@ public function mount(): void $this->whyChooseUs = ChooseUs::orderBy('sort_order')->get()->toArray(); - $this->perfumeFeatures = PerfumeFeature::orderBy('sort_order') - ->get() - ->map(fn (PerfumeFeature $perfumeFeature) => [ - 'name' => $perfumeFeature->name, - 'description' => $perfumeFeature->description, - ]) - ->toArray(); + $this->perfumeFeatures = PerfumeFeature::orderBy('sort_order')->get()->toArray(); $this->faqs = Faq::orderBy('sort_order') ->get() @@ -100,6 +98,14 @@ public function render(): View { return view('livewire.home.index', [ 'pageTitle' => 'Beranda', + 'categories' => Category::orderBy('sort_order') + ->paginate(12) + ->through(function (Category $category) { + $category->image = $category->getFirstMediaUrl('image') + ?: asset('assets/images/logo.png'); + + return $category; + }), ]); } } diff --git a/app/Models/Category.php b/app/Models/Category.php index 9fbfe17..ea75674 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -6,13 +6,15 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\SoftDeletes; +use Spatie\MediaLibrary\HasMedia; +use Spatie\MediaLibrary\InteractsWithMedia; use Spatie\Sluggable\HasSlug; use Spatie\Sluggable\SlugOptions; use Veelasky\LaravelHashId\Eloquent\HashableId; -class Category extends Model +class Category extends Model implements HasMedia { - use HasFactory, HashableId, HasSlug, SoftDeletes; + use HasFactory, HashableId, HasSlug, InteractsWithMedia, SoftDeletes; protected $guarded = ['id']; diff --git a/resources/views/livewire/home/index.blade.php b/resources/views/livewire/home/index.blade.php index 87720e0..ab90e9a 100644 --- a/resources/views/livewire/home/index.blade.php +++ b/resources/views/livewire/home/index.blade.php @@ -43,6 +43,23 @@ transform: translateY(0); transition: all .4s ease; } + + @keyframes fadeUp { + from { + opacity: 0; + transform: translateY(12px); + } + + to { + opacity: 1; + transform: translateY(0); + } + } + + .category-item { + opacity: 0; + animation: fadeUp 0.45s ease forwards; + } @endassets @@ -126,11 +143,34 @@ function animate() { link.addEventListener('click', e => { e.preventDefault(); - links.forEach(l => l.classList.remove('bg-home-foreground')); - links.forEach(l => l.classList.add('bg-gray-400')); + const targetPage = new URL(link.href).searchParams.get('page') || '1'; - e.target.classList.remove('bg-gray-400'); - e.target.classList.add('bg-home-foreground'); + // Update all dots (mobile & desktop) + links.forEach(l => { + const lPage = new URL(l.href).searchParams.get('page') || '1'; + const isActive = lPage === targetPage; + + const isMobile = l.closest('nav').classList.contains( + 'lg:hidden'); + + if (isActive) { + l.classList.add('bg-home-primary'); + if (isMobile) { + l.classList.add('w-8'); + l.classList.remove('w-2', 'bg-gray-300'); + } else { + l.classList.add('h-12'); + l.classList.remove('h-6', 'bg-gray-200'); + } + } else { + l.classList.remove('bg-home-primary', 'w-8', 'h-12'); + if (isMobile) { + l.classList.add('bg-gray-300', 'w-2'); + } else { + l.classList.add('bg-gray-200', 'h-6'); + } + } + }); fetch(link.href) .then(res => res.text()) diff --git a/resources/views/livewire/home/partials/benefits.blade.php b/resources/views/livewire/home/partials/benefits.blade.php index 84b3675..12dd0bc 100644 --- a/resources/views/livewire/home/partials/benefits.blade.php +++ b/resources/views/livewire/home/partials/benefits.blade.php @@ -1,77 +1,55 @@ -@php - $accordions = [ - [ - 'title' => 'Aroma Premium Tahan Lama', - 'content' => - 'Setiap varian dirancang menggunakan konsentrat berkualitas tinggi sehingga wanginya bertahan sepanjang hari, cocok dipakai kerja maupun aktivitas luar.', - ], - [ - 'title' => 'Formula Aman untuk Kulit', - 'content' => - 'Parfum kami menggunakan bahan yang lebih lembut di kulit sehingga nyaman digunakan setiap hari tanpa rasa perih atau iritasi.', - ], - [ - 'title' => 'Pilihan Aroma Sangat Beragam', - 'content' => - 'Mulai dari floral, fresh, woody, oriental hingga parfum harian — semua tersedia untuk menyesuaikan gaya dan kepribadianmu.', - ], - [ - 'title' => 'Harga Terjangkau Kualitas Premium', - 'content' => - 'Nikmati parfum berkelas tanpa perlu mengeluarkan biaya tinggi. Value terbaik untuk kebutuhan harian maupun koleksi pribadi.', - ], - ]; -@endphp -
- +
- Botol Parfum Elegan
- +

Keunggulan Produk Kami.

- Setiap parfum dibuat dengan standar tinggi agar kamu mendapatkan kualitas terbaik dan pengalaman wangi yang tak terlupakan. + Setiap parfum dibuat dengan standar tinggi agar kamu mendapatkan kualitas terbaik dan pengalaman + wangi yang tak terlupakan.

- @foreach ($accordions as $index => $acc) + @foreach ($perfumeFeatures as $index => $perfumeFeature)
- - -
- {{ $acc['content'] }} + {{ $perfumeFeature['description'] }}
@@ -82,4 +60,4 @@ class="overflow-hidden text-home-foreground/70 text-base leading-relaxed">
-
\ No newline at end of file + diff --git a/resources/views/livewire/home/partials/categories.blade.php b/resources/views/livewire/home/partials/categories.blade.php index ba38d7e..87dcefc 100644 --- a/resources/views/livewire/home/partials/categories.blade.php +++ b/resources/views/livewire/home/partials/categories.blade.php @@ -1,38 +1,3 @@ -@php - $industries = [ - ['name' => 'AROMA FLORAL'], - ['name' => 'AROMA FRESH'], - ['name' => 'AROMA WOODY'], - ['name' => 'AROMA ORIENTAL'], - ['name' => 'PARFUM HARIAN'], - ['name' => 'PARFUM KERJA'], - ['name' => 'PARFUM MALAM'], - ['name' => 'PARFUM PREMIUM'], - ['name' => 'AROMA SWEET'], - ['name' => 'AROMA FRUITY'], - ['name' => 'AROMA MUSKY'], - ['name' => 'LIMITED EDITION'], - ['name' => 'AROMA CITRUS'], - ['name' => 'AROMA AQUA'], - ['name' => 'AROMA SPICY'], - ['name' => 'PARFUM SPORT'], - ['name' => 'PARFUM CLASSIC'], - ['name' => 'AROMA GREEN'], - ['name' => 'AROMA TEA'], - ['name' => 'PARFUM SUMMER'], - ['name' => 'AROMA TROPICAL'], - ['name' => 'PARFUM LUXURY'], - ['name' => 'AROMA EARTHY'], - ]; - - $perPage = 12; - $page = request('page', 1); - $total = count($industries); - $start = ($page - 1) * $perPage; - $currentItems = array_slice($industries, $start, $perPage); - $totalPages = ceil($total / $perPage); -@endphp -
@@ -51,23 +16,20 @@
- @foreach ($currentItems as $industry) + @foreach ($categories as $category)
+ class="category-item group flex flex-col items-center justify-center py-10 px-4 bg-white rounded-2xl border border-gray-100 hover:border-home-primary hover:shadow-xl hover:shadow-home-primary/5 hover:-translate-y-1 transition-all duration-300 cursor-pointer h-full" + wire:key="category-{{ $category['id'] }}">
- - - + class="w-24 h-24 mb-4 rounded-full bg-gray-50 flex items-center justify-center group-hover:bg-home-primary transition-colors duration-300 overflow-hidden"> + {{ $category['name'] }}

- {{ $industry['name'] }} + {{ $category['name'] }}

@endforeach @@ -76,18 +38,18 @@ class="text-[11px] md:text-xs font-bold tracking-widest text-gray-400 uppercase diff --git a/resources/views/livewire/studio/catalog/categories.blade.php b/resources/views/livewire/studio/catalog/categories.blade.php index 6c05e58..1331054 100644 --- a/resources/views/livewire/studio/catalog/categories.blade.php +++ b/resources/views/livewire/studio/catalog/categories.blade.php @@ -47,6 +47,27 @@ placeholder="Aromaterapi merupakan wewangian yang dapat membantu meredakan stress dan meredakan perasaan sakit kepala." auotocomplete="off" class="**:data-[slot=content]:min-h-[100px]!" /> +
+

Gambar

+
+ + @error('form.image') + + @enderror +
+
+