Multiple addresses implemented for customer with the option of making the default address also, new radio button for checkout address selection made

This commit is contained in:
prashant-webkul 2018-10-27 16:43:57 +05:30
parent ad187c430c
commit 18ff527319
16 changed files with 328 additions and 199 deletions

View File

@ -11,7 +11,8 @@
<link rel="stylesheet" href="{{ asset('vendor/webkul/admin/assets/css/admin.css') }}">
<link rel="stylesheet" href="{{ asset('vendor/webkul/ui/assets/css/ui.css') }}">
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
@yield('head')
@yield('css')
@ -54,22 +55,16 @@
</script>
<script type="text/javascript" src="{{ asset('vendor/webkul/admin/assets/js/admin.js') }}"></script>
<script type="text/javascript" src="{{ asset('vendor/webkul/ui/assets/js/ui.js') }}"></script>
<script type="text/javascript" src="{{ asset('js/tinyMCE/tinymce.min.js') }}">
<script type="text/javascript" src="{{ asset('js/tinyMCE/tinymce.min.js') }}"></script>
<script>
tinymce.init({
selector: 'textarea',
height: 500,
menubar: false,
plugins: [
'advlist autolink lists link image charmap print preview anchor textcolor',
'searchreplace visualblocks code fullscreen',
'insertdatetime media table contextmenu paste code help wordcount'
],
toolbar: 'insert | undo redo | formatselect | bold italic backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | removeformat | help',
content_css: [
'//fonts.googleapis.com/css?family=Lato:300,300i,400,400i',
'//www.tinymce.com/css/codepen.min.css']
seletor: 'textarea',
height: 500,
menubar: false,
toolbar: 'insert | undo redo | formatselect | bold italic backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | removeformat'
});
</script>

View File

@ -22,51 +22,46 @@
<div class="page-content">
<div class="form-container">
@csrf()
<accordian :title="'{{ __('admin::app.settings.sliders.general') }}'" :active="true">
<div slot="body">
<div class="control-group" :class="[errors.has('title') ? 'has-error' : '']">
<label for="title">{{ __('admin::app.settings.sliders.title') }}</label>
<input type="text" class="control" name="title" v-validate="'required'">
<span class="control-error" v-if="errors.has('title')">@{{ errors.first('title') }}</span>
</div>
<div class="control-group" :class="[errors.has('title') ? 'has-error' : '']">
<label for="title">{{ __('admin::app.settings.sliders.title') }}</label>
<input type="text" class="control" name="title" v-validate="'required'">
<span class="control-error" v-if="errors.has('title')">@{{ errors.first('title') }}</span>
</div>
<?php $channels = core()->getAllChannels() ?>
<div class="control-group" :class="[errors.has('channel_id') ? 'has-error' : '']">
<label for="channel_id">{{ __('admin::app.settings.sliders.channels') }}</label>
<select class="control" id="channel_id" name="channel_id" v-validate="'required'">
@foreach($channels as $channel)
<option value="{{ $channel->id }}" @if($channel->id == old('channel_id')) selected @endif>
{{ __($channel->name) }}
</option>
@endforeach
</select>
<span class="control-error" v-if="errors.has('channel_id')">@{{ errors.first('channel_id') }}</span>
</div>
<?php $channels = core()->getAllChannels() ?>
<div class="control-group" :class="[errors.has('channel_id') ? 'has-error' : '']">
<label for="channel_id">{{ __('admin::app.settings.sliders.channels') }}</label>
<select class="control" id="channel_id" name="channel_id" v-validate="'required'">
@foreach($channels as $channel)
<option value="{{ $channel->id }}" @if($channel->id == old('channel_id')) selected @endif>
{{ __($channel->name) }}
</option>
@endforeach
</select>
<span class="control-error" v-if="errors.has('channel_id')">@{{ errors.first('channel_id') }}</span>
</div>
<div class="control-group" :class="[errors.has('image') ? 'has-error' : '']">
<label for="new_image">{{ __('admin::app.settings.sliders.image') }}</label>
<div class="control-group" :class="[errors.has('image') ? 'has-error' : '']">
<label for="new_image">{{ __('admin::app.settings.sliders.image') }}</label>
<image-upload>
<image-upload>
<input type="file" class="control" id="add_image" name="image" value="" v-validate="'image|required'" placeholder="Upload from Outer"/>
<input type="file" class="control" id="add_image" name="image" value="" v-validate="'image|required'" placeholder="Upload from Outer"/>
<span class="control-error" v-if="errors.has('image')">@{{ errors.first('image') }}</span>
{{-- The image field validation is not working, resolve it. --}}
<span class="control-error" v-if="errors.has('image')">@{{ errors.first('image') }}</span>
{{-- The image field validation is not working, resolve it. --}}
</image-upload>
</div>
</image-upload>
</div>
<div class="control-group" :class="[errors.has('content') ? 'has-error' : '']">
<label for="content">{{ __('admin::app.settings.sliders.content') }}</label>
<div class="control-group" :class="[errors.has('content') ? 'has-error' : '']">
<label for="content">{{ __('admin::app.settings.sliders.content') }}</label>
<textarea class="control" id="add_content" name="content" v-validate="'required'" rows="5"></textarea>
{{-- <textarea></textarea> --}}
<textarea class="control" id="add_content" name="content" v-validate="'required'" rows="5"></textarea>
<span class="control-error" v-if="errors.has('content')">@{{ errors.first('content') }}</span>
</div>
</div>
</accordian>
<span class="control-error" v-if="errors.has('content')">@{{ errors.first('content') }}</span>
</div>
</div>
</div>
</form>

