MAD Lab
/
Tutor > Listagens e tabelas > Ações de linha

Ações de linha

Listagens e tabelas

mad-act mad-action mad-actions mad-action-group mad-del mad-nav mad-seek
Navegação de exemplo
O <mad-nav> abaixo aponta para a página Texto (Formulários) só pra ilustrar a navegação — o Tutor não tem uma tela de "detalhe do pedido" dedicada. Numa tela real o target seria o form/detalhe do próprio registro (ex.: PedidoForm::onEdit({id})).
Navegar (mad-nav)

mad-nav abre outra tela — nunca mad-act pra isso.

Visible columns
Cod.
Descrição
Cliente
1 Pedido #1
2 Pedido #2
3 Pedido #3
4 Pedido #4
5 Pedido #5
6 Pedido #6
7 Pedido #7
8 Pedido #8
resources/views/tutor/examples/grids-acoes/navegar.blade.php
<mad-grid model="TutorDemoPedido" per-page="8">
    <mad-columns>
        <mad-col field="id" label="Cod." width="70" sort />
        <mad-col field="descricao" label="Descrição" />
        <mad-col field="{cliente->nome}" label="Cliente" />
    </mad-columns>
    <mad-actions>
        <mad-nav icon="eye" label="Ver exemplo" target="TutorFormsTexto" />
    </mad-actions>
</mad-grid>
Ações completas

mad-act, mad-action (alias), mad-action-group, mad-del, when-field e display-condition no mesmo grid.

Visible columns
Cod.
Descrição
Cliente
Estado
Valor
150 Pedido #150 Carla Almeida Entregue R$ 4.095,50
149 Pedido #149 Patrícia Souza Enviado R$ 1.516,96
148 Pedido #148 Sabrina Almeida Cancelado R$ 10.132,22
147 Pedido #147 Karina Souza Entregue R$ 5.723,62
146 Pedido #146 Rafael Pereira Enviado R$ 7.613,07
145 Pedido #145 Patrícia Souza Novo R$ 9.130,34
144 Pedido #144 Lucas Pereira Entregue R$ 14.601,98
143 Pedido #143 Rafael Pereira Enviado R$ 4.745,48
resources/views/tutor/examples/grids-acoes/completas.blade.php
@php
    // O grid "self" desta pagina compila pra uma chamada em cima da variavel
    // LEXICAL $__component. Este partial e incluido de dentro do componente
    // Blade nativo tutor-demo (escopo isolado), entao $__component nao chega
    // ate aqui sozinho. MadRenderContext::getComponent() le o MESMO dado via
    // stack estatico (push/pop em MadComponent::render()), que atravessa
    // esse aninhamento. NUNCA escreva o texto literal da tag grid+self dentro
    // de um comentario neste arquivo: o compilador de tags do MAD e
    // regex-based e nao entende comentario PHP — ele casaria a abertura da
    // tag aqui no comentario com o FECHAMENTO real la embaixo, e tudo no meio
    // vira corpo do bloco errado (bug real, ja pego em produção neste lote).
    $__component = \Mad\Component\MadRenderContext::getComponent();
@endphp
<mad-grid self per-page="8">
    <mad-columns>
        <mad-col field="id" label="Cod." width="70" center sort />
        <mad-col field="descricao" label="Descrição" sort />
        <mad-col field="{cliente->nome}" label="Cliente" />
        <mad-col field="estado_id" label="Estado" width="130"
            badge="1:info:Novo|2:warning:Em separação|3:primary:Enviado|4:success:Entregue|5:danger:Cancelado" />
        <mad-col field="valor_total" label="Valor" width="130" right money="R$" />
    </mad-columns>
    <mad-actions>
        <mad-act method="onAprovar" icon="check" label="Aprovar" primary
            when-field="estado_id" when-in="1,2" />
        <mad-act method="onEntregar" icon="truck" label="Entregar" primary
            when-field="estado_id" when-value="3" />
        <mad-action-group icon="more-horizontal" label="Mais">
            <mad-act method="onCancelar" icon="ban" label="Cancelar" danger
                confirm="Cancelar este pedido?"
                when-field="estado_id" when-nin="4,5" />
            {{-- mad-action é alias de mad-act — mesmo compilador, mesmos atributos. --}}
            <mad-action method="onReabrir" icon="rotate-ccw" label="Reabrir"
                display-condition="TutorGridsAcoes::podeReabrir" />
        </mad-action-group>
        <mad-del confirm="Excluir este pedido (demonstração)?" />
    </mad-actions>
