first commit

This commit is contained in:
2024-11-05 12:22:50 +01:00
commit e5682a3912
19641 changed files with 2948548 additions and 0 deletions

View File

@@ -0,0 +1,78 @@
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import blockwishlistModule from 'blockwishlistModule';
const tabButtons = document.querySelectorAll('.btn-group button');
const refreshButton = document.querySelector('.js-refresh');
let isLoading = false;
tabButtons.forEach((button) => {
button.addEventListener('click', () => {
if (!button.classList.contains('active')) {
tabButtons.forEach((elem) => {
elem.classList.remove('active');
});
button.classList.add('active');
const tabs = document.querySelectorAll('.wishlist-tab');
tabs.forEach((tab) => {
if (
tab.classList.contains('active')
&& tab.dataset.tab !== button.dataset.tab
) {
tab.classList.remove('active');
}
if (tab.dataset.tab === button.dataset.tab) {
tab.classList.add('active');
}
});
}
});
});
refreshButton.addEventListener('click', async () => {
if (!isLoading) {
isLoading = true;
const cacheButton = refreshButton.innerHTML;
refreshButton.innerHTML = '<i class="material-icons">hourglass_empty</i>';
const response = await fetch(`${blockwishlistModule.resetCacheUrl}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json, text/javascript, */*; q=0.01',
},
});
const {success} = await response.json();
if (success) {
location.reload();
} else {
isLoading = false;
refreshButton.innerHTML = cacheButton;
}
}
});

View File

@@ -0,0 +1,22 @@
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import TranslatableInput from '@PSJs/components/translatable-input';
new TranslatableInput();

View File

@@ -0,0 +1,76 @@
.wishlist-stats {
.card {
&-text {
width: 100%;
padding: 10px 30px;
}
}
& &-topbar {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
margin-bottom: 20px;
.btn-group {
border: 1px solid #b7ced3;
border-radius: 3px;
button {
color: #363a41;
font-size: 14px;
letter-spacing: 0;
line-height: 19px;
background: none;
font-weight: 500;
transition: 0.25s ease-out;
&:not(:last-child) {
border-right: 1px solid #b7ced3;
}
&:hover,
&.active {
background-color: #25b9d7;
color: #fff;
}
}
}
.refresh {
color: #6c868e;
font-size: 14px;
font-weight: bold;
letter-spacing: 0;
line-height: 19px;
border: 1px solid #6c868e;
border-radius: 4px;
transition: 0.25s ease-out;
background: none;
&:hover {
background: #6c868e;
color: white;
}
}
}
.wishlist-tab {
display: none;
&.active {
display: block;
}
.column-image {
img {
max-width: 50px;
}
}
.column-conversionRate {
font-weight: bold;
}
}
}

View File

@@ -0,0 +1,58 @@
.wishlist-stats .card-text {
width: 100%;
padding: 10px 30px;
}
.wishlist-stats .wishlist-stats-topbar {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
margin-bottom: 20px;
}
.wishlist-stats .wishlist-stats-topbar .btn-group {
border: 1px solid #b7ced3;
border-radius: 3px;
}
.wishlist-stats .wishlist-stats-topbar .btn-group button {
color: #363a41;
font-size: 14px;
letter-spacing: 0;
line-height: 19px;
background: none;
font-weight: 500;
transition: 0.25s ease-out;
}
.wishlist-stats .wishlist-stats-topbar .btn-group button:not(:last-child) {
border-right: 1px solid #b7ced3;
}
.wishlist-stats .wishlist-stats-topbar .btn-group button:hover, .wishlist-stats .wishlist-stats-topbar .btn-group button.active {
background-color: #25b9d7;
color: #fff;
}
.wishlist-stats .wishlist-stats-topbar .refresh {
color: #6c868e;
font-size: 14px;
font-weight: bold;
letter-spacing: 0;
line-height: 19px;
border: 1px solid #6c868e;
border-radius: 4px;
transition: 0.25s ease-out;
background: none;
}
.wishlist-stats .wishlist-stats-topbar .refresh:hover {
background: #6c868e;
color: white;
}
.wishlist-stats .wishlist-tab {
display: none;
}
.wishlist-stats .wishlist-tab.active {
display: block;
}
.wishlist-stats .wishlist-tab .column-image img {
max-width: 50px;
}
.wishlist-stats .wishlist-tab .column-conversionRate {
font-weight: bold;
}/*# sourceMappingURL=backoffice.css.map */

View File

@@ -0,0 +1 @@
{"version":3,"sources":["_stats.scss","backoffice.css"],"names":[],"mappings":"AAEI;EACE,WAAA;EACA,kBAAA;ACDN;ADKE;EACE,aAAA;EACA,8BAAA;EACA,mBAAA;EACA,eAAA;EACA,mBAAA;ACHJ;ADKI;EACE,yBAAA;EACA,kBAAA;ACHN;ADKM;EACE,cAAA;EACA,eAAA;EACA,iBAAA;EACA,iBAAA;EACA,gBAAA;EACA,gBAAA;EACA,0BAAA;ACHR;ADKQ;EACE,+BAAA;ACHV;ADMQ;EAEE,yBAAA;EACA,WAAA;ACLV;ADUI;EACE,cAAA;EACA,eAAA;EACA,iBAAA;EACA,iBAAA;EACA,iBAAA;EACA,yBAAA;EACA,kBAAA;EACA,0BAAA;EACA,gBAAA;ACRN;ADUM;EACE,mBAAA;EACA,YAAA;ACRR;ADaE;EACE,aAAA;ACXJ;ADaI;EACE,cAAA;ACXN;ADeM;EACE,eAAA;ACbR;ADiBI;EACE,iBAAA;ACfN","file":"backoffice.css"}

View File

@@ -0,0 +1 @@
@import '_stats';

View File

@@ -0,0 +1,126 @@
<!--**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*-->
<script>
import EventBus from '@components/EventBus';
import ChooseList from '../ChooseList/ChooseList';
export default {
name: 'AddToWishlist',
components: {
ChooseList,
},
props: {
url: {
type: String,
required: true,
default: '#',
},
},
data() {
return {
value: '',
isHidden: true,
productAttributeId: 0,
productId: 0,
quantity: 0,
};
},
methods: {
/**
* Open and close the modal
*/
toggleModal(forceOpen) {
if (forceOpen === true) {
this.isHidden = false;
} else {
this.isHidden = !this.isHidden;
}
},
/**
* Dispatch an event to the Create component
*/
openNewWishlistModal() {
this.toggleModal();
EventBus.$emit('showCreateWishlist');
},
},
mounted() {
/**
* Register to the event showAddToWishList so others component can open the modal of the current component
*/
EventBus.$on('showAddToWishList', (event) => {
this.toggleModal(
event.detail.forceOpen ? event.detail.forceOpen : null,
);
if (event.detail.productId) {
this.productId = event.detail.productId;
}
if (typeof event.detail.productAttributeId === 'number') {
this.productAttributeId = event.detail.productAttributeId;
}
if (event.detail.quantity) {
this.quantity = event.detail.quantity;
}
});
},
};
</script>
<style lang="scss" type="text/scss">
@import '@scss/_variables';
.wishlist {
&-add-to-new {
cursor: pointer;
transition: 0.2s ease-out;
font-size: 0.875rem;
letter-spacing: 0;
line-height: 1rem;
&:hover {
opacity: 0.7;
}
i {
margin-right: 0.3125rem;
vertical-align: middle;
color: $blue;
margin-top: -0.125rem;
font-size: 1.25rem;
}
}
&-add-to {
.modal {
&-body {
padding: 0;
}
&-footer {
text-align: left;
padding: 0.75rem 1.25rem;
}
}
}
}
</style>

View File

@@ -0,0 +1,29 @@
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import initApp from '@components/init';
import AddToWishlist from './AddToWishlist';
const props = [
{
name: 'url',
type: String,
},
];
initApp(AddToWishlist, '.wishlist-add-to', props);

View File

@@ -0,0 +1,233 @@
<!--**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*-->
<template>
<button
class="wishlist-button-add"
:class="{ 'wishlist-button-product': isProduct }"
@click="addToWishlist"
>
<i
class="material-icons"
v-if="isChecked"
>favorite</i>
<i
class="material-icons"
v-else
>favorite_border</i>
</button>
</template>
<script>
import removeFromList from '@graphqlFiles/mutations/removeFromList';
import prestashop from 'prestashop';
import EventBus from '@components/EventBus';
export default {
name: 'Button',
props: {
url: {
type: String,
required: true,
default: '#',
},
productId: {
type: Number,
required: true,
default: null,
},
productAttributeId: {
type: Number,
required: true,
default: null,
},
checked: {
type: Boolean,
required: false,
default: false,
},
isProduct: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
isChecked: this.checked === 'true',
idList: this.listId,
};
},
methods: {
/**
* Toggle the heart on this component, basically if the heart is filled,
* then this product is inside a wishlist, else it's not in a wishlist
*/
toggleCheck() {
this.isChecked = !this.isChecked;
},
/**
* If the product isn't in a wishlist, then open the "AddToWishlist" component modal,
* if he's in a wishlist, then launch a removeFromList mutation to remote the product from a wishlist
*/
async addToWishlist(event) {
event.preventDefault();
const quantity = document.querySelector(
'.product-quantity input#quantity_wanted',
);
if (!prestashop.customer.is_logged) {
EventBus.$emit('showLogin');
return;
}
if (!this.isChecked) {
EventBus.$emit('showAddToWishList', {
detail: {
productId: this.productId,
productAttributeId: parseInt(this.productAttributeId, 10),
forceOpen: true,
quantity: quantity ? parseInt(quantity.value, 10) : 0,
},
});
} else {
const {data} = await this.$apollo.mutate({
mutation: removeFromList,
variables: {
productId: this.productId,
url: this.url,
productAttributeId: this.productAttributeId,
listId: this.idList ? this.idList : this.listId,
},
});
const {removeFromList: response} = data;
EventBus.$emit('showToast', {
detail: {
type: response.success ? 'success' : 'error',
message: response.message,
},
});
if (!response.error) {
this.toggleCheck();
}
}
},
},
mounted() {
/**
* Register to event addedToWishlist to toggle the heart if the product has been added correctly
*/
EventBus.$on('addedToWishlist', (event) => {
if (
event.detail.productId === this.productId
&& parseInt(event.detail.productAttributeId, 10) === this.productAttributeId
) {
this.isChecked = true;
this.idList = event.detail.listId;
}
});
// eslint-disable-next-line
const items = productsAlreadyTagged.filter(
(e) => parseInt(e.id_product, 10) === this.productId
&& parseInt(e.id_product_attribute, 10) === this.productAttributeId,
);
if (items.length > 0) {
this.isChecked = true;
this.idList = parseInt(items[0].id_wishlist, 10);
}
if (this.isProduct) {
prestashop.on('updateProduct', ({eventType}) => {
if (eventType === 'updatedProductQuantity') {
this.isChecked = false;
}
});
prestashop.on('updatedProduct', (args) => {
const quantity = document.querySelector(
'.product-quantity input#quantity_wanted',
);
this.productAttributeId = parseInt(args.id_product_attribute, 10);
// eslint-disable-next-line
const itemsFiltered = productsAlreadyTagged.filter(
(e) => parseInt(e.id_product, 10) === this.productId
&& e.quantity.toString() === quantity.value
&& parseInt(e.id_product_attribute, 10) === this.productAttributeId,
);
if (itemsFiltered.length > 0) {
this.isChecked = true;
this.idList = parseInt(itemsFiltered[0].id_wishlist, 10);
} else {
this.isChecked = false;
}
});
}
},
};
</script>
<style lang="scss" type="text/scss">
.wishlist {
&-button {
&-product {
margin-left: 1.25rem;
}
&-add {
display: flex;
align-items: center;
justify-content: center;
height: 2.5rem;
width: 2.5rem;
min-width: 2.5rem;
padding-top: 0.1875rem;
background-color: #ffffff;
box-shadow: 0.125rem -0.125rem 0.25rem 0 rgba(0, 0, 0, 0.2);
border-radius: 50%;
cursor: pointer;
transition: 0.2s ease-out;
border: none;
&:hover {
opacity: 0.7;
}
&:focus {
outline: 0;
}
&:active {
transform: scale(1.2);
}
i {
color: #7a7a7a;
}
}
}
}
</style>

View File

@@ -0,0 +1,51 @@
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import initApp from '@components/init';
import Button from './Button';
const initButtons = () => {
const props = [
{
name: 'url',
type: String,
},
{
name: 'checked',
type: Boolean,
},
{
name: 'productId',
type: Number,
},
{
name: 'productAttributeId',
type: Number,
},
{
name: 'isProduct',
type: Boolean,
},
];
initApp(Button, '.wishlist-button', props);
};
initButtons();
export default initButtons;

View File

@@ -0,0 +1,239 @@
<!--**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*-->
<template>
<div class="wishlist-chooselist">
<ul class="wishlist-list">
<li
class="wishlist-list-item"
v-for="list of lists"
:key="list.id_wishlist"
@click="select(list.id_wishlist)"
>
<p>
{{ list.name }}
</p>
</li>
</ul>
<ContentLoader
v-if="$apollo.queries.lists.loading"
class="wishlist-list-loader"
height="105"
>
<rect
x="0"
y="12"
rx="3"
ry="0"
width="100%"
height="11"
/>
<rect
x="0"
y="36"
rx="3"
ry="0"
width="100%"
height="11"
/>
<rect
x="0"
y="60"
rx="3"
ry="0"
width="100%"
height="11"
/>
<rect
x="0"
y="84"
rx="3"
ry="0"
width="100%"
height="11"
/>
</ContentLoader>
<p
class="wishlist-list-empty"
v-if="lists && lists.length <= 0 && !$apollo.queries.lists.loading"
>
{{ emptyText }}
</p>
</div>
</template>
<script>
import getLists from '@graphqlFiles/queries/getlists';
import addtolist from '@graphqlFiles/mutations/addtolist';
import EventBus from '@components/EventBus';
import {ContentLoader} from 'vue-content-loader';
/**
* The role of this component is to render a list
* and make the possibility to choose one for the selected product
*/
export default {
name: 'ChooseList',
components: {
ContentLoader,
},
apollo: {
lists: {
query: getLists,
variables() {
return {
url: this.url,
};
},
},
},
props: {
productId: {
type: Number,
required: true,
default: 0,
},
quantity: {
type: Number,
required: true,
default: 0,
},
productAttributeId: {
type: Number,
required: true,
default: 0,
},
url: {
type: String,
required: true,
default: '',
},
emptyText: {
type: String,
required: true,
default: 'No list found',
},
addUrl: {
type: String,
required: true,
default: '',
},
},
methods: {
/**
* Select a list and add the product to it
*
* @param {Int} listId The id of the list selected
* @param {Int} userId The id of the user
* @param {Int} productId The id of the product
*/
async select(listId) {
const {data} = await this.$apollo.mutate({
mutation: addtolist,
variables: {
listId,
url: this.addUrl,
productId: this.productId,
quantity: this.quantity,
productAttributeId: this.productAttributeId,
},
});
const {addToList: response} = data;
/**
* Hide the modal inside the parent
*/
this.$emit('hide');
EventBus.$emit('showToast', {
detail: {
type: response.success ? 'success' : 'error',
message: response.message,
},
});
/**
* Send an event to the Heart the user previously clicked on
*/
EventBus.$emit('addedToWishlist', {
detail: {
productId: this.productId,
listId,
productAttributeId: this.productAttributeId,
},
});
},
},
mounted() {
/**
* Register to the event refetchList so if an other component update it, this one can update his list
*
* @param {String} 'refetchList' The event I decided to create to communicate between VueJS Apps
*/
EventBus.$on('refetchList', () => {
this.$apollo.queries.lists.refetch();
});
},
};
</script>
<style lang="scss" type="text/scss">
@import '@scss/_variables';
.wishlist {
&-list {
max-height: 55vh;
overflow-y: auto;
border-top: 1px solid #e5e5e5;
border-bottom: 1px solid #e5e5e5;
margin: 0;
&-empty {
font-size: 30;
text-align: center;
padding: 30px;
padding-bottom: 1.25rem;
font-weight: bold;
color: #000;
}
& .wishlist-list-item {
padding: 0.875rem 0;
transition: 0.25s ease-out;
cursor: pointer;
margin-bottom: 0;
&:hover {
background: lighten($blue, 45%);
}
p {
font-size: 0.875rem;
letter-spacing: 0;
color: #232323;
margin-bottom: 0;
line-height: 1rem;
padding: 0 2.5rem;
}
}
}
}
</style>

View File

@@ -0,0 +1,159 @@
<!--**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*-->
<script>
import createList from '@graphqlFiles/mutations/createlist';
import EventBus from '@components/EventBus';
/**
* This component display a modal where you can create a wishlist
*/
export default {
name: 'Create',
props: {
url: {
type: String,
required: true,
default: '#',
},
title: {
type: String,
required: true,
default: 'New wishlist',
},
label: {
type: String,
required: true,
default: 'Wishlist name',
},
placeholder: {
type: String,
required: true,
default: 'Add name',
},
cancelText: {
type: String,
required: true,
default: 'Cancel',
},
lengthText: {
type: String,
required: true,
default: 'List title is too short',
},
createText: {
type: String,
required: true,
default: 'Create',
},
},
data() {
return {
value: '',
isHidden: true,
};
},
methods: {
/**
* Toggle the modal
*/
toggleModal() {
this.isHidden = !this.isHidden;
},
/**
* Launch a createList mutation to create a Wishlist
*/
async createWishlist() {
const titleWithoutSpaces = this.value.replace(/ /g, '');
if (titleWithoutSpaces < 1) {
EventBus.$emit('showToast', {
detail: {
type: 'error',
message: this.lengthText,
},
});
return false;
}
const {data: response} = await this.$apollo.mutate({
mutation: createList,
variables: {
name: this.value,
url: this.url,
},
});
EventBus.$emit('showToast', {
detail: {
type: response.createList.success ? 'success' : 'error',
message: response.createList.message,
},
});
/**
* As this is not a real SPA, we need to inform others VueJS apps that they need to refetch the list
*/
EventBus.$emit('refetchList');
/**
* Finally hide the modal after creating the list
* and reopen the wishlist modal
*/
this.toggleModal();
EventBus.$emit('showAddToWishList', {
detail: {
forceOpen: true,
},
});
return true;
},
},
mounted() {
/**
* Register to the event showCreateWishlist so others components can toggle this modal
*
* @param {String} 'showCreateWishlist'
*/
EventBus.$on('showCreateWishlist', () => {
this.value = '';
this.toggleModal();
});
},
};
</script>
<style lang="scss" type="text/scss">
.wishlist {
&-create {
.wishlist-modal {
opacity: 0;
pointer-events: none;
z-index: 0;
&.show {
opacity: 1;
pointer-events: all;
z-index: 1053;
}
}
}
}
</style>

View File

@@ -0,0 +1,57 @@
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import initApp from '@components/init';
import Create from './Create';
const props = [
{
name: 'url',
type: String,
},
{
name: 'title',
type: String,
},
{
name: 'label',
type: String,
},
{
name: 'productId',
type: Number,
},
{
name: 'placeholder',
type: String,
},
{
name: 'cancelText',
type: String,
},
{
name: 'lengthText',
type: String,
},
{
name: 'createText',
type: String,
},
];
initApp(Create, '.wishlist-create', props);

View File

@@ -0,0 +1,177 @@
<!--**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*-->
<script>
import deleteList from '@graphqlFiles/mutations/deletelist';
import removeFromList from '@graphqlFiles/mutations/removeFromList';
import EventBus from '@components/EventBus';
/**
* This component display a modal where you can delete a wishlist
*/
export default {
name: 'Delete',
props: {
deleteProductUrl: {
type: String,
required: false,
default: '#',
},
deleteListUrl: {
type: String,
required: false,
default: '#',
},
title: {
type: String,
required: true,
default: 'Delete',
},
titleList: {
type: String,
required: true,
default: 'Delete',
},
placeholder: {
type: String,
required: true,
default: 'This action is irreversible',
},
cancelText: {
type: String,
required: true,
default: 'Cancel',
},
deleteText: {
type: String,
required: true,
default: 'Delete',
},
deleteTextList: {
type: String,
required: true,
default: 'Delete',
},
},
data() {
return {
value: '',
isHidden: true,
listId: null,
listName: '',
productId: null,
productAttributeId: null,
};
},
computed: {
confirmMessage() {
return this.placeholder.replace('%nameofthewishlist%', this.listName);
},
modalTitle() {
return this.productId ? this.title : this.titleList;
},
modalDeleteText() {
return this.productId ? this.deleteText : this.deleteTextList;
},
},
methods: {
/**
* Toggle the modal
*/
toggleModal() {
this.isHidden = !this.isHidden;
},
/**
* Launch a deleteList mutation to delete a Wishlist
*/
async deleteWishlist() {
const {data} = await this.$apollo.mutate({
mutation: this.productId ? removeFromList : deleteList,
variables: {
listId: this.listId,
productId: parseInt(this.productId, 10),
productAttributeId: parseInt(this.productAttributeId, 10),
url: this.productId ? this.deleteProductUrl : this.deleteListUrl,
},
});
const response = data.deleteList
? data.deleteList
: data.removeFromList;
/**
* As this is not a real SPA, we need to inform others VueJS apps that they need to refetch the list
*/
EventBus.$emit('refetchList');
EventBus.$emit('showToast', {
detail: {
type: response.success ? 'success' : 'error',
message: response.message,
},
});
/**
* Finally hide the modal after deleting the list
* and reopen the wishlist modal
*/
this.toggleModal();
},
},
mounted() {
/**
* Register to the event showCreateWishlist so others components can toggle this modal
*
* @param {String} 'showDeleteWishlist'
*/
EventBus.$on('showDeleteWishlist', (event) => {
this.value = '';
this.listId = event.detail.listId;
this.listName = event.detail.listName;
this.productId = null;
this.productAttributeId = null;
if (event.detail.productId) {
this.productId = event.detail.productId;
this.productAttributeId = event.detail.productAttributeId;
}
this.toggleModal();
});
},
};
</script>
<style lang="scss" type="text/scss">
.wishlist {
&-delete {
.wishlist-modal {
display: block;
opacity: 0;
pointer-events: none;
z-index: 0;
&.show {
opacity: 1;
pointer-events: all;
z-index: 1053;
}
}
}
}
</style>

View File

@@ -0,0 +1,57 @@
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import initApp from '@components/init';
import Delete from './Delete';
const props = [
{
name: 'deleteProductUrl',
type: String,
},
{
name: 'deleteListUrl',
type: String,
},
{
name: 'title',
type: String,
},
{
name: 'titleList',
type: String,
},
{
name: 'placeholder',
type: String,
},
{
name: 'cancelText',
type: String,
},
{
name: 'deleteText',
type: String,
},
{
name: 'deleteTextList',
type: String,
},
];
initApp(Delete, '.wishlist-delete', props);

View File

@@ -0,0 +1,28 @@
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import Vue from 'vue';
import prestashop from 'prestashop';
const EventBus = new Vue();
window.WishlistEventBus = EventBus;
prestashop.emit('wishlistEventBusInit');
export default EventBus;

View File

@@ -0,0 +1,351 @@
<!--**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*-->
<template>
<div class="wishlist-list-container">
<ul
class="wishlist-list"
v-if="items.length > 0 && items"
v-click-outside="emptyPopups"
>
<li
class="wishlist-list-item"
:key="list.id_wishlist"
v-for="list of items"
:class="{ 'wishlist-list-item-default': list.default }"
>
<a
class="wishlist-list-item-link"
@click="redirectToList(list.listUrl)"
>
<p class="wishlist-list-item-title">
{{ list.name }}
<span v-if="list.nbProducts">({{ list.nbProducts }})</span>
<span v-else>(0)</span>
</p>
<div class="wishlist-list-item-right">
<button
class="wishlist-list-item-actions"
@click.stop="togglePopup(list.id_wishlist)"
v-if="!list.default"
>
<i class="material-icons">more_vert</i>
</button>
<button
@click.stop="toggleShare(list.id_wishlist, list.shareUrl)"
v-if="list.default"
>
<i class="material-icons">share</i>
</button>
<div
class="dropdown-menu show"
v-if="activeDropdowns.includes(list.id_wishlist)"
>
<button @click.stop="toggleRename(list.id_wishlist, list.name)">
{{ renameText }}
</button>
<button
@click.stop="toggleShare(list.id_wishlist, list.shareUrl)"
>
{{ shareText }}
</button>
</div>
<button
@click.stop="toggleDelete(list.id_wishlist, list.name)"
v-if="!list.default"
>
<i class="material-icons">delete</i>
</button>
</div>
</a>
</li>
</ul>
<ContentLoader
v-if="loading"
class="wishlist-list-loader"
height="105"
>
<rect
x="0"
y="12"
rx="3"
ry="0"
width="100%"
height="11"
/>
<rect
x="0"
y="36"
rx="3"
ry="0"
width="100%"
height="11"
/>
<rect
x="0"
y="60"
rx="3"
ry="0"
width="100%"
height="11"
/>
<rect
x="0"
y="84"
rx="3"
ry="0"
width="100%"
height="11"
/>
</ContentLoader>
<p
class="wishlist-list-empty"
v-if="items.length <= 0 && !loading"
>
{{ emptyText }}
</p>
</div>
</template>
<script>
import {ContentLoader} from 'vue-content-loader';
import EventBus from '@components/EventBus';
import wishlistUrl from 'wishlistUrl';
import vClickOutside from 'v-click-outside';
/**
* Dumb component to display the list of Wishlist on a page
*/
export default {
name: 'List',
components: {
ContentLoader,
},
data() {
return {
activeDropdowns: [],
listUrl: wishlistUrl,
};
},
props: {
items: {
type: Array,
default: () => [],
},
renameText: {
type: String,
default: 'Rename',
},
emptyText: {
type: String,
default: '',
},
shareText: {
type: String,
default: 'Share',
},
loading: {
type: Boolean,
default: true,
},
},
methods: {
/**
* Toggle a dropdown with some actions
*
* @param {Int} id The ID of the list which contain this dropdown
*/
togglePopup(id) {
if (this.activeDropdowns.includes(id)) {
this.activeDropdowns = this.activeDropdowns.filter((e) => e !== id);
} else {
this.activeDropdowns = [];
this.activeDropdowns.push(id);
}
},
emptyPopups() {
this.activeDropdowns = [];
},
/**
* Toggle the popup to rename a list
*
* @param {Int} id The list ID so the rename popup know which list to rename
* @param {String} The base title so the rename popup can autofill it
*/
toggleRename(id, title) {
EventBus.$emit('showRenameWishlist', {
detail: {listId: id, title},
});
},
/**
* Toggle the popup to rename a list
*
* @param {Int} id The list ID so the rename popup know which list to rename
* @param {String} The base title so the rename popup can autofill it
*/
toggleShare(id, shareUrl) {
EventBus.$emit('showShareWishlist', {
detail: {listId: id, shareUrl},
});
},
/**
* Toggle the popup to rename a list
*
* @param {Int} id The list ID so the rename popup know which list to rename
* @param {String} The base title so the rename popup can autofill it
*/
toggleDelete(id) {
EventBus.$emit('showDeleteWishlist', {
detail: {listId: id, userId: 1},
});
},
/**
* Redirect to the list URI
*
* @param {String} listUrl The list url
*/
redirectToList(listUrl) {
window.location.href = listUrl;
},
},
directives: {
clickOutside: vClickOutside.directive,
},
};
</script>
<style lang="scss" type="text/scss">
@import '@scss/_variables';
.wishlist {
&-list {
margin-bottom: 0;
&-empty {
font-size: 1.875rem;
text-align: center;
padding: 1.875rem;
padding-bottom: 1.25rem;
font-weight: bold;
color: #000;
}
&-loader {
padding: 0 1.25rem;
width: 100%;
}
&-item {
&-default {
border-bottom: 1px solid #0000002e;
}
&:hover {
cursor: pointer;
.wishlist-list-item-title {
color: $blue;
}
}
&-link {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.5rem 1.25rem;
}
.dropdown-menu {
right: 1.25rem;
left: inherit;
display: flex;
flex-direction: column;
}
&-right {
position: relative;
> button {
transition: 0.25s ease-out;
&:hover {
opacity: 0.6;
}
i {
color: #7a7a7a;
}
}
.dropdown-menu {
box-sizing: border-box;
border: 1px solid #e5e5e5;
border-radius: 0.25rem;
background-color: #ffffff;
box-shadow: 0.125rem 0.125rem 0.625rem 0 rgba(0, 0, 0, 0.2);
padding: 0;
overflow: hidden;
> button {
padding: 0.625rem 1.25rem;
transition: 0.2s ease-out;
text-align: left;
&:hover {
background-color: #f1f1f1;
}
}
}
}
&-title {
color: #232323;
font-size: 1rem;
font-weight: bold;
letter-spacing: 0;
line-height: 1.375rem;
margin-bottom: 0;
span {
color: #7a7a7a;
font-size: 1rem;
letter-spacing: 0;
line-height: 1.375rem;
font-weight: normal;
margin-left: 0.3125rem;
}
}
button {
cursor: pointer;
border: none;
background: none;
&:focus {
outline: 0;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,81 @@
<!--**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*-->
<script>
import EventBus from '@components/EventBus';
import prestashop from 'prestashop';
/**
* This component display a modal where you can redirect to login page
*/
export default {
name: 'Login',
props: {
cancelText: {
type: String,
required: true,
default: 'Cancel',
},
loginText: {
type: String,
required: true,
default: 'Login',
},
},
data() {
return {
value: '',
isHidden: true,
listId: null,
prestashop,
};
},
methods: {
/**
* Toggle the modal
*/
toggleModal() {
this.isHidden = !this.isHidden;
},
},
mounted() {
/**
* Register to the event showCreateWishlist so others components can toggle this modal
*
* @param {String} 'showDeleteWishlist'
*/
EventBus.$on('showLogin', () => {
this.toggleModal();
});
},
};
</script>
<style lang="scss" type="text/scss">
.wishlist {
&-login {
.wishlist-modal {
z-index: 0;
&.show {
z-index: 1053;
}
}
}
}
</style>

View File

@@ -0,0 +1,33 @@
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import initApp from '@components/init';
import Login from './Login';
const props = [
{
name: 'loginText',
type: String,
},
{
name: 'cancelText',
type: String,
},
];
initApp(Login, '.wishlist-login', props);

View File

@@ -0,0 +1,87 @@
<!--**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*-->
<script>
import EventBus from '@components/EventBus';
/**
* Dumb component to display the list of Wishlist on a page
*/
export default {
name: 'Pagination',
data() {
return {
total: null,
minShown: null,
maxShown: null,
pageNumber: 0,
pages: [],
currentPage: null,
display: false,
};
},
methods: {
paginate(page) {
EventBus.$emit('updatePagination', {
page,
});
this.currentPage = page;
},
},
mounted() {
EventBus.$on('paginate', (payload) => {
this.total = payload.detail.total;
this.minShown = payload.detail.minShown;
this.maxShown = payload.detail.maxShown;
this.pageNumber = payload.detail.pageNumber;
this.currentPage = payload.detail.currentPage;
this.pages = payload.detail.pages;
this.display = payload.detail.display;
});
},
};
</script>
<style lang="scss" type="text/scss">
@import '@scss/_variables';
.wishlist {
&-pagination {
.previous {
margin-right: 1.875rem;
}
.js-wishlist-search-link {
cursor: pointer;
&:not([href]):not([tabindex]):hover {
color: $blue;
}
&.disabled {
cursor: inherit;
&:hover {
color: $blue;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,24 @@
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import initApp from '@components/init';
import Pagination from './Pagination';
const props = [];
initApp(Pagination, '.wishlist-pagination', props);

View File

@@ -0,0 +1,608 @@
<!--**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*-->
<template>
<div class="wishlist-product">
<a
class="wishlist-product-link"
:href="product.canonical_url"
>
<div class="wishlist-product-image">
<img
v-if="product.default_image"
:src="product.default_image.large.url"
:alt="product.default_image.legend"
:title="product.default_image.legend"
:class="{
'wishlist-product-unavailable': !product.add_to_cart_url
}"
>
<img
v-else-if="product.cover"
:src="product.cover.large.url"
:alt="product.cover.legend"
:title="product.cover.legend"
:class="{
'wishlist-product-unavailable': !product.add_to_cart_url
}"
>
<img
v-else
:src="prestashop.urls.no_picture_image.bySize.home_default.url"
>
<p
class="wishlist-product-availability"
v-if="product.show_availability"
>
<i
class="material-icons"
v-if="product.availability === 'unavailable'"
>
block
</i>
<i
class="material-icons"
v-if="product.availability === 'last_remaining_items'"
>
warning
</i>
{{ product.availability_message }}
</p>
</div>
<div class="wishlist-product-right">
<p class="wishlist-product-title">{{ product.name }}</p>
<p class="wishlist-product-price">
<span
class="wishlist-product-price-promo"
v-if="product.has_discount"
>
{{ product.regular_price }}
</span>
{{ product.price }}
</p>
<div class="wishlist-product-combinations">
<p class="wishlist-product-combinations-text">
<template v-for="(attribute, key, index) of product.attributes">
{{ attribute.group }} : {{ attribute.name }}
<span
:key="key"
v-if="index <= Object.keys(product.attributes).length - 1"
>
-
</span>
<span
:key="key + 'end'"
v-if="index == Object.keys(product.attributes).length - 1"
>
{{ quantityText }} : {{ product.wishlist_quantity }}
</span>
</template>
<span v-if="Object.keys(product.attributes).length === 0">
{{ quantityText }} : {{ product.wishlist_quantity }}
</span>
</p>
<a
:href="product.canonical_url"
v-if="!isShare"
>
<i class="material-icons">create</i>
</a>
</div>
</div>
</a>
<div class="wishlist-product-bottom">
<button
class="btn wishlist-product-addtocart"
:class="{
'btn-secondary': product.customizable === '1',
'btn-primary': product.customizable === '0'
}"
:disabled="isDisabled || forceDisable"
@click="
product.add_to_cart_url || product.customizable === '1'
? addToCartAction()
: null
"
>
<i
class="material-icons shopping-cart"
v-if="product.customizable === '0'"
>
shopping_cart
</i>
{{ product.customizable === '1' ? customizeText : addToCart }}
</button>
<button
class="wishlist-button-add"
v-if="!isShare"
@click="removeFromWishlist"
>
<i class="material-icons">delete</i>
</button>
</div>
<p
class="wishlist-product-availability wishlist-product-availability-responsive"
v-if="product.show_availability"
>
<i
class="material-icons"
v-if="product.availability === 'unavailable'"
>
block
</i>
<i
class="material-icons"
v-if="product.availability === 'last_remaining_items'"
>
warning
</i>
{{ product.availability_message }}
</p>
</div>
</template>
<script>
import EventBus from '@components/EventBus';
import headers from '@constants/headers';
import prestashop from 'prestashop';
import wishlistAddProductToCartUrl from 'wishlistAddProductToCartUrl';
export default {
name: 'Product',
props: {
product: {
type: Object,
required: true,
default: null,
},
listId: {
type: Number,
required: true,
default: null,
},
listName: {
type: String,
required: true,
default: '',
},
isShare: {
type: Boolean,
required: false,
default: false,
},
customizeText: {
type: String,
required: true,
default: 'Customize',
},
quantityText: {
type: String,
required: true,
default: 'Quantity',
},
addToCart: {
type: String,
required: true,
},
status: {
type: Number,
required: false,
default: 0,
},
hasControls: {
type: Boolean,
required: false,
default: true,
},
},
data() {
return {
prestashop,
forceDisable: false,
};
},
computed: {
isDisabled() {
const wishlistQuantity = parseInt(this.product.wishlist_quantity, 10);
const quantityAvailable = parseInt(this.product.quantity_available, 10);
const cartQuantity = parseInt(this.product.cart_quantity, 10);
if (this.product.allow_oosp) {
return false;
}
if (this.product.customizable === '1') {
return false;
}
if (wishlistQuantity > quantityAvailable) {
return true;
}
if (cartQuantity >= quantityAvailable) {
return true;
}
if (cartQuantity
&& cartQuantity + wishlistQuantity > quantityAvailable
) {
return true;
}
return !this.product.add_to_cart_url;
},
},
methods: {
/**
* Remove the product from the wishlist
*/
async removeFromWishlist() {
EventBus.$emit('showDeleteWishlist', {
detail: {
listId: this.listId,
listName: this.listName,
productId: this.product.id,
productAttributeId: this.product.id_product_attribute,
},
});
},
async addToCartAction() {
if (this.product.add_to_cart_url && this.product.customizable !== '1') {
try {
this.forceDisable = true;
const datas = new FormData();
datas.append('qty', this.product.wishlist_quantity);
datas.append('id_product', this.product.id_product);
datas.append('id_customization', this.product.id_customization);
const response = await fetch(
`${this.product.add_to_cart_url}&action=update`,
{
method: 'POST',
headers: headers.addToCart,
body: datas,
},
);
const resp = await response.json();
EventBus.$emit('refetchList');
prestashop.emit('updateCart', {
reason: {
idProduct: this.product.id_product,
idProductAttribute: this.product.id_product_attribute,
idCustomization: this.product.id_customization,
linkAction: 'add-to-cart',
},
resp,
});
$('body').on('hide.bs.modal', '#blockcart-modal', () => {
this.forceDisable = false;
});
/* eslint-disable */
const statResponse = await fetch(
`${wishlistAddProductToCartUrl}&params[idWishlist]=${this.listId}&params[id_product]=${this.product.id_product}&params[id_product_attribute]=${this.product.id_product_attribute}&params[quantity]=${this.product.wishlist_quantity}`,
{
headers: {
'Content-Type':
'application/x-www-form-urlencoded; charset=UTF-8',
Accept: 'application/json, text/javascript, */*; q=0.01'
}
}
);
/* eslint-enable */
await statResponse.json();
} catch (error) {
prestashop.emit('handleError', {
eventType: 'addProductToCart',
resp: error,
});
}
} else {
window.location.href = this.product.canonical_url;
}
},
},
};
</script>
<style lang="scss" type="text/scss">
@import '@scss/_variables';
.wishlist {
&-products-item {
margin: 1.5625rem;
}
&-product {
max-width: 15.625rem;
width: 100%;
position: relative;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
&-unavailable {
opacity: 0.5;
}
&-availability {
display: flex;
align-items: flex-start;
margin-bottom: 0;
color: #232323;
font-size: 0.75rem;
font-weight: bold;
letter-spacing: 0;
line-height: 1.0625rem;
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: 1.0625rem;
z-index: 5;
min-width: 80%;
justify-content: center;
i {
color: #ff4c4c;
margin-right: 0.3125rem;
font-size: 1.125rem;
}
&-responsive {
display: none;
position: inherit;
transform: inherit;
bottom: inherit;
margin-top: 0.625rem;
left: inherit;
}
}
&-link {
&:focus {
text-decoration: none;
}
&:hover {
img {
transform: translate(-50%, -50%) scale(1.1);
}
}
}
&-title {
margin-top: 0.625rem;
margin-bottom: 0.315rem;
color: #737373;
font-size: 0.875rem;
letter-spacing: 0;
line-height: 1.875rem;
}
&-image {
width: 15.625rem;
height: 15.625rem;
position: relative;
overflow: hidden;
img {
position: absolute;
max-width: 100%;
max-height: 100%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
transition: 0.25s ease-out;
}
}
&-price {
color: #232323;
font-size: 1rem;
font-weight: bold;
letter-spacing: 0;
line-height: 1.375rem;
&-promo {
text-decoration: line-through;
color: #737373;
font-size: 0.875rem;
font-weight: bold;
letter-spacing: 0;
line-height: 1.1875rem;
margin-right: 0.3125rem;
vertical-align: middle;
display: inline-block;
margin-top: -0.1875rem;
}
}
&-combinations {
display: flex;
align-items: flex-start;
justify-content: space-between;
a {
display: block;
color: #7a7a7a;
&:hover {
color: $blue;
}
}
&-text {
color: #7a7a7a;
font-size: 0.8125rem;
letter-spacing: 0;
line-height: 1.25rem;
min-height: 3.125rem;
margin: 0;
}
}
&-addtocart {
width: 100%;
text-transform: inherit;
padding-left: 0.625rem;
&.btn-secondary {
background-color: #dddddd;
&:hover {
background-color: #dddddd;
opacity: 0.7;
}
}
i {
margin-top: -0.1875rem;
}
}
}
&-button {
&-add {
position: absolute;
top: 0.625rem;
right: 0.625rem;
display: flex;
align-items: center;
justify-content: center;
height: 2.5rem;
width: 2.5rem;
min-width: 2.5rem;
padding-top: 0.1875rem;
background-color: #ffffff;
box-shadow: 0.125rem 0.125rem 0.25rem 0 rgba(0, 0, 0, 0.2);
border-radius: 50%;
cursor: pointer;
transition: 0.2s ease-out;
border: none;
&:hover {
opacity: 0.7;
}
&:focus {
outline: 0;
}
&:active {
transform: scale(1.2);
}
i {
color: #7a7a7a;
margin-top: -0.125rem;
}
}
}
}
@media screen and (max-width: 768px) {
.wishlist {
&-button-add {
position: inherit;
margin-left: 0.625rem;
}
&-products-item {
width: 100%;
margin: 0;
margin-bottom: 1.875rem;
&:not(:last-child) {
margin-bottom: 1.875rem;
}
}
&-product {
margin: 0;
width: 100%;
max-width: 100%;
&-link {
&:hover {
img {
transform: inherit;
}
}
}
&-bottom {
display: flex;
align-items: center;
justify-content: space-between;
}
&-right {
flex: 1;
}
&-availability {
display: none;
&-responsive {
display: block;
min-width: 100%;
justify-content: flex-start;
}
}
&-image {
width: 100px;
height: 100px;
margin-right: 1.25rem;
position: inherit;
img {
position: inherit;
left: inherit;
top: inherit;
transform: inherit;
}
}
&-link {
display: flex;
align-items: flex-start;
}
&-title {
margin-top: 0;
}
}
}
}
</style>

View File

@@ -0,0 +1,133 @@
<!--**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*-->
<script>
import renameList from '@graphqlFiles/mutations/renamelist';
import EventBus from '@components/EventBus';
/**
* A modal used to rename a list
*/
export default {
name: 'Rename',
props: {
url: {
type: String,
required: true,
default: '#',
},
title: {
type: String,
required: true,
default: 'Rename wishlist',
},
label: {
type: String,
required: true,
default: 'Wishlist name',
},
placeholder: {
type: String,
required: true,
default: 'Rename text',
},
cancelText: {
type: String,
required: true,
default: 'Cancel',
},
renameText: {
type: String,
required: true,
default: 'Rename',
},
},
data() {
return {
value: '',
isHidden: true,
listId: 0,
};
},
methods: {
/**
* Toggle the modal
*/
toggleModal() {
this.isHidden = !this.isHidden;
},
/**
* Launch a renameList mutation, then dispatch an event to everycomponent to refetch the list after renaming it
*
* @param {Int} listId Id of the list to be renamed
*/
async renameWishlist() {
const {data} = await this.$apollo.mutate({
mutation: renameList,
variables: {
name: this.value,
url: this.url,
listId: this.listId,
},
});
const {renameList: response} = data;
EventBus.$emit('refetchList');
EventBus.$emit('showToast', {
detail: {
type: response.success ? 'success' : 'error',
message: response.message,
},
});
this.toggleModal();
},
},
mounted() {
/**
* Register to the showRenameWishlist event so everycomponents can display this modal
*/
EventBus.$on('showRenameWishlist', (event) => {
this.value = event.detail.title;
this.listId = event.detail.listId;
this.toggleModal();
});
},
};
</script>
<style lang="scss" type="text/scss" scoped>
.wishlist {
&-rename {
.wishlist-modal {
display: block;
opacity: 0;
pointer-events: none;
z-index: 0;
&.show {
opacity: 1;
pointer-events: all;
z-index: 1051;
}
}
}
}
</style>

View File

@@ -0,0 +1,49 @@
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import initApp from '@components/init';
import Rename from './Rename';
const props = [
{
name: 'url',
type: String,
},
{
name: 'title',
type: String,
},
{
name: 'label',
type: String,
},
{
name: 'placeholder',
type: String,
},
{
name: 'cancelText',
type: String,
},
{
name: 'renameText',
type: String,
},
];
initApp(Rename, '.wishlist-rename', props);

View File

@@ -0,0 +1,132 @@
<!--**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*-->
<script>
import EventBus from '@components/EventBus';
/**
* This component display a modal where you can create a wishlist
*/
export default {
name: 'Share',
props: {
url: {
type: String,
required: true,
default: '#',
},
title: {
type: String,
required: true,
default: 'Share wishlist',
},
label: {
type: String,
required: true,
default: 'Share link',
},
cancelText: {
type: String,
required: true,
default: 'Cancel',
},
copyText: {
type: String,
required: true,
default: 'Copy text',
},
copiedText: {
type: String,
required: true,
default: 'Copied',
},
},
data() {
return {
value: '',
isHidden: true,
actionText: '',
};
},
methods: {
/**
* Toggle the modal
*/
toggleModal() {
this.isHidden = !this.isHidden;
},
/**
* Copy the link in the input value
*/
copyLink() {
const shareInput = document.querySelector(
'.wishlist-share .form-control',
);
shareInput.select();
shareInput.setSelectionRange(0, 99999);
document.execCommand('copy');
this.actionText = this.copiedText;
this.toggleModal();
EventBus.$emit('showToast', {
detail: {
type: 'success',
message: 'copyText',
},
});
},
},
mounted() {
this.actionText = this.copyText;
/**
* Register to the event showCreateWishlist so others components can toggle this modal
*
* @param {String} 'showCreateWishlist'
*/
EventBus.$on('showShareWishlist', async (event) => {
this.actionText = this.copyText;
this.value = event.detail.shareUrl;
this.toggleModal();
});
},
};
</script>
<style lang="scss" type="text/scss">
.wishlist {
&-share {
.wishlist-modal {
display: block;
opacity: 0;
pointer-events: none;
z-index: 0;
&.show {
opacity: 1;
pointer-events: all;
z-index: 1053;
}
}
}
}
</style>

View File

@@ -0,0 +1,49 @@
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import initApp from '@components/init';
import Share from './Share';
const props = [
{
name: 'url',
type: String,
},
{
name: 'title',
type: String,
},
{
name: 'label',
type: String,
},
{
name: 'copyText',
type: String,
},
{
name: 'copiedText',
type: String,
},
{
name: 'cancelText',
type: String,
},
];
initApp(Share, '.wishlist-share', props);

View File

@@ -0,0 +1,151 @@
<!--**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*-->
<template>
<div
class="wishlist-toast"
:class="[{ isActive: active }, type]"
>
<p class="wishlist-toast-text">
{{ text }}
</p>
</div>
</template>
<script>
import EventBus from '@components/EventBus';
export default {
name: 'Button',
props: {
renameWishlistText: {
type: String,
required: true,
},
addedWishlistText: {
type: String,
required: true,
},
deleteWishlistText: {
type: String,
required: true,
},
createWishlistText: {
type: String,
required: true,
},
deleteProductText: {
type: String,
required: true,
},
copyText: {
type: String,
required: true,
},
},
data() {
return {
text: '',
active: false,
timeout: null,
type: 'basic',
};
},
mounted() {
/**
* Register to an even so every components can show toast
*/
EventBus.$on('showToast', (event) => {
if (event.detail.message) {
if (this[event.detail.message]) {
this.text = this[event.detail.message];
} else {
this.text = event.detail.message;
}
}
this.active = true;
if (this.timeout) {
clearTimeout(this.timeout);
}
this.timeout = setTimeout(() => {
this.active = false;
this.timeout = null;
}, 2500);
this.type = event.detail.type ? event.detail.type : 'basic';
});
},
};
</script>
<style lang="scss" type="text/scss">
.wishlist {
&-toast {
padding: 0.875rem 1.25rem;
box-sizing: border-box;
width: auto;
border: 1px solid #e5e5e5;
border-radius: 4px;
background-color: #ffffff;
box-shadow: 0.125rem 0.125rem 0.625rem 0 rgba(0, 0, 0, 0.2);
position: fixed;
right: 1.25rem;
z-index: 9999;
top: 4.375rem;
transition: 0.2s ease-out;
transform: translateY(-10px);
pointer-events: none;
opacity: 0;
&.success {
background-color: #69b92d;
border-color: #69b92d;
.wishlist-toast-text {
color: white;
}
}
&.error {
background-color: #b9312d;
border-color: #b9312d;
.wishlist-toast-text {
color: white;
}
}
&.isActive {
transform: translateY(0);
pointer-events: all;
opacity: 1;
}
&-text {
color: #232323;
font-size: 0.875rem;
letter-spacing: 0;
line-height: 1.1875rem;
margin-bottom: 0;
}
}
}
</style>

View File

@@ -0,0 +1,53 @@
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import initApp from '@components/init';
import Toast from './Toast';
const props = [
{
name: 'renameWishlistText',
type: String,
},
{
name: 'createWishlistText',
type: String,
},
{
name: 'addedWishlistText',
type: String,
},
{
name: 'shareText',
type: String,
},
{
name: 'deleteWishlistText',
type: String,
},
{
name: 'deleteProductText',
type: String,
},
{
name: 'copyText',
type: String,
},
];
initApp(Toast, '.wishlist-toast', props);

View File

@@ -0,0 +1,64 @@
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import apolloClient from '@graphqlFiles/client';
/**
* Init a VueJS application to keep monolith features such as hooks or event the use of twig/smarty
*
* @param {Vue} component The component to be init
* @param {String} componentSelector A selector for querySelectorAll
* @param {Array[Object]} props An array containing Object{name, type} to parse int
*/
export default function initApp(component, componentSelector, props) {
Vue.use(VueApollo);
const apolloProvider = new VueApollo({
defaultClient: apolloClient,
});
const componentElements = document.querySelectorAll(componentSelector);
const ComponentRoot = Vue.extend(component);
const propsData = {};
componentElements.forEach((e) => {
/* eslint-disable */
for (const prop of props) {
if (e.dataset[prop.name]) {
if (prop.type === Number) {
propsData[prop.name] = parseInt(e.dataset[prop.name], 10);
} else if (prop.type === Boolean) {
propsData[prop.name] = e.dataset[prop.name] === 'true';
} else {
propsData[prop.name] = e.dataset[prop.name];
}
}
}
/* eslint-enable */
new ComponentRoot({
el: e,
delimiters: ['((', '))'],
apolloProvider,
propsData,
});
});
}

View File

@@ -0,0 +1,11 @@
const headers = {
addToCart: {
Accept: 'application/json, text/javascript',
},
products: {
'Content-Type': 'application/json',
Accept: 'application/json, text/javascript, */*; q=0.01',
},
};
export default headers;

View File

@@ -0,0 +1,366 @@
<!--**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*-->
<template>
<div class="wishlist-products-container">
<div class="wishlist-products-container-header">
<h1>
{{ title }}
<span
class="wishlist-products-count"
v-if="products.datas && products.datas.products"
>
({{ products.datas.pagination.total_items }})
</span>
</h1>
<div
class="sort-by-row"
v-if="products.datas"
>
<span class="col-sm-3 col-md-3 hidden-sm-down sort-by">{{ filter }}</span>
<div class="col-sm-9 col-xs-8 col-md-9 products-sort-order dropdown">
<button
class="btn-unstyle select-title"
rel="nofollow"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
{{ currentSort }}
<i class="material-icons float-xs-right">arrow_drop_down</i>
</button>
<div class="dropdown-menu">
<a
rel="nofollow"
@click="changeSelectedSort(sort)"
class="select-list"
:key="key"
v-for="(sort, key) in productList"
>
{{ sort.label }}
</a>
</div>
</div>
</div>
</div>
<section
id="content"
class="page-content card card-block"
>
<ul
class="wishlist-products-list"
v-if="products.datas && products.datas.products.length > 0"
>
<li
class="wishlist-products-item"
v-for="(product, key) in products.datas.products"
:key="key"
>
<Product
:product="product"
:add-to-cart="addToCart"
:customize-text="customizeText"
:quantity-text="quantityText"
:list-name="title"
:list-id="
listId ? listId : parseInt(currentWishlist.id_wishlist, 10)
"
:is-share="share"
/>
</li>
</ul>
<ContentLoader
v-if="!products.datas"
class="wishlist-list-loader"
height="105"
>
<rect
x="0"
y="12"
rx="3"
ry="0"
width="100%"
height="11"
/>
<rect
x="0"
y="36"
rx="3"
ry="0"
width="100%"
height="11"
/>
<rect
x="0"
y="60"
rx="3"
ry="0"
width="100%"
height="11"
/>
<rect
x="0"
y="84"
rx="3"
ry="0"
width="100%"
height="11"
/>
</ContentLoader>
<p
class="wishlist-list-empty"
v-if="products.datas && products.datas.products.length <= 0"
>
{{ noProductsMessage }}
</p>
</section>
</div>
</template>
<script>
import Product from '@components/Product/Product';
import getProducts from '@graphqlFiles/queries/getproducts';
import {ContentLoader} from 'vue-content-loader';
import EventBus from '@components/EventBus';
/**
* This component act as a smart component wich will handle every actions of the list one
*/
export default {
name: 'ProductsListContainer',
components: {
Product,
ContentLoader,
},
apollo: {
products: {
query: getProducts,
variables() {
return {
listId: this.listId,
url: this.apiUrl,
};
},
skip() {
return true;
},
fetchPolicy: 'network-only',
},
},
props: {
url: {
type: String,
required: false,
default: '#',
},
title: {
type: String,
required: true,
},
filter: {
type: String,
required: true,
},
noProductsMessage: {
type: String,
required: true,
},
listId: {
type: Number,
required: false,
default: 0,
},
addToCart: {
type: String,
required: true,
},
share: {
type: Boolean,
required: true,
},
customizeText: {
type: String,
required: true,
},
quantityText: {
type: String,
required: true,
},
},
data() {
return {
products: [],
currentWishlist: {},
apiUrl: window.location.href,
selectedSort: '',
};
},
methods: {
/**
* Sort by the select drop down
* @param {String} value The value selected
*/
async changeSelectedSort(value) {
this.selectedSort = value.label;
this.apiUrl = value.url;
},
},
computed: {
productList() {
const productList = this.products.datas.sort_orders.filter(
(sort) => sort.label !== this.products.datas.sort_selected,
);
return productList;
},
currentSort() {
return this.selectedSort !== ''
? this.selectedSort
: this.products.datas.sort_selected;
},
},
mounted() {
if (this.listId) {
this.$apollo.queries.products.skip = false;
}
/**
* Register to the event refetchProducts so if an other component update it, this one can update his list
*
* @param {String} 'refetchProduct' The event I decided to create to communicate between VueJS Apps
*/
EventBus.$on('refetchList', () => {
this.$apollo.queries.products.refetch();
});
EventBus.$on('updatePagination', (payload) => {
this.products = false;
this.apiUrl = payload.page.url;
});
},
};
</script>
<style lang="scss" type="text/scss">
@import '@scss/_variables';
.wishlist {
&-list-loader {
padding: 0 1.25rem;
width: 100%;
}
&-list-empty {
font-size: 30;
text-align: center;
padding: 1.875rem;
padding-bottom: 1.25rem;
font-weight: bold;
color: #000;
}
&-products-container {
.sort-by-row {
min-width: 19.6875rem;
display: flex;
align-items: center;
a {
cursor: pointer;
}
.sort-by {
padding: 0;
}
.products-sort-order {
padding: 0;
}
}
&-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 1.25rem;
}
@at-root #main & .card.page-content {
padding: 0;
margin-bottom: 0.75rem;
}
}
&-products {
&-list {
display: flex;
flex-wrap: wrap;
margin: -1.5625rem;
padding: 1.25rem 2.8125rem;
margin-top: 0;
}
&-count {
color: #7a7a7a;
font-size: 1.375rem;
font-weight: normal;
line-height: 1.875rem;
}
}
}
@media screen and (max-width: 768px) {
.wishlist {
&-products-container {
&-header {
flex-wrap: wrap;
.products-sort-order {
flex: 1;
}
.filter-button {
width: auto;
padding-right: 0;
}
.sort-by-row {
width: 100%;
}
}
.page-content.card {
box-shadow: 0.125rem 0.125rem 0.5rem 0 rgba(0, 0, 0, 0.2);
background-color: #fff;
margin-top: 1.25rem;
}
.wishlist-products-list {
justify-content: center;
margin: 0;
padding: 0.9375rem;
}
}
}
}
</style>

View File

@@ -0,0 +1,69 @@
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import initApp from '@components/init';
import ProductsListContainer from './ProductsListContainer.vue';
const props = [
{
name: 'url',
type: String,
},
{
name: 'title',
type: String,
},
{
name: 'noProductsMessage',
type: String,
},
{
name: 'addToCart',
type: String,
},
{
name: 'customizeText',
type: String,
},
{
name: 'wishlistProducts',
type: String,
},
{
name: 'wishlist',
type: String,
},
{
name: 'share',
type: Boolean,
},
{
name: 'quantityText',
type: String,
},
{
name: 'filter',
type: String,
},
{
name: 'listId',
type: Number,
},
];
initApp(ProductsListContainer, '.wishlist-products-container', props);

View File

@@ -0,0 +1,172 @@
<!--**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*-->
<template>
<div class="wishlist-container">
<div class="wishlist-container-header">
<h1>{{ title }}</h1>
<a
@click="openNewWishlistModal"
class="wishlist-add-to-new text-primary"
>
<i class="material-icons">add_circle_outline</i>
{{ addText }}
</a>
</div>
<section
id="content"
class="page-content card card-block"
>
<list
:items="lists"
:rename-text="renameText"
:share-text="shareText"
:empty-text="emptyText"
:loading="$apollo.queries.lists.loading"
/>
</section>
</div>
</template>
<script>
import List from '@components/List/List';
import getLists from '@graphqlFiles/queries/getlists';
import EventBus from '@components/EventBus';
/**
* This component act as a smart component wich will handle every actions of the list one
*/
export default {
name: 'WishlistContainer',
components: {
List,
},
apollo: {
lists: {
query: getLists,
variables() {
return {
url: this.url,
};
},
},
},
props: {
url: {
type: String,
required: true,
},
title: {
type: String,
required: true,
},
addText: {
type: String,
required: true,
},
renameText: {
type: String,
required: true,
},
emptyText: {
type: String,
required: true,
},
shareText: {
type: String,
required: true,
},
},
data() {
return {
lists: [],
};
},
methods: {
/**
* Send an event to opoen the Create Wishlist Modal
*/
openNewWishlistModal() {
EventBus.$emit('showCreateWishlist');
},
},
mounted() {
/**
* Register to the event refetchList so if an other component update it, this one can update his list
*
* @param {String} 'refetchList' The event I decided to create to communicate between VueJS Apps
*/
EventBus.$on('refetchList', () => {
this.$apollo.queries.lists.refetch();
});
},
};
</script>
<style lang="scss" type="text/scss">
@import '@scss/_variables';
.wishlist {
&-container {
&-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 1.25rem;
}
@at-root #main & .card.page-content {
padding: 0;
margin-bottom: 0.75rem;
}
}
&-add-to-new {
cursor: pointer;
transition: 0.2s ease-out;
font-size: 0.875rem;
letter-spacing: 0;
line-height: 1rem;
&:hover {
opacity: 0.7;
}
i {
margin-right: 0.3125rem;
vertical-align: middle;
margin-top: -0.125rem;
font-size: 1.25rem;
}
}
}
@media screen and (max-width: 768px) {
.wishlist {
&-container {
.page-content.card {
box-shadow: 0.125rem 0.125rem 0.5rem 0 rgba(0, 0, 0, 0.2);
background-color: #fff;
margin-top: 1.25rem;
}
}
}
}
</style>

View File

@@ -0,0 +1,53 @@
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import initApp from '@components/init';
import WishlistContainer from './WishlistContainer';
const props = [
{
name: 'url',
type: String,
},
{
name: 'title',
type: String,
},
{
name: 'addText',
type: String,
},
{
name: 'renameText',
type: String,
},
{
name: 'emptyText',
type: String,
},
{
name: 'homeLink',
type: String,
},
{
name: 'shareText',
type: String,
},
];
initApp(WishlistContainer, '.wishlist-container', props);

View File

@@ -0,0 +1,36 @@
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import {ApolloClient} from 'apollo-client';
import {SchemaLink} from 'apollo-link-schema';
import {InMemoryCache} from 'apollo-cache-inmemory';
import link from './link';
/**
* Enabling client side cache
*/
const cache = new InMemoryCache();
/**
* Creating the ApolloClient managing cache and schemas
*/
export default new ApolloClient({
link: new SchemaLink({schema: link}),
cache,
});

View File

@@ -0,0 +1,31 @@
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import {makeExecutableSchema} from 'graphql-tools';
import resolvers from './resolvers';
import typeDefs from './types';
/**
* Generate SchemaLink that ApolloClient needs to understand schemas
* and link resolvers to schemas
*/
export default makeExecutableSchema({
typeDefs,
resolvers,
});

View File

@@ -0,0 +1,35 @@
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import gql from 'graphql-tag';
export default gql`
mutation addToList($listId: Int!, $productId: Int!, $quantity: Int!, $productAttributeId: Int!, $url: String!) {
addToList(
listId: $listId
productId: $productId
quantity: $quantity
productAttributeId: $productAttributeId
url: $url
) {
success
message
}
}
`;

View File

@@ -0,0 +1,33 @@
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import gql from 'graphql-tag';
export default gql`
mutation createList($name: String!, $url: String!) {
createList(name: $name, url: $url) {
message
datas {
name
id_wishlist
}
success
}
}
`;

View File

@@ -0,0 +1,29 @@
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import gql from 'graphql-tag';
export default gql`
mutation deleteList($listId: Int!, $url: String!) {
deleteList(listId: $listId, url: $url) {
success
message
}
}
`;

View File

@@ -0,0 +1,29 @@
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import gql from 'graphql-tag';
export default gql`
mutation removeFromList($listId: Int!, $productId: Int!, $productAttributeId: Int!, $url: String!) {
removeFromList(listId: $listId, productId: $productId, productAttributeId: $productAttributeId, url: $url) {
success
message
}
}
`;

View File

@@ -0,0 +1,29 @@
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import gql from 'graphql-tag';
export default gql`
mutation renameList($name: String!, $url: String!, $listId: Int!) {
renameList(name: $name, url: $url, listId: $listId) {
message
success
}
}
`;

View File

@@ -0,0 +1,28 @@
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import gql from 'graphql-tag';
export default gql`
mutation shareList($listId: Int!, $userId: Int!) {
shareList(listId: $listId, userId: $userId) {
url
}
}
`;

View File

@@ -0,0 +1,33 @@
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import gql from 'graphql-tag';
export default gql`
query lists($url: String!) {
lists(url: $url) {
id_wishlist
name
listUrl
shareUrl
nbProducts
default
}
}
`;

View File

@@ -0,0 +1,28 @@
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import gql from 'graphql-tag';
export default gql`
query getProducts($listId: Int!, $url: String!) {
products(listId: $listId, url: $url) {
datas
}
}
`;

View File

@@ -0,0 +1,197 @@
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import EventBus from '@components/EventBus';
import headers from '@constants/headers';
import GraphQLJSON, {GraphQLJSONObject} from 'graphql-type-json';
/**
* Resolvers linked to schemas definitions
*/
export default {
JSON: GraphQLJSON,
JSONObject: GraphQLJSONObject,
Query: {
/**
* Get product from a list
*/
products: async (root, {url}) => {
const response = await fetch(`${url}&from-xhr`, {
headers: headers.products,
});
const datas = await response.json();
EventBus.$emit('paginate', {
detail: {
total: datas.pagination.total_items,
minShown: datas.pagination.items_shown_from,
maxShown: datas.pagination.items_shown_to,
pageNumber: datas.pagination.pages_count,
pages: datas.pagination.pages,
display: datas.pagination.should_be_displayed,
currentPage: datas.pagination.current_page,
},
});
window.history.pushState(datas, document.title, datas.current_url);
window.scrollTo(0, 0);
return {
datas: {
products: datas.products,
pagination: datas.pagination,
current_url: datas.current_url,
sort_orders: datas.sort_orders,
sort_selected: datas.sort_selected,
},
};
},
/**
* Get every lists from User
*/
lists: async (root, {url}) => {
const response = await fetch(url);
const datas = await response.json();
return datas.wishlists;
},
},
Mutation: {
/**
* Create a list based on a name and an userId
*
* @param {String} name The name of the list
* @param {Int} userId The ID of the user you want to create a list on
*/
createList: async (root, {name, url}) => {
const nameEncoded = encodeURIComponent(name);
const response = await fetch(`${url}&params[name]=${nameEncoded}`, {
method: 'POST',
});
const datas = await response.json();
return datas;
},
/**
* Rename a list
*
* @param {String} {name New name of the list
* @param {Int} userId Id of the user
* @param {Int} listId} ID of the list to be renamed
*/
renameList: async (root, {name, listId, url}) => {
const response = await fetch(`${url}&params[name]=${name}&params[idWishList]=${listId}`, {
method: 'POST',
});
const datas = await response.json();
return datas;
},
/**
* Add a product to a list
*
* @param {Int} listId The list id
* @param {Int} userId The user id
* @param {Int} productId The product id to be added to the list id
*
* @returns {JSON} A success or failed response
*/
addToList: async (root, {
listId, url, productId, quantity, productAttributeId,
}) => {
/* eslint-disable */
const response = await fetch(
`${url}&params[id_product]=${productId}&params[idWishList]=${listId}&params[quantity]=${quantity}&params[id_product_attribute]=${productAttributeId}`,
{
method: 'POST'
}
);
/* eslint-enable */
const datas = await response.json();
if (datas.success) {
// eslint-disable-next-line
productsAlreadyTagged.push({
id_product: productId.toString(),
id_wishlist: listId.toString(),
quantity: quantity.toString(),
id_product_attribute: productAttributeId.toString(),
});
}
return datas;
},
/**
* Remove a product from a list
*
* @param {Int} listId The list id
* @param {Int} userId The user id
* @param {Int} productId The product id to be removed from the list id
*
* @returns {JSON} A success or failed response
*/
removeFromList: async (root, {
listId, productId, url, productAttributeId,
}) => {
/* eslint-disable */
const response = await fetch(
`${url}&params[id_product]=${productId}&params[idWishList]=${listId}&params[id_product_attribute]=${productAttributeId}`,
{
method: 'POST'
}
);
/* eslint-enable */
const datas = await response.json();
if (datas.success) {
// eslint-disable-next-line
productsAlreadyTagged = productsAlreadyTagged.filter(
(e) => e.id_product !== productId.toString()
|| (e.id_product_attribute !== productAttributeId.toString() && e.id_product === productId.toString())
|| e.id_wishlist !== listId.toString(),
);
}
return datas;
},
/**
* Remove a list
*
* @param {Int} {listId} The list id
*
* @returns {JSON} a JSON success or failed response
*/
deleteList: async (root, {listId, url}) => {
const response = await fetch(`${url}&params[idWishList]=${listId}`, {
method: 'POST',
});
const datas = await response.json();
return datas;
},
},
};

View File

@@ -0,0 +1,64 @@
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
export default `
scalar JSON
scalar JSONObject
type List {
id_wishlist: Int
name: String
listUrl: String
shareUrl: String
default: Int
nbProducts: Int
}
type ShareUrl {
url: String
}
type CreateResponse {
datas: List
success: Boolean!
message: String!
}
type ProductListResponse {
datas: JSONObject
}
type Response {
success: Boolean!
message: String!
}
type Query {
products(listId: Int!, url: String!): ProductListResponse
lists(url: String!): [List]
}
type Mutation {
createList(name: String!, url: String!): CreateResponse
shareList(listId: String!, userId: Int!): ShareUrl
renameList(name: String!, url: String!, listId: Int!): Response
addToList(listId: Int!, productId: Int!, quantity: Int!, productAttributeId: Int!, url: String!): Response
removeFromList(listId: Int!, productId: Int!, productAttributeId: Int!, url: String!): Response
deleteList(listId: Int!, url: String!): Response
}
`;

View File

@@ -0,0 +1,54 @@
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import initVueButtons from '@components/Button';
import removeFromWishlistUrl from 'removeFromWishlistUrl';
const initButtons = () => {
const products = document.querySelectorAll('.js-product-miniature');
products.forEach((product) => {
const wishlistButton = document.createElement('div');
wishlistButton.classList.add('wishlist-button');
wishlistButton.dataset.productId = product.dataset.idProduct;
wishlistButton.dataset.url = removeFromWishlistUrl;
wishlistButton.dataset.productAttributeId = product.dataset.idProductAttribute;
wishlistButton.dataset.checked = false;
product.querySelector('.thumbnail-container').append(wishlistButton);
});
};
initButtons();
initVueButtons();
const productList = document.querySelectorAll('#products, .featured-products');
const config = {attributes: false, childList: true};
productList.forEach((e) => {
const callback = function () {
initButtons();
initVueButtons();
};
const observer = new MutationObserver(callback);
observer.observe(e, config);
});

View File

@@ -0,0 +1,23 @@
.wishlist {
&-footer {
&-links {
margin-bottom: 3.125rem;
> a {
font-size: 0.875rem;
letter-spacing: 0;
line-height: 1.1875rem;
&:not(:first-child) {
margin-left: 1.25rem;
}
i {
font-size: 1.25rem;
margin-right: 0.25rem;
vertical-align: middle;
}
}
}
}
}

View File

@@ -0,0 +1,114 @@
.wishlist-modal {
display: none;
opacity: 0;
pointer-events: none;
z-index: 0;
&.show {
display: block;
opacity: 1;
pointer-events: all;
z-index: 1051;
+ .modal-backdrop {
pointer-events: all;
}
}
&.fade .modal-dialog {
max-width: 34.375rem;
transform: translateY(0);
}
.close {
font-weight: 400;
color: #7a7a7a;
opacity: 1;
font-size: 2.25rem;
&:hover {
opacity: 0.6;
}
}
.modal {
&-header {
padding: 0.625rem 1.875rem;
display: flex;
align-items: center;
justify-content: space-between;
border: none;
h5 {
color: #232323;
font-size: 1.375rem;
font-weight: bold;
letter-spacing: 0;
line-height: 1.875rem;
}
&::after {
content: none;
}
}
&-text {
color: #232323;
font-size: 0.875rem;
letter-spacing: 0;
line-height: 1.875rem;
}
&-body {
padding: 0.9375 1.875rem;
.form-group {
margin-bottom: 0;
.form-control {
border-radius: 0;
background: none;
color: black;
}
}
}
&-content {
width: 100%;
}
&-cancel {
margin-right: 0.625rem;
&:hover {
opacity: 0.7;
}
}
&-footer {
padding: 0.75rem 1.875rem;
border: none;
padding-bottom: 1.875rem;
.btn {
text-transform: none;
}
}
&-backdrop {
pointer-events: none;
&.in {
pointer-events: all;
}
}
}
+ .modal-backdrop {
pointer-events: none;
&.in {
pointer-events: all;
}
}
}

View File

@@ -0,0 +1,12 @@
.products {
article {
.wishlist {
&-button-add {
position: absolute;
top: 0.635rem;
right: 0.635rem;
z-index: 10;
}
}
}
}

View File

@@ -0,0 +1,48 @@
.lang-rtl {
.products {
article {
.wishlist {
&-button-add {
right: inherit;
left: 0.635rem;
}
}
}
}
.wishlist {
&-button {
&-product {
margin-left: 0;
margin-right: 1.25rem;
}
}
&-list {
&-item {
.dropdown-menu {
right: inherit;
left: 1.25rem;
}
&-right {
.dropdown-menu {
> button {
text-align: right;
}
}
}
}
}
}
}

View File

@@ -0,0 +1 @@
$blue: #2fb5d2;

View File

@@ -0,0 +1,131 @@
.wishlist-modal {
display: none;
opacity: 0;
pointer-events: none;
z-index: 0;
}
.wishlist-modal.show {
display: block;
opacity: 1;
pointer-events: all;
z-index: 1051;
}
.wishlist-modal.show + .modal-backdrop {
pointer-events: all;
}
.wishlist-modal.fade .modal-dialog {
max-width: 34.375rem;
transform: translateY(0);
}
.wishlist-modal .close {
font-weight: 400;
color: #7a7a7a;
opacity: 1;
font-size: 2.25rem;
}
.wishlist-modal .close:hover {
opacity: 0.6;
}
.wishlist-modal .modal-header {
padding: 0.625rem 1.875rem;
display: flex;
align-items: center;
justify-content: space-between;
border: none;
}
.wishlist-modal .modal-header h5 {
color: #232323;
font-size: 1.375rem;
font-weight: bold;
letter-spacing: 0;
line-height: 1.875rem;
}
.wishlist-modal .modal-header::after {
content: none;
}
.wishlist-modal .modal-text {
color: #232323;
font-size: 0.875rem;
letter-spacing: 0;
line-height: 1.875rem;
}
.wishlist-modal .modal-body {
padding: 0.9375 1.875rem;
}
.wishlist-modal .modal-body .form-group {
margin-bottom: 0;
}
.wishlist-modal .modal-body .form-group .form-control {
border-radius: 0;
background: none;
color: black;
}
.wishlist-modal .modal-content {
width: 100%;
}
.wishlist-modal .modal-cancel {
margin-right: 0.625rem;
}
.wishlist-modal .modal-cancel:hover {
opacity: 0.7;
}
.wishlist-modal .modal-footer {
padding: 0.75rem 1.875rem;
border: none;
padding-bottom: 1.875rem;
}
.wishlist-modal .modal-footer .btn {
text-transform: none;
}
.wishlist-modal .modal-backdrop {
pointer-events: none;
}
.wishlist-modal .modal-backdrop.in {
pointer-events: all;
}
.wishlist-modal + .modal-backdrop {
pointer-events: none;
}
.wishlist-modal + .modal-backdrop.in {
pointer-events: all;
}
.products article .wishlist-button-add {
position: absolute;
top: 0.635rem;
right: 0.635rem;
z-index: 10;
}
.wishlist-footer-links {
margin-bottom: 3.125rem;
}
.wishlist-footer-links > a {
font-size: 0.875rem;
letter-spacing: 0;
line-height: 1.1875rem;
}
.wishlist-footer-links > a:not(:first-child) {
margin-left: 1.25rem;
}
.wishlist-footer-links > a i {
font-size: 1.25rem;
margin-right: 0.25rem;
vertical-align: middle;
}
.lang-rtl .products article .wishlist-button-add {
right: inherit;
left: 0.635rem;
}
.lang-rtl .wishlist-button-product {
margin-left: 0;
margin-right: 1.25rem;
}
.lang-rtl .wishlist-list-item .dropdown-menu {
right: inherit;
left: 1.25rem;
}
.lang-rtl .wishlist-list-item-right .dropdown-menu > button {
text-align: right;
}/*# sourceMappingURL=common.css.map */

View File

@@ -0,0 +1 @@
{"version":3,"sources":["_modal.scss","common.css","_product.scss","_footer-links.scss","_rtl.scss"],"names":[],"mappings":"AAAA;EACE,aAAA;EACA,UAAA;EACA,oBAAA;EACA,UAAA;ACCF;ADCE;EACE,cAAA;EACA,UAAA;EACA,mBAAA;EACA,aAAA;ACCJ;ADCI;EACE,mBAAA;ACCN;ADGE;EACE,oBAAA;EACA,wBAAA;ACDJ;ADIE;EACE,gBAAA;EACA,cAAA;EACA,UAAA;EACA,kBAAA;ACFJ;ADII;EACE,YAAA;ACFN;ADOI;EACE,0BAAA;EACA,aAAA;EACA,mBAAA;EACA,8BAAA;EACA,YAAA;ACLN;ADOM;EACE,cAAA;EACA,mBAAA;EACA,iBAAA;EACA,iBAAA;EACA,qBAAA;ACLR;ADQM;EACE,aAAA;ACNR;ADUI;EACE,cAAA;EACA,mBAAA;EACA,iBAAA;EACA,qBAAA;ACRN;ADWI;EACE,wBAAA;ACTN;ADWM;EACE,gBAAA;ACTR;ADWQ;EACE,gBAAA;EACA,gBAAA;EACA,YAAA;ACTV;ADcI;EACE,WAAA;ACZN;ADeI;EACE,sBAAA;ACbN;ADeM;EACE,YAAA;ACbR;ADiBI;EACE,yBAAA;EACA,YAAA;EACA,wBAAA;ACfN;ADiBM;EACE,oBAAA;ACfR;ADmBI;EACE,oBAAA;ACjBN;ADmBM;EACE,mBAAA;ACjBR;ADsBE;EACE,oBAAA;ACpBJ;ADsBI;EACE,mBAAA;ACpBN;;ACvFM;EACE,kBAAA;EACA,aAAA;EACA,eAAA;EACA,WAAA;AD0FR;;AE/FI;EACE,uBAAA;AFkGN;AEhGM;EACE,mBAAA;EACA,iBAAA;EACA,sBAAA;AFkGR;AEhGQ;EACE,oBAAA;AFkGV;AE/FQ;EACE,kBAAA;EACA,qBAAA;EACA,sBAAA;AFiGV;;AG1GQ;EACE,cAAA;EACA,cAAA;AH6GV;AGnGM;EACE,cAAA;EACA,qBAAA;AHqGR;AG7FQ;EACE,cAAA;EACA,aAAA;AH+FV;AGxFY;EACE,iBAAA;AH0Fd","file":"common.css"}

View File

@@ -0,0 +1,5 @@
@import '_variables';
@import '_modal';
@import '_product';
@import '_footer-links';
@import '_rtl';