Merge branch 'master' into invoice-number

This commit is contained in:
devansh bawari 2021-06-23 19:05:10 +05:30
commit 56654e1c20
10 changed files with 376 additions and 203 deletions

View File

@ -1,5 +1,6 @@
@section('css') @section('css')
@parent @parent
<style> <style>
.table th.price, .table th.weight { .table th.price, .table th.weight {
width: 100px; width: 100px;
@ -68,7 +69,7 @@
<div class="form-container"> <div class="form-container">
<div v-for='(attribute, index) in super_attributes' class="control-group" <div v-for='(attribute, index) in super_attributes' class="control-group"
:class="[errors.has('add-variant-form.' + attribute.code) ? 'has-error' : '']" :class="[errors.has('add-variant-form.' + attribute.code) ? 'has-error' : '']"
> >
<label :for="attribute.code" class="required">@{{ attribute.admin_name <label :for="attribute.code" class="required">@{{ attribute.admin_name
}}</label> }}</label>
@ -85,7 +86,7 @@
</option> </option>
</select> </select>
<span class="control-error" <span class="control-error"
v-if="errors.has('add-variant-form.' + attribute.code)">@{{ errors.first('add-variant-form.' + attribute.code) }}</span> v-if="errors.has('add-variant-form.' + attribute.code)">@{{ errors.first('add-variant-form.' + attribute.code) }}</span>
</div> </div>
<button type="submit" class="btn btn-lg btn-primary"> <button type="submit" class="btn btn-lg btn-primary">
@ -101,34 +102,31 @@
<script type="text/x-template" id="variant-list-template"> <script type="text/x-template" id="variant-list-template">
<div class="table" style="margin-top: 20px; overflow-x: auto;"> <div class="table" style="margin-top: 20px; overflow-x: auto;">
<table> <table>
<thead> <thead>
<tr> <tr>
<th class="sku">{{ __('admin::app.catalog.products.sku') }}</th> <th class="is-default">{{ __('admin::app.catalog.products.is-default') }}</th>
<th>{{ __('admin::app.catalog.products.name') }}</th> <th class="sku">{{ __('admin::app.catalog.products.sku') }}</th>
<th>{{ __('admin::app.catalog.products.images') }}</th> <th>{{ __('admin::app.catalog.products.name') }}</th>
<th>{{ __('admin::app.catalog.products.images') }}</th>
@foreach ($product->super_attributes as $attribute) @foreach ($product->super_attributes as $attribute)
<th class="{{ $attribute->code }}" <th class="{{ $attribute->code }}"
style="width: 150px">{{ $attribute->admin_name }}</th> style="width: 150px">{{ $attribute->admin_name }}</th>
@endforeach @endforeach
<th class="qty">{{ __('admin::app.catalog.products.qty') }}</th> <th class="qty">{{ __('admin::app.catalog.products.qty') }}</th>
<th class="price">{{ __('admin::app.catalog.products.price') }}</th> <th class="price">{{ __('admin::app.catalog.products.price') }}</th>
<th class="weight">{{ __('admin::app.catalog.products.weight') }}</th> <th class="weight">{{ __('admin::app.catalog.products.weight') }}</th>
<th class="status">{{ __('admin::app.catalog.products.status') }}</th> <th class="status">{{ __('admin::app.catalog.products.status') }}</th>
<th class="actions"></th> <th class="actions"></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<variant-item v-for='(variant, index) in variants' :key="index" :index="index"
<variant-item v-for='(variant, index) in variants' :variant="variant" :key="index" :variant="variant" @onRemoveVariant="removeVariant($event)">
:index="index" </variant-item>
@onRemoveVariant="removeVariant($event)"></variant-item>
</tbody> </tbody>
</table> </table>
</div> </div>
</script> </script>
@ -136,22 +134,32 @@
<script type="text/x-template" id="variant-item-template"> <script type="text/x-template" id="variant-item-template">
<tr> <tr>
<td> <td>
<div class="control-group" <div class="control-group">
:class="[errors.has(variantInputName + '[sku]') ? 'has-error' : '']"> <span class="radio">
<input type="radio" id="default_variant_id"
name="default_variant_id" :value="variant.id"
v-on:change="checkDefaultVariant(variant.id)" :checked="variant.id == default_variant_id">
<label class="radio-view" :for="[variantInputName + '[default_variant_id]']"></label>
</span>
</div>
</td>
<td>
<div class="control-group" :class="[errors.has(variantInputName + '[sku]') ? 'has-error' : '']">
<input type="text" v-validate="'required'" v-model="variant.sku" <input type="text" v-validate="'required'" v-model="variant.sku"
:name="[variantInputName + '[sku]']" class="control" :name="[variantInputName + '[sku]']" class="control"
data-vv-as="&quot;{{ __('admin::app.catalog.products.sku') }}&quot;" data-vv-as="&quot;{{ __('admin::app.catalog.products.sku') }}&quot;"
v-slugify/> v-slugify/>
<span class="control-error" v-if="errors.has(variantInputName + '[sku]')">@{{ errors.first(variantInputName + '[sku]') }}</span> <span class="control-error" v-if="errors.has(variantInputName + '[sku]')">@{{ errors.first(variantInputName + '[sku]') }}</span>
</div> </div>
</td> </td>
<td> <td>
<div class="control-group" <div class="control-group"
:class="[errors.has(variantInputName + '[name]') ? 'has-error' : '']"> :class="[errors.has(variantInputName + '[name]') ? 'has-error' : '']">
<input type="text" v-validate="'required'" v-model="variant.name" <input type="text" v-validate="'required'" v-model="variant.name"
:name="[variantInputName + '[name]']" class="control" :name="[variantInputName + '[name]']" class="control"
data-vv-as="&quot;{{ __('admin::app.catalog.products.name') }}&quot;"/> data-vv-as="&quot;{{ __('admin::app.catalog.products.name') }}&quot;"/>
<span class="control-error" v-if="errors.has(variantInputName + '[name]')">@{{ errors.first(variantInputName + '[name]') }}</span> <span class="control-error" v-if="errors.has(variantInputName + '[name]')">@{{ errors.first(variantInputName + '[name]') }}</span>
</div> </div>
</td> </td>
@ -179,9 +187,9 @@
<td v-for='(attribute, index) in superAttributes'> <td v-for='(attribute, index) in superAttributes'>
<div class="control-group"> <div class="control-group">
<input type="hidden" :name="[variantInputName + '[' + attribute.code + ']']" <input type="hidden" :name="[variantInputName + '[' + attribute.code + ']']"
:value="variant[attribute.code]"/> :value="variant[attribute.code]"/>
<input type="text" class="control" :value="optionName(variant[attribute.code])" <input type="text" class="control" :value="optionName(variant[attribute.code])"
readonly/> readonly/>
</div> </div>
</td> </td>
@ -196,15 +204,15 @@
<ul> <ul>
<li v-for='(inventorySource, index) in inventorySources'> <li v-for='(inventorySource, index) in inventorySources'>
<div class="control-group" <div class="control-group"
:class="[errors.has(variantInputName + '[inventories][' + inventorySource.id + ']') ? 'has-error' : '']"> :class="[errors.has(variantInputName + '[inventories][' + inventorySource.id + ']') ? 'has-error' : '']">
<label>@{{ inventorySource.name }}</label> <label>@{{ inventorySource.name }}</label>
<input type="text" v-validate="'numeric|min:0'" <input type="text" v-validate="'numeric|min:0'"
:name="[variantInputName + '[inventories][' + inventorySource.id + ']']" :name="[variantInputName + '[inventories][' + inventorySource.id + ']']"
v-model="inventories[inventorySource.id]" class="control" v-model="inventories[inventorySource.id]" class="control"
v-on:keyup="updateTotalQty()" v-on:keyup="updateTotalQty()"
:data-vv-as="'&quot;' + inventorySource.name + '&quot;'"/> :data-vv-as="'&quot;' + inventorySource.name + '&quot;'"/>
<span class="control-error" <span class="control-error"
v-if="errors.has(variantInputName + '[inventories][' + inventorySource.id + ']')">@{{ errors.first(variantInputName + '[inventories][' + inventorySource.id + ']') }}</span> v-if="errors.has(variantInputName + '[inventories][' + inventorySource.id + ']')">@{{ errors.first(variantInputName + '[inventories][' + inventorySource.id + ']') }}</span>
</div> </div>
</li> </li>
</ul> </ul>
@ -214,22 +222,22 @@
<td> <td>
<div class="control-group" <div class="control-group"
:class="[errors.has(variantInputName + '[price]') ? 'has-error' : '']"> :class="[errors.has(variantInputName + '[price]') ? 'has-error' : '']">
<input type="number" v-validate="'required'" v-model="variant.price" <input type="number" v-validate="'required'" v-model="variant.price"
:name="[variantInputName + '[price]']" class="control" :name="[variantInputName + '[price]']" class="control"
data-vv-as="&quot;{{ __('admin::app.catalog.products.price') }}&quot;" data-vv-as="&quot;{{ __('admin::app.catalog.products.price') }}&quot;"
step="any"/> step="any"/>
<span class="control-error" v-if="errors.has(variantInputName + '[price]')">@{{ errors.first(variantInputName + '[price]') }}</span> <span class="control-error" v-if="errors.has(variantInputName + '[price]')">@{{ errors.first(variantInputName + '[price]') }}</span>
</div> </div>
</td> </td>
<td> <td>
<div class="control-group" <div class="control-group"
:class="[errors.has(variantInputName + '[weight]') ? 'has-error' : '']"> :class="[errors.has(variantInputName + '[weight]') ? 'has-error' : '']">
<input type="number" v-validate="'required'" v-model="variant.weight" <input type="number" v-validate="'required'" v-model="variant.weight"
:name="[variantInputName + '[weight]']" class="control" :name="[variantInputName + '[weight]']" class="control"
data-vv-as="&quot;{{ __('admin::app.catalog.products.weight') }}&quot;" data-vv-as="&quot;{{ __('admin::app.catalog.products.weight') }}&quot;"
step="any"/> step="any"/>
<span class="control-error" v-if="errors.has(variantInputName + '[weight]')">@{{ errors.first(variantInputName + '[weight]') }}</span> <span class="control-error" v-if="errors.has(variantInputName + '[weight]')">@{{ errors.first(variantInputName + '[weight]') }}</span>
</div> </div>
</td> </td>
@ -239,9 +247,9 @@
<select type="text" v-model="variant.status" <select type="text" v-model="variant.status"
:name="[variantInputName + '[status]']" class="control"> :name="[variantInputName + '[status]']" class="control">
<option value="1" <option value="1"
:selected="variant.status">{{ __('admin::app.catalog.products.enabled') }}</option> :selected="variant.status">{{ __('admin::app.catalog.products.enabled') }}</option>
<option value="0" <option value="0"
:selected="!variant.status">{{ __('admin::app.catalog.products.disabled') }}</option> :selected="!variant.status">{{ __('admin::app.catalog.products.disabled') }}</option>
</select> </select>
</div> </div>
</td> </td>
@ -263,11 +271,10 @@
]; ];
}); });
var super_attributes = @json(app('\Webkul\Product\Repositories\ProductRepository')->getSuperAttributes($product)); let super_attributes = @json(app('\Webkul\Product\Repositories\ProductRepository')->getSuperAttributes($product));
var variants = @json($product->variants); let variants = @json($product->variants);
Vue.component('variant-form', { Vue.component('variant-form', {
data: function () { data: function () {
return { return {
variant: {}, variant: {},
@ -285,18 +292,18 @@
addVariant: function (formScope) { addVariant: function (formScope) {
this.$validator.validateAll(formScope).then((result) => { this.$validator.validateAll(formScope).then((result) => {
if (result) { if (result) {
var this_this = this; let self = this;
var filteredVariants = variants.filter(function (variant) { let filteredVariants = variants.filter(function (variant) {
var matchCount = 0; let matchCount = 0;
for (var key in this_this.variant) { for (let key in self.variant) {
if (variant[key] == this_this.variant[key]) { if (variant[key] == self.variant[key]) {
matchCount++; matchCount++;
} }
} }
return matchCount == this_this.super_attributes.length; return matchCount == self.super_attributes.length;
}) })
if (filteredVariants.length) { if (filteredVariants.length) {
@ -309,9 +316,9 @@
this.$root.addFlashMessages() this.$root.addFlashMessages()
} else { } else {
var optionIds = []; let optionIds = [];
for (var key in this_this.variant) { for (let key in self.variant) {
optionIds.push(this_this.variant[key]); optionIds.push(self.variant[key]);
} }
variants.push(Object.assign({ variants.push(Object.assign({
@ -331,17 +338,16 @@
}, },
resetModel: function () { resetModel: function () {
var this_this = this; let self = this;
this.super_attributes.forEach(function (attribute) { this.super_attributes.forEach(function (attribute) {
this_this.variant[attribute.code] = ''; self.variant[attribute.code] = '';
}) })
} }
} }
}); });
Vue.component('variant-list', { Vue.component('variant-list', {
template: '#variant-list-template', template: '#variant-list-template',
inject: ['$validator'], inject: ['$validator'],
@ -357,15 +363,15 @@
}, },
created: function () { created: function () {
var index = 0; let index = 0;
for (var key in this.old_variants) { for (let key in this.old_variants) {
var variant = this.old_variants[key]; let variant = this.old_variants[key];
if (key.indexOf('variant_') !== -1) { if (key.indexOf('variant_') !== -1) {
var inventories = []; let inventories = [];
for (var inventorySourceId in variant['inventories']) { for (let inventorySourceId in variant['inventories']) {
inventories.push({ inventories.push({
'qty': variant['inventories'][inventorySourceId], 'qty': variant['inventories'][inventorySourceId],
'inventory_source_id': inventorySourceId 'inventory_source_id': inventorySourceId
@ -376,13 +382,13 @@
variants.push(variant); variants.push(variant);
} else { } else {
for (var code in variant) { for (let code in variant) {
if (code != 'inventories') { if (code != 'inventories') {
variants[index][code] = variant[code]; variants[index][code] = variant[code];
} else { } else {
variants[index][code] = []; variants[index][code] = [];
for (var inventorySourceId in variant[code]) { for (let inventorySourceId in variant[code]) {
variants[index][code].push({ variants[index][code].push({
'qty': variant[code][inventorySourceId], 'qty': variant[code][inventorySourceId],
'inventory_source_id': inventorySourceId 'inventory_source_id': inventorySourceId
@ -403,11 +409,9 @@
this.variants.splice(index, 1) this.variants.splice(index, 1)
}, },
} }
}); });
Vue.component('variant-item', { Vue.component('variant-item', {
template: '#variant-item-template', template: '#variant-item-template',
props: ['index', 'variant'], props: ['index', 'variant'],
@ -416,6 +420,7 @@
data: function () { data: function () {
return { return {
default_variant_id: parseInt('{{ $product->additional['default_variant_id'] ?? null }}'),
inventorySources: @json($inventorySources), inventorySources: @json($inventorySources),
inventories: {}, inventories: {},
totalQty: 0, totalQty: 0,
@ -429,25 +434,25 @@
}, },
created: function () { created: function () {
var this_this = this; let self = this;
this.inventorySources.forEach(function (inventorySource) { this.inventorySources.forEach(function (inventorySource) {
this_this.inventories[inventorySource.id] = this_this.sourceInventoryQty(inventorySource.id) self.inventories[inventorySource.id] = self.sourceInventoryQty(inventorySource.id)
this_this.totalQty += parseInt(this_this.inventories[inventorySource.id]); self.totalQty += parseInt(self.inventories[inventorySource.id]);
}) })
}, },
mounted () { mounted () {
var this_this = this; let self = this;
this_this.variant.images.forEach(function(image) { self.variant.images.forEach(function(image) {
this_this.items.push(image) self.items.push(image)
this_this.imageCount++; self.imageCount++;
if (image.id && image.url) { if (image.id && image.url) {
this_this.imageData.push(image.url); self.imageData.push(image.url);
} else if (image.id && image.file) { } else if (image.id && image.file) {
this_this.readFile(image.file); self.readFile(image.file);
} }
}); });
}, },
@ -463,11 +468,15 @@
methods: { methods: {
removeVariant: function () { removeVariant: function () {
this.$emit('onRemoveVariant', this.variant) this.$emit('onRemoveVariant', this.variant);
},
checkDefaultVariant: function (variantId) {
this.default_variant_id = variantId;
}, },
optionName: function (optionId) { optionName: function (optionId) {
var optionName = ''; let optionName = '';
this.superAttributes.forEach(function (attribute) { this.superAttributes.forEach(function (attribute) {
attribute.options.forEach(function (option) { attribute.options.forEach(function (option) {
@ -484,7 +493,7 @@
if (!Array.isArray(this.variant.inventories)) if (!Array.isArray(this.variant.inventories))
return 0; return 0;
var inventories = this.variant.inventories.filter(function (inventory) { let inventories = this.variant.inventories.filter(function (inventory) {
return inventorySourceId === parseInt(inventory.inventory_source_id); return inventorySourceId === parseInt(inventory.inventory_source_id);
}) })
@ -497,13 +506,13 @@
updateTotalQty: function () { updateTotalQty: function () {
this.totalQty = 0; this.totalQty = 0;
for (var key in this.inventories) { for (let key in this.inventories) {
this.totalQty += parseInt(this.inventories[key]); this.totalQty += parseInt(this.inventories[key]);
} }
}, },
createFileType: function() { createFileType: function() {
var this_this = this; let self = this;
this.imageCount++; this.imageCount++;
@ -521,8 +530,8 @@
}, },
addImageView: function($event, index) { addImageView: function($event, index) {
var ref = "imageInput" + index; let ref = "imageInput" + index;
var imageInput = this.$refs[ref][0]; let imageInput = this.$refs[ref][0];
if (imageInput.files && imageInput.files[0]) { if (imageInput.files && imageInput.files[0]) {
if (imageInput.files[0].type.includes('image/')) { if (imageInput.files[0].type.includes('image/')) {
@ -537,7 +546,7 @@
}, },
readFile: function(image, index) { readFile: function(image, index) {
var reader = new FileReader(); let reader = new FileReader();
reader.onload = (e) => { reader.onload = (e) => {
this.imageData.splice(index, 1, e.target.result); this.imageData.splice(index, 1, e.target.result);

View File

@ -0,0 +1,32 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AlterProductsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('products', function (Blueprint $table) {
$table->json('additional')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('products', function (Blueprint $table) {
$table->dropColumn('additional');
});
}
}

View File

@ -12,10 +12,14 @@ use Webkul\Attribute\Models\AttributeFamilyProxy;
use Webkul\Inventory\Models\InventorySourceProxy; use Webkul\Inventory\Models\InventorySourceProxy;
use Webkul\Attribute\Repositories\AttributeRepository; use Webkul\Attribute\Repositories\AttributeRepository;
use Webkul\Product\Contracts\Product as ProductContract; use Webkul\Product\Contracts\Product as ProductContract;
use Webkul\CatalogRule\Models\CatalogRuleProductPriceProxy;
class Product extends Model implements ProductContract class Product extends Model implements ProductContract
{ {
/**
* The attributes that are mass assignable.
*
* @var $fillable
*/
protected $fillable = [ protected $fillable = [
'type', 'type',
'attribute_family_id', 'attribute_family_id',
@ -23,6 +27,20 @@ class Product extends Model implements ProductContract
'parent_id', 'parent_id',
]; ];
/**
* The attributes that should be cast.
*
* @var $casts
*/
protected $casts = [
'additional' => 'array'
];
/**
* The type of product.
*
* @var $typeInstance
*/
protected $typeInstance; protected $typeInstance;
/** /**
@ -412,11 +430,10 @@ class Product extends Model implements ProductContract
} }
/** /**
* Overrides the default Eloquent query builder * Overrides the default Eloquent query builder.
* *
* @param \Illuminate\Database\Eloquent\Builder $query * @param \Illuminate\Database\Query\Builder $query
* * @return \Webkul\Product\Database\Eloquent\Builder
* @return \Illuminate\Database\Eloquent\Builder
*/ */
public function newEloquentBuilder($query) public function newEloquentBuilder($query)
{ {

View File

@ -67,7 +67,7 @@ abstract class AbstractType
/** /**
* Product instance * Product instance
* *
* @var \Webkul\Product\Contracts\Product * @var \Webkul\Product\Models\Product
*/ */
protected $product; protected $product;

View File

@ -66,6 +66,55 @@ class Configurable extends AbstractType
*/ */
protected $productOptions = []; protected $productOptions = [];
/**
* Get default variant.
*
* @return \Webkul\Product\Models\Product
*/
public function getDefaultVariant()
{
return $this->product->variants()->find($this->getDefaultVariantId());
}
/**
* Get default variant id.
*
* @return int
*/
public function getDefaultVariantId()
{
return $this->product->additional['default_variant_id'] ?? null;
}
/**
* Set default variant id.
*
* @param int $defaultVariantId
* @return void
*/
public function setDefaultVariantId($defaultVariantId)
{
$this->product->additional = array_merge($this->product->additional ?? [], [
'default_variant_id' => $defaultVariantId
]);
}
/**
* Update default variant id if present in request.
*
* @return void
*/
public function updateDefaultVariantId()
{
$defaultVariantId = request()->get('default_variant_id');
if ($defaultVariantId) {
$this->setDefaultVariantId($defaultVariantId);
$this->product->save();
}
}
/** /**
* Create configurable product. * Create configurable product.
* *
@ -106,6 +155,9 @@ class Configurable extends AbstractType
public function update(array $data, $id, $attribute = "id") public function update(array $data, $id, $attribute = "id")
{ {
$product = parent::update($data, $id, $attribute); $product = parent::update($data, $id, $attribute);
$this->updateDefaultVariantId();
$route = request()->route() ? request()->route()->getName() : ''; $route = request()->route() ? request()->route()->getName() : '';
if ($route != 'admin.catalog.products.massupdate') { if ($route != 'admin.catalog.products.massupdate') {
@ -533,12 +585,16 @@ class Configurable extends AbstractType
* Add product. Returns error message if can't prepare product. * Add product. Returns error message if can't prepare product.
* *
* @param array $data * @param array $data
* @return array * @return array|string
*/ */
public function prepareForCart($data) public function prepareForCart($data)
{ {
if (! isset($data['selected_configurable_option']) || ! $data['selected_configurable_option']) { if (! isset($data['selected_configurable_option']) || ! $data['selected_configurable_option']) {
return trans('shop::app.checkout.cart.integrity.missing_options'); if ($this->getDefaultVariantId()) {
$data['selected_configurable_option'] = $this->getDefaultVariantId();
} else {
return trans('shop::app.checkout.cart.integrity.missing_options');
}
} }
$data = $this->getQtyRequest($data); $data = $this->getQtyRequest($data);
@ -670,8 +726,7 @@ class Configurable extends AbstractType
/** /**
* Validate cart item product price. * Validate cart item product price.
* *
* @param \Webkul\Product\Type\CartItem $item * @param \Webkul\Product\Type\CartItem $item
*
* @return \Webkul\Product\Datatypes\CartItemValidationResult * @return \Webkul\Product\Datatypes\CartItemValidationResult
*/ */
public function validateCartItem(CartItemModel $item): CartItemValidationResult public function validateCartItem(CartItemModel $item): CartItemValidationResult
@ -704,8 +759,7 @@ class Configurable extends AbstractType
/** /**
* Get product options. * Get product options.
* *
* @param string $product * @param string $product
*
* @return array * @return array
*/ */
public function getProductOptions($product = "") public function getProductOptions($product = "")

View File

@ -28,7 +28,11 @@
:id="['attribute_' + attribute.id]" :id="['attribute_' + attribute.id]"
:data-vv-as="'&quot;' + attribute.label + '&quot;'"> :data-vv-as="'&quot;' + attribute.label + '&quot;'">
<option v-for='(option, index) in attribute.options' :value="option.id">@{{ option.label }}</option> <option
v-for='(option, index) in attribute.options' :value="option.id"
:selected="index == attribute.selectedIndex">
@{{ option.label }}
</option>
</select> </select>
</span> </span>
@ -46,7 +50,8 @@
:id="['attribute_' + attribute.id + '_option_' + option.id]" :id="['attribute_' + attribute.id + '_option_' + option.id]"
:value="option.id" :value="option.id"
:data-vv-as="'&quot;' + attribute.label + '&quot;'" :data-vv-as="'&quot;' + attribute.label + '&quot;'"
@change="configure(attribute, $event.target.value)"/> @change="configure(attribute, $event.target.value)"
:checked="index == attribute.selectedIndex"/>
<span v-if="attribute.swatch_type == 'color'" :style="{ background: option.swatch_value }"></span> <span v-if="attribute.swatch_type == 'color'" :style="{ background: option.swatch_value }"></span>
@ -69,18 +74,22 @@
</div> </div>
</script> </script>
<?php $config = $configurableOptionHelper->getConfigurationConfig($product) ?> @php
$defaultVariant = $product->getTypeInstance()->getDefaultVariant();
$config = $configurableOptionHelper->getConfigurationConfig($product);
@endphp
<script> <script>
Vue.component('product-options', { Vue.component('product-options', {
template: '#product-options-template', template: '#product-options-template',
inject: ['$validator'], inject: ['$validator'],
data: function() { data: function() {
return { return {
defaultVariant: @json($defaultVariant),
config: @json($config), config: @json($config),
childAttributes: [], childAttributes: [],
@ -89,40 +98,56 @@
simpleProduct: null, simpleProduct: null,
galleryImages: [] galleryImages: [],
} }
}, },
created: function() { mounted: function() {
var config = @json($config); this.init();
var childAttributes = this.childAttributes, this.initDefaultSelection();
attributes = config.attributes.slice(),
index = attributes.length,
attribute;
while (index--) {
attribute = attributes[index];
attribute.options = [];
if (index) {
attribute.disabled = true;
} else {
this.fillSelect(attribute);
}
attribute = Object.assign(attribute, {
childAttributes: childAttributes.slice(),
prevAttribute: attributes[index - 1],
nextAttribute: attributes[index + 1]
});
childAttributes.unshift(attribute);
}
}, },
methods: { methods: {
init: function () {
let config = @json($config);
let childAttributes = this.childAttributes,
attributes = config.attributes.slice(),
index = attributes.length,
attribute;
while (index--) {
attribute = attributes[index];
attribute.options = [];
if (index) {
attribute.disabled = true;
} else {
this.fillSelect(attribute);
}
attribute = Object.assign(attribute, {
childAttributes: childAttributes.slice(),
prevAttribute: attributes[index - 1],
nextAttribute: attributes[index + 1]
});
childAttributes.unshift(attribute);
}
},
initDefaultSelection: function() {
if (this.defaultVariant) {
this.childAttributes.forEach((attribute) => {
let attributeValue = this.defaultVariant[attribute.code];
this.configure(attribute, attributeValue);
});
}
},
configure: function(attribute, value) { configure: function(attribute, value) {
this.simpleProduct = this.getSelectedProductId(attribute, value); this.simpleProduct = this.getSelectedProductId(attribute, value);
@ -152,7 +177,7 @@
}, },
getSelectedIndex: function(attribute, value) { getSelectedIndex: function(attribute, value) {
var selectedIndex = 0; let selectedIndex = 0;
attribute.options.forEach(function(option, index) { attribute.options.forEach(function(option, index) {
if (option.id == value) { if (option.id == value) {
@ -164,7 +189,7 @@
}, },
getSelectedProductId: function(attribute, value) { getSelectedProductId: function(attribute, value) {
var options = attribute.options, let options = attribute.options,
matchedOptions; matchedOptions;
matchedOptions = options.filter(function (option) { matchedOptions = options.filter(function (option) {
@ -179,7 +204,7 @@
}, },
fillSelect: function(attribute) { fillSelect: function(attribute) {
var options = this.getAttributeOptions(attribute.id), let options = this.getAttributeOptions(attribute.id),
prevOption, prevOption,
index = 1, index = 1,
allowedProducts, allowedProducts,
@ -233,15 +258,15 @@
return; return;
if (! attribute.swatch_type || attribute.swatch_type == '' || attribute.swatch_type == 'dropdown') { if (! attribute.swatch_type || attribute.swatch_type == '' || attribute.swatch_type == 'dropdown') {
var element = document.getElementById("attribute_" + attribute.id); let element = document.getElementById("attribute_" + attribute.id);
if (element) { if (element) {
element.selectedIndex = "0"; element.selectedIndex = "0";
} }
} else { } else {
var elements = document.getElementsByName('super_attribute[' + attribute.id + ']'); let elements = document.getElementsByName('super_attribute[' + attribute.id + ']');
var this_this = this; let self = this;
elements.forEach(function(element) { elements.forEach(function(element) {
element.checked = false; element.checked = false;
@ -250,7 +275,7 @@
}, },
getAttributeOptions: function (attributeId) { getAttributeOptions: function (attributeId) {
var this_this = this, let self = this,
options; options;
this.config.attributes.forEach(function(attribute, index) { this.config.attributes.forEach(function(attribute, index) {
@ -263,7 +288,7 @@
}, },
reloadPrice: function () { reloadPrice: function () {
var selectedOptionCount = 0; let selectedOptionCount = 0;
this.childAttributes.forEach(function(attribute) { this.childAttributes.forEach(function(attribute) {
if (attribute.selectedIndex) { if (attribute.selectedIndex) {
@ -271,9 +296,9 @@
} }
}); });
var priceLabelElement = document.querySelector('.price-label'); let priceLabelElement = document.querySelector('.price-label');
var priceElement = document.querySelector('.final-price'); let priceElement = document.querySelector('.final-price');
var regularPriceElement = document.querySelector('.regular-price'); let regularPriceElement = document.querySelector('.regular-price');
if (this.childAttributes.length == selectedOptionCount) { if (this.childAttributes.length == selectedOptionCount) {
priceLabelElement.style.display = 'none'; priceLabelElement.style.display = 'none';
@ -313,7 +338,7 @@
}, },
changeStock: function (productId) { changeStock: function (productId) {
var inStockElement = document.querySelector('.stock-status'); let inStockElement = document.querySelector('.stock-status');
if (productId) { if (productId) {
inStockElement.style.display= "block"; inStockElement.style.display= "block";
@ -322,7 +347,6 @@
} }
}, },
} }
}); });
</script> </script>

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
{ {
"/js/ui.js": "/js/ui.js?id=1a2a11fc54d0a5962a66", "/js/ui.js": "/js/ui.js?id=1a2a11fc54d0a5962a66",
"/css/ui.css": "/css/ui.css?id=6d93a4a052e38d6aa795" "/css/ui.css": "/css/ui.css?id=87c15a3e7af4ab272377"
} }

View File

@ -559,10 +559,10 @@ h5 {
margin: 10px 5px 5px 0px; margin: 10px 5px 5px 0px;
input { input {
left: 0; left: -4px;
opacity: 0; opacity: 0;
position: absolute; position: absolute;
top: 0; top: -1px;
z-index: 100; z-index: 100;
} }
@ -587,6 +587,15 @@ h5 {
} }
} }
.rtl {
.radio {
input {
left: unset;
right: 1px;
}
}
}
.control-group { .control-group {
display: block; display: block;
margin-bottom: 25px; margin-bottom: 25px;

View File

@ -3,6 +3,7 @@
@inject ('configurableOptionHelper', 'Webkul\Product\Helpers\ConfigurableOption') @inject ('configurableOptionHelper', 'Webkul\Product\Helpers\ConfigurableOption')
@php @php
$defaultVariant = $product->getTypeInstance()->getDefaultVariant();
$config = $configurableOptionHelper->getConfigurationConfig($product); $config = $configurableOptionHelper->getConfigurationConfig($product);
$galleryImages = productimage()->getGalleryImages($product); $galleryImages = productimage()->getGalleryImages($product);
@endphp @endphp
@ -20,7 +21,7 @@
type="hidden" type="hidden"
:value="selectedProductId" :value="selectedProductId"
id="selected_configurable_option" id="selected_configurable_option"
name="selected_configurable_option" /> name="selected_configurable_option"/>
<div <div
:key="index" :key="index"
@ -43,16 +44,18 @@
:disabled="attribute.disabled" :disabled="attribute.disabled"
:id="['attribute_' + attribute.id]" :id="['attribute_' + attribute.id]"
:name="['super_attribute[' + attribute.id + ']']" :name="['super_attribute[' + attribute.id + ']']"
@change="configure(attribute, $event.target.value, $event)" @change="configure(attribute, $event.target.value)"
:data-vv-as="'&quot;' + attribute.label + '&quot;'"> :data-vv-as="'&quot;' + attribute.label + '&quot;'">
<option <option
:value="option.id" :value="option.id"
v-for='(option, index) in attribute.options'> v-for='(option, index) in attribute.options'
:selected="index == attribute.selectedIndex">
@{{ option.label }} @{{ option.label }}
</option> </option>
</select> </select>
<div class="select-icon-container"> <div class="select-icon-container">
<span class="select-icon rango-arrow-down"></span> <span class="select-icon rango-arrow-down"></span>
</div> </div>
@ -72,7 +75,8 @@
:name="['super_attribute[' + attribute.id + ']']" :name="['super_attribute[' + attribute.id + ']']"
:id="['attribute_' + attribute.id + '_option_' + option.id]" :id="['attribute_' + attribute.id + '_option_' + option.id]"
:data-vv-as="'&quot;' + attribute.label + '&quot;'" :data-vv-as="'&quot;' + attribute.label + '&quot;'"
@change="configure(attribute, $event.target.value)"/> @change="configure(attribute, $event.target.value)"
:checked="index == attribute.selectedIndex">
<span v-if="attribute.swatch_type == 'color'" :style="{ background: option.swatch_value }"></span> <span v-if="attribute.swatch_type == 'color'" :style="{ background: option.swatch_value }"></span>
@ -97,52 +101,76 @@
<script type="text/javascript"> <script type="text/javascript">
(() => { (() => {
var galleryImages = @json($galleryImages); let galleryImages = @json($galleryImages);
Vue.component('product-options', { Vue.component('product-options', {
template: '#product-options-template', template: '#product-options-template',
inject: ['$validator'], inject: ['$validator'],
data: function() { data: function() {
return { return {
galleryImages: [], defaultVariant: @json($defaultVariant),
simpleProduct: null,
childAttributes: [],
selectedProductId: '',
config: @json($config), config: @json($config),
galleryImages: [],
simpleProduct: null,
childAttributes: [],
selectedProductId: '',
} }
}, },
created: function() { mounted: function () {
var config = @json($config); this.init();
var childAttributes = this.childAttributes, this.initDefaultSelection();
attributes = config.attributes.slice(),
index = attributes.length,
attribute;
while (index--) {
attribute = attributes[index];
attribute.options = [];
if (index) {
attribute.disabled = true;
} else {
this.fillSelect(attribute);
}
attribute = Object.assign(attribute, {
childAttributes: childAttributes.slice(),
prevAttribute: attributes[index - 1],
nextAttribute: attributes[index + 1]
});
childAttributes.unshift(attribute);
}
}, },
methods: { methods: {
configure: function(attribute, value, event) { init: function () {
let config = @json($config);
let childAttributes = this.childAttributes,
attributes = config.attributes.slice(),
index = attributes.length,
attribute;
while (index--) {
attribute = attributes[index];
attribute.options = [];
if (index) {
attribute.disabled = true;
} else {
this.fillSelect(attribute);
}
attribute = Object.assign(attribute, {
childAttributes: childAttributes.slice(),
prevAttribute: attributes[index - 1],
nextAttribute: attributes[index + 1]
});
childAttributes.unshift(attribute);
}
},
initDefaultSelection: function() {
if (this.defaultVariant) {
this.childAttributes.forEach((attribute) => {
let attributeValue = this.defaultVariant[attribute.code];
this.configure(attribute, attributeValue);
});
}
},
configure: function(attribute, value) {
this.simpleProduct = this.getSelectedProductId(attribute, value); this.simpleProduct = this.getSelectedProductId(attribute, value);
if (value) { if (value) {
@ -168,7 +196,7 @@
}, },
getSelectedIndex: function(attribute, value) { getSelectedIndex: function(attribute, value) {
var selectedIndex = 0; let selectedIndex = 0;
attribute.options.forEach(function(option, index) { attribute.options.forEach(function(option, index) {
if (option.id == value) { if (option.id == value) {
@ -180,7 +208,7 @@
}, },
getSelectedProductId: function(attribute, value) { getSelectedProductId: function(attribute, value) {
var options = attribute.options, let options = attribute.options,
matchedOptions; matchedOptions;
matchedOptions = options.filter(function (option) { matchedOptions = options.filter(function (option) {
@ -195,12 +223,12 @@
}, },
fillSelect: function(attribute) { fillSelect: function(attribute) {
var options = this.getAttributeOptions(attribute.id); let options = this.getAttributeOptions(attribute.id);
var prevOption; let prevOption;
var index = 1; let index = 1;
var allowedProducts; let allowedProducts;
var i; let i;
var j; let j;
this.clearSelect(attribute) this.clearSelect(attribute)
@ -252,13 +280,13 @@
|| attribute.swatch_type == '' || attribute.swatch_type == ''
|| attribute.swatch_type == 'dropdown' || attribute.swatch_type == 'dropdown'
) { ) {
var element = document.getElementById(`attribute_${attribute.id}`); let element = document.getElementById(`attribute_${attribute.id}`);
if (element) { if (element) {
element.selectedIndex = "0"; element.selectedIndex = "0";
} }
} else { } else {
var elements = document.getElementsByName(`super_attribute[${attribute.id}]`); let elements = document.getElementsByName(`super_attribute[${attribute.id}]`);
elements.forEach(function(element) { elements.forEach(function(element) {
element.checked = false; element.checked = false;
@ -267,7 +295,7 @@
}, },
getAttributeOptions: function (attributeId) { getAttributeOptions: function (attributeId) {
var options; let options;
this.config.attributes.forEach(function(attribute, index) { this.config.attributes.forEach(function(attribute, index) {
if (attribute.id == attributeId) { if (attribute.id == attributeId) {
@ -279,7 +307,7 @@
}, },
reloadPrice: function () { reloadPrice: function () {
var selectedOptionCount = 0; let selectedOptionCount = 0;
this.childAttributes.forEach(function(attribute) { this.childAttributes.forEach(function(attribute) {
if (attribute.selectedIndex) { if (attribute.selectedIndex) {
@ -287,9 +315,9 @@
} }
}); });
var priceLabelElement = document.querySelector('.price-label'); let priceLabelElement = document.querySelector('.price-label');
var priceElement = document.querySelector('.final-price'); let priceElement = document.querySelector('.final-price');
var regularPriceElement = document.querySelector('.regular-price'); let regularPriceElement = document.querySelector('.regular-price');
if (this.childAttributes.length == selectedOptionCount) { if (this.childAttributes.length == selectedOptionCount) {
priceLabelElement.style.display = 'none'; priceLabelElement.style.display = 'none';
@ -337,7 +365,7 @@
}, },
changeStock: function (productId) { changeStock: function (productId) {
var inStockElement = document.querySelector('.disable-box-shadow'); let inStockElement = document.querySelector('.disable-box-shadow');
if (productId) { if (productId) {
inStockElement.style.display= "block"; inStockElement.style.display= "block";
@ -347,7 +375,7 @@
}, },
} }
}); });
})() })();
</script> </script>
@endpush @endpush
@endif @endif