Tutorial Apps Script

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.

// ==========================================================================
// 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 };
  }
}
<!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>

Soluções prontas para você

Escolha um dos nossos templates profissionais de alta performance e comece a otimizar sua gestão hoje mesmo, sem mensalidades.

Vamos Construir sua Ferramenta Sob Medida?

// PROJETOS PERSONALIZADOS
🔍

Entendendo o Desafio

Nosso ponto de partida é o seu objetivo final. Analisamos a fundo a sua necessidade de negócio para mapear a estrutura e as funcionalidades da ferramenta ideal, garantindo uma solução que nasce alinhada à sua estratégia.

⚙️

Criação da Interface

Transformamos a complexidade da sua planilha em um painel de controle simples de usar. O resultado é uma tela limpa, com botões e visões claras, permitindo que você gerencie seus dados sem que precise de conhecimento técnico.

Entrega da Ferramenta

Você recebe o sistema completo e funcional, implementado diretamente na sua conta Google. Entregamos uma ferramenta pronta para o uso, desbloqueando imediatamente o potencial de uma gestão visual e centralizada.

×
Solução Pronta

Gosta de praticidade?

Sabia que nós desenvolvemos sistemas prontos baseados nesses tutoriais? Dê uma olhada nos nossos templates e pule a etapa da programação!

Conhecer os Templates