mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-24 02:14:21 -04:00
Modules, VueJS (#2552)
* Fix for comparing delete contacts change diffKeys to diff() * Client create * Client Settings * Working on localization * Refactor DataTables * protyping blade vs pure vue * Rebuild test module * Generic notes module * Small Client Notes Module * Tests for TabMenu Trait * implements tab pills in client screen * Integrate Modules
This commit is contained in:
parent
17a7f0564e
commit
bdb0f43d33
0
Modules/Notes/Config/.gitkeep
Normal file
0
Modules/Notes/Config/.gitkeep
Normal file
5
Modules/Notes/Config/config.php
Normal file
5
Modules/Notes/Config/config.php
Normal file
@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'name' => 'Notes'
|
||||
];
|
0
Modules/Notes/Console/.gitkeep
Normal file
0
Modules/Notes/Console/.gitkeep
Normal file
0
Modules/Notes/Database/Migrations/.gitkeep
Normal file
0
Modules/Notes/Database/Migrations/.gitkeep
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class NotesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('notes', function ($table) {
|
||||
$table->increments('id');
|
||||
$table->unsignedInteger('client_id')->index();
|
||||
$table->unsignedInteger('user_id')->index();
|
||||
$table->string('description');
|
||||
$table->timestamps();
|
||||
$table->foreign('client_id')->references('id')->on('clients')->onDelete('cascade');
|
||||
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
0
Modules/Notes/Database/Seeders/.gitkeep
Normal file
0
Modules/Notes/Database/Seeders/.gitkeep
Normal file
21
Modules/Notes/Database/Seeders/NotesDatabaseSeeder.php
Normal file
21
Modules/Notes/Database/Seeders/NotesDatabaseSeeder.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Notes\Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class NotesDatabaseSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
Model::unguard();
|
||||
|
||||
// $this->call("OthersTableSeeder");
|
||||
}
|
||||
}
|
0
Modules/Notes/Database/factories/.gitkeep
Normal file
0
Modules/Notes/Database/factories/.gitkeep
Normal file
0
Modules/Notes/Entities/.gitkeep
Normal file
0
Modules/Notes/Entities/.gitkeep
Normal file
18
Modules/Notes/Entities/Note.php
Normal file
18
Modules/Notes/Entities/Note.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Notes\Entities;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Note extends Model
|
||||
{
|
||||
protected $guarded = [
|
||||
'id',
|
||||
];
|
||||
|
||||
public function client()
|
||||
{
|
||||
$this->hasOne(App\Models\Client::class);
|
||||
}
|
||||
|
||||
}
|
0
Modules/Notes/Http/Controllers/.gitkeep
Normal file
0
Modules/Notes/Http/Controllers/.gitkeep
Normal file
117
Modules/Notes/Http/Controllers/NotesController.php
Normal file
117
Modules/Notes/Http/Controllers/NotesController.php
Normal file
@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Notes\Http\Controllers;
|
||||
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\UserSessionAttributes;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Modules\Notes\Entities\Note;
|
||||
use Nwidart\Modules\Facades\Module;
|
||||
use Yajra\DataTables\Html\Builder;
|
||||
|
||||
class NotesController extends Controller
|
||||
{
|
||||
use UserSessionAttributes;
|
||||
use MakesHash;
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
* @return Response
|
||||
*/
|
||||
public function index(Builder $builder)
|
||||
{
|
||||
if (request()->ajax()) {
|
||||
|
||||
$notes = Note::query()->where('company_id', '=', $this->getCurrentCompanyId());
|
||||
|
||||
return DataTables::of($notes->get())
|
||||
->addColumn('created_at', function ($note) {
|
||||
return $note->created_at;
|
||||
})
|
||||
->addColumn('description', function ($note) {
|
||||
return $note->description;
|
||||
})
|
||||
->addColumn('action', function ($note) {
|
||||
return '<a href="/notes/'. $this->encodePrimaryKey($note->id) .'/edit" class="btn btn-xs btn-primary"><i class="glyphicon glyphicon-edit"></i> Edit</a>';
|
||||
})
|
||||
->addColumn('checkbox', function ($note){
|
||||
return '<input type="checkbox" name="bulk" value="'. $note->id .'"/>';
|
||||
})
|
||||
->rawColumns(['checkbox', 'action'])
|
||||
->make(true);
|
||||
}
|
||||
|
||||
$builder->addAction();
|
||||
$builder->addCheckbox();
|
||||
|
||||
$html = $builder->columns([
|
||||
['data' => 'created_at', 'name' => 'checkbox', 'title' => '', 'searchable' => false, 'orderable' => false],
|
||||
['data' => 'description', 'name' => 'name', 'title' => trans('texts.name'), 'visible'=> true],
|
||||
['data' => 'action', 'name' => 'action', 'title' => '', 'searchable' => false, 'orderable' => false],
|
||||
]);
|
||||
|
||||
$builder->ajax([
|
||||
'url' => route('notes.index'),
|
||||
'type' => 'GET',
|
||||
'data' => 'function(d) { d.key = "value"; }',
|
||||
]);
|
||||
|
||||
$data['html'] = $html;
|
||||
|
||||
return view('notes::index', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
* @return Response
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
return view('notes::create');
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the specified resource.
|
||||
* @return Response
|
||||
*/
|
||||
public function show()
|
||||
{
|
||||
return view('notes::show');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
* @return Response
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
return view('notes::edit');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function update(Request $request)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
* @return Response
|
||||
*/
|
||||
public function destroy()
|
||||
{
|
||||
}
|
||||
}
|
0
Modules/Notes/Http/Middleware/.gitkeep
Normal file
0
Modules/Notes/Http/Middleware/.gitkeep
Normal file
0
Modules/Notes/Http/Requests/.gitkeep
Normal file
0
Modules/Notes/Http/Requests/.gitkeep
Normal file
30
Modules/Notes/Http/Requests/CreateNoteRequest.php
Normal file
30
Modules/Notes/Http/Requests/CreateNoteRequest.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Notes\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class CreateNoteRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
20
Modules/Notes/Policies/NotePolicy.php
Normal file
20
Modules/Notes/Policies/NotePolicy.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Notes\Policies;
|
||||
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
|
||||
class NotePolicy
|
||||
{
|
||||
use HandlesAuthorization;
|
||||
|
||||
/**
|
||||
* Create a new policy instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
0
Modules/Notes/Providers/.gitkeep
Normal file
0
Modules/Notes/Providers/.gitkeep
Normal file
113
Modules/Notes/Providers/NotesServiceProvider.php
Normal file
113
Modules/Notes/Providers/NotesServiceProvider.php
Normal file
@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Notes\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Database\Eloquent\Factory;
|
||||
|
||||
class NotesServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Indicates if loading of the provider is deferred.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $defer = false;
|
||||
|
||||
/**
|
||||
* Boot the application events.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->registerTranslations();
|
||||
$this->registerConfig();
|
||||
$this->registerViews();
|
||||
$this->registerFactories();
|
||||
$this->loadMigrationsFrom(__DIR__ . '/../Database/Migrations');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the service provider.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->app->register(RouteServiceProvider::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register config.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function registerConfig()
|
||||
{
|
||||
$this->publishes([
|
||||
__DIR__.'/../Config/config.php' => config_path('notes.php'),
|
||||
], 'config');
|
||||
$this->mergeConfigFrom(
|
||||
__DIR__.'/../Config/config.php', 'notes'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register views.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function registerViews()
|
||||
{
|
||||
$viewPath = resource_path('views/modules/notes');
|
||||
|
||||
$sourcePath = __DIR__.'/../Resources/views';
|
||||
|
||||
$this->publishes([
|
||||
$sourcePath => $viewPath
|
||||
],'views');
|
||||
|
||||
$this->loadViewsFrom(array_merge(array_map(function ($path) {
|
||||
return $path . '/modules/notes';
|
||||
}, \Config::get('view.paths')), [$sourcePath]), 'notes');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register translations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function registerTranslations()
|
||||
{
|
||||
$langPath = resource_path('lang/modules/notes');
|
||||
|
||||
if (is_dir($langPath)) {
|
||||
$this->loadTranslationsFrom($langPath, 'notes');
|
||||
} else {
|
||||
$this->loadTranslationsFrom(__DIR__ .'/../Resources/lang', 'notes');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an additional directory of factories.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function registerFactories()
|
||||
{
|
||||
if (! app()->environment('production')) {
|
||||
app(Factory::class)->load(__DIR__ . '/../Database/factories');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the services provided by the provider.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function provides()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
69
Modules/Notes/Providers/RouteServiceProvider.php
Normal file
69
Modules/Notes/Providers/RouteServiceProvider.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Notes\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
|
||||
class RouteServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The root namespace to assume when generating URLs to actions.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'Modules\Notes\Http\Controllers';
|
||||
|
||||
/**
|
||||
* Called before routes are registered.
|
||||
*
|
||||
* Register any model bindings or pattern based filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
parent::boot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the routes for the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function map()
|
||||
{
|
||||
$this->mapApiRoutes();
|
||||
|
||||
$this->mapWebRoutes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the "web" routes for the application.
|
||||
*
|
||||
* These routes all receive session state, CSRF protection, etc.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function mapWebRoutes()
|
||||
{
|
||||
Route::middleware('web')
|
||||
->namespace($this->namespace)
|
||||
->group(__DIR__ . '/../Routes/web.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the "api" routes for the application.
|
||||
*
|
||||
* These routes are typically stateless.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function mapApiRoutes()
|
||||
{
|
||||
Route::prefix('api')
|
||||
->middleware('api')
|
||||
->namespace($this->namespace)
|
||||
->group(__DIR__ . '/../Routes/api.php');
|
||||
}
|
||||
}
|
0
Modules/Notes/Resources/assets/.gitkeep
Normal file
0
Modules/Notes/Resources/assets/.gitkeep
Normal file
0
Modules/Notes/Resources/assets/js/app.js
vendored
Normal file
0
Modules/Notes/Resources/assets/js/app.js
vendored
Normal file
0
Modules/Notes/Resources/assets/sass/app.scss
vendored
Normal file
0
Modules/Notes/Resources/assets/sass/app.scss
vendored
Normal file
0
Modules/Notes/Resources/lang/.gitkeep
Normal file
0
Modules/Notes/Resources/lang/.gitkeep
Normal file
0
Modules/Notes/Resources/views/.gitkeep
Normal file
0
Modules/Notes/Resources/views/.gitkeep
Normal file
32
Modules/Notes/Resources/views/index.blade.php
Normal file
32
Modules/Notes/Resources/views/index.blade.php
Normal file
@ -0,0 +1,32 @@
|
||||
|
||||
@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')
|
||||
@parent
|
||||
<main class="main" >
|
||||
<!-- Breadcrumb-->
|
||||
{{ Breadcrumbs::render('clients') }}
|
||||
|
||||
<div class="container-fluid">
|
||||
<div id="ui-view">
|
||||
<div class="animated fadeIn">
|
||||
<div class="row col-lg-12 card">
|
||||
|
||||
{!! $html->table() !!}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
@endsection
|
||||
|
||||
@section('footer')
|
||||
@parent
|
||||
{!! $html->scripts() !!}
|
||||
@endsection
|
19
Modules/Notes/Resources/views/layouts/master.blade.php
Normal file
19
Modules/Notes/Resources/views/layouts/master.blade.php
Normal file
@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Module Notes</title>
|
||||
|
||||
{{-- Laravel Mix - CSS File --}}
|
||||
{{-- <link rel="stylesheet" href="{{ mix('css/notes.css') }}"> --}}
|
||||
|
||||
</head>
|
||||
<body>
|
||||
@yield('content')
|
||||
|
||||
{{-- Laravel Mix - JS File --}}
|
||||
{{-- <script src="{{ mix('js/notes.js') }}"></script> --}}
|
||||
</body>
|
||||
</html>
|
18
Modules/Notes/Routes/api.php
Normal file
18
Modules/Notes/Routes/api.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| API Routes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you can register API routes for your application. These
|
||||
| routes are loaded by the RouteServiceProvider within a group which
|
||||
| is assigned the "api" middleware group. Enjoy building your API!
|
||||
|
|
||||
|
||||
Route::middleware('auth:api')->get('/notes', function (Request $request) {
|
||||
return $request->user();
|
||||
});
|
||||
*/
|
16
Modules/Notes/Routes/web.php
Normal file
16
Modules/Notes/Routes/web.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Web Routes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you can register web routes for your application. These
|
||||
| routes are loaded by the RouteServiceProvider within a group which
|
||||
| contains the "web" middleware group. Now create something great!
|
||||
|
|
||||
*/
|
||||
|
||||
Route::group(['middleware' => ['auth:user', 'db'], 'prefix' => 'notes'], function() {
|
||||
Route::get('/', 'NotesController@index')->name('notes.index');
|
||||
});
|
0
Modules/Notes/Tests/.gitkeep
Normal file
0
Modules/Notes/Tests/.gitkeep
Normal file
80
Modules/Notes/Tests/CheckMenuModulesTest.php
Normal file
80
Modules/Notes/Tests/CheckMenuModulesTest.php
Normal file
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Notes\Tests;
|
||||
|
||||
use App\Models\Client;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Nwidart\Modules\Facades\Module;
|
||||
use Tests\TestCase;
|
||||
|
||||
class CheckMenuModulesTest extends TestCase
|
||||
{
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
}
|
||||
/**
|
||||
* A basic test example.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testModulesAreDetected()
|
||||
{
|
||||
$this->assertGreaterThan(0, Module::count());
|
||||
}
|
||||
|
||||
public function testNotesModuleExists()
|
||||
{
|
||||
$module = Module::find('Notes');
|
||||
$this->assertNotNull($module);
|
||||
|
||||
}
|
||||
|
||||
public function testNoSideBarVariableExists()
|
||||
{
|
||||
$module = Module::find('Notes');
|
||||
$this->assertNotNull($module->get('sidebar'));
|
||||
}
|
||||
|
||||
public function testViewsVariableExistsAndIsArray()
|
||||
{
|
||||
$module = Module::find('Notes');
|
||||
$this->assertTrue(is_array($module->get('views')));
|
||||
}
|
||||
|
||||
public function testViewsVariableExistsAndContainsClients()
|
||||
{
|
||||
$module = Module::find('Notes');
|
||||
$array = $module->get('views');
|
||||
$this->assertTrue(in_array('client', $array));
|
||||
}
|
||||
|
||||
public function testViewsVariableExistsAndDoesNotContainRandomObject()
|
||||
{
|
||||
$module = Module::find('Notes');
|
||||
$array = $module->get('views');
|
||||
$this->assertFalse(in_array('foo', $array));
|
||||
}
|
||||
|
||||
public function testResolvingTabMenuCorrectly()
|
||||
{
|
||||
$entity = Client::class;
|
||||
$tabs = [];
|
||||
|
||||
foreach (Module::getCached() as $module)
|
||||
{
|
||||
if(!$module['sidebar']
|
||||
&& $module['active'] == 1
|
||||
&& in_array( strtolower( class_basename($entity) ), $module['views']))
|
||||
{
|
||||
$tabs[] = $module;
|
||||
}
|
||||
}
|
||||
$this->assertFalse($module['sidebar']);
|
||||
$this->assertEquals(1,$module['active']);
|
||||
$this->assertEquals('client', strtolower(class_basename(Client::class)));
|
||||
$this->assertTrue( in_array(strtolower(class_basename(Client::class)), $module['views']) );
|
||||
$this->assertEquals(1, count($tabs));
|
||||
}
|
||||
|
||||
}
|
30
Modules/Notes/composer.json
Normal file
30
Modules/Notes/composer.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "invoiceninja/notes",
|
||||
"description": "",
|
||||
"authors": [
|
||||
{
|
||||
"name": "David Bomba",
|
||||
"email": "david@invoiceninja.com"
|
||||
}
|
||||
],
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Modules\\Notes\\Providers\\NotesServiceProvider"
|
||||
],
|
||||
"aliases": {
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Modules\\Notes\\": ""
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Tests\\": "Tests/"
|
||||
}
|
||||
},
|
||||
}
|
19
Modules/Notes/module.json
Normal file
19
Modules/Notes/module.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "Notes",
|
||||
"alias": "notes",
|
||||
"description": "Generic Notes Module",
|
||||
"keywords": [],
|
||||
"active": 1,
|
||||
"order": 0,
|
||||
"providers": [
|
||||
"Modules\\Notes\\Providers\\NotesServiceProvider"
|
||||
],
|
||||
"aliases": [],
|
||||
"icon": "bell",
|
||||
"plural": "notes",
|
||||
"base-route": "notes",
|
||||
"settings": false,
|
||||
"sidebar": false,
|
||||
"requires": [],
|
||||
"views": ["client","invoice"]
|
||||
}
|
17
Modules/Notes/package.json
Normal file
17
Modules/Notes/package.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "npm run development",
|
||||
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
|
||||
"watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
|
||||
"watch-poll": "npm run watch -- --watch-poll",
|
||||
"hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
|
||||
"prod": "npm run production",
|
||||
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cross-env": "^5.1.4",
|
||||
"laravel-mix": "^2.1",
|
||||
"laravel-mix-merge-manifest": "^0.1.1"
|
||||
}
|
||||
}
|
11
Modules/Notes/webpack.mix.js
vendored
Normal file
11
Modules/Notes/webpack.mix.js
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
const { mix } = require('laravel-mix');
|
||||
require('laravel-mix-merge-manifest');
|
||||
|
||||
mix.setPublicPath('../../public').mergeManifest();
|
||||
|
||||
mix.js(__dirname + '/Resources/assets/js/app.js', 'js/notes.js')
|
||||
.sass( __dirname + '/Resources/assets/sass/app.scss', 'css/notes.css');
|
||||
|
||||
if (mix.inProduction()) {
|
||||
mix.version();
|
||||
}
|
9
app/Datatables/ClientDatatable.php
Normal file
9
app/Datatables/ClientDatatable.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace App\Datatables;
|
||||
|
||||
|
||||
class ClientDatatable
|
||||
{
|
||||
|
||||
}
|
38
app/Datatables/EntityDataTable.php
Normal file
38
app/Datatables/EntityDataTable.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Datatables;
|
||||
|
||||
|
||||
class EntityDatatable
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns the columns to be displayed and their key/values
|
||||
* @return array Columns and key/value option pairs
|
||||
*
|
||||
* To be used to show/hide columns
|
||||
*/
|
||||
public function columns()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Display options for the ajax request
|
||||
* @return array url, type, data
|
||||
*/
|
||||
public function ajax()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the datatable
|
||||
* @return DataTable returns a DataTable instance
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -11,6 +11,7 @@ use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
use App\Repositories\ClientRepository;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\MakesMenu;
|
||||
use App\Utils\Traits\UserSessionAttributes;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
@ -21,6 +22,7 @@ class ClientController extends Controller
|
||||
{
|
||||
use UserSessionAttributes;
|
||||
use MakesHash;
|
||||
use MakesMenu;
|
||||
|
||||
protected $clientRepo;
|
||||
|
||||
@ -168,6 +170,8 @@ class ClientController extends Controller
|
||||
|
||||
$data = [
|
||||
'client' => $client,
|
||||
'settings' => [],
|
||||
'pills' => $this->makeEntityTabMenu(Client::class),
|
||||
'hashed_id' => $this->encodePrimarykey($client->id)
|
||||
];
|
||||
|
||||
|
@ -29,7 +29,7 @@ class TranslationController extends Controller
|
||||
});
|
||||
|
||||
header('Content-Type: text/javascript');
|
||||
echo('window.i18n = ' . json_encode($strings) . ';');
|
||||
echo('i18n = ' . json_encode($strings) . ';');
|
||||
exit();
|
||||
}
|
||||
|
||||
|
@ -40,8 +40,9 @@ class UpdateClientRequest extends Request
|
||||
{
|
||||
return [
|
||||
'unique' => trans('validation.unique', ['attribute' => 'email']),
|
||||
'email' => trans('validation.email', ['attribute' => 'email']),
|
||||
'name.required' =>trans('validation.required', ['attribute' => 'name']),
|
||||
'required' => trans('validation.required', ['attribute' => 'email']),
|
||||
'email' => trans('validation.email', ['attribute' => 'email'])
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,16 @@ class RouteServiceProvider extends ServiceProvider
|
||||
|
||||
});
|
||||
|
||||
|
||||
Route::bind('c', function ($value) {
|
||||
$client = \App\Models\Client::where('id', $this->decodePrimaryKey($value))->first() ?? abort(404);
|
||||
$client->load('contacts', 'primary_contact');
|
||||
|
||||
return $client;
|
||||
|
||||
});
|
||||
|
||||
|
||||
Route::bind('invoice', function ($value) {
|
||||
return \App\Models\Invoice::where('id', $this->decodePrimaryKey($value))->first() ?? abort(404);
|
||||
});
|
||||
|
47
app/Utils/Traits/MakesMenu.php
Normal file
47
app/Utils/Traits/MakesMenu.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Utils\Traits;
|
||||
|
||||
use Nwidart\Modules\Facades\Module;
|
||||
|
||||
/**
|
||||
* Class MakesMenu
|
||||
* @package App\Utils\Traits
|
||||
*/
|
||||
trait MakesMenu
|
||||
{
|
||||
|
||||
/**
|
||||
* Builds an array of available modules for this view
|
||||
* @param string $entity Class name
|
||||
* @return array of modules
|
||||
*/
|
||||
public function makeEntityTabMenu(string $entity) : array
|
||||
{
|
||||
|
||||
$tabs = [];
|
||||
|
||||
foreach (Module::getCached() as $module)
|
||||
{
|
||||
if(!$module['sidebar']
|
||||
&& $module['active'] == 1
|
||||
&& in_array( strtolower( class_basename($entity) ), $module['views']))
|
||||
{
|
||||
$tabs[] = $module;
|
||||
}
|
||||
}
|
||||
|
||||
return $tabs;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an array items to be presented on the sidebar
|
||||
* @return array menu items
|
||||
*/
|
||||
public function makeSideBarMenu()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -50,7 +50,8 @@
|
||||
"database/factories"
|
||||
],
|
||||
"psr-4": {
|
||||
"App\\": "app/"
|
||||
"App\\": "app/",
|
||||
"Modules\\": "Modules/"
|
||||
},
|
||||
"files": [
|
||||
"app/Libraries/OFX.php",
|
||||
|
188
config/modules.php
Normal file
188
config/modules.php
Normal file
@ -0,0 +1,188 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Module Namespace
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Default module namespace.
|
||||
|
|
||||
*/
|
||||
|
||||
'namespace' => 'Modules',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Module Stubs
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Default module stubs.
|
||||
|
|
||||
*/
|
||||
|
||||
'stubs' => [
|
||||
'enabled' => false,
|
||||
'path' => base_path() . '/vendor/nwidart/laravel-modules/src/Commands/stubs',
|
||||
'files' => [
|
||||
'routes/web' => 'Routes/web.php',
|
||||
'routes/api' => 'Routes/api.php',
|
||||
'views/index' => 'Resources/views/index.blade.php',
|
||||
'views/master' => 'Resources/views/layouts/master.blade.php',
|
||||
'scaffold/config' => 'Config/config.php',
|
||||
'composer' => 'composer.json',
|
||||
'assets/js/app' => 'Resources/assets/js/app.js',
|
||||
'assets/sass/app' => 'Resources/assets/sass/app.scss',
|
||||
'webpack' => 'webpack.mix.js',
|
||||
'package' => 'package.json',
|
||||
],
|
||||
'replacements' => [
|
||||
'routes/web' => ['LOWER_NAME', 'STUDLY_NAME'],
|
||||
'routes/api' => ['LOWER_NAME'],
|
||||
'webpack' => ['LOWER_NAME'],
|
||||
'json' => ['LOWER_NAME', 'STUDLY_NAME', 'MODULE_NAMESPACE'],
|
||||
'views/index' => ['LOWER_NAME'],
|
||||
'views/master' => ['LOWER_NAME', 'STUDLY_NAME'],
|
||||
'scaffold/config' => ['STUDLY_NAME'],
|
||||
'composer' => [
|
||||
'LOWER_NAME',
|
||||
'STUDLY_NAME',
|
||||
'VENDOR',
|
||||
'AUTHOR_NAME',
|
||||
'AUTHOR_EMAIL',
|
||||
'MODULE_NAMESPACE',
|
||||
],
|
||||
],
|
||||
'gitkeep' => true,
|
||||
],
|
||||
'paths' => [
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Modules path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This path used for save the generated module. This path also will be added
|
||||
| automatically to list of scanned folders.
|
||||
|
|
||||
*/
|
||||
|
||||
'modules' => base_path('Modules'),
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Modules assets path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may update the modules assets path.
|
||||
|
|
||||
*/
|
||||
|
||||
'assets' => public_path('modules'),
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| The migrations path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Where you run 'module:publish-migration' command, where do you publish the
|
||||
| the migration files?
|
||||
|
|
||||
*/
|
||||
|
||||
'migration' => base_path('database/migrations'),
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Generator path
|
||||
|--------------------------------------------------------------------------
|
||||
| Customise the paths where the folders will be generated.
|
||||
| Set the generate key to false to not generate that folder
|
||||
*/
|
||||
'generator' => [
|
||||
'config' => ['path' => 'Config', 'generate' => true],
|
||||
'command' => ['path' => 'Console', 'generate' => true],
|
||||
'migration' => ['path' => 'Database/Migrations', 'generate' => true],
|
||||
'seeder' => ['path' => 'Database/Seeders', 'generate' => true],
|
||||
'factory' => ['path' => 'Database/factories', 'generate' => true],
|
||||
'model' => ['path' => 'Entities', 'generate' => true],
|
||||
'controller' => ['path' => 'Http/Controllers', 'generate' => true],
|
||||
'filter' => ['path' => 'Http/Middleware', 'generate' => true],
|
||||
'request' => ['path' => 'Http/Requests', 'generate' => true],
|
||||
'provider' => ['path' => 'Providers', 'generate' => true],
|
||||
'assets' => ['path' => 'Resources/assets', 'generate' => true],
|
||||
'lang' => ['path' => 'Resources/lang', 'generate' => true],
|
||||
'views' => ['path' => 'Resources/views', 'generate' => true],
|
||||
'test' => ['path' => 'Tests', 'generate' => true],
|
||||
'repository' => ['path' => 'Repositories', 'generate' => false],
|
||||
'event' => ['path' => 'Events', 'generate' => false],
|
||||
'listener' => ['path' => 'Listeners', 'generate' => false],
|
||||
'policies' => ['path' => 'Policies', 'generate' => false],
|
||||
'rules' => ['path' => 'Rules', 'generate' => false],
|
||||
'jobs' => ['path' => 'Jobs', 'generate' => false],
|
||||
'emails' => ['path' => 'Emails', 'generate' => false],
|
||||
'notifications' => ['path' => 'Notifications', 'generate' => false],
|
||||
'resource' => ['path' => 'Transformers', 'generate' => false],
|
||||
],
|
||||
],
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Scan Path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you define which folder will be scanned. By default will scan vendor
|
||||
| directory. This is useful if you host the package in packagist website.
|
||||
|
|
||||
*/
|
||||
|
||||
'scan' => [
|
||||
'enabled' => false,
|
||||
'paths' => [
|
||||
base_path('vendor/*/*'),
|
||||
],
|
||||
],
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Composer File Template
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here is the config for composer.json file, generated by this package
|
||||
|
|
||||
*/
|
||||
|
||||
'composer' => [
|
||||
'vendor' => 'nwidart',
|
||||
'author' => [
|
||||
'name' => 'Nicolas Widart',
|
||||
'email' => 'n.widart@gmail.com',
|
||||
],
|
||||
],
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Caching
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here is the config for setting up caching feature.
|
||||
|
|
||||
*/
|
||||
'cache' => [
|
||||
'enabled' => true,
|
||||
'key' => 'laravel-modules',
|
||||
'lifetime' => 60,
|
||||
],
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Choose what laravel-modules will register as custom namespaces.
|
||||
| Setting one to false will require you to register that part
|
||||
| in your own Service Provider class.
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
'register' => [
|
||||
'translations' => true,
|
||||
/**
|
||||
* load files on boot or register method
|
||||
*
|
||||
* Note: boot not compatible with asgardcms
|
||||
*
|
||||
* @example boot|register
|
||||
*/
|
||||
'files' => 'register',
|
||||
],
|
||||
];
|
@ -35,6 +35,7 @@
|
||||
"vue": "^2.5.17"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/lodash": "^4.14.118",
|
||||
"@types/node": "^10.12.10",
|
||||
"hashids": "^1.2.2",
|
||||
"laravel-echo": "^1.4.0",
|
||||
@ -43,6 +44,7 @@
|
||||
"ts-loader": "3.5.0",
|
||||
"typescript": "^3.1.6",
|
||||
"vue-i18n": "^8.3.0",
|
||||
"vue-select": "^2.5.1",
|
||||
"vue-toastr": "^2.0.16"
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,9 @@
|
||||
<testsuite name="Feature">
|
||||
<directory suffix="Test.php">./tests/Feature</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Modules">
|
||||
<directory suffix="Test.php">./Modules</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
|
34984
public/js/client-edit.js
vendored
Normal file
34984
public/js/client-edit.js
vendored
Normal file
File diff suppressed because one or more lines are too long
4
public/js/client_create.js
vendored
4
public/js/client_create.js
vendored
@ -60,7 +60,7 @@
|
||||
/******/ __webpack_require__.p = "/";
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 1);
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 2);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ({
|
||||
@ -15244,7 +15244,7 @@ exports.default = Form;
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 1:
|
||||
/***/ 2:
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
module.exports = __webpack_require__("./resources/js/src/client/client_create.ts");
|
||||
|
4
public/js/client_create.min.js
vendored
4
public/js/client_create.min.js
vendored
@ -60,7 +60,7 @@
|
||||
/******/ __webpack_require__.p = "/";
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 1);
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 2);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ({
|
||||
@ -15244,7 +15244,7 @@ exports.default = Form;
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 1:
|
||||
/***/ 2:
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
module.exports = __webpack_require__("./resources/js/src/client/client_create.ts");
|
||||
|
4
public/js/coreui.js
vendored
4
public/js/coreui.js
vendored
@ -60,7 +60,7 @@
|
||||
/******/ __webpack_require__.p = "/";
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 3);
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 5);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ({
|
||||
@ -12576,7 +12576,7 @@ PerfectScrollbar.prototype.removePsClasses = function removePsClasses () {
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 3:
|
||||
/***/ 5:
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
module.exports = __webpack_require__("./node_modules/@coreui/coreui/dist/js/coreui.js");
|
||||
|
4
public/js/coreui.min.js
vendored
4
public/js/coreui.min.js
vendored
@ -60,7 +60,7 @@
|
||||
/******/ __webpack_require__.p = "/";
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 3);
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 5);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ({
|
||||
@ -12576,7 +12576,7 @@ PerfectScrollbar.prototype.removePsClasses = function removePsClasses () {
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 3:
|
||||
/***/ 5:
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
module.exports = __webpack_require__("./node_modules/@coreui/coreui/dist/js/coreui.js");
|
||||
|
11559
public/js/localization.js
vendored
Normal file
11559
public/js/localization.js
vendored
Normal file
File diff suppressed because one or more lines are too long
11559
public/js/localization.min.js
vendored
Normal file
11559
public/js/localization.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
28
public/js/ninja.js
vendored
28
public/js/ninja.js
vendored
@ -60,7 +60,7 @@
|
||||
/******/ __webpack_require__.p = "/";
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 2);
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 4);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ({
|
||||
@ -13124,30 +13124,6 @@ module.exports = g;
|
||||
|
||||
__webpack_require__("./resources/js/bootstrap.js");
|
||||
|
||||
/* 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
|
||||
* or customize the JavaScript scaffolding to fit your unique needs.
|
||||
|
||||
Vue.component('example-component', require('./components/ExampleComponent.vue'));
|
||||
Vue.component('client-edit', require('./components/client/ClientEdit.vue'));
|
||||
Vue.component('client-primary-address', require('./components/client/ClientPrimaryAddress.vue'));
|
||||
Vue.component('generic-address', require('./components/generic/Address.vue'));
|
||||
Vue.component('client-edit-form', require('./components/client/ClientEditForm.vue'));
|
||||
Vue.component('contact-edit', require('./components/client/ClientContactEdit.vue'));
|
||||
*/
|
||||
|
||||
window.onload = function () {
|
||||
|
||||
var app = new Vue({
|
||||
el: '#app'
|
||||
});
|
||||
};
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./resources/js/bootstrap.js":
|
||||
@ -13204,7 +13180,7 @@ if (token) {
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 2:
|
||||
/***/ 4:
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
module.exports = __webpack_require__("./resources/js/app.js");
|
||||
|
28
public/js/ninja.min.js
vendored
28
public/js/ninja.min.js
vendored
@ -60,7 +60,7 @@
|
||||
/******/ __webpack_require__.p = "/";
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 2);
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 4);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ({
|
||||
@ -13124,30 +13124,6 @@ module.exports = g;
|
||||
|
||||
__webpack_require__("./resources/js/bootstrap.js");
|
||||
|
||||
/* 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
|
||||
* or customize the JavaScript scaffolding to fit your unique needs.
|
||||
|
||||
Vue.component('example-component', require('./components/ExampleComponent.vue'));
|
||||
Vue.component('client-edit', require('./components/client/ClientEdit.vue'));
|
||||
Vue.component('client-primary-address', require('./components/client/ClientPrimaryAddress.vue'));
|
||||
Vue.component('generic-address', require('./components/generic/Address.vue'));
|
||||
Vue.component('client-edit-form', require('./components/client/ClientEditForm.vue'));
|
||||
Vue.component('contact-edit', require('./components/client/ClientContactEdit.vue'));
|
||||
*/
|
||||
|
||||
window.onload = function () {
|
||||
|
||||
var app = new Vue({
|
||||
el: '#app'
|
||||
});
|
||||
};
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./resources/js/bootstrap.js":
|
||||
@ -13204,7 +13180,7 @@ if (token) {
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 2:
|
||||
/***/ 4:
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
module.exports = __webpack_require__("./resources/js/app.js");
|
||||
|
28
public/js/vendor/app.js
vendored
28
public/js/vendor/app.js
vendored
@ -60,7 +60,7 @@
|
||||
/******/ __webpack_require__.p = "/";
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 2);
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 4);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ({
|
||||
@ -13124,30 +13124,6 @@ module.exports = g;
|
||||
|
||||
__webpack_require__("./resources/js/bootstrap.js");
|
||||
|
||||
/* 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
|
||||
* or customize the JavaScript scaffolding to fit your unique needs.
|
||||
|
||||
Vue.component('example-component', require('./components/ExampleComponent.vue'));
|
||||
Vue.component('client-edit', require('./components/client/ClientEdit.vue'));
|
||||
Vue.component('client-primary-address', require('./components/client/ClientPrimaryAddress.vue'));
|
||||
Vue.component('generic-address', require('./components/generic/Address.vue'));
|
||||
Vue.component('client-edit-form', require('./components/client/ClientEditForm.vue'));
|
||||
Vue.component('contact-edit', require('./components/client/ClientContactEdit.vue'));
|
||||
*/
|
||||
|
||||
window.onload = function () {
|
||||
|
||||
var app = new Vue({
|
||||
el: '#app'
|
||||
});
|
||||
};
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./resources/js/bootstrap.js":
|
||||
@ -13204,7 +13180,7 @@ if (token) {
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 2:
|
||||
/***/ 4:
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
module.exports = __webpack_require__("./resources/js/app.js");
|
||||
|
24
resources/js/app.js
vendored
24
resources/js/app.js
vendored
@ -7,27 +7,3 @@
|
||||
|
||||
require('./bootstrap');
|
||||
|
||||
/* 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
|
||||
* or customize the JavaScript scaffolding to fit your unique needs.
|
||||
|
||||
Vue.component('example-component', require('./components/ExampleComponent.vue'));
|
||||
Vue.component('client-edit', require('./components/client/ClientEdit.vue'));
|
||||
Vue.component('client-primary-address', require('./components/client/ClientPrimaryAddress.vue'));
|
||||
Vue.component('generic-address', require('./components/generic/Address.vue'));
|
||||
Vue.component('client-edit-form', require('./components/client/ClientEditForm.vue'));
|
||||
Vue.component('contact-edit', require('./components/client/ClientContactEdit.vue'));
|
||||
*/
|
||||
|
||||
window.onload = function () {
|
||||
|
||||
const app = new Vue({
|
||||
el: '#app'
|
||||
});
|
||||
|
||||
}
|
||||
|
123
resources/js/components/client/ClientAddress.vue
Normal file
123
resources/js/components/client/ClientAddress.vue
Normal file
@ -0,0 +1,123 @@
|
||||
<template>
|
||||
<div>
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="tab" href="#billing" role="tab" aria-controls="billing">{{ trans('texts.billing_address') }}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="tab" href="#shipping" role="tab" aria-controls="shipping">{{ trans('texts.shipping_address') }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="billing" role="tabpanel">
|
||||
<button type="button" class="btn btn-sm btn-light" v-on:click="$emit('copy', 'copy_shipping')"> {{ trans('texts.copy_shipping') }}</button>
|
||||
<div class="card-body">
|
||||
<div class="form-group row">
|
||||
<label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.address1') }}</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" :placeholder="trans('texts.address1')" v-model="client.address1" class="form-control">
|
||||
<div v-if="client.errors.has('address1')" class="text-danger" v-text="client.errors.get('address1')"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.address2') }}</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text":placeholder="trans('texts.address2')" v-model="client.address2" class="form-control">
|
||||
<div v-if="client.errors.has('address2')" class="text-danger" v-text="client.errors.get('address2')"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.city') }}</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text":placeholder="trans('texts.city')" v-model="client.city" class="form-control">
|
||||
<div v-if="client.errors.has('city')" class="text-danger" v-text="client.errors.get('city')"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.state') }}</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" :placeholder="trans('texts.state')" v-model="client.state" class="form-control">
|
||||
<div v-if="client.errors.has('state')" class="text-danger" v-text="client.errors.get('state')"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.postal_code') }}</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" :placeholder="trans('texts.postal_code')" v-model="client.postal_code" class="form-control">
|
||||
<div v-if="client.errors.has('postal_code')" class="text-danger" v-text="client.errors.get('postal_code')"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.country') }}</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" :placeholder="trans('texts.country')" v-model="client.country" class="form-control">
|
||||
<div v-if="client.errors.has('country')" class="text-danger" v-text="client.errors.get('country')"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane" id="shipping" role="tabpanel">
|
||||
<button type="button" class="btn btn-sm btn-light" v-on:click="$emit('copy',' copy_billing')"> {{ trans('texts.copy_billing') }}</button>
|
||||
<div class="form-group row">
|
||||
<label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.address1') }}</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" :placeholder="trans('texts.address1')" v-model="client.shipping_address1" class="form-control">
|
||||
<div v-if="client.errors.has('shipping_address1')" class="text-danger" v-text="client.errors.get('shipping_address1')"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.address2') }}</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" :placeholder="trans('texts.address2')" v-model="client.shipping_address2" class="form-control">
|
||||
<div v-if="client.errors.has('shipping_address2')" class="text-danger" v-text="client.errors.get('shipping_address2')"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.city') }}</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" :placeholder="trans('texts.city')" v-model="client.shipping_city" class="form-control">
|
||||
<div v-if="client.errors.has('shipping_city')" class="text-danger" v-text="client.errors.get('shipping_city')"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.state') }}</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" :placeholder="trans('texts.state')" v-model="client.shipping_state" class="form-control">
|
||||
<div v-if="client.errors.has('shipping_state')" class="text-danger" v-text="client.errors.get('shipping_state')"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.postal_code') }}</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" :placeholder="trans('texts.postal_code')" v-model="client.shipping_postal_code" class="form-control">
|
||||
<div v-if="client.errors.has('shipping_postal_code')" class="text-danger" v-text="client.errors.get('shipping_postal_code')"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.country') }}</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" :placeholder="trans('texts.country')" v-model="client.shipping_country" class="form-control">
|
||||
<div v-if="client.errors.has('shipping_country')" class="text-danger" v-text="client.errors.get('shipping_country')"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['client']
|
||||
}
|
||||
</script>
|
@ -3,42 +3,48 @@
|
||||
<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">
|
||||
<input ref="first_name" name="first_name" type="text" :placeholder="trans('texts.first_name')" v-model="contact.first_name" class="form-control">
|
||||
<div v-if="form.errors.has('contacts.'+error_index+'.first_name')" class="text-danger" v-text="form.errors.get('contacts.'+error_index+'.first_name')"></div>
|
||||
</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">
|
||||
<input type="text" :placeholder="trans('texts.last_name')" v-model="contact.last_name" class="form-control">
|
||||
<div v-if="form.errors.has('contacts.'+error_index+'.last_name')" class="text-danger" v-text="form.errors.get('contacts.'+error_index+'.last_name')"></div>
|
||||
</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">
|
||||
<input type="email" :placeholder="trans('texts.email')" v-model="contact.email" class="form-control">
|
||||
<div v-if="form.errors.has('contacts.'+error_index+'.email')" class="text-danger" v-text="form.errors.get('contacts.'+error_index+'.email')"></div>
|
||||
</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">
|
||||
<input type="text" :placeholder="trans('texts.phone')" v-model="contact.phone" class="form-control">
|
||||
<div v-if="form.errors.has('contacts.'+error_index+'.phone')" class="text-danger" v-text="form.errors.get('contacts.'+error_index+'.phone')"></div>
|
||||
</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">
|
||||
<input type="text" :placeholder="trans('texts.custom_value1')" v-model="contact.custom_value1" class="form-control">
|
||||
<div v-if="form.errors.has('contacts.'+error_index+'.custom_value1')" class="text-danger" v-text="form.errors.get('contacts.'+error_index+'.custom_value1')"></div>
|
||||
</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">
|
||||
<input type="text" :placeholder="trans('texts.custom_value2')" v-model="contact.custom_value2" class="form-control">
|
||||
<div v-if="form.errors.has('contacts.'+error_index+'.custom_value2')" class="text-danger" v-text="form.errors.get('contacts.'+error_index+'.custom_value2')"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -50,6 +56,6 @@
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['contact']
|
||||
props: ['contact', 'form', 'error_index']
|
||||
}
|
||||
</script>
|
||||
|
@ -3,8 +3,8 @@
|
||||
<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 v-if="errors && errors.name" class="text-danger">{{ errors.name[0] }}</div>
|
||||
<input type="text" :placeholder="trans('texts.client_name')" v-model="client.name" class="form-control">
|
||||
<div v-if="client.errors.has('name')" class="text-danger" v-text="client.errors.get('name')"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
<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 v-if="errors && errors.id_number" class="text-danger">{{ errors.id_number[0] }}</div>
|
||||
<div v-if="client.errors.has('id_number')" class="text-danger" v-text="client.errors.get('id_number')"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
<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 v-if="errors && errors.vat_number" class="text-danger">{{ errors.vat_number[0] }}</div>
|
||||
<div v-if="client.errors.has('vat_number')" class="text-danger" v-text="client.errors.get('vat_number')"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
<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 v-if="errors && errors.website" class="text-danger">{{ errors.website[0] }}</div>
|
||||
<div v-if="client.errors.has('website')" class="text-danger" v-text="client.errors.get('website')"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
<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 v-if="errors && errors.custom_value1" class="text-danger">{{ errors.custom_value1[0] }}</div>
|
||||
<div v-if="client.errors.has('custom_value1')" class="text-danger" v-text="client.errors.get('custom_value1')"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -44,7 +44,7 @@
|
||||
<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 v-if="errors && errors.custom_value2" class="text-danger">{{ errors.custom_value2[0] }}</div>
|
||||
<div v-if="client.errors.has('custom_value2')" class="text-danger" v-text="client.errors.get('custom_value2')"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,36 +1,18 @@
|
||||
<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>
|
||||
<form @submit.prevent="onSubmit" @keydown="form.errors.clear($event.target.name)">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<!-- Client Details and Address Column -->
|
||||
<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>
|
||||
<client-edit :client="form"></client-edit>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header bg-primary2">{{ trans('texts.address') }}</div>
|
||||
<client-primary-address v-bind:client="client" @copy="copy"></client-primary-address>
|
||||
<client-address v-bind:client="form" @copy="copy"></client-address>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End Client Details and Address Column -->
|
||||
@ -40,75 +22,100 @@
|
||||
<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>
|
||||
<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>
|
||||
<contact-edit v-for="(contact, index) in client.contacts"
|
||||
v-bind:contact="contact"
|
||||
v-bind:index="index"
|
||||
:key="contact.id"
|
||||
<contact-edit v-for="(contact, key, index) in form.contacts"
|
||||
:contact="contact"
|
||||
:form="form"
|
||||
:key="contact.id"
|
||||
:error_index="key"
|
||||
@remove="remove"></contact-edit>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End Contact Details Column -->
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script lang="ts">
|
||||
|
||||
|
||||
import Vue from 'vue';
|
||||
import axios from 'axios';
|
||||
import Form from '../../src/utils/form';
|
||||
import Client from '../../src/models/client-model';
|
||||
|
||||
export default {
|
||||
data: function () {
|
||||
return {
|
||||
'client': [],
|
||||
'errors': [],
|
||||
}
|
||||
},
|
||||
props: {
|
||||
clientdata: {
|
||||
type: [Object,Array],
|
||||
default: () => []
|
||||
form: new Form(<Client>this.clientdata)
|
||||
}
|
||||
},
|
||||
props: ['hashed_id', 'clientdata'],
|
||||
beforeMount: function () {
|
||||
this.client = this.clientdata;
|
||||
},
|
||||
methods: {
|
||||
copy(type) {
|
||||
methods:{
|
||||
remove(this:any, contact:any){
|
||||
let index = this.form.contacts.indexOf(contact);
|
||||
this.form.contacts.splice(index, 1);
|
||||
},
|
||||
add(this: any){
|
||||
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;
|
||||
//this.$refs.first_name[index].$el.focus();
|
||||
//this.$refs.first_name[index].focus();
|
||||
});
|
||||
},
|
||||
onSubmit() {
|
||||
this.form.put('/clients/' + this.hashed_id)
|
||||
.then(response => this.$root.$refs.toastr.s("Saved client"))
|
||||
.catch(error => {
|
||||
|
||||
this.$root.$refs.toastr.e("Error saving client");
|
||||
|
||||
});
|
||||
},
|
||||
copy(type: any) {
|
||||
if(type.includes('copy_billing')){
|
||||
this.client.primary_shipping_location = Object.assign({}, this.client.primary_billing_location);
|
||||
}else {
|
||||
this.client.primary_billing_location = Object.assign({}, this.client.primary_shipping_location);
|
||||
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.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;
|
||||
}
|
||||
},
|
||||
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 || {};
|
||||
}
|
||||
else if(error.response.status === 419) {
|
||||
//csrf token has expired, we'll need to force a page reload
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
}
|
||||
},
|
||||
created:function() {
|
||||
//console.dir('created');
|
||||
|
||||
|
||||
},
|
||||
updated:function() {
|
||||
//console.dir('updated');
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,28 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="tab" href="#billing" role="tab" aria-controls="billing">{{ trans('texts.billing_address') }}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="tab" href="#shipping" role="tab" aria-controls="shipping">{{ trans('texts.shipping_address') }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="billing" role="tabpanel">
|
||||
<button type="button" class="btn btn-sm btn-light" v-on:click="$emit('copy', 'copy_shipping')"> {{ trans('texts.copy_shipping') }}</button>
|
||||
<generic-address :data="client.primary_billing_location ? client.primary_billing_location : 'null'"></generic-address>
|
||||
</div>
|
||||
<div class="tab-pane" id="shipping" role="tabpanel">
|
||||
<button type="button" class="btn btn-sm btn-light" v-on:click="$emit('copy',' copy_billing')"> {{ trans('texts.copy_billing') }}</button>
|
||||
<generic-address :data="client.primary_shipping_location ? client.primary_shipping_location : 'null'"></generic-address>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['client']
|
||||
}
|
||||
</script>
|
37
resources/js/src/c/client-edit.ts
Normal file
37
resources/js/src/c/client-edit.ts
Normal file
@ -0,0 +1,37 @@
|
||||
/* Allows us to use our native translation easily using {{ trans() }} syntax */
|
||||
//const _ = require('lodash');
|
||||
import * as _ from "lodash"
|
||||
declare var i18n;
|
||||
|
||||
import Vue from 'vue';
|
||||
import axios from 'axios';
|
||||
|
||||
// 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);
|
||||
|
||||
Vue.prototype.trans = string => _.get(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
|
||||
* or customize the JavaScript scaffolding to fit your unique needs.
|
||||
*/
|
||||
Vue.component('example-component', require('../../components/ExampleComponent.vue'));
|
||||
Vue.component('client-edit', require('../../components/client/ClientEdit.vue'));
|
||||
Vue.component('client-address', require('../../components/client/ClientAddress.vue'));
|
||||
Vue.component('generic-address', require('../../components/generic/Address.vue'));
|
||||
Vue.component('client-edit-form', require('../../components/client/ClientEditForm.vue'));
|
||||
Vue.component('contact-edit', require('../../components/client/ClientContactEdit.vue'));
|
||||
|
||||
|
||||
window.onload = function () {
|
||||
|
||||
const app = new Vue({
|
||||
el: '#client_e'
|
||||
});
|
||||
|
||||
}
|
@ -14,7 +14,6 @@ Vue.component('vue-toastr',Toastr);
|
||||
declare var client_object: any;
|
||||
declare var hashed_id: string;
|
||||
|
||||
|
||||
new Vue({
|
||||
el : '#client_edit',
|
||||
data: function () {
|
||||
|
13
resources/js/src/settings/localization.ts
Normal file
13
resources/js/src/settings/localization.ts
Normal file
@ -0,0 +1,13 @@
|
||||
|
||||
import Vue from 'vue';
|
||||
import VueSelect from 'vue-select';
|
||||
|
||||
Vue.component('v-select', VueSelect.VueSelect)
|
||||
|
||||
new Vue({
|
||||
el: '#localization',
|
||||
data: {
|
||||
options: ['jim','bob','frank'],
|
||||
selected: 'frank',
|
||||
}
|
||||
})
|
@ -1,11 +1,11 @@
|
||||
@extends('layouts.master', ['header' => $header])
|
||||
|
||||
@section('body')
|
||||
<main class="main" id="client_edit">
|
||||
|
||||
<main class="main" id="client_e">
|
||||
|
||||
<!-- Breadcrumb-->
|
||||
{{ Breadcrumbs::render('clients.edit', $client) }}
|
||||
|
||||
|
||||
<vue-toastr ref="toastr"></vue-toastr>
|
||||
|
||||
<div class="container-fluid">
|
||||
@ -13,77 +13,40 @@
|
||||
<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>
|
||||
<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> {{ trans('texts.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>
|
||||
<a class="nav-link" id="pills-settings-tab" data-toggle="pill" href="#pills-settings" role="tab" aria-controls="pills-settings" aria-selected="false"><i class="icon-settings"></i> {{ trans('texts.settings') }}</a>
|
||||
</li>
|
||||
|
||||
@foreach($pills as $pill)
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="pills-{{ $pill['alias'] }}-tab" data-toggle="pill" href="#pills-{{ $pill['alias'] }}" role="tab" aria-controls="pills-{{ $pill['alias'] }}" aria-selected="false"><i class="icon-{{$pill['icon'] }}"></i> {{ $pill['name'] }}</a>
|
||||
</li>
|
||||
|
||||
@endforeach
|
||||
</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 -->
|
||||
<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>
|
||||
<client-edit-form :clientdata="{{ $client }}" :hashed_id="'{{ $hashed_id }}'"></client-edit-form>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-contact" role="tabpanel" aria-labelledby="pills-contact-tab">
|
||||
@foreach($pills as $pill)
|
||||
|
||||
<div class="tab-pane fade" id="pills-{{ $pill['alias'] }}" role="tabpanel" aria-labelledby="pills-{{ $pill['alias'] }}-tab">
|
||||
{{$pill['name']}}
|
||||
</div>
|
||||
|
||||
@endforeach
|
||||
|
||||
<div class="tab-pane fade" id="pills-settings" role="tabpanel" aria-labelledby="pills-settings-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>
|
||||
|
||||
@ -93,10 +56,6 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script>
|
||||
var client_object = {!! $client !!};
|
||||
var hashed_id = '{{ $hashed_id }}';
|
||||
</script>
|
||||
<script defer src=" {{ mix('/js/client-edit.js') }}"></script>
|
||||
|
||||
<script defer src=" {{ mix('/js/client_edit.min.js') }}"></script>
|
||||
@endsection
|
19
resources/views/settings/partial/localization.blade.php
Normal file
19
resources/views/settings/partial/localization.blade.php
Normal file
@ -0,0 +1,19 @@
|
||||
<div class="card" id="localization">
|
||||
<div class="card-header bg-primary2">@lang('texts.localization')</div>
|
||||
<div class="card-body">
|
||||
<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">
|
||||
<v-select v-model="selected" :options="options"></v-select>
|
||||
<div v-if="form.errors.has('name')" class="text-danger" v-text="form.errors.get('name')"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
</script>
|
||||
|
||||
<script defer src=" {{ mix('/js/localization.min.js') }}"></script>
|
@ -13,11 +13,6 @@
|
||||
<i class="nav-icon icon-user"></i> @lang('texts.clients')</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="typography.html">
|
||||
<i class="nav-icon icon-present"></i> @lang('texts.products')</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ route('invoices.index') }}">
|
||||
<i class="nav-icon icon-notebook"></i> @lang('texts.invoices')</a>
|
||||
|
@ -19,3 +19,4 @@ Breadcrumbs::for('clients.edit', function($trail, $client) {
|
||||
Breadcrumbs::for('clients.create', function($trail) {
|
||||
$trail->parent('clients');
|
||||
});
|
||||
|
||||
|
@ -43,6 +43,7 @@ Route::group(['middleware' => ['auth:user', 'db']], function () {
|
||||
Route::get('logout', 'Auth\LoginController@logout')->name('user.logout');
|
||||
Route::resource('invoices', 'InvoiceController'); // name = (invoices. index / create / show / update / destroy / edit
|
||||
Route::resource('clients', 'ClientController'); // name = (clients. index / create / show / update / destroy / edit
|
||||
Route::resource('c', 'CController'); // name = (clients. index / create / show / update / destroy / edit
|
||||
Route::resource('user', 'UserProfileController'); // name = (clients. index / create / show / update / destroy / edit
|
||||
Route::get('settings', 'SettingsController@index')->name('user.settings');
|
||||
|
||||
|
@ -57,6 +57,6 @@
|
||||
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||
},
|
||||
"include": [
|
||||
"resources/js/src/**/*"
|
||||
"resources/js/**/*"
|
||||
]
|
||||
}
|
7
webpack.mix.js
vendored
7
webpack.mix.js
vendored
@ -19,14 +19,18 @@ mix.webpackConfig({
|
||||
rules: [
|
||||
{
|
||||
test: /\.ts$/,
|
||||
loader: 'ts-loader'
|
||||
loader: 'ts-loader',
|
||||
options: { appendTsSuffixTo: [/\.vue$/] },
|
||||
exclude: /node_modules/,
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
mix.js('resources/js/src/client/client_edit.ts', 'public/js');
|
||||
mix.js('resources/js/src/c/client-edit.ts', 'public/js');
|
||||
mix.js('resources/js/src/client/client_create.ts', 'public/js');
|
||||
mix.js('resources/js/src/settings/localization.ts', 'public/js');
|
||||
mix.js('resources/js/app.js', 'public/js/vendor');
|
||||
mix.js('node_modules/@coreui/coreui/dist/js/coreui.js', 'public/js');
|
||||
|
||||
@ -38,6 +42,7 @@ mix.minify('public/js/ninja.js');
|
||||
mix.minify('public/js/coreui.js');
|
||||
mix.minify('public/js/client_edit.js');
|
||||
mix.minify('public/js/client_create.js');
|
||||
mix.minify('public/js/localization.js');
|
||||
|
||||
mix.styles([
|
||||
'node_modules/@coreui/coreui/dist/css/coreui.css',
|
||||
|
Loading…
x
Reference in New Issue
Block a user