Dashboards e widgets
mad-db-steps
mad-goal-ladder
mad-segment-stats
mad-timeline
Steps alimentados pelo banco
mad-db-steps lê a lista de estados do pedido direto do model — cor e ordem vêm da própria tabela.
✓
Novo
✓
Em separação
3
Enviado
4
Entregue
5
Cancelado
✓
Novo
✓
Em separação
3
Enviado
4
Entregue
5
Cancelado
✓
Novo
✓
Em separação
Enviado
Entregue
Cancelado
resources/views/tutor/examples/dashboards-widgets/db-steps.blade.php
<div style="display:flex;flex-direction:column;gap:22px;">
<mad-db-steps model="TutorDemoEstadoPedido" display="nome" color-field="cor" order-by="ordem asc"
value="3" variant="arrows" />
<mad-db-steps model="TutorDemoEstadoPedido" display="nome" color-field="cor" order-by="ordem asc"
value="3" variant="dots" size="sm" />
<mad-db-steps model="TutorDemoEstadoPedido" display="nome" color-field="cor" order-by="ordem asc"
value="3" variant="progress" />
</div>
Escada de metas
mad-goal-ladder: Meta → Projeção → Realizado → Saldo, com a seta de variação entre degraus.
Meta x Realizado (trimestre demo)
Meta do trimestre
R$ 150.000,00
▲ 396,8% ▲
Projeção
R$ 745.151,21
▼ -7,4% ▼
Realizado
R$ 689.954,82
▲ Meta batida ▲
Saldo até a meta
R$ 539.954,82
resources/views/tutor/examples/dashboards-widgets/goal-ladder.blade.php
@php($__component = \Mad\Component\MadRenderContext::getComponent())
<mad-goal-ladder title="Meta x Realizado (trimestre demo)" :steps="$__component->goalLadderSteps()" accent="#2F4F3F" />
app/control/Tutor/TutorDashboardsWidgets.php
/** Degraus Meta → Projeção → Realizado → Saldo, pro mad-goal-ladder. */
public function goalLadderSteps(): array
{
$meta = 150000.0;
$realizado = (float) (TutorDemoPedido::query()->sum('valor_total') ?? 0);
$projecao = round($realizado * 1.08, 2);
$saldo = round($meta - $realizado, 2);
$money = fn (float $v): string => 'R$ ' . number_format($v, 2, ',', '.');
$pct = fn (float $a, float $b): string => $b > 0
? number_format((($a - $b) / $b) * 100, 1, ',', '.') . '%'
: '—';
return [
['label' => 'Meta do trimestre', 'value' => $money($meta)],
[
'label' => 'Projeção', 'value' => $money($projecao),
'delta' => ['text' => $pct($projecao, $meta), 'dir' => $projecao >= $meta ? 'up' : 'down'],
],
[
'label' => 'Realizado', 'value' => $money($realizado),
'delta' => ['text' => $pct($realizado, $projecao), 'dir' => $realizado >= $projecao ? 'up' : 'down'],
],
[
'label' => 'Saldo até a meta', 'value' => $money(abs($saldo)), 'muted' => true,
'delta' => ['text' => $saldo <= 0 ? 'Meta batida' : 'Falta bater', 'dir' => $saldo <= 0 ? 'up' : 'down'],
],
];
}
Contadores segmentados
mad-segment-stats resume uma distribuição em uma faixa horizontal de contadores.
Pedidos por estado
74
Entregue
49,3%
24
Enviado
16,0%
20
Em separação
13,3%
17
Cancelado
11,3%
15
Novo
10,0%
resources/views/tutor/examples/dashboards-widgets/segment-stats.blade.php
@php($__component = \Mad\Component\MadRenderContext::getComponent())
<mad-segment-stats title="Pedidos por estado" :items="$__component->segmentStatsItems()" />
app/control/Tutor/TutorDashboardsWidgets.php
/** Contagem + percentual de pedidos por estado, pro mad-segment-stats. */
public function segmentStatsItems(): array
{
$total = TutorDemoPedido::query()->count();
if ($total === 0) {
return [];
}
return TutorDemoPedido::query()
->join('tutor_estados_pedido as te', 'te.id', '=', 'tutor_pedidos.estado_id')
->selectRaw('te.nome as estado_nome, te.cor as cor, count(*) as qtd')
->groupBy('te.nome', 'te.cor')
->orderByDesc('qtd')
->get()
->map(fn ($r) => [
'value' => (int) $r->qtd,
'label' => $r->estado_nome,
'pct' => round(($r->qtd / $total) * 100, 1),
'color' => $r->cor,
])
->all();
}
Linha do tempo
mad-timeline com itens manuais — o ciclo de vida de um pedido, do pedido criado até a entrega.
10/08/2026
00:00
Pedido #150 criado
Recebido e aguardando separação.
00:00
Em separação
Itens conferidos no estoque.
00:00
Enviado
Despachado para a transportadora.
00:00
Entregue
Cliente confirmou o recebimento.
resources/views/tutor/examples/dashboards-widgets/timeline.blade.php
@php($__component = \Mad\Component\MadRenderContext::getComponent())
<mad-timeline :items="$__component->timelineDoPedido()" cards />
app/control/Tutor/TutorDashboardsWidgets.php
/** Linha do tempo sintética do ciclo de vida de um pedido, pro mad-timeline. */
public function timelineDoPedido(): array
{
$pedido = TutorDemoPedido::query()->orderByDesc('id')->first();
if (!$pedido) {
return [];
}
$data = (string) $pedido->dt_pedido;
return [
['title' => "Pedido #{$pedido->id} criado", 'body' => 'Recebido e aguardando separação.', 'date' => $data, 'icon' => 'shopping-cart', 'color' => 'info'],
['title' => 'Em separação', 'body' => 'Itens conferidos no estoque.', 'date' => $data, 'icon' => 'package-search', 'color' => 'warning'],
['title' => 'Enviado', 'body' => 'Despachado para a transportadora.', 'date' => $data, 'icon' => 'truck', 'color' => 'info'],
['title' => 'Entregue', 'body' => 'Cliente confirmou o recebimento.', 'date' => $data, 'icon' => 'check-check', 'color' => 'success'],
];
}
Código-fonte completo da página
app/control/Tutor/TutorDashboardsWidgets.php
<?php
namespace App\Control\Tutor;
use App\Models\TutorDemo\TutorDemoPedido;
use App\Support\Tutor\TutorDemoSeed;
use App\Support\Tutor\TutorPage;
/**
* TutorDashboardsWidgets — página "Widgets" do catálogo.
*
* mad-db-steps é 100% declarativo (não precisa de PHP aqui). Os outros três
* widgets (goal-ladder, segment-stats, timeline) recebem dados via prop
* array — os métodos abaixo calculam esses arrays a partir da fixture
* TutorDemo (nunca @php com query Eloquent direto no blade).
*/
class TutorDashboardsWidgets extends TutorPage
{
protected string $catalogKey = 'dashboards_widgets';
public function mount(array $params = []): void
{
TutorDemoSeed::ensure();
}
/** Degraus Meta → Projeção → Realizado → Saldo, pro mad-goal-ladder. */
public function goalLadderSteps(): array
{
$meta = 150000.0;
$realizado = (float) (TutorDemoPedido::query()->sum('valor_total') ?? 0);
$projecao = round($realizado * 1.08, 2);
$saldo = round($meta - $realizado, 2);
$money = fn (float $v): string => 'R$ ' . number_format($v, 2, ',', '.');
$pct = fn (float $a, float $b): string => $b > 0
? number_format((($a - $b) / $b) * 100, 1, ',', '.') . '%'
: '—';
return [
['label' => 'Meta do trimestre', 'value' => $money($meta)],
[
'label' => 'Projeção', 'value' => $money($projecao),
'delta' => ['text' => $pct($projecao, $meta), 'dir' => $projecao >= $meta ? 'up' : 'down'],
],
[
'label' => 'Realizado', 'value' => $money($realizado),
'delta' => ['text' => $pct($realizado, $projecao), 'dir' => $realizado >= $projecao ? 'up' : 'down'],
],
[
'label' => 'Saldo até a meta', 'value' => $money(abs($saldo)), 'muted' => true,
'delta' => ['text' => $saldo <= 0 ? 'Meta batida' : 'Falta bater', 'dir' => $saldo <= 0 ? 'up' : 'down'],
],
];
}
/** Contagem + percentual de pedidos por estado, pro mad-segment-stats. */
public function segmentStatsItems(): array
{
$total = TutorDemoPedido::query()->count();
if ($total === 0) {
return [];
}
return TutorDemoPedido::query()
->join('tutor_estados_pedido as te', 'te.id', '=', 'tutor_pedidos.estado_id')
->selectRaw('te.nome as estado_nome, te.cor as cor, count(*) as qtd')
->groupBy('te.nome', 'te.cor')
->orderByDesc('qtd')
->get()
->map(fn ($r) => [
'value' => (int) $r->qtd,
'label' => $r->estado_nome,
'pct' => round(($r->qtd / $total) * 100, 1),
'color' => $r->cor,
])
->all();
}
/** Linha do tempo sintética do ciclo de vida de um pedido, pro mad-timeline. */
public function timelineDoPedido(): array
{
$pedido = TutorDemoPedido::query()->orderByDesc('id')->first();
if (!$pedido) {
return [];
}
$data = (string) $pedido->dt_pedido;
return [
['title' => "Pedido #{$pedido->id} criado", 'body' => 'Recebido e aguardando separação.', 'date' => $data, 'icon' => 'shopping-cart', 'color' => 'info'],
['title' => 'Em separação', 'body' => 'Itens conferidos no estoque.', 'date' => $data, 'icon' => 'package-search', 'color' => 'warning'],
['title' => 'Enviado', 'body' => 'Despachado para a transportadora.', 'date' => $data, 'icon' => 'truck', 'color' => 'info'],
['title' => 'Entregue', 'body' => 'Cliente confirmou o recebimento.', 'date' => $data, 'icon' => 'check-check', 'color' => 'success'],
];
}
}
resources/views/tutor/dashboards/widgets.blade.php
<x-tutor-page page="dashboards_widgets">
<x-tutor-demo demo="tutor.examples.dashboards-widgets.db-steps"
title="Steps alimentados pelo banco" desc="mad-db-steps lê a lista de estados do pedido direto do model — cor e ordem vêm da própria tabela." />
<x-tutor-demo demo="tutor.examples.dashboards-widgets.goal-ladder"
title="Escada de metas" desc="mad-goal-ladder: Meta → Projeção → Realizado → Saldo, com a seta de variação entre degraus."
php="TutorDashboardsWidgets::goalLadderSteps" />
<x-tutor-demo demo="tutor.examples.dashboards-widgets.segment-stats"
title="Contadores segmentados" desc="mad-segment-stats resume uma distribuição em uma faixa horizontal de contadores."
php="TutorDashboardsWidgets::segmentStatsItems" />
<x-tutor-demo demo="tutor.examples.dashboards-widgets.timeline"
title="Linha do tempo" desc="mad-timeline com itens manuais — o ciclo de vida de um pedido, do pedido criado até a entrega."
php="TutorDashboardsWidgets::timelineDoPedido" />
</x-tutor-page>