add to cart links for products in home

This commit is contained in:
2021-06-09 13:06:52 +02:00
parent d9d2180f94
commit 352223500f
25 changed files with 561 additions and 249 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +1,83 @@
.overlay[data-v-b98ce164]{background-color:rgba(0,0,0,0.8);position:fixed;top:0;right:0;bottom:0;left:0;z-index:99999}.modal[data-v-b98ce164]{background-color:#fff;position:absolute;box-sizing:border;max-width:80vw;max-height:70vh;top:0;right:0;bottom:0;left:0;margin:auto;padding:1em;border-radius:3px;box-shadow:2px 2px}
.overlay[data-v-b98ce164] {
background-color: rgba(0, 0, 0, 0.8);
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 99999;
}
.modal[data-v-b98ce164] {
background-color: #fff;
position: absolute;
box-sizing: border;
max-width: 80vw;
max-height: 70vh;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
padding: 1em;
border-radius: 3px;
box-shadow: 2px 2px;
}
.form-item[data-v-7bb795f8],.form-actions[data-v-7bb795f8]{display:inline-block;max-width:32%;margin:0}input[data-v-7bb795f8]{box-sizing:border-box;max-width:100%}div.description[data-v-7bb795f8]{display:none}.form-item-persistent-login[data-v-7bb795f8]{display:none}
.form-item[data-v-7bb795f8],
.form-actions[data-v-7bb795f8] {
display: inline-block;
max-width: 32%;
margin: 0;
}
input[data-v-7bb795f8] {
box-sizing: border-box;
max-width: 100%;
}
div.description[data-v-7bb795f8] {
display: none;
}
.form-item-persistent-login[data-v-7bb795f8] {
display: none;
}
.form-type-email[data-v-2acc57a0],.form-type-password-confirm[data-v-2acc57a0]{max-width:30%;margin:0}.form-type-password input.weak[data-v-2acc57a0]{border-width:2px;border-color:red !important}.form-type-password input.weak[data-v-2acc57a0]:focus{outline:none}.form-type-password input.medium[data-v-2acc57a0]{border-width:2px;border-color:orange !important}.form-type-password input.medium[data-v-2acc57a0]:focus{outline:none}.form-type-password input.strong[data-v-2acc57a0]{border-width:2px;border-color:green !important}.form-type-password input.strong[data-v-2acc57a0]:focus{outline:none}input[data-v-2acc57a0]{box-sizing:content-box;max-width:100%}label[data-v-2acc57a0],div.description[data-v-2acc57a0]{display:none}
.form-type-email[data-v-2acc57a0],
.form-type-password-confirm[data-v-2acc57a0] {
max-width: 30%;
margin: 0;
}
.form-type-password input.weak[data-v-2acc57a0] {
border-width: 2px;
border-color: red !important;
}
.form-type-password input.weak[data-v-2acc57a0]:focus {
outline: none;
}
.form-type-password input.medium[data-v-2acc57a0] {
border-width: 2px;
border-color: orange !important;
}
.form-type-password input.medium[data-v-2acc57a0]:focus {
outline: none;
}
.form-type-password input.strong[data-v-2acc57a0] {
border-width: 2px;
border-color: green !important;
}
.form-type-password input.strong[data-v-2acc57a0]:focus {
outline: none;
}
input[data-v-2acc57a0] {
box-sizing: content-box;
max-width: 100%;
}
label[data-v-2acc57a0],
div.description[data-v-2acc57a0] {
display: none;
}
fieldset[data-v-340aa566]{padding:0;margin:0;border:none}
fieldset[data-v-340aa566] {
padding: 0;
margin: 0;
border: none;
}

File diff suppressed because one or more lines are too long

View File

@@ -1239,37 +1239,22 @@ article.node--type-frontpage{
.field--name-computed-products-reference{
display: flex;
flex-flow: row nowrap;
@include col-mediaquery-max(3){
@include col-mediaquery-max(5){
flex-flow: column;
justify-content: flex-start;
}
>.field__item{
flex:0 0 50%;
flex:0 0 33%;
text-align: center;
padding: 2em 0;
a.btn{
@include btn;
background-color: #fff;
}
&:nth-child(1){
background-color: $color-base;
a.btn{
color: $color-base;
}
}
&:nth-child(2){
background-color: $color-webshowroom;
a.btn{
color: $color-webshowroom;
}
}
color: #fff;
color: #fff;
position: relative;
height:18em;
height:23em;
>article{
position: absolute;
top:50%; left:50%;
transform: translate(-50%, -50%);
width:80%;
}
.field--name-title{
@extend %front-col-field__label;
@@ -1286,8 +1271,77 @@ article.node--type-frontpage{
margin: 0;
}
}
.field--name-variations{
.field__item{
>div{
display: flex;
flex-direction: row;
justify-content: center;
align-items: baseline;
.form-actions,p{
margin:0;
}
.field--name-field-description{
margin-right: 0.5em;
p{
font-size: 1.512em;
font-weight: 800;
color: #fff;
white-space: nowrap;
text-align: right;
}
}
input.button--add-to-cart{
@include btn;
}
}
}
}
a.btn{
@include btn;
background-color: #fff;
}
&:nth-child(1){
background-color: $color-base;
a.btn{
color: $color-base;
}
.field--name-variations{
.field__item{
>div{
input.button--add-to-cart{
color: $color-base;
}
}
}
}
}
&:nth-child(2){
background-color: $color-webshowroom;
a.btn{
color: $color-webshowroom;
}
.field--name-variations{
.field__item{
>div{
input.button--add-to-cart{
color: $color-webshowroom;
}
}
}
}
}
&:nth-child(3){
background-color: $color-showrooms;
a.btn{
color: $color-showrooms;
}
>article{
max-width: 345px;
}
}
@include col-mediaquery-max(3){
@include col-mediaquery-max(5){
flex: 0 0 auto;
padding:0;
// height: auto;
@@ -2176,7 +2230,7 @@ article.card{
article.product,
.views-row{
flex:0 0 50%;
flex:0 0 33%;
text-align: center;
padding: 2em 0;
>header{
@@ -2233,6 +2287,12 @@ article.card{
aside .variation button{
color: $color-webshowroom;
}
}
&:nth-child(3){
background-color: $color-showrooms;
aside .variation button{
color: $color-showrooms;
}
}
}
@include col-mediaquery-max(3){
@@ -2247,99 +2307,128 @@ article.card{
.modal{
position: relative;
}
#pricing-modal-login-register{
position: relative;
width: 100%;
text-align: left;
h2{
margin: 0.4em 0 1.1em;
padding-right: 4em;
font-size: 1.2em;
font-weight: 300;
}
// #pricing-modal-login-register{
// position: relative;
// width: 100%;
// text-align: left;
//
// .vm--modale-loginregister{
// }
#login-register{
width: 100%;
display: flex;
flex-flow: row nowrap;
>section{
flex:0 0 250px;
form{
.form-item, .form-actions {
margin: 0.5em 0;
max-width: none;
}
.form-type-email,
.form-type-password,
.form-actions{
display:block;
}
input[type="email"],
input[type="password"]{
max-width: 11em;
}
&#user-login-form #edit-pass--description,
#edit-pass-pass1--description{
display: block;
max-width: 16em;
font-size: 0.693em;
}
span.login-message,
span.register-message{
color: red;
font-size: 0.693em;
line-height: 1.2;
display: block;
padding: 0.8em 0 0 0;
}
span.login-message[v-if="loginMessage"],
span.register-message[v-if="registerMessage"]{
display: none;
}
}
}
section.login{
form{
>div{
// display: block;
}
}
}
section.register{
}
}
@include col-mediaquery-max(3){
height:100%;
overflow-y: auto;
#login-register{
flex-flow: column;
padding: 1em;
box-sizing: content-box;
width: 100%;
h2{
margin: 0.4em 0 1.1em;
padding-right: 4em;
font-size: 1.2em;
font-weight: 300;
}
>div.wrapper{
display: flex;
flex-flow: row nowrap;
>section{
flex:0 0 auto;
form{
input[type="email"],
flex:0 0 50%;
form{
.form-item, .form-actions {
margin: 0.5em 0;
max-width: none;
}
.form-type-email,
.form-type-password,
.form-actions{
display:block;
}
input[type="email"],
input[type="password"]{
max-width: 90%;
width: 90%;
max-width: 11em;
}
&#user-login-form #edit-pass--description,
#edit-pass-pass1--description{
max-width: 90%;
width: 90%;
display: block;
max-width: 16em;
font-size: 0.693em;
}
}
&.login{
padding-bottom: 0.5em;
span.login-message,
span.register-message{
color: red;
font-size: 0.693em;
line-height: 1.2;
display: block;
padding: 0.8em 0 0 0;
}
span.login-message[v-if="loginMessage"],
span.register-message[v-if="registerMessage"]{
display: none;
}
}
}
section.login{
form{
>div{
// display: block;
}
}
}
section.register{
}
@include col-mediaquery-max(3){
flex-flow: column;
>section{
flex:0 0 auto;
form{
input[type="email"],
input[type="password"]{
max-width: 90%;
width: 90%;
}
&#user-login-form #edit-pass--description,
#edit-pass-pass1--description{
max-width: 90%;
width: 90%;
}
}
&.login{
padding-bottom: 0.5em;
}
}
}
}
}
@include col-mediaquery-max(4, landscape){
h2{
margin: 0 0 0.5em;
}
}
}
// @include col-mediaquery-max(3){
// height:100%;
// overflow-y: auto;
// #login-register{
// flex-flow: column;
// >section{
// flex:0 0 auto;
// form{
// input[type="email"],
// input[type="password"]{
// max-width: 90%;
// width: 90%;
// }
// &#user-login-form #edit-pass--description,
// #edit-pass-pass1--description{
// max-width: 90%;
// width: 90%;
// }
// }
// &.login{
// padding-bottom: 0.5em;
// }
// }
// }
// }
// @include col-mediaquery-max(4, landscape){
// h2{
// margin: 0 0 0.5em;
// }
// }
// }
#pricing{
@include col-mediaquery-max(3){
.overlay > .modal{

View File

@@ -182,6 +182,25 @@ function materiotheme_preprocess_page(&$vars){
// kint($vars['attributes']);
// }
/**
* Implements hook_form_alter
*/
function materiotheme_form_alter(&$form, FormStateInterface $form_state, $form_id) {
// commerce_order_item_variation_cart_form_form_commerce_product_variation_3
preg_match('/commerce_order_item_variation_cart_form_form_commerce_product_variation_(\d+)/', $form_id, $matches);
if ($matches && isset($matches[1]) && $variation_id = $matches[1]) {
// if (isset($form['#entity_type']) && $form['#entity_type'] == "commerce_order_item") {
// $product = $form_state->storage['product'];
// $storage = &$form_state->getStorage();
// $product = $storage['product'];
// $variations = $product->fields['variations'];
$form['actions']['submit']['#attributes'] += array(
"@click.prevent.stop" => "checkaddtocart(\$event, $variation_id)"
);
}
}
/**
* Implements hook_form_alter
*/

View File

@@ -0,0 +1,33 @@
{#
/**
* @file
*
* Default product template.
*
* Available variables:
* - attributes: HTML attributes for the wrapper.
* - product: The rendered product fields.
* Use 'product' to print them all, or print a subset such as
* 'product.title'. Use the following code to exclude the
* printing of a given field:
* @code
* {{ product|without('title') }}
* @endcode
* - product_entity: The product entity.
* - product_url: The product URL.
*
* @ingroup themeable
*/
#}
{#
<article{{ attributes }}>
{{- product -}}
</article>
#}
<article{{ attributes }}>
{{- product|without('variation_attributes') -}}
<a href="mailto:info@materio.com?subject=Multi-Joueurs" class="btn">
{% trans %}Demander un devis{% endtrans %}
</a>
</article>

View File

@@ -19,9 +19,14 @@
* @ingroup themeable
*/
#}
{#
<article{{ attributes }}>
{{- product|without('variation_attributes') -}}
<a href="/{{ language }}/pricing" @click.prevent="onClickLink" class="btn">
{% trans %}View Option Details{% endtrans %}
</a>
</article>
#}
<article{{ attributes }}>
{{- product -}}
</article>

View File

@@ -21,7 +21,7 @@
#}
<div{{ attributes }}>
{{- product_variation -}}
<a href="/pricing" @click.prevent="onClickLink" class="btn">
<!-- <a href="/pricing" @click.prevent="onClickLink" class="btn">
{% trans %}View Option Details{% endtrans %}
</a>
</a> -->
</div>

View File

@@ -25,7 +25,7 @@
</aside>
<Modal
<!-- <Modal
v-if="showLoginModal"
@close="closeModal"
:styles="{width:'500px', height:'350px'}"
@@ -38,132 +38,44 @@
@onRegistered="onRegistered"
/>
</section>
</Modal>
</Modal> -->
</article>
</template>
<script>
import { REST } from 'vuejs/api/rest-axios'
// import { REST } from 'vuejs/api/rest-axios'
import router from 'vuejs/route'
import { mapState, mapActions } from 'vuex'
import Modal from 'vuejs/components/Helper/Modal'
import LoginRegister from 'vuejs/components/Helper/LoginRegister'
// import { mapState, mapActions } from 'vuex'
import productsMixins from 'vuejs/components/productsMixins'
// import Modal from 'vuejs/components/Helper/Modal'
// import LoginRegister from 'vuejs/components/Helper/LoginRegister'
let basePath = drupalSettings.path.baseUrl + drupalSettings.path.pathPrefix;
// let basePath = drupalSettings.path.baseUrl + drupalSettings.path.pathPrefix;
export default {
name: "Product",
router,
props: ['product'],
mixins: [productsMixins],
data(){
return {
quantity: 1,
showLoginModal:false
// showLoginModal:false
}
},
computed: {
...mapState({
isloggedin: state => state.User.isloggedin,
isAdherent: state => state.User.isAdherent,
csrftoken: state => state.User.csrftoken
})
},
methods:{
// ...mapActions({
// userLogin: 'User/userLogin'
// }),
closeModal () {
this.showLoginModal = false;
},
checkaddtocart(e, variation_id) {
console.log('checkaddtocart')
if (!this.isloggedin) {
// show popup for login or register
// login or register event will be catched by onLogedin or onRegistered
this.showLoginModal = variation_id
} else {
// if already logedin directly goes to cart operations
this.addtocart(variation_id)
}
},
// event bubbled from modal login form
onLogedIn (variation_id) {
console.log('Product: onLogedIn. variation_id', variation_id)
this.addtocart(variation_id)
},
// event bubbled from modal register form
onRegistered (variation_id) {
console.log('Product: onRegistered. variation_id', variation_id)
this.addtocart(variation_id)
},
getCarts () {
// this is bugging on stage
return REST.get(`/cart?_format=json`, {}, {'X-CSRF-Token':this.csrftoken})
// .then(({ data }) => {
// console.log('current user carts: data', data)
// })
.catch((error) => {
console.warn('Issue with get cart', error)
Promise.reject(error)
})
},
deleteCart (order_id) {
console.log('deleting cart ', order_id)
return REST.delete(`/cart/${order_id}/items?_format=json`)
.then(({ data }) => {
console.log(`product cart ${order_id} deleted: data`, data)
})
.catch((error) => {
console.warn(`Issue with cart ${order_id} deleting`, error)
Promise.reject(error)
})
},
clearCarts (data) {
let promises = [];
// clear each cart as a promise
for (let i = 0; i < data.length; i++) {
promises.push(this.deleteCart(data[i].order_id))
}
// return all the promises as one
return Promise.all(promises)
},
addtocart (variation_id) {
console.log('addtocart')
this.getCarts()
.then(({data}) => {
console.log('current user carts: data', data)
this.clearCarts(data)
.then(() => {
console.log('all carts cleared')
// fill the cart with new product
REST.post(`/cart/add?_format=json`, [{
"purchased_entity_type": "commerce_product_variation",
"purchased_entity_id": variation_id,
"quantity": this.quantity
}])
.then(({ data }) => {
console.log('product added to cart: data', data)
this.closeModal()
// redirect to /cart
// window.location.href = "/cart"
// TODO: redirect to checkout instead of cart
window.location.href = `/checkout/${data[0].order_id}/order_information`
})
.catch((error) => {
console.warn('Issue with product add to cart', error)
Promise.reject(error)
})
})
})
}
},
components: {
Modal,
LoginRegister
}
// ,
// methods:{
// // ...mapActions({
// // userLogin: 'User/userLogin'
// // })
// }
// ,
// components: {
// Modal,
// LoginRegister
// }
}
</script>

View File

@@ -25,7 +25,7 @@ export default {
// vuejs attributes a inserted by form alter in same module
MA.get(`/materio_user/login_form`)
.then(({data}) => {
console.log('getLoginForm data', data)
// console.log('getLoginForm data', data)
this.form = Vue.compile(data.rendered)
this.$options.staticRenderFns = [];
this._staticTrees = [];

View File

@@ -38,7 +38,7 @@ export default {
// vuejs attributes a inserted by form alter in same module
MA.get(`/materio_user/register_form`)
.then(({data}) => {
console.log("getRegisterForm data", data)
// console.log("getRegisterForm data", data)
this.form = Vue.compile(data.rendered)
this.$options.staticRenderFns = [];
this._staticTrees = [];

View File

@@ -1,13 +1,16 @@
<template>
<div id="login-register">
<section class="login">
<h3>{{ $t("default.Login") }} </h3>
<LoginForm @onLogedIn="onLogedIn" />
</section>
<section class="register">
<h3>{{ $t("default.Register a new account") }}</h3>
<RegisterForm @onRegistered="onRegistered" />
</section>
<h2>{{ $t("materio.Please login or create a new account") }}</h2>
<div class="wrapper">
<section class="login">
<h3>{{ $t("default.Login") }} </h3>
<LoginForm @onLogedIn="onLogedIn" />
</section>
<section class="register">
<h3>{{ $t("default.Register a new account") }}</h3>
<RegisterForm @onRegistered="onRegistered" />
</section>
</div>
</div>
</template>
@@ -24,17 +27,19 @@ export default {
password:null,
registerEmail:null
}),
props:['callbackargs'],
props:['callbackargs', 'onLogedInBack', 'onRegisteredBack'],
methods: {
...mapActions({
userLogin: 'User/userLogin',
userRegister: 'User/userRegister'
}),
onLogedIn () {
this.$emit('onLogedIn', this.callbackargs)
// this.$emit('onLogedIn', this.callbackargs)
this.onLogedInBack(this.callbackargs)
},
onRegistered () {
this.$emit('onRegistered', this.callbackargs)
// this.$emit('onRegistered', this.callbackargs)
this.onRegisteredBack(this.callbackargs)
}
},
components: {

View File

@@ -80,7 +80,7 @@
></div>
</div>
<aside class="linked-materials">
<h3 class="field__label">{{$t("materio.Linked Materials")}}</h3>
<h3 class="field__label">{{ $t('materio.Linked Materials') }}</h3>
<div class="cards-list">
<ul class="">
<li v-for="node in article.linked_materials" v-bind:key="node.id">

View File

@@ -1,9 +1,11 @@
<script>
import Vue from 'vue'
import productsMixins from 'vuejs/components/productsMixins'
export default {
props: ['html', 'full'], // get the html from parent with props
mixins: [productsMixins],
data() {
return {
template: null, // compiled template from html used in render
@@ -151,7 +153,7 @@ export default {
},
watch: {
html: function(val) {
console.log('html prop changed', val)
// console.log('html prop changed', val)
this.compileTemplate()
}
}

View File

@@ -0,0 +1,138 @@
// https://forum.vuejs.org/t/how-to-use-helper-functions-for-imported-modules-in-vuejs-vue-template/6266/5
import { REST } from 'vuejs/api/rest-axios'
import Modal from 'vuejs/components/Helper/Modal'
import LoginRegister from 'vuejs/components/Helper/LoginRegister'
import { mapState } from 'vuex'
export default {
components: {
Modal,
LoginRegister
},
computed: {
...mapState({
isloggedin: state => state.User.isloggedin,
isAdherent: state => state.User.isAdherent,
csrftoken: state => state.User.csrftoken
})
},
methods: {
closeModal () {
this.showLoginModal = false
},
checkaddtocart (e, variation_id) {
console.log('checkaddtocart', e, variation_id, this.isloggedin)
if (!this.isloggedin) {
// show popup for login or register
// login or register event will be catched by onLogedin or onRegistered
// this.showLoginModal = variation_id
this.showLoginModal(variation_id)
} else {
// if already logedin directly goes to cart operations
this.addtocart(variation_id)
}
},
showLoginModal (variation_id) {
this.$modal.show(
LoginRegister,
// props
{
// item: this.item,
callbackargs: { variation_id: variation_id },
// close: (variation_id) => { this.closeModal(variation_id) },
onLogedInBack: (cba) => { this.onLogedIn(cba.variation_id) },
onRegisteredBack: (cba) => { this.onRegistered(cba.variation_id) }
// // not really an event
// // more a callback function passed as prop to the component
// addNoteId: (id) => {
// // no needs to refresh the entire item via searchresults
// // plus if we are in article, there is not searchresults
// // this.refreshItem({id: this.item.id})
// // instead create the note id directly
// this.item.note = { id: id }
// }
},
// settings
{
// name: `modal-${this.item.id}`,
draggable: false,
classes: 'vm--modale-loginregister',
width: '500px',
height: '350px'
}
)
},
// event bubbled from modal login form
onLogedIn (variation_id) {
console.log('Product: onLogedIn. variation_id', variation_id)
this.addtocart(variation_id)
},
// event bubbled from modal register form
onRegistered (variation_id) {
console.log('Product: onRegistered. variation_id', variation_id)
this.addtocart(variation_id)
},
getCarts () {
// this is bugging on stage
return REST.get('/cart?_format=json', {}, { 'X-CSRF-Token': this.csrftoken })
// .then(({ data }) => {
// console.log('current user carts: data', data)
// })
.catch((error) => {
console.warn('Issue with get cart', error)
Promise.reject(error)
})
},
deleteCart (order_id) {
console.log('deleting cart ', order_id)
return REST.delete(`/cart/${order_id}/items?_format=json`)
.then(({ data }) => {
console.log(`product cart ${order_id} deleted: data`, data)
})
.catch((error) => {
console.warn(`Issue with cart ${order_id} deleting`, error)
Promise.reject(error)
})
},
clearCarts (data) {
const promises = []
// clear each cart as a promise
for (let i = 0; i < data.length; i++) {
promises.push(this.deleteCart(data[i].order_id))
}
// return all the promises as one
return Promise.all(promises)
},
addtocart (variation_id) {
console.log('addtocart')
this.getCarts()
.then(({ data }) => {
console.log('current user carts: data', data)
this.clearCarts(data)
.then(() => {
console.log('all carts cleared')
// fill the cart with new product
REST.post('/cart/add?_format=json', [{
purchased_entity_type: 'commerce_product_variation',
purchased_entity_id: variation_id,
quantity: this.quantity
}])
.then(({ data }) => {
console.log('product added to cart: data', data)
this.closeModal()
// redirect to /cart
// window.location.href = "/cart"
// TODO: redirect to checkout instead of cart
window.location.href = `/checkout/${data[0].order_id}/order_information`
})
.catch((error) => {
console.warn('Issue with product add to cart', error)
Promise.reject(error)
})
})
})
}
}
}