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:
- Economia de Tempo: Acesso rápido a todas as informações sem navegar por múltiplas páginas
- Tomada de Decisão: Visão geral completa do desempenho da loja
- Experiência do Usuário: Interface limpa e organizada com abas e modais
- Personalizável: Código aberto para adaptações específicas do seu negócio
Tecnologias Utilizadas:
- WordPress Hooks (
admin_menu,wp_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">×</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,
]);
}
