wishlist
This commit is contained in:
parent
8ef3a2783a
commit
f2c2cf11ed
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -199,4 +199,6 @@ class ItemReference extends Model
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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">×</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">×</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
|
||||||
|
|
|
||||||
|
|
@ -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');
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue