Spaces:
Running
Running
Ctrl+K
- 1.52 kB initial commit
- 212 Bytes <!DOCTYPE html> <html lang="pt-BR"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Restaurant Stock Control</title> <script src="https://cdn.tailwindcss.com"></script> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> <script> /* PT: Bloco de configuração para o Tailwind CSS. 'extend' permite adicionar novas cores ao tema padrão sem sobrescrevê-lo. Isso permite o uso de classes como 'bg-primary', 'text-accent', etc. EN: Configuration block for Tailwind CSS. 'extend' allows adding new colors to the default theme without overwriting it. This enables the use of classes like 'bg-primary', 'text-accent', etc. */ // Configuração personalizada do Tailwind CSS tailwind.config = { theme: { extend: { colors: { // Cor primária (azul escuro) primary: '#1a365d', // Cor secundária (azul médio) secondary: '#2c5282', // Cor de destaque (azul claro) accent: '#4299e1', // Cor para mensagens de sucesso (verde) success: '#48bb78', // Cor para alertas ou avisos (laranja) warning: '#ed8936', // Cor para mensagens de erro ou perigo (vermelho) danger: '#e53e3e', } } } } </script> <style> /* PT: Estilos para a barra lateral quando estiver recolhida. EN: Styles for the sidebar when it is collapsed. */ .sidebar-collapsed { width: 80px; /* PT: Largura reduzida. / EN: Reduced width. */ overflow: hidden; /* PT: Esconde o conteúdo que transborda. / EN: Hides overflowing content. */ } /* PT: Esconde o texto dos itens de navegação quando a barra lateral está recolhida. EN: Hides the navigation item text when the sidebar is collapsed. */ .sidebar-collapsed .nav-text { display: none; } /* PT: Esconde o texto do logo quando a barra lateral está recolhida. EN: Hides the logo text when the sidebar is collapsed. */ .sidebar-collapsed .logo-text { display: none; } /* PT: Centraliza o conteúdo (ícones) dos itens de menu quando a barra lateral está recolhida. EN: Centers the content (icons) of menu items when the sidebar is collapsed. */ .sidebar-collapsed .menu-item { justify-content: center; } /* PT: Estilo para o item de menu que está ativo/selecionado. EN: Style for the active/selected menu item. */ .active-menu { background-color: rgba(66, 153, 225, 0.1); /* PT: Fundo levemente colorido. / EN: Slightly colored background. */ border-left: 4px solid #4299e1; /* PT: Borda à esquerda para indicar seleção. / EN: Left border to indicate selection. */ } /* PT: Classe para aplicar a animação 'pulse' em itens com baixo estoque. EN: Class to apply the 'pulse' animation to low-stock items. */ .low-stock { animation: pulse 2s infinite; /* PT: Nome da animação, duração, repetição infinita. / EN: Animation name, duration, infinite repetition. */ } /* PT: Define a animação 'pulse'. Cria um efeito de brilho sutil alterando a sombra do elemento. EN: Defines the 'pulse' animation. It creates a subtle glow effect by changing the element's box-shadow. */ /* Animação personalizada chamada 'pulse' */ @keyframes pulse { /* No início (0%) a sombra ao redor é visível e pequena */ 0% { box-shadow: 0 0 0 0 rgba(234, 88, 12, 0.4); /* sombra laranja suave */ } /* Aos 70% da animação, a sombra se expande e desaparece */ 70% { box-shadow: 0 0 0 10px rgba(234, 88, 12, 0); /* sombra expandida e totalmente transparente */ } /* Ao final da animação (100%), a sombra volta ao estado inicial invisível */ 100% { box-shadow: 0 0 0 0 rgba(234, 88, 12, 0); /* sombra recolhida e invisível */ } } </style> </head> <!-- Define o corpo da página com fundo cinza claro e fonte sans-serif --> <body class="bg-gray-100 font-sans"> <!-- Container principal usando Flex para dividir tela em sidebar e conteúdo --> <div class="flex h-screen overflow-hidden"> <!-- Sidebar lateral fixa --> <div id="sidebar" class="bg-primary text-white flex flex-col h-full transition-all duration-300 ease-in-out"> <!-- Cabeçalho da sidebar com logo e botão de colapsar --> <div class="p-4 flex items-center justify-between border-b border-secondary"> <!-- Logo e nome do sistema --> <div class="flex items-center"> <i class="fas fa-utensils text-2xl text-accent mr-3"></i> <!-- Ícone de talher (logo) --> <span class="logo-text text-xl font-bold">RestoControl</span> <!-- Nome da aplicação --> </div> <!-- Botão para alternar visibilidade da sidebar --> <button id="toggle-sidebar" class="text-gray-300 hover:text-white"> <i class="fas fa-bars"></i> <!-- Ícone de menu/barras --> </button> </div> <!-- Área principal do menu de navegação --> <div class="flex-1 overflow-y-auto mt-4"> <!-- Seção "Menu" com título --> <div class="px-4 py-2"> <div class="text-xs uppercase tracking-wider text-gray-400 sidebar-collapsed:text-center nav-text"> Menu </div> </div> <!-- Lista de itens do menu --> <ul> <!-- Item: Dashboard --> <li> <a href="#dashboard" class="menu-item flex items-center px-4 py-3 text-gray-300 hover:bg-secondary hover:text-white transition-colors duration-200 active-menu"> <i class="fas fa-tachometer-alt mr-3"></i> <span class="nav-text">Dashboard</span> </a> </li> <!-- Item: Garçons --> <li> <a href="#waiters" class="menu-item flex items-center px-4 py-3 text-gray-300 hover:bg-secondary hover:text-white transition-colors duration-200"> <i class="fas fa-user-tie mr-3"></i> <span class="nav-text">Garçons</span> </a> </li> <!-- Item: Estoque --> <li> <a href="#inventory" class="menu-item flex items-center px-4 py-3 text-gray-300 hover:bg-secondary hover:text-white transition-colors duration-200"> <i class="fas fa-boxes mr-3"></i> <span class="nav-text">Estoque</span> </a> </li> <!-- Item: Vendas --> <li> <a href="#sales" class="menu-item flex items-center px-4 py-3 text-gray-300 hover:bg-secondary hover:text-white transition-colors duration-200"> <i class="fas fa-cash-register mr-3"></i> <span class="nav-text">Vendas</span> </a> </li> <!-- Item: Relatórios --> <li> <a href="#reports" class="menu-item flex items-center px-4 py-3 text-gray-300 hover:bg-secondary hover:text-white transition-colors duration-200"> <i class="fas fa-chart-bar mr-3"></i> <span class="nav-text">Relatórios</span> </a> </li> </ul> </div> <!-- Rodapé da sidebar com informações do usuário logado --> <div class="p-4 border-t border-secondary"> <div class="flex items-center"> <!-- Avatar do usuário (imagem gerada por URL) --> <img src="https://ui-avatars.com/api/?name=Admin&background=4299e1&color=fff" alt="User" class="rounded-full w-10 h-10"> <!-- Nome e e-mail do usuário --> <div class="ml-3 nav-text"> <div class="text-sm font-medium">Admin</div> <div class="text-xs text-gray-400">gerente@resto.com</div> </div> <!-- Botão de logout (ícone de sair) --> <button class="ml-auto text-gray-400 hover:text-white nav-text"> <i class="fas fa-sign-out-alt"></i> </button> </div> </div> </div> <!-- Main Content --> <div class="flex-1 overflow-auto"> <div class="bg-white shadow-sm"> <div class="flex justify-between items-center p-4"> <h1 class="text-2xl font-semibold text-gray-800">Painel de Controle</h1> <div class="flex items-center space-x-4"> <div class="relative"> <i class="fas fa-search absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"></i> <input type="text" placeholder="Pesquisar..." class="bg-gray-100 pl-10 pr-4 py-2 rounded-lg focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent"> </div> <button class="text-gray-600 hover:text-gray-800 relative"> <i class="fas fa-bell text-xl"></i> <span class="absolute top-0 right-0 inline-block w-3 h-3 bg-danger rounded-full"></span> </button> </div> </div> </div> <div id="content-area" class="p-6"> <!-- Dashboard Content --> <div id="dashboard-content"> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-6"> <div class="bg-white rounded-lg shadow p-6"> <div class="flex items-center"> <div class="p-3 rounded-full bg-blue-100 text-accent"> <i class="fas fa-cash-register text-2xl"></i> </div> <div class="ml-4"> <span class="text-gray-500">Vendas Hoje</span> <h3 class="text-2xl font-bold">R$ 2,450.00</h3> </div> </div> </div> <div class="bg-white rounded-lg shadow p-6"> <div class="flex items-center"> <div class="p-3 rounded-full bg-green-100 text-success"> <i class="fas fa-user-tie text-2xl"></i> </div> <div class="ml-4"> <span class="text-gray-500">Total Garçons</span> <h3 class="text-2xl font-bold">8</h3> </div> </div> </div> <div class="bg-white rounded-lg shadow p-6"> <div class="flex items-center"> <div class="p-3 rounded-full bg-yellow-100 text-warning"> <i class="fas fa-exclamation-triangle text-2xl"></i> </div> <div class="ml-4"> <span class="text-gray-500">Produtos em Falta</span> <h3 class="text-2xl font-bold">4</h3> </div> </div> </div> <div class="bg-white rounded-lg shadow p-6"> <div class="flex items-center"> <div class="p-3 rounded-full bg-purple-100 text-indigo-600"> <i class="fas fa-box-open text-2xl"></i> </div> <div class="ml-4"> <span class="text-gray-500">Total Produtos</span> <h3 class="text-2xl font-bold">124</h3> </div> </div> </div> </div> <div class="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-6"> <div class="bg-white rounded-lg shadow p-6 lg:col-span-2"> <div class="flex justify-between items-center mb-4"> <h2 class="text-lg font-semibold">Vendas Recentes</h2> <button class="text-sm text-accent hover:underline">Ver todas</button> </div> <div class="overflow-x-auto"> <table class="min-w-full divide-y divide-gray-200"> <thead class="bg-gray-50"> <tr> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Garçom</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Mesa</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Valor</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th> </tr> </thead> <tbody class="bg-white divide-y divide-gray-200"> <tr> <td class="px-6 py-4 whitespace-nowrap"> <div class="flex items-center"> <div class="flex-shrink-0 h-10 w-10"> <img class="h-10 w-10 rounded-full" src="https://ui-avatars.com/api/?name=Carlos+S" alt=""> </div> <div class="ml-4"> <div class="text-sm font-medium text-gray-900">Carlos Silva</div> </div> </div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm text-gray-900">Mesa 5</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm text-gray-900">R$ 120.50</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800"> Pago </span> </td> </tr> <tr> <td class="px-6 py-4 whitespace-nowrap"> <div class="flex items-center"> <div class="flex-shrink-0 h-10 w-10"> <img class="h-10 w-10 rounded-full" src="https://ui-avatars.com/api/?name=Ana+P" alt=""> </div> <div class="ml-4"> <div class="text-sm font-medium text-gray-900">Ana Paula</div> </div> </div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm text-gray-900">Mesa 12</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm text-gray-900">R$ 85.75</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-yellow-100 text-yellow-800"> Pendente </span> </td> </tr> <tr> <td class="px-6 py-4 whitespace-nowrap"> <div class="flex items-center"> <div class="flex-shrink-0 h-10 w-10"> <img class="h-10 w-10 rounded-full" src="https://ui-avatars.com/api/?name=Marcos+R" alt=""> </div> <div class="ml-4"> <div class="text-sm font-medium text-gray-900">Marcos Rocha</div> </div> </div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm text-gray-900">Mesa 8</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm text-gray-900">R$ 210.00</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800"> Pago </span> </td> </tr> </tbody> </table> </div> </div> <div class="bg-white rounded-lg shadow p-6"> <h2 class="text-lg font-semibold mb-4">Produtos em Falta</h2> <div class="space-y-4"> <div class="flex items-center justify-between p-3 bg-red-50 rounded-lg low-stock"> <div class="flex items-center"> <i class="fas fa-wine-bottle text-red-500 mr-3"></i> <div> <h4 class="font-medium">Vinho Tinto</h4> <p class="text-sm text-gray-500">Estoque: 2 unidades</p> </div> </div> <button class="text-sm text-accent hover:text-secondary"> <i class="fas fa-plus"></i> Repor </button> </div> <div class="flex items-center justify-between p-3 bg-red-50 rounded-lg low-stock"> <div class="flex items-center"> <i class="fas fa-drumstick-bite text-red-500 mr-3"></i> <div> <h4 class="font-medium">Picanha</h4> <p class="text-sm text-gray-500">Estoque: 3 kg</p> </div> </div> <button class="text-sm text-accent hover:text-secondary"> <i class="fas fa-plus"></i> Repor </button> </div> <div class="flex items-center justify-between p-3 bg-red-50 rounded-lg"> <div class="flex items-center"> <i class="fas fa-cheese text-red-500 mr-3"></i> <div> <h4 class="font-medium">Queijo Minas</h4> <p class="text-sm text-gray-500">Estoque: 5 kg</p> </div> </div> <button class="text-sm text-accent hover:text-secondary"> <i class="fas fa-plus"></i> Repor </button> </div> <div class="flex items-center justify-between p-3 bg-red-50 rounded-lg"> <div class="flex items-center"> <i class="fas fa-beer text-red-500 mr-3"></i> <div> <h4 class="font-medium">Cerveja Artesanal</h4> <p class="text-sm text-gray-500">Estoque: 8 unidades</p> </div> </div> <button class="text-sm text-accent hover:text-secondary"> <i class="fas fa-plus"></i> Repor </button> </div> </div> </div> </div> </div> <!-- Waiters Content (Hidden by default) --> <div id="waiters-content" class="hidden"> <div class="flex justify-between items-center mb-6"> <h1 class="text-2xl font-semibold">Gerenciamento de Garçons</h1> <button id="add-waiter-btn" class="bg-accent hover:bg-secondary text-white px-4 py-2 rounded-lg flex items-center"> <i class="fas fa-plus mr-2"></i> Adicionar Garçom </button> </div> <!-- Add Waiter Modal (Hidden) --> <div id="waiter-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> <div class="bg-white rounded-lg p-6 w-full max-w-md"> <div class="flex justify-between items-center mb-4"> <h3 class="text-lg font-semibold">Adicionar Novo Garçom</h3> <button id="close-waiter-modal" class="text-gray-500 hover:text-gray-700"> <i class="fas fa-times"></i> </button> </div> <form id="waiter-form" class="space-y-4"> <div> <label for="waiter-name" class="block text-sm font-medium text-gray-700">Nome Completo</label> <input type="text" id="waiter-name" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> <div> <label for="waiter-email" class="block text-sm font-medium text-gray-700">Email</label> <input type="email" id="waiter-email" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> <div> <label for="waiter-phone" class="block text-sm font-medium text-gray-700">Telefone</label> <input type="tel" id="waiter-phone" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> <div class="flex justify-end space-x-3"> <button type="button" id="cancel-waiter" class="bg-gray-200 hover:bg-gray-300 text-gray-800 px-4 py-2 rounded-lg"> Cancelar </button> <button type="submit" class="bg-accent hover:bg-secondary text-white px-4 py-2 rounded-lg"> Salvar </button> </div> </form> </div> </div> <div class="bg-white shadow rounded-lg overflow-hidden"> <table class="min-w-full divide-y divide-gray-200"> <thead class="bg-gray-50"> <tr> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Foto</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Nome</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Contato</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Vendas</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Ações</th> </tr> </thead> <tbody class="bg-white divide-y divide-gray-200" id="waiters-list"> <!-- Waiters will be added here dynamically --> </tbody> </table> </div> </div> <!-- Inventory Content (Hidden by default) --> <div id="inventory-content" class="hidden"> <div class="flex justify-between items-center mb-6"> <h1 class="text-2xl font-semibold">Controle de Estoque</h1> <button id="add-item-btn" class="bg-accent hover:bg-secondary text-white px-4 py-2 rounded-lg flex items-center"> <i class="fas fa-plus mr-2"></i> Adicionar Item </button> </div> <!-- Add Inventory Item Modal (Hidden) --> <div id="inventory-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> <div class="bg-white rounded-lg p-6 w-full max-w-md"> <div class="flex justify-between items-center mb-4"> <h3 class="text-lg font-semibold">Adicionar Novo Item</h3> <button id="close-inventory-modal" class="text-gray-500 hover:text-gray-700"> <i class="fas fa-times"></i> </button> </div> <form id="inventory-form" class="space-y-4"> <div> <label for="item-name" class="block text-sm font-medium text-gray-700">Nome do Produto</label> <input type="text" id="item-name" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> <div> <label for="item-category" class="block text-sm font-medium text-gray-700">Categoria</label> <select id="item-category" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> <option value="bebidas">Bebidas</option> <option value="carnes">Carnes</option> <option value="laticinios">Laticínios</option> <option value="hortifruti">Hortifrúti</option> <option value="outros">Outros</option> </select> </div> <div> <label for="item-quantity" class="block text-sm font-medium text-gray-700">Quantidade</label> <input type="number" id="item-quantity" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> <div> <label for="item-unit" class="block text-sm font-medium text-gray-700">Unidade de Medida</label> <select id="item-unit" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> <option value="unidades">Unidades</option> <option value="litros">Litros</option> <option value="kg">Quilogramas</option> <option value="gramas">Gramas</option> </select> </div> <div> <label for="item-min-stock" class="block text-sm font-medium text-gray-700">Estoque Mínimo</label> <input type="number" id="item-min-stock" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> <div class="flex justify-end space-x-3"> <button type="button" id="cancel-inventory" class="bg-gray-200 hover:bg-gray-300 text-gray-800 px-4 py-2 rounded-lg"> Cancelar </button> <button type="submit" class="bg-accent hover:bg-secondary text-white px-4 py-2 rounded-lg"> Salvar </button> </div> </form> </div> </div> <div class="flex mb-6"> <div class="relative w-full max-w-md mr-4"> <i class="fas fa-search absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"></i> <input type="text" placeholder="Pesquisar itens..." id="search-inventory" class="bg-gray-100 pl-10 pr-4 py-2 rounded-lg w-full focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent"> </div> <select id="filter-category" class="bg-gray-100 border border-gray-300 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent"> <option value="all">Todas categorias</option> <option value="bebidas">Bebidas</option> <option value="carnes">Carnes</option> <option value="laticinios">Laticínios</option> <option value="hortifruti">Hortifrúti</option> <option value="outros">Outros</option> </select> </div> <div class="bg-white shadow rounded-lg overflow-hidden"> <table class="min-w-full divide-y divide-gray-200"> <thead class="bg-gray-50"> <tr> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Produto</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Categoria</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Quantidade</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Estoque Mín.</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Ações</th> </tr> </thead> <tbody class="bg-white divide-y divide-gray-200" id="inventory-list"> <!-- Items will be added here dynamically --> </tbody> </table> </div> </div> <!-- Sales Content (Hidden by default) --> <div id="sales-content" class="hidden"> <div class="flex justify-between items-center mb-6"> <h1 class="text-2xl font-semibold">Registro de Vendas</h1> <button id="add-sale-btn" class="bg-accent hover:bg-secondary text-white px-4 py-2 rounded-lg flex items-center"> <i class="fas fa-plus mr-2"></i> Nova Venda </button> </div> <!-- Add Sale Modal (Hidden) --> <div id="sale-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> <div class="bg-white rounded-lg p-6 w-full max-w-lg"> <div class="flex justify-between items-center mb-4"> <h3 class="text-lg font-semibold">Registrar Nova Venda</h3> <button id="close-sale-modal" class="text-gray-500 hover:text-gray-700"> <i class="fas fa-times"></i> </button> </div> <form id="sale-form" class="space-y-4"> <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> <div> <label for="sale-waiter" class="block text-sm font-medium text-gray-700">Garçom</label> <select id="sale-waiter" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> <!-- Waiters will be added here dynamically --> </select> </div> <div> <label for="sale-table" class="block text-sm font-medium text-gray-700">Número da Mesa</label> <input type="number" id="sale-table" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> </div> <div> <label class="block text-sm font-medium text-gray-700">Itens da Venda</label> <div class="mt-1 space-y-3" id="sale-items-container"> <div class="sale-item flex items-center space-x-3"> <div class="flex-grow"> <select class="item-select w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> <!-- Items will be added here dynamically --> </select> </div> <div class="w-24"> <input type="number" placeholder="Qtd" class="item-quantity w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> <div class="w-24"> <input type="number" placeholder="Preço" class="item-price w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> <button type="button" class="remove-item-btn text-red-500 hover:text-red-700"> <i class="fas fa-trash"></i> </button> </div> </div> <button type="button" id="add-item-btn-sale" class="mt-2 text-sm text-accent hover:text-secondary"> <i class="fas fa-plus mr-1"></i> Adicionar Item </button> </div> <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> <div> <label for="payment-method" class="block text-sm font-medium text-gray-700">Método de Pagamento</label> <select id="payment-method" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> <option value="dinheiro">Dinheiro</option> <option value="cartao_debito">Cartão de Débito</option> <option value="cartao_credito">Cartão de Crédito</option> <option value="pix">PIX</option> </select> </div> <div> <label for="payment-status" class="block text-sm font-medium text-gray-700">Status do Pagamento</label> <select id="payment-status" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> <option value="pago">Pago</option> <option value="pendente">Pendente</option> </select> </div> </div> <div class="bg-gray-100 p-4 rounded-lg"> <div class="flex justify-between items-center mb-2"> <span class="font-medium">Total da Venda:</span> <span id="total-sale" class="font-bold">R$ 0.00</span> </div> <div class="flex justify-between items-center"> <span class="font-medium">Comissão do Garçom (10%):</span> <span id="waiter-commission" class="font-bold">R$ 0.00</span> </div> </div> <div class="flex justify-end space-x-3"> <button type="button" id="cancel-sale" class="bg-gray-200 hover:bg-gray-300 text-gray-800 px-4 py-2 rounded-lg"> Cancelar </button> <button type="submit" class="bg-accent hover:bg-secondary text-white px-4 py-2 rounded-lg"> Registrar Venda </button> </div> </form> </div> </div> <div class="bg-white shadow rounded-lg overflow-hidden"> <table class="min-w-full divide-y divide-gray-200"> <thead class="bg-gray-50"> <tr> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Data/Hora</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Garçom</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Mesa</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Valor</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Pagamento</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Ações</th> </tr> </thead> <tbody class="bg-white divide-y divide-gray-200" id="sales-list"> <!-- Sales will be added here dynamically --> </tbody> </table> </div> </div> <!-- Reports Content (Hidden by default) --> <div id="reports-content" class="hidden"> <h1 class="text-2xl font-semibold mb-6">Relatórios</h1> <div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6"> <div class="bg-white p-6 rounded-lg shadow"> <h2 class="text-lg font-semibold mb-4">Filtros</h2> <div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4"> <div> <label for="report-period" class="block text-sm font-medium text-gray-700 mb-1">Período</label> <select id="report-period" class="w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> <option value="today">Hoje</option> <option value="yesterday">Ontem</option> <option value="week">Esta semana</option> <option value="month">Este mês</option> <option value="custom">Personalizado</option> </select> </div> <div id="custom-start-container" class="hidden"> <label for="report-start" class="block text-sm font-medium text-gray-700 mb-1">Data Inicial</label> <input type="date" id="report-start" class="w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> <div id="custom-end-container" class="hidden"> <label for="report-end" class="block text-sm font-medium text-gray-700 mb-1">Data Final</label> <input type="date" id="report-end" class="w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> </div> <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> <div> <label for="report-waiter" class="block text-sm font-medium text-gray-700 mb-1">Garçom</label> <select id="report-waiter" class="w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> <option value="all">Todos os garçons</option> <!-- Waiters will be added here dynamically --> </select> </div> <div> <label for="report-type" class="block text-sm font-medium text-gray-700 mb-1">Tipo de Relatório</label> <select id="report-type" class="w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> <option value="sales">Vendas</option> <option value="stock">Estoque</option> <option value="commissions">Comissões</option> </select> </div> </div> <div class="mt-4"> <button id="generate-report" class="bg-accent hover:bg-secondary text-white px-4 py-2 rounded-lg"> Gerar Relatório </button> </div> </div> <div class="bg-white p-6 rounded-lg shadow"> <h2 class="text-lg font-semibold mb-4">Relatórios Rápidos</h2> <div class="space-y-3"> <button class="quick-report-btn w-full text-left p-3 bg-gray-100 hover:bg-gray-200 rounded-lg flex items-center justify-between"> <span>Vendas diárias de hoje</span> <i class="fas fa-chevron-right"></i> </button> <button class="quick-report-btn w-full text-left p-3 bg-gray-100 hover:bg-gray-200 rounded-lg flex items-center justify-between"> <span>Produtos em estoque baixo</span> <i class="fas fa-chevron-right"></i> </button> <button class="quick-report-btn w-full text-left p-3 bg-gray-100 hover:bg-gray-200 rounded-lg flex items-center justify-between"> <span>Comissões do mês</span> <i class="fas fa-chevron-right"></i> </button> <button class="quick-report-btn w-full text-left p-3 bg-gray-100 hover:bg-gray-200 rounded-lg flex items-center justify-between"> <span>Melhores garçons</span> <i class="fas fa-chevron-right"></i> </button> </div> </div> </div> <div id="report-results" class="bg-white p-6 rounded-lg shadow hidden"> <div class="flex justify-between items-center mb-4"> <h2 id="report-title" class="text-lg font-semibold">Relatório de Vendas</h2> <div class="flex space-x-2"> <button class="print-report-btn bg-gray-200 hover:bg-gray-300 text-gray-800 px-3 py-1 rounded-lg flex items-center"> <i class="fas fa-print mr-1"></i> Imprimir </button> <button class="export-report-btn bg-gray-200 hover:bg-gray-300 text-gray-800 px-3 py-1 rounded-lg flex items-center"> <i class="fas fa-file-export mr-1"></i> Exportar </button> </div> </div> <div id="report-period-info" class="text-sm text-gray-600 mb-4">Período: Hoje (22/05/2023)</div> <div class="overflow-x-auto"> <table class="min-w-full divide-y divide-gray-200"> <thead class="bg-gray-50"> <tr> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Data</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Garçom</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Vendas (R$)</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Comissão (10%)</th> </tr> </thead> <tbody class="bg-white divide-y divide-gray-200"> <tr> <td class="px-6 py-4 whitespace-nowrap">22/05/2023</td> <td class="px-6 py-4 whitespace-nowrap">Carlos Silva</td> <td class="px-6 py-4 whitespace-nowrap">R$ 745.50</td> <td class="px-6 py-4 whitespace-nowrap">R$ 74.55</td> </tr> <tr> <td class="px-6 py-4 whitespace-nowrap">22/05/2023</td> <td class="px-6 py-4 whitespace-nowrap">Ana Paula</td> <td class="px-6 py-4 whitespace-nowrap">R$ 532.75</td> <td class="px-6 py-4 whitespace-nowrap">R$ 53.28</td> </tr> <tr> <td class="px-6 py-4 whitespace-nowrap">22/05/2023</td> <td class="px-6 py-4 whitespace-nowrap">Marcos Rocha</td> <td class="px-6 py-4 whitespace-nowrap">R$ 420.00</td> <td class="px-6 py-4 whitespace-nowrap">R$ 42.00</td> </tr> <tr class="bg-gray-100 font-bold"> <td class="px-6 py-4 whitespace-nowrap">Total</td> <td class="px-6 py-4 whitespace-nowrap">3 garçons</td> <td class="px-6 py-4 whitespace-nowrap">R$ 1,698.25</td> <td class="px-6 py-4 whitespace-nowrap">R$ 169.83</td> </tr> </tbody> </table> </div> <div class="mt-6"> <h3 class="text-md font-semibold mb-2">Resumo</h3> <div class="p-4 bg-blue-50 rounded-lg text-sm"> <p class="mb-1">- Total de vendas: R$ 1,698.25</p> <p class="mb-1">- Número de garçons: 3</p> <p class="mb-1">- Média de vendas por garçom: R$ 566.08</p> <p>- Melhor desempenho: Carlos Silva com R$ 745.50 em vendas</p> </div> </div> </div> </div> </div> </div> </div> <script> // Sample data // waiters , termos const waiters = [ { id: 1, name: "Carlos Silva", email: "carlos@example.com", phone: "(11) 98765-4321", sales: 8, totalSales: 1245.50, photo: "Carlos+S" }, { id: 2, name: "Ana Paula", email: "ana@example.com", phone: "(11) 98765-1234", sales: 12, totalSales: 1857.25, photo: "Ana+P" }, { id: 3, name: "Marcos Rocha", email: "marcos@example.com", phone: "(11) 98765-5678", sales: 6, totalSales: 890.00, photo: "Marcos+R" }, { id: 4, name: "Joana Santos", email: "joana@example.com", phone: "(11) 98765-8765", sales: 9, totalSales: 1340.75, photo: "Joana+S" } ]; // inventárioItens const inventoryItems = [ { id: 1, name: "Vinho Tinto", category: "bebidas", quantity: 2, unit: "unidades", minStock: 5, status: "low" }, { id: 2, name: "Picanha", category: "carnes", quantity: 3, unit: "kg", minStock: 5, status: "low" }, { id: 3, name: "Queijo Minas", category: "laticinios", quantity: 5, unit: "kg", minStock: 3, status: "ok" }, { id: 4, name: "Cerveja Artesanal", category: "bebidas", quantity: 8, unit: "unidades", minStock: 10, status: "ok" }, { id: 5, name: "Alface", category: "hortifruti", quantity: 15, unit: "unidades", minStock: 8, status: "ok" }, { id: 6, name: "Filé Mignon", category: "carnes", quantity: 7, unit: "kg", minStock: 5, status: "ok" } ]; // vendas const sales = [ { id: 1, date: "22/05/2023 14:30", waiter: "Carlos Silva", table: 5, items: ["Picanha", "Cerveja"], total: 120.50, paymentMethod: "cartao_credito", status: "pago", waiterCommission: 12.05 }, { id: 2, date: "22/05/2023 15:15", waiter: "Ana Paula", table: 12, items: ["Filé Mignon", "Vinho Tinto"], total: 85.75, paymentMethod: "dinheiro", status: "pendente", waiterCommission: 8.58 }, { id: 3, date: "22/05/2023 18:45", waiter: "Marcos Rocha", table: 8, items: ["Picanha", "Queijo Minas", "Vinho Tinto"], total: 210.00, paymentMethod: "pix", status: "pago", waiterCommission: 21.00 }, { id: 4, date: "22/05/2023 19:30", waiter: "Joana Santos", table: 3, items: ["Alface", "Queijo Minas"], total: 45.20, paymentMethod: "cartao_debito", status: "pago", waiterCommission: 4.52 } ]; // DOM Elements // eSidebarBtn = alternarBotão da barra lateral / toggle-sidebar = barra lateral de alternância const toggleSidebarBtn = document.getElementById('toggle-sidebar'); const sidebar = document.getElementById('sidebar'); const menuItems = document.querySelectorAll('.menu-item'); const contentSections = { dashboard: document.getElementById('dashboard-content'), waiters: document.getElementById('waiters-content'), inventory: document.getElementById('inventory-content'), sales: document.getElementById('sales-content'), reports: document.getElementById('reports-content') }; // Waiter Management const addWaiterBtn = document.getElementById('add-waiter-btn'); const waiterModal = document.getElementById('waiter-modal'); const closeWaiterModal = document.getElementById('close-waiter-modal'); const cancelWaiter = document.getElementById('cancel-waiter'); const waiterForm = document.getElementById('waiter-form'); const waitersList = document.getElementById('waiters-list'); // Inventory Management const addItemBtn = document.getElementById('add-item-btn'); const inventoryModal = document.getElementById('inventory-modal'); const closeInventoryModal = document.getElementById('close-inventory-modal'); const cancelInventory = document.getElementById('cancel-inventory'); const inventoryForm = document.getElementById('inventory-form'); const inventoryList = document.getElementById('inventory-list'); const searchInventory = document.getElementById('search-inventory'); const filterCategory = document.getElementById('filter-category'); // Sales Management const addSaleBtn = document.getElementById('add-sale-btn'); const saleModal = document.getElementById('sale-modal'); const closeSaleModal = document.getElementById('close-sale-modal'); const cancelSale = document.getElementById('cancel-sale'); const saleForm = document.getElementById('sale-form'); const salesList = document.getElementById('sales-list'); const saleItemsContainer = document.getElementById('sale-items-container'); const addItemBtnSale = document.getElementById('add-item-btn-sale'); const totalSaleElement = document.getElementById('total-sale'); const waiterCommissionElement = document.getElementById('waiter-commission'); // Reports const reportPeriod = document.getElementById('report-period'); const customStartContainer = document.getElementById('custom-start-container'); const customEndContainer = document.getElementById('custom-end-container'); const generateReportBtn = document.getElementById('generate-report'); const reportResults = document.getElementById('report-results'); const quickReportBtns = document.querySelectorAll('.quick-report-btn'); // Toggle sidebar toggleSidebarBtn.addEventListener('click', () => { sidebar.classList.toggle('sidebar-collapsed'); }); // Navigation menu menuItems.forEach(item => { item.addEventListener('click', (e) => { // Prevent default if it's an anchor if (item.getAttribute('href')?.startsWith('#')) { e.preventDefault(); } // Remove active class from all menu items menuItems.forEach(i => { i.parentElement.querySelector('a').classList.remove('active-menu'); }); // Add active class to clicked item item.classList.add('active-menu'); // Get target section from href const target = item.getAttribute('href')?.replace('#', ''); // Hide all content sections Object.values(contentSections).forEach(section => { section.classList.add('hidden'); }); // Show target section if (target && contentSections[target]) { contentSections[target].classList.remove('hidden'); } }); }); // Initialize default view document.querySelector('.active-menu').click(); // Waiters Management addWaiterBtn.addEventListener('click', () => { waiterModal.classList.remove('hidden'); }); closeWaiterModal.addEventListener('click', () => { waiterModal.classList.add('hidden'); }); cancelWaiter.addEventListener('click', () => { waiterModal.classList.add('hidden'); }); waiterForm.addEventListener('submit', (e) => { e.preventDefault(); const name = document.getElementById('waiter-name').value; const email = document.getElementById('waiter-email').value; const phone = document.getElementById('waiter-phone').value; if (name && email && phone) { // Generate a random ID (in a real app, this would come from the backend) const id = waiters.length > 0 ? Math.max(...waiters.map(w => w.id)) + 1 : 1; const newWaiter = { id, name, email, phone, sales: 0, totalSales: 0, photo: name.split(' ').join('+') }; waiters.push(newWaiter); renderWaiters(); waiterForm.reset(); waiterModal.classList.add('hidden'); } }); function renderWaiters() { waitersList.innerHTML = ''; waiters.forEach(waiter => { const row = document.createElement('tr'); row.innerHTML = ` <td class="px-6 py-4 whitespace-nowrap"> <div class="flex items-center"> <div class="flex-shrink-0 h-10 w-10"> <img class="h-10 w-10 rounded-full" src="https://ui-avatars.com/api/?name=${waiter.photo}&background=4299e1&color=fff" alt="${waiter.name}"> </div> </div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm font-medium text-gray-900">${waiter.name}</div> <div class="text-sm text-gray-500">${waiter.email}</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm text-gray-900">${waiter.phone}</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm text-gray-900">${waiter.sales} vendas</div> <div class="text-sm text-gray-500">R$ ${waiter.totalSales.toFixed(2)}</div> </td> <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> <button class="text-accent hover:text-secondary mr-2 edit-waiter" data-id="${waiter.id}"> <i class="fas fa-edit"></i> </button> <button class="text-red-500 hover:text-red-700 delete-waiter" data-id="${waiter.id}"> <i class="fas fa-trash"></i> </button> </td> `; waitersList.appendChild(row); }); // Add event listeners for edit and delete buttons document.querySelectorAll('.edit-waiter').forEach(btn => { btn.addEventListener('click', editWaiter); }); document.querySelectorAll('.delete-waiter').forEach(btn => { btn.addEventListener('click', deleteWaiter); }); } function editWaiter(e) { const waiterId = parseInt(e.target.closest('button').getAttribute('data-id')); const waiter = waiters.find(w => w.id === waiterId); if (waiter) { document.getElementById('waiter-name').value = waiter.name; document.getElementById('waiter-email').value = waiter.email; document.getElementById('waiter-phone').value = waiter.phone; // In a real app, you would set a flag for editing and handle the update waiterModal.classList.remove('hidden'); } } function deleteWaiter(e) { if (confirm('Tem certeza que deseja remover este garçom?')) { const waiterId = parseInt(e.target.closest('button').getAttribute('data-id')); const index = waiters.findIndex(w => w.id === waiterId); if (index !== -1) { waiters.splice(index, 1); renderWaiters(); } } } // Inventory Management addItemBtn.addEventListener('click', () => { inventoryModal.classList.remove('hidden'); }); closeInventoryModal.addEventListener('click', () => { inventoryModal.classList.add('hidden'); }); cancelInventory.addEventListener('click', () => { inventoryModal.classList.add('hidden'); }); inventoryForm.addEventListener('submit', (e) => { e.preventDefault(); const name = document.getElementById('item-name').value; const category = document.getElementById('item-category').value; const quantity = parseInt(document.getElementById('item-quantity').value); const unit = document.getElementById('item-unit').value; const minStock = parseInt(document.getElementById('item-min-stock').value); if (name && !isNaN(quantity) && !isNaN(minStock)) { // Generate a random ID (in a real app, this would come from the backend) const id = inventoryItems.length > 0 ? Math.max(...inventoryItems.map(i => i.id)) + 1 : 1; const newItem = { id, name, category, quantity, unit, minStock, status: quantity <= minStock ? 'low' : 'ok' }; inventoryItems.push(newItem); renderInventoryItems(); inventoryForm.reset(); inventoryModal.classList.add('hidden'); } }); searchInventory.addEventListener('input', () => { renderInventoryItems(); }); filterCategory.addEventListener('change', () => { renderInventoryItems(); }); function renderInventoryItems() { inventoryList.innerHTML = ''; const searchTerm = searchInventory.value.toLowerCase(); const categoryFilter = filterCategory.value; const filteredItems = inventoryItems.filter(item => { const matchesSearch = item.name.toLowerCase().includes(searchTerm); const matchesCategory = categoryFilter === 'all' || item.category === categoryFilter; return matchesSearch && matchesCategory; }); filteredItems.forEach(item => { const row = document.createElement('tr'); const statusClass = item.status === 'low' ? 'bg-red-100 text-red-800' : 'bg-green-100 text-green-800'; const statusText = item.status === 'low' ? 'Baixo' : 'OK'; const rowClass = item.status === 'low' ? 'low-stock' : ''; row.innerHTML = ` <td class="px-6 py-4 whitespace-nowrap ${rowClass}"> <div class="text-sm font-medium text-gray-900">${item.name}</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm text-gray-900 capitalize">${item.category}</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm text-gray-900">${item.quantity} ${item.unit}</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm text-gray-900">${item.minStock} ${item.unit}</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${statusClass}"> ${statusText} </span> </td> <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> <button class="text-accent hover:text-secondary mr-2 edit-item" data-id="${item.id}"> <i class="fas fa-edit"></i> </button> <button class="text-red-500 hover:text-red-700 delete-item" data-id="${item.id}"> <i class="fas fa-trash"></i> </button> </td> `; inventoryList.appendChild(row); }); // Add event listeners for edit and delete buttons document.querySelectorAll('.edit-item').forEach(btn => { btn.addEventListener('click', editInventoryItem); }); document.querySelectorAll('.delete-item').forEach(btn => { btn.addEventListener('click', deleteInventoryItem); }); } function editInventoryItem(e) { const itemId = parseInt(e.target.closest('button').getAttribute('data-id')); const item = inventoryItems.find(i => i.id === itemId); if (item) { document.getElementById('item-name').value = item.name; document.getElementById('item-category').value = item.category; document.getElementById('item-quantity').value = item.quantity; document.getElementById('item-unit').value = item.unit; document.getElementById('item-min-stock').value = item.minStock; // In a real app, you would set a flag for editing and handle the update inventoryModal.classList.remove('hidden'); } } function deleteInventoryItem(e) { if (confirm('Tem certeza que deseja remover este item do estoque?')) { const itemId = parseInt(e.target.closest('button').getAttribute('data-id')); const index = inventoryItems.findIndex(i => i.id === itemId); if (index !== -1) { inventoryItems.splice(index, 1); renderInventoryItems(); } } } // Sales Management addSaleBtn.addEventListener('click', () => { // Populate waiters dropdown const saleWaiterSelect = document.getElementById('sale-waiter'); saleWaiterSelect.innerHTML = '<option value="">Selecione um garçom</option>'; waiters.forEach(waiter => { const option = document.createElement('option'); option.value = waiter.id; option.textContent = waiter.name; saleWaiterSelect.appendChild(option); }); // Populate items dropdown const itemSelects = document.querySelectorAll('.item-select'); itemSelects.forEach(select => { select.innerHTML = '<option value="">Selecione um item</option>'; inventoryItems.forEach(item => { const option = document.createElement('option'); option.value = item.id; option.textContent = `${item.name} (${item.quantity} ${item.unit})`; select.appendChild(option); }); }); // Reset form document.getElementById('sale-table').value = ''; saleItemsContainer.innerHTML = ` <div class="sale-item flex items-center space-x-3"> <div class="flex-grow"> <select class="item-select w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> <option value="">Selecione um item</option> ${inventoryItems.map(item => `<option value="${item.id}">${item.name} (${item.quantity} ${item.unit})</option>`).join('')} </select> </div> <div class="w-24"> <input type="number" placeholder="Qtd" class="item-quantity w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> <div class="w-24"> <input type="number" placeholder="Preço" class="item-price w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> <button type="button" class="remove-item-btn text-red-500 hover:text-red-700"> <i class="fas fa-trash"></i> </button> </div> `; document.getElementById('payment-method').value = 'dinheiro'; document.getElementById('payment-status').value = 'pago'; totalSaleElement.textContent = 'R$ 0.00'; waiterCommissionElement.textContent = 'R$ 0.00'; // Add event listeners to new item addItemEventListeners(); saleModal.classList.remove('hidden'); }); closeSaleModal.addEventListener('click', () => { saleModal.classList.add('hidden'); }); cancelSale.addEventListener('click', () => { saleModal.classList.add('hidden'); }); addItemBtnSale.addEventListener('click', () => { const newItem = document.createElement('div'); newItem.className = 'sale-item flex items-center space-x-3 mt-3'; newItem.innerHTML = ` <div class="flex-grow"> <select class="item-select w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> <option value="">Selecione um item</option> ${inventoryItems.map(item => `<option value="${item.id}">${item.name} (${item.quantity} ${item.unit})</option>`).join('')} </select> </div> <div class="w-24"> <input type="number" placeholder="Qtd" class="item-quantity w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> <div class="w-24"> <input type="number" placeholder="Preço" class="item-price w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> <button type="button" class="remove-item-btn text-red-500 hover:text-red-700"> <i class="fas fa-trash"></i> </button> `; saleItemsContainer.appendChild(newItem); addItemEventListeners(); // Add event listener to the new remove button newItem.querySelector('.remove-item-btn').addEventListener('click', function() { saleItemsContainer.removeChild(newItem); updateSaleTotal(); }); }); function addItemEventListeners() { const itemSelects = document.querySelectorAll('.item-select'); const itemQuantities = document.querySelectorAll('.item-quantity'); const itemPrices = document.querySelectorAll('.item-price'); itemSelects.forEach(select => { select.addEventListener('change', updateSaleTotal); }); itemQuantities.forEach(input => { input.addEventListener('input', updateSaleTotal); }); itemPrices.forEach(input => { input.addEventListener('input', updateSaleTotal); }); document.querySelectorAll('.remove-item-btn').forEach(btn => { btn.addEventListener('click', function() { saleItemsContainer.removeChild(btn.closest('.sale-item')); updateSaleTotal(); }); }); } function updateSaleTotal() { let total = 0; document.querySelectorAll('.sale-item').forEach(item => { const quantity = parseFloat(item.querySelector('.item-quantity').value) || 0; const price = parseFloat(item.querySelector('.item-price').value) || 0; total += quantity * price; }); totalSaleElement.textContent = `R$ ${total.toFixed(2)}`; waiterCommissionElement.textContent = `R$ ${(total * 0.1).toFixed(2)}`; } saleForm.addEventListener('submit', (e) => { e.preventDefault(); const waiterId = parseInt(document.getElementById('sale-waiter').value); const tableNumber = document.getElementById('sale-table').value; const paymentMethod = document.getElementById('payment-method').value; const paymentStatus = document.getElementById('payment-status').value; if (waiterId && tableNumber) { // Collect items const items = []; let total = 0; document.querySelectorAll('.sale-item').forEach(item => { const itemId = parseInt(item.querySelector('.item-select').value); const quantity = parseFloat(item.querySelector('.item-quantity').value) || 0; const price = parseFloat(item.querySelector('.item-price').value) || 0; if (itemId && quantity && price) { const inventoryItem = inventoryItems.find(i => i.id === itemId); if (inventoryItem) { items.push(inventoryItem.name); total += quantity * price; // Update inventory (in a real app, this would be validated and handled by the backend) inventoryItem.quantity -= quantity; if (inventoryItem.quantity < 0) inventoryItem.quantity = 0; } } }); if (items.length > 0) { // Generate a random ID (in a real app, this would come from the backend) const saleId = sales.length > 0 ? Math.max(...sales.map(s => s.id)) + 1 : 1; const waiter = waiters.find(w => w.id === waiterId); const currentDate = new Date(); const formattedDate = `${currentDate.getDate()}/${currentDate.getMonth() + 1}/${currentDate.getFullYear()} ${currentDate.getHours()}:${currentDate.getMinutes().toString().padStart(2, '0')}`; const commission = total * 0.1; const newSale = { id: saleId, date: formattedDate, waiter: waiter.name, table: tableNumber, items: [...items], total: total, paymentMethod: paymentMethod, status: paymentStatus, waiterCommission: commission }; sales.push(newSale); // Update waiter stats if (waiter) { waiter.sales += 1; waiter.totalSales += total; } renderSales(); renderWaiters(); renderInventoryItems(); saleModal.classList.add('hidden'); } else { alert('Adicione pelo menos um item válido à venda.'); } } else { alert('Selecione um garçom e informe o número da mesa.'); } }); function renderSales() { salesList.innerHTML = ''; sales.forEach(sale => { const row = document.createElement('tr'); const statusClass = sale.status === 'pago' ? 'bg-green-100 text-green-800' : 'bg-yellow-100 text-yellow-800'; const paymentMethods = { 'dinheiro': 'Dinheiro', 'cartao_debito': 'Cartão Déb.', 'cartao_credito': 'Cartão Créd.', 'pix': 'PIX' }; row.innerHTML = ` <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm font-medium text-gray-900">${sale.date}</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm text-gray-900">${sale.waiter}</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm text-gray-900">Mesa ${sale.table}</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm text-gray-900">R$ ${sale.total.toFixed(2)}</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${statusClass}"> ${paymentMethods[sale.paymentMethod]} - ${sale.status.charAt(0).toUpperCase() + sale.status.slice(1)} </span> </td> <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> <button class="text-accent hover:text-secondary view-sale" data-id="${sale.id}"> <i class="fas fa-eye"></i> Detalhes </button> </td> `; salesList.appendChild(row); }); // Add event listeners for view buttons document.querySelectorAll('.view-sale').forEach(btn => { btn.addEventListener('click', viewSaleDetails); }); } function viewSaleDetails(e) { const saleId = parseInt(e.target.closest('button').getAttribute('data-id')); const sale = sales.find(s => s.id === saleId); if (sale) { alert(`Detalhes da Venda #${sale.id}\n\n` + `Garçom: ${sale.waiter}\n` + `Mesa: ${sale.table}\n` + `Itens: ${sale.items.join(', ')}\n` + `Total: R$ ${sale.total.toFixed(2)}\n` + `Comissão: R$ ${sale.waiterCommission.toFixed(2)} (10%)\n` + `Status: ${sale.status.charAt(0).toUpperCase() + sale.status.slice(1)}`); } } // Reports reportPeriod.addEventListener('change', () => { if (reportPeriod.value === 'custom') { customStartContainer.classList.remove('hidden'); customEndContainer.classList.remove('hidden'); } else { customStartContainer.classList.add('hidden'); customEndContainer.classList.add('hidden'); } }); generateReportBtn.addEventListener('click', () => { // In a real app, this would fetch data based on filters reportResults.classList.remove('hidden'); // Scroll to results reportResults.scrollIntoView({ behavior: 'smooth' }); }); quickReportBtns.forEach(btn => { btn.addEventListener('click', () => { const reportText = btn.textContent.trim(); // Set report title based on button text document.getElementById('report-title').textContent = reportText; // In a real app, this would fetch the appropriate data reportResults.classList.remove('hidden'); // Scroll to results reportResults.scrollIntoView({ behavior: 'smooth' }); }); }); // Initialize data renderWaiters(); renderInventoryItems(); renderSales(); </script> <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Marcos-007/sistemadeestoquevendasprestaurantesprime" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> </html> Refaça o a lógica operacional do site considerando o seguinte: As operaçoes de inserção e requisição de dados serão feitas em um banco de dados real, portanto: - Modele uma API de consulta a um banco de dados MongoDB que estará acessível localmente. Esse banco de dados conterá tabelas para o estoque, registro de contas, e o que mais for necessário. - implemente essa API no código html, fazendo com que a operação do site faça requisições a essa API - Initial Deployment
- 83.4 kB <!DOCTYPE html> <html lang="pt-BR"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Restaurant Stock Control</title> <script src="https://cdn.tailwindcss.com"></script> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> <script> /* PT: Bloco de configuração para o Tailwind CSS. 'extend' permite adicionar novas cores ao tema padrão sem sobrescrevê-lo. Isso permite o uso de classes como 'bg-primary', 'text-accent', etc. EN: Configuration block for Tailwind CSS. 'extend' allows adding new colors to the default theme without overwriting it. This enables the use of classes like 'bg-primary', 'text-accent', etc. */ // Configuração personalizada do Tailwind CSS tailwind.config = { theme: { extend: { colors: { // Cor primária (azul escuro) primary: '#1a365d', // Cor secundária (azul médio) secondary: '#2c5282', // Cor de destaque (azul claro) accent: '#4299e1', // Cor para mensagens de sucesso (verde) success: '#48bb78', // Cor para alertas ou avisos (laranja) warning: '#ed8936', // Cor para mensagens de erro ou perigo (vermelho) danger: '#e53e3e', } } } } </script> <style> /* PT: Estilos para a barra lateral quando estiver recolhida. EN: Styles for the sidebar when it is collapsed. */ .sidebar-collapsed { width: 80px; /* PT: Largura reduzida. / EN: Reduced width. */ overflow: hidden; /* PT: Esconde o conteúdo que transborda. / EN: Hides overflowing content. */ } /* PT: Esconde o texto dos itens de navegação quando a barra lateral está recolhida. EN: Hides the navigation item text when the sidebar is collapsed. */ .sidebar-collapsed .nav-text { display: none; } /* PT: Esconde o texto do logo quando a barra lateral está recolhida. EN: Hides the logo text when the sidebar is collapsed. */ .sidebar-collapsed .logo-text { display: none; } /* PT: Centraliza o conteúdo (ícones) dos itens de menu quando a barra lateral está recolhida. EN: Centers the content (icons) of menu items when the sidebar is collapsed. */ .sidebar-collapsed .menu-item { justify-content: center; } /* PT: Estilo para o item de menu que está ativo/selecionado. EN: Style for the active/selected menu item. */ .active-menu { background-color: rgba(66, 153, 225, 0.1); /* PT: Fundo levemente colorido. / EN: Slightly colored background. */ border-left: 4px solid #4299e1; /* PT: Borda à esquerda para indicar seleção. / EN: Left border to indicate selection. */ } /* PT: Classe para aplicar a animação 'pulse' em itens com baixo estoque. EN: Class to apply the 'pulse' animation to low-stock items. */ .low-stock { animation: pulse 2s infinite; /* PT: Nome da animação, duração, repetição infinita. / EN: Animation name, duration, infinite repetition. */ } /* PT: Define a animação 'pulse'. Cria um efeito de brilho sutil alterando a sombra do elemento. EN: Defines the 'pulse' animation. It creates a subtle glow effect by changing the element's box-shadow. */ /* Animação personalizada chamada 'pulse' */ @keyframes pulse { /* No início (0%) a sombra ao redor é visível e pequena */ 0% { box-shadow: 0 0 0 0 rgba(234, 88, 12, 0.4); /* sombra laranja suave */ } /* Aos 70% da animação, a sombra se expande e desaparece */ 70% { box-shadow: 0 0 0 10px rgba(234, 88, 12, 0); /* sombra expandida e totalmente transparente */ } /* Ao final da animação (100%), a sombra volta ao estado inicial invisível */ 100% { box-shadow: 0 0 0 0 rgba(234, 88, 12, 0); /* sombra recolhida e invisível */ } } </style> </head> <!-- Define o corpo da página com fundo cinza claro e fonte sans-serif --> <body class="bg-gray-100 font-sans"> <!-- Container principal usando Flex para dividir tela em sidebar e conteúdo --> <div class="flex h-screen overflow-hidden"> <!-- Sidebar lateral fixa --> <div id="sidebar" class="bg-primary text-white flex flex-col h-full transition-all duration-300 ease-in-out"> <!-- Cabeçalho da sidebar com logo e botão de colapsar --> <div class="p-4 flex items-center justify-between border-b border-secondary"> <!-- Logo e nome do sistema --> <div class="flex items-center"> <i class="fas fa-utensils text-2xl text-accent mr-3"></i> <!-- Ícone de talher (logo) --> <span class="logo-text text-xl font-bold">RestoControl</span> <!-- Nome da aplicação --> </div> <!-- Botão para alternar visibilidade da sidebar --> <button id="toggle-sidebar" class="text-gray-300 hover:text-white"> <i class="fas fa-bars"></i> <!-- Ícone de menu/barras --> </button> </div> <!-- Área principal do menu de navegação --> <div class="flex-1 overflow-y-auto mt-4"> <!-- Seção "Menu" com título --> <div class="px-4 py-2"> <div class="text-xs uppercase tracking-wider text-gray-400 sidebar-collapsed:text-center nav-text"> Menu </div> </div> <!-- Lista de itens do menu --> <ul> <!-- Item: Dashboard --> <li> <a href="#dashboard" class="menu-item flex items-center px-4 py-3 text-gray-300 hover:bg-secondary hover:text-white transition-colors duration-200 active-menu"> <i class="fas fa-tachometer-alt mr-3"></i> <span class="nav-text">Dashboard</span> </a> </li> <!-- Item: Garçons --> <li> <a href="#waiters" class="menu-item flex items-center px-4 py-3 text-gray-300 hover:bg-secondary hover:text-white transition-colors duration-200"> <i class="fas fa-user-tie mr-3"></i> <span class="nav-text">Garçons</span> </a> </li> <!-- Item: Estoque --> <li> <a href="#inventory" class="menu-item flex items-center px-4 py-3 text-gray-300 hover:bg-secondary hover:text-white transition-colors duration-200"> <i class="fas fa-boxes mr-3"></i> <span class="nav-text">Estoque</span> </a> </li> <!-- Item: Vendas --> <li> <a href="#sales" class="menu-item flex items-center px-4 py-3 text-gray-300 hover:bg-secondary hover:text-white transition-colors duration-200"> <i class="fas fa-cash-register mr-3"></i> <span class="nav-text">Vendas</span> </a> </li> <!-- Item: Relatórios --> <li> <a href="#reports" class="menu-item flex items-center px-4 py-3 text-gray-300 hover:bg-secondary hover:text-white transition-colors duration-200"> <i class="fas fa-chart-bar mr-3"></i> <span class="nav-text">Relatórios</span> </a> </li> </ul> </div> <!-- Rodapé da sidebar com informações do usuário logado --> <div class="p-4 border-t border-secondary"> <div class="flex items-center"> <!-- Avatar do usuário (imagem gerada por URL) --> <img src="https://ui-avatars.com/api/?name=Admin&background=4299e1&color=fff" alt="User" class="rounded-full w-10 h-10"> <!-- Nome e e-mail do usuário --> <div class="ml-3 nav-text"> <div class="text-sm font-medium">Admin</div> <div class="text-xs text-gray-400">gerente@resto.com</div> </div> <!-- Botão de logout (ícone de sair) --> <button class="ml-auto text-gray-400 hover:text-white nav-text"> <i class="fas fa-sign-out-alt"></i> </button> </div> </div> </div> <!-- Main Content --> <div class="flex-1 overflow-auto"> <div class="bg-white shadow-sm"> <div class="flex justify-between items-center p-4"> <h1 class="text-2xl font-semibold text-gray-800">Painel de Controle</h1> <div class="flex items-center space-x-4"> <div class="relative"> <i class="fas fa-search absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"></i> <input type="text" placeholder="Pesquisar..." class="bg-gray-100 pl-10 pr-4 py-2 rounded-lg focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent"> </div> <button class="text-gray-600 hover:text-gray-800 relative"> <i class="fas fa-bell text-xl"></i> <span class="absolute top-0 right-0 inline-block w-3 h-3 bg-danger rounded-full"></span> </button> </div> </div> </div> <div id="content-area" class="p-6"> <!-- Dashboard Content --> <div id="dashboard-content"> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-6"> <div class="bg-white rounded-lg shadow p-6"> <div class="flex items-center"> <div class="p-3 rounded-full bg-blue-100 text-accent"> <i class="fas fa-cash-register text-2xl"></i> </div> <div class="ml-4"> <span class="text-gray-500">Vendas Hoje</span> <h3 class="text-2xl font-bold">R$ 2,450.00</h3> </div> </div> </div> <div class="bg-white rounded-lg shadow p-6"> <div class="flex items-center"> <div class="p-3 rounded-full bg-green-100 text-success"> <i class="fas fa-user-tie text-2xl"></i> </div> <div class="ml-4"> <span class="text-gray-500">Total Garçons</span> <h3 class="text-2xl font-bold">8</h3> </div> </div> </div> <div class="bg-white rounded-lg shadow p-6"> <div class="flex items-center"> <div class="p-3 rounded-full bg-yellow-100 text-warning"> <i class="fas fa-exclamation-triangle text-2xl"></i> </div> <div class="ml-4"> <span class="text-gray-500">Produtos em Falta</span> <h3 class="text-2xl font-bold">4</h3> </div> </div> </div> <div class="bg-white rounded-lg shadow p-6"> <div class="flex items-center"> <div class="p-3 rounded-full bg-purple-100 text-indigo-600"> <i class="fas fa-box-open text-2xl"></i> </div> <div class="ml-4"> <span class="text-gray-500">Total Produtos</span> <h3 class="text-2xl font-bold">124</h3> </div> </div> </div> </div> <div class="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-6"> <div class="bg-white rounded-lg shadow p-6 lg:col-span-2"> <div class="flex justify-between items-center mb-4"> <h2 class="text-lg font-semibold">Vendas Recentes</h2> <button class="text-sm text-accent hover:underline">Ver todas</button> </div> <div class="overflow-x-auto"> <table class="min-w-full divide-y divide-gray-200"> <thead class="bg-gray-50"> <tr> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Garçom</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Mesa</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Valor</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th> </tr> </thead> <tbody class="bg-white divide-y divide-gray-200"> <tr> <td class="px-6 py-4 whitespace-nowrap"> <div class="flex items-center"> <div class="flex-shrink-0 h-10 w-10"> <img class="h-10 w-10 rounded-full" src="https://ui-avatars.com/api/?name=Carlos+S" alt=""> </div> <div class="ml-4"> <div class="text-sm font-medium text-gray-900">Carlos Silva</div> </div> </div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm text-gray-900">Mesa 5</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm text-gray-900">R$ 120.50</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800"> Pago </span> </td> </tr> <tr> <td class="px-6 py-4 whitespace-nowrap"> <div class="flex items-center"> <div class="flex-shrink-0 h-10 w-10"> <img class="h-10 w-10 rounded-full" src="https://ui-avatars.com/api/?name=Ana+P" alt=""> </div> <div class="ml-4"> <div class="text-sm font-medium text-gray-900">Ana Paula</div> </div> </div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm text-gray-900">Mesa 12</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm text-gray-900">R$ 85.75</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-yellow-100 text-yellow-800"> Pendente </span> </td> </tr> <tr> <td class="px-6 py-4 whitespace-nowrap"> <div class="flex items-center"> <div class="flex-shrink-0 h-10 w-10"> <img class="h-10 w-10 rounded-full" src="https://ui-avatars.com/api/?name=Marcos+R" alt=""> </div> <div class="ml-4"> <div class="text-sm font-medium text-gray-900">Marcos Rocha</div> </div> </div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm text-gray-900">Mesa 8</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm text-gray-900">R$ 210.00</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800"> Pago </span> </td> </tr> </tbody> </table> </div> </div> <div class="bg-white rounded-lg shadow p-6"> <h2 class="text-lg font-semibold mb-4">Produtos em Falta</h2> <div class="space-y-4"> <div class="flex items-center justify-between p-3 bg-red-50 rounded-lg low-stock"> <div class="flex items-center"> <i class="fas fa-wine-bottle text-red-500 mr-3"></i> <div> <h4 class="font-medium">Vinho Tinto</h4> <p class="text-sm text-gray-500">Estoque: 2 unidades</p> </div> </div> <button class="text-sm text-accent hover:text-secondary"> <i class="fas fa-plus"></i> Repor </button> </div> <div class="flex items-center justify-between p-3 bg-red-50 rounded-lg low-stock"> <div class="flex items-center"> <i class="fas fa-drumstick-bite text-red-500 mr-3"></i> <div> <h4 class="font-medium">Picanha</h4> <p class="text-sm text-gray-500">Estoque: 3 kg</p> </div> </div> <button class="text-sm text-accent hover:text-secondary"> <i class="fas fa-plus"></i> Repor </button> </div> <div class="flex items-center justify-between p-3 bg-red-50 rounded-lg"> <div class="flex items-center"> <i class="fas fa-cheese text-red-500 mr-3"></i> <div> <h4 class="font-medium">Queijo Minas</h4> <p class="text-sm text-gray-500">Estoque: 5 kg</p> </div> </div> <button class="text-sm text-accent hover:text-secondary"> <i class="fas fa-plus"></i> Repor </button> </div> <div class="flex items-center justify-between p-3 bg-red-50 rounded-lg"> <div class="flex items-center"> <i class="fas fa-beer text-red-500 mr-3"></i> <div> <h4 class="font-medium">Cerveja Artesanal</h4> <p class="text-sm text-gray-500">Estoque: 8 unidades</p> </div> </div> <button class="text-sm text-accent hover:text-secondary"> <i class="fas fa-plus"></i> Repor </button> </div> </div> </div> </div> </div> <!-- Waiters Content (Hidden by default) --> <div id="waiters-content" class="hidden"> <div class="flex justify-between items-center mb-6"> <h1 class="text-2xl font-semibold">Gerenciamento de Garçons</h1> <button id="add-waiter-btn" class="bg-accent hover:bg-secondary text-white px-4 py-2 rounded-lg flex items-center"> <i class="fas fa-plus mr-2"></i> Adicionar Garçom </button> </div> <!-- Add Waiter Modal (Hidden) --> <div id="waiter-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> <div class="bg-white rounded-lg p-6 w-full max-w-md"> <div class="flex justify-between items-center mb-4"> <h3 class="text-lg font-semibold">Adicionar Novo Garçom</h3> <button id="close-waiter-modal" class="text-gray-500 hover:text-gray-700"> <i class="fas fa-times"></i> </button> </div> <form id="waiter-form" class="space-y-4"> <div> <label for="waiter-name" class="block text-sm font-medium text-gray-700">Nome Completo</label> <input type="text" id="waiter-name" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> <div> <label for="waiter-email" class="block text-sm font-medium text-gray-700">Email</label> <input type="email" id="waiter-email" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> <div> <label for="waiter-phone" class="block text-sm font-medium text-gray-700">Telefone</label> <input type="tel" id="waiter-phone" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> <div class="flex justify-end space-x-3"> <button type="button" id="cancel-waiter" class="bg-gray-200 hover:bg-gray-300 text-gray-800 px-4 py-2 rounded-lg"> Cancelar </button> <button type="submit" class="bg-accent hover:bg-secondary text-white px-4 py-2 rounded-lg"> Salvar </button> </div> </form> </div> </div> <div class="bg-white shadow rounded-lg overflow-hidden"> <table class="min-w-full divide-y divide-gray-200"> <thead class="bg-gray-50"> <tr> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Foto</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Nome</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Contato</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Vendas</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Ações</th> </tr> </thead> <tbody class="bg-white divide-y divide-gray-200" id="waiters-list"> <!-- Waiters will be added here dynamically --> </tbody> </table> </div> </div> <!-- Inventory Content (Hidden by default) --> <div id="inventory-content" class="hidden"> <div class="flex justify-between items-center mb-6"> <h1 class="text-2xl font-semibold">Controle de Estoque</h1> <button id="add-item-btn" class="bg-accent hover:bg-secondary text-white px-4 py-2 rounded-lg flex items-center"> <i class="fas fa-plus mr-2"></i> Adicionar Item </button> </div> <!-- Add Inventory Item Modal (Hidden) --> <div id="inventory-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> <div class="bg-white rounded-lg p-6 w-full max-w-md"> <div class="flex justify-between items-center mb-4"> <h3 class="text-lg font-semibold">Adicionar Novo Item</h3> <button id="close-inventory-modal" class="text-gray-500 hover:text-gray-700"> <i class="fas fa-times"></i> </button> </div> <form id="inventory-form" class="space-y-4"> <div> <label for="item-name" class="block text-sm font-medium text-gray-700">Nome do Produto</label> <input type="text" id="item-name" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> <div> <label for="item-category" class="block text-sm font-medium text-gray-700">Categoria</label> <select id="item-category" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> <option value="bebidas">Bebidas</option> <option value="carnes">Carnes</option> <option value="laticinios">Laticínios</option> <option value="hortifruti">Hortifrúti</option> <option value="outros">Outros</option> </select> </div> <div> <label for="item-quantity" class="block text-sm font-medium text-gray-700">Quantidade</label> <input type="number" id="item-quantity" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> <div> <label for="item-unit" class="block text-sm font-medium text-gray-700">Unidade de Medida</label> <select id="item-unit" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> <option value="unidades">Unidades</option> <option value="litros">Litros</option> <option value="kg">Quilogramas</option> <option value="gramas">Gramas</option> </select> </div> <div> <label for="item-min-stock" class="block text-sm font-medium text-gray-700">Estoque Mínimo</label> <input type="number" id="item-min-stock" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> <div class="flex justify-end space-x-3"> <button type="button" id="cancel-inventory" class="bg-gray-200 hover:bg-gray-300 text-gray-800 px-4 py-2 rounded-lg"> Cancelar </button> <button type="submit" class="bg-accent hover:bg-secondary text-white px-4 py-2 rounded-lg"> Salvar </button> </div> </form> </div> </div> <div class="flex mb-6"> <div class="relative w-full max-w-md mr-4"> <i class="fas fa-search absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"></i> <input type="text" placeholder="Pesquisar itens..." id="search-inventory" class="bg-gray-100 pl-10 pr-4 py-2 rounded-lg w-full focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent"> </div> <select id="filter-category" class="bg-gray-100 border border-gray-300 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent"> <option value="all">Todas categorias</option> <option value="bebidas">Bebidas</option> <option value="carnes">Carnes</option> <option value="laticinios">Laticínios</option> <option value="hortifruti">Hortifrúti</option> <option value="outros">Outros</option> </select> </div> <div class="bg-white shadow rounded-lg overflow-hidden"> <table class="min-w-full divide-y divide-gray-200"> <thead class="bg-gray-50"> <tr> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Produto</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Categoria</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Quantidade</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Estoque Mín.</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Ações</th> </tr> </thead> <tbody class="bg-white divide-y divide-gray-200" id="inventory-list"> <!-- Items will be added here dynamically --> </tbody> </table> </div> </div> <!-- Sales Content (Hidden by default) --> <div id="sales-content" class="hidden"> <div class="flex justify-between items-center mb-6"> <h1 class="text-2xl font-semibold">Registro de Vendas</h1> <button id="add-sale-btn" class="bg-accent hover:bg-secondary text-white px-4 py-2 rounded-lg flex items-center"> <i class="fas fa-plus mr-2"></i> Nova Venda </button> </div> <!-- Add Sale Modal (Hidden) --> <div id="sale-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> <div class="bg-white rounded-lg p-6 w-full max-w-lg"> <div class="flex justify-between items-center mb-4"> <h3 class="text-lg font-semibold">Registrar Nova Venda</h3> <button id="close-sale-modal" class="text-gray-500 hover:text-gray-700"> <i class="fas fa-times"></i> </button> </div> <form id="sale-form" class="space-y-4"> <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> <div> <label for="sale-waiter" class="block text-sm font-medium text-gray-700">Garçom</label> <select id="sale-waiter" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> <!-- Waiters will be added here dynamically --> </select> </div> <div> <label for="sale-table" class="block text-sm font-medium text-gray-700">Número da Mesa</label> <input type="number" id="sale-table" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> </div> <div> <label class="block text-sm font-medium text-gray-700">Itens da Venda</label> <div class="mt-1 space-y-3" id="sale-items-container"> <div class="sale-item flex items-center space-x-3"> <div class="flex-grow"> <select class="item-select w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> <!-- Items will be added here dynamically --> </select> </div> <div class="w-24"> <input type="number" placeholder="Qtd" class="item-quantity w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> <div class="w-24"> <input type="number" placeholder="Preço" class="item-price w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> <button type="button" class="remove-item-btn text-red-500 hover:text-red-700"> <i class="fas fa-trash"></i> </button> </div> </div> <button type="button" id="add-item-btn-sale" class="mt-2 text-sm text-accent hover:text-secondary"> <i class="fas fa-plus mr-1"></i> Adicionar Item </button> </div> <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> <div> <label for="payment-method" class="block text-sm font-medium text-gray-700">Método de Pagamento</label> <select id="payment-method" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> <option value="dinheiro">Dinheiro</option> <option value="cartao_debito">Cartão de Débito</option> <option value="cartao_credito">Cartão de Crédito</option> <option value="pix">PIX</option> </select> </div> <div> <label for="payment-status" class="block text-sm font-medium text-gray-700">Status do Pagamento</label> <select id="payment-status" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> <option value="pago">Pago</option> <option value="pendente">Pendente</option> </select> </div> </div> <div class="bg-gray-100 p-4 rounded-lg"> <div class="flex justify-between items-center mb-2"> <span class="font-medium">Total da Venda:</span> <span id="total-sale" class="font-bold">R$ 0.00</span> </div> <div class="flex justify-between items-center"> <span class="font-medium">Comissão do Garçom (10%):</span> <span id="waiter-commission" class="font-bold">R$ 0.00</span> </div> </div> <div class="flex justify-end space-x-3"> <button type="button" id="cancel-sale" class="bg-gray-200 hover:bg-gray-300 text-gray-800 px-4 py-2 rounded-lg"> Cancelar </button> <button type="submit" class="bg-accent hover:bg-secondary text-white px-4 py-2 rounded-lg"> Registrar Venda </button> </div> </form> </div> </div> <div class="bg-white shadow rounded-lg overflow-hidden"> <table class="min-w-full divide-y divide-gray-200"> <thead class="bg-gray-50"> <tr> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Data/Hora</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Garçom</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Mesa</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Valor</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Pagamento</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Ações</th> </tr> </thead> <tbody class="bg-white divide-y divide-gray-200" id="sales-list"> <!-- Sales will be added here dynamically --> </tbody> </table> </div> </div> <!-- Reports Content (Hidden by default) --> <div id="reports-content" class="hidden"> <h1 class="text-2xl font-semibold mb-6">Relatórios</h1> <div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6"> <div class="bg-white p-6 rounded-lg shadow"> <h2 class="text-lg font-semibold mb-4">Filtros</h2> <div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4"> <div> <label for="report-period" class="block text-sm font-medium text-gray-700 mb-1">Período</label> <select id="report-period" class="w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> <option value="today">Hoje</option> <option value="yesterday">Ontem</option> <option value="week">Esta semana</option> <option value="month">Este mês</option> <option value="custom">Personalizado</option> </select> </div> <div id="custom-start-container" class="hidden"> <label for="report-start" class="block text-sm font-medium text-gray-700 mb-1">Data Inicial</label> <input type="date" id="report-start" class="w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> <div id="custom-end-container" class="hidden"> <label for="report-end" class="block text-sm font-medium text-gray-700 mb-1">Data Final</label> <input type="date" id="report-end" class="w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> </div> <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> <div> <label for="report-waiter" class="block text-sm font-medium text-gray-700 mb-1">Garçom</label> <select id="report-waiter" class="w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> <option value="all">Todos os garçons</option> <!-- Waiters will be added here dynamically --> </select> </div> <div> <label for="report-type" class="block text-sm font-medium text-gray-700 mb-1">Tipo de Relatório</label> <select id="report-type" class="w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> <option value="sales">Vendas</option> <option value="stock">Estoque</option> <option value="commissions">Comissões</option> </select> </div> </div> <div class="mt-4"> <button id="generate-report" class="bg-accent hover:bg-secondary text-white px-4 py-2 rounded-lg"> Gerar Relatório </button> </div> </div> <div class="bg-white p-6 rounded-lg shadow"> <h2 class="text-lg font-semibold mb-4">Relatórios Rápidos</h2> <div class="space-y-3"> <button class="quick-report-btn w-full text-left p-3 bg-gray-100 hover:bg-gray-200 rounded-lg flex items-center justify-between"> <span>Vendas diárias de hoje</span> <i class="fas fa-chevron-right"></i> </button> <button class="quick-report-btn w-full text-left p-3 bg-gray-100 hover:bg-gray-200 rounded-lg flex items-center justify-between"> <span>Produtos em estoque baixo</span> <i class="fas fa-chevron-right"></i> </button> <button class="quick-report-btn w-full text-left p-3 bg-gray-100 hover:bg-gray-200 rounded-lg flex items-center justify-between"> <span>Comissões do mês</span> <i class="fas fa-chevron-right"></i> </button> <button class="quick-report-btn w-full text-left p-3 bg-gray-100 hover:bg-gray-200 rounded-lg flex items-center justify-between"> <span>Melhores garçons</span> <i class="fas fa-chevron-right"></i> </button> </div> </div> </div> <div id="report-results" class="bg-white p-6 rounded-lg shadow hidden"> <div class="flex justify-between items-center mb-4"> <h2 id="report-title" class="text-lg font-semibold">Relatório de Vendas</h2> <div class="flex space-x-2"> <button class="print-report-btn bg-gray-200 hover:bg-gray-300 text-gray-800 px-3 py-1 rounded-lg flex items-center"> <i class="fas fa-print mr-1"></i> Imprimir </button> <button class="export-report-btn bg-gray-200 hover:bg-gray-300 text-gray-800 px-3 py-1 rounded-lg flex items-center"> <i class="fas fa-file-export mr-1"></i> Exportar </button> </div> </div> <div id="report-period-info" class="text-sm text-gray-600 mb-4">Período: Hoje (22/05/2023)</div> <div class="overflow-x-auto"> <table class="min-w-full divide-y divide-gray-200"> <thead class="bg-gray-50"> <tr> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Data</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Garçom</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Vendas (R$)</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Comissão (10%)</th> </tr> </thead> <tbody class="bg-white divide-y divide-gray-200"> <tr> <td class="px-6 py-4 whitespace-nowrap">22/05/2023</td> <td class="px-6 py-4 whitespace-nowrap">Carlos Silva</td> <td class="px-6 py-4 whitespace-nowrap">R$ 745.50</td> <td class="px-6 py-4 whitespace-nowrap">R$ 74.55</td> </tr> <tr> <td class="px-6 py-4 whitespace-nowrap">22/05/2023</td> <td class="px-6 py-4 whitespace-nowrap">Ana Paula</td> <td class="px-6 py-4 whitespace-nowrap">R$ 532.75</td> <td class="px-6 py-4 whitespace-nowrap">R$ 53.28</td> </tr> <tr> <td class="px-6 py-4 whitespace-nowrap">22/05/2023</td> <td class="px-6 py-4 whitespace-nowrap">Marcos Rocha</td> <td class="px-6 py-4 whitespace-nowrap">R$ 420.00</td> <td class="px-6 py-4 whitespace-nowrap">R$ 42.00</td> </tr> <tr class="bg-gray-100 font-bold"> <td class="px-6 py-4 whitespace-nowrap">Total</td> <td class="px-6 py-4 whitespace-nowrap">3 garçons</td> <td class="px-6 py-4 whitespace-nowrap">R$ 1,698.25</td> <td class="px-6 py-4 whitespace-nowrap">R$ 169.83</td> </tr> </tbody> </table> </div> <div class="mt-6"> <h3 class="text-md font-semibold mb-2">Resumo</h3> <div class="p-4 bg-blue-50 rounded-lg text-sm"> <p class="mb-1">- Total de vendas: R$ 1,698.25</p> <p class="mb-1">- Número de garçons: 3</p> <p class="mb-1">- Média de vendas por garçom: R$ 566.08</p> <p>- Melhor desempenho: Carlos Silva com R$ 745.50 em vendas</p> </div> </div> </div> </div> </div> </div> </div> <script> // Sample data // waiters , termos const waiters = [ { id: 1, name: "Carlos Silva", email: "carlos@example.com", phone: "(11) 98765-4321", sales: 8, totalSales: 1245.50, photo: "Carlos+S" }, { id: 2, name: "Ana Paula", email: "ana@example.com", phone: "(11) 98765-1234", sales: 12, totalSales: 1857.25, photo: "Ana+P" }, { id: 3, name: "Marcos Rocha", email: "marcos@example.com", phone: "(11) 98765-5678", sales: 6, totalSales: 890.00, photo: "Marcos+R" }, { id: 4, name: "Joana Santos", email: "joana@example.com", phone: "(11) 98765-8765", sales: 9, totalSales: 1340.75, photo: "Joana+S" } ]; // inventárioItens const inventoryItems = [ { id: 1, name: "Vinho Tinto", category: "bebidas", quantity: 2, unit: "unidades", minStock: 5, status: "low" }, { id: 2, name: "Picanha", category: "carnes", quantity: 3, unit: "kg", minStock: 5, status: "low" }, { id: 3, name: "Queijo Minas", category: "laticinios", quantity: 5, unit: "kg", minStock: 3, status: "ok" }, { id: 4, name: "Cerveja Artesanal", category: "bebidas", quantity: 8, unit: "unidades", minStock: 10, status: "ok" }, { id: 5, name: "Alface", category: "hortifruti", quantity: 15, unit: "unidades", minStock: 8, status: "ok" }, { id: 6, name: "Filé Mignon", category: "carnes", quantity: 7, unit: "kg", minStock: 5, status: "ok" } ]; // vendas const sales = [ { id: 1, date: "22/05/2023 14:30", waiter: "Carlos Silva", table: 5, items: ["Picanha", "Cerveja"], total: 120.50, paymentMethod: "cartao_credito", status: "pago", waiterCommission: 12.05 }, { id: 2, date: "22/05/2023 15:15", waiter: "Ana Paula", table: 12, items: ["Filé Mignon", "Vinho Tinto"], total: 85.75, paymentMethod: "dinheiro", status: "pendente", waiterCommission: 8.58 }, { id: 3, date: "22/05/2023 18:45", waiter: "Marcos Rocha", table: 8, items: ["Picanha", "Queijo Minas", "Vinho Tinto"], total: 210.00, paymentMethod: "pix", status: "pago", waiterCommission: 21.00 }, { id: 4, date: "22/05/2023 19:30", waiter: "Joana Santos", table: 3, items: ["Alface", "Queijo Minas"], total: 45.20, paymentMethod: "cartao_debito", status: "pago", waiterCommission: 4.52 } ]; // DOM Elements // eSidebarBtn = alternarBotão da barra lateral / toggle-sidebar = barra lateral de alternância const toggleSidebarBtn = document.getElementById('toggle-sidebar'); const sidebar = document.getElementById('sidebar'); const menuItems = document.querySelectorAll('.menu-item'); const contentSections = { dashboard: document.getElementById('dashboard-content'), waiters: document.getElementById('waiters-content'), inventory: document.getElementById('inventory-content'), sales: document.getElementById('sales-content'), reports: document.getElementById('reports-content') }; // Waiter Management const addWaiterBtn = document.getElementById('add-waiter-btn'); const waiterModal = document.getElementById('waiter-modal'); const closeWaiterModal = document.getElementById('close-waiter-modal'); const cancelWaiter = document.getElementById('cancel-waiter'); const waiterForm = document.getElementById('waiter-form'); const waitersList = document.getElementById('waiters-list'); // Inventory Management const addItemBtn = document.getElementById('add-item-btn'); const inventoryModal = document.getElementById('inventory-modal'); const closeInventoryModal = document.getElementById('close-inventory-modal'); const cancelInventory = document.getElementById('cancel-inventory'); const inventoryForm = document.getElementById('inventory-form'); const inventoryList = document.getElementById('inventory-list'); const searchInventory = document.getElementById('search-inventory'); const filterCategory = document.getElementById('filter-category'); // Sales Management const addSaleBtn = document.getElementById('add-sale-btn'); const saleModal = document.getElementById('sale-modal'); const closeSaleModal = document.getElementById('close-sale-modal'); const cancelSale = document.getElementById('cancel-sale'); const saleForm = document.getElementById('sale-form'); const salesList = document.getElementById('sales-list'); const saleItemsContainer = document.getElementById('sale-items-container'); const addItemBtnSale = document.getElementById('add-item-btn-sale'); const totalSaleElement = document.getElementById('total-sale'); const waiterCommissionElement = document.getElementById('waiter-commission'); // Reports const reportPeriod = document.getElementById('report-period'); const customStartContainer = document.getElementById('custom-start-container'); const customEndContainer = document.getElementById('custom-end-container'); const generateReportBtn = document.getElementById('generate-report'); const reportResults = document.getElementById('report-results'); const quickReportBtns = document.querySelectorAll('.quick-report-btn'); // Toggle sidebar toggleSidebarBtn.addEventListener('click', () => { sidebar.classList.toggle('sidebar-collapsed'); }); // Navigation menu menuItems.forEach(item => { item.addEventListener('click', (e) => { // Prevent default if it's an anchor if (item.getAttribute('href')?.startsWith('#')) { e.preventDefault(); } // Remove active class from all menu items menuItems.forEach(i => { i.parentElement.querySelector('a').classList.remove('active-menu'); }); // Add active class to clicked item item.classList.add('active-menu'); // Get target section from href const target = item.getAttribute('href')?.replace('#', ''); // Hide all content sections Object.values(contentSections).forEach(section => { section.classList.add('hidden'); }); // Show target section if (target && contentSections[target]) { contentSections[target].classList.remove('hidden'); } }); }); // Initialize default view document.querySelector('.active-menu').click(); // Waiters Management addWaiterBtn.addEventListener('click', () => { waiterModal.classList.remove('hidden'); }); closeWaiterModal.addEventListener('click', () => { waiterModal.classList.add('hidden'); }); cancelWaiter.addEventListener('click', () => { waiterModal.classList.add('hidden'); }); waiterForm.addEventListener('submit', (e) => { e.preventDefault(); const name = document.getElementById('waiter-name').value; const email = document.getElementById('waiter-email').value; const phone = document.getElementById('waiter-phone').value; if (name && email && phone) { // Generate a random ID (in a real app, this would come from the backend) const id = waiters.length > 0 ? Math.max(...waiters.map(w => w.id)) + 1 : 1; const newWaiter = { id, name, email, phone, sales: 0, totalSales: 0, photo: name.split(' ').join('+') }; waiters.push(newWaiter); renderWaiters(); waiterForm.reset(); waiterModal.classList.add('hidden'); } }); function renderWaiters() { waitersList.innerHTML = ''; waiters.forEach(waiter => { const row = document.createElement('tr'); row.innerHTML = ` <td class="px-6 py-4 whitespace-nowrap"> <div class="flex items-center"> <div class="flex-shrink-0 h-10 w-10"> <img class="h-10 w-10 rounded-full" src="https://ui-avatars.com/api/?name=${waiter.photo}&background=4299e1&color=fff" alt="${waiter.name}"> </div> </div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm font-medium text-gray-900">${waiter.name}</div> <div class="text-sm text-gray-500">${waiter.email}</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm text-gray-900">${waiter.phone}</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm text-gray-900">${waiter.sales} vendas</div> <div class="text-sm text-gray-500">R$ ${waiter.totalSales.toFixed(2)}</div> </td> <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> <button class="text-accent hover:text-secondary mr-2 edit-waiter" data-id="${waiter.id}"> <i class="fas fa-edit"></i> </button> <button class="text-red-500 hover:text-red-700 delete-waiter" data-id="${waiter.id}"> <i class="fas fa-trash"></i> </button> </td> `; waitersList.appendChild(row); }); // Add event listeners for edit and delete buttons document.querySelectorAll('.edit-waiter').forEach(btn => { btn.addEventListener('click', editWaiter); }); document.querySelectorAll('.delete-waiter').forEach(btn => { btn.addEventListener('click', deleteWaiter); }); } function editWaiter(e) { const waiterId = parseInt(e.target.closest('button').getAttribute('data-id')); const waiter = waiters.find(w => w.id === waiterId); if (waiter) { document.getElementById('waiter-name').value = waiter.name; document.getElementById('waiter-email').value = waiter.email; document.getElementById('waiter-phone').value = waiter.phone; // In a real app, you would set a flag for editing and handle the update waiterModal.classList.remove('hidden'); } } function deleteWaiter(e) { if (confirm('Tem certeza que deseja remover este garçom?')) { const waiterId = parseInt(e.target.closest('button').getAttribute('data-id')); const index = waiters.findIndex(w => w.id === waiterId); if (index !== -1) { waiters.splice(index, 1); renderWaiters(); } } } // Inventory Management addItemBtn.addEventListener('click', () => { inventoryModal.classList.remove('hidden'); }); closeInventoryModal.addEventListener('click', () => { inventoryModal.classList.add('hidden'); }); cancelInventory.addEventListener('click', () => { inventoryModal.classList.add('hidden'); }); inventoryForm.addEventListener('submit', (e) => { e.preventDefault(); const name = document.getElementById('item-name').value; const category = document.getElementById('item-category').value; const quantity = parseInt(document.getElementById('item-quantity').value); const unit = document.getElementById('item-unit').value; const minStock = parseInt(document.getElementById('item-min-stock').value); if (name && !isNaN(quantity) && !isNaN(minStock)) { // Generate a random ID (in a real app, this would come from the backend) const id = inventoryItems.length > 0 ? Math.max(...inventoryItems.map(i => i.id)) + 1 : 1; const newItem = { id, name, category, quantity, unit, minStock, status: quantity <= minStock ? 'low' : 'ok' }; inventoryItems.push(newItem); renderInventoryItems(); inventoryForm.reset(); inventoryModal.classList.add('hidden'); } }); searchInventory.addEventListener('input', () => { renderInventoryItems(); }); filterCategory.addEventListener('change', () => { renderInventoryItems(); }); function renderInventoryItems() { inventoryList.innerHTML = ''; const searchTerm = searchInventory.value.toLowerCase(); const categoryFilter = filterCategory.value; const filteredItems = inventoryItems.filter(item => { const matchesSearch = item.name.toLowerCase().includes(searchTerm); const matchesCategory = categoryFilter === 'all' || item.category === categoryFilter; return matchesSearch && matchesCategory; }); filteredItems.forEach(item => { const row = document.createElement('tr'); const statusClass = item.status === 'low' ? 'bg-red-100 text-red-800' : 'bg-green-100 text-green-800'; const statusText = item.status === 'low' ? 'Baixo' : 'OK'; const rowClass = item.status === 'low' ? 'low-stock' : ''; row.innerHTML = ` <td class="px-6 py-4 whitespace-nowrap ${rowClass}"> <div class="text-sm font-medium text-gray-900">${item.name}</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm text-gray-900 capitalize">${item.category}</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm text-gray-900">${item.quantity} ${item.unit}</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm text-gray-900">${item.minStock} ${item.unit}</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${statusClass}"> ${statusText} </span> </td> <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> <button class="text-accent hover:text-secondary mr-2 edit-item" data-id="${item.id}"> <i class="fas fa-edit"></i> </button> <button class="text-red-500 hover:text-red-700 delete-item" data-id="${item.id}"> <i class="fas fa-trash"></i> </button> </td> `; inventoryList.appendChild(row); }); // Add event listeners for edit and delete buttons document.querySelectorAll('.edit-item').forEach(btn => { btn.addEventListener('click', editInventoryItem); }); document.querySelectorAll('.delete-item').forEach(btn => { btn.addEventListener('click', deleteInventoryItem); }); } function editInventoryItem(e) { const itemId = parseInt(e.target.closest('button').getAttribute('data-id')); const item = inventoryItems.find(i => i.id === itemId); if (item) { document.getElementById('item-name').value = item.name; document.getElementById('item-category').value = item.category; document.getElementById('item-quantity').value = item.quantity; document.getElementById('item-unit').value = item.unit; document.getElementById('item-min-stock').value = item.minStock; // In a real app, you would set a flag for editing and handle the update inventoryModal.classList.remove('hidden'); } } function deleteInventoryItem(e) { if (confirm('Tem certeza que deseja remover este item do estoque?')) { const itemId = parseInt(e.target.closest('button').getAttribute('data-id')); const index = inventoryItems.findIndex(i => i.id === itemId); if (index !== -1) { inventoryItems.splice(index, 1); renderInventoryItems(); } } } // Sales Management addSaleBtn.addEventListener('click', () => { // Populate waiters dropdown const saleWaiterSelect = document.getElementById('sale-waiter'); saleWaiterSelect.innerHTML = '<option value="">Selecione um garçom</option>'; waiters.forEach(waiter => { const option = document.createElement('option'); option.value = waiter.id; option.textContent = waiter.name; saleWaiterSelect.appendChild(option); }); // Populate items dropdown const itemSelects = document.querySelectorAll('.item-select'); itemSelects.forEach(select => { select.innerHTML = '<option value="">Selecione um item</option>'; inventoryItems.forEach(item => { const option = document.createElement('option'); option.value = item.id; option.textContent = `${item.name} (${item.quantity} ${item.unit})`; select.appendChild(option); }); }); // Reset form document.getElementById('sale-table').value = ''; saleItemsContainer.innerHTML = ` <div class="sale-item flex items-center space-x-3"> <div class="flex-grow"> <select class="item-select w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> <option value="">Selecione um item</option> ${inventoryItems.map(item => `<option value="${item.id}">${item.name} (${item.quantity} ${item.unit})</option>`).join('')} </select> </div> <div class="w-24"> <input type="number" placeholder="Qtd" class="item-quantity w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> <div class="w-24"> <input type="number" placeholder="Preço" class="item-price w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> <button type="button" class="remove-item-btn text-red-500 hover:text-red-700"> <i class="fas fa-trash"></i> </button> </div> `; document.getElementById('payment-method').value = 'dinheiro'; document.getElementById('payment-status').value = 'pago'; totalSaleElement.textContent = 'R$ 0.00'; waiterCommissionElement.textContent = 'R$ 0.00'; // Add event listeners to new item addItemEventListeners(); saleModal.classList.remove('hidden'); }); closeSaleModal.addEventListener('click', () => { saleModal.classList.add('hidden'); }); cancelSale.addEventListener('click', () => { saleModal.classList.add('hidden'); }); addItemBtnSale.addEventListener('click', () => { const newItem = document.createElement('div'); newItem.className = 'sale-item flex items-center space-x-3 mt-3'; newItem.innerHTML = ` <div class="flex-grow"> <select class="item-select w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> <option value="">Selecione um item</option> ${inventoryItems.map(item => `<option value="${item.id}">${item.name} (${item.quantity} ${item.unit})</option>`).join('')} </select> </div> <div class="w-24"> <input type="number" placeholder="Qtd" class="item-quantity w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> <div class="w-24"> <input type="number" placeholder="Preço" class="item-price w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-accent focus:border-accent"> </div> <button type="button" class="remove-item-btn text-red-500 hover:text-red-700"> <i class="fas fa-trash"></i> </button> `; saleItemsContainer.appendChild(newItem); addItemEventListeners(); // Add event listener to the new remove button newItem.querySelector('.remove-item-btn').addEventListener('click', function() { saleItemsContainer.removeChild(newItem); updateSaleTotal(); }); }); function addItemEventListeners() { const itemSelects = document.querySelectorAll('.item-select'); const itemQuantities = document.querySelectorAll('.item-quantity'); const itemPrices = document.querySelectorAll('.item-price'); itemSelects.forEach(select => { select.addEventListener('change', updateSaleTotal); }); itemQuantities.forEach(input => { input.addEventListener('input', updateSaleTotal); }); itemPrices.forEach(input => { input.addEventListener('input', updateSaleTotal); }); document.querySelectorAll('.remove-item-btn').forEach(btn => { btn.addEventListener('click', function() { saleItemsContainer.removeChild(btn.closest('.sale-item')); updateSaleTotal(); }); }); } function updateSaleTotal() { let total = 0; document.querySelectorAll('.sale-item').forEach(item => { const quantity = parseFloat(item.querySelector('.item-quantity').value) || 0; const price = parseFloat(item.querySelector('.item-price').value) || 0; total += quantity * price; }); totalSaleElement.textContent = `R$ ${total.toFixed(2)}`; waiterCommissionElement.textContent = `R$ ${(total * 0.1).toFixed(2)}`; } saleForm.addEventListener('submit', (e) => { e.preventDefault(); const waiterId = parseInt(document.getElementById('sale-waiter').value); const tableNumber = document.getElementById('sale-table').value; const paymentMethod = document.getElementById('payment-method').value; const paymentStatus = document.getElementById('payment-status').value; if (waiterId && tableNumber) { // Collect items const items = []; let total = 0; document.querySelectorAll('.sale-item').forEach(item => { const itemId = parseInt(item.querySelector('.item-select').value); const quantity = parseFloat(item.querySelector('.item-quantity').value) || 0; const price = parseFloat(item.querySelector('.item-price').value) || 0; if (itemId && quantity && price) { const inventoryItem = inventoryItems.find(i => i.id === itemId); if (inventoryItem) { items.push(inventoryItem.name); total += quantity * price; // Update inventory (in a real app, this would be validated and handled by the backend) inventoryItem.quantity -= quantity; if (inventoryItem.quantity < 0) inventoryItem.quantity = 0; } } }); if (items.length > 0) { // Generate a random ID (in a real app, this would come from the backend) const saleId = sales.length > 0 ? Math.max(...sales.map(s => s.id)) + 1 : 1; const waiter = waiters.find(w => w.id === waiterId); const currentDate = new Date(); const formattedDate = `${currentDate.getDate()}/${currentDate.getMonth() + 1}/${currentDate.getFullYear()} ${currentDate.getHours()}:${currentDate.getMinutes().toString().padStart(2, '0')}`; const commission = total * 0.1; const newSale = { id: saleId, date: formattedDate, waiter: waiter.name, table: tableNumber, items: [...items], total: total, paymentMethod: paymentMethod, status: paymentStatus, waiterCommission: commission }; sales.push(newSale); // Update waiter stats if (waiter) { waiter.sales += 1; waiter.totalSales += total; } renderSales(); renderWaiters(); renderInventoryItems(); saleModal.classList.add('hidden'); } else { alert('Adicione pelo menos um item válido à venda.'); } } else { alert('Selecione um garçom e informe o número da mesa.'); } }); function renderSales() { salesList.innerHTML = ''; sales.forEach(sale => { const row = document.createElement('tr'); const statusClass = sale.status === 'pago' ? 'bg-green-100 text-green-800' : 'bg-yellow-100 text-yellow-800'; const paymentMethods = { 'dinheiro': 'Dinheiro', 'cartao_debito': 'Cartão Déb.', 'cartao_credito': 'Cartão Créd.', 'pix': 'PIX' }; row.innerHTML = ` <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm font-medium text-gray-900">${sale.date}</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm text-gray-900">${sale.waiter}</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm text-gray-900">Mesa ${sale.table}</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <div class="text-sm text-gray-900">R$ ${sale.total.toFixed(2)}</div> </td> <td class="px-6 py-4 whitespace-nowrap"> <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${statusClass}"> ${paymentMethods[sale.paymentMethod]} - ${sale.status.charAt(0).toUpperCase() + sale.status.slice(1)} </span> </td> <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> <button class="text-accent hover:text-secondary view-sale" data-id="${sale.id}"> <i class="fas fa-eye"></i> Detalhes </button> </td> `; salesList.appendChild(row); }); // Add event listeners for view buttons document.querySelectorAll('.view-sale').forEach(btn => { btn.addEventListener('click', viewSaleDetails); }); } function viewSaleDetails(e) { const saleId = parseInt(e.target.closest('button').getAttribute('data-id')); const sale = sales.find(s => s.id === saleId); if (sale) { alert(`Detalhes da Venda #${sale.id}\n\n` + `Garçom: ${sale.waiter}\n` + `Mesa: ${sale.table}\n` + `Itens: ${sale.items.join(', ')}\n` + `Total: R$ ${sale.total.toFixed(2)}\n` + `Comissão: R$ ${sale.waiterCommission.toFixed(2)} (10%)\n` + `Status: ${sale.status.charAt(0).toUpperCase() + sale.status.slice(1)}`); } } // Reports reportPeriod.addEventListener('change', () => { if (reportPeriod.value === 'custom') { customStartContainer.classList.remove('hidden'); customEndContainer.classList.remove('hidden'); } else { customStartContainer.classList.add('hidden'); customEndContainer.classList.add('hidden'); } }); generateReportBtn.addEventListener('click', () => { // In a real app, this would fetch data based on filters reportResults.classList.remove('hidden'); // Scroll to results reportResults.scrollIntoView({ behavior: 'smooth' }); }); quickReportBtns.forEach(btn => { btn.addEventListener('click', () => { const reportText = btn.textContent.trim(); // Set report title based on button text document.getElementById('report-title').textContent = reportText; // In a real app, this would fetch the appropriate data reportResults.classList.remove('hidden'); // Scroll to results reportResults.scrollIntoView({ behavior: 'smooth' }); }); }); // Initialize data renderWaiters(); renderInventoryItems(); renderSales(); </script> <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Marcos-007/sistemadeestoquevendasprestaurantesprime" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> </html> Refaça o a lógica operacional do site considerando o seguinte: As operaçoes de inserção e requisição de dados serão feitas em um banco de dados real, portanto: - Modele uma API de consulta a um banco de dados MongoDB que estará acessível localmente. Esse banco de dados conterá tabelas para o estoque, registro de contas, e o que mais for necessário. - implemente essa API no código html, fazendo com que a operação do site faça requisições a essa API - Initial Deployment
- 388 Bytes initial commit