View File

@ -0,0 +1,37 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class ModifyCustomerAddressTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (Schema::hasTable('customer_addresses')) {
Schema::table('customer_addresses', function (Blueprint $table) {
$table->boolean('default_address')->default(0)->after('phone');
$table->string('name')->nullable()->after('phone');
});
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
if (Schema::hasTable('customer_addresses')) {
Schema::table('customer_addresses', function (Blueprint $table) {
$table->dropColumn(['default', 'name']);
});
}
}
}

View File

@ -9,10 +9,8 @@ use Webkul\Customer\Repositories\CustomerAddressRepository;
use Auth;
/**
* Customer controlller for the customer
* basically for the tasks of customers
* which will be done after customer
* authenticastion.
* Customer controlller for the customer basically for the tasks of customers which will
* be done after customer authenticastion.
*
* @author Prashant Singh <prashant.singh852@webkul.com>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
@ -34,35 +32,18 @@ class AddressController extends Controller
$this->_config = request('_config');
$this->customer = $customer;
$this->customer = auth()->guard('customer')->user();
$this->address = $address;
}
/**
* Getting logged in customer address helper
*
* @return array
*/
private function getAddress($id) {
$address = collect($this->address->findOneWhere(['customer_id'=>$id]));
return $address;
}
/**
* Address Route index page
*
* @return view
*/
public function index() {
$id = auth()->guard('customer')->user()->id;
$address = $this->getAddress($id);
return view($this->_config['view'])->with('address', $address);
return view($this->_config['view'])->with('addresses', $this->customer->addresses);
}
/**
@ -80,90 +61,64 @@ class AddressController extends Controller
* @return view
*/
public function create() {
$id = auth()->guard('customer')->user()->id;
$data = collect(request()->input())->except('_token')->toArray();
$this->validate(request(), [
'address1' => 'string|required',
'address2' => 'string|required',
'country' => 'string|required',
'state' => 'string|required',
'city' => 'string|required',
'postcode' => 'required',
]);
$cust_id['customer_id'] = $id;
$cust_id['customer_id'] = $this->customer->id;
$data = array_merge($cust_id, $data);
$address = $this->getAddress($id);
if(count($address) == 0 || $address->isEmpty()) {
if($this->address->create($data)) {
session()->flash('success', 'Address have been successfully added.');
return redirect()->route($this->_config['redirect']);
} else {
session()->flash('error', 'Address cannot be added.');
return redirect()->back();
}
} else {
session()->flash('error', 'Cannot create a new address due to previously existing address');
return redirect()-route('customer.address.edit');
if($this->customer->addresses->count() == 0) {
$data['default_address'] = 1;
}
if($this->address->create($data)) {
session()->flash('success', 'Address have been successfully added.');
return redirect()->route($this->_config['redirect']);
} else {
session()->flash('error', 'Address cannot be added.');
return redirect()->back();
}
}
/**
* For editing the existing address of the customer
* For editing the existing addresses of current logged in customer
*
* @return view
*/
public function showEdit() {
$id = auth()->guard('customer')->user()->id;
$address = $this->getAddress($id);
return view($this->_config['view'])->with('address', $address);
public function showEdit($id) {
$address = $this->customer->addresses->where('id', $id);
return view($this->_config['view'])->with('address', $address->first());
}
/**
* Edits the premade resource of customer called
* Edit's the premade resource of customer called
* Address.
*
* @return redirect
*/
public function edit() {
$id = auth()->guard('customer')->user()->id;
public function edit($id) {
$this->validate(request(), [
'address1' => 'string|required',
'address2' => 'string|required',
'country' => 'string|required',
'state' => 'string|required',
'city' => 'string|required',
'postcode' => 'required',
]);
$data = collect(request()->input())->except('_token')->toArray();
$address = $this->getAddress($id);
if($this->address->update($data, $id)) {
Session()->flash('success','Address Updated Successfully.');
@ -174,4 +129,38 @@ class AddressController extends Controller
return redirect()->route('customer.address.edit');
}
}
/**
* To change the default address or make the default address, by default when first address is created will be the default address
*
* @return Mixed | @return response
*/
public function makeDefault($id) {
$default = $this->customer->defaultAddress;
$result = $this->address->find($default->id)->update(['default_address' => 0]);
if($this->address->find($id)->update(['default_address' => 1])) {
} else {
session()->flash('success', 'Default Cannot Be Address Changed');
}
return redirect()->back();
}
/**
* Delete address of the current customer
*
* @param integer $id
*
* @return response mixed
*/
public function destroy($id) {
$this->address->delete($id);
session()->flash('success', trans('shop::app.address.delete.success'));
return redirect()->back();
}
}

View File

@ -7,6 +7,12 @@ use Illuminate\Http\Response;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
use Illuminate\Support\Facades\Password;
/**
* Forgot Password controlller for the customer.
*
* @author Prashant Singh <prashant.singh852@webkul.com>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class ForgotPasswordController extends Controller
{

View File

@ -10,6 +10,12 @@ use Illuminate\Support\Facades\Hash;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Support\Str;
/**
* Reset Password controlller for the customer.
*
* @author Prashant Singh <prashant.singh852@webkul.com>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class ResetPasswordController extends Controller
{

View File

@ -8,6 +8,7 @@ use Illuminate\Support\Facades\Event;
use Webkul\Customer\Models\Customer;
use Webkul\Customer\Http\Listeners\CustomerEventsHandler;
use Cart;
/**
* Session controller for the user customer
*
@ -27,7 +28,6 @@ class SessionController extends Controller
{
$this->middleware('customer')->except(['show','create']);
$this->_config = request('_config');
$subscriber = new CustomerEventsHandler;
@ -53,13 +53,11 @@ class SessionController extends Controller
]);
if (!auth()->guard('customer')->attempt(request(['email', 'password']))) {
session()->flash('error', 'Please check your credentials and try again.');
return back();
}
//Event passed to prepare cart after login
Event::fire('customer.after.login', $request->input('email'));
return redirect()->intended(route($this->_config['redirect']));

View File

@ -11,10 +11,8 @@ use Cart;
use Auth;
/**
* Customer controlller for the customer
* basically for the tasks of customers
* which will be done after customer
* authenticastion.
* Customer controlller for the customer basically for the tasks of customers which will be done
* after customer authenticastion.
*
* @author Prashant Singh <prashant.singh852@webkul.com>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)

View File

@ -50,8 +50,16 @@ class Customer extends Authenticatable
/**
* Get the customer address that owns the customer.
*/
public function address()
public function addresses()
{
return $this->hasOne(CustomerAddress::class);
return $this->hasMany(CustomerAddress::class, 'customer_id');
}
/**
* Get default customer address that owns the customer.
*/
public function defaultAddress()
{
return $this->hasOne(CustomerAddress::class, 'customer_id')->where('default_address', 1);
}
}

View File

@ -7,5 +7,5 @@ class CustomerAddress extends Model
{
protected $table = 'customer_addresses';
protected $fillable = ['customer_id' ,'address1', 'address2', 'country', 'state', 'city', 'postcode'];
protected $fillable = ['customer_id' ,'address1', 'address2', 'country', 'state', 'city', 'postcode', 'default_address'];
}

View File

@ -23,32 +23,4 @@ class CustomerAddressRepository extends Repository
{
return 'Webkul\Customer\Models\CustomerAddress';
}
/**
* @param array $data
* @return mixed
*/
public function create(array $data)
{
$address = $this->model->create($data);
return $address;
}
/**
* @param array $data
* @param $id
* @param string $attribute
* @return mixed
*/
public function update(array $data, $id, $attribute = "id")
{
$address = $this->findOneByField('customer_id', $id);
$address->update($data);
return $address;
}
}

