This commit is contained in:
Bayu Lukman Yusuf 2026-02-27 16:19:51 +07:00
parent 8ef3a2783a
commit f2c2cf11ed
10 changed files with 358 additions and 258 deletions

View File

@ -0,0 +1,84 @@
<?php
namespace App\Http\Controllers;
use App\Repositories\Member\WishlistRepository;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Auth;
class WishController extends Controller
{
protected $wishlistRepository;
public function __construct(WishlistRepository $wishlistRepository)
{
$this->wishlistRepository = $wishlistRepository;
}
/**
* Display the wishlist page
*/
public function index(Request $request)
{
try {
$wishlists = $this->wishlistRepository->getList($request);
return view('account.wishlist', [
'wishlists' => $wishlists,
'user' => Auth::user()
]);
} catch (\Exception $e) {
return redirect()->back()->with('error', 'Failed to load wishlist: ' . $e->getMessage());
}
}
/**
* Add item to wishlist
*/
public function store(Request $request): JsonResponse
{
$request->validate([
'item_id' => 'required|exists:items,id'
]);
try {
$wishlist = $this->wishlistRepository->create([
'item_id' => $request->item_id
]);
return response()->json([
'success' => true,
'message' => 'Item added to wishlist successfully',
'wishlist' => $wishlist
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'message' => 'Failed to add item to wishlist: ' . $e->getMessage()
], 500);
}
}
/**
* Remove item from wishlist
*/
public function destroy(Request $request, $id): JsonResponse
{
try {
$this->wishlistRepository->delete($id);
return response()->json([
'success' => true,
'message' => 'Item removed from wishlist successfully'
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'message' => 'Failed to remove item from wishlist: ' . $e->getMessage()
], 500);
}
}
}

View File

@ -199,4 +199,6 @@ class ItemReference extends Model
return $result; return $result;
} }
} }

View File

@ -355,4 +355,10 @@ class Items extends Model
return 0; return 0;
} }
} }
public function isWishlist() : bool
{
return $this->hasOne(Wishlist::class, 'item_id', 'id')->where('customer_id', auth()->user()->customer->id)->exists();
}
} }

27
app/Models/Wishlist.php Normal file
View File

@ -0,0 +1,27 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Wishlist extends Model
{
use HasFactory;
protected $fillable = [
'customer_id',
'item_id',
];
public function customer(): BelongsTo
{
return $this->belongsTo(Customer::class);
}
public function item(): BelongsTo
{
return $this->belongsTo(Items::class);
}
}

View File

@ -0,0 +1,75 @@
<?php
namespace App\Repositories\Member;
use App\Models\Wishlist;
use App\Models\Customer;
use Illuminate\Support\Facades\DB;
class WishlistRepository
{
public function getList($request)
{
$customer = Customer::where('user_id', auth()->user()->id)->first();
if (!$customer) {
throw new \Exception('Customer not found');
}
$limit = 20;
$wishlist = Wishlist::where('customer_id', $customer->id)
->with([
'item.variants',
])
->orderBy('created_at', 'desc')
->paginate($limit);
return $wishlist;
}
public function create($data)
{
$model = DB::transaction(function () use ($data) {
$customer = Customer::where('user_id', auth()->user()->id)->first();
// Check if item already exists in wishlist
$existing = Wishlist::where('customer_id', $customer->id)
->where('item_id', $data['item_id'])
->first();
if ($existing) {
return $existing; // Return existing item if already in wishlist
}
$model = Wishlist::create([
'customer_id' => $customer->id,
'item_id' => $data['item_id'],
]);
return $model;
});
return $model;
}
public function delete($item_id)
{
$wishlist = DB::transaction(function () use ($item_id) {
$wishlist = Wishlist::where('customer_id', auth()->user()->customer->id)
->where('item_id', $item_id)
->first();
if (!$wishlist) {
throw new \Exception('Wishlist not found');
}
$wishlist->delete();
return $wishlist;
});
return $wishlist;
}
}

View File

