O meu mais novo projeto (ou side-gig?), Zenkai Zone, já se transformou em um site de e-commerce sólido, mas sejamos francos - usar apenas o template padrão do Magento 2 é meio sem graça, quer dizer, vamos lá, eu sou o cara que criou um jogo inteiro para este blog, eu deveria conseguir trazer um pouco de diversão para o meu e-commerce. Não é mesmo?
Ideação#
Eu queria dar um toque especial, injetar um pouco de personalidade e tornar a experiência de compra mais envolvente. Então, decidi criar o GameShop - uma extensão para Magento 2 desenvolvida especificamente para a Zenkai Zone que transforma a vitrine em algo que lembra uma loja de RPG old-school. Com uma página dedicada, completa com um avatar de lojista, endpoints de API customizados e um toque de magia em SEO para compartilhamento nas mídias sociais, a ideia era deixar meu site menos monótono e mais divertido.

Eu combinei Vue.js, TailwindCSS, APIs customizadas do Magento e uns ajustes de SEO para dar aquele toque especial na Zenkai Zone. Ao longo do caminho, você verá trechos de código e meus comentários sobre o que está acontecendo nos bastidores.
O que estamos construindo#
- Uma nova página de vitrine alimentada por Vue + Tailwind em
/game-shop, personalizada para a Zenkai Zone. - Endpoints de API para gerenciar carrinho, categorias e produtos de formas que o núcleo do Magento talvez não permita tanta flexibilidade.
- Metadados amigáveis para SEO e tags Open Graph para uma pré-visualização matadora no compartilhamento em redes sociais.
- Uma nova configuração no painel de administração para ativar ou desativar toda a extensão sem precisar mexer no código.
Estrutura do módulo Magento 2#
Segue a estrutura organizada de arquivos para o módulo GameShop. Mantê-la organizada significa que sempre posso encontrar o que preciso - mesmo se estiver no meio de outra ideia para a Zenkai Zone:
app/code/Werules/GameShop/
├── Api/
│ ├── CartManagementInterface.php
│ ├── CategoryManagementInterface.php
│ ├── ProductManagementInterface.php
├── Controller/
│ ├── Index/
│ │ ├── Index.php
├── etc/
│ ├── adminhtml/
│ │ ├── system.xml
│ ├── frontend/
│ │ ├── di.xml
│ │ ├── module.xml
│ │ ├── routes.xml
│ ├── webapi.xml
├── Model/
│ ├── CartManagement.php
│ ├── CategoryManagement.php
│ ├── ProductManagement.php
├── view/
│ ├── frontend/
│ │ ├── layout/
│ │ │ ├── werules_gameshop_index_index.xml
│ │ ├── templates/
│ │ │ ├── index.phtml
│ │ ├── web/
│ │ │ ├── css/
│ │ │ │ ├── gameshop.css
│ │ │ ├── js/
│ │ │ │ ├── vue.global.js
│ │ │ │ ├── tailwind.min.js
│ │ │ ├── images/
│ │ │ │ ├── shopkeeper.png
├── registration.php
Passo 1: a página customizada no frontend#
Definindo a rota#
O primeiro passo foi configurar a rota. No arquivo routes.xml, mapeei /game-shop para a rota do módulo customizado.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
<router id="standard">
<route id="werules_gameshop" frontName="game-shop">
<module name="Werules_GameShop"/>
</route>
</router>
</config>Criando o controller#
Em seguida, veio o controller Index.php. Este arquivo decide se a nova página do GameShop será exibida ou, se a extensão estiver desativada, mostra a página padrão de “rota não encontrada” do Magento.
<?php
namespace Werules\GameShop\Controller\Index;
use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;
use Magento\Framework\Controller\ResultFactory;
use Magento\Framework\App\Config\ScopeConfigInterface;
class Index extends Action
{
protected $scopeConfig;
public function __construct(
Context $context,
ScopeConfigInterface $scopeConfig
) {
parent::__construct($context);
$this->scopeConfig = $scopeConfig;
}
public function execute()
{
// Check if the extension is activated for [Zenkai Zone](https://zenkaizone.com)
$isEnabled = $this->scopeConfig->isSetFlag('werules/gameshop/enabled', \Magento\Store\Model\ScopeInterface::SCOPE_STORE);
if (!$isEnabled) {
// If not, redirect to Magento's default no-route page
return $this->resultFactory->create(ResultFactory::TYPE_FORWARD)->forward('noroute');
}
// Otherwise, load our new, fun page
$resultPage = $this->resultFactory->create(ResultFactory::TYPE_PAGE);
$resultPage->getConfig()->getTitle()->set(__('Game Shop'));
return $resultPage;
}
}Passo 2: APIs customizadas do Magento#
O sistema de API do Magento 2 é sólido, mas eu queria mais controle para a Zenkai Zone. Ao introduzir interfaces e implementações customizadas, pude adaptar o comportamento exatamente como necessário.
API de gerenciamento de carrinho (CartManagement.php)#
Se a extensão estiver desativada, a API retorna educadamente zero; caso contrário, ela se comporta como as funções padrão de carrinho - só que com um toque extra de personalidade.
public function getCartItemCount()
{
if (!$this->isModuleEnabled()) {
return 0;
}
$quote = $this->cart->getQuote();
return (int)$quote->getItemsQty();
}
public function addItemToCart($productId, $qty = 1)
{
if (!$this->isModuleEnabled()) {
return ['success' => false, 'message' => 'GameShop is disabled.', 'cart_count' => 0];
}
// Normal add-to-cart logic here
}API de gerenciamento de produtos (ProductManagement.php)#
De forma similar, esta API fornece listagens de produtos de qualquer categoria, mas somente se a extensão estiver ativa.
public function getProductsByCategoryId($categoryId)
{
if (!$this->isModuleEnabled()) {
return [];
}
// Standard product retrieval logic
}Desenhando uma interface de RPG retrô para o GameShop#
Eu queria dar à Zenkai Zone uma dose extra de personalidade. Injetando uma vibe de “loja clássica de RPG” usando Vue.js para reatividade e TailwindCSS para estilização, a vitrine vira algo divertido - bem mais interessante do que uma página de e-commerce típica.
Segue o layout:
- Cabeçalho fixo exibindo o nome da loja.
- Barra lateral do lojista com um avatar, contagem de itens do carrinho em tempo real e opções de menu (Comprar, Vender, Falar, Sair).
- Painel principal exibindo listagens dinâmicas de categorias e produtos, além de uma visualização detalhada do produto.

