Implemented the view client screen

This commit is contained in:
Hillel Coren 2013-11-29 14:09:21 +02:00
parent b6da02a6b9
commit 470e0bf06e
15 changed files with 382 additions and 87 deletions

View File

@ -0,0 +1,21 @@
<?php
class ActivityController extends \BaseController {
public function getDatatable($clientId)
{
return Datatable::collection(Activity::where('account_id','=',Auth::user()->account_id)
->where('client_id','=',$clientId)->get())
->addColumn('date', function($model)
{
return $model->created_at->format('m/d/y h:i a');
})
->addColumn('message', function($model)
{
return $model->message;
})
->orderColumns('date')
->make();
}
}

View File

@ -79,7 +79,10 @@ class ClientController extends \BaseController {
*/ */
public function show($id) public function show($id)
{ {
$client = Client::find($id); $client = Client::with('contacts')->find($id);
trackViewed(Request::url(), $client->name);
return View::make('clients.show')->with('client', $client); return View::make('clients.show')->with('client', $client);
} }
@ -128,6 +131,7 @@ class ClientController extends \BaseController {
$client = Client::find($id); $client = Client::find($id);
} else { } else {
$client = new Client; $client = new Client;
$client->account_id = Auth::user()->account_id;
} }
$client->name = Input::get('name'); $client->name = Input::get('name');

View File

