merge bagisto master, resolve conflicts

This commit is contained in:
Steffen Mahler 2020-06-17 08:29:55 +02:00
commit 3de5601df0
114 changed files with 3112 additions and 1120 deletions

3
.gitignore vendored
View File

@ -1,5 +1,5 @@
/docker-compose-collection
/bin
/storage/dcc-data/
/node_modules
/public/hot
/public/storage
@ -22,7 +22,6 @@ yarn.lock
package-lock.json
yarn.lock
.php_cs.cache
storage/
storage/*.key
/docker-compose-collection/
/resources/themes/velocity/*

View File

@ -2,7 +2,7 @@
namespace App\Exceptions;
use Exception;
use Throwable;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
class Handler extends ExceptionHandler
@ -29,10 +29,10 @@ class Handler extends ExceptionHandler
/**
* Report or log an exception.
*
* @param \Exception $exception
* @param \Throwable $exception
* @return void
*/
public function report(Exception $exception)
public function report(Throwable $exception)
{
parent::report($exception);
}
@ -41,10 +41,10 @@ class Handler extends ExceptionHandler
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $exception
* @param \Throwable $exception
* @return \Illuminate\Http\Response
*/
public function render($request, Exception $exception)
public function render($request, Throwable $exception)
{
return parent::render($request, $exception);
}

View File

@ -12,6 +12,6 @@ class VerifyCsrfToken extends Middleware
* @var array
*/
protected $except = [
//
'paypal/standard/ipn'
];
}

View File

