Como Criar um Painel de Monitoramento WooCommerce Personalizado no WordPress

Otimize sua Loja WooCommerce com um Painel de Monitoramento Personalizado

Se você gerencia uma loja WooCommerce, sabe como é importante ter uma visão rápida e organizada de todos os pedidos e produtos. O snippet de código que compartilhamos hoje cria um painel de monitoramento completo diretamente no admin do WordPress, oferecendo funcionalidades avançadas que vão além do padrão do WooCommerce.

O Que Este Painel Oferece:

📊 Visualização em Abas Intuitiva

  • Aba de Produtos: Lista os 10 produtos mais recentes com ID, nome, preço, status de estoque e ações rápidas
  • Aba de Pedidos: Exibe todos os pedidos do sistema com filtros por status

🎯 Funcionalidades Avançadas

  • Sistema de filtros por status de pedidos (processando, concluído, cancelado, etc.)
  • Badges coloridos para cada status para identificação visual rápida
  • Modal pop-up com resumo detalhado de produtos e pedidos
  • Design responsivo e profissional integrado ao admin do WordPress

Principais Benefícios:

  1. Economia de Tempo: Acesso rápido a todas as informações sem navegar por múltiplas páginas
  2. Tomada de Decisão: Visão geral completa do desempenho da loja
  3. Experiência do Usuário: Interface limpa e organizada com abas e modais
  4. Personalizável: Código aberto para adaptações específicas do seu negócio

Tecnologias Utilizadas:

  • WordPress Hooks (admin_menuwp_ajax_)
  • WC_Order_Query para consultas otimizadas
  • AJAX para carregamento assíncrono
  • CSS customizado para interface profissional
  • Nonce WordPress para segurança

Este painel é ideal para donos de e-commerce, desenvolvedores WordPress e gerentes de loja que precisam de uma solução robusta de monitoramento. O código está pronto para implementação e pode ser facilmente customizado para atender necessidades específicas.

Dica Profissional: Você pode expandir este painel adicionando métricas de vendas, gráficos de performance ou integrações com sistemas de ERP.

/**
 * Adiciona a página de monitoramento de WooCommerce ao painel.
 */
function adicionar_pagina_monitoramento_woocommerce() {
    add_menu_page(
        'Monitoramento WooCommerce',           // Título da página
        'Monitoramento WC',                    // Título do menu
        'manage_options',                      // Capacidade necessária
        'monitoramento-woocommerce',           // Slug
        'conteudo_pagina_monitoramento',       // Função de conteúdo
        'dashicons-chart-bar',                 // Ícone
        6                                      // Posição no menu
    );
}
add_action('admin_menu', 'adicionar_pagina_monitoramento_woocommerce');

/**
 * Renderiza o conteúdo da página.
 */