</mad-grid>
app/control/Tutor/TutorGridsAcoes.php
/** Novo(1)/Em separação(2) -> Enviado(3). */
public function onAprovar(int $id): MadResponse
{
    $pedido = TutorDemoPedido::findOrFail($id);
    $pedido->estado_id = 3;
    $pedido->save();

    return (new MadResponse())
        ->toast("Pedido #{$id} aprovado — agora está Enviado.", 'success')
        ->manageRow($id, static::class);
}
Buscar pedido (mad-seek)

Campo de busca modal com múltiplas colunas — seleciona o registro e dispara uma ação.

resources/views/tutor/examples/grids-acoes/buscar.blade.php
<mad-form submit="onBuscarPedido">
    <mad-seek name="pedidoBuscaId" model="TutorDemoPedido" display="descricao"
        label="Pedido" placeholder="Buscar por descrição..." modal-title="Selecionar pedido"
        per-page="8">
        <mad-column field="id" label="Cod." width="70" sort />
        <mad-column field="descricao" label="Descrição" sort filter />
        <mad-column field="valor_total" label="Valor" width="120" right money="R$" />
    </mad-seek>

    <mad-btn type="submit" variant="primary" icon="search">Buscar</mad-btn>
</mad-form>
app/control/Tutor/TutorGridsAcoes.php
public function onBuscarPedido(): MadResponse
{
    if ($this->pedidoBuscaId === '') {
        return (new MadResponse())->toast('Selecione um pedido na busca.', 'warning');
    }

    $pedido = TutorDemoPedido::with(['cliente', 'estado'])->find($this->pedidoBuscaId);

    if (!$pedido) {
        return (new MadResponse())->toast('Pedido não encontrado.', 'danger');
    }

    $cliente = $pedido->cliente->nome ?? '—';
    $estado  = $pedido->estado->nome ?? '—';

    return (new MadResponse())->toast(
        "Pedido #{$pedido->id} — {$pedido->descricao} — cliente {$cliente} — estado {$estado}.",
        'success'
    );
}
Código-fonte completo da página
app/control/Tutor/TutorGridsAcoes.php
<?php

namespace App\Control\Tutor;

use App\Models\TutorDemo\TutorDemoPedido;
use App\Support\Tutor\TutorDemoSeed;
use Mad\Grid\MadDataGrid;
use Mad\Http\MadResponse;

/**
 * TutorGridsAcoes — ações de linha: mad-act, mad-action (alias), mad-nav,
 * mad-del, mad-action-group, mad-seek.
 *
 * NÃO estende TutorPage: <mad-grid self> exige que o HOST seja literalmente
 * uma subclasse de MadDataGrid (colunas/ações leem $model/$with/métodos
 * DESTA classe) — TutorPage estende MadComponent puro, sem esse contrato.
 * view() abaixo aponta pro MESMO caminho que o TutorCatalog usaria
 * (tutor.grids.acoes) — TutorCatalog/TutorPage.php não são tocados.
 */
class TutorGridsAcoes extends MadDataGrid
{
    protected static string $wrapper = self::INTERNAL;

    protected string $model       = TutorDemoPedido::class;
    protected array  $with        = ['cliente', 'estado'];
    protected string $defaultSort = 'id DESC';
    protected string $actionSide  = 'left';

    /** Campo do <mad-seek> do exemplo "Buscar pedido". */
    public string $pedidoBuscaId = '';

    public function mount(array $params = []): void
    {
        TutorDemoSeed::ensure();
        parent::mount($params);
    }

    // ── Ações de linha ──────────────────────────────────────────────────

    /** Novo(1)/Em separação(2) -> Enviado(3). */
    public function onAprovar(int $id): MadResponse
    {
        $pedido = TutorDemoPedido::findOrFail($id);
        $pedido->estado_id = 3;
        $pedido->save();

        return (new MadResponse())
            ->toast("Pedido #{$id} aprovado — agora está Enviado.", 'success')
            ->manageRow($id, static::class);
    }

    /** Enviado(3) -> Entregue(4). */
    public function onEntregar(int $id): MadResponse
    {
        $pedido = TutorDemoPedido::findOrFail($id);
        $pedido->estado_id = 4;
        $pedido->save();

        return (new MadResponse())
            ->toast("Pedido #{$id} marcado como Entregue.", 'success')
            ->manageRow($id, static::class);
    }

