1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-09 20:52:56 +01:00

Implemented dashboard

This commit is contained in:
Hillel Coren 2014-02-16 22:32:25 +02:00
parent 21afbf4309
commit 642765b09b
19 changed files with 562 additions and 183 deletions

View File

@ -0,0 +1,50 @@
<?php
class DashboardController extends \BaseController {
public function index()
{
// total_income, billed_clients, invoice_sent and active_clients
$select = DB::raw('SUM(clients.paid_to_date) total_income,
COUNT(DISTINCT CASE WHEN invoices.id IS NOT NULL THEN clients.id ELSE null END) - 1 billed_clients,
SUM(CASE WHEN invoices.invoice_status_id >= '.INVOICE_STATUS_SENT.' THEN 1 ELSE 0 END) invoices_sent,
COUNT(DISTINCT clients.id) active_clients');
$metrics = DB::table('clients')
->select($select)
->leftJoin('invoices', 'clients.id', '=', 'invoices.client_id')
->where('clients.account_id', '=', Auth::user()->account_id)
->where('clients.deleted_at', '=', null)
->groupBy('clients.account_id')
->first();
$invoiceAvg = DB::table('invoices')
->where('invoices.account_id', '=', Auth::user()->account_id)
->where('invoices.deleted_at', '=', null)
->avg('amount');
$activities = Activity::where('activities.account_id', '=', Auth::user()->account_id)
->orderBy('created_at', 'desc')->take(6)->get();
$pastDue = Invoice::scope()->where('due_date', '<', date('Y-m-d'))
->orderBy('due_date', 'asc')->take(6)->get();
$upcoming = Invoice::scope()->where('due_date', '>', date('Y-m-d'))
->orderBy('due_date', 'asc')->take(6)->get();
$data = [
'totalIncome' => Utils::formatMoney($metrics->total_income, Session::get(SESSION_CURRENCY)),
'billedClients' => $metrics->billed_clients,
'invoicesSent' => $metrics->invoices_sent,
'activeClients' => $metrics->active_clients,
'invoiceAvg' => Utils::formatMoney($invoiceAvg, Session::get(SESSION_CURRENCY)),
'activities' => $activities,
'pastDue' => $pastDue,
'upcoming' => $upcoming
];
return View::make('dashboard', $data);
}
}

View File

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddCascaseDrops extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('invoices', function($table)
{
$table->dropForeign('invoices_account_id_foreign');
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
}
}

View File

@ -32,6 +32,11 @@ class Invoice extends EntityModel
return $this->invoice_number;
}
public function getLink()
{
return link_to('invoices/' . $this->public_id, $this->invoice_number);
}
public function getEntityType()
{
return ENTITY_INVOICE;

View File

@ -12,14 +12,14 @@ class InvoiceRepository
{
$query = \DB::table('invoices')
->join('clients', 'clients.id', '=','invoices.client_id')
->join('invoice_statuses', 'invoice_statuses.id', '=', 'invoices.invoice_status_id')
->join('contacts', 'contacts.client_id', '=', 'clients.id')
->where('invoices.account_id', '=', $accountId)
->join('invoice_statuses', 'invoice_statuses.id', '=', 'invoices.invoice_status_id')
->join('contacts', 'contacts.client_id', '=', 'clients.id')
->where('invoices.account_id', '=', $accountId)
->where('invoices.deleted_at', '=', null)
->where('clients.deleted_at', '=', null)
->where('invoices.is_recurring', '=', false)
->where('contacts.is_primary', '=', true)
->select('clients.public_id as client_public_id', 'invoice_number', 'clients.name as client_name', 'invoices.public_id', 'amount', 'invoices.balance', 'invoice_date', 'due_date', 'invoice_statuses.name as invoice_status_name', 'clients.currency_id', 'contacts.first_name', 'contacts.last_name', 'contacts.email');
->select('clients.public_id as client_public_id', 'invoice_number', 'clients.name as client_name', 'invoices.public_id', 'amount', 'invoices.balance', 'invoice_date', 'due_date', 'invoice_statuses.name as invoice_status_name', 'clients.currency_id', 'contacts.first_name', 'contacts.last_name', 'contacts.email');
if ($clientPublicId)
{

View File

@ -85,7 +85,7 @@ Route::get('logout', 'UserController@logout');
Route::group(array('before' => 'auth'), function()
{
Route::get('home', function() { return View::make('header'); });
Route::get('dashboard', 'DashboardController@index');
Route::get('account/getSearchData', array('as' => 'getSearchData', 'uses' => 'AccountController@getSearchData'));
Route::get('account/{section?}', 'AccountController@showSection');
Route::post('account/{section?}', 'AccountController@doSection');

View File

@ -0,0 +1,141 @@
@extends('header')
@section('content')
<div class="row">
<div class="col-md-4">
<div class="panel panel-default">
<div class="panel-body">
<img src="{{ asset('images/totalincome.png') }}" class="in-image"/>
<div class="in-bold">
{{ $totalIncome }}
</div>
<div class="in-thin">
in total income
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="panel panel-default">
<div class="panel-body">
<img src="{{ asset('images/clients.png') }}" class="in-image"/>
<div class="in-bold">
{{ $billedClients }}
</div>
<div class="in-thin">
{{ Utils::pluralize('billed client', $billedClients) }}
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="panel panel-default">
<div class="panel-body">
<img src="{{ asset('images/totalinvoices.png') }}" class="in-image"/>
<div class="in-bold">
{{ $invoicesSent }}
</div>
<div class="in-thin">
{{ Utils::pluralize('invoice', $invoicesSent) }} sent
</div>
</div>
</div>
</div>
</div>
<p>&nbsp;</p>
<div class="row">
<div class="col-md-6">
<div class="panel panel-default" style="min-height:320px">
<div class="panel-heading" style="background-color:#2299c0">
<h3 class="panel-title in-bold-white">
<i class="glyphicon glyphicon-exclamation-sign"></i> Notifications
</h3>
</div>
<ul class="panel-body list-group">
@foreach ($activities as $activity)
<li class="list-group-item">
<span style="color:#888;font-style:italic">{{ Utils::timestampToDateTimeString(strtotime($activity->created_at)) }}:</span>
{{ Utils::decodeActivity($activity->message) }}
</li>
@endforeach
</ul>
</div>
</div>
<div class="col-md-6">
<div class="panel panel-default" style="min-height:320px">
<div class="panel-heading" style="background-color:#e37329">
<h3 class="panel-title in-bold-white">
<i class="glyphicon glyphicon-time"></i> Invoices Past Due
</h3>
</div>
<div class="panel-body">
<table class="table">
<thead>
<th>Invoice #</th>
<th>Client</th>
<th>Due date</th>
<th>Balance due</th>
</thead>
<tbody>
@foreach ($pastDue as $invoice)
<tr>
<td>{{ $invoice->getLink() }}</td>
<td>{{ $invoice->client->getDisplayName() }}</td>
<td>{{ Utils::fromSqlDate($invoice->due_date) }}</td>
<td>{{ Utils::formatMoney($invoice->balance, $invoice->client->currency_id) }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="panel panel-default" style="min-height:320px">
<div class="panel-heading">
<h3 class="panel-title">
<i class="glyphicon glyphicon-time"></i> Upcoming invoices
</h3>
</div>
<div class="panel-body">
<table class="table">
<thead>
<th>Invoice #</th>
<th>Client</th>
<th>Due date</th>
<th>Balance due</th>
</thead>
<tbody>
@foreach ($upcoming as $invoice)
<tr>
<td>{{ $invoice->getLink() }}</td>
<td>{{ $invoice->client->getDisplayName() }}</td>
<td>{{ Utils::fromSqlDate($invoice->due_date) }}</td>
<td>{{ Utils::formatMoney($invoice->balance, $invoice->client->currency_id) }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
<div class="col-md-6">
<div class="col-md-6 active-clients">
<div class="in-bold in-white" style="font-size:42px">{{ $activeClients }}</div>
<div class="in-thin in-white">{{ Utils::pluralize('active client', $activeClients) }}</div>
</div>
<div class="col-md-6 average-invoice">
<div><b>average invoice</b></div>
<div class="in-bold in-white" style="font-size:42px">{{ $invoiceAvg }}</div>
</div>
</div>
</div>
@stop

View File

@ -77,12 +77,14 @@
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a href="{{ URL::to('/rocksteady') }}" class='navbar-brand'>Invoice Ninja</a>
<a href="{{ URL::to('/rocksteady') }}" class='navbar-brand'>
<img src="{{ asset('images/invoiceninja-logo.png') }}" style="height:18px;width:auto"/>
</a>
</div>
<div class="collapse navbar-collapse" id="navbar-collapse-1">
<ul class="nav navbar-nav" style="font-weight: bold">
{{-- HTML::nav_link('home', 'Home') --}}
{{ HTML::nav_link('dashboard', 'Dashboard') }}
{{ HTML::menu_link('client') }}
{{ HTML::menu_link('invoice') }}
{{ HTML::menu_link('payment') }}
@ -121,7 +123,7 @@
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Recently Viewed <b class="caret"></b></a>
<a href="#" class="dropdown-toggle" data-toggle="dropdown">History <b class="caret"></b></a>
<ul class="dropdown-menu">
@if (count(Session::get(RECENTLY_VIEWED)) == 0)
<li><a href="#">No items</a></li>

View File

@ -24,7 +24,7 @@
<div class="form-group">
<label for="client" class="control-label col-lg-4 col-sm-4">Client</label>
<div class="col-lg-8 col-sm-8" style="padding-top: 7px">
<a id="editClientLink" href="#" data-bind="click: $root.showClientForm">{{ $client->getDisplayName() }}</a>
<a id="editClientLink" class="pointer" data-bind="click: $root.showClientForm">{{ $client->getDisplayName() }}</a>
</div>
</div>
<div style="display:none">
@ -34,7 +34,7 @@
<div class="form-group" style="margin-bottom: 8px">
<div class="col-lg-8 col-sm-8 col-lg-offset-4 col-sm-offset-4">
<a id="createClientLink" data-bind="click: $root.showClientForm, text: $root.clientLinkText"></a>
<a id="createClientLink" class="pointer" data-bind="click: $root.showClientForm, text: $root.clientLinkText"></a>
</div>
</div>

View File

@ -1,15 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Invoice Ninja {{ isset($title) ? $title : '' }}</title>
<link rel="canonical" href="https://www.invoiceninja.com"></link>
<link href="{{ asset('favicon.ico') }}" rel="icon" type="image/x-icon">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="{{ asset('favicon.ico') }}" rel="icon" type="image/x-icon">
<meta name="description" content="">
<meta name="author" content="">
<meta property="og:site_name" content="Invoice Ninja"></meta>
<meta property="og:url" content="https://www.invoiceninja.com"></meta>
<meta property="og:title" content="Invoice Ninja"></meta>
<meta property="og:image" content="https://fbcdn-sphotos-b-a.akamaihd.net/hphotos-ak-ash3/t31/1548037_274756319355261_10423754_o.jpg"></meta>
<meta property="og:description" content="Simple, Intuitive Invoicing."></meta>
<meta name="keywords" content="Invoice Ninja"></meta>
<title>Invoice Ninja {{ isset($title) ? $title : '' }}</title>
<script src="{{ asset('vendor/jquery/jquery.min.js') }}" type="text/javascript"></script>
<script type="text/javascript">
window.onerror = function(e) {

415
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -19,6 +19,10 @@ div.panel {
padding-right: 0px !important;
}
.pointer {
cursor: pointer;
}
.form-actions {
margin: 0;
background-color: transparent;
@ -86,6 +90,20 @@ table.table thead .sorting { background: url('') no-repeat center right; }
margin-top: 0;
}
.navbar {
background-color: #0b4d78 !important;
background-image: none;
background-repeat: no-repeat;
filter: none;
}
.navbar .active > a {
background-color: #09334f !important;
background-image: none;
background-repeat: no-repeat;
filter: none;
}
.navbar .sub-menu:before {
border-bottom: 7px solid transparent;
border-left: none;
@ -105,6 +123,52 @@ table.table thead .sorting { background: url('') no-repeat center right; }
}
/*******************
Dashboard
*******************/
.in-bold {
font-size: 26px;
font-weight: bold;;
}
.in-thin {
font-size: 26px;
color: #888;
}
.in-bold-white {
font-weight: bold;
color: white;
}
.in-image {
float:left;padding-right:16px;
}
.in-white {
color: white;
}
.active-clients {
background-color: #2299c0;
background-image:url('../images/activeclients.png');
background-position:center;
background-repeat: no-repeat;
height: 200px;
padding-top: 44px;
text-align: center;
}
.average-invoice {
background-color: #ecd817;
height: 200px;
padding-top: 60px;
text-align: center;
}
.invoice-table tbody {
border-style: none !important;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
public/images/clients.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB