Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,30 @@
composer require rapidez/multiple-wishlists
```

Then you should run the migrations as this will add the `rapidez_wishlist` and `rapidez_wishlist_item` table in order to save multiple wishlists.
```shell
php artisan migrate
```

It's not recommended to publish every view, rather you should overwrite only the files necessary. However, you can still publish all of the views with the following command:
```
php artisan vendor:publish --provider="Rapidez\MultipleWishlist\MultipleWishlistServiceProvider" --tag=views
```

You also should probably add a new "wishlists" button to the Rapidez account menu, if you use it in your project (which is in `rapidez/account/resources/views/partials/menu.blade.php`)

## Usage

You can add a wishlist button to the product pages by adding:
```blade
<x-rapidez-mw::button.wishlist product-id="item.entity_id"/>
{{-- Or: --}}
<x-rapidez-mw::button.wishlist product-id="{{ $product->entity_id }}"/>
```
depending on if it's in a listing or the current product on the PDP.

`/account/wishlists` will show your wishlists.

## API endpoints
The API uses mostly Laravel apiResource endpoints. All of the exposed endpoints can be found below. Note that every request except for `GET /wishlists/shared/{token}` requires a bearer token header for authorization. This is the magento oauth token of the customer.

Expand Down
33 changes: 17 additions & 16 deletions resources/js/Wishlist.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import { useShare } from '@vueuse/core'
import { wishlists, create, remove, update, addItem, removeItem } from './stores/useWishlists'
import { refresh as refreshCart } from 'Vendor/rapidez/core/resources/js/stores/useCart'
import { mask as cartMask } from 'Vendor/rapidez/core/resources/js/stores/useMask'

import wishlistItem from './WishlistItem.vue'
Vue.component('wishlist-item', wishlistItem)

export default {
components: [
wishlistItem
],
props: {
wishlistId: {
type: Number,
Expand All @@ -16,14 +17,14 @@ export default {
sharedId: String,
},

mounted() {
if(this.sharedId) {
this.fetchShared()
}
emits: ['update:modelValue'],

setup() {
const { share, isSupported } = useShare()
this.shareFn = share
this.isSupported = isSupported
return {
shareFn: share,
isSupported,
}
},

data() {
Expand All @@ -32,14 +33,13 @@ export default {
added: false,
editing: null,
sharedWishlist: null,

shareFn: null,
isSupported: null,
wishlists: wishlists
}
},

methods: {
isWishlisted(productId) {
console.log(wishlists, this.wishlists)
return this.wishlists && Array.isArray(this.wishlists) && this.wishlists.some(e => this.findItem(e, productId))
},

Expand Down Expand Up @@ -138,10 +138,6 @@ export default {
},

computed: {
wishlists() {
return wishlists.value
},

wishlist() {
if (this.wishlistId) {
return this.getWishlist(this.wishlistId)
Expand All @@ -162,7 +158,12 @@ export default {
},

render() {
return this.$scopedSlots.default(this)
return this.$slots.default(this)
},
mounted() {
if(this.sharedId) {
this.fetchShared()
}
},
}
</script>
2 changes: 1 addition & 1 deletion resources/js/WishlistItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default {
},

render() {
return this.$scopedSlots.default(this)
return this.$slots.default(this)
},

mounted() {
Expand Down
11 changes: 6 additions & 5 deletions resources/js/package.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { clear } from './stores/useWishlists';
import './stores/useWishlists';
import { defineAsyncComponent } from 'vue'

Vue.component('wishlist', () => import('./Wishlist.vue'))

document.addEventListener('vue:loaded', (event) => {
window.app.$on('logged-out', clear);
document.addEventListener('vue:loaded', function (event) {
const vue = event.detail.vue
vue.component('wishlist', defineAsyncComponent(() => import('./Wishlist.vue')))
vue.component('wishlist-item', defineAsyncComponent(() => import('./WishlistItem.vue')))
})
36 changes: 16 additions & 20 deletions resources/js/stores/useWishlists.js
Original file line number Diff line number Diff line change
@@ -1,53 +1,51 @@
import { useLocalStorage } from "@vueuse/core"
import { computed, watch } from "vue"
import { computed, ref, watch } from "vue"
import { token } from 'Vendor/rapidez/core/resources/js/stores/useUser'
import { on } from 'Vendor/rapidez/core/resources/js/polyfills/emit'

export const wishlistStorage = useLocalStorage('wishlists', [])
let isRefreshing = false
let hasFetched = false
const isRefreshing = ref(false)
const hasFetched = ref(false)

export const refresh = async function () {
if (!token.value) {
clear()
return true
}

if (isRefreshing) {
if (isRefreshing.value) {
console.debug('Refresh canceled, request already in progress...')
return
}

isRefreshing = true
isRefreshing.value = true
try {
wishlistStorage.value = (await window.rapidezAPI('GET', 'wishlists', {})) || []
hasFetched = true
hasFetched.value = true
} catch (error) {
console.error(error)
Notify(window.config.translations.errors.wrong, 'error')
}
isRefreshing = false
isRefreshing.value = false
}

export const clear = async function () {
wishlistStorage.value = []
hasFetched = false
hasFetched.value = false
}

export const wishlists = computed({
get() {
if (!hasFetched && wishlistStorage.value.length === 0) {
refresh()
}

return wishlistStorage.value
},
set(value) {
wishlistStorage.value = value
export const wishlists = computed(() => {
if (!hasFetched.value && wishlistStorage.value.length === 0) {
refresh()
}

return wishlistStorage.value
})

watch(token, refresh)

on('logged-out', clear, {autoremove: false});

export default () => wishlists

export const create = async function (title) {
Expand Down Expand Up @@ -109,8 +107,6 @@ export const update = async function (id, data) {
}
}



export const addItem = async function (id, productId) {
try {
let fetchData = {
Expand Down
10 changes: 5 additions & 5 deletions resources/views/account/partials/details/product/image.blade.php
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<template v-if="product.thumbnail">
<template v-if="wishlistItem.product.thumbnail">
<td class="w-20 !px-0 max-md:w-16">
<img
v-if="product.thumbnail"
v-if="wishlistItem.product.thumbnail"
class="object-contain h-16 w-20 shrink-0"
:alt="product.name"
:src="`/storage/{{ config('rapidez.store') }}/resizes/200/magento/catalog/product${product.thumbnail}.webp`"
:alt="wishlistItem.product.name"
:src="`/storage/{{ config('rapidez.store') }}/resizes/200/magento/catalog/product${wishlistItem.product.thumbnail}.webp`"
>
<x-rapidez::no-image v-else />
<x-rapidez::no-image v-else="" />
</td>
</template>
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<td class="max-sm:w-56 sm:flex-1">
<div class="flex flex-col items-start">
<a :href="product.url" class="font-medium">@{{ product.name }}</a>
<span v-if="category" class="text-muted text-sm">@{{ category }}</span>
<span v-if="!product.in_stock" class="text-sm text">@lang('Not in stock')</span>
<a :href="wishlistItem.product.url" class="font-medium">@{{ wishlistItem.product.name }}</a>
<span v-if="wishlistItem.category" class="text-muted text-sm">@{{ wishlistItem.category }}</span>
<span v-if="!wishlistItem.product.in_stock" class="text-sm text">@lang('Not in stock')</span>
</div>
</td>
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<td>
<span class="text-sm font-bold text">@{{ product.price | price }}</span>
<span class="text-sm font-bold text">@{{ window.price(wishlistItem.product.price) }}</span>
</td>
Original file line number Diff line number Diff line change
@@ -1,25 +1,7 @@
<td class="w-40">
<div class="flex w-20 overflow-hidden rounded border">
<button
@disabled(!$editable)
v-bind:disabled="self.quantity <= 0"
class="flex-1 bg transition hover:bg-opacity-80"
v-on:click="self.quantity--"
>-</button>
<input
class="h-10 w-2/5 border-none px-0 text-center text-sm [appearance:textfield] focus:ring-0 [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
name="qty"
type="number"
{{ $attributes }}
@disabled(!$editable)
v-model="self.quantity"
min="0"
v-bind:step="product.qty_increments"
/>
<button
@disabled(!$editable)
class="flex-1 bg transition hover:bg-opacity-80"
v-on:click="self.quantity++"
>+</button>
</div>
<x-rapidez::quantity
v-model="wishlistItem.quantity"
v-bind:min="0"
v-bind:step="(wishlistItem.product.qty_increments ?? 1)"
/>
</td>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@if($editable)
<td>
<button @click="remove" class="hover:opacity-75 max-md:ml-auto">
<button @click="wishlistItem.remove" class="hover:opacity-75 max-md:ml-auto">
<x-heroicon-s-heart class="size-5 text" />
</button>
</td>
Expand Down
2 changes: 1 addition & 1 deletion resources/views/account/partials/details/title.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<span>@lang('Sharing link'): <a :href="shareUrl" class="text-primary underline">@{{ shareUrl }}</a></span>
<template v-if="isSupported">
<x-rapidez::button.secondary v-on:click="share">@lang('Share')</x-rapidez::button.secondary>
</template>
</template>
</div>
@endif
</template>
Expand Down
7 changes: 3 additions & 4 deletions resources/views/account/partials/details/wishlist.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,13 @@
</x-rapidez-mw::table.header>
<tbody>
<template v-for="(item, index) in wishlist.items">
<wishlist-item :data="items" :item="item" :wishlist-id="wishlist.id">
<wishlist-item :data="items" :item="item" :wishlist-id="wishlist.id" v-slot="wishlistItem">
<div
class="border-b flex flex-wrap items-center gap-y-5 py-5 *:px-2 last:border-none md:align-middle md:table-row md:*:py-5 md:*:px-1.5"
v-bind:class="{'opacity-70': product && !product.in_stock }"
slot-scope="{ _renderProxy: self, product, remove, category }"
v-bind:class="{'opacity-70': wishlistItem?.product && !wishlistItem?.product?.in_stock }"
v-bind:key="item.id"
>
<template v-if="product">
<template v-if="wishlistItem?.product">
@include('rapidez-mw::account.partials.details.product.image')
@include('rapidez-mw::account.partials.details.product.name')
@include('rapidez-mw::account.partials.details.product.remove')
Expand Down
2 changes: 1 addition & 1 deletion resources/views/partials/item/add.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
</div>
<button @click="toggleItem(wishlist, {{ $productId }})">
<x-heroicon-s-heart v-if="findItem(wishlist, {{ $productId }})" class="size-5 text hover:opacity-80" />
<x-heroicon-o-heart v-else class="size-5 text-muted hover:text-primary" />
<x-heroicon-o-heart v-else="" class="size-5 text-muted hover:text-primary" />
</button>
4 changes: 2 additions & 2 deletions resources/views/partials/item/button.blade.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<button v-if="$root.loggedIn" class="p-3" @click="toggle" aria-label="@lang('Add to wishlist')">
<button v-if="window.app.config.globalProperties.loggedIn.value" class="p-3" @click="toggle" aria-label="@lang('Add to wishlist')">
<x-heroicon-s-heart v-if="isWishlisted({{ $productId }})" class="size-5 text hover:opacity-80" />
<x-heroicon-o-heart v-else v-bind:class="{ 'text-primary': isOpen, 'text-muted hover:text-primary': !isOpen }" class="size-5 transition" />
<x-heroicon-o-heart v-else="" v-bind:class="{ 'text-primary': isOpen, 'text-muted hover:text-primary': !isOpen }" class="size-5 transition" />
</button>

<button v-else class="p-3" @click="toggle" aria-label="@lang('Add to wishlist')">
Expand Down
2 changes: 1 addition & 1 deletion resources/views/partials/item/login.blade.php
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<template v-if="!$root.loggedIn">
<template v-if="!window.app.config.globalProperties.loggedIn.value">
@lang('You must log in to add a product to your order list.') <a href="/login" class="text-primary underline underline-offset-3">@lang('login')</a>
</template>