Client CRUD with VueJS (#2497)

*  working on js localizations

* remove dependencies

* Pad Hashes to at least 10 characters in length

* Inject JS translations into front end dynamically

* Implement VueJS for Client Edit Page with reactivity

* Conditionally hide rows if not enabled (custom_value)

* Split client template into smaller components

* implementing ui buttons

* CRUD cycles of a client

* Working on Client CRUD - Integrity constraint issues
This commit is contained in:
David Bomba 2018-11-11 00:24:36 +11:00 committed by GitHub
parent 7e57b0f2fd
commit b989cf82b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 181219 additions and 218 deletions

View File

@ -3,6 +3,7 @@
namespace App\Http\Controllers;
use App\Http\Requests\Client\EditClientRequest;
use App\Http\Requests\Client\UpdateClientRequest;
use App\Models\Client;
use Illuminate\Http\Request;
use Yajra\DataTables\Facades\DataTables;
@ -10,7 +11,6 @@ use Yajra\DataTables\Html\Builder;
class ClientController extends Controller
{
/**
* Display a listing of the resource.
*
@ -118,6 +118,7 @@ class ClientController extends Controller
*/
public function edit(EditClientRequest $request)
{
$client = $request->entity(Client::class, request('client'));
$client->load('contacts', 'primary_billing_location', 'primary_shipping_location', 'locations', 'primary_contact');
@ -138,9 +139,21 @@ class ClientController extends Controller
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
public function update(UpdateClientRequest $request, $id)
{
//
\Illuminate\Support\Facades\Log::error(print_r($request->input('contacts'),1));
$client = $request->entity(Client::class, request('client'));
$client->fill($request->all())->save();
$client->contacts()->delete();
$client->contacts()->create($request->input('contacts'));
$client->load('contacts', 'primary_billing_location', 'primary_shipping_location', 'locations', 'primary_contact');
return response()->json($client, 200);
}
/**

View File

@ -0,0 +1,101 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
class TranslationController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$strings = Cache::rememberForever('lang.js', function () {
$lang = config('app.locale');
$files = glob(resource_path('lang/' . $lang . '/*.php'));
$strings = [];
foreach ($files as $file) {
$name = basename($file, '.php');
$strings[$name] = require $file;
}
return $strings;
});
header('Content-Type: text/javascript');
echo('window.i18n = ' . json_encode($strings) . ';');
exit();
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
//
}
}

View File

@ -18,5 +18,15 @@ class EditClientRequest extends Request
// return ! auth()->user(); //todo permissions
}
public function sanitize()
{
$input = $this->all();
//$input['id'] = $this->encodePrimaryKey($input['id']);
//$this->replace($input);
return $this->all();
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Http\Requests\Client;
use App\Http\Requests\Request;
class UpdateClientRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
// return ! auth()->user(); //todo permissions
}
public function rules()
{
return [
'name' => 'required',
];
}
}

View File

@ -3,13 +3,44 @@
namespace App\Models;
use Laracasts\Presenter\PresentableTrait;
use Hashids\Hashids;
use App\Utils\Traits\MakesHash;
class Client extends BaseModel
{
use PresentableTrait;
use MakesHash;
protected $presenter = 'App\Models\Presenters\ClientPresenter';
protected $appends = ['hash_id'];
protected $fillable = [
'name',
'id_number',
'vat_number',
'work_phone',
'custom_value1',
'custom_value2',
'address1',
'address2',
'city',
'state',
'postal_code',
'country_id',
'private_notes',
'size_id',
'industry_id',
'currency_id',
'language_id',
'payment_terms',
'website',
];
public function getHashIdAttribute()
{
return $this->encodePrimaryKey($this->id);
}
public function contacts()
{

View File

@ -20,9 +20,15 @@ class ClientContact extends Authenticatable
* @var array
*/
protected $fillable = [
'first_name', 'last_name', 'email', 'password',
'first_name',
'last_name',
'email',
'password',
'phone',
'custom_value1',
'custom_value2',
];
/**
* The attributes that should be hidden for arrays.
*

View File

@ -36,21 +36,21 @@ trait MakesHash
*/
public function getDbCode($db) : string
{
$hashids = new Hashids();
$hashids = new Hashids('', 10);
return $hashids->encode( str_replace( MultiDB::DB_PREFIX, "", $db ) );
}
public function encodePrimaryKey($value)
{
$hashids = new Hashids();
$hashids = new Hashids('', 10);
return $hashids->encode($value);
}
public function decodePrimaryKey($value)
{
$hashids = new Hashids();
$hashids = new Hashids('', 10);
$decoded_array = $hashids->decode($value);

View File

@ -27,7 +27,6 @@
"laravel/framework": "5.7.*",
"laravel/socialite": "^3.1",
"laravel/tinker": "^1.0",
"mariuzzo/laravel-js-localization": "^1.4",
"nwidart/laravel-modules": "^4.0",
"predis/predis": "^1.1",
"spatie/laravel-html": "^2.19",

131
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "b872ce32e507245bb3568a6a1bd18c51",
"content-hash": "753b0ff8fa34e1e5a54d950d3d780d30",
"packages": [
{
"name": "asgrim/ofxparser",
@ -1473,89 +1473,6 @@
],
"time": "2018-11-01T10:33:16+00:00"
},
{
"name": "mariuzzo/laravel-js-localization",
"version": "v1.4.7",
"source": {
"type": "git",
"url": "https://github.com/rmariuzzo/Laravel-JS-Localization.git",
"reference": "e36ea8dadfa680d862262af2ea4abbe5697bc03e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/rmariuzzo/Laravel-JS-Localization/zipball/e36ea8dadfa680d862262af2ea4abbe5697bc03e",
"reference": "e36ea8dadfa680d862262af2ea4abbe5697bc03e",
"shasum": ""
},
"require": {
"illuminate/config": ">=4.2",
"illuminate/console": ">=4.2",
"illuminate/filesystem": ">=4.2",
"php": ">=5.4.0",
"tedivm/jshrink": "1.0.*"
},
"require-dev": {
"phpunit/phpunit": "4.8.*"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Mariuzzo\\LaravelJsLocalization\\LaravelJsLocalizationServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Mariuzzo\\LaravelJsLocalization\\": "src/Mariuzzo/LaravelJsLocalization/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Rubens Mariuzzo",
"email": "rubens@mariuzzo.com",
"homepage": "https://github.com/rmariuzzo",
"role": "Developer"
},
{
"name": "German Popoter",
"email": "me@gpopoteur.com",
"homepage": "https://github.com/gpopoteur",
"role": "Developer"
},
{
"name": "Galievskiy Dmitriy",
"homepage": "https://github.com/xAockd",
"role": "Developer"
},
{
"name": "Ramon Ackermann",
"homepage": "https://github.com/sboo",
"role": "Developer"
},
{
"name": "Pe Ell",
"homepage": "https://github.com/a-komarev",
"role": "Developer"
}
],
"description": "Laravel Localization in JavaScript",
"homepage": "https://github.com/rmariuzzo/laravel-js-localization",
"keywords": [
"JS",
"i18n",
"javascript",
"lang",
"laravel",
"laravel 5",
"localization"
],
"time": "2017-11-23T04:07:56+00:00"
},
{
"name": "markbaker/complex",
"version": "1.4.7",
@ -3497,52 +3414,6 @@
],
"time": "2018-10-02T16:36:10+00:00"
},
{
"name": "tedivm/jshrink",
"version": "v1.0.1",
"source": {
"type": "git",
"url": "https://github.com/tedious/JShrink.git",
"reference": "7575d9d96f113bc7c1c28ec8231ee086751a9078"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/tedious/JShrink/zipball/7575d9d96f113bc7c1c28ec8231ee086751a9078",
"reference": "7575d9d96f113bc7c1c28ec8231ee086751a9078",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"fabpot/php-cs-fixer": "0.4.0",
"phpunit/phpunit": "4.0.*",
"satooshi/php-coveralls": "dev-master"
},
"type": "library",
"autoload": {
"psr-0": {
"JShrink": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Robert Hafner",
"email": "tedivm@tedivm.com"
}
],
"description": "Javascript Minifier built in PHP",
"homepage": "http://github.com/tedious/JShrink",
"keywords": [
"javascript",
"minifier"
],
"time": "2014-11-11T03:54:14+00:00"
},
{
"name": "tijsverkoyen/css-to-inline-styles",
"version": "2.2.1",

View File

@ -29,8 +29,10 @@
"vue": "^2.5.17"
},
"dependencies": {
"hashids": "^1.2.2",
"laravel-echo": "^1.4.0",
"quill": "^1.3.6",
"socket.io-client": "^2.1.1"
"socket.io-client": "^2.1.1",
"vue-i18n": "^8.3.0"
}
}

16890
public/css/ninja.css vendored

File diff suppressed because one or more lines are too long

16890
public/css/ninja.min.css vendored

File diff suppressed because one or more lines are too long

32566
public/js/ninja.js vendored

File diff suppressed because one or more lines are too long

32566
public/js/ninja.min.js vendored

File diff suppressed because one or more lines are too long

106
resources/assets/js/vendor/I18n.js vendored Normal file
View File

@ -0,0 +1,106 @@
export default class I18n
{
/**
* Initialize a new translation instance.
*
* @param {string} key
* @return {void}
*/
constructor(key = 'translations')
{
this.key = key;
}
/**
* Get and replace the string of the given key.
*
* @param {string} key
* @param {object} replace
* @return {string}
*/
trans(key, replace = {})
{
return this._replace(this._extract(key), replace);
}
/**
* Get and pluralize the strings of the given key.
*
* @param {string} key
* @param {number} count
* @param {object} replace
* @return {string}
*/
trans_choice(key, count = 1, replace = {})
{
let translations = this._extract(key, '|').split('|'), translation;
translations.some(t => translation = this._match(t, count));
translation = translation || (count > 1 ? translations[1] : translations[0]);
return this._replace(translation, replace);
}
/**
* Match the translation limit with the count.
*
* @param {string} translation
* @param {number} count
* @return {string|null}
*/
_match(translation, count)
{
let match = translation.match(/^[\{\[]([^\[\]\{\}]*)[\}\]](.*)/);
if (! match) return;
if (match[1].includes(',')) {
let [from, to] = match[1].split(',');
if (to === '*' && count >= from) {
return match[2];
} else if (from === '*' && count <= to) {
return match[2];
} else if (count >= from && count <= to) {
return match[2];
}
}
return match[1] == count ? match[2] : null;
}
/**
* Replace the placeholders.
*
* @param {string} translation
* @param {object} replace
* @return {string}
*/
_replace(translation, replace)
{
for (let placeholder in replace) {
translation = translation
.replace(`:${placeholder}`, replace[placeholder])
.replace(`:${placeholder.toUpperCase()}`, replace[placeholder].toUpperCase())
.replace(
`:${placeholder.charAt(0).toUpperCase()}${placeholder.slice(1)}`,
replace[placeholder].charAt(0).toUpperCase()+replace[placeholder].slice(1)
);
}
return translation.trim();
}
/**
* The extract helper.
*
* @param {string} key
* @param {mixed} value
* @return {mixed}
*/
_extract(key, value = null)
{
return key.toString().split('.').reduce((t, i) => t[i] || (value || key), window[this.key]);
}
}

File diff suppressed because it is too large Load Diff

16
resources/js/app.js vendored
View File

@ -6,11 +6,20 @@
*/
require('./bootstrap');
window.Vue = require('vue');
/* Development only*/
Vue.config.devtools = true;
window.axios = require('axios');
window.axios.defaults.headers.common = {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-TOKEN' : document.querySelector('meta[name="csrf-token"]').getAttribute('content')
};
/* Allows us to use our native translation easily using {{ trans() }} syntax */
const _ = require('lodash');
Vue.prototype.trans = string => _.get(window.i18n, string);
/**
* Next, we will create a fresh Vue application instance and attach it to
* the page. Then, you may begin adding components to this application
@ -18,6 +27,9 @@ Vue.config.devtools = true;
*/
Vue.component('example-component', require('./components/ExampleComponent.vue'));
Vue.component('client-edit', require('./components/client/ClientEdit.vue'));
Vue.component('client-edit-form', require('./components/client/ClientEditForm.vue'));
Vue.component('contact-edit', require('./components/client/ClientContactEdit.vue'));
window.onload = function () {

View File

@ -7,6 +7,7 @@
<div class="card-body">
I'm an example component.
{{ trans('texts.clients')}}
</div>
</div>
</div>

View File

@ -0,0 +1,55 @@
<template>
<div class="card-body">
<div class="form-group row">
<label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.first_name') }}</label>
<div class="col-sm-9">
<input type="text" name="first_name" :placeholder="trans('texts.first_name')" v-model="contact.first_name" class="form-control" id="first_name">
</div>
</div>
<div class="form-group row">
<label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.last_name') }}</label>
<div class="col-sm-9">
<input type="text" name="last_name" :placeholder="trans('texts.last_name')" v-model="contact.last_name" class="form-control" id="last_name">
</div>
</div>
<div class="form-group row">
<label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.email') }}</label>
<div class="col-sm-9">
<input type="email" name="email" :placeholder="trans('texts.email')" v-model="contact.email" class="form-control" id="email">
</div>
</div>
<div class="form-group row">
<label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.phone') }}</label>
<div class="col-sm-9">
<input type="text" name="phone" :placeholder="trans('texts.phone')" v-model="contact.phone" class="form-control" id="phone">
</div>
</div>
<div class="form-group row" v-if="contact.custom_value1">
<label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.custom_value1') }}</label>
<div class="col-sm-9">
<input type="text" name="custom_value1" :placeholder="trans('texts.custom_value1')" v-model="contact.custom_value1" class="form-control" id="custom_value1">
</div>
</div>
<div class="form-group row" v-if="contact.custom_value1">
<label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.custom_value2') }}</label>
<div class="col-sm-9">
<input type="text" name="custom_value2" :placeholder="trans('texts.custom_value2')" v-model="contact.custom_value2" class="form-control" id="custom_value2">
</div>
</div>
<div class="float-right">
<button type="button" class="btn btn-danger" v-on:click="$emit('remove',contact.id)"> {{ trans('texts.remove_contact') }}</button>
</div>
</div>
</template>
<script>
export default {
props: ['contact']
}
</script>

View File

@ -0,0 +1,51 @@
<template>
<div class="card-body">
<div class="form-group row">
<label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.client_name') }}</label>
<div class="col-sm-9">
<input type="text" name="name" :placeholder="trans('texts.client_name')" v-model="client.name" class="form-control" id="name">
</div>
<div v-if="errors && errors.name" class="text-danger">{{ errors.name[0] }}</div>
</div>
<div class="form-group row">
<label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.id_number') }}</label>
<div class="col-sm-9">
<input type="text" name="id_number" :placeholder="trans('texts.id_number')" v-model="client.id_number" class="form-control" id="id_number">
</div>
</div>
<div class="form-group row">
<label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.vat_number') }}</label>
<div class="col-sm-9">
<input type="text" name="vat_number" :placeholder="trans('texts.vat_number')" v-model="client.vat_number" class="form-control" id="vat_number">
</div>
</div>
<div class="form-group row">
<label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.website') }}</label>
<div class="col-sm-9">
<input type="text" name="website" :placeholder="trans('texts.website')" v-model="client.website" class="form-control" id="websites">
</div>
</div>
<div class="form-group row" v-if="client.custom_value1">
<label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.custom_value1') }}</label>
<div class="col-sm-9">
<input type="text" name="custom_value1" :placeholder="trans('texts.custom_value1')" v-model="client.custom_value1" class="form-control" id="custom_value1">
</div>
</div>
<div class="form-group row">
<label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.custom_value2') }}</label>
<div class="col-sm-9">
<input type="text" name="custom_value2" :placeholder="trans('texts.custom_value2')" v-model="client.custom_value2" class="form-control" id="custom_value2">
</div>
</div>
</div>
</template>
<script>
export default {
props: ['client','errors']
}
</script>

View File

@ -0,0 +1,104 @@
<template>
<form @submit.prevent="submit">
<div class="container-fluid">
<div class="row form-group">
<div class="col-md-12">
<span class="float-right">
<div class="btn-group ml-2">
<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 dropdown-toggle dropdown-toggle-split" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="sr-only">Toggle Dropdown</span>
</button>
<div class="dropdown-menu">
<a class="dropdown-item" href="#"><i class="fa fa-plus-circle"></i> {{ trans('texts.add_contact') }}</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#">{{ trans('texts.archive_client') }}</a>
<a class="dropdown-item" href="#">{{ trans('texts.delete_client') }}</a>
</div>
</div>
</span>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header bg-primary2">{{ trans('texts.edit_client') }}</div>
<client-edit :client="client" :errors="errors"></client-edit>
</div>
</div>
<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"><i class="fa fa-plus-circle"></i> {{ trans('texts.add_contact') }}</button>
</span>
</div>
<contact-edit v-for="(contact, index) in client.contacts"
v-bind:contact="contact"
v-bind:index="index"
:key="contact.id"
@remove="remove"></contact-edit>
</div>
</div>
</div>
</div>
</form>
</template>
<script>
export default {
data: function () {
return {
'client': [],
'errors': [],
}
},
props: {
clientdata: {
type: [Object,Array],
default: () => []
}
},
beforeMount: function () {
this.client = this.clientdata;
},
methods: {
remove (itemId) {
this.client.contacts = this.client.contacts.filter(function (item) {
return itemId != item.id;
});
},
submit() {
this.errors = {};
axios.put('/clients/' + this.client.hash_id, this.client).then(response => {
this.client = response.data;
console.dir(response);
}).catch(error => {
if (error.response.status === 422) {
this.errors = error.response.data.errors || {};
}
});
},
},
created:function() {
//console.dir('created');
},
updated:function() {
//console.dir('updated');
}
}
</script>

View File

@ -1,43 +1,12 @@
@extends('layouts.master', ['header' => $header])
@section('head')
@parent
<link rel="stylesheet" href="//cdn.datatables.net/1.10.18/css/dataTables.bootstrap4.min.css">
<script src="//cdn.datatables.net/1.10.18/js/jquery.dataTables.min.js"></script>
<script src="//cdn.datatables.net/1.10.18/js/dataTables.bootstrap4.min.js"></script>
@endsection
@section('body')
<main class="main">
<main class="main" id="app">
<!-- Breadcrumb-->
{{ Breadcrumbs::render('clients.edit', $client) }}
<div class="container-fluid" >
<div class="row">
<div class="col-lg-12">
{{ html()->form('PUT', route('signup.submit'))->open() }}
<example-component></example-component>
</div>
</div>
{{ html()->form()->close() }}
</div>
<client-edit-form v-bind:clientdata="{{ $client }}"></client-edit-form>
</main>
@endsection
@endsection

View File

@ -7,7 +7,6 @@
<script src="//cdn.datatables.net/1.10.18/js/dataTables.bootstrap4.min.js"></script>
@endsection
@section('body')
@parent
<main class="main" >
@ -26,15 +25,9 @@
</div>
</div>
</main>
@endsection
@section('footer')
@parent
{!! $html->scripts() !!}
@endsection
@endsection

View File

@ -3,11 +3,17 @@
@section('body')
<main class="main">
<!-- Breadcrumb-->
{{ Breadcrumbs::render('dashboard') }}
{{ Breadcrumbs::render('dashboard') }}
<div class="container-fluid">
<div class="row">
<div class="col-lg-12">
<div class="col-lg-6">test</div>
<div class="col-lg-6">test2</div>
</div>
</div>
</div>
</main>
@endsection

View File

@ -9,6 +9,4 @@
</body>
</html>

View File

@ -63,28 +63,34 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
<script src=" {{ mix('/js/ninja.min.js') }}"></script>
<script src="/js/lang.js"></script>
<style type="text/css">
.bg-primary2 {
background-color: #167090 !important;
color: #fff;
}
a.bg-primary2:hover, a.bg-primary:focus,
button.bg-primary:hover,
button.bg-primary:focus {
background-color: #56b3d4 !important;
}
</style>
@yield('head')
</head>
@include('header', $header)
@yield('header')
@include('sidebar')
@yield('sidebar')
@section('body')
@yield('body')
@include('dashboard.aside')
@include('footer')
@yield('footer')
</html>

View File

@ -1,5 +1,4 @@
<?php
/*
* Authentication Routes Laravel Defaults... replaces //Auth::routes();
*/
@ -71,6 +70,12 @@ Route::group(['prefix' => 'contact', 'middleware' => 'auth:contact'], function
});
/*
* Injects users translation strings in json format for frontend consumption.
*/
Route::get('js/lang.js', 'TranslationController@index')->name('assets.lang');
/* Dev Playground
Route::get('/mailable', function () {

2
webpack.mix.js vendored
View File

@ -15,8 +15,6 @@ mix.js('resources/js/app.js', 'public/js/vendor');
mix.scripts([
'node_modules/@coreui/coreui/dist/js/coreui.js',
//'node_modules/vue/dist/vue.min.js',
//'node_modules/vue/dist/vue.js',
'public/js/vendor/app.js'
], 'public/js/ninja.js');