function conteudo_pagina_monitoramento() {
    // Pega o status do URL, se existir.
    $filtro_status = isset($_GET['status']) ? sanitize_text_field($_GET['status']) : '';
    ?>
    <style>
        .wc-monitor-wrap { padding: 20px; background-color: #f0f2f5; }
        .wc-monitor-wrap h1 { border-bottom: 2px solid #ddd; padding-bottom: 10px; margin-bottom: 20px; }
        .wc-monitor-table { background: #fff; border-radius: 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); padding: 10px; overflow: hidden; }
        .wc-monitor-table table { width: 100%; border-collapse: collapse; }
        .wc-monitor-table th, .wc-monitor-table td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; }
        .wc-monitor-table th { background-color: #f5f5f5; font-weight: bold; }
        .wc-monitor-table tr:last-child td { border-bottom: none; }
        .wc-status-badge { display: inline-block; padding: 4px 8px; border-radius: 4px; color: #fff; font-weight: bold; font-size: 0.8em; }
        .status-on-hold { background-color: #ffb838; }
        .status-processing { background-color: #0073aa; }
        .status-completed { background-color: #7ad03a; }
        .status-cancelled { background-color: #dc3232; }
        .status-failed { background-color: #dc3232; }
        .status-refunded { background-color: #ff9800; }
        .status-pending-payment { background-color: #e5b016; }
        .status-draft { background-color: #a0a5aa; }
        .order-filter-form { margin-bottom: 20px; display: flex; gap: 10px; align-items: center; }
        .filter-info { font-style: italic; color: #555; margin-top: -10px; margin-bottom: 20px; }
        
        /* Estilos das Abas */
        .tabs-nav { margin: 0; padding: 0; list-style: none; border-bottom: 2px solid #ddd; }
        .tabs-nav li { display: inline-block; margin-right: 5px; }
        .tabs-nav a {
            display: block;
            padding: 10px 15px;
            background: #e9e9e9;
            color: #555;
            text-decoration: none;
            border-radius: 5px 5px 0 0;
            border: 1px solid #ddd;
            border-bottom: none;
        }
        .tabs-nav a.active {
            background: #fff;
            color: #000;
            border-color: #ddd;
            position: relative;
            bottom: -2px;
            z-index: 2;
        }
        .tab-content { padding: 20px; background: #fff; border: 1px solid #ddd; border-top: none; position: relative; z-index: 1; }
        .tab-pane { display: none; }
        .tab-pane.active { display: block; }

        /* Estilos do Pop-up (Modal) */
        .modal-overlay {
            display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.6);
            z-index: 1000; justify-content: center; align-items: center;
        }
        .modal-content {
            background: #fff; padding: 25px; border-radius: 8px; box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
            width: 90%; max-width: 600px; position: relative; max-height: 80vh; overflow-y: auto;
        }
        .modal-close-btn {
            position: absolute; top: 10px; right: 15px; background: none; border: none; font-size: 1.5em; cursor: pointer; color: #aaa;
        }
        .modal-content h3 { border-bottom: 1px solid #eee; padding-bottom: 10px; margin-top: 0; }
        .modal-body p { margin-bottom: 10px; line-height: 1.5; }
        .modal-body strong { color: #333; }
    </style>

    <div class="wc-monitor-wrap">
        <h1>Monitoramento Geral do WooCommerce</h1>
        <p>Visão rápida dos produtos e de todos os pedidos do sistema.</p>
        <hr>

        <ul class="tabs-nav">
            <li><a href="#tab-produtos" class="active">Produtos</a></li>
            <li><a href="#tab-pedidos">Pedidos</a></li>
        </ul>

        <div class="tab-content">
            <div id="tab-produtos" class="tab-pane active">
                <h2>Produtos Recentes</h2>
                <div class="wc-monitor-table">
                    <table>
                        <thead>
                            <tr>
                                <th>ID</th>
                                <th>Nome</th>
                                <th>Preço</th>
                                <th>Estoque</th>
                                <th>Ações</th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php
                            $args_produtos = array(
                                'post_type'      => 'product',
                                'posts_per_page' => 10,
                                'orderby'        => 'date',
                                'order'          => 'DESC',
                            );
                            $loop_produtos = new WP_Query($args_produtos);
                            if ($loop_produtos->have_posts()) {
                                while ($loop_produtos->have_posts()) {
                                    $loop_produtos->the_post();
                                    $produto = wc_get_product(get_the_ID());
                                    ?>
                                    <tr>
                                        <td><?php echo esc_html($produto->get_id()); ?></td>
                                        <td><a href="<?php echo get_edit_post_link($produto->get_id()); ?>"><?php echo esc_html($produto->get_name()); ?></a></td>
                                        <td><?php echo wc_price($produto->get_price()); ?></td>
                                        <td><?php echo $produto->get_stock_status() === 'instock' ? 'Em Estoque' : 'Fora de Estoque'; ?></td>
                                        <td><button class="button button-secondary ver-resumo-item" data-id="<?php echo esc_attr($produto->get_id()); ?>" data-tipo="produto">Ver Resumo</button></td>
                                    </tr>
                                    <?php
                                }
                            } else {
                                echo '<tr><td colspan="5">Nenhum produto encontrado.</td></tr>';
                            }
                            wp_reset_postdata();
                            ?>
                        </tbody>
                    </table>
                </div>
                <p><a href="<?php echo admin_url('edit.php?post_type=product'); ?>">Ver todos os produtos</a></p>
            </div>

            <div id="tab-pedidos" class="tab-pane">
                <h2>Todos os Pedidos do Sistema</h2>
                
                <form method="get" class="order-filter-form">
                    <input type="hidden" name="page" value="monitoramento-woocommerce" />
                    <label for="order-status-filter">Filtrar por Status:</label>
                    <select name="status" id="order-status-filter">
                        <option value="">Todos os Status</option>
                        <?php
                        $statuses = wc_get_order_statuses();
                        foreach ($statuses as $key => $name) {
                            $status_slug = str_replace('wc-', '', $key);
                            echo '<option value="' . esc_attr($status_slug) . '" ' . selected($filtro_status, $status_slug, false) . '>' . esc_html($name) . '</option>';
                        }
                        ?>
                    </select>
                    <button type="submit" class="button">Filtrar</button>
                </form>

                <?php
                if (!empty($filtro_status)) {
                    echo '<p class="filter-info">Mostrando pedidos com o status: <strong>' . esc_html(wc_get_order_status_name('wc-' . $filtro_status)) . '</strong>.</p>';
                }
                ?>

                <div class="wc-monitor-table">
                    <table>
                        <thead>
                            <tr>
                                <th>ID do Pedido</th>
                                <th>Cliente</th>
                                <th>Total</th>
                                <th>Status</th>
                                <th>Data</th>
                                <th>Ações</th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php
                            $args_pedidos = array(
                                'limit'   => -1,
                                'orderby' => 'date',
                                'order'   => 'DESC',
                                'status'  => 'any',
                            );

                            if (!empty($filtro_status)) {
                                $args_pedidos['status'] = $filtro_status;
                            }

                            $orders_query = new WC_Order_Query($args_pedidos);
                            $pedidos = $orders_query->get_orders();

                            if (!empty($pedidos)) {
                                foreach ($pedidos as $pedido) {
                                    $status_class = 'status-' . str_replace('wc-', '', $pedido->get_status());
                                    ?>
                                    <tr>
                                        <td><a href="<?php echo get_edit_post_link($pedido->get_id()); ?>">#<?php echo esc_html($pedido->get_order_number()); ?></a></td>
                                        <td><?php echo esc_html($pedido->get_billing_first_name() . ' ' . $pedido->get_billing_last_name()); ?></td>
                                        <td><?php echo wc_price($pedido->get_total()); ?></td>
                                        <td><span class="wc-status-badge <?php echo esc_attr($status_class); ?>"><?php echo wc_get_order_status_name($pedido->get_status()); ?></span></td>
                                        <td><?php echo esc_html(wc_format_datetime($pedido->get_date_created())); ?></td>
                                        <td><button class="button button-secondary ver-resumo-item" data-id="<?php echo esc_attr($pedido->get_id()); ?>" data-tipo="pedido">Ver Resumo</button></td>
                                    </tr>
                                    <?php
                                }
                            } else {
                                $message = 'Nenhum pedido encontrado com este status.';
                                if (empty($filtro_status)) {
                                    $message = 'Nenhum pedido encontrado no sistema.';
                                }
                                echo '<tr><td colspan="6">' . esc_html($message) . '</td></tr>';
                            }
                            ?>
                        </tbody>
                    </table>
                </div>
                <p><a href="<?php echo admin_url('edit.php?post_type=shop_order'); ?>">Ver todos os pedidos</a></p>
            </div>
        </div>
    </div>

    <div id="resumo-modal" class="modal-overlay">
        <div class="modal-content">
            <button class="modal-close-btn">&times;</button>
            <h3 id="modal-titulo">Carregando...</h3>
            <div id="modal-body" class="modal-body">
                <p>Aguarde...</p>
            </div>
        </div>
    </div>

    <script>
        jQuery(document).ready(function($) {
            // Lógica para as abas
            $('.tabs-nav a').on('click', function(e) {
                e.preventDefault();
                const targetTab = $(this).attr('href');

                $('.tabs-nav a').removeClass('active');
                $(this).addClass('active');

                $('.tab-pane').removeClass('active');
                $(targetTab).addClass('active');
            });

            // Adiciona o token de segurança (nonce) para o pop-up
            const nonce = '<?php echo wp_create_nonce("resumo-item-nonce"); ?>';

            // Abre o modal ao clicar no botão
            $('.ver-resumo-item').on('click', function() {
                const itemId = $(this).data('id');
                const itemTipo = $(this).data('tipo');
                const modal = $('#resumo-modal');
                const modalTitulo = $('#modal-titulo');
                const modalBody = $('#modal-body');

                // Mostra o modal e o carregamento
                modal.css('display', 'flex');
                modalTitulo.text('Carregando Resumo...');
                modalBody.html('<p>Aguarde enquanto as informações são carregadas...</p>');

                // Chamada AJAX para buscar o resumo
                $.ajax({
                    url: '<?php echo admin_url("admin-ajax.php"); ?>',
                    type: 'POST',
                    data: {
                        action: 'buscar_resumo_item',
                        nonce: nonce,
                        id: itemId,
                        tipo: itemTipo
                    },
                    success: function(response) {
                        if (response.success) {
                            modalTitulo.text(response.data.titulo);
                            modalBody.html(response.data.conteudo);
                        } else {
                            modalTitulo.text('Erro');
                            modalBody.html('<p>Não foi possível carregar o resumo: ' + response.data + '</p>');
                        }
                    },
                    error: function() {
                        modalTitulo.text('Erro de Conexão');
                        modalBody.html('<p>Ocorreu um erro ao se comunicar com o servidor.</p>');
                    }
                });
            });

            // Fecha o modal ao clicar no botão de fechar ou no overlay
            $('.modal-close-btn, .modal-overlay').on('click', function(e) {
                if (e.target === this) {
                    $('#resumo-modal').hide();
                }
            });
        });
    </script>
    <?php
}

/**
 * Funções de callback AJAX para buscar o resumo do item.
 */
add_action('wp_ajax_buscar_resumo_item', 'buscar_resumo_item_callback');
function buscar_resumo_item_callback() {
    if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'resumo-item-nonce')) {
        wp_send_json_error('Nonce de segurança inválido.');
    }

    $item_id = isset($_POST['id']) ? intval($_POST['id']) : 0;
    $item_tipo = isset($_POST['tipo']) ? sanitize_text_field($_POST['tipo']) : '';

    if (!$item_id || !in_array($item_tipo, ['produto', 'pedido'])) {
        wp_send_json_error('Dados inválidos.');
    }

    $titulo = '';
    $conteudo = '';

    if ($item_tipo === 'produto') {
        $produto = wc_get_product($item_id);
        if ($produto) {
            $titulo = 'Resumo do Produto: ' . $produto->get_name();
            $conteudo .= '<p><strong>Preço:</strong> ' . wc_price($produto->get_price()) . '</p>';
            $conteudo .= '<p><strong>Estoque:</strong> ' . ($produto->get_stock_status() === 'instock' ? 'Em Estoque' : 'Fora de Estoque') . '</p>';
            $conteudo .= '<p><strong>SKU:</strong> ' . esc_html($produto->get_sku()) . '</p>';
            $conteudo .= '<p><strong>Descrição:</strong> ' . wp_kses_post(wpautop($produto->get_short_description())) . '</p>';
        } else {
            wp_send_json_error('Produto não encontrado.');
        }
    } elseif ($item_tipo === 'pedido') {
        $pedido = wc_get_order($item_id);
        if ($pedido) {
            $titulo = 'Resumo do Pedido: #' . $pedido->get_order_number();
            $conteudo .= '<p><strong>Status:</strong> <span class="wc-status-badge status-' . str_replace('wc-', '', $pedido->get_status()) . '">' . wc_get_order_status_name($pedido->get_status()) . '</span></p>';
            $conteudo .= '<p><strong>Cliente:</strong> ' . esc_html($pedido->get_formatted_billing_full_name()) . '</p>';
            $conteudo .= '<p><strong>Data:</strong> ' . wc_format_datetime($pedido->get_date_created()) . '</p>';
            $conteudo .= '<p><strong>Total:</strong> ' . wc_price($pedido->get_total()) . '</p>';
            $conteudo .= '<h4>Itens do Pedido:</h4><ul>';
            foreach ($pedido->get_items() as $item) {
                $conteudo .= '<li>' . esc_html($item->get_name()) . ' x ' . esc_html($item->get_quantity()) . ' (' . wc_price($item->get_total()) . ')</li>';
            }
            $conteudo .= '</ul>';
        } else {
            wp_send_json_error('Pedido não encontrado.');
        }
    }

    wp_send_json_success([
        'titulo' => $titulo,
        'conteudo' => $conteudo,
    ]);
}
Voltar ao Blog