revosystems / sidecar
Laravel report tool
Installs: 24 452
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 4
Forks: 2
Open Issues: 0
Requires
- php: ^8.0
- laravel/framework: 8.x|9.x|10.x|11.x
- revosystems/dejavu: ^0.2
Requires (Dev)
- phpunit/phpunit: 10.0.x-dev
- dev-master
- 4.1.5
- 4.1.4
- 4.1.3
- 4.1.2
- 4.1.1
- 4.1.0
- 4.0.33
- 4.0.32
- 4.0.31
- 4.0.30
- 4.0.29
- 4.0.28
- 4.0.27
- 4.0.26
- 4.0.25
- 4.0.24
- 4.0.23
- 4.0.22
- 4.0.21
- 4.0.20
- 4.0.19
- 4.0.18
- 4.0.17
- 4.0.16
- 4.0.15
- 4.0.14
- 4.0.13
- 4.0.12
- 4.0.11
- 4.0.10
- 4.0.9
- 4.0.8
- 4.0.7
- 4.0.6
- 4.0.5
- 4.0.4
- 4.0.3
- 4.0.2
- 4.0.1
- 4.0.0
- 3.x-dev
- 3.0.14
- 3.0.13
- 3.0.12
- 3.0.11
- 3.0.10
- 3.0.9
- 3.0.8
- 3.0.7
- 3.0.6
- 3.0.5
- 3.0.4
- 3.0.3
- 3.0.2
- 3.0.1
- 3.0
- 2.3.1
- 2.3.0
- 2.2.1
- 2.2.0
- 2.1.2
- 2.1.1
- 2.1.0
- 2.0.2
- 2.0.1
- 2.0.0
- 1.1.7
- 1.1.6
- 1.1.5
- 1.1.4
- 1.1.3
- 1.1.2
- 1.1.1
- 1.1.0
- 1.0.21
- 1.0.20
- 1.0.19
- 1.0.18
- 1.0.17
- 1.0.16
- 1.0.15
- 1.0.14
- 1.0.13
- 1.0.12
- 1.0.11
- 1.0.10
- 1.0.9
- 1.0.8
- 1.0.7
- 1.0.6
- 1.0.5
- 1.0.4
- 1.0.3
- 1.0.2
- 1.0.1
- 1.0.0
- dev-bugfix/raw-expressions
- dev-feature/fix-getting-period-range
- dev-feature/laravel-11
- dev-feature/update-dejavu
- dev-feature/components
- dev-feature/columns
- dev-feature/saved-panels
- dev-bugfix/rev-15027
- dev-fix_sqlite_sidecar
- dev-feature/rev-14410
- dev-feature/rev-13688
- dev-feature/rev-12015
- dev-laravel-10
- dev-bugfix/filter-on-belongs-to-relationship
- dev-feature/REV-10787
- dev-bugfix/REV-11106
- dev-feature/REV-11033
- dev-release/1.X
- dev-hotfix/REV-10374-decimal-and-currency-to-csv-thousands-separator
- dev-feature/tests
- dev-feature/compare
This package is auto-updated.
Last update: 2024-10-10 15:44:32 UTC
README
Install
composer require revosystems/sidecar
You need to have
tailwind 2.x
andapline 3.x
in you main template
Add the blades path to the tailwindcss config file
purge: [
...
'./vendor/revosystems/sidecar/**/*.blade.php',
],
And define your brand color like so:
theme: {
extend: {
colors: {
'brand': '#F2653A',
}
},
},
You should also include the chart.js and choices.js to your main template's header with this line:
{!! \Revo\Sidecar\Sidecar::dependencies() !!}
Configuration
Publish the configuration tot adapt it to you project
php artisan vendor:publish
In config/sidecar.php
you will find the following parameters that can be adapted for your project
In assets/css/sidecar.css
you will find the default styles
You should add those files to your assets compilation.
Global Variables
You can customize some runtime variables implementing the serving callback
class AppServiceProvider extends ServiceProvider
public function boot() {
Sidecar::$usesMultitenant = true; // When true, all the caches and jobs will use the `auth()->user()->id` as prefix
Sidecar::serving(function(){
\Revo\Sidecar\ExportFields\Date::$timezone = auth()->user()->timezone; // The timezone to display the dates
\Revo\Sidecar\ExportFields\Date::$openingTime = auth()->user()->getBusiness()->openingTime; // To define a day change time instead of 00:00
\Revo\Sidecar\ExportFields\Currency::setFormatter('es_ES', auth()->user()->currency ?? 'EUR'); // For the currency field
});
}
Reports
To create your report you should create a new file caled WathereverYouWantReport
(note it needs to end with Report) in the folder you defined in the reportsPath
of the config file
This report class needs to extend the main Revo\Sidecar\Report
class
And define the main model class and implement the fields method
<?php
namespace App\Reports;
use Revo\Sidecar\Report;
use App\Post;
class OrdersReport extends Report {
protected $model = Post::class;
public function getFields() : array{
return [ ];
}
}
Now we just need to define the fields we want to show from our report
We can also add default query filters overriding the query()
method
public function query() : Builder {
return parent::query()->withTrashed();
}
We can also add MainActions as a button in the top right corner.
public function mainActions(): array
{
return [
MainAction::make(?string $title = null, ?string $icon = null, ?string $url = '')
];
}
More features
Fields
You can define the export fields with a simple array, most of them share the same features
Creation
ExportField::make($field, $title, $dependsOnField)
Default options
Text
· When filtering it will perform a like
and you can enter you custom search text
Number
· It will align the row to the right
· When grouping by, by default will do a sum (you can change to it with the onGroupingBy()
funciton)
· When filtering it will allow you to chose the operator and the amount
· It provides the function trimZeros()
that will remove the trailing zeros of decimal values
Decimal
· Extends from number
Currency
· Extends from number
Percentage
· Extends from number and adds a % symbol to the html export
Date
· This field allows different grouping by options (hour, day, dayOfWeek, week, month, quarter)
· When enabling the filterable, by default uses the last 7 days
· The filterOnClick option, performs a depth grouping filter, so if you group by month, and you click Novemeber, it will filter just november, grouping by week
· Date field also comes with a timeFilterable()
that shows a time frame filter as well
Computed
· This field allows you to perform operations on the field, for example Computed::make('guests/total')
· You can define the groupingBy operation as well Computed::make('guests/total')->onGroupingBy('sum(guests)/sum(total)')
Id
· Id field shows the id
of the row
· When grouping by, it will perform a count
· It is very common to use the id only when grouping Id::make()->onlyWhenGrouping()
BelongsTo
· When you have a belongs to relationship on your model, you can use this field to automatically create the filters/groups by
· By default it will use the name
field on the relationship, use the relationShipDisplayField($field)
function to use another field
· It will perform the needed joins when sorting / grouping By
· You can scope the belongs by filter options by providing the list of ids filterOptionsIdsScope()
· If you need to perform the join always you can call the defaultJoin()
You would usually filter the whole query as well to avoid accessing issues
BelongsToThrough
· When you have a belongs to through relationship on your model, you can use this field to automatically create the filters/groups by, it will perform
· By default it will use the name
field on the relationship, use the relationShipDisplayField($field)
function to use another field
· You need to define the pivot relation using the through()
function BelongsToThrough::make('user')->trough('comment')
· It will perform the needed joins when sorting / grouping By
HasMany
· This field, will display all the hasMany models related, imploding the name with a ,
. By default it will use the name
field on the relation, you can change it with relationShipDisplayField($field)
· This field is not filterable neither groupable
HasOne
· It provides the defaultJoin()
option to perform the join in every query
· It is not filterable neither groupable
Enum
· It works for fields that are enum (consts) like a status
or type
· There is the options()
function where you define the array of [["value" => "displayName"]]
that will be used to display and filter
Icon
· A simple field, that will show the fontawesome icon using the field
value as icon name
Create your ExportField
You can create your own Export Fields by just extending any of the previous fields, or the main Sidecar\ExportFields\ExportField
<?php
namespace App\Reports\Sidecar;
use Revo\Sidecar\ExportFields\Link;
class Preview extends Link
{
protected ?string $linkClasses = "showPopup";
public static function make($field, $title = null, $dependsOnField = null)
{
$field = parent::make($field, $title, $dependsOnField);
$field->route = $title;
return $field;
}
public function getTitle(): string
{
return "";
}
public function getLinkTitle($row) : string {
return '<i class="fa fa-eye fa-fw"></i>';
}
public function toHtml($row): string
{
$link = route($this->route, $this->getValue($row));
return "<a href='{$link}' class='{$this->linkClasses}' style='color:gray;'>{$this->getLinkTitle($row)}</a>";
}
}
The ExportField
has the function toHtml($row)
that is the one that will display the value when in the browser, when exporting to CSV it will use the getValue($row)
so you can customize both.
It is common to override the
getFilterKey()
with something likenon-filterable
to avoid collisions with the common fields, in the case of thisPreview
file, would collide with theId
field as it is the one to use in the link
Widgets
A report can define a set of widgets to summarize what is being displayed (not paginated) so when it is not grouping it will show them. Right now there are just two possible widgets
<?php
namespace App\Reports\V2;
use App\Models\Orders\Order;
use Revo\Sidecar\Widgets\Count;
use Revo\Sidecar\Widgets\Sum;
class OrdersReport extends Report
{
protected $model = Order::class;
public function getFields() : array{
return [
...
];
}
public function getWidgets() : array
{
return [
Count::make('id', __('admin.count')),
Sum::make('guests', trans_choice('admin.guest', 2)),
];
}
}
Dashboard
Sidecar
also comes with nice dashboard panels that can be embeded into any page
You just need to create one by extending the Revo\Sidecar\Panel
and define the dimesion
and the metric
You need to pass the filters to apply to this report.
<?php
namespace App\Reports\V2\Sidecar\Widgets;
use App\Models\Orders\Order;
use App\Reports\V2\OrdersReport;
use Illuminate\Database\Eloquent\Builder;
use Revo\Sidecar\ExportFields\Currency;
use Revo\Sidecar\ExportFields\Date;
use Revo\Sidecar\ExportFields\ExportField;
use Revo\Sidecar\Filters\Filters;
use Revo\Sidecar\Panels\Panel;
class Sales extends Panel
{
protected $model = Order::class;
protected ?string $tooltip = "salesByDayDesc";
public function query() : Builder{
return parent::query()->whereNull('canceled')->whereNull('merged')->whereNotNull('opened');
}
public function __construct()
{
$filters = (new Filters())->groupingBy(['opened' => 'day'])
->forPeriod('opened', 'last30days')
->sortBy('opened', 'asc');
parent::__construct("salesByDay", $filters);
}
public function metricField(): ExportField
{
return Currency::make('total');
}
public function dimensionField(): ExportField
{
return Date::make('opened')->filterable();
}
public function getFullReportLink(): ?string
{
return url('reports/v2/orders?' . $this->filters->getQueryString() );
}
}
You can implement the
getFullReportLink()
to link to a full report Note$filters->getQueryString()
returns the query string to use for theurl
Finally you can render them in your blade with something like this
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
@foreach($panels as $panel)
{!! $panel->render() !!}
@endforeach
</div>
This dashboard panels will be cached until the next day, and will be loaded with ajax.
Panel Type
There are different types of Panels
defined by the enum PanelType
, bar
, list
, trend
(default), and pie
class TopPaymentMethods extends Panel
{
...
public PanelType $type = PanelType::pie;
...
}
``