Show tasks in client portal #1370

This commit is contained in:
Hillel Coren 2017-11-21 09:35:28 +02:00
parent 3ae70383af
commit 787602a992
10 changed files with 110 additions and 5 deletions

View File

@ -14,6 +14,7 @@ use App\Ninja\Repositories\CreditRepository;
use App\Ninja\Repositories\DocumentRepository; use App\Ninja\Repositories\DocumentRepository;
use App\Ninja\Repositories\InvoiceRepository; use App\Ninja\Repositories\InvoiceRepository;
use App\Ninja\Repositories\PaymentRepository; use App\Ninja\Repositories\PaymentRepository;
use App\Ninja\Repositories\TaskRepository;
use App\Services\PaymentService; use App\Services\PaymentService;
use Auth; use Auth;
use Barracuda\ArchiveStream\ZipArchive; use Barracuda\ArchiveStream\ZipArchive;
@ -36,7 +37,14 @@ class ClientPortalController extends BaseController
private $paymentRepo; private $paymentRepo;
private $documentRepo; 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->invoiceRepo = $invoiceRepo;
$this->paymentRepo = $paymentRepo; $this->paymentRepo = $paymentRepo;
@ -44,6 +52,7 @@ class ClientPortalController extends BaseController
$this->documentRepo = $documentRepo; $this->documentRepo = $documentRepo;
$this->paymentService = $paymentService; $this->paymentService = $paymentService;
$this->creditRepo = $creditRepo; $this->creditRepo = $creditRepo;
$this->taskRepo = $taskRepo;
} }
public function view($invitationKey) public function view($invitationKey)
@ -556,6 +565,46 @@ class ClientPortalController extends BaseController
return $this->creditRepo->getClientDatatable($contact->client_id); 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() public function documentIndex()
{ {
if (! $contact = $this->getContact()) { if (! $contact = $this->getContact()) {

View File

@ -66,6 +66,7 @@ class Authenticate
if (! $contact) { if (! $contact) {
return \Redirect::to('client/session_expired'); return \Redirect::to('client/session_expired');
} }
$account = $contact->account; $account = $contact->account;
if (Auth::guard('user')->check() && Auth::user('user')->account_id == $account->id) { 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)) { if (env('PHANTOMJS_SECRET') && $request->phantomjs_secret && hash_equals(env('PHANTOMJS_SECRET'), $request->phantomjs_secret)) {
$authenticated = true; $authenticated = true;
} }
if ($authenticated) {
$request->merge(['contact' => $contact]);
}
} }
if (! $authenticated) { if (! $authenticated) {

View File

@ -59,10 +59,9 @@ class Client extends EntityModel
'shipping_state', 'shipping_state',
'shipping_postal_code', 'shipping_postal_code',
'shipping_country_id', 'shipping_country_id',
'show_tasks_in_portal',
]; ];
/** /**
* @return array * @return array
*/ */

View File

@ -8,6 +8,7 @@ use App\Models\Task;
use Auth; use Auth;
use Session; use Session;
use DB; use DB;
use Utils;
class TaskRepository extends BaseRepository class TaskRepository extends BaseRepository
{ {
@ -101,6 +102,38 @@ class TaskRepository extends BaseRepository
return $query; 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) public function save($publicId, $data, $task = null)
{ {
if ($task) { if ($task) {

View File

@ -44,6 +44,7 @@ class ClientTransformer extends EntityTransformer
* @SWG\Property(property="shipping_state", type="string", example="NY") * @SWG\Property(property="shipping_state", type="string", example="NY")
* @SWG\Property(property="shipping_postal_code", type="string", example=10010) * @SWG\Property(property="shipping_postal_code", type="string", example=10010)
* @SWG\Property(property="shipping_country_id", type="integer", example=840) * @SWG\Property(property="shipping_country_id", type="integer", example=840)
* @SWG\Property(property="show_tasks_in_portal", type="boolean", example=false)
*/ */
protected $defaultIncludes = [ protected $defaultIncludes = [
'contacts', 'contacts',
@ -149,6 +150,7 @@ class ClientTransformer extends EntityTransformer
'shipping_state' => $client->shipping_state, 'shipping_state' => $client->shipping_state,
'shipping_postal_code' => $client->shipping_postal_code, 'shipping_postal_code' => $client->shipping_postal_code,
'shipping_country_id' => (int) $client->shipping_country_id, 'shipping_country_id' => (int) $client->shipping_country_id,
'show_tasks_in_portal' => (bool) $client->show_tasks_in_portal,
]); ]);
} }
} }

View File

