1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-05 18:52:44 +01:00

Stubs for Client Settings (#2655)

* Clean up Client Show

* Working on Show Client menu action

* working on client view permissions

* Finishing up Client Statement View

* Workig on client settings

* add mix manifest
This commit is contained in:
David Bomba 2019-02-04 23:06:19 +11:00 committed by GitHub
parent a7e557c7a4
commit 1ad19734e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 238025 additions and 179 deletions

View File

@ -2,6 +2,7 @@
namespace App\Datatables;
use App\Datatables\MakesActionMenu;
use App\Filters\ClientFilters;
use App\Models\Client;
use App\Utils\Traits\MakesHash;
@ -31,6 +32,7 @@ class ClientDatatable extends EntityDatatable
{
$this->filter = $filter;
}
/**
* Returns paginated results for the datatable
*
@ -43,20 +45,6 @@ class ClientDatatable extends EntityDatatable
}
/**
* @param int $company_id
* @param bool $userId
* @return mixed
*/
private function find(int $company_id, $userId = false)
{
return $query;
}
/**
* Returns the action dropdown menu
*
@ -79,7 +67,7 @@ class ClientDatatable extends EntityDatatable
/*
* Build a collection of action
*/
$rows = $this->processActions($requested_actions, $rows, Client::class);
$rows = $this->processActionsForDatatable($requested_actions, $rows, Client::class);
/*
* Add a _view_ link directly to the client

View File

@ -25,11 +25,16 @@ trait MakesActionMenu
['action' => 'edit_client_client_id', 'permission' => 'edit_client', 'route' => 'clients.edit', 'key' => 'client_id', 'name' => ctrans('texts.edit')],
['action' => 'create_task_client_id', 'permission' => 'create_task', 'route' => 'tasks.create', 'key' => 'client_id', 'name' => ctrans('texts.new_task')],
['action' => 'create_invoice_client_id', 'permission' => 'create_invoice', 'route' => 'invoices.create', 'key' => 'client_id', 'name' => ctrans('texts.new_invoice')],
['action' => 'create_quote_client_id', 'permission' => 'create_quote', 'route' => 'quotes.create', 'key' => 'client_id', 'name' => ctrans('texts.new_quote')],
['action' => 'create_recurring_invoice_client_id', 'permission' => 'create_recurring_invoice', 'route' => 'recurring_invoices.create', 'key' => 'client_id', 'name' => ctrans('texts.new_recurring_invoice')],
['action' => 'enter_payment_client_id', 'permission' => 'create_payment', 'route' => 'payments.create', 'key' => 'client_id', 'name' => ctrans('texts.enter_payment')],
['action' => 'enter_credit_client_id', 'permission' => 'create_credit', 'route' => 'credits.create', 'key' => 'client_id', 'name' => ctrans('texts.enter_credit')],
['action' => 'enter_expense_client_id', 'permission' => 'create_expense', 'route' => 'expenses.create', 'key' => 'client_id', 'name' => ctrans('texts.enter_expense')]
['action' => 'enter_expense_client_id', 'permission' => 'create_expense', 'route' => 'expenses.create', 'key' => 'client_id', 'name' => ctrans('texts.enter_expense')],
['action' => 'view_statement_client_id', 'permission' => 'edit_client', 'route' => 'client_statement.show', 'key' => 'client_id', 'name' => ctrans('texts.view_statement')],
]);
}
/**
@ -49,7 +54,7 @@ trait MakesActionMenu
* @param Class::class - need so we can harvest entity string
* @return stdClass
*/
public function processActions(array $requested_actions, $rows, $entity)
public function processActionsForDatatable(array $requested_actions, $rows, $entity)
{
$rows->map(function ($row) use ($requested_actions, $entity){
@ -64,6 +69,30 @@ trait MakesActionMenu
}
/**
* Method which allows the generation of a collection of action links
* for use when populating a Button.
*
* @param array $requested_actions - array of requested actions for menu
* @param Class::class - need so we can harvest entity string
* @return stdClass
*/
public function processActionsForButton(array $requested_actions, $entity)
{
$rows = $this->filterActions($requested_actions, auth()->user()->permissions(), auth()->user()->isAdmin());
$updated_actions = $rows->map(function($row) use ($entity){
$row['url'] = route($row['route'], [$row['key'] => $this->encodePrimaryKey($entity->id)]);
return $row;
});
return $updated_actions;
}
/**
* Builds the actions for a single row of a datatable
*

View File

@ -3,6 +3,8 @@
namespace App\Http\Controllers;
use App\Datatables\ClientDatatable;
use App\Datatables\MakesActionMenu;
use App\Factory\ClientFactory;
use App\Http\Requests\Client\CreateClientRequest;
use App\Http\Requests\Client\EditClientRequest;
use App\Http\Requests\Client\ShowClientRequest;
@ -19,7 +21,6 @@ use App\Utils\Traits\MakesHash;
use App\Utils\Traits\MakesMenu;
use App\Utils\Traits\UserSessionAttributes;
use Illuminate\Http\Request;
use App\Factory\ClientFactory;
/**
* Class ClientController
@ -30,6 +31,7 @@ class ClientController extends Controller
use UserSessionAttributes;
use MakesHash;
use MakesMenu;
use MakesActionMenu;
/**
* @var ClientRepository
@ -48,8 +50,11 @@ class ClientController extends Controller
*/
public function __construct(ClientRepository $clientRepo, ClientDatatable $clientDatatable)
{
$this->clientRepo = $clientRepo;
$this->clientDatatable = $clientDatatable;
}
/**
@ -70,6 +75,79 @@ class ClientController extends Controller
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show(ShowClientRequest $request, Client $client)
{
$requested_view_statement_actions = [
'create_invoice_client_id',
'create_task_client_id',
'create_quote_client_id',
'create_recurring_invoice_client_id',
'create_payment_client_id',
'create_expense_client_id'
];
$data = [
'client' => $client,
'company' => auth()->user()->company(),
'meta' => collect([
'google_maps_api_key' => config('ninja.google_maps_api_key'),
'edit_client_permission' => auth()->user()->can('edit', $client),
'edit_client_route' => $this->processActionsForButton(['edit_client_client_id'], $client),
'view_statement_permission' => auth()->user()->can('view', $client),
'view_statement_route' => $this->processActionsForButton(['view_statement_client_id'], $client),
'view_statement_actions' => $this->processActionsForButton($requested_view_statement_actions, $client)
])
];
return view('client.show', $data);
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit(EditClientRequest $request, Client $client)
{
$data = [
'client' => $client,
'settings' => $client,
'pills' => $this->makeEntityTabMenu(Client::class),
'hashed_id' => $this->encodePrimarykey($client->id),
'countries' => Country::all(),
'company' => auth()->user()->company()
];
return view('client.edit', $data);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param App\Models\Client $client
* @return \Illuminate\Http\Response
*/
public function update(UpdateClientRequest $request, Client $client)
{
$client = UpdateClient::dispatchNow($request, $client);
return response()->json($client, 200);
}
/**
* Show the form for creating a new resource.
*
@ -96,75 +174,13 @@ class ClientController extends Controller
*/
public function store(StoreClientRequest $request)
{
$client = StoreClient::dispatchNow($request, new Client);
$client->load('contacts', 'primary_contact');
$client->hashed_id = $this->encodePrimarykey($client->id);
/*
$data = [
'client' => $client,
'hashed_id' => $this->encodePrimarykey($client->id)
];
Log::error(print_r($client,1));
*/
return response()->json($client, 200);
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show(ShowClientRequest $request, Client $client)
{
$data = [
'client' => $client,
'company' => auth()->user()->company(),
'meta' => collect([
'google_maps_api_key' => config('ninja.google_maps_api_key')
])
];
return view('client.show', $data);
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit(EditClientRequest $request, Client $client)
{
$data = [
'client' => $client,
'settings' => [],
'pills' => $this->makeEntityTabMenu(Client::class),
'hashed_id' => $this->encodePrimarykey($client->id),
'countries' => Country::all(),
'company' => auth()->user()->company()
];
return view('client.edit', $data);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param App\Models\Client $client
* @return \Illuminate\Http\Response
*/
public function update(UpdateClientRequest $request, Client $client)
{
$client = UpdateClient::dispatchNow($request, $client);
return response()->json($client, 200);
}
@ -189,6 +205,7 @@ class ClientController extends Controller
{
$action = request()->input('action');
$ids = request()->input('ids');
$clients = Client::withTrashed()->find($ids);
@ -205,6 +222,16 @@ class ClientController extends Controller
}
/**
* Returns a client statement
*
* @return [type] [description]
*/
public function statement()
{
//todo
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace App\Http\Controllers;
/**
* Class ClientStatementController
* @package App\Http\Controllers
*/
class ClientStatementController extends Controller
{
/**
* Displays a client statement view for a given
* client_id.
* @return view
*/
public function show()
{
}
/**
* Updates the show view data dependent on
* configured variables
* @return json
*/
public function update()
{
}
}

View File

@ -29,7 +29,7 @@ class User extends Authenticatable implements MustVerifyEmail
protected $presenter = 'App\Models\Presenters\UserPresenter';
protected $with = ['companies'];
protected $with = ['companies','user_companies'];
/**
* The attributes that are mass assignable.
*
@ -99,7 +99,7 @@ class User extends Authenticatable implements MustVerifyEmail
public function user_company()
{
return $this->user_companies()->where('company_id', $this->getCurrentCompanyId())->first();
return $this->user_companies->where('company_id', $this->getCurrentCompanyId())->first();
}

47
composer.lock generated
View File

@ -1309,16 +1309,16 @@
},
{
"name": "league/flysystem",
"version": "1.0.49",
"version": "1.0.50",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem.git",
"reference": "a63cc83d8a931b271be45148fa39ba7156782ffd"
"reference": "dab4e7624efa543a943be978008f439c333f2249"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/a63cc83d8a931b271be45148fa39ba7156782ffd",
"reference": "a63cc83d8a931b271be45148fa39ba7156782ffd",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/dab4e7624efa543a943be978008f439c333f2249",
"reference": "dab4e7624efa543a943be978008f439c333f2249",
"shasum": ""
},
"require": {
@ -1389,7 +1389,7 @@
"sftp",
"storage"
],
"time": "2018-11-23T23:41:29+00:00"
"time": "2019-02-01T08:50:36+00:00"
},
{
"name": "league/oauth1-client",
@ -2473,16 +2473,16 @@
},
{
"name": "spatie/laravel-html",
"version": "2.20.0",
"version": "2.20.1",
"source": {
"type": "git",
"url": "https://github.com/spatie/laravel-html.git",
"reference": "85ea814d6f8077eafd749e603547b9a4b368bb56"
"reference": "a4cf0a0780d5ca1adc0860a8379dd4b1a23daafb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/laravel-html/zipball/85ea814d6f8077eafd749e603547b9a4b368bb56",
"reference": "85ea814d6f8077eafd749e603547b9a4b368bb56",
"url": "https://api.github.com/repos/spatie/laravel-html/zipball/a4cf0a0780d5ca1adc0860a8379dd4b1a23daafb",
"reference": "a4cf0a0780d5ca1adc0860a8379dd4b1a23daafb",
"shasum": ""
},
"require": {
@ -2537,7 +2537,7 @@
"html",
"spatie"
],
"time": "2019-01-18T08:30:17+00:00"
"time": "2019-02-01T22:45:56+00:00"
},
{
"name": "swiftmailer/swiftmailer",
@ -5069,16 +5069,16 @@
},
{
"name": "phpunit/phpunit",
"version": "7.5.2",
"version": "7.5.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "7c89093bd00f7d5ddf0ab81dee04f801416b4944"
"reference": "2cb759721e53bc05f56487f628c6b9fbb6c18746"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/7c89093bd00f7d5ddf0ab81dee04f801416b4944",
"reference": "7c89093bd00f7d5ddf0ab81dee04f801416b4944",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2cb759721e53bc05f56487f628c6b9fbb6c18746",
"reference": "2cb759721e53bc05f56487f628c6b9fbb6c18746",
"shasum": ""
},
"require": {
@ -5149,7 +5149,7 @@
"testing",
"xunit"
],
"time": "2019-01-15T08:19:08+00:00"
"time": "2019-02-01T05:24:07+00:00"
},
{
"name": "sebastian/code-unit-reverse-lookup",
@ -5318,28 +5318,31 @@
},
{
"name": "sebastian/environment",
"version": "4.0.2",
"version": "4.1.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/environment.git",
"reference": "4a43e9af57b4afa663077b9bc85255dbc6e8a2bd"
"reference": "6fda8ce1974b62b14935adc02a9ed38252eca656"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/4a43e9af57b4afa663077b9bc85255dbc6e8a2bd",
"reference": "4a43e9af57b4afa663077b9bc85255dbc6e8a2bd",
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6fda8ce1974b62b14935adc02a9ed38252eca656",
"reference": "6fda8ce1974b62b14935adc02a9ed38252eca656",
"shasum": ""
},
"require": {
"php": "^7.1"
},
"require-dev": {
"phpunit/phpunit": "^7.4"
"phpunit/phpunit": "^7.5"
},
"suggest": {
"ext-posix": "*"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.0-dev"
"dev-master": "4.1-dev"
}
},
"autoload": {
@ -5364,7 +5367,7 @@
"environment",
"hhvm"
],
"time": "2019-01-28T15:26:03+00:00"
"time": "2019-02-01T05:27:49+00:00"
},
{
"name": "sebastian/exporter",

View File

@ -47,9 +47,11 @@
"socket.io-client": "^2.1.1",
"ts-loader": "3.5.0",
"typescript": "^3.1.6",
"vue-affix": "^0.4.0",
"vue-chartjs": "^3.4.0",
"vue-events": "^3.1.0",
"vue-multiselect": "^2.1.3",
"vue-scrollactive": "^0.8.0",
"vue-select": "^2.5.1",
"vue-toastr": "^2.0.16",
"vuex": "^3.1.0"

16916
public/css/ninja.css vendored

File diff suppressed because one or more lines are too long

16916
public/css/ninja.min.css vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

20483
public/js/client_edit.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

24768
public/js/client_list.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

84
public/js/client_settings.js vendored Normal file
View File

@ -0,0 +1,84 @@
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "/";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ({
/***/ "./resources/js/src/client/client_settings.ts":
/***/ (function(module, exports) {
"use strict";
throw new Error("Module build failed: Error: ENOENT: no such file or directory, open '/Users/davidbomba/Development/invoice-ninja/resources/js/src/client/client_settings.ts'");
/***/ }),
/***/ 0:
/***/ (function(module, exports, __webpack_require__) {
module.exports = __webpack_require__("./resources/js/src/client/client_settings.ts");
/***/ })
/******/ });

84
public/js/client_settings.min.js vendored Normal file
View File

@ -0,0 +1,84 @@
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "/";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ({
/***/ "./resources/js/src/client/client_settings.ts":
/***/ (function(module, exports) {
"use strict";
throw new Error("Module build failed: Error: ENOENT: no such file or directory, open '/Users/davidbomba/Development/invoice-ninja/resources/js/src/client/client_settings.ts'");
/***/ }),
/***/ 0:
/***/ (function(module, exports, __webpack_require__) {
module.exports = __webpack_require__("./resources/js/src/client/client_settings.ts");
/***/ })
/******/ });

17040
public/js/client_show.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

12588
public/js/coreui.js vendored

File diff suppressed because one or more lines are too long

12591
public/js/coreui.min.js vendored

File diff suppressed because one or more lines are too long

11560
public/js/localization.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
public/js/ninja.js vendored
View File

@ -1 +0,0 @@

View File

@ -1 +0,0 @@

18
public/mix-manifest.json Normal file
View File

@ -0,0 +1,18 @@
{
"/js/client_list.js": "/js/client_list.js?id=88d90244e855305bcfff",
"/js/client_edit.js": "/js/client_edit.js?id=23f35208ca7847252c92",
"/js/client_show.js": "/js/client_show.js?id=be7307363fb5779cdb24",
"/js/client_create.js": "/js/client_create.js?id=ca114ece0bbf0eb79d38",
"/js/localization.js": "/js/localization.js?id=85f6f7672f9cf65d9745",
"/js/coreui.js": "/js/coreui.js?id=9cfda6dd6df9aaeea844",
"/js/ninja.js": "/js/ninja.js?id=d41d8cd98f00b204e980",
"/js/ninja.min.js": "/js/ninja.min.js?id=d41d8cd98f00b204e980",
"/js/coreui.min.js": "/js/coreui.min.js?id=9cfda6dd6df9aaeea844",
"/js/client_show.min.js": "/js/client_show.min.js?id=be7307363fb5779cdb24",
"/js/client_edit.min.js": "/js/client_edit.min.js?id=23f35208ca7847252c92",
"/js/client_create.min.js": "/js/client_create.min.js?id=ca114ece0bbf0eb79d38",
"/js/client_list.min.js": "/js/client_list.min.js?id=88d90244e855305bcfff",
"/js/localization.min.js": "/js/localization.min.js?id=85f6f7672f9cf65d9745",
"/css/ninja.css": "/css/ninja.css?id=28421bc494c5086ac359",
"/css/ninja.min.css": "/css/ninja.min.css?id=28421bc494c5086ac359"
}

View File

@ -13,7 +13,8 @@ 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'));
Vue.component('client-settings', require('../components/client/ClientSettings.vue'));
window.onload = function () {
const app = new Vue({

View File

@ -0,0 +1,421 @@
<template>
<div class="row">
<div class="col-2">
<affix class="menu sidebar-menu" relative-element-selector="#example-content" :offset="{ top: 50, bottom:100 }" :scroll-affix="false" style="width: 200px">
<div class="menu-label">
<h2></h2>
</div>
<scrollactive
class="menu-list"
active-class="is-active"
:offset="50"
:duration="800"
:exact="true"
>
<a href="#intro" class="scrollactive-item" title="Intro">Intro</a><br>
<a href="#standard-affix" class="scrollactive-item" title="Standard Affix">Standard Affix</a><br>
<a href="#scroll-affix" class="scrollactive-item" title="Scroll Affix">Scroll Affix</a><br>
<a href="#markup-1" class="scrollactive-item" title="Markup 1">Markup 1</a><br>
<a href="#markup-2" class="scrollactive-item" title="Markup 2">Markup 2</a><br>
<a href="#markup-3" class="scrollactive-item" title="Markup 3">Markup 3</a><br>
</scrollactive>
</affix>
</div>
<div class="col-10">
<div id="example-content">
<section id="intro">
<div class="card">
<div class="card-header bg-primary2">{{ trans('texts.edit_client') }}</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
</div>
</div>
</section>
<section id="standard-affix">
<div class="card">
<div class="card-header bg-primary2">{{ trans('texts.edit_client') }}</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
</div>
</div>
</section>
<section id="scroll-affix">
<div class="card">
<div class="card-header bg-primary2">{{ trans('texts.edit_client') }}</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
</div>
</div>
</section>
<section id="markup-1">
<div class="card">
<div class="card-header bg-primary2">{{ trans('texts.edit_client') }}</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
</div>
</div>
</section>
<section id="markup-2">
<div class="card">
<div class="card-header bg-primary2">{{ trans('texts.edit_client') }}</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
</div>
</div>
</section>
<section id="markup-3">
<div class="card">
<div class="card-header bg-primary2">{{ trans('texts.edit_client') }}</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="trans('texts.client_name')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
</div>
</div>
</section>
</div>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { Affix } from 'vue-affix';
var VueScrollactive = require('vue-scrollactive');
Vue.use(VueScrollactive);
export default {
components: {
Affix,
},
props: ['settings'],
mounted() {
},
methods: {
onItemChanged(event, currentItem, lastActiveItem) {
// your logic
},
},
}
</script>
<style>
a.scrollactive-item.is-active {
color: #42b983;
font-family: helvetica;
text-decoration: none;
}
a.scrollactive-item.is-active:hover {
text-decoration: none;
color: #42b983;
}
a.scrollactive-item.is-active:active {
color: #42b983;
}
.menu-list a {
color: black;
font-family: helvetica;
text-decoration: none;
}
.menu-list a:hover {
text-decoration: none;
color: #42b983;
}
.menu-list a:active {
color: #42b983;
text-decoration: none;
}
</style>

View File

@ -8,30 +8,26 @@
<div class="float-right">
<div class="btn-group ml-2">
<button type="button" class="btn btn-lg btn-secondary">{{ trans('texts.edit_client') }}</button>
<button type="button" class="btn btn-lg btn-secondary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<button type="button" class="btn btn-lg btn-secondary" :disabled="editClientIsDisabled" v-for="link in this.meta.edit_client_route" @click="goToUrl(link.url)">{{ trans('texts.edit_client') }}</button>
<button type="button" class="btn btn-lg btn-secondary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" :disabled="editClientIsDisabled">
<span class="sr-only">Toggle Dropdown</span>
</button>
<div class="dropdown-menu" x-placement="top-start" style="position: absolute; transform: translate3d(189px, -2px, 0px); top: 0px; left: 0px; will-change: transform;">
<a class="dropdown-item" href="#">Action</a>
<a class="dropdown-item" href="#">Another action</a>
<a class="dropdown-item" href="#">Something else here</a>
<a class="dropdown-item" href="#" @click="itemAction('archive', client, rowIndex)" v-if="client.deleted_at == null">{{ trans('texts.archive') }}</a>
<a class="dropdown-item" href="#" @click="itemAction('restore', client, rowIndex)" v-if="client.is_deleted == 1 || client.deleted_at != null">{{ trans('texts.restore') }}</a>
<a class="dropdown-item" href="#" @click="itemAction('delete', client, rowIndex)" v-if="client.is_deleted == 0">{{ trans('texts.delete') }}</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#">Separated link</a>
<a class="dropdown-item" @click="itemAction('purge', client, rowIndex)">{{ trans('texts.purge_client') }}</a>
</div>
</div>
<div class="btn-group ml-2">
<button type="button" class="btn btn-lg btn-primary">{{ trans('texts.view_statement') }}</button>
<button type="button" class="btn btn-lg btn-primary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<button type="button" class="btn btn-lg btn-primary" :disabled="viewStatementIsDisabled" v-for="link in this.meta.view_statement_route" @click="goToUrl(link.url)">{{ trans('texts.view_statement') }}</button>
<button type="button" class="btn btn-lg btn-primary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" :disabled="viewStatementIsDisabled">
<span class="sr-only">Toggle Dropdown</span>
</button>
<div class="dropdown-menu" x-placement="top-start" style="position: absolute; transform: translate3d(189px, -2px, 0px); top: 0px; left: 0px; will-change: transform;">
<a class="dropdown-item" href="#">Action</a>
<a class="dropdown-item" href="#">Another action</a>
<a class="dropdown-item" href="#">Something else here</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#">Separated link</a>
<a class="dropdown-item" v-for="link in this.meta.view_statement_actions" :href="link.url">{{ link.name }}</a>
</div>
</div>
@ -102,7 +98,7 @@
</div>
<div v-if="">
<div v-if="this.meta.google_maps_api_key">
<iframe
width="100%"
@ -123,10 +119,14 @@
export default {
props: ['client', 'company', 'meta'],
mounted() {
console.dir(this.meta.google_maps_api_key)
console.dir(this.clientAddress)
},
methods: {
goToUrl(url) {
location.href=url
}
},
computed: {
mapUrl: {
get: function() {
@ -158,7 +158,17 @@ export default {
return encodeURIComponent(addressArray.join(","))
}
}
},
viewStatementIsDisabled() :any
{
return ! this.meta.view_statement_permission
},
editClientIsDisabled() :any
{
return ! this.meta.edit_client_permission
}
}
}

View File

@ -48,8 +48,9 @@
@endforeach
<div class="tab-pane fade" id="pills-settings" role="tabpanel" aria-labelledby="pills-settings-tab">
<div class="tab-pane fade" id="pills-settings" role="tabpanel" aria-labelledby="pills-settings-tab" style="background-color: #e4e5e6; padding: 0px;">
<client-settings :settings="{{ $settings }}"></client-settings>
</div>

View File

@ -40,20 +40,45 @@ Route::get('auth/{provider}/callback', 'Auth\LoginController@handleProviderCallb
Route::group(['middleware' => ['auth:user', 'db']], function () {
Route::resource('dashboard', 'DashboardController'); // name = (dashboard. index / create / show / update / destroy / edit
Route::get('logout', 'Auth\LoginController@logout')->name('user.logout');
Route::resource('invoices', 'InvoiceController'); // name = (invoices. index / create / show / update / destroy / edit
Route::post('invoices/bulk', 'InvoiceController@bulk')->name('invoices.bulk');
Route::resource('quotes', 'QuoteController'); // name = (quotes. index / create / show / update / destroy / edit
Route::post('quotes/bulk', 'QuoteController@bulk')->name('quotes.bulk');
Route::resource('recurring_invoices', 'RecurringInvoiceController'); // name = (recurring_invoices. index / create / show / update / destroy / edit
Route::post('recurring_invoices/bulk', 'RecurringInvoiceController@bulk')->name('recurring_invoices.bulk');
Route::resource('clients', 'ClientController'); // name = (clients. index / create / show / update / destroy / edit
Route::post('clients/bulk', 'ClientController@bulk')->name('clients.bulk');
Route::resource('client_statement', 'ClientStatementController@statement'); // name = (client_statement. index / create / show / update / destroy / edit
Route::resource('tasks', 'TaskController'); // name = (tasks. index / create / show / update / destroy / edit
Route::post('tasks/bulk', 'TaskController@bulk')->name('tasks.bulk');
Route::resource('payments', 'PaymentController'); // name = (payments. index / create / show / update / destroy / edit
Route::post('payments/bulk', 'PaymentController@bulk')->name('payments.bulk');
Route::resource('credits', 'CreditController'); // name = (credits. index / create / show / update / destroy / edit
Route::post('credits/bulk', 'CreditController@bulk')->name('credits.bulk');
Route::resource('expenses', 'ExpenseController'); // name = (expenses. index / create / show / update / destroy / edit
Route::post('expenses/bulk', 'ExpenseController@bulk')->name('expenses.bulk');
Route::resource('user', 'UserProfileController'); // name = (clients. index / create / show / update / destroy / edit
Route::get('settings', 'SettingsController@index')->name('user.settings');