Docs›Componentes (Admin)›mad-org-chart
Componentes (Admin)

mad-org-chart

Organograma de hierarquia auto-referente com pan/zoom, collapse, busca, lazy-load e drag re-parent.

Organograma: renderiza uma hierarquia auto-referente (FK do tipo parent_id) como arvore de cards com pan, zoom, collapse, busca, lazy-load por nivel e drag para re-parent. Uma tag declarativa, com filho opcional para o template do card.

Compilador: Mad\OrgChart\MadOrgChartCompiler (passo 1.10 do MadBlade). Componente: Mad\OrgChart\MadOrgChart. Sem host proprio, o compiler instancia um Mad\OrgChart\MadOrgChartStandalone. Client: Alpine madOrgChart (mad-orgchart.js + mad-orgchart.css).

Tag

<mad-org-chart model="Employee" database="business"
               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::onEdit({id})"
               where="ativo=1" draggable />

Props de <mad-org-chart>

Prop Tipo Default Descricao
model string — Model dos nos. Obrigatorio
database string MAIN_DATABASE Conexao
parent-field string parent_id FK auto-referente
title string nome Campo do titulo do card
subtitle string '' Campo do subtitulo
avatar-field string '' Campo da foto. Sem ele o card usa as iniciais
metric string '' Campo numerico exibido no badge
metric-label string '' Rotulo da metrica
order-field string '' Ordenacao dos irmaos
draggable bool false Habilita drag para re-parent
lazy-depth int 0 Niveis renderizados de inicio (0 = arvore toda)
max-nodes int 1000 Guard de nos por request
root-id int 0 Restringe a arvore a um no raiz
click-target string '' Abre um form ao clicar no card, ex. EmployeeForm::onEdit({id})
where string '' Filtro DSL (ativo=1|tipo=P) ou :where="$closure"

<mad-org-chart-card>

Filho opcional com o template Blade do card. O corpo e arbitrario e recebe $item, $id e $chart no escopo.

<mad-org-chart model="Employee" title="nome" subtitle="cargo">
    <mad-org-chart-card>
        <strong>{!! e($item['nome']) !!}</strong>
        <small>#{!! $id !!}</small>
    </mad-org-chart-card>
</mad-org-chart>

Avulso ou pagina propria

A tag funciona embutida em qualquer blade (varias na mesma pagina, inclusive). O que muda e a disponibilidade das acoes wire:

Cenario Funciona avulso?
Arvore completa sem drag (pan, zoom, collapse, busca, click-target — tudo client-side) Sim, em qualquer pagina/host
lazy-depth > 0 (expandir busca filhos via onLoadChildren) Nao: o host da pagina precisa ser subclasse de MadOrgChart
draggable (re-parent via onReparent) Nao: idem

Regra pratica: visualizacao readonly que cabe no max-nodes vira widget embutido; ligou lazy ou drag (ou a hierarquia estoura o max-nodes) entao gere subclasse e pagina propria — mesma regra do kanban e do gantt embutidos.

Comportamento no client

  • Pan arrastando o fundo do viewport; zoom por scroll ancorado no cursor (30% a 200%) e botoes -/+/Resetar na toolbar.
  • Collapse pelo badge de contagem na base do card com filhos.
  • Lazy-load: no alem do lazy-depth mostra o badge com a contagem pendente; o clique busca os filhos no servidor (onLoadChildren, op html) e o evento mad-orgchart:children limpa o spinner.
  • Busca filtra por titulo e subtitulo, destaca os hits, expande ancestrais colapsados e centraliza o primeiro.
  • Drag re-parent (com draggable): o servidor valida e a arvore re-renderiza inteira.
  • Titulo e subtitulo saem escapados.

A carga e BFS por nivel: uma query por profundidade (whereIn nos pais) mais uma query agregada de contagem de filhos — sem N+1 por no.

Subclasse (hooks)

use Mad\OrgChart\MadOrgChart;

class OrganogramaRh extends MadOrgChart
{
    protected static string $wrapper = self::INTERNAL;

    protected string $model         = 'Employee';
    protected string $database      = 'business';
    protected string $parentField   = 'manager_id';
    protected string $titleField    = 'nome';
    protected string $subtitleField = 'cargo';
    protected bool   $draggable     = true;
    protected int    $lazyDepth     = 2;

    /** Escopo aplicado em TODO acesso a no (anti-IDOR). */
    protected function query(\Illuminate\Database\Eloquent\Builder $q): void
    {
        $q->where('unit_id', MadSession::unitId())->where('ativo', 1);
    }

    protected function canReparent(object $node, ?object $newParent): bool
    {
        return $this->draggable;
    }

    protected function afterReparent(object $node, int $oldParentId, int $newParentId): void
    {
    }
}

Gotchas

  • Sem query($q) o organograma enxerga todo mundo: e ele que fecha o IDOR do onLoadChildren e do onReparent.
  • onReparent valida em camadas: draggable ligado, depois canReparent(), depois cycle-check (sobe do novo pai ate a raiz; se passar pelo no movido, recusa "destino e descendente"), so entao persiste e forca full render.
  • parent-field e order-field sao validados como identificadores SQL.
  • Estourar max-nodes trunca a carga (isTruncated()); nesse caso use lazy-depth ou root-id em vez de aumentar o guard sem criterio.
  • Atributo mad-* nao aceita {{ }} — expressao PHP entra com dois-pontos (:where="$filtro").