From: Stephen Cameron Date: Tue, 13 Aug 2019 19:21:30 +0000 (+0200) Subject: Cart and quote functionality. WIP #2769 @9 X-Git-Url: http://git.cubedesigners.com/?a=commitdiff_plain;h=b7ba31fe84fcd921aaab78ba17828c11704335f4;p=pmi.git Cart and quote functionality. WIP #2769 @9 --- diff --git a/.gitignore b/.gitignore index eb84a4e..8931bb7 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ /public/mix-manifest.json /public/css /public/js +/public/vendor /storage /vendor /.idea diff --git a/app/Http/Controllers/AjaxController.php b/app/Http/Controllers/AjaxController.php index b92abf6..ab07625 100644 --- a/app/Http/Controllers/AjaxController.php +++ b/app/Http/Controllers/AjaxController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers; use App\Models\Page; +use App\Models\Product; use Cubist\Backpack\app\Http\Controllers\CubistFrontController; use Cubist\Backpack\app\Magic\PageData; use Illuminate\Http\Request; @@ -89,6 +90,47 @@ class AjaxController extends CubistFrontController }); } + public function cart(Request $request) { + + $request->validate([ + 'action' => 'required|string', // add/update/delete + 'id' => 'required|numeric', + 'quantity' => 'numeric', + ]); + + $id = $request->input('id'); + $quantity = $request->input('quantity', 1); + + // Cart items stored as an array with IDs as keys and quantities as values + // Get existing session or an empty array + $cart_items = $request->session()->get('cart_items', []); + + switch($request->input('action')) { + + case 'add': + // If the item already exists in the cart, increment the quantity + if (isset($cart_items[$id])) { + $cart_items[$id] += $quantity; + } else { + $cart_items[$id] = $quantity; + } + break; + + case 'update': + $cart_items[$id] = $quantity; + break; + + case 'delete': + unset($cart_items[$id]); + break; + } + + // Save back to the session + $request->session()->put('cart_items', $cart_items); + + return Product::getCartData(); + } + // Subscribe to newsletter via MailChimp API public function newsletter(Request $request) { diff --git a/app/Models/Product.php b/app/Models/Product.php index 8fa9f25..904badd 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -28,6 +28,12 @@ class Product extends CubistMagicModel parent::__construct($attributes); } + // Define relationship with Product Types + public function type() + { + return $this->belongsTo('App\Models\ProductType', 'product_type'); + } + public function setFields() { parent::setFields(); @@ -294,4 +300,56 @@ class Product extends CubistMagicModel } return $res; } + + + /** + * Custom accessor to return fallback image + * by accessing $product->image_fallback... + */ + public function getImageFallbackAttribute() { + return asset('images/product-details/product-placeholder.svg'); + } + + /** + * Custom accessor to return main product image (or fallback) + * by accessing $product->image... + */ + public function getImageAttribute() { + if ($this->images) { + + $image = $this->getFirstMediaUrl($this->images); + + if ($image) { + return $image; + } + } + + return $this->image_fallback; + } + + + /** + * Fetch selected product data for use in cart Vue component + * @return array + */ + public static function getCartData() { + + $cart_items = session('cart_items', []); + + $cart_data = []; + $products = self::with('media')->whereIn('id', array_keys($cart_items))->get(); + + foreach ($products as $product) { + $cart_data[] = [ + 'id' => $product->id, + 'name' => $product->name, + 'category' => $product->type->name, + 'quantity' => $cart_items[$product->id], + 'image' => $product->image, + ]; + } + + return $cart_data; + } + } diff --git a/app/Templates/Base.php b/app/Templates/Base.php index ac248b3..ea8aa7b 100644 --- a/app/Templates/Base.php +++ b/app/Templates/Base.php @@ -42,6 +42,11 @@ class Base extends TemplatePage 'tab' => $tab, ]); + $this->addField(['name' => 'form_button_text', + 'type' => 'Text', + 'label' => 'Texte du bouton', + 'tab' => $tab]); + $this->addField(['name' => 'form_confirmation', 'type' => 'Text', 'label' => 'Message de confirmation', diff --git a/public/images/tick.svg b/public/images/tick.svg new file mode 100644 index 0000000..e76b080 --- /dev/null +++ b/public/images/tick.svg @@ -0,0 +1 @@ + diff --git a/resources/js/app.js b/resources/js/app.js index 104b03a..b9759ab 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -43,14 +43,22 @@ const app = new Vue({ }, mounted() { - eventBus.$on('update-item-quantity', update => { - const index = this.items.findIndex((item) => item.id == update.id); - this.items[index].quantity = update.quantity; + + eventBus.$on('add-item', data => { + data.action = 'add'; + this.saveCart(data); + }); + + eventBus.$on('update-item', data => { + data.action = 'update'; + this.saveCart(data); }); eventBus.$on('delete-item', id => { - const index = this.items.findIndex((item) => item.id == id); - this.items.splice(index, 1); + this.saveCart({ + action: 'delete', + id: id + }); }); }, @@ -63,6 +71,20 @@ const app = new Vue({ methods: { + saveCart(data) { + let root = this; + + axios.post('/ajax/cart', data) + .then(function(response) { + console.log('Cart updated'); + console.table(response.data); + root.items = response.data; + }) + .catch(function(error) { + console.error('Error saving cart!', error); + }); + }, + openCart() { document.body.classList.add('cart-open'); }, diff --git a/resources/js/components/Cart.vue b/resources/js/components/Cart.vue index 81b6bf1..e601582 100644 --- a/resources/js/components/Cart.vue +++ b/resources/js/components/Cart.vue @@ -16,7 +16,7 @@ props: { items: { - type: Object, + type: Array, required: true, } }, diff --git a/resources/js/components/CartAdd.vue b/resources/js/components/CartAdd.vue new file mode 100644 index 0000000..f0f4f2d --- /dev/null +++ b/resources/js/components/CartAdd.vue @@ -0,0 +1,46 @@ + + + + + diff --git a/resources/js/components/CartItem.vue b/resources/js/components/CartItem.vue index d9e347b..09753f5 100644 --- a/resources/js/components/CartItem.vue +++ b/resources/js/components/CartItem.vue @@ -38,7 +38,7 @@ methods: { updateQuantity(newValue, oldValue) { - eventBus.$emit('update-item-quantity', { + eventBus.$emit('update-item', { id: this.item.id, quantity: newValue, }); diff --git a/resources/styles/components/buttons.styl b/resources/styles/components/buttons.styl index 26ac84f..59c976e 100644 --- a/resources/styles/components/buttons.styl +++ b/resources/styles/components/buttons.styl @@ -6,6 +6,9 @@ &-text @apply z-10 relative + &-no-hover:before + display: none + &:hover:before transform: scaleX(1) opacity: 1 diff --git a/resources/views/components/text-block.blade.php b/resources/views/components/text-block.blade.php index 5c8cae5..3c1c6d2 100644 --- a/resources/views/components/text-block.blade.php +++ b/resources/views/components/text-block.blade.php @@ -9,6 +9,8 @@
+ {{ $preTitle ?? '' }} + @isset($title) <{{ $titleTag }} class="{{ $titleClass }}">{{ $title }} @endisset diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index 1766fa3..668dd49 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -12,25 +12,7 @@ @include('cubist::body.begin') -@php - if (config('features.quote')) { - //#### Generate temporary cart data - $cart_items = []; - for ($i = 0; $i < 6; $i++) { - $cart_items[$i] = [ - 'id' => $i + 1, - 'quantity' => rand(1, 15), - 'name' => 'Modèle '. rand(1000, 1500), - 'category' => 'Capteur de force', - 'image' => '/storage/products/'. rand(1,6) .'.png', - ]; - } - } else { - $cart_items = []; - } -@endphp - -
+
@include('partials.header') diff --git a/resources/views/pages/cart.blade.php b/resources/views/pages/cart.blade.php index e17e213..949c494 100644 --- a/resources/views/pages/cart.blade.php +++ b/resources/views/pages/cart.blade.php @@ -10,7 +10,7 @@ {{-- Nested divs to allow grey backgrounds of columns to match the height of their content instead of total height --}}
- +
diff --git a/resources/views/pages/product-detail.blade.php b/resources/views/pages/product-detail.blade.php index e71d3cc..4addb9e 100644 --- a/resources/views/pages/product-detail.blade.php +++ b/resources/views/pages/product-detail.blade.php @@ -7,9 +7,9 @@ @section('content') - - - {{$product->name}} + + +
Ref: {{ $product->reference }}
@@ -37,7 +37,13 @@ @if(config('features.quote')) - {{__('Ajouter à ma sélection')}} + + {{__('Ajouter à ma sélection')}} + + + ? @endif @@ -122,7 +128,7 @@ {{-- Image holder --}}
+ style="background-image: url({{ $rel->getEntity()->image }})">
{{-- Product details --}} diff --git a/resources/views/pages/products.blade.php b/resources/views/pages/products.blade.php index e992ba0..c9de1e8 100644 --- a/resources/views/pages/products.blade.php +++ b/resources/views/pages/products.blade.php @@ -102,7 +102,7 @@
+ style="background-image: url({{ $product->getEntity()->image }})">
diff --git a/resources/views/partials/form.blade.php b/resources/views/partials/form.blade.php index 1628bed..4fd9810 100644 --- a/resources/views/partials/form.blade.php +++ b/resources/views/partials/form.blade.php @@ -63,13 +63,13 @@ @endforeach
-
+
@markdown($global->get('form_privacy'))
*{{__('Champs obligatoires')}} + data-sending="{{__('Envoi en cours')}}">{{ $page->get('form_button_text', __('Envoyer')) }}
diff --git a/yarn.lock b/yarn.lock index 0f907d0..f6b9de7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4274,6 +4274,11 @@ lodash@^4, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.5: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== +lodash@^4.17.15: + version "4.17.15" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" + integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== + loglevel@^1.6.2: version "1.6.2" resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.2.tgz#668c77948a03dbd22502a3513ace1f62a80cc372"