initial commit

This commit is contained in:
prateek srivastava 2019-12-30 20:26:13 +05:30
parent baae82b633
commit 8296fdb6d4
10 changed files with 759 additions and 27 deletions

View File

@ -27,6 +27,10 @@ return [
'value' => false
]
]
], [
'name' => 'featured_product',
'title' => 'velocity::app.admin.system.general.featured-product',
'type' => 'text',
]
]
], [

View File

@ -0,0 +1,80 @@
<?php
namespace Webkul\Velocity\Repositories\Product;
use DB;
use Illuminate\Container\Container as App;
use Illuminate\Support\Facades\Event;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Webkul\Attribute\Repositories\AttributeRepository;
use Webkul\Core\Eloquent\Repository;
use Webkul\Product\Repositories\ProductFlatRepository;
use Webkul\Product\Models\ProductAttributeValue;
/**
* Product Repository
*
* @author Jitendra Singh <jitendra@webkul.com>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class ProductRepository extends Repository
{
/**
* AttributeRepository object
*
* @var array
*/
protected $attributeRepository;
/**
* Create a new controller instance.
*
* @param Webkul\Attribute\Repositories\AttributeRepository $attributeRepository
* @return void
*/
public function __construct(
AttributeRepository $attributeRepository,
App $app
)
{
$this->attributeRepository = $attributeRepository;
parent::__construct($app);
}
/**
* Specify Model class name
*
* @return mixed
*/
function model()
{
return 'Webkul\Product\Contracts\Product';
}
/**
* Returns featured product
*
* @return Collection
*/
public function getFeaturedProducts($count)
{
$results = app(ProductFlatRepository::class)->scopeQuery(function($query) {
$channel = request()->get('channel') ?: (core()->getCurrentChannelCode() ?: core()->getDefaultChannelCode());
$locale = request()->get('locale') ?: app()->getLocale();
return $query->distinct()
->addSelect('product_flat.*')
->where('product_flat.status', 1)
->where('product_flat.visible_individually', 1)
->where('product_flat.featured', 1)
->where('product_flat.channel', $channel)
->where('product_flat.locale', $locale)
->orderBy('product_id', 'desc');
})->paginate($count);
return $results;
}
}

View File