Montando o layout#
Dentro do arquivo index.phtml, configurei o container para minha aplicação Vue:
O container da aplicação Vue.js#
<div id="app" class="min-h-screen bg-black text-orange-200 font-mono p-6 flex flex-col space-y-4 text-lg">- O Vue monta na
#app. - O fundo preto + texto laranja evocam aquele visual nostálgico de consoles antigos.
Um cabeçalho fixo para o nome da loja#
<div class="border-2 border-orange-500 p-3 sticky top-0 bg-black z-50">
<h1 class="text-3xl md:text-4xl font-bold tracking-widest uppercase">
<?php echo $currentStoreName; ?>
</h1>
</div>- O cabeçalho fixo mantém o nome da loja visível, para que os clientes sempre saibam que estão na Zenkai Zone.
Navegação lateral: avatar do lojista & menu#
Esta seção é toda sobre personalidade. O avatar do lojista dá as boas-vindas aos visitantes, enquanto a contagem do carrinho e os botões do menu (Comprar, Vender, Falar, Sair) adicionam um toque interativo.
<!-- Shopkeeper Avatar -->
<div class="border-2 border-orange-500 p-2 flex justify-center items-center bg-black">
<img src="<?= $block->getViewFileUrl('Werules_GameShop::images/shopkeeper.png'); ?>"
alt="<?= __('Shopkeeper Avatar'); ?>"
class="w-32 h-32 object-cover border border-orange-500 bg-black avatar">
</div>
<!-- Cart Count -->
<div class="mb-4">
<div class="text-lg text-orange-400"><?= __('Cart items:'); ?></div>
<div class="text-2xl font-bold">{{ cartCount }}</div>
</div>Atualizações em tempo real via Vue mantêm a interface dinâmica e responsiva.
Adicionando os botões do menu#
<button class="block w-full py-3 border border-orange-500 hover:bg-orange-900 text-center px-4 text-lg"
:class="{'bg-orange-500 text-black': menuSelection === 'Buy'}"
@click="onMenuItemClick('Buy')">
<?= __('Buy'); ?>
</button>
<button class="block w-full py-3 border border-orange-500 hover:bg-orange-900 text-center px-4 text-lg"
:class="{'bg-orange-500 text-black': menuSelection === 'Sell'}"
@click="onMenuItemClick('Sell')">
<?= __('Sell'); ?>
</button>
<button class="block w-full py-3 border border-orange-500 hover:bg-orange-900 text-center px-4 text-lg"
@click="onMenuItemClick('Exit')">
<?= __('Exit'); ?>
</button>- A estilização condicional destaca a seleção atual.
- O botão Sair pode redirecionar os clientes de volta ao site principal da Zenkai Zone, se necessário.
Área de conteúdo principal: categorias & listagens de produtos#
Quando os usuários selecionam “Comprar”, eles veem categorias exibidas no topo. Ao selecionar uma categoria, uma grade de produtos é mostrada - conteúdo dinâmico alimentado pelas nossas APIs customizadas.
Abas de categorias#
<!-- Category Tabs -->
<div v-if="activeView === 'list'" class="flex flex-wrap gap-2 mb-3">
<button v-for="cat in categories" :key="cat.id"
class="px-3 py-1 border border-orange-500 hover:bg-orange-500 hover:text-black transition"
:class="{'bg-orange-500 text-black': currentCategoryId === cat.id}"
@click="loadProducts(cat.id, cat.name)">
{{ cat.name }}
</button>
</div>- As categorias são buscadas através do endpoint
rest/V1/gameshop/categories. - Um loop Vue
v-forgarante que cada categoria seja renderizada automaticamente.
Grade de listagem de produtos#
<div v-if="activeView === 'list'">
<div v-if="products.length === 0" class="text-center text-orange-400 text-lg mt-6">
<?= __('No products found in this category.'); ?>
</div>
<div class="space-y-3">
<div v-for="product in products" :key="product.id"
class="border border-orange-500 p-4 flex flex-col md:flex-row items-center cursor-pointer hover:bg-orange-900 transition"
@click="showDetails(product)">
<img :src="product.image_url"
class="w-32 h-32 object-cover border border-orange-500 bg-black">
<div class="flex-1 text-center md:text-left">
<span class="block text-lg">{{ product.name }}</span>
<span class="block text-xl text-orange-300 mt-2">{{ formatPrice(product.price) }}</span>
</div>
</div>
</div>
</div>- Clicar em um produto exibe a visualização detalhada.