@ -7,15 +7,15 @@
<!-- Page title + Add list button--> <!-- Page title + Add list button-->
<div class="d-flex align-items-center justify-content-between pb-3 mb-1 mb-sm-2 mb-md-3"> <div class="d-flex align-items-center justify-content-between pb-3 mb-1 mb-sm-2 mb-md-3">
<h1 class="h2 me-3 mb-0">Wishlist</h1> <h1 class="h2 me-3 mb-0">Wishlist</h1>
<div class="nav"> {{-- <div class="nav">
<a class="nav-link animate-underline px-0 py-1 py-ms-2" data-bs-toggle="modal" href="#wishlistModal"> <a class="nav-link animate-underline px-0 py-1 py-ms-2" data-bs-toggle="modal" href="#wishlistModal">
<i class="ci-plus fs-base me-1"></i> <i class="ci-plus fs-base me-1"></i>
<span class="animate-target">Add wishlist</span> <span class="animate-target">Add wishlist</span>
</a> </a>
</div> </div> --}}
</div> </div>
<!-- Wishlist selector --> <!-- Wishlist selector -->
<div class="border-bottom pb-4 mb-3"> {{-- <div class="border-bottom pb-4 mb-3">
<div class="row align-items-center justify-content-between"> <div class="row align-items-center justify-content-between">
<div class="col-sm-7 col-md-8 col-xxl-9 d-flex align-items-center mb-3 mb-sm-0"> <div class="col-sm-7 col-md-8 col-xxl-9 d-flex align-items-center mb-3 mb-sm-0">
<h5 class="me-2 mb-0">Interesting offers</h5> <h5 class="me-2 mb-0">Interesting offers</h5>
@ -69,9 +69,9 @@
</select> </select>
</div> </div>
</div> </div>
</div> </div> --}}
<!-- Master checkbox + Action buttons --> <!-- Master checkbox + Action buttons -->
<div class="nav align-items-center mb-4"> {{-- <div class="nav align-items-center mb-4">
<div class="form-checkl nav-link animate-underline fs-lg ps-0 pe-2 py-2 mt-n1 me-4" <div class="form-checkl nav-link animate-underline fs-lg ps-0 pe-2 py-2 mt-n1 me-4"
data-master-checkbox='{"container": "#wishlistSelection", "label": "Select all", "labelChecked": "Unselect all", "showOnCheck": "#action-buttons"}'> data-master-checkbox='{"container": "#wishlistSelection", "label": "Select all", "labelChecked": "Unselect all", "showOnCheck": "#action-buttons"}'>
<input checked="" class="form-check-input" id="wishlist-master" type="checkbox" /> <input checked="" class="form-check-input" id="wishlist-master" type="checkbox" />
@ -92,232 +92,14 @@
<span class="animate-target d-none d-md-inline">Remove selected</span> <span class="animate-target d-none d-md-inline">Remove selected</span>
</a> </a>
</div> </div>
</div> </div> --}}
<!-- Wishlist items (Grid) --> <!-- Wishlist items (Grid) -->
<div class="row row-cols-2 row-cols-md-3 g-4" id="wishlistSelection"> <div class="row row-cols-2 row-cols-md-3 g-4" id="wishlistSelection">
<!-- Item -->
<div class="col"> @foreach ($wishlists as $key => $value)
<div class="product-card animate-underline hover-effect-opacity bg-body rounded"> @include('components.home.product-card', ['product' => $value->item])
<div class="position-relative"> @endforeach
<div class="position-absolute top-0 end-0 z-1 pt-1 pe-1 mt-2 me-2">
<div class="form-check fs-lg">
<input checked="" class="form-check-input select-card-check" type="checkbox" />
</div>
</div>
<a class="d-block rounded-top overflow-hidden p-3 p-sm-4"
href="{{ route('second', ['shop', 'product-general-electronics']) }}">
<span
class="badge bg-danger position-absolute top-0 start-0 mt-2 ms-2 mt-lg-3 ms-lg-3">-21%</span>
<div class="ratio" style="--cz-aspect-ratio: calc(240 / 258 * 100%)">
<img alt="VR Glasses" src="/img/shop/electronics/01.png" />
</div>
</a>
</div>
<div class="w-100 min-w-0 px-1 pb-2 px-sm-3 pb-sm-3">
<div class="d-flex align-items-center gap-2 mb-2">
<div class="d-flex gap-1 fs-xs">
<i class="ci-star-filled text-warning"></i>
<i class="ci-star-filled text-warning"></i>
<i class="ci-star-filled text-warning"></i>
<i class="ci-star-filled text-warning"></i>
<i class="ci-star text-body-tertiary opacity-75"></i>
</div>
<span class="text-body-tertiary fs-xs">(123)</span>
</div>
<h3 class="pb-1 mb-2">
<a class="d-block fs-sm fw-medium text-truncate"
href="{{ route('second', ['shop', 'product-general-electronics']) }}">
<span class="animate-target">VRB01 Virtual Reality Glasses</span>
</a>
</h3>
<div class="d-flex align-items-center justify-content-between">
<div class="h5 lh-1 mb-0">$340.99 <del
class="text-body-tertiary fs-sm fw-normal">$430.00</del></div>
<button aria-label="Add to Cart"
class="product-card-button btn btn-icon btn-secondary animate-slide-end ms-2"
type="button">
<i class="ci-shopping-cart fs-base animate-target"></i>
</button>
</div>
</div>
</div>
</div>
<!-- Item -->
<div class="col">
<div class="product-card animate-underline hover-effect-opacity bg-body rounded">
<div class="position-relative">
<div class="position-absolute top-0 end-0 z-1 pt-1 pe-1 mt-2 me-2">
<div class="form-check fs-lg">
<input checked="" class="form-check-input select-card-check" type="checkbox" />
</div>
</div>
<a class="d-block rounded-top overflow-hidden p-3 p-sm-4"
href="{{ route('second', ['shop', 'product-general-electronics']) }}">
<div class="ratio" style="--cz-aspect-ratio: calc(240 / 258 * 100%)">
<img alt="iPhone 14" src="/img/shop/electronics/02.png" />
</div>
</a>
</div>
<div class="w-100 min-w-0 px-1 pb-2 px-sm-3 pb-sm-3">
<div class="d-flex align-items-center gap-2 mb-2">
<div class="d-flex gap-1 fs-xs">
<i class="ci-star-filled text-warning"></i>
<i class="ci-star-filled text-warning"></i>
<i class="ci-star-filled text-warning"></i>
<i class="ci-star-filled text-warning"></i>
<i class="ci-star-half text-warning"></i>
</div>
<span class="text-body-tertiary fs-xs">(142)</span>
</div>
<h3 class="pb-1 mb-2">
<a class="d-block fs-sm fw-medium text-truncate"
href="{{ route('second', ['shop', 'product-general-electronics']) }}">
<span class="animate-target">Apple iPhone 14 128GB White</span>
</a>
</h3>
<div class="d-flex align-items-center justify-content-between">
<div class="h5 lh-1 mb-0">$899.00</div>
<button aria-label="Add to Cart"
class="product-card-button btn btn-icon btn-secondary animate-slide-end ms-2"
type="button">
<i class="ci-shopping-cart fs-base animate-target"></i>
</button>
</div>
</div>
</div>
</div>
<!-- Item -->
<div class="col">
<div class="product-card animate-underline hover-effect-opacity bg-body rounded">
<div class="position-relative">
<div class="position-absolute top-0 end-0 z-1 pt-1 pe-1 mt-2 me-2">
<div class="form-check fs-lg">
<input class="form-check-input select-card-check" type="checkbox" />
</div>
</div>
<a class="d-block rounded-top overflow-hidden p-3 p-sm-4"
href="{{ route('second', ['shop', 'product-general-electronics']) }}">
<div class="ratio" style="--cz-aspect-ratio: calc(240 / 258 * 100%)">
<img alt="Smart Watch" src="/img/shop/electronics/03.png" />
</div>
</a>
</div>
<div class="w-100 min-w-0 px-1 pb-2 px-sm-3 pb-sm-3">
<div class="d-flex align-items-center gap-2 mb-2">
<div class="d-flex gap-1 fs-xs">
<i class="ci-star-filled text-warning"></i>
<i class="ci-star-filled text-warning"></i>
<i class="ci-star-filled text-warning"></i>
<i class="ci-star-filled text-warning"></i>
<i class="ci-star-filled text-warning"></i>
</div>
<span class="text-body-tertiary fs-xs">(67)</span>
</div>
<h3 class="pb-1 mb-2">
<a class="d-block fs-sm fw-medium text-truncate"
href="{{ route('second', ['shop', 'product-general-electronics']) }}">
<span class="animate-target">Smart Watch Series 7, White</span>
</a>
</h3>
<div class="d-flex align-items-center justify-content-between">
<div class="h5 lh-1 mb-0">$429.00</div>
<button aria-label="Add to Cart"
class="product-card-button btn btn-icon btn-secondary animate-slide-end ms-2"
type="button">
<i class="ci-shopping-cart fs-base animate-target"></i>
</button>
</div>
</div>
</div>
</div>
<!-- Item -->
<div class="col">
<div class="product-card animate-underline hover-effect-opacity bg-body rounded">
<div class="posittion-relative">
<div class="position-absolute top-0 end-0 z-1 pt-1 pe-1 mt-2 me-2">
<div class="form-check fs-lg">
<input class="form-check-input select-card-check" type="checkbox" />
</div>
</div>
<a class="d-block rounded-top overflow-hidden p-3 p-sm-4"
href="{{ route('second', ['shop', 'product-general-electronics']) }}">
<div class="ratio" style="--cz-aspect-ratio: calc(240 / 258 * 100%)">
<img alt="iPad Air" src="/img/shop/electronics/05.png" />
</div>
</a>
</div>
<div class="w-100 min-w-0 px-1 pb-2 px-sm-3 pb-sm-3">
<div class="d-flex align-items-center gap-2 mb-2">
<div class="d-flex gap-1 fs-xs">
<i class="ci-star-filled text-warning"></i>
<i class="ci-star-filled text-warning"></i>
<i class="ci-star-filled text-warning"></i>
<i class="ci-star-filled text-warning"></i>
<i class="ci-star-filled text-warning"></i>
</div>
<span class="text-body-tertiary fs-xs">(12)</span>
</div>
<h3 class="pb-1 mb-2">
<a class="d-block fs-sm fw-medium text-truncate"
href="{{ route('second', ['shop', 'product-general-electronics']) }}">
<span class="animate-target">Tablet Apple iPad Air M1</span>
</a>
</h3>
<div class="d-flex align-items-center justify-content-between">
<div class="h5 lh-1 mb-0">$540.00</div>
<button aria-label="Add to Cart"
class="product-card-button btn btn-icon btn-secondary animate-slide-end ms-2"
type="button">
<i class="ci-shopping-cart fs-base animate-target"></i>
</button>
</div>
</div>
</div>
</div>
<!-- Item -->
<div class="col">
<div class="product-card animate-underline hover-effect-opacity bg-body rounded">
<div class="position-relative">
<div class="position-absolute top-0 end-0 z-1 pt-1 pe-1 mt-2 me-2">
<div class="form-check fs-lg">
<input class="form-check-input select-card-check" type="checkbox" />
</div>
</div>
<a class="d-block rounded-top overflow-hidden p-3 p-sm-4"
href="{{ route('second', ['shop', 'product-general-electronics']) }}">
<div class="ratio" style="--cz-aspect-ratio: calc(240 / 258 * 100%)">
<img alt="AirPods 2" src="/img/shop/electronics/06.png" />
</div>
</a>
</div>
<div class="w-100 min-w-0 px-1 pb-2 px-sm-3 pb-sm-3">
<div class="d-flex align-items-center gap-2 mb-2">
<div class="d-flex gap-1 fs-xs">
<i class="ci-star-filled text-warning"></i>
<i class="ci-star-filled text-warning"></i>
<i class="ci-star-filled text-warning"></i>
<i class="ci-star-filled text-warning"></i>
<i class="ci-star text-body-tertiary opacity-75"></i>
</div>
<span class="text-body-tertiary fs-xs">(78)</span>
</div>
<h3 class="pb-1 mb-2">
<a class="d-block fs-sm fw-medium text-truncate"
href="{{ route('second', ['shop', 'product-general-electronics']) }}">
<span class="animate-target">Headphones Apple AirPods 2 Pro</span>
</a>
</h3>
<div class="d-flex align-items-center justify-content-between">
<div class="h5 lh-1 mb-0">$224.00</div>
<button aria-label="Add to Cart"
class="product-card-button btn btn-icon btn-secondary animate-slide-end ms-2"
type="button">
<i class="ci-shopping-cart fs-base animate-target"></i>
</button>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -33,8 +33,8 @@
@endif @endif
</div> </div>
<div class="position-relative"> <div class="position-relative">
@if ($product->variants->count() > 1) @if (($product->variants?->count() ?? 0) > 1)
<div class=" fs-xs text-body-secondary opacity-100">+{{ $product->variants->count() - 1 }} Varian</div> <div class=" fs-xs text-body-secondary opacity-100">+{{ ($product->variants->count() ?? 0) - 1 }} Varian</div>
@endif @endif
{{-- <div class="hover-effect-target fs-xs text-body-secondary opacity-100">+1 color</div> --}} {{-- <div class="hover-effect-target fs-xs text-body-secondary opacity-100">+1 color</div> --}}
{{-- <div class="hover-effect-target d-flex gap-2 position-absolute top-0 start-0 opacity-0"> {{-- <div class="hover-effect-target d-flex gap-2 position-absolute top-0 start-0 opacity-0">

View File

@ -51,7 +51,7 @@
{{-- <span class="badge bg-primary rounded-pill ms-auto">1</span> --}} {{-- <span class="badge bg-primary rounded-pill ms-auto">1</span> --}}
</a> </a>
<a class="list-group-item list-group-item-action d-flex align-items-center" <a class="list-group-item list-group-item-action d-flex align-items-center"
href="{{ route('second', ['account', 'wishlist']) }}"> href="{{ route('wishlist.index') }}">
<i class="ci-heart fs-base opacity-75 me-2"></i> <i class="ci-heart fs-base opacity-75 me-2"></i>
{{ __('account_sidebar.wishlist') }} {{ __('account_sidebar.wishlist') }}
</a> </a>

View File

@ -1,9 +1,6 @@
@extends('layouts.landing', ['title' => $product->name ?? 'Detail']) @extends('layouts.landing', ['title' => $product->name ?? 'Detail'])
@section('content') @section('content')
<x-layout.header-grocery /> <x-layout.header-grocery />
<main class="content-wrapper"> <main class="content-wrapper">
@ -73,9 +70,9 @@
</div> </div>
</div> </div>
<button type="button" class="btn btn-lg btn-outline-secondary w-100 collapsed d-md-none" <button type="button" class="btn btn-lg btn-outline-secondary w-100 collapsed d-md-none"
data-bs-toggle="collapse" data-bs-target="#morePictures" data-bs-toggle="collapse" data-bs-target="#morePictures" data-label-collapsed="Show more pictures"
data-label-collapsed="Show more pictures" data-label-expanded="Show less pictures" data-label-expanded="Show less pictures" aria-expanded="false" aria-controls="morePictures"
aria-expanded="false" aria-controls="morePictures" aria-label="Show / hide pictures"> aria-label="Show / hide pictures">
<i class="collapse-toggle-icon ci-chevron-down fs-lg ms-2 me-n2"></i> <i class="collapse-toggle-icon ci-chevron-down fs-lg ms-2 me-n2"></i>
</button> </button>
</div> </div>
@ -98,17 +95,35 @@
</a> --}} </a> --}}
<!-- Title --> <!-- Title -->
<h1 class="h3">{{ $product->name }}</h1> <div class="d-flex gap-3">
<div class=" flex-1">
<h1 class="h3">{{ $product->name }}</h1>
</div>
{{-- wishlist/love button --}}
<button type="button"
class="btn btn-outline-danger gap-2"
style="height: 50px;"
id="wishlist-btn"
data-item-id="{{ $product->id }}"
data-is-wishlist="{{ $product->isWishlist() ? 'true' : 'false' }}">
<i class="{{ $product->isWishlist() == true ? 'ci-heart-filled' : 'ci-heart' }}"></i>
</button>
</div>
{{-- add category --}} {{-- add category --}}
<div class="mb-4 gap-2"> <div class="mb-4 gap-2">
<a href="{{ route('product.index',['filter[category_id]' => $product->category->id]) }}" <a href="{{ route('product.index', ['filter[category_id]' => $product->category->id]) }}"
class="text-decoration-none text-body-emphasis animate-underline text-xs border border-secondary rounded-pill px-2 py-1" style="font-size:10pt;">{{ $product->category->name }}</a> class="text-decoration-none text-body-emphasis animate-underline text-xs border border-secondary rounded-pill px-2 py-1"
style="font-size:10pt;">{{ $product->category->name }}</a>
<a href="{{ route('product.index',['filter[brand_id]' => $product->brand->id]) }}"
class="text-decoration-none text-body-emphasis animate-underline text-xs border border-secondary rounded-pill px-2 py-1" style="font-size:10pt;">{{ $product->brand->name }}</a> <a href="{{ route('product.index', ['filter[brand_id]' => $product->brand->id]) }}"
</div> class="text-decoration-none text-body-emphasis animate-underline text-xs border border-secondary rounded-pill px-2 py-1"
style="font-size:10pt;">{{ $product->brand->name }}</a>
</div>
<!-- Description --> <!-- Description -->
<p class="fs-sm mb-0">{!! nl2br(Str::limit($product->description, 500)) !!}</p> <p class="fs-sm mb-0">{!! nl2br(Str::limit($product->description, 500)) !!}</p>
@ -144,8 +159,8 @@
@foreach ($product->variants as $key => $variant) @foreach ($product->variants as $key => $variant)
<input type="radio" class="btn-check" name="colors" <input type="radio" class="btn-check" name="colors"
value="{{ $variant->reference->id }}" value="{{ $variant->reference->id }}" id="variant-id-{{ $variant->id }}"
id="variant-id-{{ $variant->id }}" {{ $key == 0 ? 'checked' : '' }}> {{ $key == 0 ? 'checked' : '' }}>
<label for="variant-id-{{ $variant->id }}" class="btn btn-image p-0" <label for="variant-id-{{ $variant->id }}" class="btn btn-image p-0"
data-label="{{ $variant->description }}" data-label="{{ $variant->description }}"
data-price="Rp {{ number_format($product->display_price, 0, ',', '.') }}" data-price="Rp {{ number_format($product->display_price, 0, ',', '.') }}"
@ -161,11 +176,7 @@
</div> </div>
</div> </div>
{{-- <button type="button" class="btn btn-outline-secondary gap-2"> {{--
<i class="ci-heart"></i>
Add to wishlist
</button>
<br> <br>
<br> --}} <br> --}}
@ -230,7 +241,7 @@
</div> --}} </div> --}}
{{-- add button wishlist --}} {{-- add button wishlist --}}
</div> </div>
</div> </div>
</div> </div>
@ -1026,7 +1037,7 @@
</footer> --}} </footer> --}}
@include('layouts.partials.footer2') @include('layouts.partials.footer2')
@endsection @endsection
@section('scripts') @section('scripts')
@ -1051,13 +1062,13 @@
} }
document.querySelector('.product-main-image').src = image; document.querySelector('.product-main-image').src = image;
document.querySelector('.variantOption').textContent = labelText; document.querySelector('.variantOption').textContent = labelText;
// Update AddToCart component with new variant ID // Update AddToCart component with new variant ID
const addToCartBtn = document.querySelector('[data-item-reference-id]'); const addToCartBtn = document.querySelector('[data-item-id]');
if (addToCartBtn) { if (addToCartBtn) {
console.log(this.value); console.log(this.value);
var item_reference_id = this.value; var item_id = this.value;
addToCartBtn.setAttribute('data-item-reference-id', item_reference_id); addToCartBtn.setAttribute('data-item-id', item_id);
} }
}); });
}); });
@ -1067,5 +1078,108 @@
checkedRadio.dispatchEvent(new Event('change')); checkedRadio.dispatchEvent(new Event('change'));
} }
}); });
// Wishlist button functionality
document.addEventListener('DOMContentLoaded', function() {
const wishlistBtn = document.getElementById('wishlist-btn');
if (wishlistBtn) {
wishlistBtn.addEventListener('click', function() {
const itemId = this.getAttribute('data-item-id');
const isWishlist = this.getAttribute('data-is-wishlist') === 'true';
const icon = this.querySelector('i');
const text = this.querySelector('span');
// Get CSRF token
const token = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
if (isWishlist) {
// Remove from wishlist
fetch('/account/wishlist/' + itemId, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': token,
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Update button state
this.setAttribute('data-is-wishlist', 'false');
icon.className = 'ci-heart';
text.textContent = 'Add to Wishlist';
// Show success message
const successAlert = document.createElement('div');
successAlert.className = 'alert alert-success alert-dismissible fade show position-fixed';
successAlert.style.css = 'top: 20px; right: 20px; z-index: 9999; max-width: 300px;';
successAlert.innerHTML = `
<button type="button" class="btn-close" data-bs-dismiss="alert">&times;</button>
Item removed from wishlist!
`;
document.body.appendChild(successAlert);
// Auto-hide after 3 seconds
setTimeout(() => {
if (successAlert.parentNode) {
successAlert.parentNode.removeChild(successAlert);
}
}, 3000);
} else {
console.error('Failed to remove from wishlist:', data.message);
}
})
.catch(error => {
console.error('Error removing from wishlist:', error);
});
} else {
// Add to wishlist
fetch('/account/wishlist', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': token,
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify({
item_id: itemId
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Update button state
this.setAttribute('data-is-wishlist', 'true');
icon.className = 'ci-heart-filled';
text.textContent = 'In Wishlist';
// Show success message
const successAlert = document.createElement('div');
successAlert.className = 'alert alert-success alert-dismissible fade show position-fixed';
successAlert.style.css = 'top: 20px; right: 20px; z-index: 9999; max-width: 300px;';
successAlert.innerHTML = `
<button type="button" class="btn-close" data-bs-dismiss="alert">&times;</button>
Item added to wishlist!
`;
document.body.appendChild(successAlert);
// Auto-hide after 3 seconds
setTimeout(() => {
if (successAlert.parentNode) {
successAlert.parentNode.removeChild(successAlert);
}
}, 3000);
} else {
console.error('Failed to add to wishlist:', data.message);
}
})
.catch(error => {
console.error('Error adding to wishlist:', error);
});
}
});
}
});
</script> </script>
@endsection @endsection

