From 787602a9928e290907c622631f1bc2af2a887893 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 21 Nov 2017 09:35:28 +0200 Subject: [PATCH] Show tasks in client portal #1370 --- .../Controllers/ClientPortalController.php | 51 ++++++++++++++++++- app/Http/Middleware/Authenticate.php | 5 ++ app/Models/Client.php | 3 +- app/Ninja/Repositories/TaskRepository.php | 33 ++++++++++++ app/Ninja/Transformers/ClientTransformer.php | 2 + ..._11_15_114422_add_subdomain_to_lookups.php | 4 ++ resources/lang/en/texts.php | 1 + resources/views/clients/edit.blade.php | 9 +++- resources/views/public/header.blade.php | 5 ++ routes/web.php | 2 + 10 files changed, 110 insertions(+), 5 deletions(-) diff --git a/app/Http/Controllers/ClientPortalController.php b/app/Http/Controllers/ClientPortalController.php index 0a874a514a4d..8cbb1b1dfafe 100644 --- a/app/Http/Controllers/ClientPortalController.php +++ b/app/Http/Controllers/ClientPortalController.php @@ -14,6 +14,7 @@ use App\Ninja\Repositories\CreditRepository; use App\Ninja\Repositories\DocumentRepository; use App\Ninja\Repositories\InvoiceRepository; use App\Ninja\Repositories\PaymentRepository; +use App\Ninja\Repositories\TaskRepository; use App\Services\PaymentService; use Auth; use Barracuda\ArchiveStream\ZipArchive; @@ -36,7 +37,14 @@ class ClientPortalController extends BaseController private $paymentRepo; private $documentRepo; - public function __construct(InvoiceRepository $invoiceRepo, PaymentRepository $paymentRepo, ActivityRepository $activityRepo, DocumentRepository $documentRepo, PaymentService $paymentService, CreditRepository $creditRepo) + public function __construct( + InvoiceRepository $invoiceRepo, + PaymentRepository $paymentRepo, + ActivityRepository $activityRepo, + DocumentRepository $documentRepo, + PaymentService $paymentService, + CreditRepository $creditRepo, + TaskRepository $taskRepo) { $this->invoiceRepo = $invoiceRepo; $this->paymentRepo = $paymentRepo; @@ -44,6 +52,7 @@ class ClientPortalController extends BaseController $this->documentRepo = $documentRepo; $this->paymentService = $paymentService; $this->creditRepo = $creditRepo; + $this->taskRepo = $taskRepo; } public function view($invitationKey) @@ -556,6 +565,46 @@ class ClientPortalController extends BaseController return $this->creditRepo->getClientDatatable($contact->client_id); } + public function taskIndex() + { + if (! $contact = $this->getContact()) { + return $this->returnError(); + } + + $account = $contact->account; + $account->loadLocalizationSettings($contact->client); + + if (! $contact->client->show_tasks_in_portal) { + return redirect()->to($account->enable_client_portal_dashboard ? '/client/dashboard' : '/client/payment_methods/'); + } + + if (! $account->enable_client_portal) { + return $this->returnError(); + } + + $color = $account->primary_color ? $account->primary_color : '#0b4d78'; + + $data = [ + 'color' => $color, + 'account' => $account, + 'title' => trans('texts.tasks'), + 'entityType' => ENTITY_TASK, + 'columns' => Utils::trans(['project', 'date', 'duration', 'description']), + 'sortColumn' => 1, + ]; + + return response()->view('public_list', $data); + } + + public function taskDatatable() + { + if (! $contact = $this->getContact()) { + return false; + } + + return $this->taskRepo->getClientDatatable($contact->client_id); + } + public function documentIndex() { if (! $contact = $this->getContact()) { diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php index e7bbcd467268..c233ac7e770d 100644 --- a/app/Http/Middleware/Authenticate.php +++ b/app/Http/Middleware/Authenticate.php @@ -66,6 +66,7 @@ class Authenticate if (! $contact) { return \Redirect::to('client/session_expired'); } + $account = $contact->account; if (Auth::guard('user')->check() && Auth::user('user')->account_id == $account->id) { @@ -85,6 +86,10 @@ class Authenticate if (env('PHANTOMJS_SECRET') && $request->phantomjs_secret && hash_equals(env('PHANTOMJS_SECRET'), $request->phantomjs_secret)) { $authenticated = true; } + + if ($authenticated) { + $request->merge(['contact' => $contact]); + } } if (! $authenticated) { diff --git a/app/Models/Client.php b/app/Models/Client.php index 104325b080a9..10ef1a1510c9 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -59,10 +59,9 @@ class Client extends EntityModel 'shipping_state', 'shipping_postal_code', 'shipping_country_id', + 'show_tasks_in_portal', ]; - - /** * @return array */ diff --git a/app/Ninja/Repositories/TaskRepository.php b/app/Ninja/Repositories/TaskRepository.php index 327164741e30..56da52cc62eb 100644 --- a/app/Ninja/Repositories/TaskRepository.php +++ b/app/Ninja/Repositories/TaskRepository.php @@ -8,6 +8,7 @@ use App\Models\Task; use Auth; use Session; use DB; +use Utils; class TaskRepository extends BaseRepository { @@ -101,6 +102,38 @@ class TaskRepository extends BaseRepository return $query; } + public function getClientDatatable($clientId) + { + $query = DB::table('tasks') + ->leftJoin('projects', 'projects.id', '=', 'tasks.project_id') + ->where('tasks.client_id', '=', $clientId) + ->where('tasks.is_deleted', '=', false) + ->whereNull('tasks.invoice_id') + ->select( + 'tasks.description', + 'tasks.time_log', + 'tasks.time_log as duration', + DB::raw("SUBSTRING(time_log, 3, 10) date"), + 'projects.name as project' + ); + + $table = \Datatable::query($query) + ->addColumn('project', function ($model) { + return $model->project; + }) + ->addColumn('date', function ($model) { + return Task::calcStartTime($model); + }) + ->addColumn('duration', function ($model) { + return Utils::formatTime(Task::calcDuration($model)); + }) + ->addColumn('description', function ($model) { + return $model->description; + }); + + return $table->make(); + } + public function save($publicId, $data, $task = null) { if ($task) { diff --git a/app/Ninja/Transformers/ClientTransformer.php b/app/Ninja/Transformers/ClientTransformer.php index a4277045a9b5..1644e6e25e5d 100644 --- a/app/Ninja/Transformers/ClientTransformer.php +++ b/app/Ninja/Transformers/ClientTransformer.php @@ -44,6 +44,7 @@ class ClientTransformer extends EntityTransformer * @SWG\Property(property="shipping_state", type="string", example="NY") * @SWG\Property(property="shipping_postal_code", type="string", example=10010) * @SWG\Property(property="shipping_country_id", type="integer", example=840) + * @SWG\Property(property="show_tasks_in_portal", type="boolean", example=false) */ protected $defaultIncludes = [ 'contacts', @@ -149,6 +150,7 @@ class ClientTransformer extends EntityTransformer 'shipping_state' => $client->shipping_state, 'shipping_postal_code' => $client->shipping_postal_code, 'shipping_country_id' => (int) $client->shipping_country_id, + 'show_tasks_in_portal' => (bool) $client->show_tasks_in_portal, ]); } } diff --git a/database/migrations/2017_11_15_114422_add_subdomain_to_lookups.php b/database/migrations/2017_11_15_114422_add_subdomain_to_lookups.php index 3469fa0ff5e5..01194bb540a6 100644 --- a/database/migrations/2017_11_15_114422_add_subdomain_to_lookups.php +++ b/database/migrations/2017_11_15_114422_add_subdomain_to_lookups.php @@ -33,6 +33,8 @@ class AddSubdomainToLookups extends Migration $table->string('shipping_state')->nullable(); $table->string('shipping_postal_code')->nullable(); $table->unsignedInteger('shipping_country_id')->nullable(); + $table->boolean('show_tasks_in_portal')->default(0); + $table->boolean('send_reminders')->default(1); }); Schema::table('clients', function ($table) { @@ -68,6 +70,8 @@ class AddSubdomainToLookups extends Migration $table->dropColumn('shipping_state'); $table->dropColumn('shipping_postal_code'); $table->dropColumn('shipping_country_id'); + $table->dropColumn('show_tasks_in_portal'); + $table->dropColumn('send_reminders'); }); Schema::table('account_gateways', function ($table) { diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 221a2334aac4..74ebe7f3d60f 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -2544,6 +2544,7 @@ $LANG = array( 'show_shipping_address_help' => 'Require client to provide their shipping address', 'ship_to_billing_address' => 'Ship to billing address', 'delivery_note' => 'Delivery Note', + 'show_tasks_in_portal' => 'Show tasks in the client portal' ); diff --git a/resources/views/clients/edit.blade.php b/resources/views/clients/edit.blade.php index e15227c357a4..073f181531d5 100644 --- a/resources/views/clients/edit.blade.php +++ b/resources/views/clients/edit.blade.php @@ -24,6 +24,7 @@ @if ($client) {!! Former::populate($client) !!} {!! Former::populateField('task_rate', floatval($client->task_rate) ? Utils::roundSignificant($client->task_rate) : '') !!} + {!! Former::populateField('show_tasks_in_portal', intval($client->show_tasks_in_portal)) !!} {!! Former::hidden('public_id') !!} @else {!! Former::populateField('invoice_number_counter', 1) !!} @@ -172,7 +173,7 @@
-
+
{!! Former::select('currency_id')->addOption('','') ->placeholder($account->currency ? $account->currency->name : '') ->fromQuery($currencies, 'name', 'id') !!} @@ -198,6 +199,10 @@ {!! Former::text('task_rate') ->placeholder($account->present()->taskRate) ->help('task_rate_help') !!} + {!! Former::checkbox('show_tasks_in_portal') + ->text(trans('texts.show_tasks_in_portal')) + ->label('client_portal') + ->value(1) !!} @endif
diff --git a/resources/views/public/header.blade.php b/resources/views/public/header.blade.php index 693ac33726f7..e48b95406fe7 100644 --- a/resources/views/public/header.blade.php +++ b/resources/views/public/header.blade.php @@ -86,6 +86,11 @@ {!! link_to('/client/dashboard', trans('texts.dashboard') ) !!} @endif + @if (request()->contact && request()->contact->client->show_tasks_in_portal) +
  • + {!! link_to('/client/tasks', trans('texts.tasks') ) !!} +
  • + @endif @if (isset($hasQuotes) && $hasQuotes)
  • {!! link_to('/client/quotes', trans('texts.quotes') ) !!} diff --git a/routes/web.php b/routes/web.php index 94f24f1a7699..9d0f39e9a150 100644 --- a/routes/web.php +++ b/routes/web.php @@ -36,6 +36,7 @@ Route::group(['middleware' => ['lookup:contact', 'auth:client']], function () { Route::post('client/invoices/auto_bill', 'ClientPortalController@setAutoBill'); Route::get('client/documents', 'ClientPortalController@documentIndex'); Route::get('client/payments', 'ClientPortalController@paymentIndex'); + Route::get('client/tasks', 'ClientPortalController@taskIndex'); Route::get('client/dashboard/{contact_key?}', 'ClientPortalController@dashboard'); Route::get('client/documents/js/{documents}/{filename}', 'ClientPortalController@getDocumentVFSJS'); Route::get('client/documents/{invitation_key}/{documents}/{filename?}', 'ClientPortalController@getDocument'); @@ -47,6 +48,7 @@ Route::group(['middleware' => ['lookup:contact', 'auth:client']], function () { Route::get('api/client.recurring_invoices', ['as' => 'api.client.recurring_invoices', 'uses' => 'ClientPortalController@recurringInvoiceDatatable']); Route::get('api/client.documents', ['as' => 'api.client.documents', 'uses' => 'ClientPortalController@documentDatatable']); Route::get('api/client.payments', ['as' => 'api.client.payments', 'uses' => 'ClientPortalController@paymentDatatable']); + Route::get('api/client.tasks', ['as' => 'api.client.tasks', 'uses' => 'ClientPortalController@taskDatatable']); Route::get('api/client.activity', ['as' => 'api.client.activity', 'uses' => 'ClientPortalController@activityDatatable']); });