# Dashboard Reporting
The dashboard reports on a period the user chooses with a single date range control, and every section on the page follows that same period. The figures are produced by reporting helpers under Webkul\Admin\Helpers\Reporting.
# 🧱 The reporting helpers
Each helper covers one subject and extends AbstractReporting:
| Helper | Reports on |
|---|---|
Lead | Lead counts, stages and value. |
Quote | Quotes raised over the period. |
Person | The contacts bringing in the most revenue. |
Product | The products sold most over the period. |
Activity | Activities logged over the period. |
Organization | Organization figures. |
AbstractReporting holds the period itself, so a helper only writes the query for its own subject.
# ⚙️ The period
The base class keeps a current period and the one immediately before it, which is what lets every section show a change against the previous period.
| Method | Returns |
|---|---|
getStartDate() / getEndDate() | The period being reported on. |
getLastStartDate() / getLastEndDate() | The period immediately before it, of the same length. |
getPercentageChange($previous, $current) | The change between the two. |
# Setting the period
$reporting = app(\Webkul\Admin\Helpers\Reporting\Lead::class)
->setStartDate($startDate)
->setEndDate($endDate);
Two rules are applied as the dates are set:
- An end date in the future is pulled back to now. There is nothing yet to report on beyond today.
- An inverted range is corrected. If the start date is later than the end date, the start is pulled back to the end date, so a reversed range reports on a single day rather than returning nothing.
Set the start date before the end date
The inverted-range correction runs inside setEndDate() and adjusts the start. Calling them the other way round leaves the range uncorrected.
# 📝 Grouping over time
getTimeInterval() splits the period into points for a chart, choosing the grouping from the length of the range:
$reporting->getTimeInterval($startDate, $endDate, 'created_at', $period);
Passing 'auto' picks the grouping from the length of the range:
| Range | Grouped by | SQL grouping |
|---|---|---|
| More than 5 months | Months | MONTH(created_at) |
| More than 6 weeks | Weeks | WEEK(created_at) |
| Less than 6 weeks | Days | DAYOFYEAR(created_at) |
It returns both the grouping column and the intervals to plot:
[
'group_column' => 'DAYOFYEAR(created_at)',
'intervals' => [...],
]
# 🔒 Scoping a report to the user
A helper that reports on records belonging to users should restrict its query to the acting user's data scope. bouncer()->getAuthorizedUserIds() returns null for a user who may see everything, and the ids they are limited to otherwise:
->when(
($userIds = bouncer()->getAuthorizedUserIds()) !== null,
fn ($query) => $query->whereIn('leads.user_id', $userIds)
)
Scope every helper that reports on owned records
A helper without this clause reports across the whole installation, so a user restricted to their own records still sees global figures. Person and Product apply it; check any helper you add, and any you extend.
# 🧩 Adding your own report
# 1. Extend the base class
<?php
namespace Webkul\Example\Helpers\Reporting;
use Webkul\Admin\Helpers\Reporting\AbstractReporting;
class Example extends AbstractReporting
{
public function getTotalExamplesOverTime($period = 'auto'): array
{
return $this->getTimeInterval(
$this->getStartDate(),
$this->getEndDate(),
'created_at',
$period
);
}
}
# 2. Read the period from the base class
Use getStartDate() and getEndDate() rather than reading the request, so your report follows the same control as the rest of the page.
# 3. Verify
php artisan tinker --execute="\$r = app(Webkul\Admin\Helpers\Reporting\Lead::class)->setStartDate(now()->subDays(30))->setEndDate(now()); echo \$r->getStartDate().' to '.\$r->getEndDate();"