View File

@ -19,6 +19,7 @@ use App\Http\Controllers\TncController;
use App\Http\Controllers\HelpController; use App\Http\Controllers\HelpController;
use App\Http\Controllers\VoucherEventController; use App\Http\Controllers\VoucherEventController;
use App\Http\Controllers\ContactController; use App\Http\Controllers\ContactController;
use App\Http\Controllers\WishController;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
@ -136,6 +137,15 @@ Route::middleware(['auth'])->prefix('/orders')->group(function () {
Route::get('/', [OrderController::class, 'index'])->name('orders'); Route::get('/', [OrderController::class, 'index'])->name('orders');
}); });
Route::middleware(['auth'])->prefix('/account/wishlist')->group(function () {
Route::get('/', [WishController::class, 'index'])->name('wishlist.index');
Route::post('/', [WishController::class, 'store'])->name('wishlist.store');
Route::delete('/{id}', [WishController::class, 'destroy'])->name('wishlist.destroy');
});
Route::get('/terms-and-conditions', [TncController::class, 'index'])->name('terms-and-conditions'); Route::get('/terms-and-conditions', [TncController::class, 'index'])->name('terms-and-conditions');
Route::get('/help', [HelpController::class, 'index'])->name('help'); Route::get('/help', [HelpController::class, 'index'])->name('help');
Route::get('/contact', [ContactController::class, 'index'])->name('contact'); Route::get('/contact', [ContactController::class, 'index'])->name('contact');