Using datatables to render home dashboard

This commit is contained in:
Deon George 2018-06-19 22:31:49 +10:00
parent 7e503eb1bc
commit 1ac764f05e
No known key found for this signature in database
GPG Key ID: 7670E8DC27415254
17 changed files with 7637 additions and 141 deletions

View File

@ -95,4 +95,4 @@ test:
# if you have any task for testing frontend # if you have any task for testing frontend
# set it in your package.json script # set it in your package.json script
# comment this out if you don't have a frontend test # comment this out if you don't have a frontend test
- npm test # npm test

View File

@ -8,7 +8,16 @@ class UserServicesController extends Controller
{ {
public function __construct() public function __construct()
{ {
//$this->middleware('auth'); }
public function invoices()
{
return ['data'=>Auth::user()->invoices_due->values()];
}
public function payments()
{
return ['data'=>Auth::user()->payment_history->values()];
} }
public function services() public function services()

View File

@ -8,7 +8,22 @@ class Invoice extends Model
{ {
protected $table = 'ab_invoice'; protected $table = 'ab_invoice';
protected $dates = ['due_date']; protected $dates = ['due_date'];
protected $with = ['invoiceitems.taxes','account.country.currency','paymentitems']; protected $with = ['items.taxes','account.country.currency','paymentitems'];
protected $appends = [
'date_due',
'due',
'invoice_id_url',
'total',
];
protected $visible = [
'date_due',
'due',
'id',
'invoice_id_url',
'total',
];
private $_total = 0; private $_total = 0;
@ -17,7 +32,7 @@ class Invoice extends Model
return $this->belongsTo(\App\User::class); return $this->belongsTo(\App\User::class);
} }
public function invoiceitems() public function items()
{ {
return $this->hasMany(InvoiceItem::class); return $this->hasMany(InvoiceItem::class);
} }
@ -29,7 +44,7 @@ class Invoice extends Model
public function getDueAttribute() public function getDueAttribute()
{ {
return $this->currency()->round($this->total - $this->paid); return sprintf('%3.'.$this->currency()->rounding.'f',$this->total - $this->paid);
} }
public function getDateDueAttribute() public function getDateDueAttribute()
@ -37,11 +52,16 @@ class Invoice extends Model
return $this->due_date->format('Y-m-d'); return $this->due_date->format('Y-m-d');
} }
public function getInvoiceNumberAttribute() public function getInvoiceIdAttribute()
{ {
return sprintf('%02s-%04s-%04s',$this->site_id,$this->account_id,$this->id); return sprintf('%02s-%04s-%04s',$this->site_id,$this->account_id,$this->id);
} }
public function getInvoiceIdUrlAttribute()
{
return sprintf('<a href="/u/invoice/view/%s">%s</a>',$this->id,$this->invoice_id);
}
public function getPaidAttribute() public function getPaidAttribute()
{ {
return $this->currency()->round($this->paymentitems->sum('alloc_amt')); return $this->currency()->round($this->paymentitems->sum('alloc_amt'));
@ -51,14 +71,14 @@ class Invoice extends Model
{ {
if (! $this->_total) if (! $this->_total)
{ {
foreach ($this->invoiceitems as $o) foreach ($this->items as $o)
{ {
//if ($o->active) if ($o->active)
$this->_total += $this->currency()->round($o->total); $this->_total += $this->currency()->round($o->total);
} }
} }
return $this->_total; return sprintf('%3.'.$this->currency()->rounding.'f',$this->_total);
} }
public function currency() public function currency()

View File

@ -8,9 +8,41 @@ class Payment extends Model
{ {
protected $table = 'ab_payment'; protected $table = 'ab_payment';
protected $dates = ['date_payment']; protected $dates = ['date_payment'];
protected $with = ['account.country.currency','items'];
public function getPaymentDateAttribute() protected $appends = [
'date_paid',
'total',
];
protected $visible = [
'date_paid',
'id',
'total',
];
public function account()
{
return $this->belongsTo(\App\User::class);
}
public function items()
{
return $this->hasMany(PaymentItem::class);
}
public function getDatePaidAttribute()
{ {
return $this->date_payment->format('Y-m-d'); return $this->date_payment->format('Y-m-d');
} }
public function getTotalAttribute()
{
return sprintf('%3.'.$this->currency()->rounding.'f',$this->total_amt);
}
public function currency()
{
return $this->account->country->currency;
}
} }

View File

@ -7,8 +7,30 @@ use Illuminate\Database\Eloquent\Model;
class Service extends Model class Service extends Model
{ {
protected $table = 'ab_service'; protected $table = 'ab_service';
protected $with = ['product.descriptions','account.language','service_adsl','service_domain.tld','service_ssl','service_voip'];
protected $dates = ['date_last_invoice','date_next_invoice']; protected $dates = ['date_last_invoice','date_next_invoice'];
protected $with = ['product.descriptions','account.language','service_adsl','service_domain.tld','service_ssl'];
protected $appends = [
'category',
'next_invoice',
'product_name',
'service_id',
'service_id_url',
'service_name',
'status',
];
protected $visible = [
'active',
'category',
'data_orig',
'id',
'next_invoice',
'product_name',
'service_id',
'service_id_url',
'service_name',
'status',
];
public function account() public function account()
{ {
@ -25,16 +47,34 @@ class Service extends Model
return $this->belongsTo(ServiceDomain::class,'id','service_id'); return $this->belongsTo(ServiceDomain::class,'id','service_id');
} }
public function service_host()
{
return $this->belongsTo(ServiceHost::class,'id','service_id');
}
public function service_ssl() public function service_ssl()
{ {
return $this->belongsTo(ServiceSsl::class,'id','service_id'); return $this->belongsTo(ServiceSsl::class,'id','service_id');
} }
public function service_voip()
{
return $this->belongsTo(ServiceVoip::class,'id','service_id');
}
public function product() public function product()
{ {
return $this->belongsTo(Product::class); return $this->belongsTo(Product::class);
} }
/**
* Only query active categories
*/
public function scopeActive()
{
return $this->where('active',TRUE);
}
public function getCategoryAttribute() public function getCategoryAttribute()
{ {
return $this->product->prod_plugin_file; return $this->product->prod_plugin_file;
@ -42,7 +82,12 @@ class Service extends Model
public function getNextInvoiceAttribute() public function getNextInvoiceAttribute()
{ {
return $this->date_next_invoice->format('Y-m-d'); return $this->date_next_invoice ? $this->date_next_invoice->format('Y-m-d') : NULL;
}
public function getProductNameAttribute()
{
return $this->product->name($this->account->language);
} }
/** /**
@ -54,21 +99,40 @@ class Service extends Model
{ {
case 'ADSL': return $this->service_adsl; case 'ADSL': return $this->service_adsl;
case 'DOMAIN': return $this->service_domain; case 'DOMAIN': return $this->service_domain;
case 'HOST': return $this->service_host;
case 'SSL': return $this->service_ssl; case 'SSL': return $this->service_ssl;
default: abort(500,'Havent handled case for: '.$this->product->prod_plugin_file); case 'VOIP': return $this->service_voip;
default: return NULL;
} }
} }
public function getStatusAttribute()
{
return $this->active ? 'Active' : 'Inactive';
}
public function getServiceExpireAttribute() public function getServiceExpireAttribute()
{ {
return 'TBA'; return 'TBA';
} }
public function getServiceIdAttribute()
{
return sprintf('%02s-%05s',$this->site_id,$this->id);
}
public function getServiceIdUrlAttribute()
{
return sprintf('<a href="/u/service/view/%s">%s</a>',$this->id,$this->service_id);
}
public function getServiceNameAttribute() public function getServiceNameAttribute()
{ {
if (! isset($this->getServiceDetail()->name)) dd($this,$this->product,$this->getServiceDetail()); if (! isset($this->getServiceDetail()->name))
return sprintf('%s: %s',$this->product->name($this->account->language),$this->getServiceDetail()->name); return 'Unknown';
return $this->getServiceDetail()->name;
} }
public function getServiceNumberAttribute() public function getServiceNumberAttribute()

View File

@ -0,0 +1,20 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class ServiceHost extends Model
{
protected $table = 'ab_service__hosting';
public function service()
{
return $this->belongsTo(Service::class);
}
public function getNameAttribute()
{
return sprintf('%s',$this->domain_name);
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class ServiceVoip extends Model
{
protected $table = 'ab_service__voip';
public function service()
{
return $this->belongsTo(Service::class);
}
public function getNameAttribute()
{
return $this->service_number;
}
}

View File

@ -87,7 +87,7 @@ class User extends Authenticatable
return $this->invoices return $this->invoices
->where('active',TRUE) ->where('active',TRUE)
->sortBy('id') ->sortBy('id')
->transform(function ($item) { if ($item->due) return $item; }) ->transform(function ($item) { if ((float) $item->due) return $item; })
->reverse() ->reverse()
->filter(); ->filter();
} }
@ -106,8 +106,7 @@ class User extends Authenticatable
public function getServicesActiveAttribute() public function getServicesActiveAttribute()
{ {
return $this return $this->services
->services
->where('active',TRUE); ->where('active',TRUE);
} }
} }

5124
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -39,6 +39,7 @@
}, },
"dependencies": { "dependencies": {
"datatables.net": "^1.10.16", "datatables.net": "^1.10.16",
"datatables.net-dt": "^1.10.16" "datatables.net-dt": "^1.10.16",
"npm": "^6.1.0"
} }
} }

2247
public/js/app.js vendored

File diff suppressed because one or more lines are too long

View File

@ -13,14 +13,13 @@
@section('main-content') @section('main-content')
<div class="content"> <div class="content">
<div class="row"> <div class="row">
<div class="col-sm-3"> <div class="col-sm-3">
<div class="info-box"> <div class="info-box">
<span class="info-box-icon bg-red"><i class="fa fa-dollar"></i></span> <span class="info-box-icon bg-red"><i class="fa fa-dollar"></i></span>
<div class="info-box-content"> <div class="info-box-content">
<span class="info-box-text">Account Balance</span> <span class="info-box-text">Account Balance</span>
<span class="info-box-number"><small>$</small>TBA {{ number_format($user->invoices_due->sum('due'),2) }}</span> <span class="info-box-number"><small>$</small>{{ number_format($user->invoices_due->sum('due'),2) }}</span>
</div> </div>
</div> </div>
</div> </div>

View File

@ -11,24 +11,16 @@
<div class="box-body"> <div class="box-body">
@if ($user->invoices_due->count()) @if ($user->invoices_due->count())
<table class="table table-bordered table-striped table-hover" id="table"> <table class="table table-bordered table-striped table-hover" id="invoices" style="width: 100%;">
<thead>
<tr> <tr>
<th>Invoice</th> <th>Invoice</th>
<th>Total</th> <th>Total</th>
<th>Due</th> <th>Due</th>
<th>Date</th> <th>Date Date</th>
</tr> </tr>
@foreach ($user->invoices_due as $o) </thead>
@php <tfoot>
$co = $o->currency();
@endphp
<tr>
<td>{{ $o->invoice_number }}</td>
<td class="right">{{ number_format($o->total,$co->rounding) }}</td>
<td class="right">{{ number_format($o->due,$co->rounding) }}</td>
<td>{{ $o->date_due }}</td>
</tr>
@endforeach
<tr> <tr>
<th>Count {{ $user->invoices_due->count() }}</th> <th>Count {{ $user->invoices_due->count() }}</th>
{{-- @todo Number format should configured by currency --}} {{-- @todo Number format should configured by currency --}}
@ -36,9 +28,50 @@
<th class="right">{{ number_format($user->invoices_due->sum('due'),2) }}</th> <th class="right">{{ number_format($user->invoices_due->sum('due'),2) }}</th>
<th>&nbsp;</th> <th>&nbsp;</th>
</tr> </tr>
</tfoot>
</table> </table>
@else @else
<p>No invoices due</p> <p>No invoices due</p>
@endif @endif
</div> </div>
</div> </div>
@section('page-scripts')
@css('https://cdn.datatables.net/responsive/2.2.1/css/responsive.dataTables.min.css')
@css('https://cdn.datatables.net/rowgroup/1.0.2/css/rowGroup.dataTables.min.css')
@js('https://cdn.datatables.net/responsive/2.2.1/js/dataTables.responsive.min.js')
@js('https://cdn.datatables.net/rowgroup/1.0.2/js/dataTables.rowGroup.min.js')
<style>
table.dataTable {
width: 100%;
}
table.dataTable td {
outline: none;
}
</style>
<script type="text/javascript">
$(document).ready(function() {
$('#invoices').DataTable( {
responsive: true,
ajax: {
url: "/api/u/invoices"
},
columns: [
{ data: "invoice_id_url" },
{ data: "total" },
{ data: "due" },
{ data: "date_due" }
],
language: {
emptyTable: "No Invoices Due"
},
order: [3, 'asc']
});
$('#invoices tbody').on('click','tr', function () {
$(this).toggleClass('selected');
});
});
</script>
@append

View File

@ -11,27 +11,54 @@
<div class="box-body"> <div class="box-body">
@if ($user->payment_history->count()) @if ($user->payment_history->count())
<table class="table table-bordered table-striped table-hover" id="table"> <table class="table table-bordered table-striped table-hover" id="payments" style="width: 100%;">
<thead>
<tr> <tr>
<th>Date</th> <th>Date</th>
<th>Amount</th> <th>Amount</th>
</tr> </tr>
@php </thead>
$c=0;
@endphp
@foreach ($user->payment_history as $o)
@if(! isset($limit) OR $c++ > $limit)
@break;
@endif
<tr>
<td>{{ $o->payment_date }}</td>
{{-- @todo Number format should configured by currency --}}
<td class="right">{{ number_format($o->total_amt,2) }}</td>
</tr>
@endforeach
</table> </table>
@else @else
<p>No payments recorded</p> <p>No payments recorded</p>
@endif @endif
</div> </div>
</div> </div>
@section('page-scripts')
@css('https://cdn.datatables.net/responsive/2.2.1/css/responsive.dataTables.min.css')
@css('https://cdn.datatables.net/rowgroup/1.0.2/css/rowGroup.dataTables.min.css')
@js('https://cdn.datatables.net/responsive/2.2.1/js/dataTables.responsive.min.js')
@js('https://cdn.datatables.net/rowgroup/1.0.2/js/dataTables.rowGroup.min.js')
<style>
table.dataTable {
width: 100%;
}
table.dataTable td {
outline: none;
}
</style>
<script type="text/javascript">
$(document).ready(function() {
$('#payments').DataTable( {
responsive: true,
ajax: {
url: "/api/u/payments"
},
columns: [
{ data: "date_paid" },
{ data: "total" },
],
language: {
emptyTable: "No Payments On File"
},
order: [0, 'desc']
});
$('#payments tbody').on('click','tr', function () {
$(this).toggleClass('selected');
});
});
</script>
@append

View File

@ -14,9 +14,10 @@
<table class="table table-bordered table-striped table-hover" id="services" style="width: 100%;"> <table class="table table-bordered table-striped table-hover" id="services" style="width: 100%;">
<thead> <thead>
<tr> <tr>
<th>ID</th>
<th>Category</th> <th>Category</th>
<th>Service</th> <th>Service</th>
<th>Name</th> <th>Product</th>
<th>Status</th> <th>Status</th>
<th>Next Invoice</th> <th>Next Invoice</th>
</tr> </tr>
@ -24,7 +25,7 @@
<tfoot> <tfoot>
<tr> <tr>
<th>Count {{ $user->services_active->count() }}</th> <th>Count {{ $user->services_active->count() }}</th>
<th colspan="4">&nbsp;</th> <th colspan="5">&nbsp;</th>
</tr> </tr>
</tfoot> </tfoot>
</table> </table>
@ -40,32 +41,44 @@
@js('https://cdn.datatables.net/responsive/2.2.1/js/dataTables.responsive.min.js') @js('https://cdn.datatables.net/responsive/2.2.1/js/dataTables.responsive.min.js')
@js('https://cdn.datatables.net/rowgroup/1.0.2/js/dataTables.rowGroup.min.js') @js('https://cdn.datatables.net/rowgroup/1.0.2/js/dataTables.rowGroup.min.js')
<style>
table.dataTable td {
outline: none;
}
</style>
<script type="text/javascript"> <script type="text/javascript">
$(document).ready(function() { $(document).ready(function() {
var table = $('#services').DataTable( { $('#services').DataTable( {
rowGroup: { rowGroup: {
dataSrc: 'product_id', dataSrc: 'product_name',
startRender: null, startRender: null,
endRender: function ( rows, group ) { endRender: function ( rows, group ) {
return group +' ('+rows.count()+')'; return rows.count()+' x ' + group;
}, },
}, },
orderFixed: [2, 'asc'], orderFixed: [3, 'asc'],
responsive: true, responsive: true,
ajax: "/api/u/services", ajax: {
columns: [ url: "/api/u/services"
{
defaultContent: '&nbsp;'
}, },
{ data: "id" }, columns: [
{ data: "product_id" }, { data: "service_id_url" },
{ data: "active" }, { data: "category" },
{ data: "date_next_invoice" } { data: "service_name" },
{ data: "product_name" },
{ data: "status" },
{ data: "next_invoice" }
], ],
language: { language: {
emptyTable: "No Active Services" emptyTable: "No Active Services"
}, },
order: [4, 'asc'] order: [5, 'asc']
});
$('#services tbody').on('click','tr', function () {
$(this).toggleClass('selected');
//var data = table.row(this).data();
//window.location.href = '/u/service/view/'+data.id;
}); });
}); });
</script> </script>

View File

@ -20,5 +20,7 @@ Route::middleware('auth:api')->get('/user', function (Request $request) {
*/ */
Route::group(['middleware'=>'auth:api'], function() { Route::group(['middleware'=>'auth:api'], function() {
Route::get('/u/invoices','UserServicesController@invoices');
Route::get('/u/payments','UserServicesController@payments');
Route::get('/u/services','UserServicesController@services'); Route::get('/u/services','UserServicesController@services');
}); });