Today I'm starting a series of reviews: will be trying and testing various Laravel-based open-source projects available on GitHub and elsewhere. First to check out - is a self-hosted CRM project called Flarepoint.
Other reviews in this series:
- FlarePoint: Laravel-based free CRM
- Invoice Ninja – Laravel-powered solution for better invoicing
- Attendize – event tickets selling system based on Laravel 5
- Ribbbon – project management system on Laravel 5.1 and Vue.js
- Faveo: impressive helpdesk system built on Laravel
- Confomo: Laravel-based website to meet Twitter friends at conferences
For those who don't know what CRM is - it stands for "Customer Relationship Management". Basically, it's a system to manage data of your clients, potential leads, tasks assigned to them for follow-up or other actions.
Flarepoint is a CRM built on Laravel 5.3 - launched quite recently, in July 2016. Its author is Casper Bottelet from Denmark.
Here's how Flarepoint CRM looks in action:
As you can see, it's a dashboard with all the functionality I had mentioned above.
Installation
Flarepoint is distributed as a full Laravel project for download and install on your server. That's exactly what I did myself - tested it on my local Homestead.
Installation went well, instructions are perfectly clear and simple. Just clone, run composer install, database migration/seeds and you're good to go. Well done.
Especially I liked that the system comes with ready-made seed files with data for Departments, Industries and other settings.
Usage
After installation, you can launch the application and see login screen.
The first default user to login is admin@admin.com - admin123, provided in installation guide. Convenient.
We log in and see the same dashboard but empty.
And then, one by one, we can fill in the data for our clients.
In general, everything is straightforward. One thing I didn't like that "Create entry" actions were only in the main menu on the left, but there were no "create" buttons in client list, or any lists. But that's a minor issue, no big deal.
Laravel code
As I mentioned, Flarepoint comes as open-source project, so you can modify it however you want. So let's take a look what's underneath in the code. I usually start evaluating the codebase from routes file - in this case, since it's Laravel 5.3 - we look at routes/web.php file.
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| This file is where you may define all of the routes that are handled
| by your application. Just tell Laravel the URIs it should respond
| to using a Closure or controller method. Build something great!
|
*/
Route::auth();
Route::get('/logout', 'Auth\LoginController@logout');
Route::group(['middleware' =--> ['auth']], function () {
/**
* MAIN
*/
Route::get('/', 'PagesController@dashboard');
Route::get('dashboard', 'PagesController@dashboard')->name('dashboard');
/**
* USERS
*/
Route::get('users/data', 'UsersController@anyData')->name('users.data');
Route::get('users/taskdata/{id}', 'UsersController@taskData')->name('users.taskdata');
Route::get('users/closedtaskdata/{id}', 'UsersController@closedTaskData')->name('users.closedtaskdata');
Route::get('users/clientdata/{id}', 'UsersController@clientData')->name('users.clientdata');
Route::resource('users', 'UsersController');
/* ROLES */
Route::resource('roles', 'RolesController');
/**
* CLIENTS
*/
Route::get('clients/data', 'ClientsController@anyData')->name('clients.data');
Route::post('clients/create/cvrapi', 'ClientsController@cvrapiStart');
Route::post('clients/upload/{id}', 'DocumentsController@upload');
Route::resource('clients', 'ClientsController');
/**
* TASKS
*/
Route::get('tasks/data', 'TasksController@anyData')->name('tasks.data');
Route::patch('tasks/updatestatus/{id}', 'TasksController@updateStatus');
Route::patch('tasks/updateassign/{id}', 'TasksController@updateAssign');
Route::post('tasks/updatetime/{id}', 'TasksController@updateTime');
Route::post('tasks/invoice/{id}', 'TasksController@invoice');
Route::resource('tasks', 'TasksController');
Route::post('tasks/comments/{id}', 'CommentController@store');
/**
* LEADS
*/
Route::get('leads/data', 'LeadsController@anyData')->name('leads.data');
Route::patch('leads/updateassign/{id}', 'LeadsController@updateAssign');
Route::resource('leads', 'LeadsController');
Route::post('leads/notes/{id}', 'NotesController@store');
Route::patch('leads/updatestatus/{id}', 'LeadsController@updateStatus');
Route::patch('leads/updatefollowup/{id}', 'LeadsController@updateFollowup')->name('leads.followup');
/**
* SETTINGS
*/
Route::get('settings', 'SettingsController@index')->name('settings.index');
Route::patch('settings/permissionsUpdate', 'SettingsController@permissionsUpdate');
Route::patch('settings/overall', 'SettingsController@updateOverall');
/**
* DEPARTMENTS
*/
Route::resource('departments', 'DepartmentsController');
/**
* INTEGRATIONS
*/
Route::resource('integrations', 'IntegrationsController');
/* SLACK */
Route::get('Integration/slack', 'IntegrationsController@slack');
/**
* NOTIFICATIONS
*/
Route::get('notifications/getall', 'NotificationsController@getAll')->name('notifications.get');
Route::post('notifications/markread', 'NotificationsController@markRead');
Route::get('notifications/markall', 'NotificationsController@markAll');
/**
* INVOICES
*/
Route::resource('invoices', 'InvoicesController');
Route::post('invoice/updatepayment/{id}', 'InvoicesController@updatePayment')->name('invoice.payment.date');
Route::post('invoice/reopenpayment/{id}', 'InvoicesController@reopenPayment')->name('invoice.payment.reopen');
Route::post('invoice/sentinvoice/{id}', 'InvoicesController@updateSentStatus')->name('invoice.sent');
Route::post('invoice/reopensentinvoice/{id}', 'InvoicesController@updateSentReopen')->name('invoice.sent.reopen');
Route::post('invoice/newitem/{id}', 'InvoicesController@newItem')->name('invoice.new.item');
/**
* IMPORT AND EXPORT
*/
Route::get('documents/import', 'DocumentsController@import');
});
Again, everything is pretty simple - all actions hidden under auth middleware, and most of them are Resource controllers, except for additional action for every item.
We also see a few integrations (like Slack), import of documents and permissions management. All really useful.
Now, let's take a look at one random Controller - let's say, ClientsController.php:
class ClientsController extends Controller { protected $users; protected $clients; protected $settings; public function __construct( UserRepositoryContract $users, ClientRepositoryContract $clients, SettingRepositoryContract $settings ) { $this->users = $users; $this->clients = $clients; $this->settings = $settings; $this->middleware('client.create', ['only' => ['create']]); $this->middleware('client.update', ['only' => ['edit']]); } /** * Display a listing of the resource. * * @return Response */ public function index() { return view('clients.index'); }
As we can see, Repository pattern is being used, and index() method just calls the view. So I assume there's something going on in JavaScript side - let's look at clients/index.blade.php view:
@extends('layouts.master')
@section('heading')
@stop
@section('content')
<table class="table wp-block-table table-hover">
<thead>
<tr>
<th>@lang('client.headers.name')</th>
<th>@lang('client.headers.company')</th>
<th>@lang('client.headers.mail')</th>
<th>@lang('client.headers.primary_number')</th>
<th> </th>
<th> </th>
</tr>
</thead>
</table>
@stop
@push('scripts')
<script>
$(function() {
$('#clients-table').DataTable({
processing: true,
serverSide: true,
ajax: '{!! route('clients.data') !!}',
columns: [
{ data: 'namelink', name: 'name' },
{ data: 'company_name', name: 'company_name' },
{ data: 'email', name: 'email' },
{ data: 'primary_number', name: 'primary_number' },
{ data: 'edit', name: 'edit', orderable: false, searchable: false},
{ data: 'delete', name: 'delete', orderable: false, searchable: false},
]
});
});
</script>
@endpush
Guess what - I was right, the data is served via Datatables package and with AJAX.
Conclusion
All in all, Flarepoint looks like a really good minimal CRM project - I can congratulate Casper on great work:
- Useful and usable system
- Clean and professional code
- Simple-to-use and with proper documentation
You can also use Flarepoint as an example project to learn or improve your Laravel knowledge.
Some links once again:
Other reviews in this series:
- FlarePoint: Laravel-based free CRM
- Invoice Ninja – Laravel-powered solution for better invoicing
- Attendize – event tickets selling system based on Laravel 5
- Ribbbon – project management system on Laravel 5.1 and Vue.js
- Faveo: impressive helpdesk system built on Laravel
- Confomo: Laravel-based website to meet Twitter friends at conferences
No comments or questions yet...