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;
use App\Http\Requests\Client\EditClientRequest;
use App\Http\Requests\Client\StoreClientRequest;
use App\Http\Requests\Client\UpdateClientRequest;
use App\Jobs\Client\StoreClient;
use App\Jobs\Client\UpdateClient;
@ -12,6 +13,7 @@ use App\Repositories\ClientRepository;
use App\Utils\Traits\MakesHash;
use App\Utils\Traits\UserSessionAttributes;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Yajra\DataTables\Facades\DataTables;
use Yajra\DataTables\Html\Builder;
@ -101,8 +103,11 @@ class ClientController extends Controller
public function create()
{
$client = new Client;
$client->name = '';
$client->company_id = $this->getCurrentCompanyId();
$client_contact = new ClientContact;
$client_contact->first_name = "";
$client_contact->id = 0;
$client->contacts->add($client_contact);
@ -120,16 +125,22 @@ class ClientController extends Controller
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
public function store(StoreClientRequest $request)
{
$client = StoreClient::dispatchNow($request, new Client);
$client->load('contacts', 'primary_contact');
$client->hashed_id = $this->encodePrimarykey($client->id);
/*
$data = [
'client' => $client,
'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()
{
/* 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 [
'name' => 'required',
'contacts.*.email' => 'email|unique:client_contacts,email'
'unique' => trans('validation.unique', ['attribute' => '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()
{
/* 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';
$contacts = request('contacts');

View File

@ -19,7 +19,7 @@ class ClientContactRepository extends BaseRepository
$contacts = collect($contacts);
/* 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);
});

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");
// Register vue component
vue_1.default.component('vue-toastr', vue_toastr_1.default);
//declare var axios: any;
//declare var Vue: any;
new vue_1.default({
el: '#client_edit',
data: function () {
@ -14982,16 +14980,16 @@ new vue_1.default({
};
},
mounted: function () {
console.log('mounted');
//console.log('mounted')
},
beforeMount: function () {
console.log('before mount');
//console.log('before mount')
},
created: function () {
console.dir('created');
//console.dir('created')
},
updated: function () {
console.dir('updated');
//console.dir('updated')
},
methods: {
remove: function (contact) {
@ -15000,8 +14998,7 @@ new vue_1.default({
},
add: function () {
var _this = this;
console.dir('i will add a contact here');
this.form.contacts.push({ first_name: '', last_name: '', email: '', phone: '', id: -1 });
this.form.contacts.push({ first_name: '', last_name: '', email: '', phone: '', id: 0 });
window.scrollTo(0, document.body.scrollHeight || document.documentElement.scrollHeight);
this.$nextTick(function () {
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.
*
* @param {object} data
*/
Form.prototype.onSuccess = function (data) {
this.update(data);
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");
// Register vue component
vue_1.default.component('vue-toastr', vue_toastr_1.default);
//declare var axios: any;
//declare var Vue: any;
new vue_1.default({
el: '#client_edit',
data: function () {
@ -14982,16 +14980,16 @@ new vue_1.default({
};
},
mounted: function () {
console.log('mounted');
//console.log('mounted')
},
beforeMount: function () {
console.log('before mount');
//console.log('before mount')
},
created: function () {
console.dir('created');
//console.dir('created')
},
updated: function () {
console.dir('updated');
//console.dir('updated')
},
methods: {
remove: function (contact) {
@ -15000,8 +14998,7 @@ new vue_1.default({
},
add: function () {
var _this = this;
console.dir('i will add a contact here');
this.form.contacts.push({ first_name: '', last_name: '', email: '', phone: '', id: -1 });
this.form.contacts.push({ first_name: '', last_name: '', email: '', phone: '', id: 0 });
window.scrollTo(0, document.body.scrollHeight || document.documentElement.scrollHeight);
this.$nextTick(function () {
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.
*
* @param {object} data
*/
Form.prototype.onSuccess = function (data) {
this.update(data);
this.errors.clear();
};
/**

View File

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

View File

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

View File

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

View File

@ -6,10 +6,26 @@
<!-- Breadcrumb-->
{{ 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">
<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>
<li class="nav-item">
<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>
</li>
</ul>
<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 -->
@ -24,33 +40,59 @@
<!-- 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>
</div>
</form>
</div>
<div class="tab-pane fade" id="pills-contact" role="tabpanel" aria-labelledby="pills-contact-tab">
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
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
heard of them, vinyl craft beer blog stumptown. Pitchfork sustainable tofu synth chambray yr.
</div>
</div>
</div>
</main>
<script>
var client_object = {!! $client !!};
var hashed_id = '{{ $hashed_id }}';

View File

@ -4,7 +4,7 @@
<div class="form-group row">
<label for="name" class="col-sm-3 col-form-label text-right">@lang('texts.client_name')</label>
<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>
</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);