@ -33,6 +33,8 @@ class AddSubdomainToLookups extends Migration
$table->string('shipping_state')->nullable(); $table->string('shipping_state')->nullable();
$table->string('shipping_postal_code')->nullable(); $table->string('shipping_postal_code')->nullable();
$table->unsignedInteger('shipping_country_id')->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) { Schema::table('clients', function ($table) {
@ -68,6 +70,8 @@ class AddSubdomainToLookups extends Migration
$table->dropColumn('shipping_state'); $table->dropColumn('shipping_state');
$table->dropColumn('shipping_postal_code'); $table->dropColumn('shipping_postal_code');
$table->dropColumn('shipping_country_id'); $table->dropColumn('shipping_country_id');
$table->dropColumn('show_tasks_in_portal');
$table->dropColumn('send_reminders');
}); });
Schema::table('account_gateways', function ($table) { Schema::table('account_gateways', function ($table) {

View File

@ -2544,6 +2544,7 @@ $LANG = array(
'show_shipping_address_help' => 'Require client to provide their shipping address', 'show_shipping_address_help' => 'Require client to provide their shipping address',
'ship_to_billing_address' => 'Ship to billing address', 'ship_to_billing_address' => 'Ship to billing address',
'delivery_note' => 'Delivery Note', 'delivery_note' => 'Delivery Note',
'show_tasks_in_portal' => 'Show tasks in the client portal'
); );

View File

@ -24,6 +24,7 @@
@if ($client) @if ($client)
{!! Former::populate($client) !!} {!! Former::populate($client) !!}
{!! Former::populateField('task_rate', floatval($client->task_rate) ? Utils::roundSignificant($client->task_rate) : '') !!} {!! 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') !!} {!! Former::hidden('public_id') !!}
@else @else
{!! Former::populateField('invoice_number_counter', 1) !!} {!! Former::populateField('invoice_number_counter', 1) !!}
@ -172,7 +173,7 @@
<div role="tabpanel"> <div role="tabpanel">
<ul class="nav nav-tabs" role="tablist" style="border: none"> <ul class="nav nav-tabs" role="tablist" style="border: none">
<li role="presentation" class="active"> <li role="presentation" class="active">
<a href="#defaults" aria-controls="defaults" role="tab" data-toggle="tab">{{ trans('texts.defaults') }}</a> <a href="#settings" aria-controls="settings" role="tab" data-toggle="tab">{{ trans('texts.settings') }}</a>
</li> </li>
<li role="presentation"> <li role="presentation">
<a href="#notes" aria-controls="notes" role="tab" data-toggle="tab">{{ trans('texts.notes') }}</a> <a href="#notes" aria-controls="notes" role="tab" data-toggle="tab">{{ trans('texts.notes') }}</a>
@ -183,7 +184,7 @@
</ul> </ul>
</div> </div>
<div class="tab-content" style="padding-top:24px;"> <div class="tab-content" style="padding-top:24px;">
<div role="tabpanel" class="tab-pane active" id="defaults"> <div role="tabpanel" class="tab-pane active" id="settings">
{!! Former::select('currency_id')->addOption('','') {!! Former::select('currency_id')->addOption('','')
->placeholder($account->currency ? $account->currency->name : '') ->placeholder($account->currency ? $account->currency->name : '')
->fromQuery($currencies, 'name', 'id') !!} ->fromQuery($currencies, 'name', 'id') !!}
@ -198,6 +199,10 @@
{!! Former::text('task_rate') {!! Former::text('task_rate')
->placeholder($account->present()->taskRate) ->placeholder($account->present()->taskRate)
->help('task_rate_help') !!} ->help('task_rate_help') !!}
{!! Former::checkbox('show_tasks_in_portal')
->text(trans('texts.show_tasks_in_portal'))
->label('client_portal')
->value(1) !!}
@endif @endif
</div> </div>
<div role="tabpanel" class="tab-pane" id="notes"> <div role="tabpanel" class="tab-pane" id="notes">

View File

@ -86,6 +86,11 @@
{!! link_to('/client/dashboard', trans('texts.dashboard') ) !!} {!! link_to('/client/dashboard', trans('texts.dashboard') ) !!}
</li> </li>
@endif @endif
@if (request()->contact && request()->contact->client->show_tasks_in_portal)
<li {!! Request::is('*client/tasks') ? 'class="active"' : '' !!}>
{!! link_to('/client/tasks', trans('texts.tasks') ) !!}
</li>
@endif
@if (isset($hasQuotes) && $hasQuotes) @if (isset($hasQuotes) && $hasQuotes)
<li {!! Request::is('*client/quotes') ? 'class="active"' : '' !!}> <li {!! Request::is('*client/quotes') ? 'class="active"' : '' !!}>
{!! link_to('/client/quotes', trans('texts.quotes') ) !!} {!! link_to('/client/quotes', trans('texts.quotes') ) !!}

View File

@ -36,6 +36,7 @@ Route::group(['middleware' => ['lookup:contact', 'auth:client']], function () {
Route::post('client/invoices/auto_bill', 'ClientPortalController@setAutoBill'); Route::post('client/invoices/auto_bill', 'ClientPortalController@setAutoBill');
Route::get('client/documents', 'ClientPortalController@documentIndex'); Route::get('client/documents', 'ClientPortalController@documentIndex');
Route::get('client/payments', 'ClientPortalController@paymentIndex'); Route::get('client/payments', 'ClientPortalController@paymentIndex');
Route::get('client/tasks', 'ClientPortalController@taskIndex');
Route::get('client/dashboard/{contact_key?}', 'ClientPortalController@dashboard'); Route::get('client/dashboard/{contact_key?}', 'ClientPortalController@dashboard');
Route::get('client/documents/js/{documents}/{filename}', 'ClientPortalController@getDocumentVFSJS'); Route::get('client/documents/js/{documents}/{filename}', 'ClientPortalController@getDocumentVFSJS');
Route::get('client/documents/{invitation_key}/{documents}/{filename?}', 'ClientPortalController@getDocument'); 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.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.documents', ['as' => 'api.client.documents', 'uses' => 'ClientPortalController@documentDatatable']);
Route::get('api/client.payments', ['as' => 'api.client.payments', 'uses' => 'ClientPortalController@paymentDatatable']); 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']); Route::get('api/client.activity', ['as' => 'api.client.activity', 'uses' => 'ClientPortalController@activityDatatable']);
}); });