Product details page added
This commit is contained in:
parent
bc087010ee
commit
0c8b5a03e2
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace App\Criteria;
|
||||
|
||||
use Prettus\Repository\Contracts\CriteriaInterface;
|
||||
use Prettus\Repository\Contracts\RepositoryInterface;
|
||||
|
||||
/**
|
||||
* Class MyCriteria.
|
||||
*
|
||||
* @package namespace App\Criteria;
|
||||
*/
|
||||
class MyCriteria implements CriteriaInterface
|
||||
{
|
||||
/**
|
||||
* Apply criteria in query repository
|
||||
*
|
||||
* @param string $model
|
||||
* @param RepositoryInterface $repository
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function apply($model, RepositoryInterface $repository)
|
||||
{
|
||||
return $model;
|
||||
}
|
||||
}
|
||||
|
|
@ -133,7 +133,8 @@ return [
|
|||
'value_per_channel' => 'Value Per Channel',
|
||||
'is_filterable' => 'Use in Layered Navigation',
|
||||
'is_configurable' => 'Use To Create Configurable Product',
|
||||
'admin_name' => 'Admin Name'
|
||||
'admin_name' => 'Admin Name',
|
||||
'is_visible_on_front' => 'Visible on Product View Page on Front-end'
|
||||
],
|
||||
'families' => [
|
||||
'families' => 'Families',
|
||||
|
|
|
|||
|
|
@ -154,6 +154,14 @@
|
|||
<option value="1">{{ __('admin::app.catalog.attributes.yes') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label for="is_visible_on_front">{{ __('admin::app.catalog.attributes.is_visible_on_front') }}</label>
|
||||
<select class="control" id="is_visible_on_front" name="is_visible_on_front">
|
||||
<option value="0">{{ __('admin::app.catalog.attributes.no') }}</option>
|
||||
<option value="1">{{ __('admin::app.catalog.attributes.yes') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</accordian>
|
||||
|
|
|
|||
|
|
@ -210,6 +210,18 @@
|
|||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label for="is_visible_on_front">{{ __('admin::app.catalog.attributes.is_visible_on_front') }}</label>
|
||||
<select class="control" id="is_visible_on_front" name="is_visible_on_front">
|
||||
<option value="0" {{ $attribute->is_visible_on_front ? '' : 'selected' }}>
|
||||
{{ __('admin::app.catalog.attributes.no') }}
|
||||
</option>
|
||||
<option value="1" {{ $attribute->is_visible_on_front ? 'selected' : '' }}>
|
||||
{{ __('admin::app.catalog.attributes.yes') }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</accordian>
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ class CreateAttributesTable extends Migration
|
|||
$table->boolean('is_filterable')->default(0);
|
||||
$table->boolean('is_configurable')->default(0);
|
||||
$table->boolean('is_user_defined')->default(1);
|
||||
$table->boolean('is_visible_on_front')->default(0);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ class Attribute extends TranslatableModel
|
|||
{
|
||||
public $translatedAttributes = ['name'];
|
||||
|
||||
protected $fillable = ['code', 'admin_name', 'type', 'position', 'is_required', 'is_unique', 'value_per_locale', 'value_per_channel', 'is_filterable', 'is_configurable'];
|
||||
protected $fillable = ['code', 'admin_name', 'type', 'position', 'is_required', 'is_unique', 'value_per_locale', 'value_per_channel', 'is_filterable', 'is_configurable', 'is_visible_on_front'];
|
||||
|
||||
protected $with = ['options'];
|
||||
|
||||
|
|
@ -22,14 +22,6 @@ class Attribute extends TranslatableModel
|
|||
return $this->hasMany(AttributeOption::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the options.
|
||||
*/
|
||||
public function filter_attributes()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope a query to only include popular users.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Webkul\Core;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Webkul\Core\Models\Channel as ChannelModel;
|
||||
use Webkul\Core\Models\Locale as LocaleModel;
|
||||
use Webkul\Core\Models\Currency as CurrencyModel;
|
||||
|
|
@ -14,7 +15,12 @@ class Core
|
|||
* @return Collection
|
||||
*/
|
||||
public function getAllChannels() {
|
||||
return ChannelModel::all();
|
||||
static $channels;
|
||||
|
||||
if($channels)
|
||||
return $channels;
|
||||
|
||||
return $channels = ChannelModel::all();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -23,7 +29,12 @@ class Core
|
|||
* @return mixed
|
||||
*/
|
||||
public function getCurrentChannel() {
|
||||
return ChannelModel::first();
|
||||
static $channel;
|
||||
|
||||
if($channel)
|
||||
return $channel;
|
||||
|
||||
return $channel = ChannelModel::first();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -32,7 +43,12 @@ class Core
|
|||
* @return string
|
||||
*/
|
||||
public function getCurrentChannelCode() {
|
||||
return ($channel = $this->getCurrentChannel()) ? $channel->code : '';
|
||||
static $channelCode;
|
||||
|
||||
if($channelCode)
|
||||
return $channelCode;
|
||||
|
||||
return ($channel = $this->getCurrentChannel()) ? $channelCode = $channel->code : '';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -41,7 +57,12 @@ class Core
|
|||
* @return Collection
|
||||
*/
|
||||
public function getAllLocales() {
|
||||
return LocaleModel::all();
|
||||
static $locales;
|
||||
|
||||
if($locales)
|
||||
return $locales;
|
||||
|
||||
return $locales = LocaleModel::all();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -51,7 +72,12 @@ class Core
|
|||
*/
|
||||
public function getAllCurrencies()
|
||||
{
|
||||
return CurrencyModel::all();
|
||||
static $currencies;
|
||||
|
||||
if($currencies)
|
||||
return $currencies;
|
||||
|
||||
return $currencies = CurrencyModel::all();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -61,9 +87,12 @@ class Core
|
|||
*/
|
||||
public function getCurrentCurrency()
|
||||
{
|
||||
$currency = CurrencyModel::first();
|
||||
static $currency;
|
||||
|
||||
return $currency;
|
||||
if($currency)
|
||||
return $currency;
|
||||
|
||||
return $currency = CurrencyModel::first();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -73,7 +102,12 @@ class Core
|
|||
*/
|
||||
public function getCurrentCurrencyCode()
|
||||
{
|
||||
return ($currency = $this->getCurrentCurrency()) ? $currency->code : '';
|
||||
static $currencyCode;
|
||||
|
||||
if($currencyCode)
|
||||
return $currencyCode;
|
||||
|
||||
return ($currency = $this->getCurrentCurrency()) ? $currencyCode = $currency->code : '';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -83,7 +117,12 @@ class Core
|
|||
*/
|
||||
public function getCurrentCurrencySymbol()
|
||||
{
|
||||
return $this->getCurrentCurrency()->symbol;
|
||||
static $currencySymbol;
|
||||
|
||||
if($currencySymbol)
|
||||
return $currencySymbol;
|
||||
|
||||
return $currencySymbol = $this->getCurrentCurrency()->symbol;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -171,4 +210,25 @@ class Core
|
|||
}
|
||||
|
||||
// $timezonelist = \DateTimeZone::listIdentifiers(\DateTimeZone::ALL);
|
||||
|
||||
|
||||
/**
|
||||
* Format date using current channel.
|
||||
*
|
||||
* @param date|null $date
|
||||
* @param string $format
|
||||
* @return string
|
||||
*/
|
||||
public function formatDate($date = null, $format = 'd-m-Y H:i:s')
|
||||
{
|
||||
$channel = $this->getCurrentChannel();
|
||||
|
||||
if (is_null($date)) {
|
||||
$date = Carbon::now();
|
||||
}
|
||||
|
||||
$date->setTimezone($channel->timezone);
|
||||
|
||||
return $date->format($format);
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
{
|
||||
$results = [];
|
||||
|
||||
while (list($key, $values) = each($input)) {
|
||||
foreach ($input as $key => $values) {
|
||||
if (empty($values)) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -41,7 +41,7 @@
|
|||
$results = array_merge($results, $append);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,4 +16,8 @@ class Customer extends Authenticatable
|
|||
|
||||
protected $table = 'customers';
|
||||
protected $hidden = ['password','remember_token'];
|
||||
|
||||
public function getNameAttribute() {
|
||||
return ucfirst($this->first_name) . ' ' . ucfirst($this->last_name);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
namespace Webkul\Product\Contracts\Criteria;
|
||||
|
||||
use Prettus\Repository\Contracts\CriteriaInterface;
|
||||
use Prettus\Repository\Contracts\RepositoryInterface;
|
||||
use Webkul\Product\Models\ProductAttributeValue;
|
||||
use Webkul\Attribute\Repositories\AttributeRepository;
|
||||
use Webkul\Product\Product\AbstractProduct;
|
||||
|
||||
/**
|
||||
* Class MyCriteria.
|
||||
*
|
||||
* @package namespace App\Criteria;
|
||||
*/
|
||||
class AttributeToSelectCriteria extends AbstractProduct implements CriteriaInterface
|
||||
{
|
||||
/**
|
||||
* @var AttributeRepository
|
||||
*/
|
||||
protected $attribute;
|
||||
|
||||
/**
|
||||
* @var array|string
|
||||
*/
|
||||
protected $attributeToSelect;
|
||||
|
||||
/**
|
||||
* @param Webkul\Attribute\Repositories\AttributeRepository $attribute
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(AttributeRepository $attribute)
|
||||
{
|
||||
$this->attribute = $attribute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set attributes in attributeToSelect variable
|
||||
*
|
||||
* @param array|string $attributes
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function addAttribueToSelect($attributes)
|
||||
{
|
||||
$this->attributeToSelect = $attributes;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply criteria in query repository
|
||||
*
|
||||
* @param string $model
|
||||
* @param RepositoryInterface $repository
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function apply($model, RepositoryInterface $repository)
|
||||
{
|
||||
$model = $model->select('products.*');
|
||||
|
||||
foreach ($this->attributeToSelect as $code) {
|
||||
$attribute = $this->attribute->findOneByField('code', $code);
|
||||
|
||||
if(!$attribute)
|
||||
continue;
|
||||
|
||||
$productValueAlias = 'pav_' . $attribute->code;
|
||||
|
||||
$model = $model->leftJoin('product_attribute_values as ' . $productValueAlias, function($qb) use($attribute, $productValueAlias) {
|
||||
|
||||
$qb = $this->applyChannelLocaleFilter($attribute, $qb, $productValueAlias);
|
||||
|
||||
$qb->on('products.id', $productValueAlias . '.product_id')
|
||||
->where($productValueAlias . '.attribute_id', $attribute->id);
|
||||
});
|
||||
|
||||
$model = $model->addSelect($productValueAlias . '.' . ProductAttributeValue::$attributeTypeFields[$attribute->type] . ' as ' . $code);
|
||||
}
|
||||
|
||||
return $model;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
namespace Webkul\Product\Contracts\Criteria;
|
||||
|
||||
use Prettus\Repository\Contracts\CriteriaInterface;
|
||||
use Prettus\Repository\Contracts\RepositoryInterface;
|
||||
use Webkul\Product\Models\ProductAttributeValue;
|
||||
use Webkul\Attribute\Repositories\AttributeRepository;
|
||||
use Webkul\Product\Product\AbstractProduct;
|
||||
|
||||
/**
|
||||
* Class MyCriteria.
|
||||
*
|
||||
* @package namespace App\Criteria;
|
||||
*/
|
||||
class FilterByAttributesCriteria extends AbstractProduct implements CriteriaInterface
|
||||
{
|
||||
/**
|
||||
* @var AttributeRepository
|
||||
*/
|
||||
protected $attribute;
|
||||
|
||||
/**
|
||||
* @param Webkul\Attribute\Repositories\AttributeRepository $attribute
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(AttributeRepository $attribute)
|
||||
{
|
||||
$this->attribute = $attribute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply criteria in query repository
|
||||
*
|
||||
* @param string $model
|
||||
* @param RepositoryInterface $repository
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function apply($model, RepositoryInterface $repository)
|
||||
{
|
||||
$model = $model->leftJoin('products as variants', 'products.id', '=', 'variants.parent_id');
|
||||
|
||||
$model = $model->where(function($query1) use($model) {
|
||||
$aliases = [
|
||||
'products' => 'filter_',
|
||||
'variants' => 'variant_filter_'
|
||||
];
|
||||
|
||||
foreach($aliases as $table => $alias) {
|
||||
$query1 = $query1->orWhere(function($query2) use($model, $table, $alias) {
|
||||
|
||||
foreach (request()->input() as $code => $value) {
|
||||
$aliasTemp = $alias . $code;
|
||||
|
||||
$attribute = $this->attribute->findOneByField('code', $code);
|
||||
|
||||
if(!$attribute)
|
||||
continue;
|
||||
|
||||
$model = $model->leftJoin('product_attribute_values as ' . $aliasTemp, $table . '.id', '=', $aliasTemp . '.product_id');
|
||||
|
||||
$query2 = $this->applyChannelLocaleFilter($attribute, $query2, $aliasTemp);
|
||||
|
||||
$column = ProductAttributeValue::$attributeTypeFields[$attribute->type];
|
||||
|
||||
$temp = explode(',', $value);
|
||||
if($attribute->type != 'price') {
|
||||
$query2 = $query2->where($aliasTemp . '.attribute_id', $attribute->id);
|
||||
|
||||
$query2 = $query2->where(function($query3) use($aliasTemp, $column, $temp) {
|
||||
foreach($temp as $code => $filterValue) {
|
||||
$query3 = $query3->orWhere($aliasTemp . '.' . $column, $filterValue);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$query2 = $query2->where($aliasTemp . '.' . $column, '>=', current($temp))
|
||||
->where($aliasTemp . '.' . $column, '<=', end($temp))
|
||||
->where($aliasTemp . '.attribute_id', $attribute->id);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return $model;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace Webkul\Product\Contracts\Criteria;
|
||||
|
||||
use Prettus\Repository\Contracts\CriteriaInterface;
|
||||
use Prettus\Repository\Contracts\RepositoryInterface;
|
||||
|
||||
/**
|
||||
* Class MyCriteria.
|
||||
*
|
||||
* @package namespace App\Criteria;
|
||||
*/
|
||||
class FilterByCategoryCriteria implements CriteriaInterface
|
||||
{
|
||||
/**
|
||||
* @var integer
|
||||
*/
|
||||
protected $categoryId;
|
||||
|
||||
/**
|
||||
* @param integer $categoryId
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($categoryId)
|
||||
{
|
||||
$this->categoryId = $categoryId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply criteria in query repository
|
||||
*
|
||||
* @param string $model
|
||||
* @param RepositoryInterface $repository
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function apply($model, RepositoryInterface $repository)
|
||||
{
|
||||
$model = $model->leftJoin('product_categories', 'products.id', '=', 'product_categories.product_id')
|
||||
->where('product_categories.category_id', $this->categoryId);
|
||||
|
||||
return $model;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace Webkul\Product\Contracts\Criteria;
|
||||
|
||||
use Prettus\Repository\Contracts\CriteriaInterface;
|
||||
use Prettus\Repository\Contracts\RepositoryInterface;
|
||||
use Webkul\Product\Models\ProductAttributeValue;
|
||||
use Webkul\Attribute\Repositories\AttributeRepository;
|
||||
use Webkul\Product\Product\AbstractProduct;
|
||||
|
||||
/**
|
||||
* Class MyCriteria.
|
||||
*
|
||||
* @package namespace App\Criteria;
|
||||
*/
|
||||
class SortCriteria extends AbstractProduct implements CriteriaInterface
|
||||
{
|
||||
/**
|
||||
* @var AttributeRepository
|
||||
*/
|
||||
protected $attribute;
|
||||
|
||||
/**
|
||||
* @param Webkul\Attribute\Repositories\AttributeRepository $attribute
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(AttributeRepository $attribute)
|
||||
{
|
||||
$this->attribute = $attribute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply criteria in query repository
|
||||
*
|
||||
* @param string $model
|
||||
* @param RepositoryInterface $repository
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function apply($model, RepositoryInterface $repository)
|
||||
{
|
||||
$params = request()->input();
|
||||
|
||||
if(isset($params['sort'])) {
|
||||
if($params['sort'] == 'name' || $params['sort'] == 'price') {
|
||||
$attribute = $this->attribute->findOneByField('code', $params['sort']);
|
||||
|
||||
$alias = 'sort_' . $params['sort'];
|
||||
|
||||
$model = $model->leftJoin('product_attribute_values as ' . $alias, function($qb) use($attribute, $alias) {
|
||||
|
||||
$qb = $this->applyChannelLocaleFilter($attribute, $qb, $alias);
|
||||
|
||||
$qb->on('products.id', $alias . '.product_id')
|
||||
->where($alias . '.attribute_id', $attribute->id);
|
||||
});
|
||||
|
||||
$model = $model->addSelect($alias . '.' . ProductAttributeValue::$attributeTypeFields[$attribute->type] . ' as ' . $attribute->code);
|
||||
|
||||
$model = $model->orderBy($attribute->code, $params['order']);
|
||||
|
||||
} else {
|
||||
$model = $model->orderBy($params['sort'], $params['order']);
|
||||
}
|
||||
} else {
|
||||
$model = $model->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
return $model;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace Webkul\Product\Database\Eloquent;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder as BaseBuilder;
|
||||
use Illuminate\Pagination\Paginator;
|
||||
|
||||
/**
|
||||
* @mixin \Illuminate\Database\Query\Builder
|
||||
*/
|
||||
class Builder extends BaseBuilder
|
||||
{
|
||||
/**
|
||||
* Paginate the given query.
|
||||
*
|
||||
* @param int $perPage
|
||||
* @param array $columns
|
||||
* @param string $pageName
|
||||
* @param int|null $page
|
||||
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function paginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null)
|
||||
{
|
||||
$page = $page ?: Paginator::resolveCurrentPage($pageName);
|
||||
|
||||
$perPage = $perPage ?: $this->model->getPerPage();
|
||||
|
||||
$results = ($total = $this->toBase()->getCountForPagination($columns))
|
||||
? $this->forPage($page, $perPage)->get($columns)
|
||||
: $this->model->newCollection();
|
||||
|
||||
return $this->paginator($results, $total, $perPage, $page, [
|
||||
'path' => Paginator::resolveCurrentPath(),
|
||||
'pageName' => $pageName,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -111,7 +111,7 @@ class Product extends Model
|
|||
*/
|
||||
public function up_sells()
|
||||
{
|
||||
return $this->belongsToMany(self::class, 'product_up_sells');
|
||||
return $this->belongsToMany(self::class, 'product_up_sells', 'parent_id', 'child_id');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -141,7 +141,7 @@ class Product extends Model
|
|||
public function getAttribute($key)
|
||||
{
|
||||
if (!method_exists(self::class, $key) && !in_array($key, ['parent_id', 'attribute_family_id']) && !isset($this->attributes[$key])) {
|
||||
if ($this->isCustomAttribute($key)) {
|
||||
if (isset($this->id) && $this->isCustomAttribute($key)) {
|
||||
$this->attributes[$key] = '';
|
||||
|
||||
$attributeModel = $this->attribute_family->custom_attributes()->where('attributes.code', $key)->first();
|
||||
|
|
@ -185,32 +185,46 @@ class Product extends Model
|
|||
|
||||
$hiddenAttributes = $this->getHidden();
|
||||
|
||||
$channel = request()->get('channel') ?: core()->getCurrentChannelCode();
|
||||
if(isset($this->id)) {
|
||||
$channel = request()->get('channel') ?: core()->getCurrentChannelCode();
|
||||
|
||||
$locale = request()->get('locale') ?: app()->getLocale();
|
||||
$locale = request()->get('locale') ?: app()->getLocale();
|
||||
|
||||
foreach ($this->attribute_family->custom_attributes as $attribute) {
|
||||
if (in_array($attribute->code, $hiddenAttributes)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if($attribute->value_per_channel) {
|
||||
if($attribute->value_per_locale) {
|
||||
$attributeValue = $this->attribute_values()->where('channel', $channel)->where('locale', $locale)->where('attribute_id', $attribute->id)->first();
|
||||
} else {
|
||||
$attributeValue = $this->attribute_values()->where('channel', $channel)->where('attribute_id', $attribute->id)->first();
|
||||
foreach ($this->attribute_family->custom_attributes as $attribute) {
|
||||
if (in_array($attribute->code, $hiddenAttributes)) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if($attribute->value_per_locale) {
|
||||
$attributeValue = $this->attribute_values()->where('locale', $locale)->where('attribute_id', $attribute->id)->first();
|
||||
} else {
|
||||
$attributeValue = $this->attribute_values()->where('attribute_id', $attribute->id)->first();
|
||||
}
|
||||
}
|
||||
|
||||
$attributes[$attribute->code] = $attributeValue[ProductAttributeValue::$attributeTypeFields[$attribute->type]];
|
||||
if($attribute->value_per_channel) {
|
||||
if($attribute->value_per_locale) {
|
||||
$attributeValue = $this->attribute_values()->where('channel', $channel)->where('locale', $locale)->where('attribute_id', $attribute->id)->first();
|
||||
} else {
|
||||
$attributeValue = $this->attribute_values()->where('channel', $channel)->where('attribute_id', $attribute->id)->first();
|
||||
}
|
||||
} else {
|
||||
if($attribute->value_per_locale) {
|
||||
$attributeValue = $this->attribute_values()->where('locale', $locale)->where('attribute_id', $attribute->id)->first();
|
||||
} else {
|
||||
$attributeValue = $this->attribute_values()->where('attribute_id', $attribute->id)->first();
|
||||
}
|
||||
}
|
||||
|
||||
$attributes[$attribute->code] = $attributeValue[ProductAttributeValue::$attributeTypeFields[$attribute->type]];
|
||||
}
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the default Eloquent query builder
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
public function newEloquentBuilder($query)
|
||||
{
|
||||
return new \Webkul\Product\Database\Eloquent\Builder($query);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -3,8 +3,17 @@
|
|||
namespace Webkul\Product\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Webkul\Customer\Models\Customer;
|
||||
|
||||
class ProductReview extends Model
|
||||
{
|
||||
protected $fillable = [];
|
||||
|
||||
/**
|
||||
* Get the product attribute family that owns the product.
|
||||
*/
|
||||
public function customer()
|
||||
{
|
||||
return $this->belongsTo(Customer::class);
|
||||
}
|
||||
}
|
||||
|
|
@ -35,30 +35,4 @@ abstract class AbstractProduct
|
|||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds attributes to select
|
||||
*
|
||||
* @param QB $qb
|
||||
* @return QB
|
||||
*/
|
||||
public function addSelectAttributes($qb)
|
||||
{
|
||||
foreach ($this->attributeToSelect as $code) {
|
||||
$attribute = $this->attribute->findOneByField('code', $code);
|
||||
|
||||
$productValueAlias = 'pav_' . $attribute->code;
|
||||
|
||||
$qb->leftJoin('product_attribute_values as ' . $productValueAlias, function($leftJoin) use($attribute, $productValueAlias) {
|
||||
|
||||
$leftJoin->on('products.id', $productValueAlias . '.product_id');
|
||||
|
||||
$leftJoin = $this->applyChannelLocaleFilter($attribute, $leftJoin, $productValueAlias)->where($productValueAlias . '.attribute_id', $attribute->id);
|
||||
});
|
||||
|
||||
$qb->addSelect($productValueAlias . '.' . ProductAttributeValue::$attributeTypeFields[$attribute->type] . ' as ' . $code);
|
||||
}
|
||||
|
||||
return $qb;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Webkul\Product\Product;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Webkul\Product\Repositories\ProductRepository as Product;
|
||||
use Webkul\Attribute\Repositories\AttributeRepository as Attribute;
|
||||
|
||||
class Collection extends AbstractProduct
|
||||
{
|
||||
/**
|
||||
* ProductRepository object
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $product;
|
||||
|
||||
/**
|
||||
* AttributeRepository object
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $attribute;
|
||||
|
||||
/**
|
||||
* array object
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $attributeToSelect = [
|
||||
'name',
|
||||
'description',
|
||||
'short_description',
|
||||
'price',
|
||||
'special_price',
|
||||
'special_price_from',
|
||||
'special_price_to'
|
||||
];
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*
|
||||
* @param Webkul\Product\Repositories\ProductRepository $product
|
||||
* @param Webkul\Attribute\Repositories\AttributeRepository $attribute
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Product $product, Attribute $attribute)
|
||||
{
|
||||
$this->product = $product;
|
||||
|
||||
$this->attribute = $attribute;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $attributes
|
||||
* @return Void
|
||||
*/
|
||||
public function addAttributesToSelect($attributes)
|
||||
{
|
||||
$this->attributeToSelect = array_unique(
|
||||
array_merge($this->attributeToSelect, $attributes)
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param integer $categoryId
|
||||
* @return Collection
|
||||
*/
|
||||
public function getCollection($categoryId = null)
|
||||
{
|
||||
$qb = $this->product->getModel()
|
||||
->select('products.*')
|
||||
->join('product_categories', 'products.id', '=', 'product_categories.product_id')
|
||||
->where('product_categories.category_id', $categoryId);
|
||||
|
||||
$this->addSelectAttributes($qb);
|
||||
|
||||
// foreach (request()->input() as $code => $value) {
|
||||
// $filterAlias = 'filter_' . $code;
|
||||
|
||||
// $qb->leftJoin('product_attribute_values as ' . $filterAlias, 'products.id', '=', $filterAlias . '.product_id');
|
||||
|
||||
// $qb->where($filterAlias . '.' . ProductAttributeValue::$attributeTypeFields[$attribute->type], $value);
|
||||
// }
|
||||
|
||||
// if(0) {
|
||||
// $qb->orderBy('id', 'desc');
|
||||
// }
|
||||
|
||||
return $qb->paginate(9);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace Webkul\Shop\Product;
|
||||
namespace Webkul\Product\Product;
|
||||
|
||||
class Review extends AbstractProduct
|
||||
{
|
||||
/**
|
||||
* Returns the product's avg rating
|
||||
*
|
||||
* @param Product $product
|
||||
* @return float
|
||||
*/
|
||||
public function getAverageRating($product)
|
||||
{
|
||||
return round($product->reviews->average('rating'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total review of the product
|
||||
*
|
||||
* @param Product $product
|
||||
* @return integer
|
||||
*/
|
||||
public function getTotalReviews($product)
|
||||
{
|
||||
return $product->reviews()->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the formated created at date
|
||||
*
|
||||
* @param ProductReview $review
|
||||
* @return integer
|
||||
*/
|
||||
public function formatDate($reviewCreatedAt)
|
||||
{
|
||||
return core()->formatDate($reviewCreatedAt, 'd, M Y');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
<?php
|
||||
|
||||
namespace Webkul\Product\Product;
|
||||
|
||||
class Toolbar extends AbstractProduct
|
||||
{
|
||||
/**
|
||||
* Returns available sort orders
|
||||
*
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
public function getAvailableOrders()
|
||||
{
|
||||
return [
|
||||
'name-asc' => 'from-a-z',
|
||||
'name-desc' => 'from-z-a',
|
||||
'created_at-desc' => 'newest-first',
|
||||
'created_at-asc' => 'oldest-first',
|
||||
'price-asc' => 'cheapest-first',
|
||||
'price-desc' => 'expansive-first'
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Returns available limits
|
||||
*
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
public function getAvailableLimits()
|
||||
{
|
||||
return [9, 15, 21, 28];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sort order url
|
||||
*
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
public function getOrderUrl($key)
|
||||
{
|
||||
$keys = explode('-', $key);
|
||||
|
||||
return request()->fullUrlWithQuery([
|
||||
'sort' => current($keys),
|
||||
'order' => end($keys)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the limit url
|
||||
*
|
||||
* @param integer $limit
|
||||
* @return string
|
||||
*/
|
||||
public function getLimitUrl($limit)
|
||||
{
|
||||
return request()->fullUrlWithQuery([
|
||||
'limit' => $limit
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mode url
|
||||
*
|
||||
* @param string $mode
|
||||
* @return string
|
||||
*/
|
||||
public function getModeUrl($mode)
|
||||
{
|
||||
return request()->fullUrlWithQuery([
|
||||
'mode' => $mode
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if sort order is active
|
||||
*
|
||||
* @param string $key
|
||||
* @return boolean
|
||||
*/
|
||||
public function isOrderCurrent($key)
|
||||
{
|
||||
$params = request()->input();
|
||||
|
||||
if(isset($params['sort']) && $key == $params['sort'] . '-' . $params['order'])
|
||||
return true;
|
||||
elseif(!isset($params['sort']) && $key == 'created_at-desc')
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if limit is active
|
||||
*
|
||||
* @param integer $limit
|
||||
* @return boolean
|
||||
*/
|
||||
public function isLimitCurrent($limit)
|
||||
{
|
||||
$params = request()->input();
|
||||
|
||||
if(isset($params['limit']) && $limit == $params['limit'])
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if mode is active
|
||||
*
|
||||
* @param string $key
|
||||
* @return boolean
|
||||
*/
|
||||
public function isModeActive($key)
|
||||
{
|
||||
$params = request()->input();
|
||||
|
||||
if(isset($params['mode']) && $key == $params['mode'])
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current mode
|
||||
*
|
||||
* @param string $mode
|
||||
* @return string
|
||||
*/
|
||||
public function getCurrentMode()
|
||||
{
|
||||
$params = request()->input();
|
||||
|
||||
if(isset($params['mode']))
|
||||
return $params['mode'];
|
||||
|
||||
return 'grid';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace Webkul\Product\Product;
|
||||
|
||||
class View extends AbstractProduct
|
||||
{
|
||||
/**
|
||||
* Returns the visible custom attributes
|
||||
*
|
||||
* @param Product $product
|
||||
* @return integer
|
||||
*/
|
||||
public function getAdditionalData($product)
|
||||
{
|
||||
$data = [];
|
||||
|
||||
$attributes = $product->attribute_family->custom_attributes;
|
||||
|
||||
foreach($attributes as $attribute) {
|
||||
if($attribute->is_visible_on_front) {
|
||||
$data[] = [
|
||||
'code' => $attribute->code,
|
||||
'label' => $attribute->name,
|
||||
'value' => $product->{$attribute->code},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
|
@ -74,7 +74,7 @@ class ProductAttributeValueRepository extends Repository
|
|||
*/
|
||||
public function isValueUnique($productId, $attributeId, $column, $value)
|
||||
{
|
||||
$result = $this->resetScope()->model->where($column, $value)->where('attribute_id', '!=', $attributeId)->where('product_id', '!=', $productId)->get();
|
||||
$result = $this->resetScope()->model->where($column, $value)->where('attribute_id', '=', $attributeId)->where('product_id', '!=', $productId)->get();
|
||||
|
||||
return $result->count() ? false : true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,11 @@ use Webkul\Product\Repositories\ProductAttributeValueRepository;
|
|||
use Webkul\Product\Repositories\ProductInventoryRepository;
|
||||
use Webkul\Product\Repositories\ProductImageRepository;
|
||||
use Webkul\Product\Models\ProductAttributeValue;
|
||||
use Webkul\Product\Contracts\Criteria\SortCriteria;
|
||||
use Webkul\Product\Contracts\Criteria\AttributeToSelectCriteria;
|
||||
use Webkul\Product\Contracts\Criteria\FilterByAttributesCriteria;
|
||||
use Webkul\Product\Contracts\Criteria\FilterByCategoryCriteria;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
|
||||
/**
|
||||
* Product Repository
|
||||
|
|
@ -177,6 +182,8 @@ class ProductRepository extends Repository
|
|||
}
|
||||
}
|
||||
|
||||
$previousVariantIds = $product->variants->pluck('id');
|
||||
|
||||
if(isset($data['variants'])) {
|
||||
foreach ($data['variants'] as $variantId => $variantData) {
|
||||
if (str_contains($variantId, 'variant_')) {
|
||||
|
|
@ -187,13 +194,22 @@ class ProductRepository extends Repository
|
|||
|
||||
$this->createVariant($product, $permutation, $variantData);
|
||||
} else {
|
||||
if(is_numeric($index = $previousVariantIds->search($variantId))) {
|
||||
$previousVariantIds->forget($index);
|
||||
}
|
||||
|
||||
$variantData['channel'] = $data['channel'];
|
||||
$variantData['locale'] = $data['locale'];
|
||||
|
||||
$this->updateVariant($variantData, $variantId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($previousVariantIds as $variantId) {
|
||||
$this->delete($variantId);
|
||||
}
|
||||
|
||||
$this->productInventory->saveInventories($data, $product);
|
||||
|
||||
$this->productImage->uploadImages($data, $product);
|
||||
|
|
@ -358,4 +374,53 @@ class ProductRepository extends Repository
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param integer $categoryId
|
||||
* @return Collection
|
||||
*/
|
||||
public function findAllByCategory($categoryId = null)
|
||||
{
|
||||
$this->pushCriteria(app(SortCriteria::class));
|
||||
$this->pushCriteria(app(FilterByAttributesCriteria::class));
|
||||
$this->pushCriteria(new FilterByCategoryCriteria($categoryId));
|
||||
$this->pushCriteria(app(AttributeToSelectCriteria::class)->addAttribueToSelect([
|
||||
'name',
|
||||
'description',
|
||||
'short_description',
|
||||
'price',
|
||||
'special_price',
|
||||
'special_price_from',
|
||||
'special_price_to'
|
||||
]));
|
||||
|
||||
$params = request()->input();
|
||||
|
||||
return $this->scopeQuery(function($query){
|
||||
return $query->distinct()->addSelect('products.*');
|
||||
})->paginate(isset($params['limit']) ? $params['limit'] : 9, ['products.id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrive product from slug
|
||||
*
|
||||
* @param string $slug
|
||||
* @return mixed
|
||||
*/
|
||||
public function findBySlugOrFail($slug)
|
||||
{
|
||||
$attribute = $this->attribute->findOneByField('code', 'url_key');
|
||||
|
||||
$attributeValue = $this->attributeValue->findOneWhere([
|
||||
'attribute_id' => $attribute->id,
|
||||
ProductAttributeValue::$attributeTypeFields[$attribute->type] => $slug
|
||||
]);
|
||||
|
||||
if($attributeValue && $attributeValue->product)
|
||||
return $attributeValue->product;
|
||||
|
||||
throw (new ModelNotFoundException)->setModel(
|
||||
get_class($this->model), $slug
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace Webkul\Product\Repositories;
|
||||
|
||||
use Webkul\Core\Eloquent\Repository;
|
||||
|
||||
/**
|
||||
* Product Review Reposotory
|
||||
*
|
||||
* @author Jitendra Singh <jitendra@webkul.com>
|
||||
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
|
||||
*/
|
||||
class ProductReviewRepository extends Repository
|
||||
{
|
||||
/**
|
||||
* Specify Model class name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
function model()
|
||||
{
|
||||
return 'Webkul\Product\Models\ProductReview';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
namespace Webkul\Shop\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Webkul\Product\Repositories\ProductRepository as Product;
|
||||
|
||||
/**
|
||||
* Product controller
|
||||
*
|
||||
* @author Jitendra Singh <jitendra@webkul.com>
|
||||
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
|
||||
*/
|
||||
class ProductController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* Contains route related configuration
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_config;
|
||||
|
||||
/**
|
||||
* ProductRepository object
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $product;
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*
|
||||
* @param Webkul\Product\Repositories\ProductRepository $product
|
||||
* @return void
|
||||
*/
|
||||
public function __construct( Product $product)
|
||||
{
|
||||
$this->product = $product;
|
||||
|
||||
$this->_config = request('_config');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param string $slug
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index($slug)
|
||||
{
|
||||
$product = $this->product->findBySlugOrFail($slug);
|
||||
|
||||
return view($this->_config['view'], compact('product'));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
namespace Webkul\Shop\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Webkul\Product\Repositories\ProductRepository as Product;
|
||||
use Webkul\Product\Repositories\ProductReviewRepository as ProductReview;
|
||||
|
||||
/**
|
||||
* Review controller
|
||||
*
|
||||
* @author Jitendra Singh <jitendra@webkul.com>
|
||||
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
|
||||
*/
|
||||
class ReviewController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* Contains route related configuration
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_config;
|
||||
|
||||
/**
|
||||
* ProductRepository object
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $product;
|
||||
|
||||
/**
|
||||
* ProductReviewRepository object
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $productReview;
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*
|
||||
* @param Webkul\Product\Repositories\ProductRepository $product
|
||||
* @param Webkul\Product\Repositories\ProductReviewRepository $productReview
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Product $product, ProductReview $productReview)
|
||||
{
|
||||
$this->product = $product;
|
||||
|
||||
$this->productReview = $productReview;
|
||||
|
||||
$this->_config = request('_config');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param string $slug
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index($slug)
|
||||
{
|
||||
$product = $this->product->findBySlugOrFail($slug);
|
||||
|
||||
return view($this->_config['view'], compact('product'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
* @param string $slug
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function create($slug)
|
||||
{
|
||||
$product = $this->product->findBySlugOrFail($slug);
|
||||
|
||||
return view($this->_config['view'], compact('product'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$this->validate(request(), [
|
||||
'name' => 'required',
|
||||
]);
|
||||
|
||||
$this->productReview->create(request()->all());
|
||||
|
||||
session()->flash('success', 'Review submitted successfully.');
|
||||
|
||||
return redirect()->route($this->_config['redirect']);
|
||||
}
|
||||
}
|
||||
|
|
@ -8,11 +8,30 @@ Route::group(['middleware' => ['web']], function () {
|
|||
|
||||
Route::get('/categories/{slug}', 'Webkul\Shop\Http\Controllers\CategoryController@index')->defaults('_config', [
|
||||
'view' => 'shop::products.index'
|
||||
]);
|
||||
])->name('shop.categories.index');
|
||||
|
||||
Route::get('/products/{slug}', 'Webkul\Shop\Http\Controllers\ProductController@index')->defaults('_config', [
|
||||
'view' => 'shop::products.view'
|
||||
])->name('shop.products.index');
|
||||
|
||||
|
||||
// Product Review routes
|
||||
Route::get('/reviews/{slug}', 'Webkul\Shop\Http\Controllers\ReviewController@index')->defaults('_config', [
|
||||
'view' => 'shop::products.reviews.index'
|
||||
])->name('shop.reviews.index');
|
||||
|
||||
Route::get('/reviews/create/{slug}', 'Webkul\Shop\Http\Controllers\ReviewController@create')->defaults('_config', [
|
||||
'view' => 'shop::products.reviews.create'
|
||||
])->name('shop.reviews.create');
|
||||
|
||||
Route::post('/reviews/create/{slug}', 'Webkul\Core\Http\Controllers\ReviewController@store')->defaults('_config', [
|
||||
'redirect' => 'admin.reviews.index'
|
||||
])->name('admin.reviews.store');
|
||||
|
||||
|
||||
Route::view('/cart', 'shop::store.product.view.cart.index');
|
||||
|
||||
Route::view('/products/{slug}', 'shop::store.product.details.index');
|
||||
// Route::view('/products/{slug}', 'shop::products.view');
|
||||
|
||||
//customer routes starts here
|
||||
Route::prefix('customer')->group(function () {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
//shop variables
|
||||
$font-color: #242424;
|
||||
$font-size-base: 16px;
|
||||
$font-name: "Montserrat", sans-serif;
|
||||
$background-color: #f2f2f2;
|
||||
$footer-back: #f2f2f2;
|
||||
|
|
@ -23,6 +25,7 @@ $horizontal-rule-color: #E8E8E8;
|
|||
//product
|
||||
$real-price:#A5A5A5;
|
||||
$product-font-color: #242424;
|
||||
$product-special-price-color: #FF6472;
|
||||
$product-price-color: #FF6472;
|
||||
$dark-blue-shade: #0031F0;
|
||||
$bar-color: #D8D8D8;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ body {
|
|||
margin: 0;
|
||||
padding: 0;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
color: $font-color;
|
||||
font-size: $font-size-base;
|
||||
}
|
||||
|
||||
* {
|
||||
|
|
@ -196,7 +197,7 @@ body {
|
|||
|
||||
.nav a {
|
||||
display:block;
|
||||
color: #242424;
|
||||
color: $font-color;
|
||||
text-decoration: none;
|
||||
padding: 0.8em 0.3em 0.8em 0.5em;
|
||||
text-transform: uppercase;
|
||||
|
|
@ -356,13 +357,13 @@ section.slider-block {
|
|||
.layered-filter-wrapper {
|
||||
width: 25%;
|
||||
float: left;
|
||||
margin-right: 20px;
|
||||
padding-right: 20px;
|
||||
min-height: 1px;
|
||||
|
||||
.filter-title {
|
||||
border-bottom: 1px solid #E8E8E8;
|
||||
font-size: 16px;
|
||||
color: #242424;
|
||||
color: $font-color;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
|
|
@ -442,6 +443,7 @@ section.slider-block {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
.main-container-wrapper {
|
||||
margin-left: 10%;
|
||||
margin-right: 10%;
|
||||
|
|
@ -451,6 +453,11 @@ section.slider-block {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.main {
|
||||
display: inline-block;
|
||||
width: 75%
|
||||
}
|
||||
|
||||
.product-grid {
|
||||
display: grid;
|
||||
grid-gap: 30px;
|
||||
|
|
@ -471,58 +478,129 @@ section.slider-block {
|
|||
display: flex;
|
||||
flex-flow: column;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.product-image img {
|
||||
align-self: center;
|
||||
width: 100%;
|
||||
margin-bottom: 14px;
|
||||
.product-card {
|
||||
|
||||
.product-image img {
|
||||
align-self: center;
|
||||
width: 100%;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.product-name {
|
||||
font-size: 16px;
|
||||
margin-bottom: 14px;
|
||||
width: 100%;
|
||||
color: $font-color;
|
||||
|
||||
a {
|
||||
color: $font-color;
|
||||
}
|
||||
}
|
||||
|
||||
.product-description {
|
||||
font-size: 16px;
|
||||
margin-bottom: 14px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.product-ratings {
|
||||
width: 100%;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.cart-fav-seg {
|
||||
display: inline-flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
|
||||
.addtocart {
|
||||
border-radius: 0px;
|
||||
margin-right: 10px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.product-list {
|
||||
.product-card {
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.product-image {
|
||||
float: left;
|
||||
width: 30%;
|
||||
height: 350px;
|
||||
|
||||
img {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.product-name {
|
||||
.product-information {
|
||||
float: right;
|
||||
width: 70%;
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.top-toolbar {
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
margin-bottom: 25px;
|
||||
|
||||
.page-info {
|
||||
float: left;
|
||||
color: $font-color;
|
||||
line-height: 45px;
|
||||
}
|
||||
|
||||
.pager {
|
||||
float: right;
|
||||
|
||||
label {
|
||||
font-size: 16px;
|
||||
margin-bottom: 14px;
|
||||
width: 100%;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.product-price {
|
||||
select {
|
||||
background: #FFFFFF;
|
||||
border: 1px solid #C7C7C7;
|
||||
border-radius: 3px;
|
||||
font-size: 16px;
|
||||
margin-bottom: 14px;
|
||||
width: 100%;
|
||||
font-weight: 600;
|
||||
color: $font-color;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.price-label {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
.view-mode {
|
||||
display: inline-block;
|
||||
margin-right: 20px;
|
||||
|
||||
.regular-price {
|
||||
font-size: 16px;
|
||||
color: #A5A5A5;
|
||||
text-decoration: line-through;
|
||||
margin-right: 10px;
|
||||
}
|
||||
a, span {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
|
||||
.special-price {
|
||||
font-size: 16px;
|
||||
color: #FF6472;
|
||||
&.grid-view {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.product-ratings {
|
||||
width: 100%;
|
||||
margin-bottom: 14px;
|
||||
.sorter {
|
||||
display: inline-block;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.cart-fav-seg {
|
||||
display: inline-flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
|
||||
.addtocart {
|
||||
border-radius: 0px;
|
||||
margin-right: 10px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.limiter {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -602,6 +680,30 @@ section.slider-block {
|
|||
}
|
||||
}
|
||||
|
||||
.product-price {
|
||||
font-size: 16px;
|
||||
margin-bottom: 14px;
|
||||
width: 100%;
|
||||
font-weight: 600;
|
||||
|
||||
.price-label {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.regular-price {
|
||||
font-size: 16px;
|
||||
color: #A5A5A5;
|
||||
text-decoration: line-through;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.special-price {
|
||||
font-size: 16px;
|
||||
color: #FF6472;
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
background-color: $footer-back;
|
||||
padding-left: 10%;
|
||||
|
|
@ -805,7 +907,7 @@ section.slider-block {
|
|||
.section-head {
|
||||
.profile-heading {
|
||||
font-size: 28px;
|
||||
color: #242424;
|
||||
color: $font-color;
|
||||
text-transform: capitalize;
|
||||
text-align: left;
|
||||
}
|
||||
|
|
@ -912,18 +1014,8 @@ section.product-detail, section.product-review {
|
|||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.price {
|
||||
.product-price {
|
||||
margin-bottom: 14px;
|
||||
|
||||
.main-price {
|
||||
font-size: 24px;
|
||||
color: $product-price-color;
|
||||
}
|
||||
|
||||
.real-price {
|
||||
color: $real-price;
|
||||
text-decoration-line: line-through;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1059,12 +1151,12 @@ section.product-detail, section.product-review {
|
|||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.price {
|
||||
.product-price {
|
||||
margin-bottom: 14px;
|
||||
font-size: 24px;
|
||||
|
||||
.main-price {
|
||||
.special-price {
|
||||
font-size: 24px;
|
||||
color: $product-price-color;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1076,9 +1168,21 @@ section.product-detail, section.product-review {
|
|||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
hr{
|
||||
border-top: 1px solid $horizontal-rule-color;
|
||||
margin-bottom: 17px;
|
||||
.full-specifications {
|
||||
td {
|
||||
padding: 10px 0;
|
||||
color: #5E5E5E;
|
||||
|
||||
&:first-child {
|
||||
padding-right: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.accordian .accordian-header {
|
||||
font-size: 16px;
|
||||
padding-left: 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.attributes {
|
||||
|
|
@ -1160,21 +1264,21 @@ section.product-detail, section.product-review {
|
|||
font-size: 16px;
|
||||
}
|
||||
|
||||
.full-specification{
|
||||
|
||||
}
|
||||
|
||||
.rating-reviews {
|
||||
margin-top: 30px;
|
||||
|
||||
.title{
|
||||
.title {
|
||||
margin-bottom: 15px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.overall {
|
||||
margin-bottom: 5px;
|
||||
|
||||
.number{
|
||||
font-size: 34px;
|
||||
.review-info {
|
||||
.number {
|
||||
font-size: 34px;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
|
|
@ -1182,7 +1286,6 @@ section.product-detail, section.product-review {
|
|||
border-radius: 0px !important;
|
||||
}
|
||||
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.reviews {
|
||||
|
|
@ -1194,11 +1297,23 @@ section.product-detail, section.product-review {
|
|||
|
||||
.stars {
|
||||
margin-bottom: 15px;
|
||||
display: inline-block;
|
||||
|
||||
.icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.message {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.reviewer-details {
|
||||
color: #5E5E5E;
|
||||
}
|
||||
}
|
||||
|
||||
.view-all {
|
||||
margin-top:15px;
|
||||
color: $logo-color;
|
||||
|
|
@ -1207,33 +1322,7 @@ section.product-detail, section.product-review {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// .related-products-wrapper {
|
||||
// margin-bottom: 80px;
|
||||
|
||||
// .title{
|
||||
// margin-bottom: 22px;
|
||||
// text-align: center;
|
||||
// }
|
||||
|
||||
// .horizontal-rule {
|
||||
// height: 1px;
|
||||
// background: $horizontal-rule-color;
|
||||
// width: 148px;
|
||||
// margin-bottom: 24px;
|
||||
// margin-left:auto;
|
||||
// margin-right:auto;
|
||||
// }
|
||||
|
||||
// .related-products {
|
||||
// display: grid;
|
||||
// grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
// grid-gap: 10px;
|
||||
// }
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
/* cart pages and elements css begins here */
|
||||
|
|
@ -1462,14 +1551,25 @@ section.cart {
|
|||
|
||||
}
|
||||
|
||||
.related-products-wrapper {
|
||||
.attached-products-wrapper {
|
||||
margin-bottom: 80px;
|
||||
|
||||
.title{
|
||||
margin-bottom: 22px;
|
||||
.title {
|
||||
margin-bottom: 40px;
|
||||
font-size: 18px;
|
||||
color: $product-font-color;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
|
||||
.border-bottom {
|
||||
border-bottom: 1px solid rgba(162, 162, 162, 0.2);
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
left: 50%;
|
||||
margin-left: -50px;
|
||||
}
|
||||
}
|
||||
|
||||
.horizontal-rule {
|
||||
|
|
@ -1480,11 +1580,4 @@ section.cart {
|
|||
margin-left:auto;
|
||||
margin-right:auto;
|
||||
}
|
||||
|
||||
.related-products {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
grid-gap: 10px;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,23 @@
|
|||
display: inline-block;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.dropdown-right-icon{
|
||||
background-image:URL('../images/icon-dropdown-left.svg');
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
margin-left:auto;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.grid-view-icon {
|
||||
background-image:URL('../images/icon-grid-view.svg');
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.list-view-icon {
|
||||
background-image:URL('../images/icon-list-view.svg');
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
|
@ -35,6 +35,20 @@ return [
|
|||
'products' => [
|
||||
'layered-nav-title' => 'Shop By',
|
||||
'price-label' => 'As low as',
|
||||
'remove-filter-link-title' => 'Clear All'
|
||||
'remove-filter-link-title' => 'Clear All',
|
||||
'sort-by' => 'Sort By',
|
||||
'from-a-z' => 'From A-Z',
|
||||
'from-z-a' => 'From Z-A',
|
||||
'newest-first' => 'Newest First',
|
||||
'oldest-first' => 'Oldest First',
|
||||
'cheapest-first' => 'Cheapest First',
|
||||
'expansive-first' => 'Expansive First',
|
||||
'show' => 'Show',
|
||||
'pager-info' => 'Showing :showing of :total Items',
|
||||
'description' => 'Description',
|
||||
'specification' => 'Specification',
|
||||
'total-reviews' => ':total Reviews',
|
||||
'by' => 'By :name',
|
||||
'up-sell-title' => 'We found other products you might like!'
|
||||
]
|
||||
];
|
||||
|
|
@ -1 +1 @@
|
|||
<button class="btn btn-md btn-primary addtocart">Add to Cart</button>
|
||||
<button class="btn btn-lg btn-primary addtocart">Add to Cart</button>
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
<div class="product-card">
|
||||
<div class="product-image">
|
||||
<img src="{{ bagisto_asset('images/gogs.png') }}" />
|
||||
</div>
|
||||
|
||||
<div class="product-name">
|
||||
<span>{{ $product->name }}</span>
|
||||
</div>
|
||||
|
||||
@include ('shop::products.price', ['product' => $product])
|
||||
|
||||
@if ($product->reviews->count())
|
||||
|
||||
@include ('shop::products.review', ['product' => $product])
|
||||
|
||||
@endif
|
||||
|
||||
@include ('shop::products.add-to', ['product' => $product])
|
||||
|
||||
</div>
|
||||
|
|
@ -2,24 +2,40 @@
|
|||
|
||||
@section('content-wrapper')
|
||||
|
||||
@include ('shop::products.layered-navigation')
|
||||
@include ('shop::products.list.layered-navigation')
|
||||
|
||||
<div class="main" style="display: inline-block">
|
||||
|
||||
<div class="product-grid max-3-col">
|
||||
@inject ('productRepository', 'Webkul\Product\Repositories\ProductRepository')
|
||||
|
||||
@inject ('productHelper', 'Webkul\Product\Product\Collection')
|
||||
|
||||
<?php $products = $productHelper->getCollection($category->id); ?>
|
||||
|
||||
@foreach ($products as $product)
|
||||
<?php $products = $productRepository->findAllByCategory($category->id); ?>
|
||||
|
||||
@include ('shop::products.card', ['product' => $product])
|
||||
@include ('shop::products.list.toolbar')
|
||||
|
||||
@endforeach
|
||||
@inject ('toolbarHelper', 'Webkul\Product\Product\Toolbar')
|
||||
|
||||
</div>
|
||||
@if ($toolbarHelper->getCurrentMode() == 'grid')
|
||||
<div class="product-grid max-3-col">
|
||||
|
||||
@foreach ($products as $product)
|
||||
|
||||
@include ('shop::products.list.card', ['product' => $product])
|
||||
|
||||
@endforeach
|
||||
|
||||
</div>
|
||||
@else
|
||||
<div class="product-list">
|
||||
|
||||
@foreach ($products as $product)
|
||||
|
||||
@include ('shop::products.list.card', ['product' => $product])
|
||||
|
||||
@endforeach
|
||||
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="bottom-toolbar">
|
||||
|
||||
{{ $products->appends(request()->input())->links() }}
|
||||
|
|
@ -28,8 +44,4 @@
|
|||
|
||||
</div>
|
||||
|
||||
@stop
|
||||
|
||||
@push('scripts')
|
||||
|
||||
@endpush
|
||||
@stop
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<div class="product-card">
|
||||
<div class="product-image">
|
||||
<a href="{{ route('shop.products.index', $product->url_key) }}" title="{{ $product->name }}">
|
||||
<img src="{{ bagisto_asset('images/gogs.png') }}" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="product-information">
|
||||
|
||||
<div class="product-name">
|
||||
|
||||
{{ $product->id }}
|
||||
|
||||
<a href="" title="{{ $product->name }}">
|
||||
<span>{{ $product->name }}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="product-description">
|
||||
{{ $product->short_description }}
|
||||
</div>
|
||||
|
||||
@include ('shop::products.price', ['product' => $product])
|
||||
|
||||
@if ($product->reviews->count())
|
||||
|
||||
@include ('shop::products.review', ['product' => $product])
|
||||
|
||||
@endif
|
||||
|
||||
@include ('shop::products.add-to', ['product' => $product])
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
@inject ('attributeRepository', 'Webkul\Attribute\Repositories\AttributeRepository')
|
||||
|
||||
<div class="layered-filter-wrapper">
|
||||
|
||||
<layered-navigation></layered-navigation>
|
||||
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
|
|
@ -129,8 +131,8 @@
|
|||
|
||||
sliderConfig: {
|
||||
value: [
|
||||
100,
|
||||
250
|
||||
0,
|
||||
0
|
||||
],
|
||||
max: 500,
|
||||
processStyle: {
|
||||
|
|
@ -171,7 +173,7 @@
|
|||
|
||||
clearFilters () {
|
||||
if(this.attribute.type == 'price') {
|
||||
this.sliderConfig.value = [100, 250];
|
||||
this.sliderConfig.value = [0, 0];
|
||||
}
|
||||
|
||||
this.appliedFilters = [];
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
@inject ('toolbarHelper', 'Webkul\Product\Product\Toolbar')
|
||||
|
||||
<div class="top-toolbar">
|
||||
|
||||
<div class="page-info">
|
||||
{{ __('shop::app.products.pager-info', ['showing' => $products->firstItem() . '-' . $products->lastItem(), 'total' => $products->total()]) }}
|
||||
</div>
|
||||
|
||||
<div class="pager">
|
||||
|
||||
<div class="view-mode">
|
||||
@if ($toolbarHelper->isModeActive('grid'))
|
||||
<span class="grid-view">
|
||||
<i class="icon grid-view-icon"></i>
|
||||
</span>
|
||||
@else
|
||||
<a href="{{ $toolbarHelper->getModeUrl('grid') }}" class="grid-view">
|
||||
<i class="icon grid-view-icon"></i>
|
||||
</a>
|
||||
@endif
|
||||
|
||||
@if ($toolbarHelper->isModeActive('list'))
|
||||
<span class="list-view">
|
||||
<i class="icon list-view-icon"></i>
|
||||
</span>
|
||||
@else
|
||||
<a href="{{ $toolbarHelper->getModeUrl('list') }}" class="list-view">
|
||||
<i class="icon list-view-icon"></i>
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="sorter">
|
||||
<label>{{ __('shop::app.products.sort-by') }}</label>
|
||||
|
||||
<select onchange="window.location.href = this.value">
|
||||
|
||||
@foreach ($toolbarHelper->getAvailableOrders() as $key => $order)
|
||||
|
||||
<option value="{{ $toolbarHelper->getOrderUrl($key) }}" {{ $toolbarHelper->isOrderCurrent($key) ? 'selected' : '' }}>
|
||||
{{ __('shop::app.products.' . $order) }}
|
||||
</option>
|
||||
|
||||
@endforeach
|
||||
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="limiter">
|
||||
<label>{{ __('shop::app.products.show') }}</label>
|
||||
|
||||
<select onchange="window.location.href = this.value">
|
||||
|
||||
@foreach ($toolbarHelper->getAvailableLimits() as $limit)
|
||||
|
||||
<option value="{{ $toolbarHelper->getLimitUrl($limit) }}" {{ $toolbarHelper->isLimitCurrent($limit) ? 'selected' : '' }}>
|
||||
{{ $limit }}
|
||||
</option>
|
||||
|
||||
@endforeach
|
||||
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
@extends('shop::layouts.master')
|
||||
|
||||
@section('content-wrapper')
|
||||
<section class="product-detail">
|
||||
<div class="category-breadcrumbs">
|
||||
|
||||
<span class="breadcrumb">Home</span> > <span class="breadcrumb">Men</span> > <span class="breadcrumb">Slit Open Jeans</span>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="layouter">
|
||||
|
||||
@include ('shop::products.view.gallery')
|
||||
|
||||
<div class="details">
|
||||
|
||||
<div class="product-heading">
|
||||
<span>{{ $product->name }}</span>
|
||||
</div>
|
||||
|
||||
<div class="rating">
|
||||
<img src="{{ bagisto_asset('images/5star.svg') }}" />
|
||||
75 Ratings & 11 Reviews
|
||||
</div>
|
||||
|
||||
@include ('shop::products.price', ['product' => $product])
|
||||
|
||||
@include ('shop::products.view.stock')
|
||||
|
||||
<br/>
|
||||
|
||||
<div class="description">
|
||||
{{ $product->short_description }}
|
||||
</div>
|
||||
|
||||
@if ($product->type == 'configurable')
|
||||
|
||||
@include ('shop::products.view.configurable-options')
|
||||
|
||||
@endif
|
||||
|
||||
<accordian :title="{{ __('shop::app.products.description') }}" :active="true">
|
||||
<div slot="header">
|
||||
{{ __('shop::app.products.description') }}
|
||||
<i class="icon expand-icon right"></i>
|
||||
</div>
|
||||
|
||||
<div slot="body">
|
||||
<div class="full-description">
|
||||
{{ $product->description }}
|
||||
</div>
|
||||
</div>
|
||||
</accordian>
|
||||
|
||||
@include ('shop::products.view.attributes')
|
||||
|
||||
@include ('shop::products.view.reviews')
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@include ('shop::products.view.up-sells')
|
||||
|
||||
</section>
|
||||
@endsection
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
@inject ('productViewHelper', 'Webkul\Product\Product\View')
|
||||
|
||||
<accordian :title="{{ __('shop::app.products.specification') }}" :active="false">
|
||||
<div slot="header">
|
||||
{{ __('shop::app.products.specification') }}
|
||||
<i class="icon expand-icon right"></i>
|
||||
</div>
|
||||
|
||||
<div slot="body">
|
||||
<table class="full-specifications">
|
||||
|
||||
@foreach ($productViewHelper->getAdditionalData($product) as $attribute)
|
||||
|
||||
<tr>
|
||||
<td>{{ $attribute['label'] }}</td>
|
||||
<td> - {{ $attribute['value'] }}</td>
|
||||
</tr>
|
||||
|
||||
@endforeach
|
||||
|
||||
</table>
|
||||
</div>
|
||||
</accordian>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<div class="attributes">
|
||||
|
||||
<div class="attribute color">
|
||||
<div class="title">Color</div>
|
||||
|
||||
<div class="values">
|
||||
<div class="colors red"></div>
|
||||
<div class="colors blue"></div>
|
||||
<div class="colors green"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="attribute size">
|
||||
<div class="title">Size</div>
|
||||
|
||||
<div class="values">
|
||||
<div class="size xl">XL</div>
|
||||
<div class="size xxl">XXL</div>
|
||||
<div class="size xxxl">XXXL</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="attribute quantity">
|
||||
<div class="title">Quantity</div>
|
||||
|
||||
<div class="values">
|
||||
<div class="size">1</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<div class="product-image-group">
|
||||
|
||||
<div class="side-group">
|
||||
<img src="{{ bagisto_asset('images/jeans.jpg') }}" />
|
||||
<img src="{{ bagisto_asset('images/jeans.jpg') }}" />
|
||||
<img src="{{ bagisto_asset('images/jeans.jpg') }}" />
|
||||
<img src="{{ bagisto_asset('images/jeans.jpg') }}" />
|
||||
</div>
|
||||
|
||||
<div class="product-hero-image">
|
||||
<img src="{{ bagisto_asset('images/jeans_big.jpg') }}" />
|
||||
<img class="wishlist" src="{{ bagisto_asset('images/wish.svg') }}" />
|
||||
<img class="share" src="{{ bagisto_asset('images/icon-share.svg') }}" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
@inject ('reviewHelper', 'Webkul\Product\Product\Review')
|
||||
|
||||
@if ($total = $reviewHelper->getTotalReviews($product))
|
||||
<div class="rating-reviews">
|
||||
<div class="title">
|
||||
Ratings & Reviews
|
||||
</div>
|
||||
|
||||
<div class="overall">
|
||||
<div class="review-info">
|
||||
|
||||
<span class="number">
|
||||
{{ $reviewHelper->getAverageRating($product) }}
|
||||
</span>
|
||||
|
||||
<span class="stars">
|
||||
@for ($i = 1; $i <= $reviewHelper->getAverageRating($product); $i++)
|
||||
|
||||
<span class="icon star-icon"></span>
|
||||
|
||||
@endfor
|
||||
</span>
|
||||
|
||||
<div class="total-reviews">
|
||||
{{ __('shop::app.products.total-reviews', ['total' => $total]) }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<a href="{{ route('shop.reviews.create', $product->url_key) }}" class="btn btn-lg btn-primary">Write Review</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="reviews">
|
||||
|
||||
@foreach ($product->reviews()->paginate(5) as $review)
|
||||
<div class="review">
|
||||
<div class="title">
|
||||
{{ $review->title }}
|
||||
</div>
|
||||
|
||||
<span class="stars">
|
||||
@for ($i = 1; $i <= $review->rating; $i++)
|
||||
|
||||
<span class="icon star-icon"></span>
|
||||
|
||||
@endfor
|
||||
</span>
|
||||
|
||||
<div class="message">
|
||||
{{ $review->comment }}
|
||||
</div>
|
||||
|
||||
<div class="reviewer-details">
|
||||
<span class="by">
|
||||
{{ __('shop::app.products.by', ['name' => $review->customer->name]) }},
|
||||
</span>
|
||||
|
||||
<span class="when">
|
||||
{{ $reviewHelper->formatDate($review->created_at) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
<a href="{{ route('shop.reviews.index', $product->url_key) }}" class="view-all">View All</a>
|
||||
|
||||
<hr/>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<div class="stock-status">
|
||||
InStock
|
||||
</div>
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
@if ($product->up_sells()->count())
|
||||
<div class="attached-products-wrapper">
|
||||
|
||||
<div class="title">
|
||||
{{ __('shop::app.products.up-sell-title') }}
|
||||
<span class="border-bottom"></span>
|
||||
</div>
|
||||
|
||||
<div class="product-grid max-4-col">
|
||||
|
||||
@foreach ($product->up_sells()->paginate(4) as $up_sell_product)
|
||||
|
||||
@include ('shop::products.list.card', ['product' => $up_sell_product])
|
||||
|
||||
@endforeach
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@endif
|
||||
|
|
@ -53,9 +53,13 @@ class AccountController extends Controller
|
|||
'email' => 'email|unique:admins,email,' . $user->id,
|
||||
'password' => 'nullable|confirmed'
|
||||
]);
|
||||
|
||||
|
||||
$user->update(request(['name', 'email', 'password']));
|
||||
$data = request()->all();
|
||||
|
||||
if(!$data['password'])
|
||||
unset($data['password']);
|
||||
|
||||
$user->update($data);
|
||||
|
||||
session()->flash('success', 'Account changes saved successfully.');
|
||||
|
||||
|
|
|
|||
|
|
@ -116,7 +116,12 @@ class UserController extends Controller
|
|||
*/
|
||||
public function update(UserForm $request, $id)
|
||||
{
|
||||
$this->admin->update(request()->all(), $id);
|
||||
$data = request()->all();
|
||||
|
||||
if(!$data['password'])
|
||||
unset($data['password']);
|
||||
|
||||
$this->admin->update($data, $id);
|
||||
|
||||
session()->flash('success', 'User updated successfully.');
|
||||
|
||||
|
|
|
|||
|
|
@ -11,11 +11,24 @@
|
|||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.grid-view-icon {
|
||||
background-image: URL("../images/icon-grid-view.svg");
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.list-view-icon {
|
||||
background-image: URL("../images/icon-list-view.svg");
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
color: #242424;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
* {
|
||||
|
|
@ -430,7 +443,7 @@ section.slider-block div.slider-content div.slider-control .light-right-icon {
|
|||
.layered-filter-wrapper {
|
||||
width: 25%;
|
||||
float: left;
|
||||
margin-right: 20px;
|
||||
padding-right: 20px;
|
||||
min-height: 1px;
|
||||
}
|
||||
|
||||
|
|
@ -522,6 +535,11 @@ section.slider-block div.slider-content div.slider-control .light-right-icon {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.main-container-wrapper .main {
|
||||
display: inline-block;
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
.main-container-wrapper .product-grid {
|
||||
display: grid;
|
||||
grid-gap: 30px;
|
||||
|
|
@ -552,49 +570,36 @@ section.slider-block div.slider-content div.slider-control .light-right-icon {
|
|||
justify-content: center;
|
||||
}
|
||||
|
||||
.main-container-wrapper .product-grid .product-card .product-image img {
|
||||
.main-container-wrapper .product-card .product-image img {
|
||||
-ms-flex-item-align: center;
|
||||
align-self: center;
|
||||
width: 100%;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.main-container-wrapper .product-grid .product-card .product-name {
|
||||
.main-container-wrapper .product-card .product-name {
|
||||
font-size: 16px;
|
||||
margin-bottom: 14px;
|
||||
width: 100%;
|
||||
color: #242424;
|
||||
}
|
||||
|
||||
.main-container-wrapper .product-grid .product-card .product-price {
|
||||
.main-container-wrapper .product-card .product-name a {
|
||||
color: #242424;
|
||||
}
|
||||
|
||||
.main-container-wrapper .product-card .product-description {
|
||||
font-size: 16px;
|
||||
margin-bottom: 14px;
|
||||
width: 100%;
|
||||
font-weight: 600;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.main-container-wrapper .product-grid .product-card .product-price .price-label {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.main-container-wrapper .product-grid .product-card .product-price .regular-price {
|
||||
font-size: 16px;
|
||||
color: #A5A5A5;
|
||||
text-decoration: line-through;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.main-container-wrapper .product-grid .product-card .product-price .special-price {
|
||||
font-size: 16px;
|
||||
color: #FF6472;
|
||||
}
|
||||
|
||||
.main-container-wrapper .product-grid .product-card .product-ratings {
|
||||
.main-container-wrapper .product-card .product-ratings {
|
||||
width: 100%;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.main-container-wrapper .product-grid .product-card .cart-fav-seg {
|
||||
.main-container-wrapper .product-card .cart-fav-seg {
|
||||
display: -webkit-inline-box;
|
||||
display: -ms-inline-flexbox;
|
||||
display: inline-flex;
|
||||
|
|
@ -604,12 +609,91 @@ section.slider-block div.slider-content div.slider-control .light-right-icon {
|
|||
align-items: center;
|
||||
}
|
||||
|
||||
.main-container-wrapper .product-grid .product-card .cart-fav-seg .addtocart {
|
||||
.main-container-wrapper .product-card .cart-fav-seg .addtocart {
|
||||
border-radius: 0px;
|
||||
margin-right: 10px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.main-container-wrapper .product-list .product-card {
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.main-container-wrapper .product-list .product-card .product-image {
|
||||
float: left;
|
||||
width: 30%;
|
||||
height: 350px;
|
||||
}
|
||||
|
||||
.main-container-wrapper .product-list .product-card .product-image img {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.main-container-wrapper .product-list .product-card .product-information {
|
||||
float: right;
|
||||
width: 70%;
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
.main-container-wrapper .product-list .product-card:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.main-container-wrapper .top-toolbar {
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.main-container-wrapper .top-toolbar .page-info {
|
||||
float: left;
|
||||
color: #242424;
|
||||
line-height: 45px;
|
||||
}
|
||||
|
||||
.main-container-wrapper .top-toolbar .pager {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.main-container-wrapper .top-toolbar .pager label {
|
||||
font-size: 16px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.main-container-wrapper .top-toolbar .pager select {
|
||||
background: #FFFFFF;
|
||||
border: 1px solid #C7C7C7;
|
||||
border-radius: 3px;
|
||||
font-size: 16px;
|
||||
color: #242424;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.main-container-wrapper .top-toolbar .pager .view-mode {
|
||||
display: inline-block;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.main-container-wrapper .top-toolbar .pager .view-mode a, .main-container-wrapper .top-toolbar .pager .view-mode span {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.main-container-wrapper .top-toolbar .pager .view-mode a.grid-view, .main-container-wrapper .top-toolbar .pager .view-mode span.grid-view {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.main-container-wrapper .top-toolbar .pager .sorter {
|
||||
display: inline-block;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.main-container-wrapper .top-toolbar .pager .limiter {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.main-container-wrapper .bottom-toolbar {
|
||||
display: block;
|
||||
margin-top: 40px;
|
||||
|
|
@ -691,6 +775,30 @@ section.slider-block div.slider-content div.slider-control .light-right-icon {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.product-price {
|
||||
font-size: 16px;
|
||||
margin-bottom: 14px;
|
||||
width: 100%;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.product-price .price-label {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.product-price .regular-price {
|
||||
font-size: 16px;
|
||||
color: #A5A5A5;
|
||||
text-decoration: line-through;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.product-price .special-price {
|
||||
font-size: 16px;
|
||||
color: #FF6472;
|
||||
}
|
||||
|
||||
.footer {
|
||||
background-color: #f2f2f2;
|
||||
padding-left: 10%;
|
||||
|
|
@ -1013,21 +1121,10 @@ section.product-detail div.layouter .mixed-group .details .product-name, section
|
|||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
section.product-detail div.layouter .mixed-group .details .price, section.product-review div.layouter .mixed-group .details .price {
|
||||
section.product-detail div.layouter .mixed-group .details .product-price, section.product-review div.layouter .mixed-group .details .product-price {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
section.product-detail div.layouter .mixed-group .details .price .main-price, section.product-review div.layouter .mixed-group .details .price .main-price {
|
||||
font-size: 24px;
|
||||
color: #FF6472;
|
||||
}
|
||||
|
||||
section.product-detail div.layouter .mixed-group .details .price .real-price, section.product-review div.layouter .mixed-group .details .price .real-price {
|
||||
color: #A5A5A5;
|
||||
-webkit-text-decoration-line: line-through;
|
||||
text-decoration-line: line-through;
|
||||
}
|
||||
|
||||
section.product-detail div.layouter .rating-reviews, section.product-review div.layouter .rating-reviews {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
|
@ -1189,13 +1286,13 @@ section.product-detail div.layouter .details .rating, section.product-review div
|
|||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
section.product-detail div.layouter .details .price, section.product-review div.layouter .details .price {
|
||||
section.product-detail div.layouter .details .product-price, section.product-review div.layouter .details .product-price {
|
||||
margin-bottom: 14px;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
section.product-detail div.layouter .details .price .main-price, section.product-review div.layouter .details .price .main-price {
|
||||
section.product-detail div.layouter .details .product-price .special-price, section.product-review div.layouter .details .product-price .special-price {
|
||||
font-size: 24px;
|
||||
color: #FF6472;
|
||||
}
|
||||
|
||||
section.product-detail div.layouter .details .stock-status, section.product-review div.layouter .details .stock-status {
|
||||
|
|
@ -1206,9 +1303,19 @@ section.product-detail div.layouter .details .description, section.product-revie
|
|||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
section.product-detail div.layouter .details hr, section.product-review div.layouter .details hr {
|
||||
border-top: 1px solid #E8E8E8;
|
||||
margin-bottom: 17px;
|
||||
section.product-detail div.layouter .details .full-specifications td, section.product-review div.layouter .details .full-specifications td {
|
||||
padding: 10px 0;
|
||||
color: #5E5E5E;
|
||||
}
|
||||
|
||||
section.product-detail div.layouter .details .full-specifications td:first-child, section.product-review div.layouter .details .full-specifications td:first-child {
|
||||
padding-right: 40px;
|
||||
}
|
||||
|
||||
section.product-detail div.layouter .details .accordian .accordian-header, section.product-review div.layouter .details .accordian .accordian-header {
|
||||
font-size: 16px;
|
||||
padding-left: 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
section.product-detail div.layouter .details .attributes, section.product-review div.layouter .details .attributes {
|
||||
|
|
@ -1296,13 +1403,14 @@ section.product-detail div.layouter .details .rating-reviews, section.product-re
|
|||
|
||||
section.product-detail div.layouter .details .rating-reviews .title, section.product-review div.layouter .details .rating-reviews .title {
|
||||
margin-bottom: 15px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
section.product-detail div.layouter .details .rating-reviews .overall, section.product-review div.layouter .details .rating-reviews .overall {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
section.product-detail div.layouter .details .rating-reviews .overall .number, section.product-review div.layouter .details .rating-reviews .overall .number {
|
||||
section.product-detail div.layouter .details .rating-reviews .overall .review-info .number, section.product-review div.layouter .details .rating-reviews .overall .review-info .number {
|
||||
font-size: 34px;
|
||||
}
|
||||
|
||||
|
|
@ -1322,12 +1430,22 @@ section.product-detail div.layouter .details .rating-reviews .reviews .review, s
|
|||
|
||||
section.product-detail div.layouter .details .rating-reviews .reviews .review .stars, section.product-review div.layouter .details .rating-reviews .reviews .review .stars {
|
||||
margin-bottom: 15px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
section.product-detail div.layouter .details .rating-reviews .reviews .review .stars .icon, section.product-review div.layouter .details .rating-reviews .reviews .review .stars .icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
section.product-detail div.layouter .details .rating-reviews .reviews .review .message, section.product-review div.layouter .details .rating-reviews .reviews .review .message {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
section.product-detail div.layouter .details .rating-reviews .reviews .review .reviewer-details, section.product-review div.layouter .details .rating-reviews .reviews .review .reviewer-details {
|
||||
color: #5E5E5E;
|
||||
}
|
||||
|
||||
section.product-detail div.layouter .details .rating-reviews .reviews .view-all, section.product-review div.layouter .details .rating-reviews .reviews .view-all {
|
||||
margin-top: 15px;
|
||||
color: #0031f0;
|
||||
|
|
@ -1567,18 +1685,29 @@ section.cart .cart-content .right-side .coupon-section .after-coupon-amount .amo
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
.related-products-wrapper {
|
||||
.attached-products-wrapper {
|
||||
margin-bottom: 80px;
|
||||
}
|
||||
|
||||
.related-products-wrapper .title {
|
||||
margin-bottom: 22px;
|
||||
.attached-products-wrapper .title {
|
||||
margin-bottom: 40px;
|
||||
font-size: 18px;
|
||||
color: #242424;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.related-products-wrapper .horizontal-rule {
|
||||
.attached-products-wrapper .title .border-bottom {
|
||||
border-bottom: 1px solid rgba(162, 162, 162, 0.2);
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
left: 50%;
|
||||
margin-left: -50px;
|
||||
}
|
||||
|
||||
.attached-products-wrapper .horizontal-rule {
|
||||
height: 1px;
|
||||
background: #E8E8E8;
|
||||
width: 148px;
|
||||
|
|
@ -1586,9 +1715,3 @@ section.cart .cart-content .right-side .coupon-section .after-coupon-amount .amo
|
|||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.related-products-wrapper .related-products {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
grid-gap: 10px;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue