Tutorial – Crie um Menu Profissional para suas Planilhas Google

Cansado de se perder no meio de dezenas de abas lá embaixo na sua planilha? Navegar por abas padrão é um processo lento, poluído e que deixa o seu trabalho com cara de amador. Neste tutorial completo, vamos construir um sistema que transforma essa confusão visual em uma Interface de Software Profissional.

Neste projeto, desenvolveremos um Gestor de Estoque equipado com um menu superior fixo e ícones modernos. Você aprenderá como usar o Google Apps Script para criar uma navegação dinâmica, onde o conteúdo da tela muda instantaneamente ao clique de um botão, sem precisar recarregar a página.

O que você vai aprender:

  • Interface Web Moderna: Como criar uma barra de navegação azul estilo Google usando HTML e CSS.

  • Navegação Dinâmica: O segredo da função JavaScript para alternar entre as abas da planilha com um clique.

  • Automação com Apps Script: Como usar a função getSheetData para ler qualquer aba de forma genérica e inteligente.

Código.gs

// ==========================================================================
// ARQUIVO: Código.gs
// ==========================================================================

const PROJECT_TITLE = "Gestor de Estoque";

function doGet(e) {
  const tpl = HtmlService.createTemplateFromFile('Index');
  return tpl.evaluate()
      .setTitle(PROJECT_TITLE)
      .addMetaTag('viewport', 'width=device-width, initial-scale=1.0')
      .setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}

/**
 * Função genérica para buscar dados de qualquer aba.
 * Usa getDisplayValues() para trazer os dados já formatados (R$, Datas) como texto.
 */
function getSheetData(sheetName) {
  try {
    const ss = SpreadsheetApp.getActiveSpreadsheet();
    const sheet = ss.getSheetByName(sheetName);
    
    if (!sheet) return { error: `Aba "${sheetName}" não encontrada.` };
    
    const lastRow = sheet.getLastRow();
    const lastCol = sheet.getLastColumn();
    
    if (lastRow<1||lastCol<1) return { headers:[], data:[] }; // Aba vazia
    
    // Pega cabeçalhos e dados
    const headers = sheet.getRange(1, 1, 1, lastCol).getValues()[0];
    
    // Se tiver dados além do cabeçalho
    let data =[];
    if (lastRow>1) {
      data = sheet.getRange(2, 1, lastRow - 1, lastCol).getDisplayValues();
    }
    
    return { headers: headers, data: data, sheetName: sheetName };
    
  } catch (e) {
    return { error: e.message };
  }
}

Index.html