Visualização detalhada do produto#
Quando um produto é selecionado, uma visualização detalhada mostra seu nome, preço, descrição e um botão de Adicionar ao Carrinho.
<div v-if="activeView === 'detail' && currentProduct" class="space-y-4">
<p class="italic text-lg mb-2">
<?= sprintf(__('Ah, the %s! This item might do something special...'), '<strong>{{ currentProduct.name }}</strong>'); ?>
</p>
<div class="flex flex-col md:flex-row space-y-4 items-start border border-orange-500 p-4">
<img :src="currentProduct.image_url"
class="w-72 h-72 object-cover border border-orange-500 bg-black">
<div class="flex-1">
<h3 class="text-2xl font-bold">{{ currentProduct.name }}</h3>
<p class="text-orange-300 text-xl mb-3">{{ formatPrice(product.price) }}</p>
<p class="text-lg text-orange-300">
{{ currentProduct.description }}
</p>
</div>
</div>
<div class="flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:space-x-6">
<button @click="addToCart(currentProduct.id)"
class="px-5 py-3 bg-orange-500 text-black border border-orange-500 hover:bg-orange-600 transition text-xl">
<?= __('Add to Cart'); ?>
</button>
<button @click="goBackToList()"
class="px-5 py-3 border border-orange-500 hover:bg-orange-900 text-orange-200 transition text-xl">
<?= __('Back'); ?>
</button>
</div>
</div>A simples alternância entre as visualizações “list” e “detail” - alimentada pelo Vue - mantém a experiência do usuário suave e sem interrupções.

