MAD Lab
/
Tutor > Módulos visuais > Organograma

Organograma

Módulos visuais

mad-org-chart mad-org-chart-node mad-org-chart-card
Árvore básica

mad-org-chart com título, subtítulo e métrica — zero PHP (fixture TutorDemoOrgFuncionario, conexão business).

/
  • AD
    Ana Diretora
    CEO
    3
    • BG
      Bruno Gerente
      Gerente Comercial
      2
      • EV
        Elisa Vendas
        Vendedora
        0
      • FV
        Fabio Vendas
        Vendedor
        0
    • CG
      Carla Gerente
      Gerente Financeiro
      2
      • GC
        Gina Contas
        Analista Financeiro
        0
      • HC
        Hugo Contas
        Analista Financeiro
        0
    • DG
      Diego Gerente
      Gerente de TI
      3
      • ID
        Iris Dev
        Dev Pleno
        0
      • JD
        Joao Dev
        Dev Júnior
        0
      • KS
        Karina Suporte
        Suporte técnico
        0
resources/views/tutor/examples/modulos-org-chart/basico.blade.php
<mad-org-chart model="TutorDemoOrgFuncionario" database="business"
    parent-field="chefe_id" title="nome" subtitle="cargo"
    metric="equipe" metric-label="pessoas na equipe" order-field="nome" />
Card customizado

mad-org-chart-card com Blade arbitrário por nó ($item, $id, $chart em escopo).

/
  • Ana Diretora
    CEO
    3 na equipe
    • Bruno Gerente
      Gerente Comercial
      2 na equipe
      • Elisa Vendas
        Vendedora
      • Fabio Vendas
        Vendedor
    • Carla Gerente
      Gerente Financeiro
      2 na equipe
      • Gina Contas
        Analista Financeiro
      • Hugo Contas
        Analista Financeiro
    • Diego Gerente
      Gerente de TI
      3 na equipe
      • Iris Dev
        Dev Pleno
      • Joao Dev
        Dev Júnior
      • Karina Suporte
        Suporte técnico
resources/views/tutor/examples/modulos-org-chart/card-customizado.blade.php
<mad-org-chart model="TutorDemoOrgFuncionario" database="business"
    parent-field="chefe_id" order-field="nome">
    <mad-org-chart-card>
        <strong>{{ $item->nome }}</strong><br>
        <span style="color:var(--mad-text-muted); font-size:12px;">{{ $item->cargo }}</span>
        @if ($item->equipe > 0)
            <div style="margin-top:4px;">
                <span class="tutor-chip">{{ $item->equipe }} na equipe</span>
            </div>
        @endif
    </mad-org-chart-card>
</mad-org-chart>
Drag re-parent + lazy-load

draggable e lazy-depth exigem o HOST da página (TutorModulosOrgChart::afterReparent recalcula a métrica dos dois chefes envolvidos).

/
  • AD
    Ana Diretora
    CEO
    3
resources/views/tutor/examples/modulos-org-chart/drag-lazy.blade.php
@php
    // Partial de <x-tutor-demo> = escopo Blade ISOLADO (component nativo) —
    // `$that` (host que os compiladores dos widgets grandes leem via
    // `$that ?? null`) não atravessa esse aninhamento sozinho. Reabre aqui
    // porque draggable/lazy-depth só funcionam contra o HOST de verdade
    // (TutorModulosOrgChart, subclasse de MadOrgChart — ver comentário da
    // classe): sem isto, a tag cairia num MadOrgChartStandalone novo a cada
    // request e o drag/expand nunca chegaria em onReparent/onLoadChildren.
    $that = \Mad\Component\MadRenderContext::getComponent();
@endphp
{{-- Tag inline sem atributos reusa a config já declarada nas props
     protected da classe (model/parentField/draggable/lazyDepth=1). --}}
<mad-org-chart />
app/control/Tutor/TutorModulosOrgChart.php
/**
 * Após um re-parent (drag), o `equipe` (badge de métrica) dos dois chefes
 * envolvidos fica desatualizado — recalcula pela contagem real de
 * subordinados diretos. Contrato: OrgChartDevView não faz isso (não tinha
 * necessidade); aqui é o exemplo de uso do hook.
 */
protected function afterReparent(object $node, int $oldParentId, int $newParentId): void
{
    foreach (array_unique(array_filter([$oldParentId, $newParentId])) as $chefeId) {
        TutorDemoOrgFuncionario::query()->whereKey($chefeId)->update([
            'equipe' => TutorDemoOrgFuncionario::query()->where('chefe_id', $chefeId)->count(),
        ]);
    }
}
Código-fonte completo da página
app/control/Tutor/TutorModulosOrgChart.php
<?php

namespace App\Control\Tutor;

use App\Models\TutorDemo\TutorDemoOrgFuncionario;
use Illuminate\Support\Facades\Schema;
use Mad\OrgChart\MadOrgChart;