<!DOCTYPE html>
<html>
<head>
  <base target="_top">
  <!-- Fonte Google (Roboto) -->
  <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
  <!-- Ícones Material Design -->
  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
 
  <style>
    :root {
      --primary-color: #1a73e8; /* Azul Google */
      --header-height: 60px;
      --bg-color: #f8f9fa;
      --card-bg: #ffffff;
      --text-color: #202124;
      --border-color: #dadce0;
    }
 
    * { box-sizing: border-box; }
     
    body {
      margin: 0;
      padding: 0;
      font-family: 'Roboto', sans-serif;
      background-color: var(--bg-color);
      color: var(--text-color);
      padding-top: var(--header-height); /* Espaço para o menu fixo */
    }
 
    /* --- MENU SUPERIOR FIXO --- */
    .navbar {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: var(--header-height);
      background-color: var(--primary-color);
      color: white;
      display: flex;
      align-items: center;
      padding: 0 20px;
      box-shadow: 0 2px 4px rgba(0,0,0,0.2);
      z-index: 1000;
    }
 
    .nav-title {
      font-size: 1.2rem;
      font-weight: 500;
      margin-right: 40px;
      display: flex;
      align-items: center;
      gap: 10px;
    }
 
    .nav-links {
      display: flex;
      gap: 5px;
      overflow-x: auto; /* Permite rolar no mobile */
    }
 
    .nav-btn {
      background: none;
      border: none;
      color: rgba(255,255,255,0.7);
      padding: 8px 16px;
      border-radius: 4px;
      cursor: pointer;
      font-size: 0.95rem;
      font-weight: 500;
      transition: all 0.2s;
      white-space: nowrap;
    }
 
    .nav-btn:hover { color: white; background-color: rgba(255,255,255,0.1); }
    .nav-btn.active { color: var(--primary-color); background-color: white; }
 
    /* --- ÁREA DE CONTEÚDO --- */
    .container {
      max-width: 1200px;
      margin: 20px auto;
      padding: 0 15px;
    }
 
    .content-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 20px;
    }
 
    h2 { margin: 0; font-weight: 400; color: #5f6368; }
 
    /* --- TABELA ESTILIZADA --- */
    .table-responsive {
      overflow-x: auto;
      background: var(--card-bg);
      border-radius: 8px;
      box-shadow: 0 1px 3px rgba(0,0,0,0.1);
      border: 1px solid var(--border-color);
    }
 
    table {
      width: 100%;
      border-collapse: collapse;
      min-width: 600px; /* Garante largura mínima */
    }
 
    th, td {
      padding: 12px 15px;
      text-align: left;
      border-bottom: 1px solid var(--border-color);
    }
 
    th {
      background-color: #f1f3f4;
      font-weight: 600;
      color: #5f6368;
      position: sticky;
      top: 0;
    }
 
    tr:hover { background-color: #f8f9fa; }
    tr:last-child td { border-bottom: none; }
 
    /* --- LOADER --- */
    #loader {
      display: none;
      justify-content: center;
      padding: 40px;
    }
    .spinner {
      border: 4px solid #f3f3f3;
      border-top: 4px solid var(--primary-color);
      border-radius: 50%;
      width: 40px;
      height: 40px;
      animation: spin 1s linear infinite;
    }
    @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
 
  </style>
</head>
<body>
 
  <!-- Menu Superior -->
  <nav class="navbar">
    <div class="nav-title">
      <span class="material-icons">inventory_2</span> Estoque
    </div>
    <div class="nav-links">
      <button class="nav-btn active" onclick="loadTab('Produtos', this)">Produtos</button>
      <button class="nav-btn" onclick="loadTab('Estoque', this)">Estoque</button>
      <button class="nav-btn" onclick="loadTab('Vendas', this)">Vendas</button>
      <button class="nav-btn" onclick="loadTab('Histórico', this)">Histórico</button>
    </div>
  </nav>
 
  <!-- Área Principal -->
  <div class="container">
    <div class="content-header">
      <h2 id="pageTitle">Produtos</h2>
      <button onclick="refreshData()" style="border:none; background:none; cursor:pointer; color: #1a73e8;">
        <span class="material-icons">refresh</span>
      </button>
    </div>
 
    <!-- Loading -->
    <div id="loader"><div class="spinner"></div></div>
 
    <!-- Tabela Dinâmica -->
    <div id="tableContainer" class="table-responsive">
      <!-- Tabela inserida aqui via JS -->
    </div>
  </div>
 
  <script>
    let currentTab = 'Produtos';
 
    // Ao carregar a página
    document.addEventListener('DOMContentLoaded', () => {
      loadTab('Produtos', document.querySelector('.nav-btn.active'));
    });
 
    function loadTab(sheetName, btnElement) {
      currentTab = sheetName;
       
      // Atualiza visual do menu
      if (btnElement) {
        document.querySelectorAll('.nav-btn').forEach(b => b.classList.remove('active'));
        btnElement.classList.add('active');
      }
 
      // Atualiza título e mostra loader
      document.getElementById('pageTitle').textContent = sheetName;
      document.getElementById('tableContainer').innerHTML = '';
      document.getElementById('loader').style.display = 'flex';
 
      // Chama o Backend
      google.script.run
        .withSuccessHandler(renderTable)
        .withFailureHandler(showError)
        .getSheetData(sheetName);
    }
 
    function refreshData() {
      // Simula um clique no botão ativo atual para recarregar
      const activeBtn = document.querySelector('.nav-btn.active');
      loadTab(currentTab, activeBtn);
    }
 
    function renderTable(response) {
      document.getElementById('loader').style.display = 'none';
      const container = document.getElementById('tableContainer');
 
      if (response.error) {
        container.innerHTML = `<p style="padding:20px; color:red;">${response.error}</p>`;
        return;
      }
 
      if (!response.data || response.data.length === 0) {
        container.innerHTML = `<p style="padding:20px; text-align:center; color:#888;">Nenhum registro encontrado em ${response.sheetName}.</p>`;
        return;
      }
 
      // Monta a Tabela HTML
      let html = '<table><thead><tr>';
       
      // Cabeçalhos
      response.headers.forEach(h => {
        html += `<th>${h}</th>`;
      });
      html += '</tr></thead><tbody>';
 
      // Dados
      response.data.forEach(row => {
        html += '<tr>';
        row.forEach(cell => {
          html += `<td>${cell}</td>`;
        });
        html += '</tr>';
      });
 
      html += '</tbody></table>';
      container.innerHTML = html;
    }
 
    function showError(err) {
      document.getElementById('loader').style.display = 'none';
      document.getElementById('tableContainer').innerHTML = `<p style="color:red; padding:20px;">Erro de conexão: ${err.message}</p>`;
    }
  </script>
</body>
</html>

Acompanhe nas redes sociais

Projetos Personalizados

Precisa de uma solução sob medida?

© 2025 Transformando Planilhas. Todos os direitos reservados.