Create client (#2543)

* Fix for comparing delete contacts change diffKeys to diff()

* Client create

* Create client

* Create client
This commit is contained in:
David Bomba 2018-12-07 21:57:20 +11:00 committed by GitHub
parent 7ee295ec44
commit 17a7f0564e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 4421 additions and 190 deletions

View File

@ -3,6 +3,7 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Http\Requests\Client\EditClientRequest; use App\Http\Requests\Client\EditClientRequest;
use App\Http\Requests\Client\StoreClientRequest;
use App\Http\Requests\Client\UpdateClientRequest; use App\Http\Requests\Client\UpdateClientRequest;
use App\Jobs\Client\StoreClient; use App\Jobs\Client\StoreClient;
use App\Jobs\Client\UpdateClient; use App\Jobs\Client\UpdateClient;
@ -12,6 +13,7 @@ use App\Repositories\ClientRepository;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use App\Utils\Traits\UserSessionAttributes; use App\Utils\Traits\UserSessionAttributes;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Yajra\DataTables\Facades\DataTables; use Yajra\DataTables\Facades\DataTables;
use Yajra\DataTables\Html\Builder; use Yajra\DataTables\Html\Builder;
@ -101,8 +103,11 @@ class ClientController extends Controller
public function create() public function create()
{ {
$client = new Client; $client = new Client;
$client->name = '';
$client->company_id = $this->getCurrentCompanyId();
$client_contact = new ClientContact; $client_contact = new ClientContact;
$client_contact->first_name = ""; $client_contact->first_name = "";
$client_contact->id = 0;
$client->contacts->add($client_contact); $client->contacts->add($client_contact);
@ -120,16 +125,22 @@ class ClientController extends Controller
* @param \Illuminate\Http\Request $request * @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response * @return \Illuminate\Http\Response
*/ */
public function store(Request $request) public function store(StoreClientRequest $request)
{ {
$client = StoreClient::dispatchNow($request, new Client); $client = StoreClient::dispatchNow($request, new Client);
$client->load('contacts', 'primary_contact');
$client->hashed_id = $this->encodePrimarykey($client->id);
/*
$data = [ $data = [
'client' => $client, 'client' => $client,
'hashed_id' => $this->encodePrimarykey($client->id) 'hashed_id' => $this->encodePrimarykey($client->id)
]; ];
*/
Log::error(print_r($client,1));
return response()->json($client, 200);
return view('client.edit', $data);
} }
/** /**

View File

@ -19,12 +19,27 @@ class StoreClientRequest extends Request
} }
public function rules() public function rules()
{
/* Ensure we have a client name, and that all emails are unique*/
$rules['name'] = 'required';
$contacts = request('contacts');
for ($i = 0; $i < count($contacts); $i++) {
$rules['contacts.' . $i . '.email'] = 'required|email|unique:client_contacts,email,' . isset($contacts[$i]['id']);
}
return $rules;
}
public function messages()
{ {
return [ return [
'name' => 'required', 'unique' => trans('validation.unique', ['attribute' => 'email']),
'contacts.*.email' => 'email|unique:client_contacts,email' //'required' => trans('validation.required', ['attribute' => 'email']),
'contacts.*.email.required' => trans('validation.email', ['attribute' => 'email']),
]; ];
} }

View File

@ -22,8 +22,7 @@ class UpdateClientRequest extends Request
public function rules() public function rules()
{ {
/* Ensure we have a client name, and that all emails are unique*/
/* Ensure we have a client name, and that all emails are unique!!!!!*/
$rules['name'] = 'required'; $rules['name'] = 'required';
$contacts = request('contacts'); $contacts = request('contacts');

View File

@ -29,7 +29,7 @@ class ClientContact extends Authenticatable
protected $guarded = [ protected $guarded = [
'id', 'id',
]; ];
protected $hidden = [ protected $hidden = [
'password', 'password',
'remember_token', 'remember_token',

View File

@ -19,7 +19,7 @@ class ClientContactRepository extends BaseRepository
$contacts = collect($contacts); $contacts = collect($contacts);
/* Get array of IDs which have been removed from the contacts array and soft delete each contact */ /* Get array of IDs which have been removed from the contacts array and soft delete each contact */
collect($client->contacts->pluck('id'))->diffKeys($contacts->pluck('id'))->each(function($contact){ collect($client->contacts->pluck('id'))->diff($contacts->pluck('id'))->each(function($contact){
ClientContact::destroy($contact); ClientContact::destroy($contact);
}); });

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -14972,8 +14972,6 @@ var vue_toastr_1 = __importDefault(__webpack_require__("./node_modules/vue-toast
__webpack_require__("./node_modules/vue-toastr/src/vue-toastr.scss"); __webpack_require__("./node_modules/vue-toastr/src/vue-toastr.scss");
// Register vue component // Register vue component
vue_1.default.component('vue-toastr', vue_toastr_1.default); vue_1.default.component('vue-toastr', vue_toastr_1.default);
//declare var axios: any;
//declare var Vue: any;
new vue_1.default({ new vue_1.default({
el: '#client_edit', el: '#client_edit',
data: function () { data: function () {
@ -14982,16 +14980,16 @@ new vue_1.default({
}; };
}, },
mounted: function () { mounted: function () {
console.log('mounted'); //console.log('mounted')
}, },
beforeMount: function () { beforeMount: function () {
console.log('before mount'); //console.log('before mount')
}, },
created: function () { created: function () {
console.dir('created'); //console.dir('created')
}, },
updated: function () { updated: function () {
console.dir('updated'); //console.dir('updated')
}, },
methods: { methods: {
remove: function (contact) { remove: function (contact) {
@ -15000,8 +14998,7 @@ new vue_1.default({
}, },
add: function () { add: function () {
var _this = this; var _this = this;
console.dir('i will add a contact here'); this.form.contacts.push({ first_name: '', last_name: '', email: '', phone: '', id: 0 });
this.form.contacts.push({ first_name: '', last_name: '', email: '', phone: '', id: -1 });
window.scrollTo(0, document.body.scrollHeight || document.documentElement.scrollHeight); window.scrollTo(0, document.body.scrollHeight || document.documentElement.scrollHeight);
this.$nextTick(function () { this.$nextTick(function () {
var index = _this.form.contacts.length - 1; var index = _this.form.contacts.length - 1;
@ -15209,12 +15206,24 @@ var Form = /** @class */ (function () {
}); });
}); });
}; };
/**
* Update form data on success
*
* @param {object} data
*/
Form.prototype.update = function (data) {
this.originalData = data;
for (var field in data) {
this[field] = data[field];
}
};
/** /**
* Handle a successful form submission. * Handle a successful form submission.
* *
* @param {object} data * @param {object} data
*/ */
Form.prototype.onSuccess = function (data) { Form.prototype.onSuccess = function (data) {
this.update(data);
this.errors.clear(); this.errors.clear();
}; };
/** /**

View File

@ -14972,8 +14972,6 @@ var vue_toastr_1 = __importDefault(__webpack_require__("./node_modules/vue-toast
__webpack_require__("./node_modules/vue-toastr/src/vue-toastr.scss"); __webpack_require__("./node_modules/vue-toastr/src/vue-toastr.scss");
// Register vue component // Register vue component
vue_1.default.component('vue-toastr', vue_toastr_1.default); vue_1.default.component('vue-toastr', vue_toastr_1.default);
//declare var axios: any;
//declare var Vue: any;
new vue_1.default({ new vue_1.default({
el: '#client_edit', el: '#client_edit',
data: function () { data: function () {
@ -14982,16 +14980,16 @@ new vue_1.default({
}; };
}, },
mounted: function () { mounted: function () {
console.log('mounted'); //console.log('mounted')
}, },
beforeMount: function () { beforeMount: function () {
console.log('before mount'); //console.log('before mount')
}, },
created: function () { created: function () {
console.dir('created'); //console.dir('created')
}, },
updated: function () { updated: function () {
console.dir('updated'); //console.dir('updated')
}, },
methods: { methods: {
remove: function (contact) { remove: function (contact) {
@ -15000,8 +14998,7 @@ new vue_1.default({
}, },
add: function () { add: function () {
var _this = this; var _this = this;
console.dir('i will add a contact here'); this.form.contacts.push({ first_name: '', last_name: '', email: '', phone: '', id: 0 });
this.form.contacts.push({ first_name: '', last_name: '', email: '', phone: '', id: -1 });
window.scrollTo(0, document.body.scrollHeight || document.documentElement.scrollHeight); window.scrollTo(0, document.body.scrollHeight || document.documentElement.scrollHeight);
this.$nextTick(function () { this.$nextTick(function () {
var index = _this.form.contacts.length - 1; var index = _this.form.contacts.length - 1;
@ -15209,12 +15206,24 @@ var Form = /** @class */ (function () {
}); });
}); });
}; };
/**
* Update form data on success
*
* @param {object} data
*/
Form.prototype.update = function (data) {
this.originalData = data;
for (var field in data) {
this[field] = data[field];
}
};
/** /**
* Handle a successful form submission. * Handle a successful form submission.
* *
* @param {object} data * @param {object} data
*/ */
Form.prototype.onSuccess = function (data) { Form.prototype.onSuccess = function (data) {
this.update(data);
this.errors.clear(); this.errors.clear();
}; };
/** /**

View File

@ -1,77 +1,82 @@
//import * as Vue from 'vue'; //import * as Vue from 'vue';
import Vue from 'vue'; import Vue from 'vue';
import axios from 'axios'; import axios from 'axios';
import Form from '../utils/form';
import Client from '../models/client-model';
// import Toastr
import Toastr from 'vue-toastr';
// import toastr scss file: need webpack sass-loader
require('vue-toastr/src/vue-toastr.scss');
// Register vue component
Vue.component('vue-toastr',Toastr);
declare var client_object: any; declare var client_object: any;
declare var hashed_id: string;
new Vue({ new Vue({
el : '#client_create', el : '#client_create',
data: function () { data: function () {
return { return {
'client': [], form: new Form(<Client>client_object)
'errors': [],
} }
}, },
mounted(this: any) { mounted(this: any) {
//this.client = {!! $client !!}; //console.log('mounted')
this.client = client_object;
console.dir(this.client);
}, },
beforeMount: function () { beforeMount: function () {
console.log('before mount') //console.log('before mount')
}, },
created:function() { created:function() {
console.dir('created') //console.dir('created')
}, },
updated:function() { updated:function() {
console.dir('updated') //console.dir('updated')
}, },
methods:{ methods:{
remove(this: any, contact:any){ remove(this:any, contact:any){
let index = this.client.contacts.indexOf(contact); let index = this.form.contacts.indexOf(contact);
this.client.contacts.splice(index, 1); this.form.contacts.splice(index, 1);
}, },
add(this: any){ add(this: any){
console.dir('i will add a contact here') this.form.contacts.push({first_name: '', last_name: '', email: '', phone: '', id: 0});
this.client.contacts.push({first_name: '', last_name: '', email: '', phone: '', id: -1});
window.scrollTo(0, document.body.scrollHeight || document.documentElement.scrollHeight); window.scrollTo(0, document.body.scrollHeight || document.documentElement.scrollHeight);
this.$nextTick(() => { this.$nextTick(() => {
let index = this.client.contacts.length - 1; let index = this.form.contacts.length - 1;
let input = this.$refs.first_name[index]; let input = this.$refs.first_name[index];
input.focus(); input.focus();
}); });
}, },
submit(this: any) { onSubmit() {
this.errors = {}; this.form.post('/clients/')
.then(response => {
axios.post('/clients/', this.client).then(response => { this.$root.$refs.toastr.s("Created client"); //how are we going to handle translations here?
// axios.put('/clients/' + {{ $client->present()->id }}, this.client).then(response => {
this.client = response.data; window.location.href = '/clients/' + this.form.hashed_id + '/edit';
}).catch(error => {
if (error.response.status === 422) { })
this.errors = error.response.data.errors || {}; .catch(error => {
}
else if(error.response.status === 419) { this.$root.$refs.toastr.e("Error saving client");
//csrf token has expired, we'll need to force a page reload
} });
});
}, },
copy(type: any) { copy(type: any) {
if(type.includes('copy_billing')){ if(type.includes('copy_billing')){
this.client.shipping_address1 = this.client.address1; this.form.shipping_address1 = this.form.address1;
this.client.shipping_address2 = this.client.address2; this.form.shipping_address2 = this.form.address2;
this.client.shipping_city = this.client.city; this.form.shipping_city = this.form.city;
this.client.shipping_state = this.client.state; this.form.shipping_state = this.form.state;
this.client.shipping_postal_code = this.client.postal_code; this.form.shipping_postal_code = this.form.postal_code;
this.client.shipping_country_id = this.client.country_id; this.form.shipping_country_id = this.form.country_id;
}else { }else {
this.client.address1 = this.client.shipping_address1; this.form.address1 = this.form.shipping_address1;
this.client.address2 = this.client.shipping_address2; this.form.address2 = this.form.shipping_address2;
this.client.city = this.client.shipping_city; this.form.city = this.form.shipping_city;
this.client.state = this.client.shipping_state; this.form.state = this.form.shipping_state;
this.client.postal_code = this.client.shipping_postal_code; this.form.postal_code = this.form.shipping_postal_code;
this.client.country_id = this.client.shipping_country_id; this.form.country_id = this.form.shipping_country_id;
} }
} }
} }
}); });

View File

@ -2,6 +2,8 @@
import Vue from 'vue'; import Vue from 'vue';
import axios from 'axios'; import axios from 'axios';
import Form from '../utils/form'; import Form from '../utils/form';
import Client from '../models/client-model';
// import Toastr // import Toastr
import Toastr from 'vue-toastr'; import Toastr from 'vue-toastr';
// import toastr scss file: need webpack sass-loader // import toastr scss file: need webpack sass-loader
@ -11,27 +13,26 @@ Vue.component('vue-toastr',Toastr);
declare var client_object: any; declare var client_object: any;
declare var hashed_id: string; declare var hashed_id: string;
//declare var axios: any;
//declare var Vue: any;
new Vue({ new Vue({
el : '#client_edit', el : '#client_edit',
data: function () { data: function () {
return { return {
form: new Form(client_object) form: new Form(<Client>client_object)
} }
}, },
mounted(this: any) { mounted(this: any) {
console.log('mounted') //console.log('mounted')
}, },
beforeMount: function () { beforeMount: function () {
console.log('before mount') //console.log('before mount')
}, },
created:function() { created:function() {
console.dir('created') //console.dir('created')
}, },
updated:function() { updated:function() {
console.dir('updated') //console.dir('updated')
}, },
methods:{ methods:{
remove(this:any, contact:any){ remove(this:any, contact:any){
@ -39,8 +40,7 @@ declare var hashed_id: string;
this.form.contacts.splice(index, 1); this.form.contacts.splice(index, 1);
}, },
add(this: any){ add(this: any){
console.dir('i will add a contact here') this.form.contacts.push({first_name: '', last_name: '', email: '', phone: '', id: 0});
this.form.contacts.push({first_name: '', last_name: '', email: '', phone: '', id: -1});
window.scrollTo(0, document.body.scrollHeight || document.documentElement.scrollHeight); window.scrollTo(0, document.body.scrollHeight || document.documentElement.scrollHeight);
this.$nextTick(() => { this.$nextTick(() => {
let index = this.form.contacts.length - 1; let index = this.form.contacts.length - 1;

View File

@ -136,13 +136,27 @@ export default class Form {
} }
/**
* Update form data on success
*
* @param {object} data
*/
update(data)
{
this.originalData = data;
for (let field in data) {
this[field] = data[field];
}
}
/** /**
* Handle a successful form submission. * Handle a successful form submission.
* *
* @param {object} data * @param {object} data
*/ */
onSuccess(data) { onSuccess(data) {
this.update(data);
this.errors.clear(); this.errors.clear();
} }

View File

@ -6,9 +6,11 @@
<!-- Breadcrumb--> <!-- Breadcrumb-->
{{ Breadcrumbs::render('clients.create') }} {{ Breadcrumbs::render('clients.create') }}
<form @submit.prevent="submit"> <form @submit.prevent="onSubmit" @keydown="form.errors.clear($event.target.name)">
<div class="container-fluid"> <div class="container-fluid">
<vue-toastr ref="toastr"></vue-toastr>
<div class="row"> <div class="row">
<!-- Client Details and Address Column --> <!-- Client Details and Address Column -->
<div class="col-md-6"> <div class="col-md-6">
@ -29,7 +31,7 @@
</span> </span>
</div> </div>
<template v-for="contact in client.contacts"> <template v-for="(contact, key, index) in form.contacts">
@include('client.partial.contact_details') @include('client.partial.contact_details')
</template> </template>
@ -40,7 +42,7 @@
<div class="row"> <div class="row">
<div class="col-md-12 text-center"> <div class="col-md-12 text-center">
<button class="btn btn-lg btn-success" type="button" @click="submit"><i class="fa fa-save"></i> {{ trans('texts.save') }}</button> <button class="btn btn-lg btn-success" type="button" @click="onSubmit"><i class="fa fa-save"></i> {{ trans('texts.save') }}</button>
</div> </div>
</div> </div>
@ -51,6 +53,7 @@
</main> </main>
<script> <script>
var client_object = {!! $client !!}; var client_object = {!! $client !!};
var hashed_id = '';
</script> </script>
<script defer src=" {{ mix('/js/client_create.min.js') }}"></script> <script defer src=" {{ mix('/js/client_create.min.js') }}"></script>

View File

@ -6,51 +6,93 @@
<!-- Breadcrumb--> <!-- Breadcrumb-->
{{ Breadcrumbs::render('clients.edit', $client) }} {{ Breadcrumbs::render('clients.edit', $client) }}
<form @submit.prevent="onSubmit" @keydown="form.errors.clear($event.target.name)"> <vue-toastr ref="toastr"></vue-toastr>
<div class="container-fluid"> <div class="container-fluid">
<vue-toastr ref="toastr"></vue-toastr> <div class="col" style="padding: 0px;">
<ul class="nav nav-pills mb-1" id="pills-tab" role="tablist">
<li class="nav-item">
<a class="nav-link active show" id="pills-home-tab" data-toggle="pill" href="#pills-home" role="tab" aria-controls="pills-home" aria-selected="true"><i class="icon-user"></i> Client</a>
</li>
<div class="row"> <li class="nav-item">
<!-- Client Details and Address Column --> <a class="nav-link" id="pills-contact-tab" data-toggle="pill" href="#pills-contact" role="tab" aria-controls="pills-contact" aria-selected="false"><i class="icon-settings"></i> Settings</a>
<div class="col-md-6"> </li>
</ul>
@include('client.partial.client_details', $client)
@include('client.partial.client_location') <div class="tab-content" id="pills-tabContent">
<div class="tab-pane fade active show" id="pills-home" role="tabpanel" aria-labelledby="pills-home-tab" style="background-color: #e4e5e6; padding: 0px;">
<form @submit.prevent="onSubmit" @keydown="form.errors.clear($event.target.name)">
<div class="row">
<!-- Client Details and Address Column -->
<div class="col-md-6">
@include('client.partial.client_details', $client)
@include('client.partial.client_location')
</div>
<!-- End Client Details and Address Column -->
<!-- Contact Details Column -->
<div class="col-md-6">
<div class="card">
<div class="card-header bg-primary2">{{ trans('texts.contact_information') }}
<span class="float-right">
<button type="button" class="btn btn-primary btn-sm" @click="add()"><i class="fa fa-plus-circle"></i> {{ trans('texts.add_contact') }}</button>
</span>
</div>
<template v-for="(contact, key, index) in form.contacts">
@include('client.partial.contact_details')
</template>
</div>
</div>
<!-- End Contact Details Column -->
</div>
<div class="row">
<div class="col-md-12 text-center">
<button class="btn btn-lg btn-success" type="button" @click="onSubmit"><i class="fa fa-save"></i> {{ trans('texts.save') }}</button>
</div>
</div>
</form>
</div> </div>
<!-- End Client Details and Address Column -->
<!-- Contact Details Column --> <div class="tab-pane fade" id="pills-contact" role="tabpanel" aria-labelledby="pills-contact-tab">
<div class="col-md-6">
<div class="card"> Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy
<div class="card-header bg-primary2">{{ trans('texts.contact_information') }} hoodie helvetica. DIY synth PBR banksy irony. Leggings gentrify squid 8-bit cred pitchfork. Williamsburg banh mi whatever gluten-free, carles pitchfork biodiesel fixie etsy retro mlkshk vice blog. Scenester cred you probably haven't
<span class="float-right"> heard of them, vinyl craft beer blog stumptown. Pitchfork sustainable tofu synth chambray yr.
<button type="button" class="btn btn-primary btn-sm" @click="add()"><i class="fa fa-plus-circle"></i> {{ trans('texts.add_contact') }}</button>
</span>
</div>
<template v-for="(contact, key, index) in form.contacts">
@include('client.partial.contact_details')
</template>
</div>
</div>
<!-- End Contact Details Column -->
</div>
<div class="row">
<div class="col-md-12 text-center">
<button class="btn btn-lg btn-success" type="button" @click="onSubmit"><i class="fa fa-save"></i> {{ trans('texts.save') }}</button>
</div> </div>
</div>
</div>
</div> </div>
</form>
</main> </main>
<script> <script>
var client_object = {!! $client !!}; var client_object = {!! $client !!};
var hashed_id = '{{ $hashed_id }}'; var hashed_id = '{{ $hashed_id }}';

View File

@ -4,7 +4,7 @@
<div class="form-group row"> <div class="form-group row">
<label for="name" class="col-sm-3 col-form-label text-right">@lang('texts.client_name')</label> <label for="name" class="col-sm-3 col-form-label text-right">@lang('texts.client_name')</label>
<div class="col-sm-9"> <div class="col-sm-9">
<input name="name" placeholder="@lang('texts.name')" class="form-control" v-model="form.name" value="{{ $client->name }}"> <input name="name" placeholder="@lang('texts.name')" class="form-control" v-model="form.name">
<div v-if="form.errors.has('name')" class="text-danger" v-text="form.errors.get('name')"></div> <div v-if="form.errors.has('name')" class="text-danger" v-text="form.errors.get('name')"></div>
</div> </div>
</div> </div>

View File

@ -0,0 +1,37 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Print Table</title>
<meta charset="UTF-8">
<meta name=description content="">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Bootstrap CSS -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
<style>
body {margin: 20px}
</style>
</head>
<body>
<table class="table table-bordered table-condensed table-striped">
@foreach($data as $row)
@if ($row == reset($data))
<tr>
@foreach($row as $key => $value)
<th>{!! $key !!}</th>
@endforeach
</tr>
@endif
<tr>
@foreach($row as $key => $value)
@if(is_string($value) || is_numeric($value))
<td>{!! $value !!}</td>
@else
<td></td>
@endif
@endforeach
</tr>
@endforeach
</table>
</body>
</html>

View File

@ -0,0 +1 @@
(function(window,$){window.LaravelDataTables=window.LaravelDataTables||{};window.LaravelDataTables["%1$s"]=$("#%1$s").DataTable(%2$s);})(window,jQuery);