/**
 * TutorModulosOrgChart — página "Organograma" do MAD Lab (catálogo
 * modulos_org_chart). Cobre: mad-org-chart, mad-org-chart-card.
 *
 * `mad-org-chart-node` (tag do catálogo) NÃO existe como componente público —
 * é o NOME DO PARTIAL interno (components/org-chart-node.blade.php) que o
 * próprio `<mad-org-chart>` usa por baixo pra recursão dos nós; confirmado via
 * mad_props (mad-org-chart-node → "Componente não encontrado"). A tag
 * documentada pra customizar o card por nó é `<mad-org-chart-card>`, coberta
 * no demo "Card customizado" abaixo.
 *
 * ATENÇÃO (molde OrgChartDevView): draggable/lazy-depth só funcionam quando o
 * HOST da página é subclasse de MadOrgChart (onLoadChildren/onReparent são
 * métodos de instância) — por isso esta página estende MadOrgChart em vez de
 * TutorPage. `view()` é reimplementado manualmente (perdemos o catalogKey
 * automático de TutorPage, que não dá pra herdar junto — herança única).
 *
 * Só UM <mad-org-chart> pode ficar vinculado ao host por request: cada tag
 * `_renderInlineOrgChart()` MUTA $this (model/parentField/draggable/...) e só
 * a config do ÚLTIMO tag renderizado sobrevive pro round-trip do wire
 * (onReparent/onLoadChildren). Os dois demos menores abaixo são renderizados
 * como <mad-org-chart-card>/tag standalone (fora do host — ver partials, que
 * NÃO reabrem `$that`) e o demo grande (draggable+lazy) é o ÚLTIMO da página.
 */
class TutorModulosOrgChart extends MadOrgChart
{
    protected static string $wrapper = self::INTERNAL;

    protected string $model         = TutorDemoOrgFuncionario::class;
    protected string $database      = 'business';
    protected string $parentField   = 'chefe_id';
    protected string $titleField    = 'nome';
    protected string $subtitleField = 'cargo';
    protected string $metricField   = 'equipe';
    protected string $metricLabel   = 'pessoas na equipe';
    protected string $orderField    = 'nome';
    protected bool   $draggable     = true;
    protected int    $lazyDepth     = 1;

    public function mount(array $params = []): void
    {
        $this->ensureFixture();
        parent::mount($params);
    }

    /**
     * Após um re-parent (drag), o `equipe` (badge de métrica) dos dois chefes
     * envolvidos fica desatualizado — recalcula pela contagem real de
     * subordinados diretos. Contrato: OrgChartDevView não faz isso (não tinha
     * necessidade); aqui é o exemplo de uso do hook.
     */
    protected function afterReparent(object $node, int $oldParentId, int $newParentId): void
    {
        foreach (array_unique(array_filter([$oldParentId, $newParentId])) as $chefeId) {
            TutorDemoOrgFuncionario::query()->whereKey($chefeId)->update([
                'equipe' => TutorDemoOrgFuncionario::query()->where('chefe_id', $chefeId)->count(),
            ]);
        }
    }

    // ── Fixture ──────────────────────────────────────────────────────────

    private function ensureFixture(): void
    {
        $schema = Schema::connection('business');

        if (!$schema->hasTable('tutor_org_funcionarios')) {
            $schema->create('tutor_org_funcionarios', function ($t) {
                $t->increments('id');
                $t->string('nome');
                $t->string('cargo')->nullable();
                $t->integer('chefe_id')->nullable();
                $t->integer('equipe')->default(0);
            });
        }

        if (TutorDemoOrgFuncionario::query()->count() > 0) {
            return;
        }

        TutorDemoOrgFuncionario::query()->insert([
            ['id' => 1,  'nome' => 'Ana Diretora',    'cargo' => 'CEO',                  'chefe_id' => null, 'equipe' => 3],
            ['id' => 2,  'nome' => 'Bruno Gerente',   'cargo' => 'Gerente Comercial',    'chefe_id' => 1,    'equipe' => 2],
            ['id' => 3,  'nome' => 'Carla Gerente',   'cargo' => 'Gerente Financeiro',   'chefe_id' => 1,    'equipe' => 2],
            ['id' => 4,  'nome' => 'Diego Gerente',   'cargo' => 'Gerente de TI',        'chefe_id' => 1,    'equipe' => 3],
            ['id' => 5,  'nome' => 'Elisa Vendas',    'cargo' => 'Vendedora',            'chefe_id' => 2,    'equipe' => 0],
            ['id' => 6,  'nome' => 'Fabio Vendas',    'cargo' => 'Vendedor',             'chefe_id' => 2,    'equipe' => 0],
            ['id' => 7,  'nome' => 'Gina Contas',     'cargo' => 'Analista Financeiro',  'chefe_id' => 3,    'equipe' => 0],
            ['id' => 8,  'nome' => 'Hugo Contas',     'cargo' => 'Analista Financeiro',  'chefe_id' => 3,    'equipe' => 0],
            ['id' => 9,  'nome' => 'Iris Dev',        'cargo' => 'Dev Pleno',            'chefe_id' => 4,    'equipe' => 0],
            ['id' => 10, 'nome' => 'Joao Dev',        'cargo' => 'Dev Júnior',           'chefe_id' => 4,    'equipe' => 0],
            ['id' => 11, 'nome' => 'Karina Suporte',  'cargo' => 'Suporte técnico',      'chefe_id' => 4,    'equipe' => 0],
        ]);
    }

    protected function view(): string|array
    {
        return ['tutor.modulos.org-chart', []];
    }
}
resources/views/tutor/modulos/org-chart.blade.php
<x-tutor-page page="modulos_org_chart">
    <x-tutor-demo demo="tutor.examples.modulos-org-chart.basico"
        title="Árvore básica" desc="mad-org-chart com título, subtítulo e métrica — zero PHP (fixture TutorDemoOrgFuncionario, conexão business)." />

    <x-tutor-demo demo="tutor.examples.modulos-org-chart.card-customizado"
        title="Card customizado" desc="mad-org-chart-card com Blade arbitrário por nó ($item, $id, $chart em escopo)." />

    <x-tutor-demo demo="tutor.examples.modulos-org-chart.drag-lazy"
        title="Drag re-parent + lazy-load" desc="draggable e lazy-depth exigem o HOST da página (TutorModulosOrgChart::afterReparent recalcula a métrica dos dois chefes envolvidos)."
        php="TutorModulosOrgChart::afterReparent" />
</x-tutor-page>