@ -13,18 +13,28 @@ class InvoiceController extends \BaseController {
return View::make('invoices.index'); return View::make('invoices.index');
} }
public function getDatatable() public function getDatatable($clientId = null)
{ {
return Datatable::collection(Invoice::with('client','invoice_items')->where('account_id','=',Auth::user()->account_id)->get()) $collection = Invoice::with('client','invoice_items')->where('account_id','=',Auth::user()->account_id);
->addColumn('number', function($model)
if ($clientId) {
$collection->where('client_id','=',$clientId);
}
$table = Datatable::collection($collection->get())->addColumn('number', function($model)
{ {
return link_to('invoices/' . $model->id . '/edit', $model->invoice_number); return link_to('invoices/' . $model->id . '/edit', $model->invoice_number);
}) });
->addColumn('client', function($model)
if (!$clientId)
{ {
$table->addColumn('client', function($model) {
return link_to('clients/' . $model->client->id, $model->client->name); return link_to('clients/' . $model->client->id, $model->client->name);
}) });
->addColumn('amount', function($model)
}
return $table->addColumn('amount', function($model)
{ {
return '$' . money_format('%i', $model->getTotal()); return '$' . money_format('%i', $model->getTotal());
}) })
@ -176,6 +186,7 @@ class InvoiceController extends \BaseController {
public function edit($id) public function edit($id)
{ {
$invoice = Invoice::with('client', 'invoice_items')->find($id); $invoice = Invoice::with('client', 'invoice_items')->find($id);
trackViewed(Request::url(), $invoice->invoice_number . ' - ' . $invoice->client->name);
$data = array( $data = array(
'invoice' => $invoice, 'invoice' => $invoice,

View File

@ -7,14 +7,24 @@ class PaymentController extends \BaseController
return View::make('payments.index'); return View::make('payments.index');
} }
public function getDatatable() public function getDatatable($clientId = null)
{ {
return Datatable::collection(Payment::with('invoice.client')->where('account_id', '=', Auth::user()->account_id)->get()) $collection = Payment::with('invoice.client')->where('account_id', '=', Auth::user()->account_id);
->addColumn('client', function($model)
if ($clientId) {
$collection->where('client_id','=',$clientId);
}
$table = Datatable::collection($collection->get());
if (!$clientId) {
$table->addColumn('client', function($model)
{ {
return link_to('clients/' . $model->invoice->client->id, $model->invoice->client->name); return link_to('clients/' . $model->invoice->client->id, $model->invoice->client->name);
}) });
->addColumn('invoice', function($model) }
return $table->addColumn('invoice', function($model)
{ {
return link_to('invoices/' . $model->invoice->id . '/edit', $model->invoice->number); return link_to('invoices/' . $model->invoice->id . '/edit', $model->invoice->number);
}) })

View File

@ -195,6 +195,7 @@ class ConfideSetupUsersTable extends Migration {
$t->increments('id'); $t->increments('id');
$t->integer('invoice_id'); $t->integer('invoice_id');
$t->integer('account_id'); $t->integer('account_id');
$t->integer('client_id');
$t->integer('contact_id'); $t->integer('contact_id');
$t->integer('user_id'); $t->integer('user_id');
$t->timestamps(); $t->timestamps();

8
app/libraries/entity.php Normal file
View File

@ -0,0 +1,8 @@
<?
class Entity
{
public $id;
public $type;
}

View File

@ -27,6 +27,58 @@ class Client extends Eloquent
{ {
return $this->hasMany('Contact'); return $this->hasMany('Contact');
} }
public function getAddress()
{
$str = '';
if ($this->address1) {
$str .= $this->address1 . '<br/>';
}
if ($this->address2) {
$str .= $this->address2 . '<br/>';
}
if ($this->city) {
$str .= $this->city . ', ';
}
if ($this->state) {
$str .= $this->state . ' ';
}
if ($this->postal_code) {
$str .= $this->postal_code;
}
if ($str)
{
$str = '<p>' . $str . '</p>';
}
return $str;
}
public function getPhone()
{
$str = '';
if ($this->work_phone)
{
$str .= '<i class="fa fa-phone" style="width: 20px"></i>' . $this->work_phone;
}
return $str;
}
public function getNotes()
{
$str = '';
if ($this->notes)
{
$str .= '<i>' . $this->notes . '</i>';
}
return $str;
}
} }
Client::created(function($client) Client::created(function($client)

View File

@ -32,11 +32,38 @@ class Contact extends Eloquent
if ($fullName == ' ') if ($fullName == ' ')
{ {
return "Unknown"; return 'Guest';
} }
else else
{ {
return $fullName; return $fullName;
} }
} }
public function getDetails()
{
$str = '';
if ($this->first_name || $this->last_name)
{
$str .= '<b>' . $this->first_name . ' ' . $this->last_name . '</b><br/>';
}
if ($this->email)
{
$str .= '<i class="fa fa-envelope" style="width: 20px"></i>' . HTML::mailto($this->email, $this->email) . '<br/>';
}
if ($this->phone)
{
$str .= '<i class="fa fa-phone" style="width: 20px"></i>' . $this->phone;
}
if ($str)
{
$str = '<p>' . $str . '</p>';
}
return $str;
}
} }

View File

@ -39,15 +39,17 @@ Route::group(array('before' => array('auth', 'csrf')), function()
Route::get('api/clients', array('as'=>'api.clients', 'uses'=>'ClientController@getDatatable')); Route::get('api/clients', array('as'=>'api.clients', 'uses'=>'ClientController@getDatatable'));
Route::resource('invoices', 'InvoiceController'); Route::resource('invoices', 'InvoiceController');
Route::get('api/invoices', array('as'=>'api.invoices', 'uses'=>'InvoiceController@getDatatable')); Route::get('api/invoices/{client_id?}', array('as'=>'api.invoices', 'uses'=>'InvoiceController@getDatatable'));
Route::get('invoices/create/{client_id}', 'InvoiceController@create'); Route::get('invoices/create/{client_id}', 'InvoiceController@create');
Route::get('payments', 'PaymentController@index'); Route::get('payments', 'PaymentController@index');
Route::get('api/payments', array('as'=>'api.payments', 'uses'=>'PaymentController@getDatatable')); Route::get('api/payments/{client_id?}', array('as'=>'api.payments', 'uses'=>'PaymentController@getDatatable'));
Route::get('home', function() { return View::make('header'); }); Route::get('home', function() { return View::make('header'); });
Route::get('reports', function() { return View::make('header'); }); Route::get('reports', function() { return View::make('header'); });
Route::get('payments/create', function() { return View::make('header'); }); Route::get('payments/create', function() { return View::make('header'); });
Route::get('api/activities/{client_id?}', array('as'=>'api.activities', 'uses'=>'ActivityController@getDatatable'));
}); });
// Confide routes // Confide routes
@ -65,9 +67,14 @@ Route::get('logout', 'UserController@logout');
HTML::macro('nav_link', function($url, $text, $url2 = '') { HTML::macro('nav_link', function($url, $text, $url2 = '', $extra = '') {
$class = ( Request::is($url) || Request::is($url.'/*') || Request::is($url2) ) ? ' class="active"' : ''; $class = ( Request::is($url) || Request::is($url.'/*') || Request::is($url2) ) ? ' class="active"' : '';
return '<li'.$class.'><a href="'.URL::to($url).'">'.$text.'</a></li>'; return '<li'.$class.'><a href="'.URL::to($url).'" '.$extra.'>'.$text.'</a></li>';
});
HTML::macro('tab_link', function($url, $text, $active = false) {
$class = $active ? ' class="active"' : '';
return '<li'.$class.'><a href="'.URL::to($url).'" data-toggle="tab">'.$text.'</a></li>';
}); });
HTML::macro('menu_link', function($type) { HTML::macro('menu_link', function($type) {
@ -131,10 +138,51 @@ function processedRequest($url)
Session::put('_token', md5(microtime())); Session::put('_token', md5(microtime()));
} }
function trackViewed($url, $name)
{
$viewed = Session::get(RECENTLY_VIEWED);
if (!$viewed)
{
$viewed = [];
}
$object = new stdClass;
$object->url = $url;
$object->name = $name;
for ($i=0; $i<count($viewed); $i++)
{
$item = $viewed[$i];
if ($object->url == $item->url)
{
array_splice($viewed, $i, 1);
}
}
array_unshift($viewed, $object);
if (count($viewed) > 5)
{
array_pop($viewed);
}
Session::put(RECENTLY_VIEWED, $viewed);
}
define("ENV_DEVELOPMENT", "local"); define("ENV_DEVELOPMENT", "local");
define("ENV_STAGING", "staging"); define("ENV_STAGING", "staging");
define("ENV_PRODUCTION", "production"); define("ENV_PRODUCTION", "production");
define("RECENTLY_VIEWED", "RECENTLY_VIEWED");
define("ENTITY_CLIENT", "Client");
define("ENTITY_INVOICE", "Invoice");
define("ENTITY_PAYMENT", "Payment");
define("ACCOUNT_DETAILS", "details"); define("ACCOUNT_DETAILS", "details");
define("ACCOUNT_SETTINGS", "settings"); define("ACCOUNT_SETTINGS", "settings");
define("ACCOUNT_IMPORT", "import"); define("ACCOUNT_IMPORT", "import");

View File

@ -17,6 +17,7 @@ ClassLoader::addDirectories(array(
app_path().'/controllers', app_path().'/controllers',
app_path().'/models', app_path().'/models',
app_path().'/database/seeds', app_path().'/database/seeds',
app_path().'/libraries',
)); ));

View File

@ -24,6 +24,9 @@
@endif @endif
{{ Former::populateField('phone', $account->users()->first()->phone) }} {{ Former::populateField('phone', $account->users()->first()->phone) }}
<div class="row">
<div class="col-md-6">
{{ Former::legend('Account') }} {{ Former::legend('Account') }}
{{ Former::text('name') }} {{ Former::text('name') }}
@ -35,13 +38,6 @@
</center> </center>
@endif @endif
{{ Former::legend('Users') }}
{{ Former::text('first_name') }}
{{ Former::text('last_name') }}
{{ Former::text('email')->label('Email/Username') }}
{{ Former::text('phone') }}
{{ Former::legend('Address') }} {{ Former::legend('Address') }}
{{ Former::text('address1')->label('Street') }} {{ Former::text('address1')->label('Street') }}
{{ Former::text('address2')->label('Apt/Floor') }} {{ Former::text('address2')->label('Apt/Floor') }}
@ -51,6 +47,18 @@
{{ Former::select('country_id')->addOption('','')->label('Country') {{ Former::select('country_id')->addOption('','')->label('Country')
->fromQuery($countries, 'name', 'id')->select($account ? $account->country_id : '') }} ->fromQuery($countries, 'name', 'id')->select($account ? $account->country_id : '') }}
</div>
<div class="col-md-6">
{{ Former::legend('Users') }}
{{ Former::text('first_name') }}
{{ Former::text('last_name') }}
{{ Former::text('email')->label('Email/Username') }}
{{ Former::text('phone') }}
</div>
</div>
{{ Former::actions( Button::lg_primary_submit('Save') ) }} {{ Former::actions( Button::lg_primary_submit('Save') ) }}
{{ Former::close() }} {{ Former::close() }}

View File

@ -20,11 +20,28 @@
@endif @endif
<div class="row">
<div class="col-md-6">
{{ Former::legend('Organization') }} {{ Former::legend('Organization') }}
{{ Former::text('name') }} {{ Former::text('name') }}
{{ Former::text('work_phone')->label('Phone') }} {{ Former::text('work_phone')->label('Phone') }}
{{ Former::textarea('notes') }} {{ Former::textarea('notes') }}
{{ Former::legend('Address') }}
{{ Former::text('address1')->label('Street') }}
{{ Former::text('address2')->label('Apt/Floor') }}
{{ Former::text('city') }}
{{ Former::text('state') }}
{{ Former::text('postal_code') }}
{{ Former::select('country_id')->addOption('','')->label('Country')
->fromQuery($countries, 'name', 'id')->select($client ? $client->country_id : '') }}
</div>
<div class="col-md-6">
{{ Former::legend('Contacts') }} {{ Former::legend('Contacts') }}
<div data-bind='template: { foreach: contacts, <div data-bind='template: { foreach: contacts,
beforeRemove: hideContact, beforeRemove: hideContact,
@ -45,18 +62,11 @@
</span> </span>
</div> </div>
</div> </div>
<div class="clearfix"></div>
</div> </div>
{{ Former::legend('Address') }} </div>
{{ Former::text('address1')->label('Street') }} </div>
{{ Former::text('address2')->label('Apt/Floor') }}
{{ Former::text('city') }}
{{ Former::text('state') }}
{{ Former::text('postal_code') }}
{{ Former::select('country_id')->addOption('','')->label('Country')
->fromQuery($countries, 'name', 'id')->select($client ? $client->country_id : '') }}
{{ Former::hidden('data')->data_bind("value: ko.toJSON(model)") }} {{ Former::hidden('data')->data_bind("value: ko.toJSON(model)") }}

View File

@ -2,13 +2,87 @@
@section('content') @section('content')
<h3>View Client</h3>
{{ $client->name }}
<div class="pull-right"> <div class="pull-right">
{{ Button::link(URL::to('clients/' . $client->id . '/edit'), 'Edit Client') }} {{ Button::link(URL::to('clients/' . $client->id . '/edit'), 'Edit Client') }}
{{ Button::primary_link(URL::to('invoices/create/' . $client->id), 'Create Invoice') }} {{ Button::primary_link(URL::to('invoices/create/' . $client->id), 'Create Invoice') }}
</div> </div>
<h2>{{ $client->name }}</h2>
<div class="row">
<div class="col-md-3">
<h3>Details</h3>
<p>{{ $client->getAddress() }}</p>
<p>{{ $client->getPhone() }}</p>
<p>{{ $client->getNotes() }}</p>
</div>
<div class="col-md-3">
<h3>Contacts</h3>
@foreach ($client->contacts as $contact)
{{ $contact->getDetails() }}
@endforeach
</div>
<div class="col-md-6">
<h3>Balance</h3>
<h3>$0.00 <small>Paid to Date USD</small></h3>
<h3>$0.00 <small>Outstanding USD</small></h3>
</div>
</div>
<p>&nbsp;</p>
<ul class="nav nav-tabs nav-justified">
{{ HTML::tab_link('#activity', 'Activity', true) }}
{{ HTML::tab_link('#invoices', 'Invoices') }}
{{ HTML::tab_link('#payments', 'Payments') }}
</ul>
<div class="tab-content">
<div class="tab-pane active" id="activity">
{{ Datatable::table()
->addColumn('Date', 'Message')
->setUrl(url('api/activities/'. $client->id))
->setOptions('sPaginationType', 'bootstrap')
->setOptions('bFilter', false)
->render() }}
</div>
<div class="tab-pane" id="invoices">
{{ Datatable::table()
->addColumn('Invoice Number', 'Amount', 'Date')
->setUrl(url('api/invoices/' . $client->id))
->setOptions('sPaginationType', 'bootstrap')
->setOptions('bFilter', false)
->render() }}
</div>
<div class="tab-pane" id="payments">
{{ Datatable::table()
->addColumn('Invoice Number', 'Amount', 'Date')
->setUrl(url('api/payments/' . $client->id))
->setOptions('sPaginationType', 'bootstrap')
->setOptions('bFilter', false)
->render() }}
</div>
</div>
<script type="text/javascript">
$(function() {
});
</script>
@stop @stop

View File

@ -24,11 +24,12 @@
--> -->
<link rel="stylesheet" type="text/css" href="{{ asset('css/bootstrap.css') }}"/> <link rel="stylesheet" type="text/css" href="{{ asset('css/bootstrap.css') }}"/>
<script src="{{ asset('js/bootstrap.js') }}" type="text/javascript"></script>
<!-- <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap-theme.min.css"> --> <!-- <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap-theme.min.css"> -->
{{-- Basset::show('bootstrapper.css') --}} {{-- Basset::show('bootstrapper.css') --}}
{{ Basset::show('bootstrapper.js') }} {{-- Basset::show('bootstrapper.js') --}}
<script src="{{ asset('js/bootstrap-combobox.js') }}" type="text/javascript"></script> <script src="{{ asset('js/bootstrap-combobox.js') }}" type="text/javascript"></script>
@ -61,7 +62,7 @@
body > div.container { body > div.container {
max-width: 850px; /*max-width: 850px;*/
} }
label.control-label { label.control-label {
@ -69,6 +70,11 @@
} }
div.panel {
padding-left: 0px !important;
padding-right: 0px !important;
}
.form-actions { .form-actions {
margin: 0; margin: 0;
background-color: transparent; background-color: transparent;
@ -233,7 +239,7 @@
<p/> <p/>
<div> <div>
<span style="font-size:38px">LOGO</span> <span style="font-size:30px">Invoice Ninja</span>
<div style="float:right;text-align:right"> <div style="float:right;text-align:right">
Logged in as Guest (<u>Sign up</u>) | {{ link_to('account/details', 'My Account') }} Logged in as Guest (<u>Sign up</u>) | {{ link_to('account/details', 'My Account') }}
<p class="text-danger">This is a sample site, the data is erased</p> <p class="text-danger">This is a sample site, the data is erased</p>
@ -261,11 +267,25 @@
{{ HTML::menu_link('payment') }} {{ HTML::menu_link('payment') }}
{{-- HTML::nav_link('reports', 'Reports') --}} {{-- HTML::nav_link('reports', 'Reports') --}}
</ul> </ul>
<form class="navbar-form navbar-right" role="search"> <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>
<ul class="dropdown-menu">
@if (count(Session::get(RECENTLY_VIEWED)) == 0)
<li><a href="#">No items</a></li>
@else
@foreach (Session::get(RECENTLY_VIEWED) as $link)
<li><a href="{{ $link->url }}">{{ $link->name }}</a></li>
@endforeach
@endif
</ul>
</li>
<form class="navbar-form navbar-left" role="search">
<div class="form-group"> <div class="form-group">
<input type="text" id="search" class="form-control" placeholder="Search"> <input type="text" id="search" class="form-control" placeholder="Search">
</div> </div>
</form> </form>
</ul>
</div><!-- /.navbar-collapse --> </div><!-- /.navbar-collapse -->
</nav> </nav>

View File

@ -5,7 +5,7 @@
{{ Button::primary_link(URL::to('invoices/create'), 'New Invoice', array('class' => 'pull-right')) }} {{ Button::primary_link(URL::to('invoices/create'), 'New Invoice', array('class' => 'pull-right')) }}
{{ Datatable::table() {{ Datatable::table()
->addColumn('Number', 'Client', 'Amount', 'Date') ->addColumn('Invoice Number', 'Client', 'Amount', 'Date')
->setUrl(route('api.invoices')) ->setUrl(route('api.invoices'))
->setOptions('sPaginationType', 'bootstrap') ->setOptions('sPaginationType', 'bootstrap')
->setOptions('bFilter', false) ->setOptions('bFilter', false)