    /** Qualquer estado exceto Entregue(4)/Cancelado(5) -> Cancelado(5). */
    public function onCancelar(int $id): MadResponse
    {
        $pedido = TutorDemoPedido::findOrFail($id);
        $pedido->estado_id = 5;
        $pedido->save();

        return (new MadResponse())
            ->toast("Pedido #{$id} cancelado.", 'warning')
            ->manageRow($id, static::class);
    }

    /** Cancelado(5) -> Novo(1) — reabre o pedido (via a tag <mad-action>, alias de mad-act). */
    public function onReabrir(int $id): MadResponse
    {
        $pedido = TutorDemoPedido::findOrFail($id);
        $pedido->estado_id = 1;
        $pedido->save();

        return (new MadResponse())
            ->toast("Pedido #{$id} reaberto — voltou para Novo.", 'info')
            ->manageRow($id, static::class);
    }

    /**
     * <mad-del> exige onMadGridDelete(int $id) na subclasse (o built-in só
     * existe na MadGrid zero-PHP). Aqui a exclusão é SIMULADA de propósito:
     * os 150 pedidos da fixture são compartilhados por TODOS os visitantes
     * do Tutor (TutorDemoSeed::ensure() só re-semeia quando a tabela fica
     * vazia) — apagar de verdade a cada clique de demonstração esgotaria o
     * dataset com o tempo. removeRow() tira a linha da TELA sem tocar o
     * banco; um novo carregamento da página mostra o pedido de novo.
     */
    public function onMadGridDelete(int $id): MadResponse
    {
        return (new MadResponse())
            ->toast("Exclusão simulada — o pedido #{$id} não foi removido do banco (dado de demonstração compartilhado).", 'info')
            ->removeRow($id, static::class);
    }

    /** Display-condition de AÇÃO: recebe array (não stdClass — isso é regra de coluna). */
    public static function podeReabrir(array $row): bool
    {
        return (int) ($row['estado_id'] ?? 0) === 5;
    }

    // ── mad-seek: localizar um pedido pela descrição ─────────────────────

    public function onBuscarPedido(): MadResponse
    {
        if ($this->pedidoBuscaId === '') {
            return (new MadResponse())->toast('Selecione um pedido na busca.', 'warning');
        }

        $pedido = TutorDemoPedido::with(['cliente', 'estado'])->find($this->pedidoBuscaId);

        if (!$pedido) {
            return (new MadResponse())->toast('Pedido não encontrado.', 'danger');
        }

        $cliente = $pedido->cliente->nome ?? '—';
        $estado  = $pedido->estado->nome ?? '—';

        return (new MadResponse())->toast(
            "Pedido #{$pedido->id} — {$pedido->descricao} — cliente {$cliente} — estado {$estado}.",
            'success'
        );
    }

    protected function view(): string|array
    {
        return 'tutor.grids.acoes';
    }
}
resources/views/tutor/grids/acoes.blade.php
<x-tutor-page page="grids_acoes">
    <mad-callout type="info" title="Navegação de exemplo" icon="info">
        O <code>&lt;mad-nav&gt;</code> abaixo aponta para a página <strong>Texto</strong>
        (Formulários) só pra ilustrar a navegação — o Tutor não tem uma tela de
        "detalhe do pedido" dedicada. Numa tela real o target seria o
        form/detalhe do próprio registro (ex.: <code>PedidoForm::onEdit({id})</code>).
    </mad-callout>

    <x-tutor-demo demo="tutor.examples.grids-acoes.navegar"
        title="Navegar (mad-nav)" desc="mad-nav abre outra tela — nunca mad-act pra isso." />

    <x-tutor-demo demo="tutor.examples.grids-acoes.completas"
        title="Ações completas" desc="mad-act, mad-action (alias), mad-action-group, mad-del, when-field e display-condition no mesmo grid."
        php="TutorGridsAcoes::onAprovar" />

    <x-tutor-demo demo="tutor.examples.grids-acoes.buscar"
        title="Buscar pedido (mad-seek)" desc="Campo de busca modal com múltiplas colunas — seleciona o registro e dispara uma ação."
        php="TutorGridsAcoes::onBuscarPedido" />
</x-tutor-page>