View File

@ -122,12 +122,10 @@ Route::group(['middleware' => ['web', 'theme', 'locale', 'currency']], function
//customer account
Route::prefix('account')->group(function () {
Route::get('index', 'Webkul\Customer\Http\Controllers\AccountController@index')->defaults('_config', [
'view' => 'shop::customers.account.index'
])->name('customer.account.index');
/* Profile Routes Starts Here */
Route::get('profile', 'Webkul\Customer\Http\Controllers\CustomerController@index')->defaults('_config', [
'view' => 'shop::customers.account.profile.index'
@ -157,13 +155,17 @@ Route::group(['middleware' => ['web', 'theme', 'locale', 'currency']], function
'redirect' => 'customer.address.index'
])->name('customer.address.create');
Route::get('address/edit', 'Webkul\Customer\Http\Controllers\AddressController@showEdit')->defaults('_config', [
Route::get('address/edit/{id}', 'Webkul\Customer\Http\Controllers\AddressController@showEdit')->defaults('_config', [
'view' => 'shop::customers.account.address.edit'
])->name('customer.address.edit');
Route::post('address/edit', 'Webkul\Customer\Http\Controllers\AddressController@edit')->defaults('_config', [
Route::post('address/edit/{id}', 'Webkul\Customer\Http\Controllers\AddressController@edit')->defaults('_config', [
'redirect' => 'customer.address.index'
])->name('customer.address.edit');
Route::get('address/default/{id}', 'Webkul\Customer\Http\Controllers\AddressController@makeDefault')->name('make.default.address');
Route::get('address/delete/{id}', 'Webkul\Customer\Http\Controllers\AddressController@destroy')->name('address.delete');
/* Routes for Addresses ends here */
/* Wishlist route */

View File

@ -140,7 +140,64 @@ body {
margin-top: 90px;
}
//components
.bold {
font-weight: bold;
color: #3A3A3A;
}
/* The container */
.radio-container {
display: block;
position: relative;
padding-left: 35px;
margin-bottom: 12px;
cursor: pointer;
font-size: 16px;
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
input {
position: absolute;
opacity: 0;
cursor: pointer;
}
/* Create a custom radio button */
.checkmark {
position: absolute;
top: 0;
left: 0;
height: 16px;
width: 16px;
background-color: white;
border: 2px solid #FF6472;
border-radius: 50%;
}
/* Create the indicator (the dot/circle - hidden when not checked) */
.checkmark:after {
content: "";
position: absolute;
display: none;
}
.checkmark:after {
top: 2px;
left: 2px;
width: 8px;
height: 8px;
border-radius: 50%;
background: #FF6472;
}
}
/* Show the indicator (dot/circle) when checked */
.radio-container input:checked ~ .checkmark:after {
display: block;
}
//wishlist icon hover properties
.add-to-wishlist {
@ -2735,7 +2792,6 @@ section.review {
margin-top: 2%;
table tbody tr {
display: grid;
margin-bottom: 20px;
}
}
@ -2754,7 +2810,6 @@ section.review {
//Account content inside a table
.account-table-content {
color: $font-color;
margin-top: 1.4%;
table {
tbody {
@ -2770,6 +2825,64 @@ section.review {
}
}
//address holder and address card
.address-holder {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
flex-wrap: wrap;
width: 100%;
}
.address-card {
width: 260px;
border: 1px solid #E8E8E8;
display: flex;
position: relative;
flex-direction: row;
justify-content: flex-start;
align-items: flex-start;
padding: 25px 1px 22px 15px;
margin-right: 15px;
margin-bottom: 15px;
.control-group {
width: 15px;
height: 15px;
margin-top: 10px;
}
.details {
font-weight: lighter;
margin-left: 15px;
span {
display: block;
margin: 8px;
}
.control-links {
width: 90%;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
.btn {
height: 30px;
}
}
.default-address {
position: absolute;
top: -3px;
right: -3px;
}
}
}
.edit-form {
display: flex;
border: 1px solid $border-color;

View File

@ -122,6 +122,9 @@ return [
'edit' => 'Edit',
'empty' => 'You don\'t have any saved addresses here, please create a new one by clicking the link below.',
'create' => 'Create Address',
'delete' => 'Delete',
'make-default' => 'Make Default',
'default' => 'Default'
],
'create' => [
@ -140,6 +143,10 @@ return [
'page-title' => 'Customer - Edit Address',
'title' => 'Edit Address',
'submit' => 'Edit Address'
],
'delete' => [
'success' => 'Address Successfully Deleted',
'failure' => 'Address Cannot Be Deleted'
]
],

View File

@ -16,62 +16,65 @@
<span class="back-icon"><a href="{{ route('customer.account.index') }}"><i class="icon icon-menu-back"></i></a></span>
<span class="account-heading">{{ __('shop::app.customer.account.address.index.title') }}</span>
@if(!$address->isEmpty())
<span class="account-action">
<a href="{{ route('customer.address.edit') }}">
{{ __('shop::app.customer.account.address.index.edit') }}
</a>
</span>
@if(!$addresses->isEmpty())
<span class="account-action">
<a href="{{ route('customer.address.create') }}">{{ __('shop::app.customer.account.address.index.add') }}</a>
</span>
@else
<span></span>
<span></span>
@endif
<div class="horizontal-rule"></div>
</div>
<div class="account-table-content">
@if($address->isEmpty())
@if($addresses->isEmpty())
<div>{{ __('shop::app.customer.account.address.index.empty') }}</div>
<br/>
<a href="{{ route('customer.address.create') }}">{{ __('shop::app.customer.account.address.index.add') }}</a>
@else
<table>
<tbody>
<tr>
<td>{{ __('shop::app.customer.account.address.create.address1') }}</td>
<td>{{ $address['address1'] }}</td>
</tr>
<div class="address-holder">
@foreach($addresses as $address)
<div class="address-card">
{{-- <div class="control-group">
<label class="radio-container">
<input class="control" type="radio" name="radio">
<span class="checkmark"></span>
</label>
</div> --}}
<tr>
<td>{{ __('shop::app.customer.account.address.create.address2') }}</td>
<td>{{ $address['address2'] }}</td>
</tr>
<div class="details">
<span class="bold">{{ auth()->guard('customer')->user()->name }}</span>
<span>{{ $address['address1'] }}</span>
<span>{{ $address['address2'] }}</span>
<span>{{ $address['country'] }}</span>
<span>{{ $address['state'] }}</span>
<span>{{ $address['city'] }}</span>
<span>{{ $address['postcode'] }}</span>
<tr>
<td>{{ __('shop::app.customer.account.address.create.country') }}</td>
<td>{{ $address['country'] }}</td>
</tr>
<div class="control-links mt-20">
<span>
<a href="{{ route('customer.address.edit', $address['id']) }}"><i class="icon pencil-lg-icon"></i></a>
</span>
<tr>
<td>{{ __('shop::app.customer.account.address.create.state') }}</td>
<td>{{ $address['state'] }}</td>
</tr>
<span>
<a href="{{ route('address.delete', $address['id']) }}"><i class="icon trash-icon"></i></a>
</span>
</div>
<tr>
<td>{{ __('shop::app.customer.account.address.create.city') }}</td>
<td>{{ $address['city'] }}</td>
</tr>
@if($address['default_address'] == 1)
<span class="default-address badge badge-md badge-success">{{ __('shop::app.customer.account.address.index.default') }}</span>
@else
<div class="make-default mt-20">
<a href="{{ route('make.default.address', $address['id']) }}" class="btn btn-md btn-primary">{{ __('shop::app.customer.account.address.index.make-default') }}</a>
</div>
@endif
<tr>
<td>{{ __('shop::app.customer.account.address.create.postcode') }}</td>
<td>{{ $address['postcode'] }}</td>
</tr>
</tbody>
</table>
</div>
</div>
@endforeach
</div>
@endif
</div>
</div>
</div>
@endsection

View File

@ -14,7 +14,7 @@
<span></span>
</div>
<form method="post" action="{{ route('customer.address.edit') }}">
<form method="post" action="{{ route('customer.address.edit', $address['id']) }}">
<div class="account-table-content">
@csrf