Kitchen sink (demo)
Demonstração de todos os componentes em uma única página.
Tour consolidado por boa parte dos componentes do MAD em ação: form com
dezenas de campos diferentes, listagem com agrupamento, drawer com mestre/detalhe e
chips pivot, dashboard com KPIs/charts/pivot, calendário/kanban/gantt, os widgets ERP
da linha 5.x (mad-sheet, mad-reconcile,
mad-org-chart, mad-pdv) e os blocos de
feedback (toast, modal, drawer, alert, steps). Use como referência visual e ponto de
partida para copiar/colar.
Cada bloco abaixo é independente — copie só a seção que precisa, ajuste
name/model/método PHP, e funciona isolado. Para a lista
completa de props de qualquer tag, use a referência de
Components.
1. Form com diversos tipos de campo
<mad-form submit="onSave">
<mad-form-section title="Texto" icon="type">
<mad-form-grid :cols="3">
<mad-input-field name="nome" label="Nome" required />
<mad-input-field name="cpf" label="CPF" mask="cpf" strip-mask />
<mad-input-field name="placa" label="Placa" mask="placa" force-case="upper" />
<mad-input-field name="email" label="Email" type="email" icon="mail" />
<mad-password-field name="senha" label="Senha" :strong-password="true" />
<mad-otp-field name="codigo" label="Código de verificação" :length="6" />
</mad-form-grid>
<mad-textarea-field name="observacoes" label="Observações" :rows="4" />
<mad-html-editor-field name="conteudo" label="Conteúdo HTML" :height="240" />
</mad-form-section>
<mad-separator />
<mad-form-section title="Endereço (autopreenchido)" icon="map-pin">
<mad-form-grid :cols="3">
<mad-cep-field name="cep" label="CEP"
:fill-fields="['rua'=>'rua','cidade_id'=>'cidade_id','estado_id'=>'estado_id']" />
<mad-cnpj-field name="cnpj" label="CNPJ"
:fill-fields="['razao_social'=>'razao_social']" />
<mad-scanner-field name="cod_barras" label="EAN" type="both" />
</mad-form-grid>
</mad-form-section>
<mad-separator />
<mad-form-section title="Números" icon="hash">
<mad-form-grid :cols="3">
<mad-number-field name="quantidade" label="Quantidade" :min="1" step="1" />
<mad-numeric-field name="peso" label="Peso (kg)" :decimals="3" />
<mad-money-field name="preco" label="Preço" />
<mad-spinner-field name="qtd_itens" label="Itens" :min="1" :max="99" />
<mad-range-field name="desconto" label="Desconto" :min="0" :max="100" suffix="%" />
</mad-form-grid>
</mad-form-section>
<mad-separator />
<mad-form-section title="Data e hora" icon="calendar">
<mad-form-grid :cols="3">
<mad-date-field name="dt_nascimento" label="Nascimento" />
<mad-time-field name="hora_entrega" label="Hora de entrega" />
<mad-datetime-field name="dt_evento" label="Data + hora" />
</mad-form-grid>
<mad-daterange-field name_start="dt_ini" name_end="dt_fim" label="Período" presets />
</mad-form-section>
<mad-separator />
<mad-form-section title="Seleção" icon="check-square">
<mad-form-grid :cols="3">
<mad-select-field name="status" label="Status" :items="['A'=>'Ativo','I'=>'Inativo']" />
<mad-dbcombo-field name="categoria_id" label="Categoria" model="Categoria" display="nome" />
<mad-radio-field name="tipo" label="Tipo" :options="['F'=>'Física','J'=>'Jurídica']" inline />
<mad-checkbox-field name="ativo" label="Ativo" />
<mad-switch-field name="marketing" label="Marketing" />
<mad-color-field name="cor" label="Cor" />
</mad-form-grid>
<mad-checkbox-group-field name="permissoes" label="Permissões"
:options="['read'=>'Ler','write'=>'Escrever','delete'=>'Excluir']" layout="horizontal" />
<mad-multi-entry-field name="tags" label="Tags" placeholder="Digite e Enter..." />
<mad-sort-list-field name="ordem"
:items="['nome'=>'Nome','email'=>'Email','fone'=>'Telefone']" />
</mad-form-section>
<mad-separator />
<mad-form-section title="Upload" icon="upload">
<mad-form-grid :cols="2">
<mad-file-field name="contrato" label="Contrato"
storage="disk" folder="uploads/contratos" accept=".pdf" />
<mad-image-field name="foto" label="Foto" crop aspect-ratio="1:1"
storage="disk" folder="uploads/fotos" />
<mad-avatar-field name="avatar" label="Avatar"
storage="disk" folder="uploads/avatars" />
<mad-signature-field name="assinatura" label="Assinatura" modes="draw,type,upload"
storage="disk" folder="uploads/assinaturas" />
</mad-form-grid>
<mad-multi-file-field name="anexos" label="Anexos"
storage="disk" folder="uploads/docs" mode="comma" :max-files="5" />
</mad-form-section>
<mad-separator />
<mad-form-actions>
<mad-btn type="submit" variant="primary" icon="save">Salvar</mad-btn>
<mad-btn variant="ghost" close-drawer>Cancelar</mad-btn>
</mad-form-actions>
</mad-form>
2. Listagem com agrupamento, busca e export
<mad-grid self per-page="20" searchable :search-columns="['nome','email']"
sticky card-view
:group-by="['categoria.nome']" group-mask="{categoria_nome}" group-total
export-title="Lista de Clientes">
<mad-columns>
<mad-col field="id" label="Cod." width="70" center sort />
<mad-col field="nome" label="Nome" sort filter card-role="title" />
<mad-col field="email" label="Email" filter />
<mad-col field="cidade->nome" label="Cidade" sort filter />
<mad-col field="dt_cadastro" label="Cadastro" date="d/m/Y" card-role="subtitle" />
<mad-col field="status" label="Status"
badge="A:success:Ativo|I:danger:Inativo|P:warning:Pendente" card-role="badge" />
<mad-col field="valor_total" label="Total" right money="R$" total="sum" card-role="highlight" />
</mad-columns>
<mad-actions>
<mad-nav icon="eye" label="Ver" target="ClienteDetalhe::onShow({id})" />
<mad-nav icon="pencil" label="Editar" target="ClienteForm::onEdit({id})" />
<mad-action-group icon="more-horizontal" label="Ações">
<mad-act method="onAprovar" icon="check-circle" label="Aprovar" primary
when-field="status" when-value="P" />
<mad-act method="onCancelar" icon="ban" label="Cancelar" danger
confirm="Tem certeza?" when-field="status" when-nin="cancelado" />
</mad-action-group>
</mad-actions>
</mad-grid>
3. Drawer com mestre/detalhe + chips pivot
<mad-form submit="onSave">
<mad-form-section title="Pedido" icon="file-text">
<mad-form-grid :cols="2">
<mad-dbcombo-field name="cliente_id" label="Cliente" model="Pessoa" display="nome" required />
<mad-date-field name="dt_pedido" label="Data" required />
</mad-form-grid>
</mad-form-section>
<mad-separator />
<mad-form-section title="Itens" icon="package">
<mad-detail-form name="dfPedidoItem" model="PedidoItem" foreign-key="pedido_id" mode="drawer">
<mad-detail-fields>
<mad-form-grid :cols="2">
<mad-dbcombo-field name="produto_id" label="Produto" model="Produto" display="nome" required />
<mad-numeric-field name="quantidade" label="Qtd" :decimals="3" required />
</mad-form-grid>
<mad-money-field name="valor" label="Valor" />
</mad-detail-fields>
<mad-columns>
<mad-col field="produto->nome" label="Produto" />
<mad-col field="quantidade" label="Qtd" right />
<mad-col field="valor" label="Valor" right money="R$" total="sum" />
</mad-columns>
</mad-detail-form>
</mad-form-section>
<mad-separator />
<mad-form-section title="Tags" icon="tag">
<mad-db-blocks name="tags" mode="flat" pivot-model="Tag"
row-view="components.partials.tag-chip" on-add="onTagCreate">
<mad-db-blocks-form>
<mad-input-field name="name" required />
<mad-color-field name="color" />
</mad-db-blocks-form>
</mad-db-blocks>
</mad-form-section>
<mad-form-actions>
<mad-btn type="submit" variant="primary" icon="save">Salvar</mad-btn>
</mad-form-actions>
</mad-form>
// No controller (MadComponent) — db-blocks exige o trait dedicado.
use Mad\Form\MadDbBlocksTrait;
class PedidoForm extends \Mad\Component\MadComponent
{
use MadDbBlocksTrait;
public function onTagCreate(Tag $tag): void
{
$tag->created_by = session('userid');
}
}
4. Dashboard com KPIs + charts + pivot
<mad-form-grid :cols="4">
<mad-db-metric-card model="PedidoVenda" :query="$that->queryPedidos()"
total="count" label="Pedidos" icon="shopping-cart" format="integer" />
<mad-db-metric-card model="PedidoVenda" :query="$that->queryPedidos()"
field="valor_total" total="sum" label="Faturamento" variant="success" format="money:R$ " />
<mad-db-metric-card model="Cliente" total="count" label="Clientes" icon="users" format="integer" />
<mad-db-metric-card model="Produto" total="count" :filters="[['ativo','=','1']]"
label="Produtos ativos" icon="package" format="integer" />
</mad-form-grid>
<mad-form-grid :cols="2">
<mad-db-chart type="line" model="PedidoVenda" :query="$that->queryPedidos()"
group-by="dt_pedido" field="valor_total" total="sum"
title="Vendas por dia" format="currency:R$" area />
<mad-db-chart type="donut" model="PedidoVenda" :query="$that->queryPedidos()"
group-by="status" total="count" title="Por status" legend percentage />
</mad-form-grid>
<mad-pivot-table model="PedidoVenda" title="Detalhamento" subtotals>
<mad-pivot-row field="vendedor.nome" />
<mad-pivot-col field="status" />
<mad-pivot-value field="valor_total" aggregation="sum" format="currency" />
</mad-pivot-table>
Ver Dashboard real
para o controller completo (MadDashboard + baseQuery()) por trás deste $that.
5. Calendário, Kanban e Gantt
Os três têm modo 100% declarativo: a tag sozinha já faz auto-query no
Eloquent a partir de model + mapeamento de campos — sem subclasse PHP
dedicada. Para customizar comportamento (hooks de drag/resize, cards custom), existe
a variante via subclasse (Mad\Calendar\MadCalendarComponent,
Mad\Calendar\MadKanban, Mad\Calendar\MadGanttComponent) —
ver Calendários, Kanban e Gantt.
<mad-calendar model="Evento" database="business"
title-field="titulo" start-field="dt_inicio" end-field="dt_fim"
color-field="cor" default-view="agendaWeek" editable
click-target="EventoForm::onEdit({id})"
period-type="date-range" date-field="dt_inicio" remember-filters />
<mad-kanban model="Negociacao" database="business"
stage-model="EtapaNegociacao" stage-field="etapa_negociacao_id"
value-field="valor_total" click-target="NegociacaoForm::onEdit({id})" />
<mad-gantt model="ProjetoTarefa" database="business"
name-field="titulo" start-field="dt_inicio" end-field="dt_fim"
parent-field="parent_id" progress-field="percentual" color-field="cor" />
6. Widgets ERP: sheet, reconcile, org-chart e PDV
Quatro componentes de tela inteira que nasceram na linha 5.x e seguem o mesmo padrao
dos widgets grandes (compiler em compile-time + MadComponent de runtime +
config cacheada em sessao + client Alpine dedicado). Todos funcionam embutidos numa
blade qualquer; so as acoes que falam com o servidor (lazy-load, re-parent, finalizacao
de venda) exigem que o host da pagina seja subclasse do componente.
| Tag | Para que serve | Classe de runtime |
|---|---|---|
mad-sheet | Planilha de lancamento em lote (entry-first): nasce vazia, existe para digitar registros NOVOS (inventario, lancamentos contabeis). Listar existentes continua sendo mad-grid. | Mad\Sheet\MadSheet |
mad-reconcile | Conciliacao two-panel (extrato x lancamentos) com auto-match e grupos N:M persistidos em mad_reconcile_group/mad_reconcile_item. | Mad\Reconcile\MadReconcile |
mad-org-chart | Hierarquia auto-referente (FK parent-field) como arvore grafica: pan, zoom, collapse, busca, lazy-load e drag re-parent. | Mad\OrgChart\MadOrgChart |
mad-pdv | Frente de caixa (POS): scan bar, carrinho, formas de pagamento com troco/parcelamento, vendas em espera e cupom nao-fiscal. | Mad\Pdv\MadPdvComponent |
<mad-sheet model="StockCount" database="business" rows-min="10" max-rows="2000">
<mad-sheet-col field="produto_id" type="combo" source="Produto" source-label="nome"
label="Produto" required width="220px" />
<mad-sheet-col field="quantidade" type="number" label="Qtd" total="sum" decimals="0" required />
<mad-sheet-col field="valor" type="money" label="Valor unit." total="sum" width="140px" />
<mad-sheet-col field="data" type="date" label="Data" :default="date('Y-m-d')" width="150px" />
</mad-sheet>
<mad-reconcile left-model="BankStatementLine" right-model="LedgerEntry"
database="business" match-on="amount,date:3,document"
left-amount="valor" right-amount="valor"
left-date="data" right-date="data_lancamento"
left-doc="documento" right-doc="numero_doc"
left-label="descricao" right-label="historico"
left-title="Extrato bancario" right-title="Lancamentos"
tolerance="0.01" max-rows="500" context="banco-conta1" />
<mad-org-chart model="Employee" parent-field="manager_id"
title="nome" subtitle="cargo" avatar-field="foto"
metric="equipe_count" metric-label="equipe"
order-field="nome" lazy-depth="2" max-nodes="1000"
click-target="EmployeeForm::onShow({id})" />
<mad-pdv model="Produto" database="business"
name-field="nome" price-field="preco" code-field="codigo" barcode-field="ean"
sale-model="Venda" item-model="VendaItem" payment-model="VendaPagamento"
customer-model="Cliente" customer-display="nome"
currency-symbol="R$ " hold-sales ask-document>
<mad-pdv-column field="{categoria->nome}" label="Categoria" width="140px" />
<mad-pdv-column mode="input" item-field="vendedor" label="Vendedor" required maxlength="40" />
<mad-pdv-payment method="dinheiro" label="Dinheiro" allow-change entra-no-caixa hotkey="F5" />
<mad-pdv-payment method="cartao_credito" label="Credito"
gera-titulo sacado="adquirente" max-parcelas="12" />
<mad-pdv-action label="Abrir gaveta" icon="archive" method="onOpenDrawer" />
</mad-pdv>
mad-* nao aceita {{ }}
Os compilers desses quatro componentes rodam antes do Blade, sobre o
texto cru. Expressao PHP entra com o prefixo dois-pontos —
:rows-min="$that->rowsMin()", :default="date('Y-m-d')",
:where="$filtro" — nunca com chaves duplas. O mesmo vale para
condicional Blade dentro do bloco: @if em volta de
<mad-pdv-payment> some no render, porque a sub-tag e extraida por
expressao regular antes da condicional existir.
7. Toast, modal, drawer, alert, badge
<mad-form-grid :cols="3">
<mad-btn variant="success" mad:click="onSucesso">Toast Success</mad-btn>
<mad-btn variant="danger" mad:click="onErro">Toast Error</mad-btn>
<mad-btn variant="primary" open-modal="confirmar">Abrir Modal</mad-btn>
<mad-btn variant="primary" open-drawer="form">Abrir Drawer</mad-btn>
</mad-form-grid>
<mad-modal name="confirmar" title="Confirmar?" size="sm">
Tem certeza que deseja prosseguir?
</mad-modal>
<mad-drawer name="form" title="Editar" size="md">
<mad-form submit="onSave">
<mad-input-field name="nome" label="Nome" />
<mad-form-actions>
<mad-btn type="submit" variant="primary">Salvar</mad-btn>
</mad-form-actions>
</mad-form>
</mad-drawer>
<mad-alert type="info" title="Atenção">Mensagem informativa.</mad-alert>
<mad-alert type="success">Operação bem-sucedida.</mad-alert>
<mad-alert type="warning">Verifique os dados.</mad-alert>
<mad-alert type="danger">Algo deu errado.</mad-alert>
<mad-badge variant="success">Ativo</mad-badge>
<mad-badge variant="danger" icon="x">Cancelado</mad-badge>
<mad-steps variant="dots" :current="2">
<mad-step icon="user" label="Dados" />
<mad-step icon="credit-card" label="Pagamento" />
<mad-step icon="check" label="Confirmação" />
</mad-steps>
public function onSucesso(): \Mad\Http\MadResponse
{
return \Mad\Ui\MadToast::success('Deu certo!');
}
public function onErro(): \Mad\Http\MadResponse
{
return \Mad\Ui\MadToast::danger('Falhou — tente de novo.');
}
8. Geração de PDF
Mad\Doc\MadDocPdf::fromView() renderiza uma view Blade via DOMPDF e retorna
os bytes do PDF — não um objeto com ->stream(). Como o
endpoint reativo (_mad-wire) só fala JSON, o download roda numa rota HTTP
comum, fora do ciclo do MadComponent:
// app/Http/Controllers/PedidoPdfController.php
namespace App\Http\Controllers;
use App\Models\PedidoVenda;
use Illuminate\Http\Response;
use Mad\Doc\MadDocPdf;
class PedidoPdfController extends Controller
{
public function show(int $id): Response
{
$pedido = PedidoVenda::with('itens')->findOrFail($id);
$pdf = MadDocPdf::fromView('pdf.pedido', ['pedido' => $pedido]);
return response($pdf, 200, [
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'inline; filename="pedido-' . $pedido->id . '.pdf"',
]);
}
}
// routes/modules/admin.php
Route::middleware(['mad.auth', 'mad.permission'])->group(function () {
Route::get('/pedidos/{id}/pdf', [\App\Http\Controllers\PedidoPdfController::class, 'show'])
->name('pedidos.pdf');
});
<mad-btn icon="file-text" href="@{{ route('pedidos.pdf', $pedido->id) }}" target="_blank">
Baixar PDF
</mad-btn>
Copie qualquer trecho desta página, cole numa view nova, ajuste o model e o método PHP, rode. Cada bloco funciona isolado, sem dependência entre si.