Conversa com o lojista: o menu “Falar”#
Para dar um toque de personalidade, o menu “Falar” permite que o lojista compartilhe frases de efeito aleatórias. É um recurso opcional que adiciona ainda mais caráter à experiência de compra.
<div v-else-if="menuSelection === 'Talk'" class="flex items-center justify-center h-64 border-2 border-orange-500 p-6">
<p class="text-3xl text-orange-400 text-center">
{{ currentTalkLine }}
</p>
</div>methods: {
onMenuItemClick(selection) {
if (selection === 'Talk') {
const randomIndex = Math.floor(Math.random() * this.talkLines.length);
this.currentTalkLine = this.talkLines[randomIndex];
}
}
}Este pequeno recurso pode até ser estendido para oferecer descontos surpresa ou recomendações de produtos.
Passo 3: SEO#
Para impulsionar o compartilhamento nas mídias sociais da Zenkai Zone, adicionei meta tags e dados JSON-LD dentro do <head>. Isso garante que plataformas como Facebook e Twitter exibam uma pré-visualização bacana com a imagem do lojista.
Atualize werules_gameshop_index_index.xml#
<referenceBlock name="head.additional">
<block class="Magento\Framework\View\Element\Template"
name="werules.gameshop.head"
template="Werules_GameShop::seo/head.phtml"/>
</referenceBlock>Crie o head.phtml#
<?php
$mediaUrl = $block->getViewFileUrl('Werules_GameShop::images/shopkeeper.png');
$baseUrl = $block->getBaseUrl();
$pageTitle = __('GameShop - Buy & Sell Games');
$pageDescription = __('Find the best gaming deals, buy and sell games easily!');
?>
<!-- Open Graph Meta Tags -->
<meta property="og:image" content="<?= $mediaUrl ?>"/>
<meta property="og:image:alt" content="GameShop Shopkeeper Avatar"/>
<meta property="og:url" content="<?= $baseUrl ?>game-shop"/>
<!-- Twitter Card -->
<meta name="twitter:image" content="<?= $mediaUrl ?>"/>
<!-- JSON-LD Structured Data -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebPage",
"name": "<?= $pageTitle ?>",
"description": "<?= $pageDescription ?>",
"image": "<?= $mediaUrl ?>",
"url": "<?= $baseUrl ?>game-shop",
"publisher": {
"@type": "Organization",
"name": "GameShop",
"logo": "<?= $mediaUrl ?>"
}
}
</script>Com essas tags implementadas, a extensão fica bonita e também funciona bem para SEO e compartilhamento nas redes sociais.
Etapas finais & testes#
Ativando a extensão#
bin/magento module:enable Werules_GameShop
bin/magento setup:upgrade
bin/magento cache:flushAgora o seu módulo está oficialmente ativo.
Testando o frontend#
Acesse https://seumagento.com/game-shop e se você vir a interface em preto e laranja com o lojista, o trabalho está concluído.
Claro, você também pode conferir uma demonstração ao vivo da extensão GameShop em zenkaizone.com/game-shop.
Conclusão#
É isso aí! A vitrine da Zenkai Zone agora tem uma extensão customizada para Magento 2 com novas APIs para carrinho e produtos e um design divertido, inspirado em RPG, com um SEO decente. E ainda tem muito espaço para expandir este conceito:
- Autenticação de usuários para permitir que os clientes salvem seu progresso na loja virtual.
- Integração de checkout com a rota customizada
game-shoppara uma experiência completa de vitrine alternativa. - Filtragem avançada de produtos para combinar gêneros de jogos ou até mesmo referências retrô aleatórias.
Eu me diverti muito misturando um pouco da nostalgia dos jogos com a funcionalidade moderna do e-commerce usando Vue.js e TailwindCSS. O resultado final é uma extensão maluquinha que deixa a Zenkai Zone bem mais divertida.
Contribuindo com a extensão GameShop#
Como em todos os meus projetos, a extensão GameShop é open-source e está disponível no GitHub. Faz um fork, testa e contribui com o projeto se quiser. Adoraria ver o que você vai criar!
Para onde vamos a partir daqui? Talvez integrar o ChatGPT para oferecer recomendações de produtos personalizadas ou adicionar um sistema de moedas para que os clientes possam trocar “moedas de ouro” por descontos.