@ -147,7 +147,7 @@
margin-left: 0px;
}
.ml15 {
margin-left: 15px;
margin-left: 15px !important;
}
.body-blur {
filter: blur(4px);

View File

@ -20,7 +20,8 @@ return [
'general' => [
'status' => 'Status',
'active' => 'Active',
'inactive' => 'Inactive'
'inactive' => 'Inactive',
'featured-product' => 'Featured Product'
],
'category' => [
'icon-status' => 'Category Icon Status',

View File

@ -1,5 +1,12 @@
@php
$featuredProducts = app('Webkul\Product\Repositories\ProductRepository')->getFeaturedProducts();
$config = core()->getConfigData('velocity.configuration.general.featured_product');
if ($config > 4)
$count = $config;
else
$count = 4;
$featuredProducts = app('Webkul\Velocity\Repositories\Product\ProductRepository')->getFeaturedProducts($count);
$featuredProductsCount = $featuredProducts->count();

View File

@ -1,34 +1,33 @@
<div class="newsletter-subscription">
<div class="newsletter-wrapper">
@if (core()->getConfigData('customer.settings.newsletter.subscription'))
<div class="newsletter-subscription">
<div class="newsletter-wrapper">
{!! DbView::make(core()->getCurrentChannel())->field('subscription_bar_content')->render() !!}
{!! DbView::make(core()->getCurrentChannel())->field('subscription_bar_content')->render() !!}
<div class="subscribe-newsletter">
<div class="subscribe-newsletter">
@if (core()->getConfigData('customer.settings.newsletter.subscription'))
<div class="form-container">
<div class="form-container">
<form action="{{ route('shop.subscribe') }}">
<div class="subscriber-form-div">
<div
:class="`control-group ${errors.has('subscriber_email') ? 'has-error' : ''}`">
<form action="{{ route('shop.subscribe') }}">
<div class="subscriber-form-div">
<div
:class="`control-group ${errors.has('subscriber_email') ? 'has-error' : ''}`">
<input
type="email"
class="control subscribe-field"
name="subscriber_email"
placeholder="{{ __('velocity::app.customer.login-form.your-email-address') }}"
required />
<input
type="email"
class="control subscribe-field"
name="subscriber_email"
placeholder="{{ __('velocity::app.customer.login-form.your-email-address') }}"
required />
<button class="theme-btn subscribe-btn fw6">
{{ __('shop::app.subscription.subscribe') }}
</button>
<button class="theme-btn subscribe-btn fw6">
{{ __('shop::app.subscription.subscribe') }}
</button>
</div>
</div>
</div>
</form>
</div>
</form>
</div>
@endif
</div>
</div>
</div>
@endif

View File

@ -0,0 +1,229 @@
@if ($product->type == 'bundle')
{!! view_render_event('bagisto.shop.products.view.bundle-options.before', ['product' => $product]) !!}
<bundle-option-list></bundle-option-list>
{!! view_render_event('bagisto.shop.products.view.bundle-options.after', ['product' => $product]) !!}
@push('scripts')
<script type="text/x-template" id="bundle-option-list-template">
<div class="bundle-options-wrapper">
<div class="bundle-option-list">
<h3>{{ __('shop::app.products.customize-options') }}</h3>
<bundle-option-item
v-for="(option, index) in options"
:option="option"
:key="index"
:index="index"
@onProductSelected="productSelected(option, $event)">
</bundle-option-item>
</div>
<div class="bundle-summary">
<h3>{{ __('shop::app.products.your-customization') }}</h3>
<quantity-changer></quantity-changer>
<div class="control-group">
<label>{{ __('shop::app.products.total-amount') }}</label>
<div class="bundle-price">
@{{ formated_total_price | currency(currency_options) }}
</div>
</div>
<ul class="bundle-items">
<li v-for="(option, index) in options">
@{{ option.label }}
<div class="selected-products">
<div v-for="(product, index1) in option.products" v-if="product.is_default">
@{{ product.qty + ' x ' + product.name }}
</div>
</div>
</li>
</ul>
</div>
</div>
</script>
<script type="text/x-template" id="bundle-option-item-template">
<div class="bundle-option-item">
<div class="control-group" :class="[errors.has('bundle_options[' + option.id + '][]') ? 'has-error' : '']">
<label :class="[option.is_required ? 'required' : '']">@{{ option.label }}</label>
<div v-if="option.type == 'select'">
<select class="control" :name="'bundle_options[' + option.id + '][]'" v-model="selected_product" v-validate="option.is_required ? 'required' : ''" :data-vv-as="option.label + '&quot;'">
<option value="">{{ __('shop::app.products.choose-selection') }}</option>
<option v-for="(product, index2) in option.products" :value="product.id">
@{{ product.name + ' + ' + product.price.final_price.formated_price }}
</option>
</select>
</div>
<div v-if="option.type == 'radio'">
<span class="radio" v-if="! option.is_required">
<input type="radio" :name="'bundle_options[' + option.id + '][]'" v-model="selected_product" value="0" :id="'bundle_options[' + option.id + '][]'">
<label class="radio-view" :for="'bundle_options[' + option.id + '][]'"></label>
{{ __('shop::app.products.none') }}
</span>
<span class="radio" v-for="(product, index2) in option.products">
<input type="radio" :name="'bundle_options[' + option.id + '][]'" v-model="selected_product" v-validate="option.is_required ? 'required' : ''" :data-vv-as="'&quot;' + option.label + '&quot;'" :value="product.id" :id="'bundle_options[' + option.id + '][]'">
<label class="radio-view" :for="'bundle_options[' + option.id + '][]'"></label>
@{{ product.name }}
<span class="price">
+ @{{ product.price.final_price.formated_price }}
</span>
</span>
</div>
<div v-if="option.type == 'checkbox'">
<span class="checkbox" v-for="(product, index2) in option.products">
<input type="checkbox" :name="'bundle_options[' + option.id + '][]'" :value="product.id" v-model="selected_product" v-validate="option.is_required ? 'required' : ''" :data-vv-as="'&quot;' + option.label + '&quot;'" :id="'bundle_options[' + option.id + '][]'">
<label class="checkbox-view" :for="'bundle_options[' + option.id + '][]'"></label>
@{{ product.name }}
<span class="price">
+ @{{ product.price.final_price.formated_price }}
</span>
</span>
</div>
<div v-if="option.type == 'multiselect'">
<select class="control" :name="'bundle_options[' + option.id + '][]'" v-model="selected_product" v-validate="option.is_required ? 'required' : ''" :data-vv-as="'&quot;' + option.label + '&quot;'" multiple>
<option v-for="(product, index2) in option.products" :value="product.id">
@{{ product.name + ' + ' + product.price.final_price.formated_price }}
</option>
</select>
</div>
<span class="control-error" v-if="errors.has('bundle_options[' + option.id + '][]')">
@{{ errors.first('bundle_options[' + option.id + '][]') }}
</span>
</div>
<div v-if="option.type == 'select' || option.type == 'radio'">
<quantity-changer
:control-name="'bundle_option_qty[' + option.id + ']'"
:validations="parseInt(selected_product) ? 'required|numeric|min_value:1' : ''"
:quantity="product_qty"
@onQtyUpdated="qtyUpdated($event)">
</quantity-changer>
</div>
</div>
</script>
<script>
Vue.component('bundle-option-list', {
template: '#bundle-option-list-template',
inject: ['$validator'],
data: function() {
return {
config: @json(app('Webkul\Product\Helpers\BundleOption')->getBundleConfig($product)),
options: [],
currency_options: @json(core()->getAccountJsSymbols())
}
},
computed: {
formated_total_price: function() {
var total = 0;
for (var key in this.options) {
for (var key1 in this.options[key].products) {
if (! this.options[key].products[key1].is_default)
continue;
total += this.options[key].products[key1].qty * this.options[key].products[key1].price.final_price.price;
}
}
return total;
}
},
created: function() {
for (var key in this.config.options) {
this.options.push(this.config.options[key])
}
},
methods: {
productSelected: function(option, value) {
var selectedProductIds = Array.isArray(value) ? value : [value];
for (var key in option.products) {
option.products[key].is_default = selectedProductIds.indexOf(option.products[key].id) > -1 ? 1 : 0;
}
}
}
});
Vue.component('bundle-option-item', {
template: '#bundle-option-item-template',
props: ['index', 'option'],
inject: ['$validator'],
data: function() {
return {
selected_product: (this.option.type == 'checkbox' || this.option.type == 'multiselect') ? [] : null,
qty_validations: ''
}
},
computed: {
product_qty: function() {
return this.option.products[this.selected_product]
? this.option.products[this.selected_product].qty
: 0;
}
},
watch: {
selected_product: function (value) {
this.qty_validations = this.selected_product ? 'required|numeric|min_value:1' : '';
this.$emit('onProductSelected', value)
}
},
created: function() {
for (var key1 in this.option.products) {
if (! this.option.products[key1].is_default)
continue;
if (this.option.type == 'checkbox' || this.option.type == 'multiselect') {
this.selected_product.push(this.option.products[key1].id)
} else {
this.selected_product = this.option.products[key1].id
}
}
},
methods: {
qtyUpdated: function(qty) {
if (! this.option.products[this.selected_product])
return;
this.option.products[this.selected_product].qty = qty;
}
}
});
</script>
@endpush
@endif

View File

@ -0,0 +1,326 @@
@if (Webkul\Product\Helpers\ProductType::hasVariants($product->type))
@inject ('configurableOptionHelper', 'Webkul\Product\Helpers\ConfigurableOption')
{!! view_render_event('bagisto.shop.products.view.configurable-options.before', ['product' => $product]) !!}
<h1>xcvxcf</h1>
<product-options></product-options>
{!! view_render_event('bagisto.shop.products.view.configurable-options.after', ['product' => $product]) !!}
@push('scripts')
<script type="text/x-template" id="product-options-template">
<div class="attributes">
<input type="hidden" id="selected_configurable_option" name="selected_configurable_option" :value="selectedProductId">
<div v-for='(attribute, index) in childAttributes' class="attribute control-group" :class="[errors.has('super_attribute[' + attribute.id + ']') ? 'has-error' : '']">
<label class="required">@{{ attribute.label }}</label>
<span v-if="! attribute.swatch_type || attribute.swatch_type == '' || attribute.swatch_type == 'dropdown'">
<select
class="control"
v-validate="'required'"
:name="['super_attribute[' + attribute.id + ']']"
:disabled="attribute.disabled"
@change="configure(attribute, $event.target.value)"
:id="['attribute_' + attribute.id]"
:data-vv-as="'&quot;' + attribute.label + '&quot;'">
<option v-for='(option, index) in attribute.options' :value="option.id">@{{ option.label }}</option>
</select>
</span>
<span class="swatch-container" v-else>
<label class="swatch"
v-for='(option, index) in attribute.options'
v-if="option.id"
:data-id="option.id"
:for="['attribute_' + attribute.id + '_option_' + option.id]">
<input type="radio"
v-validate="'required'"
:name="['super_attribute[' + attribute.id + ']']"
:id="['attribute_' + attribute.id + '_option_' + option.id]"
:value="option.id"
:data-vv-as="'&quot;' + attribute.label + '&quot;'"
@change="configure(attribute, $event.target.value)"/>
<span v-if="attribute.swatch_type == 'color'" :style="{ background: option.swatch_value }"></span>
<img v-if="attribute.swatch_type == 'image'" :src="option.swatch_value" />
<span v-if="attribute.swatch_type == 'text'">
@{{ option.label }}
</span>
</label>
<span v-if="! attribute.options.length" class="no-options">{{ __('shop::app.products.select-above-options') }}</span>
</span>
<span class="control-error" v-if="errors.has('super_attribute[' + attribute.id + ']')">
@{{ errors.first('super_attribute[' + attribute.id + ']') }}
</span>
</div>
</div>
</script>
<?php $config = $configurableOptionHelper->getConfigurationConfig($product) ?>
<script>
Vue.component('product-options', {
template: '#product-options-template',
inject: ['$validator'],
data: function() {
return {
config: @json($config),
childAttributes: [],
selectedProductId: '',
simpleProduct: null,
galleryImages: []
}
},
created: function() {
this.galleryImages = galleryImages.slice(0)
var config = @json($config);
var 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);
}
},
methods: {
configure: function(attribute, value) {
this.simpleProduct = this.getSelectedProductId(attribute, value);
if (value) {
attribute.selectedIndex = this.getSelectedIndex(attribute, value);
if (attribute.nextAttribute) {
attribute.nextAttribute.disabled = false;
this.fillSelect(attribute.nextAttribute);
this.resetChildren(attribute.nextAttribute);
} else {
this.selectedProductId = this.simpleProduct;
}
} else {
attribute.selectedIndex = 0;
this.resetChildren(attribute);
this.clearSelect(attribute.nextAttribute)
}
this.reloadPrice();
this.changeProductImages();
this.changeStock(this.simpleProduct);
},
getSelectedIndex: function(attribute, value) {
var selectedIndex = 0;
attribute.options.forEach(function(option, index) {
if (option.id == value) {
selectedIndex = index;
}
})
return selectedIndex;
},
getSelectedProductId: function(attribute, value) {
var options = attribute.options,
matchedOptions;
matchedOptions = options.filter(function (option) {
return option.id == value;
});
if (matchedOptions[0] != undefined && matchedOptions[0].allowedProducts != undefined) {
return matchedOptions[0].allowedProducts[0];
}
return undefined;
},
fillSelect: function(attribute) {
var options = this.getAttributeOptions(attribute.id),
prevOption,
index = 1,
allowedProducts,
i,
j;
this.clearSelect(attribute)
attribute.options = [{'id': '', 'label': this.config.chooseText, 'products': []}];
if (attribute.prevAttribute) {
prevOption = attribute.prevAttribute.options[attribute.prevAttribute.selectedIndex];
}
if (options) {
for (i = 0; i < options.length; i++) {
allowedProducts = [];
if (prevOption) {
for (j = 0; j < options[i].products.length; j++) {
if (prevOption.allowedProducts && prevOption.allowedProducts.indexOf(options[i].products[j]) > -1) {
allowedProducts.push(options[i].products[j]);
}
}
} else {
allowedProducts = options[i].products.slice(0);
}
if (allowedProducts.length > 0) {
options[i].allowedProducts = allowedProducts;
attribute.options[index] = options[i];
index++;
}
}
}
},
resetChildren: function(attribute) {
if (attribute.childAttributes) {
attribute.childAttributes.forEach(function (set) {
set.selectedIndex = 0;
set.disabled = true;
});
}
},
clearSelect: function (attribute) {
if (! attribute)
return;
if (! attribute.swatch_type || attribute.swatch_type == '' || attribute.swatch_type == 'dropdown') {
var element = document.getElementById("attribute_" + attribute.id);
if (element) {
element.selectedIndex = "0";
}
} else {
var elements = document.getElementsByName('super_attribute[' + attribute.id + ']');
var this_this = this;
elements.forEach(function(element) {
element.checked = false;
})
}
},
getAttributeOptions: function (attributeId) {
var this_this = this,
options;
this.config.attributes.forEach(function(attribute, index) {
if (attribute.id == attributeId) {
options = attribute.options;
}
})
return options;
},
reloadPrice: function () {
var selectedOptionCount = 0;
this.childAttributes.forEach(function(attribute) {
if (attribute.selectedIndex) {
selectedOptionCount++;
}
});
var priceLabelElement = document.querySelector('.price-label');
var priceElement = document.querySelector('.final-price');
if (this.childAttributes.length == selectedOptionCount) {
priceLabelElement.style.display = 'none';
priceElement.innerHTML = this.config.variant_prices[this.simpleProduct].final_price.formated_price;
eventBus.$emit('configurable-variant-selected-event', this.simpleProduct)
} else {
priceLabelElement.style.display = 'inline-block';
priceElement.innerHTML = this.config.regular_price.formated_price;
eventBus.$emit('configurable-variant-selected-event', 0)
}
},
changeProductImages: function () {
galleryImages.splice(0, galleryImages.length)
this.galleryImages.forEach(function(image) {
galleryImages.push(image)
});
if (this.simpleProduct) {
this.config.variant_images[this.simpleProduct].forEach(function(image) {
galleryImages.unshift(image)
});
}
},
changeStock: function (productId) {
var inStockElement = document.getElementById('in-stock');
if (productId) {
inStockElement.style.display= "block";
} else {
inStockElement.style.display= "none";
}
},
}
});
</script>
@endpush
@endif

View File

@ -0,0 +1,50 @@
@if ($product->type == 'downloadable')
{!! view_render_event('bagisto.shop.products.view.downloadable.before', ['product' => $product]) !!}
<div class="downloadable-container">
@if ($product->downloadable_samples->count())
<div class="sample-list">
<h3>{{ __('shop::app.products.samples') }}</h3>
<ul>
@foreach ($product->downloadable_samples as $sample)
<li>
<a href="{{ route('shop.downloadable.download_sample', ['type' => 'sample', 'id' => $sample->id]) }}" target="_blank">
{{ $sample->title }}
</a>
</li>
@endforeach
</ul>
</div>
@endif
@if ($product->downloadable_links->count())
<div class="link-list control-group" :class="[errors.has('links[]') ? 'has-error' : '']">
<h3>{{ __('shop::app.products.links') }}</h3>
<ul>
@foreach ($product->downloadable_links as $link)
<li>
<span class="checkbox">
<input type="checkbox" name="links[]" v-validate="'required'" value="{{ $link->id }}" id="{{ $link->id }}" data-vv-as="&quot;{{ __('shop::app.products.links') }}&quot;"/>
<label class="checkbox-view" for="{{ $link->id }}"></label>
{{ $link->title . ' + ' . core()->currency($link->price) }}
</span>
@if ($link->sample_file || $link->sample_url)
<a href="{{ route('shop.downloadable.download_sample', ['type' => 'link', 'id' => $link->id]) }}" target="_blank">
{{ __('shop::app.products.sample') }}
</a>
@endif
</li>
@endforeach
</ul>
<span class="control-error" v-if="errors.has('links[]')">@{{ errors.first('links[]') }}</span>
</div>
@endif
</div>
{!! view_render_event('bagisto.shop.products.view.downloadable.before', ['product' => $product]) !!}
@endif

View File

@ -0,0 +1,36 @@
@if ($product->type == 'grouped')
{!! view_render_event('bagisto.shop.products.view.grouped_products.before', ['product' => $product]) !!}
<div class="grouped-product-container">
@if ($product->grouped_products->count())
<div class="grouped-product-list">
<ul>
<li>
<span>{{ __('shop::app.products.name') }}</span>
<span>{{ __('shop::app.products.qty') }}</span>
</li>
@foreach ($product->grouped_products as $groupedProduct)
<li>
<span class="name">
{{ $groupedProduct->associated_product->name }}
@include ('shop::products.price', ['product' => $groupedProduct->associated_product])
</span>
<span class="qty">
<quantity-changer
:control-name="'qty[{{$groupedProduct->associated_product_id}}]'"
:validations="'required|numeric|min_value:0'"
quantity="{{ $groupedProduct->qty }}"
min-quantity="0">
</quantity-changer>
</span>
</li>
@endforeach
</ul>
</div>
@endif
</div>
{!! view_render_event('bagisto.shop.products.view.grouped_products.before', ['product' => $product]) !!}
@endif