@ -9,7 +9,7 @@
"license": "MIT",
"type": "project",
"require": {
"php": "^7.2.0",
"php": "^7.2.5",
"ext-curl": "*",
"ext-intl": "*",
"ext-mbstring": "*",
@ -18,23 +18,23 @@
"ext-pdo_mysql": "*",
"ext-tokenizer": "*",
"astrotomic/laravel-translatable": "^11.0.0",
"barryvdh/laravel-dompdf": "0.8.5",
"barryvdh/laravel-debugbar": "^3.1",
"barryvdh/laravel-dompdf": "0.8.6",
"doctrine/dbal": "2.9.2",
"fideloper/proxy": "^4.0",
"fideloper/proxy": "^4.2",
"flynsarmy/db-blade-compiler": "^5.5",
"guzzlehttp/guzzle": "~6.0",
"fzaninotto/faker": "^1.9.1",
"guzzlehttp/guzzle": "~6.3",
"intervention/image": "^2.4",
"intervention/imagecache": "^2.3",
"kalnoy/nestedset": "5.0.0",
"kalnoy/nestedset": "5.0.1",
"konekt/concord": "^1.2",
"laravel/framework": "^6.0",
"laravel/helpers": "^1.1",
"laravel/tinker": "^1.0",
"maatwebsite/excel": "3.1.18",
"laravel/framework": "^7.0",
"laravel/tinker": "^2.0",
"laravel/ui": "^2.0",
"maatwebsite/excel": "3.1.19",
"prettus/l5-repository": "^2.6",
"tymon/jwt-auth": "^1.0.0",
"barryvdh/laravel-debugbar": "^3.1",
"fzaninotto/faker": "^1.4"
"tymon/jwt-auth": "^1.0.0"
},
"require-dev": {
@ -46,9 +46,9 @@
"codeception/module-webdriver": "^1.0",
"filp/whoops": "^2.0",
"fzaninotto/faker": "^1.4",
"mockery/mockery": "^1.0",
"nunomaduro/collision": "^2.0",
"phpunit/phpunit": "^7.0"
"mockery/mockery": "^1.3.1",
"nunomaduro/collision": "^4.1",
"phpunit/phpunit": "^8.5"
},
"replace": {

2261
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -164,7 +164,7 @@ return [
|
*/
'secure' => env('SESSION_SECURE_COOKIE', false),
'secure' => env('SESSION_SECURE_COOKIE', null),
/*
|--------------------------------------------------------------------------

View File

@ -2,7 +2,10 @@
namespace Webkul\API\Http\Controllers\Shop;
use Exception;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Log;
use Webkul\Checkout\Repositories\CartRepository;
use Webkul\Checkout\Repositories\CartItemRepository;
use Webkul\API\Http\Resources\Checkout\Cart as CartResource;
@ -42,9 +45,9 @@ class CartController extends Controller
/**
* Controller instance
*
* @param \Webkul\Checkout\Repositories\CartRepository $cartRepository
* @param \Webkul\Checkout\Repositories\CartItemRepository $cartItemRepository
* @param \Webkul\Checkout\Repositories\WishlistRepository $wishlistRepository
* @param \Webkul\Checkout\Repositories\CartRepository $cartRepository
* @param \Webkul\Checkout\Repositories\CartItemRepository $cartItemRepository
* @param \Webkul\Checkout\Repositories\WishlistRepository $wishlistRepository
*/
public function __construct(
CartRepository $cartRepository,
@ -69,7 +72,7 @@ class CartController extends Controller
/**
* Get customer cart
*
* @return \Illuminate\Http\Response
* @return \Illuminate\Http\JsonResponse
*/
public function get()
{
@ -86,10 +89,11 @@ class CartController extends Controller
/**
* Store a newly created resource in storage.
*
* @param int $id
* @return \Illuminate\Http\Response
* @param int $id
*
* @return \Illuminate\Http\JsonResponse
*/
public function store($id)
public function store($id): ?JsonResponse
{
if (request()->get('is_buy_now')) {
Event::dispatch('shop.item.buy-now', $id);
@ -97,36 +101,46 @@ class CartController extends Controller
Event::dispatch('checkout.cart.item.add.before', $id);
$result = Cart::addProduct($id, request()->except('_token'));
try {
$result = Cart::addProduct($id, request()->except('_token'));
if (! $result) {
$message = session()->get('warning') ?? session()->get('error');
if (is_array($result) && isset($result['warning'])) {
return response()->json([
'error' => $result['warning'],
], 400);
}
if ($customer = auth($this->guard)->user()) {
$this->wishlistRepository->deleteWhere(['product_id' => $id, 'customer_id' => $customer->id]);
}
Event::dispatch('checkout.cart.item.add.after', $result);
Cart::collectTotals();
$cart = Cart::getCart();
return response()->json([
'error' => session()->get('warning'),
], 400);
'message' => __('shop::app.checkout.cart.item.success'),
'data' => $cart ? new CartResource($cart) : null,
]);
} catch (Exception $e) {
Log::error('API CartController: ' . $e->getMessage(),
['product_id' => $id, 'cart_id' => cart()->getCart() ?? 0]);
return response()->json([
'error' => [
'message' => $e->getMessage(),
'code' => $e->getCode()
]
]);
}
if ($customer = auth($this->guard)->user()) {
$this->wishlistRepository->deleteWhere(['product_id' => $id, 'customer_id' => $customer->id]);
}
Event::dispatch('checkout.cart.item.add.after', $result);
Cart::collectTotals();
$cart = Cart::getCart();
return response()->json([
'message' => __('shop::app.checkout.cart.item.success'),
'data' => $cart ? new CartResource($cart) : null,
]);
}
/**
* Update the specified resource in storage.
*
* @return \Illuminate\Http\Response
* @return \Illuminate\Http\JsonResponse
*/
public function update()
{
@ -161,7 +175,7 @@ class CartController extends Controller
/**
* Remove the specified resource from storage.
*
* @return \Illuminate\Http\Response
* @return \Illuminate\Http\JsonResponse
*/
public function destroy()
{
@ -182,8 +196,9 @@ class CartController extends Controller
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
* @param int $id
*
* @return \Illuminate\Http\JsonResponse
*/
public function destroyItem($id)
{
@ -206,7 +221,9 @@ class CartController extends Controller
/**
* Function to move a already added product to wishlist will run only on customer authentication.
*
* @param \Webkul\Checkout\Repositories\CartItemRepository $id
* @param \Webkul\Checkout\Repositories\CartItemRepository $id
*
* @return \Illuminate\Http\JsonResponse
*/
public function moveToWishlist($id)
{

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
{
"/js/admin.js": "/js/admin.js?id=2701b1627d73faa941d6",
"/css/admin.css": "/css/admin.css?id=f22618adc68fda9b42c0"
"/js/admin.js": "/js/admin.js?id=a14c779523092ac32b88",
"/css/admin.css": "/css/admin.css?id=5002aa6219af1ce7629b"
}

View File

@ -53,13 +53,13 @@ return [
'key' => 'catalog.categories',
'name' => 'admin::app.layouts.categories',
'route' => 'admin.catalog.categories.index',
'sort' => 3,
'sort' => 2,
'icon-class' => '',
], [
'key' => 'catalog.attributes',
'name' => 'admin::app.layouts.attributes',
'route' => 'admin.catalog.attributes.index',
'sort' => 2,
'sort' => 3,
'icon-class' => '',
], [
'key' => 'catalog.families',

View File

@ -59,13 +59,12 @@ class AddressDataGrid extends DataGrid
$queryBuilder->groupBy('ca.id')
->addSelect(DB::raw(DB::getTablePrefix() . 'country_states.default_name as state_name'));
$this->addFilter('address_id', 'ca.id');
$this->addFilter('company_name', 'ca.company_name');
$this->addFilter('address1', 'ca.address1');
$this->addFilter('postcode', 'ca.postcode');
$this->addFilter('city', 'ca.city');
$this->addFilter('state_name', DB::raw(DB::getTablePrefix() . 'country_states.default_name'));
$this->addFilter('country_name', DB::raw(DB::getTablePrefix() . 'countries.name'));
$this->addFilter('postcode', 'ca.postcode');
$this->addFilter('default_address', 'ca.default_address');
$this->setQueryBuilder($queryBuilder);
@ -73,15 +72,6 @@ class AddressDataGrid extends DataGrid
public function addColumns()
{
$this->addColumn([
'index' => 'address_id',
'label' => trans('admin::app.customers.addresses.address-id'),
'type' => 'number',
'searchable' => true,
'sortable' => true,
'filterable' => true,
]);
$this->addColumn([
'index' => 'company_name',
'label' => trans('admin::app.customers.addresses.company-name'),
@ -100,6 +90,15 @@ class AddressDataGrid extends DataGrid
'filterable' => true,
]);
$this->addColumn([
'index' => 'postcode',
'label' => trans('admin::app.customers.addresses.postcode'),
'type' => 'string',
'searchable' => true,
'sortable' => true,
'filterable' => true,
]);
$this->addColumn([
'index' => 'city',
'label' => trans('admin::app.customers.addresses.city'),
@ -127,15 +126,6 @@ class AddressDataGrid extends DataGrid
'filterable' => true,
]);
$this->addColumn([
'index' => 'postcode',
'label' => trans('admin::app.customers.addresses.postcode'),
'type' => 'string',
'searchable' => true,
'sortable' => true,
'filterable' => true,
]);
$this->addColumn([
'index' => 'default_address',
'label' => trans('admin::app.customers.addresses.default-address'),

View File

@ -53,6 +53,10 @@ class ProductDataGrid extends DataGrid
$queryBuilder->where('channel', $this->channel);
}
if ($currentLocale = app()->getLocale()) {
$queryBuilder->where('product_flat.locale', $currentLocale);
}
$queryBuilder->groupBy('product_flat.product_id');
$this->addFilter('product_id', 'product_flat.product_id');

View File

@ -5,8 +5,11 @@ namespace Webkul\Admin\Http\Controllers\Customer;
use Illuminate\Support\Facades\Event;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Customer\Repositories\CustomerRepository;
use Webkul\Customer\Repositories\CustomerAddressRepository;
use Webkul\Customer\Repositories\CustomerGroupRepository;
use Webkul\Core\Repositories\ChannelRepository;
use Webkul\Admin\Mail\NewCustomerNotification;
use Mail;
@ -26,6 +29,13 @@ class CustomerController extends Controller
*/
protected $customerRepository;
/**
* CustomerAddress Repository object
*
* @var \Webkul\Customer\Repositories\CustomerAddressRepository
*/
protected $customerAddressRepository;
/**
* CustomerGroupRepository object
*
@ -44,26 +54,26 @@ class CustomerController extends Controller
* Create a new controller instance.
*
* @param \Webkul\Customer\Repositories\CustomerRepository $customerRepository
* @param \Webkul\Customer\Repositories\CustomerAddressRepository $customerAddressRepository
* @param \Webkul\Customer\Repositories\CustomerGroupRepository $customerGroupRepository
* @param \Webkul\Core\Repositories\ChannelRepository $channelRepository
*/
public function __construct(
CustomerRepository $customerRepository,
CustomerAddressRepository $customerAddressRepository,
CustomerGroupRepository $customerGroupRepository,
ChannelRepository $channelRepository
)
{
$this->_config = request('_config');
$this->middleware('admin');
$this->customerRepository = $customerRepository;
$this->customerGroupRepository = $customerGroupRepository;
$this->channelRepository = $channelRepository;
}
)
{
$this->_config = request('_config');
$this->middleware('admin');
$this->customerRepository = $customerRepository;
$this->customerAddressRepository = $customerAddressRepository;
$this->customerGroupRepository = $customerGroupRepository;
$this->channelRepository = $channelRepository;
}
/**
* Display a listing of the resource.
@ -141,12 +151,11 @@ class CustomerController extends Controller
public function edit($id)
{
$customer = $this->customerRepository->findOrFail($id);
$address = $this->customerAddressRepository->find($id);
$customerGroup = $this->customerGroupRepository->findWhere([['code', '<>', 'guest']]);
$channelName = $this->channelRepository->all();
return view($this->_config['view'], compact('customer', 'customerGroup', 'channelName'));
return view($this->_config['view'], compact('customer', 'address', 'customerGroup', 'channelName'));
}
/**

View File

@ -1,6 +1,10 @@
.rtl {
direction: rtl;
.form-container {
text-align: right !important;
}
.navbar-top {
.navbar-top-right {
text-align: left;
@ -43,6 +47,12 @@
.control-group {
margin-right: 20px;
margin-left: 0px;
&.has-error.date, &.has-error.date {
&::after {
margin-top: -12px;
}
}
}
.control-group.date {
@ -234,9 +244,8 @@
.control-group.date {
&::after {
margin-top: 15px;
position: absolute;
margin-right: -35px;
margin-right: -34px;
right: 70%;
}
}

View File

@ -85,7 +85,7 @@
<div class="control-group">
<div class="control-group" :class="[errors.has(inputName + '[value]') ? 'has-error' : '']">
<input type="number" v-validate="'required|min_value:0'" :name="[inputName + '[value]']" v-model="customerGroupPrice.value" class="control" data-vv-as="&quot;{{ __('admin::app.catalog.products.value') }}&quot;"/>
<input type="number" v-validate="'required|min_value:0'" :name="[inputName + '[value]']" v-model="customerGroupPrice.value" class="control" data-vv-as="&quot;{{ __('admin::app.datagrid.price') }}&quot;"/>
<span class="control-error" v-if="errors.has(inputName + '[value]')">@{{ errors.first(inputName + '[value]') }}</span>
</div>
</div>

View File

@ -1,6 +1,6 @@
<select v-validate="'{{$validations}}'" class="control" id="{{ $attribute->code }}" name="{{ $attribute->code }}[]" data-vv-as="&quot;{{ $attribute->admin_name }}&quot;" multiple>
@foreach ($attribute->options as $option)
@foreach ($attribute->options()->orderBy('sort_order')->get() as $option)
<option value="{{ $option->id }}" {{ in_array($option->id, explode(',', $product[$attribute->code])) ? 'selected' : ''}}>
{{ $option->admin_name }}
</option>

View File

@ -4,7 +4,7 @@
@if ($attribute->code != 'tax_category_id')
@foreach ($attribute->options as $option)
@foreach ($attribute->options()->orderBy('sort_order')->get() as $option)
<option value="{{ $option->id }}" {{ $option->id == $selectedOption ? 'selected' : ''}}>
{{ $option->admin_name }}
</option>

View File

@ -6,129 +6,31 @@
@section('content')
<div class="content">
<div class="page-header">
<div class="page-title">
<h1>
<i class="icon angle-left-icon back-link" onclick="history.length > 1 ? history.go(-1) : window.location = '{{ url('/admin/dashboard') }}';"></i>
{{ $customer->first_name . " " . $customer->last_name }}
</h1>
</div>
</div>
<tabs>
{!! view_render_event('bagisto.admin.customer.edit.before', ['customer' => $customer]) !!}
<form method="POST" action="{{ route('admin.customer.update', $customer->id) }}">
<div class="page-header">
<div class="page-title">
<h1>
<i class="icon angle-left-icon back-link" onclick="history.length > 1 ? history.go(-1) : window.location = '{{ url('/admin/dashboard') }}';"></i>
{{ __('admin::app.customers.customers.title') }}
</h1>
<tab name="{{ __('admin::app.sales.orders.info') }}" :selected="true">
<div class="sale-container">
@include('admin::customers.general')
</div>
</tab>
<div class="page-action">
<button type="submit" class="btn btn-lg btn-primary">
{{ __('admin::app.customers.customers.save-btn-title') }}
</button>
<tab name="{{ __('admin::app.customers.customers.addresses') }}" :selected="false">
<div class="style:overflow: auto;">&nbsp;</div>
{!! view_render_event('bagisto.admin.customer.addresses.list.before') !!}
{!! app('Webkul\Admin\DataGrids\AddressDataGrid')->render() !!}
{!! view_render_event('bagisto.admin.customer.addresses.list.after') !!}
</div>
</div>
<div class="page-content">
<div class="form-container">
@csrf()
<input name="_method" type="hidden" value="PUT">
<accordian :title="'{{ __('admin::app.account.general') }}'" :active="true">
<div slot="body">
{!! view_render_event('bagisto.admin.customer.edit.form.before', ['customer' => $customer]) !!}
<div class="control-group" :class="[errors.has('first_name') ? 'has-error' : '']">
<label for="first_name" class="required"> {{ __('admin::app.customers.customers.first_name') }}</label>
<input type="text" class="control" name="first_name" v-validate="'required'" value="{{old('first_name') ?:$customer->first_name}}"
data-vv-as="&quot;{{ __('shop::app.customer.signup-form.firstname') }}&quot;"/>
<span class="control-error" v-if="errors.has('first_name')">@{{ errors.first('first_name') }}</span>
</div>
{!! view_render_event('bagisto.admin.customer.edit.first_name.after', ['customer' => $customer]) !!}
<div class="control-group" :class="[errors.has('last_name') ? 'has-error' : '']">
<label for="last_name" class="required"> {{ __('admin::app.customers.customers.last_name') }}</label>
<input type="text" class="control" name="last_name" v-validate="'required'" value="{{old('last_name') ?:$customer->last_name}}" data-vv-as="&quot;{{ __('shop::app.customer.signup-form.lastname') }}&quot;">
<span class="control-error" v-if="errors.has('last_name')">@{{ errors.first('last_name') }}</span>
</div>
{!! view_render_event('bagisto.admin.customer.edit.last_name.after', ['customer' => $customer]) !!}
<div class="control-group" :class="[errors.has('email') ? 'has-error' : '']">
<label for="email" class="required"> {{ __('admin::app.customers.customers.email') }}</label>
<input type="email" class="control" name="email" v-validate="'required|email'" value="{{old('email') ?:$customer->email}}" data-vv-as="&quot;{{ __('shop::app.customer.signup-form.email') }}&quot;">
<span class="control-error" v-if="errors.has('email')">@{{ errors.first('email') }}</span>
</div>
{!! view_render_event('bagisto.admin.customer.edit.email.after', ['customer' => $customer]) !!}
<div class="control-group" :class="[errors.has('gender') ? 'has-error' : '']">
<label for="gender" class="required">{{ __('admin::app.customers.customers.gender') }}</label>
<select name="gender" class="control" value="{{ $customer->gender }}" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.customers.customers.gender') }}&quot;">
<option value="" {{ $customer->gender == "" ? 'selected' : '' }}></option>
<option value="{{ __('admin::app.customers.customers.male') }}" {{ $customer->gender == __('admin::app.customers.customers.male') ? 'selected' : '' }}>{{ __('admin::app.customers.customers.male') }}</option>
<option value="{{ __('admin::app.customers.customers.female') }}" {{ $customer->gender == __('admin::app.customers.customers.female') ? 'selected' : '' }}>{{ __('admin::app.customers.customers.female') }}</option>
<option value="{{ __('admin::app.customers.customers.other') }}" {{ $customer->gender == __('admin::app.customers.customers.other') ? 'selected' : '' }}>{{ __('admin::app.customers.customers.other') }}</option>
</select>
<span class="control-error" v-if="errors.has('gender')">@{{ errors.first('gender') }}</span>
</div>
{!! view_render_event('bagisto.admin.customer.edit.gender.after', ['customer' => $customer]) !!}
<div class="control-group">
<label for="status" class="required">{{ __('admin::app.customers.customers.status') }}</label>
<label class="switch">
<input type="checkbox" id="status" name="status" value="{{ $customer->status }}" {{ $customer->status ? 'checked' : '' }}>
<span class="slider round"></span>
</label>
<span class="control-error" v-if="errors.has('status')">@{{ errors.first('status') }}</span>
</div>
{!! view_render_event('bagisto.admin.customer.edit.status.after', ['customer' => $customer]) !!}
<div class="control-group" :class="[errors.has('date_of_birth') ? 'has-error' : '']">
<label for="dob">{{ __('admin::app.customers.customers.date_of_birth') }}</label>
<input type="date" class="control" name="date_of_birth" value="{{ old('date_of_birth') ?:$customer->date_of_birth }}" v-validate="" data-vv-as="&quot;{{ __('admin::app.customers.customers.date_of_birth') }}&quot;">
<span class="control-error" v-if="errors.has('date_of_birth')">@{{ errors.first('date_of_birth') }}</span>
</div>
{!! view_render_event('bagisto.admin.customer.edit.date_of_birth.after', ['customer' => $customer]) !!}
<div class="control-group" :class="[errors.has('phone') ? 'has-error' : '']">
<label for="phone">{{ __('admin::app.customers.customers.phone') }}</label>
<input type="text" class="control" name="phone" value="{{ $customer->phone }}" data-vv-as="&quot;{{ __('admin::app.customers.customers.phone') }}&quot;">
<span class="control-error" v-if="errors.has('phone')">@{{ errors.first('phone') }}</span>
</div>
{!! view_render_event('bagisto.admin.customer.edit.phone.after', ['customer' => $customer]) !!}
<div class="control-group">
<label for="customerGroup" >{{ __('admin::app.customers.customers.customer_group') }}</label>
@if (! is_null($customer->customer_group_id))
<?php $selectedCustomerOption = $customer->group->id ?>
@else
<?php $selectedCustomerOption = '' ?>
@endif
<select class="control" name="customer_group_id">
@foreach ($customerGroup as $group)
<option value="{{ $group->id }}" {{ $selectedCustomerOption == $group->id ? 'selected' : '' }}>
{{ $group->name}}
</option>
@endforeach
</select>
</div>
</div>
</accordian>
</div>
</div>
</form>
</tab>
{!! view_render_event('bagisto.admin.customer.edit.after', ['customer' => $customer]) !!}
</tabs>
</div>
@stop

View File

@ -0,0 +1,105 @@
{!! view_render_event('bagisto.admin.customer.edit.before', ['customer' => $customer]) !!}
<form method="POST" action="{{ route('admin.customer.update', $customer->id) }}">
<div class="page-content">
<div class="form-container">
@csrf()
<input name="_method" type="hidden" value="PUT">
<div class="style:overflow: auto;">&nbsp;</div>
<div slot="body">
{!! view_render_event('bagisto.admin.customer.edit.form.before', ['customer' => $customer]) !!}
<div class="control-group" :class="[errors.has('first_name') ? 'has-error' : '']">
<label for="first_name" class="required"> {{ __('admin::app.customers.customers.first_name') }}</label>
<input type="text" class="control" name="first_name" v-validate="'required'" value="{{old('first_name') ?:$customer->first_name}}"
data-vv-as="&quot;{{ __('shop::app.customer.signup-form.firstname') }}&quot;"/>
<span class="control-error" v-if="errors.has('first_name')">@{{ errors.first('first_name') }}</span>
</div>
{!! view_render_event('bagisto.admin.customer.edit.first_name.after', ['customer' => $customer]) !!}
<div class="control-group" :class="[errors.has('last_name') ? 'has-error' : '']">
<label for="last_name" class="required"> {{ __('admin::app.customers.customers.last_name') }}</label>
<input type="text" class="control" name="last_name" v-validate="'required'" value="{{old('last_name') ?:$customer->last_name}}" data-vv-as="&quot;{{ __('shop::app.customer.signup-form.lastname') }}&quot;">
<span class="control-error" v-if="errors.has('last_name')">@{{ errors.first('last_name') }}</span>
</div>
{!! view_render_event('bagisto.admin.customer.edit.last_name.after', ['customer' => $customer]) !!}
<div class="control-group" :class="[errors.has('email') ? 'has-error' : '']">
<label for="email" class="required"> {{ __('admin::app.customers.customers.email') }}</label>
<input type="email" class="control" name="email" v-validate="'required|email'" value="{{old('email') ?:$customer->email}}" data-vv-as="&quot;{{ __('shop::app.customer.signup-form.email') }}&quot;">
<span class="control-error" v-if="errors.has('email')">@{{ errors.first('email') }}</span>
</div>
{!! view_render_event('bagisto.admin.customer.edit.email.after', ['customer' => $customer]) !!}
<div class="control-group" :class="[errors.has('gender') ? 'has-error' : '']">
<label for="gender" class="required">{{ __('admin::app.customers.customers.gender') }}</label>
<select name="gender" class="control" value="{{ $customer->gender }}" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.customers.customers.gender') }}&quot;">
<option value="" {{ $customer->gender == "" ? 'selected' : '' }}></option>
<option value="{{ __('admin::app.customers.customers.male') }}" {{ $customer->gender == __('admin::app.customers.customers.male') ? 'selected' : '' }}>{{ __('admin::app.customers.customers.male') }}</option>
<option value="{{ __('admin::app.customers.customers.female') }}" {{ $customer->gender == __('admin::app.customers.customers.female') ? 'selected' : '' }}>{{ __('admin::app.customers.customers.female') }}</option>
<option value="{{ __('admin::app.customers.customers.other') }}" {{ $customer->gender == __('admin::app.customers.customers.other') ? 'selected' : '' }}>{{ __('admin::app.customers.customers.other') }}</option>
</select>
<span class="control-error" v-if="errors.has('gender')">@{{ errors.first('gender') }}</span>
</div>
{!! view_render_event('bagisto.admin.customer.edit.gender.after', ['customer' => $customer]) !!}
<div class="control-group">
<label for="status" class="required">{{ __('admin::app.customers.customers.status') }}</label>
<label class="switch">
<input type="checkbox" id="status" name="status" value="{{ $customer->status }}" {{ $customer->status ? 'checked' : '' }}>
<span class="slider round"></span>
</label>
<span class="control-error" v-if="errors.has('status')">@{{ errors.first('status') }}</span>
</div>
{!! view_render_event('bagisto.admin.customer.edit.status.after', ['customer' => $customer]) !!}
<div class="control-group" :class="[errors.has('date_of_birth') ? 'has-error' : '']">
<label for="dob">{{ __('admin::app.customers.customers.date_of_birth') }}</label>
<input type="date" class="control" name="date_of_birth" value="{{ old('date_of_birth') ?:$customer->date_of_birth }}" v-validate="" data-vv-as="&quot;{{ __('admin::app.customers.customers.date_of_birth') }}&quot;">
<span class="control-error" v-if="errors.has('date_of_birth')">@{{ errors.first('date_of_birth') }}</span>
</div>
{!! view_render_event('bagisto.admin.customer.edit.date_of_birth.after', ['customer' => $customer]) !!}
<div class="control-group" :class="[errors.has('phone') ? 'has-error' : '']">
<label for="phone">{{ __('admin::app.customers.customers.phone') }}</label>
<input type="text" class="control" name="phone" value="{{ $customer->phone }}" data-vv-as="&quot;{{ __('admin::app.customers.customers.phone') }}&quot;">
<span class="control-error" v-if="errors.has('phone')">@{{ errors.first('phone') }}</span>
</div>
{!! view_render_event('bagisto.admin.customer.edit.phone.after', ['customer' => $customer]) !!}
<div class="control-group">
<label for="customerGroup" >{{ __('admin::app.customers.customers.customer_group') }}</label>
@if (! is_null($customer->customer_group_id))
<?php $selectedCustomerOption = $customer->group->id ?>
@else
<?php $selectedCustomerOption = '' ?>
@endif
<select class="control" name="customer_group_id">
@foreach ($customerGroup as $group)
<option value="{{ $group->id }}" {{ $selectedCustomerOption == $group->id ? 'selected' : '' }}>
{{ $group->name}}
</option>
@endforeach
</select>
</div>
</div>
<button type="submit" class="btn btn-lg btn-primary">
{{ __('admin::app.customers.customers.save-btn-title') }}
</button>
</div>
</div>
</form>
{!! view_render_event('bagisto.admin.customer.edit.after', ['customer' => $customer]) !!}

View File

@ -11,40 +11,6 @@
<div class="page-header">
<div class="page-title">
<h1>{{ __('admin::app.promotions.cart-rules.title') }}</h1>
<div class="control-group">
<select class="control" id="channel-switcher" name="channel"
onchange="reloadPage('channel', this.value)">
<option value="all" {{ ! isset($channel) ? 'selected' : '' }}>
{{ __('admin::app.admin.system.all-channels') }}
</option>
@foreach (core()->getAllChannels() as $channelModel)
<option
value="{{ $channelModel->id }}" {{ (isset($channel) && ($channelModel->id) == $channel) ? 'selected' : '' }}>
{{ $channelModel->name }}
</option>
@endforeach
</select>
</div>
<div class="control-group">
<select class="control" id="customer-group-switcher" name="customer_group"
onchange="reloadPage('customer_group', this.value)">
<option value="all" {{ ! isset($locale) ? 'selected' : '' }}>
{{ __('admin::app.admin.system.all-customer-groups') }}
</option>
@foreach (core()->getAllCustomerGroups() as $customerGroupModel)
<option
value="{{ $customerGroupModel->id }}" {{ (isset($customerGroupModel) && ($customerGroupModel->id) == $customer_group) ? 'selected' : '' }}>
{{ $customerGroupModel->name }}
</option>
@endforeach
</select>
</div>
</div>
<div class="page-action">

View File

@ -53,7 +53,7 @@
<div class="control-group" :class="[errors.has('inventory_sources[]') ? 'has-error' : '']">
<label for="inventory_sources" class="required">{{ __('admin::app.settings.channels.inventory_sources') }}</label>
<select v-validate="'required'" class="control" id="inventory_sources" name="inventory_sources[]" data-vv-as="&quot;{{ __('admin::app.settings.channels.inventory_sources') }}&quot;" multiple>
@foreach (app('Webkul\Inventory\Repositories\InventorySourceRepository')->all() as $inventorySource)
@foreach (app('Webkul\Inventory\Repositories\InventorySourceRepository')->findWhere(['status' => 1]) as $inventorySource)
<option value="{{ $inventorySource->id }}" {{ old('inventory_sources') && in_array($inventorySource->id, old('inventory_sources')) ? 'selected' : '' }}>
{{ $inventorySource->name }}
</option>

View File

@ -56,7 +56,7 @@
<label for="inventory_sources" class="required">{{ __('admin::app.settings.channels.inventory_sources') }}</label>
<?php $selectedOptionIds = old('inventory_sources') ?: $channel->inventory_sources->pluck('id')->toArray() ?>
<select v-validate="'required'" class="control" id="inventory_sources" name="inventory_sources[]" data-vv-as="&quot;{{ __('admin::app.settings.channels.inventory_sources') }}&quot;" multiple>
@foreach (app('Webkul\Inventory\Repositories\InventorySourceRepository')->all() as $inventorySource)
@foreach (app('Webkul\Inventory\Repositories\InventorySourceRepository')->findWhere(['status' => 1]) as $inventorySource)
<option value="{{ $inventorySource->id }}" {{ in_array($inventorySource->id, $selectedOptionIds) ? 'selected' : '' }}>
{{ $inventorySource->name }}
</option>

View File

@ -30,7 +30,7 @@ $factory->define(AttributeOption::class, function (Faker $faker, array $attribut
];
});
$factory->defineAs(AttributeOption::class, 'swatch_color', function (Faker $faker, array $attributes) {
$factory->define(AttributeOption::class, function (Faker $faker, array $attributes) {
return [
'admin_name' => $faker->word,
'sort_order' => $faker->randomDigit,
@ -43,7 +43,7 @@ $factory->defineAs(AttributeOption::class, 'swatch_color', function (Faker $fake
];
});
$factory->defineAs(AttributeOption::class, 'swatch_image', function (Faker $faker, array $attributes) {
$factory->define(AttributeOption::class, function (Faker $faker, array $attributes) {
return [
'admin_name' => $faker->word,
'sort_order' => $faker->randomDigit,
@ -56,7 +56,7 @@ $factory->defineAs(AttributeOption::class, 'swatch_image', function (Faker $fake
];
});
$factory->defineAs(AttributeOption::class, 'swatch_dropdown', function (Faker $faker, array $attributes) {
$factory->define(AttributeOption::class, function (Faker $faker, array $attributes) {
return [
'admin_name' => $faker->word,
'sort_order' => $faker->randomDigit,
@ -69,7 +69,7 @@ $factory->defineAs(AttributeOption::class, 'swatch_dropdown', function (Faker $f
];
});
$factory->defineAs(AttributeOption::class, 'swatch_text', function (Faker $faker, array $attributes) {
$factory->define(AttributeOption::class, function (Faker $faker, array $attributes) {
return [
'admin_name' => $faker->word,
'sort_order' => $faker->randomDigit,

View File

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddSalePricesToBookingProductEventTickets extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('booking_product_event_tickets', function (Blueprint $table) {
$table->decimal('special_price', 12,4)->after('qty')->nullable();
$table->dateTime('special_price_from')->after('special_price')->nullable();
$table->dateTime('special_price_to')->after('special_price_from')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('booking_product_event_tickets', function (Blueprint $table) {
$table->dropColumn('special_price');
$table->dropColumn('special_price_from');
$table->dropColumn('special_price_to');
});
}
}

View File

@ -47,10 +47,19 @@ class EventTicket extends Booking
public function formatPrice($tickets)
{
foreach ($tickets as $index => $ticket) {
$price = $ticket->price;
if ($this->isInSale($ticket)) {
$price = $ticket->special_price;
$tickets[$index]['original_converted_price'] = core()->convertPrice($ticket->price);
$tickets[$index]['original_formated_price'] = core()->currency($ticket->price);
}
$tickets[$index]['id'] = $ticket->id;
$tickets[$index]['converted_price'] = core()->convertPrice($ticket->price);
$tickets[$index]['formated_price'] = $formatedPrice = core()->currency($ticket->price);
$tickets[$index]['formated_price_text'] = trans('bookingproduct::app.shop.products.per-ticket-price', ['price' => $formatedPrice]);
$tickets[$index]['converted_price'] = core()->convertPrice($price);
$tickets[$index]['formated_price'] = $formatedPrice = core()->currency($price);
$tickets[$index]['formated_price_text'] = __('bookingproduct::app.shop.products.per-ticket-price', ['price' => $formatedPrice]);
}
return $tickets;
@ -102,10 +111,15 @@ class EventTicket extends Booking
$ticket = $bookingProduct->event_tickets()->find($product['additional']['booking']['ticket_id']);
$products[$key]['price'] += core()->convertPrice($ticket->price);
$products[$key]['base_price'] += $ticket->price;
$products[$key]['total'] += (core()->convertPrice($ticket->price) * $products[$key]['quantity']);
$products[$key]['base_total'] += ($ticket->price * $products[$key]['quantity']);
$price = $ticket->price;
if ($this->isInSale($ticket)) {
$price = $ticket->special_price;
}
$products[$key]['price'] += core()->convertPrice($price);
$products[$key]['base_price'] += $price;
$products[$key]['total'] += (core()->convertPrice($price) * $products[$key]['quantity']);
$products[$key]['base_total'] += ($price * $products[$key]['quantity']);
}
return $products;
@ -131,9 +145,13 @@ class EventTicket extends Booking
return true;
}
$price += $ticket->price;
if ($this->isInSale($ticket)) {
$price += $ticket->special_price;
} else {
$price += $ticket->price;
}
if ($price == $item->base_price) {
if ($price === $item->base_price) {
return;
}
@ -145,4 +163,17 @@ class EventTicket extends Booking
$item->save();
}
/**
* Determines whether a single ticket is in Sale, i.e. has a valid sale price
*
* @return bool
*/
public function isInSale($ticket): bool
{
return $ticket->special_price !== null
&& $ticket->special_price > 0.0
&& ($ticket->special_price_from === null || $ticket->special_price_from === '0000-00-00 00:00:00' || $ticket->special_price_from <= Carbon::now())
&& ($ticket->special_price_to === null || $ticket->special_price_to === '0000-00-00 00:00:00' || $ticket->special_price_to > Carbon::now());
}
}

View File

@ -13,8 +13,8 @@ class Booking extends Model implements BookingContract
protected $fillable = [
'qty',
'from',
'to',
'available_from',
'available_to',
'order_item_id',
'booking_product_event_ticket_id',
'product_id',

View File

@ -10,10 +10,13 @@ class BookingProductEventTicket extends TranslatableModel implements BookingProd
public $timestamps = false;
public $translatedAttributes = ['name', 'description'];
protected $fillable = [
'price',
'qty',
'special_price',
'special_price_from',
'special_price_to',
'booking_product_id',
];
}

View File

@ -28,6 +28,31 @@ class BookingProductEventTicketRepository extends Repository
if (isset($data['tickets'])) {
foreach ($data['tickets'] as $ticketId => $ticketInputs) {
if (
! array_key_exists('special_price', $ticketInputs)
|| empty($ticketInputs['special_price'])
|| $ticketInputs['special_price'] === '0.0000'
) {
$ticketInputs['special_price'] = null;
}
if (
! array_key_exists('special_price_from', $ticketInputs)
|| empty($ticketInputs['special_price_from'])
|| $ticketInputs['special_price_from'] === '0000-00-00 00:00:00'
) {
$ticketInputs['special_price_from'] = null;
}
if (
! array_key_exists('special_price_to', $ticketInputs)
|| empty($ticketInputs['special_price_to'])
|| $ticketInputs['special_price_to'] === '0000-00-00 00:00:00'
) {
$ticketInputs['special_price_to'] = null;
}
if (Str::contains($ticketId, 'ticket_')) {
$this->create(array_merge([
'booking_product_id' => $bookingProduct->id,

View File

@ -48,6 +48,9 @@ return [
'price' => 'السعر',
'quantity' => 'كمية',
'description' => 'وصف',
'special-price' => 'Special Price',
'special-price-from' => 'Valid From',
'special-price-to' => 'Valid Until',
'charged-per' => 'اتهم لكل',
'guest' => 'زائر',
'table' => 'الطاولة',

View File

@ -48,6 +48,9 @@ return [
'price' => 'Price',
'quantity' => 'Quantity',
'description' => 'Description',
'special-price' => 'Special Price',
'special-price-from' => 'Valid From',
'special-price-to' => 'Valid Until',
'charged-per' => 'Charged Per',
'guest' => 'Guest',
'table' => 'Table',

View File

@ -48,6 +48,9 @@ return [
'price' => 'قیمت',
'quantity' => 'تعداد',
'description' => 'شرح',
'special-price' => 'Special Price',
'special-price-from' => 'Valid From',
'special-price-to' => 'Valid Until',
'charged-per' => 'به اتهام در هر',
'guest' => 'مهمان',
'table' => 'جدول',

View File

@ -48,6 +48,9 @@ return [
'price' => 'Prezzo',
'quantity' => 'Quantità',
'description' => 'Descrizione',
'special-price' => 'Special Price',
'special-price-from' => 'Valid From',
'special-price-to' => 'Valid Until',
'charged-per' => 'Charged Per',
'guest' => 'Ospite',
'table' => 'Tavolo',

View File

@ -48,6 +48,9 @@ return [
'price' => 'Prijs',
'quantity' => 'Aantal stuks',
'description' => 'Beschrijving',
'special-price' => 'Special Price',
'special-price-from' => 'Valid From',
'special-price-to' => 'Valid Until',
'charged-per' => 'In rekening gebracht per',
'guest' => 'Gast',
'table' => 'Tafel',

View File

@ -48,6 +48,9 @@ return [
'price' => 'Preço',
'quantity' => 'Quantidade',
'description' => 'Descrição',
'special-price' => 'Special Price',
'special-price-from' => 'Valid From',
'special-price-to' => 'Valid Until',
'charged-per' => 'Cobrado por',
'guest' => 'Hóspede',
'table' => 'Mesa',

View File

@ -17,6 +17,11 @@
.has-control-group .control-group:last-child {
padding-left: 10px;
}
.rtl .control-group.date::after {
right: 100%;
margin-right: -40px;
}
</style>
@stop
@ -149,6 +154,13 @@
available_to: ''
}
}
},
created: function() {
this.booking.available_from = "{{ $bookingProduct && $bookingProduct->available_from ? $bookingProduct->available_from->format('Y-m-d H:i:s') : '' }}";
this.booking.available_to = "{{ $bookingProduct && $bookingProduct->available_to ? $bookingProduct->available_to->format('Y-m-d H:i:s') : '' }}";
}
});
</script>

View File

@ -17,7 +17,7 @@
<div class="section-content">
<ticket-list :tickets="tickets"></ticket-list>
</div>
</div>
</div>
@ -26,16 +26,6 @@
<script type="text/x-template" id="ticket-list-template">
<div class="ticket-list table">
<table>
<thead>
<tr>
<th>{{ __('bookingproduct::app.admin.catalog.products.name') }}</th>
<th>{{ __('bookingproduct::app.admin.catalog.products.price') }}</th>
<th>{{ __('bookingproduct::app.admin.catalog.products.quantity') }}</th>
<th>{{ __('bookingproduct::app.admin.catalog.products.description') }}</th>
<th class="actions"></th>
</tr>
</thead>
<tbody>
<ticket-item
v-for="(ticket, index) in tickets"
@ -57,36 +47,65 @@
<tr>
<td>
<div class="control-group" :class="[errors.has(controlName + '[{{$locale}}][name]') ? 'has-error' : '']">
<label class="ticket-label">{{ __('bookingproduct::app.admin.catalog.products.name') }}</label>
<input type="text" v-validate="'required'" :name="controlName + '[{{$locale}}][name]'" v-model="ticketItem.name" class="control" data-vv-as="&quot;{{ __('bookingproduct::app.admin.catalog.products.name') }}&quot;">
<span class="control-error" v-if="errors.has(controlName + '[{{$locale}}][name]')">
@{{ errors.first(controlName + '[{!!$locale!!}][name]') }}
</span>
</div>
<div class="control-group">
<label class="ticket-label">{{ __('bookingproduct::app.admin.catalog.products.special-price') }}</label>
<input type="text" :name="controlName + '[special_price]'" v-model="ticketItem.special_price" class="control">
</div>
</td>
<td>
<div class="control-group" :class="[errors.has(controlName + '[price]') ? 'has-error' : '']">
<label class="ticket-label">{{ __('bookingproduct::app.admin.catalog.products.price') }}</label>
<input type="text" v-validate="'required'" :name="controlName + '[price]'" v-model="ticketItem.price" class="control" data-vv-as="&quot;{{ __('bookingproduct::app.admin.catalog.products.price') }}&quot;">
<span class="control-error" v-if="errors.has(controlName + '[price]')">
@{{ errors.first(controlName + '[price]') }}
</span>
</div>
<div class="control-group date" :class="[errors.has(controlName + '[special_price_from]') ? 'has-error' : '']">
<label class="ticket-label">{{ __('bookingproduct::app.admin.catalog.products.special-price-from') }}</label>
<datetime>
<input type="text" v-validate="'date_format:yyyy-MM-dd HH:mm:ss|after:{{\Carbon\Carbon::yesterday()->format('Y-m-d 23:59:59')}}'" :name="controlName + '[special_price_from]'" v-model="ticketItem.special_price_from" class="control" data-vv-as="&quot;{{ __('bookingproduct::app.admin.catalog.products.special-price-from') }}&quot;" ref="special_price_from" style="width:70%"/>
</datetime>
<span class="control-error" v-if="errors.has(controlName + '[special_price_from]')">@{{ errors.first(controlName + '[special_price_from]') }}</span>
</div>
</td>
<td>
<div class="control-group" :class="[errors.has(controlName + '[qty]') ? 'has-error' : '']">
<label class="ticket-label">{{ __('bookingproduct::app.admin.catalog.products.quantity') }}</label>
<input type="text" v-validate="'required|min_value:0'" :name="controlName + '[qty]'" v-model="ticketItem.qty" class="control" data-vv-as="&quot;{{ __('bookingproduct::app.admin.catalog.products.qty') }}&quot;">
<span class="control-error" v-if="errors.has(controlName + '[qty]')">
@{{ errors.first(controlName + '[qty]') }}
</span>
</div>
<div class="control-group date" :class="[errors.has(controlName + '[special_price_to]') ? 'has-error' : '']">
<label class="ticket-label">{{ __('bookingproduct::app.admin.catalog.products.special-price-to') }}</label>
<datetime>
<input type="text" v-validate="'date_format:yyyy-MM-dd HH:mm:ss|after:special_price_from'" :name="controlName + '[special_price_to]'" v-model="ticketItem.special_price_to" class="control" data-vv-as="&quot;{{ __('bookingproduct::app.admin.catalog.products.special-price-to') }}&quot;" ref="special_price_to" style="width:70%"/>
</datetime>
<span class="control-error" v-if="errors.has(controlName + '[special_price_to]')">@{{ errors.first(controlName + '[special_price_to]') }}</span>
</div>
</td>
<td>
<div class="control-group" :class="[errors.has(controlName + '[{{$locale}}][description]') ? 'has-error' : '']">
<label class="ticket-label">{{ __('bookingproduct::app.admin.catalog.products.description') }}</label>
<textarea type="text" v-validate="'required'" :name="controlName + '[{{$locale}}][description]'" v-model="ticketItem.description" class="control" data-vv-as="&quot;{{ __('bookingproduct::app.admin.catalog.products.description') }}&quot;"></textarea>
<span class="control-error" v-if="errors.has(controlName + '[{{$locale}}][description]')">
@ -129,7 +148,10 @@
'name': '',
'price': '',
'qty': 0,
'description': ''
'description': '',
'special_price': '',
'special_price_from': '',
'special_price_to': '',
});
},
@ -166,4 +188,10 @@
});
</script>
@endpush
<style>
.ticket-label {
font-weight: 700;
margin: 20px 0 10px 0;
}
</style>
@endpush

View File

@ -25,7 +25,11 @@
@{{ ticket.name }}
</div>
<div class="ticket-price">
<div v-if="ticket.original_formated_price" class="ticket-price">
<span class="regular-price">@{{ ticket.original_formated_price }}</span>
<span class="special-price">@{{ ticket.formated_price_text }}</span>
</div>
<div v-else class="ticket-price">
@{{ ticket.formated_price_text }}
</div>
</div>
@ -62,4 +66,15 @@
</script>
<style>
.ticket-price .regular-price{
color: #a5a5a5;
text-decoration: line-through;
margin-right: 5px;
}
.ticket-price .special-price {
color: #ff6472;
font-size: larger;
}
</style>
@endpush

View File

@ -25,7 +25,11 @@
@{{ ticket.name }}
</div>
<div class="ticket-price">
<div v-if="ticket.original_formated_price" class="ticket-price">
<span class="regular-price">@{{ ticket.original_formated_price }}</span>
<span class="special-price">@{{ ticket.formated_price_text }}</span>
</div>
<div v-else class="ticket-price">
@{{ ticket.formated_price_text }}
</div>
</div>
@ -62,4 +66,15 @@
</script>
<style>
.ticket-price .regular-price{
color: #a5a5a5;
text-decoration: line-through;
margin-right: 5px;
}
.ticket-price .special-price {
color: #ff6472;
font-size: larger;
}
</style>
@endpush

View File

@ -2,6 +2,8 @@
namespace Webkul\Checkout;
use Exception;
use Illuminate\Support\Facades\Log;
use Webkul\Checkout\Models\Cart as CartModel;
use Webkul\Checkout\Models\CartAddress;
use Webkul\Checkout\Repositories\CartRepository;
@ -120,9 +122,11 @@ class Cart
/**
* Add Items in a cart with some cart and item details.
*
* @param int $productId
* @param array $data
* @return \Webkul\Checkout\Contracts\Cart|\Exception|array
* @param int $productId
* @param array $data
*
* @return \Webkul\Checkout\Contracts\Cart|string|array
* @throws Exception
*/
public function addProduct($productId, $data)
{
@ -149,7 +153,7 @@ class Cart
session()->forget('cart');
}
throw new \Exception($cartProducts);
throw new Exception($cartProducts);
} else {
$parentCartItem = null;
@ -163,7 +167,7 @@ class Cart
if (! $cartItem) {
$cartItem = $this->cartItemRepository->create(array_merge($cartProduct, ['cart_id' => $cart->id]));
} else {
if (isset($cartProduct['parent_id']) && $cartItem->parent_id != $parentCartItem->id) {
if (isset($cartProduct['parent_id']) && $cartItem->parent_id !== $parentCartItem->id) {
$cartItem = $this->cartItemRepository->create(array_merge($cartProduct, [
'cart_id' => $cart->id
]));
@ -234,7 +238,8 @@ class Cart
* Update cart items information
*
* @param array $data
* @return bool|void|\Exception
*
* @return bool|void|Exception
*/
public function updateItems($data)
{
@ -246,19 +251,19 @@ class Cart
}
if ($item->product && $item->product->status === 0) {
throw new \Exception(__('shop::app.checkout.cart.item.inactive'));
throw new Exception(__('shop::app.checkout.cart.item.inactive'));
}
if ($quantity <= 0) {
$this->removeItem($itemId);
throw new \Exception(__('shop::app.checkout.cart.quantity.illegal'));
throw new Exception(__('shop::app.checkout.cart.quantity.illegal'));
}
$item->quantity = $quantity;
if (! $this->isItemHaveQuantity($item)) {
throw new \Exception(__('shop::app.checkout.cart.quantity.inventory_warning'));
throw new Exception(__('shop::app.checkout.cart.quantity.inventory_warning'));
}
Event::dispatch('checkout.cart.update.before', $item);

View File

@ -3,12 +3,34 @@
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use Faker\Generator as Faker;
use Webkul\Checkout\Models\Cart;
use Webkul\Checkout\Models\CartItem;
use Webkul\Product\Models\Product;
$factory->define(CartItem::class, function (Faker $faker) {
$factory->define(CartItem::class, function (Faker $faker, array $attributes) {
$now = date("Y-m-d H:i:s");
if (isset($attributes['product_id'])) {
$product = Product::where('id', $attributes['product_id'])->first();
} else {
$product = factory(Product::class)->create();
}
$fallbackPrice = $faker->randomFloat(4, 0, 1000);
return [
'quantity' => 1,
'sku' => $product->sku,
'type' => $product->type,
'name' => $product->name,
'price' => $product->price ?? $fallbackPrice,
'base_price' => $product->price ?? $fallbackPrice,
'total' => $product->price ?? $fallbackPrice,
'base_total' => $product->price ?? $fallbackPrice,
'product_id' => $product->id,
'cart_id' => function () {
return factory(Cart::class)->create()->id;
},
'created_at' => $now,
'updated_at' => $now,
];

View File

@ -2,7 +2,7 @@
namespace Webkul\Core\Exceptions;
use Exception;
use Throwable;
use Illuminate\Auth\AuthenticationException;
use Doctrine\DBAL\Driver\PDOException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
@ -22,10 +22,10 @@ class Handler extends AppExceptionHandler
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $exception
* @param \Throwable $exception
* @return \Illuminate\Http\Response
*/
public function render($request, Exception $exception)
public function render($request, Throwable $exception)
{
$path = $this->isAdminUri() ? 'admin' : 'shop';

View File

@ -4,6 +4,7 @@
use Faker\Generator as Faker;
use Webkul\Customer\Models\Customer;
use Illuminate\Support\Arr;
use Webkul\Customer\Models\CustomerAddress;
$factory->define(CustomerAddress::class, function (Faker $faker) {
@ -24,7 +25,7 @@ $factory->define(CustomerAddress::class, function (Faker $faker) {
'city' => $faker->city,
'postcode' => $faker->postcode,
'phone' => $faker->e164PhoneNumber,
'default_address' => array_random([0, 1]),
'default_address' => Arr::random([0, 1]),
'address_type' => CustomerAddress::ADDRESS_TYPE,
];
});

View File

@ -4,6 +4,7 @@
use Faker\Generator as Faker;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Arr;
use Webkul\Customer\Models\Customer;
$factory->define(Customer::class, function (Faker $faker) {
@ -13,7 +14,7 @@ $factory->define(Customer::class, function (Faker $faker) {
return [
'first_name' => $faker->firstName(),
'last_name' => $faker->lastName,
'gender' => array_random(['male', 'female', 'other']),
'gender' => Arr::random(['male', 'female', 'other']),
'email' => $faker->email,
'status' => 1,
'password' => Hash::make($password),

View File

@ -2,14 +2,10 @@
namespace Webkul\Customer\Http\Controllers;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
use Illuminate\Support\Facades\Password;
class ForgotPasswordController extends Controller
{
use SendsPasswordResetEmails;
/**
* Contains route related configuration
*

View File

@ -12,5 +12,3 @@ Route::group(['middleware' => ['web']], function () {
});
Route::get('paypal/standard/ipn', 'Webkul\Paypal\Http\Controllers\StandardController@ipn')->name('paypal.standard.ipn');
Route::post('paypal/standard/ipn', 'Webkul\Paypal\Http\Controllers\StandardController@ipn')->name('paypal.standard.ipn');

View File

@ -175,7 +175,7 @@ class ProductController extends Controller
$categories = $this->categoryRepository->getCategoryTree();
$inventorySources = $this->inventorySourceRepository->all();
$inventorySources = $this->inventorySourceRepository->findWhere(['status' => 1]);
return view($this->_config['view'], compact('product', 'categories', 'inventorySources'));
}
@ -303,7 +303,7 @@ class ProductController extends Controller
/**
* To be manually invoked when data is seeded into products
*
*
* @return \Illuminate\Http\Response
*/
public function sync()

View File

@ -356,13 +356,17 @@ class ProductRepository extends Repository
->where('product_flat.channel', $channel)
->where('product_flat.locale', $locale)
->whereNotNull('product_flat.url_key')
->where(function($sub_query) use ($term) {
$sub_query->where('product_flat.name', 'like', '%' . urldecode($term) . '%')
->orWhere('product_flat.short_description', 'like', '%' . urldecode($term) . '%');
})
->where(function($subQuery) use ($term) {
$queries = explode('_', $term);
foreach (array_map('trim', $queries) as $value) {
$subQuery->orWhere('product_flat.name', 'like', '%' . urldecode($value) . '%')
->orWhere('product_flat.short_description', 'like', '%' . urldecode($value) . '%');
}
})
->orderBy('product_id', 'desc');
})->paginate(16);
return $results;
}

View File

@ -3,6 +3,7 @@
namespace Webkul\Product\Repositories;
use Illuminate\Container\Container as App;
use Illuminate\Support\Facades\Storage;
use Webkul\Core\Eloquent\Repository;
use Webkul\Product\Repositories\ProductRepository;
@ -41,4 +42,15 @@ class SearchRepository extends Repository
{
return $this->productRepository->searchProductByAttribute($data['term'] ?? '');
}
/**
* @param array $data
* @return void
*/
public function uploadSearchImage($data)
{
$path = request()->file('image')->store('product-search');
return Storage::url($path);
}
}

View File

@ -20,7 +20,7 @@ class Grouped extends AbstractType
* @var \Webkul\Product\Repositories\ProductGroupedProductRepository
*/
protected $productGroupedProductRepository;
/**
* Skip attribute for downloadable product type
*
@ -30,7 +30,7 @@ class Grouped extends AbstractType
/**
* These blade files will be included in product edit page
*
*
* @var array
*/
protected $additionalViews = [
@ -124,7 +124,7 @@ class Grouped extends AbstractType
*
* @return float
*/
public function getMinimalPrice()
public function getMinimalPrice($qty = null)
{
$minPrices = [];
@ -180,7 +180,7 @@ class Grouped extends AbstractType
if (is_string($cartProducts)) {
return $cartProducts;
}
$products = array_merge($products, $cartProducts);
}

View File

@ -3,8 +3,8 @@
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use Faker\Generator as Faker;
use Webkul\Product\Models\Product;
use Webkul\Sales\Models\Order;
use Webkul\Product\Models\Product;
use Webkul\Sales\Models\OrderItem;
$factory->define(OrderItem::class, function (Faker $faker, array $attributes) {
@ -16,14 +16,16 @@ $factory->define(OrderItem::class, function (Faker $faker, array $attributes) {
$product = factory(Product::class)->create();
}
$fallbackPrice = $faker->randomFloat(4, 0, 1000);
return [
'sku' => $product->sku,
'type' => $product->type,
'name' => $product->name,
'price' => $product->price,
'base_price' => $product->price,
'total' => $product->price,
'base_total' => $product->price,
'price' => $product->price ?? $fallbackPrice,
'base_price' => $product->price ?? $fallbackPrice,
'total' => $product->price ?? $fallbackPrice,
'base_total' => $product->price ?? $fallbackPrice,
'product_id' => $product->id,
'qty_ordered' => 1,
'qty_shipped' => 0,
@ -37,7 +39,4 @@ $factory->define(OrderItem::class, function (Faker $faker, array $attributes) {
'updated_at' => $now,
'product_type' => Product::class,
];
});
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,12 +1,12 @@
/*!
* Sizzle CSS Selector Engine v2.3.4
* Sizzle CSS Selector Engine v2.3.5
* https://sizzlejs.com/
*
* Copyright JS Foundation and other contributors
* Released under the MIT license
* https://js.foundation/
*
* Date: 2019-04-08
* Date: 2020-03-14
*/
/*!
@ -27,7 +27,7 @@
*/
/*!
* jQuery JavaScript Library v3.4.1
* jQuery JavaScript Library v3.5.1
* https://jquery.com/
*
* Includes Sizzle.js
@ -37,7 +37,7 @@
* Released under the MIT license
* https://jquery.org/license
*
* Date: 2019-05-01T21:04Z
* Date: 2020-05-04T22:49Z
*/
/**

View File

@ -1,4 +1,4 @@
{
"/js/shop.js": "/js/shop.js?id=6bb928c2e527b045a56b",
"/css/shop.css": "/css/shop.css?id=d1c42d10589004fac3f8"
"/js/shop.js": "/js/shop.js?id=382caa06d43f2cac5ee6",
"/css/shop.css": "/css/shop.css?id=5799059fc0844d8b4574"
}

View File

@ -2,6 +2,7 @@
namespace Webkul\Shop\Http\Controllers;
use Illuminate\Support\Facades\Log;
use Webkul\Customer\Repositories\WishlistRepository;
use Webkul\Product\Repositories\ProductRepository;
use Webkul\Checkout\Contracts\Cart as CartModel;
@ -73,7 +74,7 @@ class CartController extends Controller
}
if ($result instanceof CartModel) {
session()->flash('success', trans('shop::app.checkout.cart.item.success'));
session()->flash('success', __('shop::app.checkout.cart.item.success'));
if ($customer = auth()->guard('customer')->user()) {
$this->wishlistRepository->deleteWhere(['product_id' => $id, 'customer_id' => $customer->id]);
@ -86,10 +87,13 @@ class CartController extends Controller
}
}
} catch(\Exception $e) {
session()->flash('error', trans($e->getMessage()));
session()->flash('error', __($e->getMessage()));
$product = $this->productRepository->find($id);
Log::error('Shop CartController: ' . $e->getMessage(),
['product_id' => $id, 'cart_id' => cart()->getCart() ?? 0]);
return redirect()->route('shop.productOrCategory.index', $product->url_key);
}

View File

@ -4,8 +4,9 @@ namespace Webkul\Shop\Http\Controllers;
use Webkul\Shop\Http\Controllers\Controller;
use Webkul\Core\Repositories\SliderRepository;
use Webkul\Product\Repositories\SearchRepository;
class HomeController extends Controller
class HomeController extends Controller
{
/**
* SliderRepository object
@ -14,16 +15,29 @@ use Webkul\Core\Repositories\SliderRepository;
*/
protected $sliderRepository;
/**
* SearchRepository object
*
* @var \Webkul\Core\Repositories\SearchRepository
*/
protected $searchRepository;
/**
* Create a new controller instance.
*
* @param \Webkul\Core\Repositories\SliderRepository $sliderRepository
* @param \Webkul\Product\Repositories\SearchRepository $searchRepository
* @return void
*/
public function __construct(SliderRepository $sliderRepository)
public function __construct(
SliderRepository $sliderRepository,
SearchRepository $searchRepository
)
{
$this->sliderRepository = $sliderRepository;
$this->searchRepository = $searchRepository;
parent::__construct();
}
@ -39,10 +53,10 @@ use Webkul\Core\Repositories\SliderRepository;
$currentLocale = core()->getCurrentLocale();
$sliderData = $this->sliderRepository
->where('channel_id', $currentChannel->id)
->where('locale', $currentLocale->code)
->get()
->toArray();
->where('channel_id', $currentChannel->id)
->where('locale', $currentLocale->code)
->get()
->toArray();
return view($this->_config['view'], compact('sliderData'));
}
@ -56,4 +70,16 @@ use Webkul\Core\Repositories\SliderRepository;
{
abort(404);
}
/**
* Upload image for product search with machine learning
*
* @return \Illuminate\Http\Response
*/
public function upload()
{
$url = $this->searchRepository->uploadSearchImage(request()->all());
return $url;
}
}

View File

@ -67,8 +67,17 @@ class ProductsCategoriesProxyController extends Controller
return view($this->_config['product_view'], compact('product', 'customer'));
}
abort(404);
}
abort(404);
$sliderRepository = app('Webkul\Core\Repositories\SliderRepository');
$sliderData = $sliderRepository
->where('channel_id', core()->getCurrentChannel()->id)
->where('locale', core()->getCurrentLocale()->code)
->get()
->toArray();
return view('shop::home.index', compact('sliderData'));
}
}

View File

@ -18,6 +18,9 @@ Route::group(['middleware' => ['web', 'locale', 'theme', 'currency']], function
'view' => 'shop::search.search'
])->name('shop.search.index');
//Upload image for search product
Route::post('/upload-search-image', 'Webkul\Shop\Http\Controllers\HomeController@upload')->name('shop.image.search.upload');
//Country State Selector
Route::get('get/countries', 'Webkul\Core\Http\Controllers\CountryStateController@getCountries')->defaults('_config', [
'view' => 'shop::test'
@ -201,7 +204,7 @@ Route::group(['middleware' => ['web', 'locale', 'theme', 'currency']], function
//Customer Profile Edit Form Store
Route::post('profile/edit', 'Webkul\Customer\Http\Controllers\CustomerController@update')->defaults('_config', [
'redirect' => 'customer.profile.index'
])->name('customer.profile.edit');
])->name('customer.profile.store');
//Customer Profile Delete Form Store
Route::post('profile/destroy', 'Webkul\Customer\Http\Controllers\CustomerController@destroy')->defaults('_config', [
@ -225,7 +228,7 @@ Route::group(['middleware' => ['web', 'locale', 'theme', 'currency']], function
Route::post('addresses/create', 'Webkul\Customer\Http\Controllers\AddressController@store')->defaults('_config', [
'view' => 'shop::customers.account.address.address',
'redirect' => 'customer.address.index'
])->name('customer.address.create');
])->name('customer.address.store');
//Customer Address Edit Form Show
Route::get('addresses/edit/{id}', 'Webkul\Customer\Http\Controllers\AddressController@edit')->defaults('_config', [
@ -235,7 +238,7 @@ Route::group(['middleware' => ['web', 'locale', 'theme', 'currency']], function
//Customer Address Edit Form Store
Route::put('addresses/edit/{id}', 'Webkul\Customer\Http\Controllers\AddressController@update')->defaults('_config', [
'redirect' => 'customer.address.index'
])->name('customer.address.edit');
])->name('customer.address.update');
//Customer Address Make Default
Route::get('addresses/default/{id}', 'Webkul\Customer\Http\Controllers\AddressController@makeDefault')->name('make.default.address');

View File

@ -38,7 +38,9 @@ $(document).ready(function () {
el: "#app",
data: {
modalIds: {}
modalIds: {},
show_loader: false
},
mounted: function () {
@ -114,6 +116,14 @@ $(document).ready(function () {
showModal(id) {
this.$set(this.modalIds, id, true);
},
showLoader() {
this.show_loader = true;
},
hideLoader() {
this.show_loader = false;
}
}
});

View File

@ -775,6 +775,7 @@ section.slider-block {
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
.search-field {
height: 38px;
@ -804,6 +805,21 @@ section.slider-block {
margin: 0px;
}
}
.image-search-container {
position: absolute;
right: 41px;
top: 7px;
background: #fff;
img {
display: none;
}
input {
display: none;
}
}
}
}
}
@ -4483,4 +4499,41 @@ section.review {
b {
font-weight: 500;
}
}
.image-search-result {
background-color: rgba(0, 65, 255, 0.1);
border: 1px solid #0041ff;
padding: 20px;
margin-bottom: 20px;
border-radius: 2px;
display: inline-block;
width: 100%;
.searched-image {
float: left;
img {
width: 150px;
height: auto;
box-shadow: rgba(0, 0, 0, 0.32) 1px 1px 3px 0px;
}
}
.searched-terms {
margin-left: 20px;
display: inline-block;
h3 {
margin-top: 0;
}
.term-list {
a {
padding: 5px 8px;
background: #fff;
margin-right: 10px;
}
}
}
}

View File

@ -71,7 +71,8 @@ return [
'no-results' => 'لا توجد نتائج',
'page-title' => 'بحث',
'found-results' => 'تم العثور على نتائج البحث',
'found-result' => 'تم العثور على نتيجة البحث'
'found-result' => 'تم العثور على نتيجة البحث',
'analysed-keywords' => 'Analysed Keywords'
],
'reviews' => [

View File

@ -71,7 +71,8 @@ return [
'no-results' => 'Keine Ergebnisse gefunden',
'page-title' => config('app.name') . ' - Suchen',
'found-results' => 'Suchergebnisse gefunden',
'found-result' => 'Suchergebnis gefunden'
'found-result' => 'Suchergebnis gefunden',
'analysed-keywords' => 'Analysed Keywords'
],
'reviews' => [

View File

@ -71,7 +71,8 @@ return [
'no-results' => 'No Results Found',
'page-title' => config('app.name') . ' - Search',
'found-results' => 'Search Results Found',
'found-result' => 'Search Result Found'
'found-result' => 'Search Result Found',
'analysed-keywords' => 'Analysed Keywords'
],
'reviews' => [

View File

@ -68,7 +68,8 @@ return [
'no-results' => 'No hay resultados',
'page-title' => 'Búsqueda',
'found-results' => 'No hay resultados de búsqueda',
'found-result' => 'Resultados de la búsqueda'
'found-result' => 'Resultados de la búsqueda',
'analysed-keywords' => 'Analysed Keywords'
],
'reviews' => [

View File

@ -71,7 +71,8 @@ return [
'no-results' => 'نتیجه ای پیدا نشد',
'page-title' => 'فروشگاه - جستجو',
'found-results' => 'نتایج جستجو یافت شد',
'found-result' => 'نتیجه جستجو یافت شد'
'found-result' => 'نتیجه جستجو یافت شد',
'analysed-keywords' => 'Analysed Keywords'
],
'reviews' => [

View File

@ -71,7 +71,8 @@ return [
'no-results' => 'Nessun risultato trovato',
'page-title' => config('app.name') . ' - Cerca',
'found-results' => 'Risultati trovati',
'found-result' => 'Risultato trovato'
'found-result' => 'Risultato trovato',
'analysed-keywords' => 'Analysed Keywords'
],
'reviews' => [

View File

@ -68,7 +68,8 @@ return [
'no-results' => 'お探しの条件に合う検索結果が見つかりませんでした。',
'page-title' => '検索',
'found-results' => '検索結果',
'found-result' => '検索結果'
'found-result' => '検索結果',
'analysed-keywords' => 'Analysed Keywords'
],
'reviews' => [

View File

@ -70,7 +70,8 @@ return [
'no-results' => 'No Results Found',
'page-title' => config('app.name') . ' - Search',
'found-results' => 'Search Results Found',
'found-result' => 'Search Result Found'
'found-result' => 'Search Result Found',
'analysed-keywords' => 'Analysed Keywords'
],
'reviews' => [

View File

@ -71,7 +71,8 @@ return [
'no-results' => 'Nie znaleziono wyników',
'page-title' => config('app.name') . ' - Szukaj',
'found-results' => 'Dostępne wyniki wyszukiwania',
'found-result' => 'Dostępny wynik wyszukiwania'
'found-result' => 'Dostępny wynik wyszukiwania',
'analysed-keywords' => 'Analysed Keywords'
],
'reviews' => [
@ -79,7 +80,7 @@ return [
'add-review-page-title' => 'Dodaj recenzję',
'write-review' => 'Napisz recenzję',
'review-title' => 'Nadaj opinii tytuł',
'product-review-page-title' => ''Opinia o produkcie',
'product-review-page-title' => 'Opinia o produkcie',
'rating-reviews' => 'Oceny i recenzje',
'submit' => 'WYŚLIJ',
'delete-all' => 'Wszystkie recenzje zostały pomyślnie usunięte',
@ -113,7 +114,8 @@ return [
'success' => 'Konto utworzone pomyślnie.',
'success-verify' => 'Konto zostało utworzone pomyślnie, wysłano wiadomość e-mail w celu weryfikacji.',
'success-verify-email-unsent' => 'Konto zostało utworzone pomyślnie, lecz e-mail weryfikacyjny nie został wysłany.',
'failed' => 'Błąd! Nie można utworzyć konta, spróbuj ponownie później.' => 'Twoje konto jest już zweryfikowane lub spróbuj ponownie wysłać nowy e-mail weryfikacyjny.',
'failed' => 'Błąd! Nie można utworzyć konta, spróbuj ponownie później.',
'success-verify-email-unsent' => 'Twoje konto jest już zweryfikowane lub spróbuj ponownie wysłać nowy e-mail weryfikacyjny.',
'verification-not-sent' => 'Błąd! Problem z wysłaniem e-maila weryfikacyjnego, spróbuj ponownie później.',
'verification-sent' => 'Wysłano e-mail weryfikacyjny',
'verified' => 'Twoje konto zostało zweryfikowane, spróbuj się teraz zalogować.',
@ -205,7 +207,7 @@ return [
'make-default' => 'Ustaw jako domyślny',
'default' => 'Domyślny',
'contact' => 'Kontakt',
'confirm-delete' => ''Czy na pewno chcesz usunąć ten adres?',
'confirm-delete' => 'Czy na pewno chcesz usunąć ten adres?',
'default-delete' => 'Nie można zmienić domyślnego adresu .',
'enter-password' => 'Wprowadź hasło.',
],
@ -465,7 +467,7 @@ return [
'quantity-error' => 'Żądana ilość nie jest dostępna.',
'cart-subtotal' => 'Suma częściowa koszyka',
'cart-remove-action' => 'Czy na pewno chcesz to zrobić ?',
'partial-cart-update' => 'Tylko niektóre produkty zostały zaktualizowane' => '',
'partial-cart-update' => 'Tylko niektóre produkty zostały zaktualizowane',
'event' => [
'expired' => 'To wydarzenie wygasło.'
]

View File

@ -71,7 +71,8 @@ return [
'no-results' => 'Nenhum resultado encontrado',
'page-title' => 'Buscar',
'found-results' => 'Resultados da pesquisa encontrados',
'found-result' => 'Resultado da pesquisa encontrado'
'found-result' => 'Resultado da pesquisa encontrado',
'analysed-keywords' => 'Analysed Keywords'
],
'reviews' => [

View File

@ -19,7 +19,7 @@
{!! view_render_event('bagisto.shop.customers.account.address.create.before') !!}
<form id="customer-address-form" method="post" action="{{ route('customer.address.create') }}" @submit.prevent="onSubmit">
<form id="customer-address-form" method="post" action="{{ route('customer.address.store') }}" @submit.prevent="onSubmit">
<div class="account-table-content">
@csrf

View File

@ -19,7 +19,7 @@
{!! view_render_event('bagisto.shop.customers.account.address.edit.before', ['address' => $address]) !!}
<form id="customer-address-form" method="post" action="{{ route('customer.address.edit', $address->id) }}" @submit.prevent="onSubmit">
<form id="customer-address-form" method="post" action="{{ route('customer.address.update', $address->id) }}" @submit.prevent="onSubmit">
<div class="account-table-content">
@method('PUT')

View File

@ -21,7 +21,7 @@
{!! view_render_event('bagisto.shop.customers.account.profile.edit.before', ['customer' => $customer]) !!}
<form method="post" action="{{ route('customer.profile.edit') }}" @submit.prevent="onSubmit">
<form method="post" action="{{ route('customer.profile.store') }}" @submit.prevent="onSubmit">
<div class="edit-form">
@csrf

View File

@ -18,7 +18,10 @@
<form role="search" action="{{ route('shop.search.index') }}" method="GET" style="display: inherit;">
<input type="search" name="term" class="search-field" placeholder="{{ __('shop::app.header.search-text') }}" required>
<image-search-component></image-search-component>
<div class="search-icon-wrapper">
<button class="" class="background: none;">
<i class="icon icon-search"></i>
</button>
@ -194,6 +197,90 @@
</div>
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/mobilenet"></script>
<script type="text/x-template" id="image-search-component-template">
<div>
<label class="image-search-container" for="image-search-container">
<i class="icon camera-icon"></i>
<input type="file" id="image-search-container" ref="image_search_input" v-on:change="uploadImage()"/>
<img id="uploaded-image-url" :src="uploaded_image_url"/>
</label>
</div>
</script>
<script>
Vue.component('image-search-component', {
template: '#image-search-component-template',
data: function() {
return {
uploaded_image_url: ''
}
},
methods: {
uploadImage: function() {
var self = this;
self.$root.showLoader();
var formData = new FormData();
formData.append('image', this.$refs.image_search_input.files[0]);
axios.post("{{ route('shop.image.search.upload') }}", formData, {headers: {'Content-Type': 'multipart/form-data'}})
.then(function(response) {
self.uploaded_image_url = response.data;
var net;
async function app() {
var analysedResult = [];
var queryString = '';
net = await mobilenet.load();
const imgElement = document.getElementById('uploaded-image-url');
const result = await net.classify(imgElement);
result.forEach(function(value) {
queryString = value.className.split(',');
if (queryString.length > 1) {
analysedResult = analysedResult.concat(queryString)
} else {
analysedResult.push(queryString[0])
}
})
localStorage.searched_image_url = self.uploaded_image_url;
queryString = localStorage.searched_terms = analysedResult.join('_');
self.$root.hideLoader();
window.location.href = "{{ route('shop.search.index') }}" + '?term=' + queryString + '&image-search=1';
}
app();
})
.catch(function() {
self.$root.hideLoader();
});
}
}
});
</script>
<script>
$(document).ready(function() {

View File

@ -81,6 +81,8 @@
</p>
</div>
@endif
<overlay-loader :is-open="show_loader"></overlay-loader>
</div>
<script type="text/javascript">

View File

@ -5,6 +5,10 @@
@endsection
@section('content-wrapper')
@if (request('image-search'))
<image-search-result-component></image-search-result-component>
@endif
@if (! $results)
{{ __('shop::app.search.no-results') }}
@endif
@ -39,4 +43,47 @@
@endif
</div>
@endif
@endsection
@endsection
@push('scripts')
<script type="text/x-template" id="image-search-result-component-template">
<div class="image-search-result">
<div class="searched-image">
<img :src="searched_image_url"/>
</div>
<div class="searched-terms">
<h3>{{ __('shop::app.search.analysed-keywords') }}</h3>
<div class="term-list">
<a v-for="term in searched_terms" :href="'{{ route('shop.search.index') }}?term=' + term">
@{{ term }}
</a>
</div>
</div>
</div>
</script>
<script>
Vue.component('image-search-result-component', {
template: '#image-search-result-component-template',
data: function() {
return {
searched_image_url: localStorage.searched_image_url,
searched_terms: []
}
},
created: function() {
this.searched_terms = localStorage.searched_terms.split('_');
console.log(this.searched_terms)
}
});
</script>
@endpush

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><circle cx="12" cy="12" r="3.2"/><path d="M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"/></svg>

After

Width:  |  Height:  |  Size: 324 B

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
{
"/js/ui.js": "/js/ui.js?id=5c5ae91d95c2c0668124",
"/css/ui.css": "/css/ui.css?id=676aac66854686c7def3"
"/js/ui.js": "/js/ui.js?id=b56f2fff2a85ae08608f",
"/css/ui.css": "/css/ui.css?id=881ed8b3d2e495260a71"
}

Binary file not shown.

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><circle cx="12" cy="12" r="3.2"/><path d="M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"/></svg>

After

Width:  |  Height:  |  Size: 324 B

View File

@ -22,6 +22,7 @@ import DateComponent from './components/date';
import TimeComponent from './components/time';
import SwatchPicker from './components/swatch-picker';
import Debounce from './directives/debounce';
import OverlayLoader from './components/overlay-loader';
Vue.component('flash-wrapper', FlashWrapper);
Vue.component('flash', Flash);
@ -45,6 +46,7 @@ Vue.component('date', DateComponent);
Vue.component("time-component", TimeComponent);
Vue.component('swatch-picker', SwatchPicker);
Vue.directive('debounce', Debounce);
Vue.component('overlay-loader', OverlayLoader);
Vue.filter('truncate', function (value, limit, trail) {
if (! value)
value = '';

View File

@ -1,15 +1,20 @@
<template>
<span>
<span style="display: flex;align-items: center;">
<slot>
<input type="text" :name="name" class="control" :value="value" data-input>
</slot>
<button
class="btn"
style="height:100%;margin-left: 8px; margin-top: 5px;"
@click.prevent="clear"
> <span class="icon trash-icon"></span> </button>
</span>
</template>
<script>
import Flatpickr from 'flatpickr';
import Flatpickr from 'flatpickr';
export default {
export default {
props: {
name: String,
value: String,
@ -27,12 +32,19 @@
var element = this.$el.getElementsByTagName('input')[0];
this.datepicker = new Flatpickr(
element, {
allowInput: true,
altFormat: 'Y-m-d',
dateFormat: 'Y-m-d',
weekNumbers: true,
onChange: function(selectedDates, dateStr, instance) {
this_this.$emit('onChange', dateStr)
},
});
}
},
methods: {
clear() {
this.datepicker.clear();
}
}
};
</script>

View File

@ -1,15 +1,20 @@
<template>
<span>
<span style="display: flex;align-items: center;">
<slot>
<input type="text" :name="name" class="control" :value="value" data-input>
</slot>
<button
class="btn"
style="height:100%;margin-left: 8px; margin-top: 5px;"
@click.prevent="clear"
> <span class="icon trash-icon"></span> </button>
</span>
</template>
<script>
import Flatpickr from "flatpickr";
import Flatpickr from "flatpickr";
export default {
export default {
props: {
name: String,
value: String
@ -35,10 +40,17 @@
dateFormat: "Y-m-d H:i:S",
enableTime: true,
time_24hr: true,
weekNumbers: true,
onChange: function (selectedDates, dateStr, instance) {
this_this.$emit('onChange', dateStr)
},
});
},
methods: {
clear() {
this.datepicker.clear();
}
}
};
</script>

View File

@ -0,0 +1,31 @@
<template>
<div class="overlay-loader" v-if="isLoaderOpen">
<div id="loader" class="cp-spinner cp-round"></div>
</div>
</template>
<script>
export default {
props: ['id', 'isOpen'],
computed: {
isLoaderOpen () {
this.addClassToBody();
return this.isOpen;
}
},
methods: {
addClassToBody () {
var body = document.querySelector("body");
if (this.isOpen) {
body.classList.add("modal-open");
} else {
body.classList.remove("modal-open");
}
}
}
}
</script>

View File

@ -1165,4 +1165,13 @@ modal {
font-weight: bold;
}
}
}
.overlay-loader {
position: fixed;
z-index: 11;
top: 50%;
left: 50%;
margin-top: -24px;
margin-left: -24px;
}

View File

@ -340,4 +340,10 @@
width: 17px;
height: 17px;
background-image: url("../images/Icon-star.svg");
}
.camera-icon {
background-image: url("../images/Camera.svg");
width: 24px;
height: 24px;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
{
"/js/velocity.js": "/js/velocity.js?id=38c9bb2e6b07d87d123f",
"/js/velocity.js": "/js/velocity.js?id=8800d2830d2fa9f0254d",
"/css/velocity-admin.css": "/css/velocity-admin.css?id=612d35e452446366eef7",
"/css/velocity.css": "/css/velocity.css?id=fb18207e4410872a861a"
"/css/velocity.css": "/css/velocity.css?id=aaf9dc15817e6b912dd2"
}

View File

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

View File

@ -13,9 +13,10 @@ class VelocityMetaDataSeeder extends Seeder
DB::table('velocity_meta_data')->insert([
'id' => 1,
'locale' => 'en',
'home_page_content' => "<p>@include('shop::home.advertisements.advertisement-four')@include('shop::home.featured-products') @include('shop::home.product-policy') @include('shop::home.advertisements.advertisement-three') @include('shop::home.new-products') @include('shop::home.advertisements.advertisement-two')</p>",
'footer_left_content' => trans('velocity::app.admin.meta-data.footer-left-raw-content'),
'footer_left_content' => __('velocity::app.admin.meta-data.footer-left-raw-content'),
'footer_middle_content' => '<div class="col-lg-6 col-md-12 col-sm-12 no-padding"><ul type="none"><li><a href="https://webkul.com/about-us/company-profile/">About Us</a></li><li><a href="https://webkul.com/about-us/company-profile/">Customer Service</a></li><li><a href="https://webkul.com/about-us/company-profile/">What&rsquo;s New</a></li><li><a href="https://webkul.com/about-us/company-profile/">Contact Us </a></li></ul></div><div class="col-lg-6 col-md-12 col-sm-12 no-padding"><ul type="none"><li><a href="https://webkul.com/about-us/company-profile/"> Order and Returns </a></li><li><a href="https://webkul.com/about-us/company-profile/"> Payment Policy </a></li><li><a href="https://webkul.com/about-us/company-profile/"> Shipping Policy</a></li><li><a href="https://webkul.com/about-us/company-profile/"> Privacy and Cookies Policy </a></li></ul></div>',
'slider' => 1,

View File

@ -214,13 +214,21 @@ class Helper extends Review
*
* @return array
*/
public function getVelocityMetaData()
public function getVelocityMetaData($locale = null, $default = true)
{
try {
$metaData = $this->velocityMetadataRepository->get();
if (! $locale) {
$locale = request()->get('locale') ?: app()->getLocale();
}
if (! ($metaData && isset($metaData[0]) && $metaData = $metaData[0])) {
$metaData = null;
try {
$metaData = $this->velocityMetadataRepository->findOneWhere([
'locale' => $locale
]);
if (! $metaData && $default) {
$metaData = $this->velocityMetadataRepository->findOneWhere([
'locale' => 'en'
]);
}
return $metaData;
@ -287,6 +295,7 @@ class Helper extends Review
* @param \Webkul\Product\Contracts\Product $product
* @param bool $list
* @param array $metaInformation
*
* @return array
*/
public function formatProduct($product, $list = false, $metaInformation = [])

View File

@ -15,6 +15,8 @@ class ConfigurationController extends Controller
*/
protected $velocityMetaDataRepository;
protected $locale;
/**
* Create a new controller instance.
*
@ -28,6 +30,8 @@ class ConfigurationController extends Controller
$this->velocityHelper = app('Webkul\Velocity\Helpers\Helper');
$this->velocityMetaDataRepository = $velocityMetadataRepository;
$this->locale = request()->get('locale') ?: app()->getLocale();
}
/**
@ -35,12 +39,16 @@ class ConfigurationController extends Controller
*/
public function renderMetaData()
{
$velocityMetaData = $this->velocityHelper->getVelocityMetaData();
$velocityMetaData = $this->velocityHelper->getVelocityMetaData($this->locale, false);
if ($velocityMetaData && $velocityMetaData->advertisement) {
$velocityMetaData->advertisement = $this->manageAddImages(json_decode($velocityMetaData->advertisement, true));
if (! $velocityMetaData) {
$this->createMetaData($this->locale);
$velocityMetaData = $this->velocityHelper->getVelocityMetaData($this->locale);
}
$velocityMetaData->advertisement = $this->manageAddImages(json_decode($velocityMetaData->advertisement, true) ?: []);
return view($this->_config['view'], [
'metaData' => $velocityMetaData,
]);
@ -63,7 +71,9 @@ class ConfigurationController extends Controller
];
}
$velocityMetaData = $this->velocityMetaDataRepository->findorFail($id);
$velocityMetaData = $this->velocityMetaDataRepository->findOneWhere([
'id' => $id,
]);
$advertisement = json_decode($velocityMetaData->advertisement, true);
@ -101,18 +111,21 @@ class ConfigurationController extends Controller
unset($params['images']);
unset($params['slides']);
$params['locale'] = $this->locale;
// update row
$product = $this->velocityMetaDataRepository->update($params, $id);
session()->flash('success', trans('admin::app.response.update-success', ['name' => 'Velocity Theme']));
return redirect()->route($this->_config['redirect']);
return redirect()->route($this->_config['redirect'], ['locale' => $this->locale]);
}
/**
* @param array $data
* @param int $index
* @param array $advertisement
* @param array $data
* @param int $index
* @param array $advertisement
*
* @return array
*/
public function uploadAdvertisementImages($data, $index, $advertisement)
@ -177,8 +190,9 @@ class ConfigurationController extends Controller
}
/**
* @param array $data
* @param int $index
* @param array $data
* @param int $index
*
* @return mixed
*/
public function uploadImage($data, $index)
@ -201,6 +215,7 @@ class ConfigurationController extends Controller
/**
* @param array $addImages
*
* @return array
*/
public function manageAddImages($addImages)
@ -224,4 +239,21 @@ class ConfigurationController extends Controller
return $imagePaths;
}
private function createMetaData($locale)
{
\DB::table('velocity_meta_data')->insert([
'locale' => $locale,
'home_page_content' => "<p>@include('shop::home.advertisements.advertisement-four')@include('shop::home.featured-products') @include('shop::home.product-policy') @include('shop::home.advertisements.advertisement-three') @include('shop::home.new-products') @include('shop::home.advertisements.advertisement-two')</p>",
'footer_left_content' => __('velocity::app.admin.meta-data.footer-left-raw-content'),
'footer_middle_content' => '<div class="col-lg-6 col-md-12 col-sm-12 no-padding"><ul type="none"><li><a href="https://webkul.com/about-us/company-profile/">About Us</a></li><li><a href="https://webkul.com/about-us/company-profile/">Customer Service</a></li><li><a href="https://webkul.com/about-us/company-profile/">What&rsquo;s New</a></li><li><a href="https://webkul.com/about-us/company-profile/">Contact Us </a></li></ul></div><div class="col-lg-6 col-md-12 col-sm-12 no-padding"><ul type="none"><li><a href="https://webkul.com/about-us/company-profile/"> Order and Returns </a></li><li><a href="https://webkul.com/about-us/company-profile/"> Payment Policy </a></li><li><a href="https://webkul.com/about-us/company-profile/"> Shipping Policy</a></li><li><a href="https://webkul.com/about-us/company-profile/"> Privacy and Cookies Policy </a></li></ul></div>',
'slider' => 1,
'subscription_bar_content' => '<div class="social-icons col-lg-6"><a href="https://webkul.com" target="_blank" class="unset" rel="noopener noreferrer"><i class="fs24 within-circle rango-facebook" title="facebook"></i> </a> <a href="https://webkul.com" target="_blank" class="unset" rel="noopener noreferrer"><i class="fs24 within-circle rango-twitter" title="twitter"></i> </a> <a href="https://webkul.com" target="_blank" class="unset" rel="noopener noreferrer"><i class="fs24 within-circle rango-linked-in" title="linkedin"></i> </a> <a href="https://webkul.com" target="_blank" class="unset" rel="noopener noreferrer"><i class="fs24 within-circle rango-pintrest" title="Pinterest"></i> </a> <a href="https://webkul.com" target="_blank" class="unset" rel="noopener noreferrer"><i class="fs24 within-circle rango-youtube" title="Youtube"></i> </a> <a href="https://webkul.com" target="_blank" class="unset" rel="noopener noreferrer"><i class="fs24 within-circle rango-instagram" title="instagram"></i></a></div>',
'product_policy' => '<div class="row col-12 remove-padding-margin"><div class="col-lg-4 col-sm-12 product-policy-wrapper"><div class="card"><div class="policy"><div class="left"><i class="rango-van-ship fs40"></i></div> <div class="right"><span class="font-setting fs20">Free Shipping on Order $20 or More</span></div></div></div></div> <div class="col-lg-4 col-sm-12 product-policy-wrapper"><div class="card"><div class="policy"><div class="left"><i class="rango-exchnage fs40"></i></div> <div class="right"><span class="font-setting fs20">Product Replace &amp; Return Available </span></div></div></div></div> <div class="col-lg-4 col-sm-12 product-policy-wrapper"><div class="card"><div class="policy"><div class="left"><i class="rango-exchnage fs40"></i></div> <div class="right"><span class="font-setting fs20">Product Exchange and EMI Available </span></div></div></div></div></div>',
]);
}
}

View File

@ -3,6 +3,7 @@
namespace Webkul\Velocity\Http\Controllers\Shop;
use Cart;
use Illuminate\Support\Facades\Log;
use Webkul\Velocity\Helpers\Helper;
use Webkul\Checkout\Contracts\Cart as CartModel;
use Webkul\Product\Repositories\ProductRepository;
@ -93,16 +94,19 @@ class CartController extends Controller
} catch(\Exception $exception) {
$product = $this->productRepository->find($id);
Log::error('Velocity CartController: ' . $exception->getMessage(),
['product_id' => $id, 'cart_id' => cart()->getCart() ?? 0]);
$response = [
'status' => 'danger',
'message' => trans($exception->getMessage()),
'message' => __($exception->getMessage()),
'redirectionRoute' => route('shop.productOrCategory.index', $product->url_key),
];
}
return $response ?? [
'status' => 'danger',
'message' => trans('velocity::app.error.something_went_wrong'),
'message' => __('velocity::app.error.something_went_wrong'),
];
}

View File

@ -81,6 +81,7 @@ class ComparisonController extends Controller
$productFlat = $productFlatRepository
->where('product_id', $productId)
->orWhere('parent_id', $productId)
->get()
->first();

View File

@ -55,7 +55,7 @@ Route::group(['middleware' => ['web', 'locale', 'theme', 'currency']], function
]);
Route::get('/items-count', 'ShopController@getItemsCount')
->name('velocity.product.details');
->name('velocity.product.item-count');
Route::get('/detailed-products', 'ShopController@getDetailedProducts')
->name('velocity.product.details');

View File

@ -71,7 +71,11 @@ class VelocityServiceProvider extends ServiceProvider
$loader->alias('velocity', VelocityFacade::class);
}
// this function will provide global variables shared by view (blade files)
/**
* this function will provide global variables shared by view (blade files)
*
* @return boolean
*/
private function loadPublishableAssets()
{
$this->publishes([
@ -85,7 +89,11 @@ class VelocityServiceProvider extends ServiceProvider
return true;
}
// this function will provide global variables shared by view (blade files)
/**
* this function will provide global variables shared by view (blade files)
*
* @return boolean
*/
private function loadGloableVariables()
{
$velocityHelper = app('Webkul\Velocity\Helpers\Helper');

Some files were not shown because too many files have changed in this diff Show More