Spaces:
Running
Running
<html lang="pt-BR"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>MenuMaster - Gerenciador de Cardápios</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"> | |
<style> | |
.dark-mode { | |
background-color: #1a202c; | |
color: #f7fafc; | |
} | |
.light-mode { | |
background-color: #f7fafc; | |
color: #1a202c; | |
} | |
.card-dark { | |
background-color: #2d3748; | |
border-color: #4a5568; | |
} | |
.card-light { | |
background-color: #ffffff; | |
border-color: #e2e8f0; | |
} | |
.transition-all { | |
transition: all 0.3s ease; | |
} | |
.max-h-0 { | |
max-height: 0; | |
overflow: hidden; | |
} | |
.max-h-screen { | |
max-height: 1000px; | |
} | |
#imagePreview { | |
max-width: 200px; | |
max-height: 200px; | |
} | |
</style> | |
</head> | |
<body class="light-mode min-h-screen"> | |
<div class="container mx-auto px-4 py-8"> | |
<!-- Header --> | |
<header class="flex justify-between items-center mb-8"> | |
<h1 class="text-3xl font-bold text-indigo-600 dark:text-indigo-400"> | |
<i class="fas fa-utensils mr-2"></i>MenuMaster | |
</h1> | |
<div class="flex items-center space-x-4"> | |
<div class="relative"> | |
<select id="userSelect" class="bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500"> | |
<option value="">Selecione um usuário</option> | |
</select> | |
</div> | |
<button id="themeToggle" class="p-2 rounded-full bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-200"> | |
<i class="fas fa-moon dark:hidden"></i> | |
<i class="fas fa-sun hidden dark:inline"></i> | |
</button> | |
<button id="newUserBtn" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-md"> | |
<i class="fas fa-user-plus mr-1"></i> Novo Usuário | |
</button> | |
</div> | |
</header> | |
<!-- New User Modal --> | |
<div id="newUserModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> | |
<div class="bg-white dark:bg-gray-800 rounded-lg p-6 w-full max-w-md"> | |
<div class="flex justify-between items-center mb-4"> | |
<h3 class="text-xl font-semibold dark:text-white">Novo Usuário</h3> | |
<button id="closeUserModal" class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"> | |
<i class="fas fa-times"></i> | |
</button> | |
</div> | |
<div class="mb-4"> | |
<label for="userName" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Nome do Usuário</label> | |
<input type="text" id="userName" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:text-white"> | |
</div> | |
<div class="flex justify-end space-x-3"> | |
<button id="cancelUserBtn" class="px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-md text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">Cancelar</button> | |
<button id="saveUserBtn" class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700">Salvar</button> | |
</div> | |
</div> | |
</div> | |
<!-- Tabs Navigation --> | |
<div class="mb-8"> | |
<div class="flex border-b border-gray-200 dark:border-gray-700"> | |
<button data-tab="items" class="tab-btn py-4 px-6 font-medium text-sm border-b-2 border-transparent hover:text-indigo-600 dark:hover:text-indigo-400 hover:border-gray-300 dark:hover:border-gray-600 transition-all flex items-center"> | |
<i class="fas fa-cube mr-2"></i> Itens do Cardápio | |
</button> | |
<button data-tab="dishes" class="tab-btn py-4 px-6 font-medium text-sm border-b-2 border-transparent hover:text-indigo-600 dark:hover:text-indigo-400 hover:border-gray-300 dark:hover:border-gray-600 transition-all flex items-center"> | |
<i class="fas fa-utensils mr-2"></i> Pratos | |
</button> | |
<button data-tab="menu" class="tab-btn py-4 px-6 font-medium text-sm border-b-2 border-transparent hover:text-indigo-600 dark:hover:text-indigo-400 hover:border-gray-300 dark:hover:border-gray-600 transition-all flex items-center"> | |
<i class="fas fa-calendar-alt mr-2"></i> Cardápio Semanal | |
</button> | |
<button data-tab="report" class="tab-btn py-4 px-6 font-medium text-sm border-b-2 border-transparent hover:text-indigo-600 dark:hover:text-indigo-400 hover:border-gray-300 dark:hover:border-gray-600 transition-all flex items-center"> | |
<i class="fas fa-file-alt mr-2"></i> Relatório | |
</button> | |
</div> | |
</div> | |
<!-- Items Tab Content --> | |
<div id="items-tab" class="tab-content"> | |
<div class="flex justify-between items-center mb-6"> | |
<h2 class="text-2xl font-semibold dark:text-white"> | |
<i class="fas fa-cube mr-2"></i> Itens do Cardápio | |
</h2> | |
<button id="newItemBtn" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-md flex items-center"> | |
<i class="fas fa-plus mr-1"></i> Novo Item | |
</button> | |
</div> | |
<!-- Item Type Filter --> | |
<div class="mb-6 flex space-x-4"> | |
<button data-filter="all" class="filter-btn px-4 py-2 bg-indigo-600 text-white rounded-md">Todos</button> | |
<button data-filter="protein" class="filter-btn px-4 py-2 bg-indigo-100 dark:bg-indigo-900 text-indigo-800 dark:text-indigo-200 rounded-md">Proteínas</button> | |
<button data-filter="side" class="filter-btn px-4 py-2 bg-indigo-100 dark:bg-indigo-900 text-indigo-800 dark:text-indigo-200 rounded-md">Acompanhamentos</button> | |
<button data-filter="dessert" class="filter-btn px-4 py-2 bg-indigo-100 dark:bg-indigo-900 text-indigo-800 dark:text-indigo-200 rounded-md">Sobremesas</button> | |
</div> | |
<!-- Items Grid --> | |
<div id="itemsGrid" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> | |
<!-- Items will be loaded here --> | |
</div> | |
</div> | |
<!-- New Item Modal --> | |
<div id="itemModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> | |
<div class="bg-white dark:bg-gray-800 rounded-lg p-6 w-full max-w-md max-h-screen overflow-y-auto"> | |
<div class="flex justify-between items-center mb-4"> | |
<h3 class="text-xl font-semibold dark:text-white">Novo Item</h3> | |
<button id="closeItemModal" class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"> | |
<i class="fas fa-times"></i> | |
</button> | |
</div> | |
<form id="itemForm"> | |
<div class="mb-4"> | |
<label for="itemName" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Nome do Item</label> | |
<input type="text" id="itemName" required class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:text-white"> | |
</div> | |
<div class="mb-4"> | |
<label for="itemType" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Tipo</label> | |
<select id="itemType" required class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:text-white"> | |
<option value="">Selecione...</option> | |
<option value="protein">Proteína</option> | |
<option value="side">Acompanhamento</option> | |
<option value="dessert">Sobremesa</option> | |
</select> | |
</div> | |
<div class="mb-4"> | |
<label for="itemCalories" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Calorias (kcal)</label> | |
<input type="number" id="itemCalories" required class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:text-white"> | |
</div> | |
<div class="mb-4"> | |
<label for="itemQuantity" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Quantidade Disponível</label> | |
<input type="number" id="itemQuantity" required class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:text-white"> | |
</div> | |
<div class="mb-4"> | |
<label for="itemDescription" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Descrição</label> | |
<textarea id="itemDescription" rows="3" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:text-white"></textarea> | |
</div> | |
<div class="mb-4"> | |
<label for="itemImage" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Imagem</label> | |
<input type="file" id="itemImage" accept="image/*" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:text-white"> | |
<div class="mt-2"> | |
<img id="imagePreview" src="#" alt="Preview" class="hidden rounded-md"> | |
</div> | |
</div> | |
<input type="hidden" id="itemId"> | |
<div class="flex justify-end space-x-3"> | |
<button type="button" id="cancelItemBtn" class="px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-md text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">Cancelar</button> | |
<button type="submit" id="saveItemBtn" class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700">Salvar</button> | |
</div> | |
</form> | |
</div> | |
</div> | |
<!-- Dishes Tab Content --> | |
<div id="dishes-tab" class="tab-content hidden"> | |
<div class="flex justify-between items-center mb-6"> | |
<h2 class="text-2xl font-semibold dark:text-white"> | |
<i class="fas fa-utensils mr-2"></i> Pratos | |
</h2> | |
<button id="newDishBtn" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-md flex items-center"> | |
<i class="fas fa-plus mr-1"></i> Novo Prato | |
</button> | |
</div> | |
<!-- Dishes Grid --> | |
<div id="dishesGrid" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> | |
<!-- Dishes will be loaded here --> | |
</div> | |
</div> | |
<!-- New Dish Modal --> | |
<div id="dishModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> | |
<div class="bg-white dark:bg-gray-800 rounded-lg p-6 w-full max-w-2xl max-h-screen overflow-y-auto"> | |
<div class="flex justify-between items-center mb-4"> | |
<h3 class="text-xl font-semibold dark:text-white">Novo Prato</h3> | |
<button id="closeDishModal" class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"> | |
<i class="fas fa-times"></i> | |
</button> | |
</div> | |
<form id="dishForm"> | |
<div class="mb-4"> | |
<label for="dishName" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Nome do Prato</label> | |
<input type="text" id="dishName" required class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:text-white"> | |
</div> | |
<div class="mb-4"> | |
<label for="dishDescription" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Descrição</label> | |
<textarea id="dishDescription" rows="3" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:text-white"></textarea> | |
</div> | |
<div class="mb-6"> | |
<h4 class="text-lg font-medium dark:text-white mb-3">Selecione os Itens</h4> | |
<div class="mb-4"> | |
<h5 class="font-medium dark:text-white mb-2">Proteínas</h5> | |
<div id="proteinItems" class="space-y-2"> | |
<!-- Protein items will be loaded here --> | |
</div> | |
</div> | |
<div class="mb-4"> | |
<h5 class="font-medium dark:text-white mb-2">Acompanhamentos</h5> | |
<div id="sideItems" class="space-y-2"> | |
<!-- Side items will be loaded here --> | |
</div> | |
</div> | |
<div class="mb-4"> | |
<h5 class="font-medium dark:text-white mb-2">Sobremesas</h5> | |
<div id="dessertItems" class="space-y-2"> | |
<!-- Dessert items will be loaded here --> | |
</div> | |
</div> | |
</div> | |
<div class="mb-4 p-4 rounded-lg bg-indigo-50 dark:bg-indigo-900"> | |
<h4 class="font-medium dark:text-white mb-2">Resumo Nutricional</h4> | |
<div class="flex justify-between"> | |
<span class="text-gray-700 dark:text-gray-300">Calorias totais:</span> | |
<span id="totalCalories" class="font-semibold">0 kcal</span> | |
</div> | |
</div> | |
<input type="hidden" id="dishId"> | |
<div class="flex justify-end space-x-3"> | |
<button type="button" id="cancelDishBtn" class="px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-md text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">Cancelar</button> | |
<button type="submit" id="saveDishBtn" class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700">Salvar</button> | |
</div> | |
</form> | |
</div> | |
</div> | |
<!-- Menu Tab Content --> | |
<div id="menu-tab" class="tab-content hidden"> | |
<div class="flex justify-between items-center mb-6"> | |
<h2 class="text-2xl font-semibold dark:text-white"> | |
<i class="fas fa-calendar-alt mr-2"></i> Cardápio Semanal | |
</h2> | |
<button id="generateMenuBtn" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-md flex items-center"> | |
<i class="fas fa-magic mr-1"></i> Gerar Cardápio | |
</button> | |
</div> | |
<div class="overflow-x-auto"> | |
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700"> | |
<thead class="bg-gray-50 dark:bg-gray-800"> | |
<tr> | |
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Dia</th> | |
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Almoço</th> | |
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Jantar</th> | |
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Ações</th> | |
</tr> | |
</thead> | |
<tbody id="menuTableBody" class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700"> | |
<!-- Menu will be loaded here --> | |
</tbody> | |
</table> | |
</div> | |
</div> | |
<!-- Report Tab Content --> | |
<div id="report-tab" class="tab-content hidden"> | |
<div class="flex justify-between items-center mb-6"> | |
<h2 class="text-2xl font-semibold dark:text-white"> | |
<i class="fas fa-file-alt mr-2"></i> Relatório Semanal | |
</h2> | |
<button id="printReportBtn" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-md flex items-center"> | |
<i class="fas fa-print mr-1"></i> Imprimir Relatório | |
</button> | |
</div> | |
<div id="reportContent" class="bg-white dark:bg-gray-800 p-6 rounded-lg shadow"> | |
<div class="flex justify-between items-center mb-6"> | |
<div> | |
<h3 class="text-xl font-bold dark:text-white">Relatório Semanal</h3> | |
<p class="text-gray-600 dark:text-gray-400" id="reportDateRange">Semana de 01/01/2023 a 07/01/2023</p> | |
</div> | |
<div class="text-right"> | |
<h4 class="text-lg font-semibold dark:text-white">Total de Calorias</h4> | |
<p class="text-2xl font-bold text-indigo-600 dark:text-indigo-400" id="reportTotalCalories">0 kcal</p> | |
</div> | |
</div> | |
<div class="mb-8"> | |
<h4 class="text-lg font-semibold border-b pb-2 mb-4 dark:text-white dark:border-gray-700">Cardápio Semanal</h4> | |
<div id="weeklyMenuReport" class="space-y-6"> | |
<!-- Weekly menu report will be loaded here --> | |
</div> | |
</div> | |
<div> | |
<h4 class="text-lg font-semibold border-b pb-2 mb-4 dark:text-white dark:border-gray-700">Estoque de Ingredientes</h4> | |
<div class="overflow-x-auto"> | |
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700"> | |
<thead class="bg-gray-50 dark:bg-gray-700"> | |
<tr> | |
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Item</th> | |
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Tipo</th> | |
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Quantidade</th> | |
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Status</th> | |
</tr> | |
</thead> | |
<tbody id="inventoryReport" class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700"> | |
<!-- Inventory report will be loaded here --> | |
</tbody> | |
</table> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<script> | |
// Data storage | |
let users = JSON.parse(localStorage.getItem('menuMasterUsers')) || []; | |
let currentUser = null; | |
let items = []; | |
let dishes = []; | |
let weeklyMenu = []; | |
// DOM Elements | |
const themeToggle = document.getElementById('themeToggle'); | |
const userSelect = document.getElementById('userSelect'); | |
const newUserBtn = document.getElementById('newUserBtn'); | |
const newUserModal = document.getElementById('newUserModal'); | |
const closeUserModal = document.getElementById('closeUserModal'); | |
const cancelUserBtn = document.getElementById('cancelUserBtn'); | |
const saveUserBtn = document.getElementById('saveUserBtn'); | |
const userName = document.getElementById('userName'); | |
// Tab elements | |
const tabContents = document.querySelectorAll('.tab-content'); | |
const tabButtons = document.querySelectorAll('.tab-btn'); | |
// Item elements | |
const newItemBtn = document.getElementById('newItemBtn'); | |
const itemModal = document.getElementById('itemModal'); | |
const closeItemModal = document.getElementById('closeItemModal'); | |
const cancelItemBtn = document.getElementById('cancelItemBtn'); | |
const saveItemBtn = document.getElementById('saveItemBtn'); | |
const itemForm = document.getElementById('itemForm'); | |
const itemsGrid = document.getElementById('itemsGrid'); | |
const filterButtons = document.querySelectorAll('.filter-btn'); | |
const itemImage = document.getElementById('itemImage'); | |
const imagePreview = document.getElementById('imagePreview'); | |
// Dish elements | |
const newDishBtn = document.getElementById('newDishBtn'); | |
const dishModal = document.getElementById('dishModal'); | |
const closeDishModal = document.getElementById('closeDishModal'); | |
const cancelDishBtn = document.getElementById('cancelDishBtn'); | |
const saveDishBtn = document.getElementById('saveDishBtn'); | |
const dishForm = document.getElementById('dishForm'); | |
const dishesGrid = document.getElementById('dishesGrid'); | |
const proteinItems = document.getElementById('proteinItems'); | |
const sideItems = document.getElementById('sideItems'); | |
const dessertItems = document.getElementById('dessertItems'); | |
const totalCalories = document.getElementById('totalCalories'); | |
// Menu elements | |
const generateMenuBtn = document.getElementById('generateMenuBtn'); | |
const menuTableBody = document.getElementById('menuTableBody'); | |
// Report elements | |
const printReportBtn = document.getElementById('printReportBtn'); | |
const reportContent = document.getElementById('reportContent'); | |
// Initialize the app | |
document.addEventListener('DOMContentLoaded', () => { | |
loadUsers(); | |
setupEventListeners(); | |
updateUI(); | |
}); | |
// Load users from localStorage | |
function loadUsers() { | |
userSelect.innerHTML = '<option value="">Selecione um usuário</option>'; | |
users.forEach(user => { | |
const option = document.createElement('option'); | |
option.value = user.id; | |
option.textContent = user.name; | |
userSelect.appendChild(option); | |
}); | |
} | |
// Setup event listeners | |
function setupEventListeners() { | |
// Theme toggle | |
themeToggle.addEventListener('click', toggleTheme); | |
// User management | |
userSelect.addEventListener('change', (e) => { | |
currentUser = users.find(user => user.id === e.target.value) || null; | |
if (currentUser) { | |
loadUserData(); | |
} | |
updateUI(); | |
}); | |
newUserBtn.addEventListener('click', () => { | |
newUserModal.classList.remove('hidden'); | |
}); | |
closeUserModal.addEventListener('click', () => { | |
newUserModal.classList.add('hidden'); | |
}); | |
cancelUserBtn.addEventListener('click', () => { | |
newUserModal.classList.add('hidden'); | |
}); | |
saveUserBtn.addEventListener('click', addNewUser); | |
// Tab navigation | |
tabButtons.forEach(button => { | |
button.addEventListener('click', () => { | |
const tabId = button.getAttribute('data-tab'); | |
showTab(tabId); | |
}); | |
}); | |
// Item management | |
newItemBtn.addEventListener('click', () => { | |
showItemModal(); | |
}); | |
closeItemModal.addEventListener('click', () => { | |
itemModal.classList.add('hidden'); | |
}); | |
cancelItemBtn.addEventListener('click', () => { | |
itemModal.classList.add('hidden'); | |
}); | |
itemForm.addEventListener('submit', (e) => { | |
e.preventDefault(); | |
saveItem(); | |
}); | |
itemImage.addEventListener('change', (e) => { | |
const file = e.target.files[0]; | |
if (file) { | |
const reader = new FileReader(); | |
reader.onload = (event) => { | |
imagePreview.src = event.target.result; | |
imagePreview.classList.remove('hidden'); | |
}; | |
reader.readAsDataURL(file); | |
} | |
}); | |
// Filter buttons | |
filterButtons.forEach(button => { | |
button.addEventListener('click', () => { | |
const filter = button.getAttribute('data-filter'); | |
filterItems(filter); | |
}); | |
}); | |
// Dish management | |
newDishBtn.addEventListener('click', () => { | |
showDishModal(); | |
}); | |
closeDishModal.addEventListener('click', () => { | |
dishModal.classList.add('hidden'); | |
}); | |
cancelDishBtn.addEventListener('click', () => { | |
dishModal.classList.add('hidden'); | |
}); | |
dishForm.addEventListener('submit', (e) => { | |
e.preventDefault(); | |
saveDish(); | |
}); | |
// Menu management | |
generateMenuBtn.addEventListener('click', generateWeeklyMenu); | |
// Report management | |
printReportBtn.addEventListener('click', printReport); | |
} | |
// Toggle between dark and light theme | |
function toggleTheme() { | |
const body = document.body; | |
if (body.classList.contains('dark-mode')) { | |
body.classList.remove('dark-mode'); | |
body.classList.add('light-mode'); | |
} else { | |
body.classList.remove('light-mode'); | |
body.classList.add('dark-mode'); | |
} | |
} | |
// Add new user | |
function addNewUser() { | |
const name = userName.value.trim(); | |
if (name === '') return; | |
const newUser = { | |
id: Date.now().toString(), | |
name: name, | |
items: [], | |
dishes: [], | |
weeklyMenu: [] | |
}; | |
users.push(newUser); | |
localStorage.setItem('menuMasterUsers', JSON.stringify(users)); | |
userName.value = ''; | |
newUserModal.classList.add('hidden'); | |
loadUsers(); | |
// Select the new user | |
userSelect.value = newUser.id; | |
currentUser = newUser; | |
updateUI(); | |
} | |
// Load user data | |
function loadUserData() { | |
if (!currentUser) return; | |
items = currentUser.items || []; | |
dishes = currentUser.dishes || []; | |
weeklyMenu = currentUser.weeklyMenu || []; | |
renderItems(); | |
renderDishes(); | |
renderWeeklyMenu(); | |
renderReport(); | |
} | |
// Save user data | |
function saveUserData() { | |
if (!currentUser) return; | |
currentUser.items = items; | |
currentUser.dishes = dishes; | |
currentUser.weeklyMenu = weeklyMenu; | |
localStorage.setItem('menuMasterUsers', JSON.stringify(users)); | |
} | |
// Update UI based on current state | |
function updateUI() { | |
const hasUser = currentUser !== null; | |
// Enable/disable tabs based on user selection | |
tabButtons.forEach((button, index) => { | |
if (index > 0) { // Skip the first tab (items) | |
button.disabled = !hasUser; | |
} | |
}); | |
// Enable/disable buttons | |
newItemBtn.disabled = !hasUser; | |
newDishBtn.disabled = !hasUser; | |
generateMenuBtn.disabled = !hasUser; | |
printReportBtn.disabled = !hasUser; | |
// Show appropriate content | |
if (!hasUser) { | |
showTab('items'); | |
itemsGrid.innerHTML = '<div class="col-span-full text-center py-8 text-gray-500">Selecione um usuário para começar</div>'; | |
} | |
} | |
// Show selected tab | |
function showTab(tabId) { | |
tabContents.forEach(content => { | |
content.classList.add('hidden'); | |
}); | |
tabButtons.forEach(button => { | |
button.classList.remove('border-indigo-600', 'text-indigo-600', 'dark:border-indigo-400', 'dark:text-indigo-400'); | |
}); | |
document.getElementById(`${tabId}-tab`).classList.remove('hidden'); | |
document.querySelector(`[data-tab="${tabId}"]`).classList.add('border-indigo-600', 'text-indigo-600', 'dark:border-indigo-400', 'dark:text-indigo-400'); | |
} | |
// Show item modal | |
function showItemModal(item = null) { | |
if (!currentUser) return; | |
itemForm.reset(); | |
imagePreview.src = '#'; | |
imagePreview.classList.add('hidden'); | |
if (item) { | |
document.getElementById('itemId').value = item.id; | |
document.getElementById('itemName').value = item.name; | |
document.getElementById('itemType').value = item.type; | |
document.getElementById('itemCalories').value = item.calories; | |
document.getElementById('itemQuantity').value = item.quantity; | |
document.getElementById('itemDescription').value = item.description || ''; | |
if (item.image) { | |
imagePreview.src = item.image; | |
imagePreview.classList.remove('hidden'); | |
} | |
document.querySelector('#itemModal h3').textContent = 'Editar Item'; | |
} else { | |
document.getElementById('itemId').value = ''; | |
document.querySelector('#itemModal h3').textContent = 'Novo Item'; | |
} | |
itemModal.classList.remove('hidden'); | |
} | |
// Save item | |
function saveItem() { | |
const id = document.getElementById('itemId').value; | |
const name = document.getElementById('itemName').value.trim(); | |
const type = document.getElementById('itemType').value; | |
const calories = parseInt(document.getElementById('itemCalories').value); | |
const quantity = parseInt(document.getElementById('itemQuantity').value); | |
const description = document.getElementById('itemDescription').value.trim(); | |
const image = imagePreview.src !== '#' ? imagePreview.src : ''; | |
if (!name || !type || isNaN(calories) || isNaN(quantity)) return; | |
const itemData = { | |
id: id || Date.now().toString(), | |
name, | |
type, | |
calories, | |
quantity, | |
description, | |
image | |
}; | |
if (id) { | |
// Update existing item | |
const index = items.findIndex(item => item.id === id); | |
if (index !== -1) { | |
items[index] = itemData; | |
} | |
} else { | |
// Add new item | |
items.push(itemData); | |
} | |
saveUserData(); | |
renderItems(); | |
itemModal.classList.add('hidden'); | |
} | |
// Delete item | |
function deleteItem(id) { | |
if (!confirm('Tem certeza que deseja excluir este item?')) return; | |
items = items.filter(item => item.id !== id); | |
saveUserData(); | |
renderItems(); | |
} | |
// Filter items by type | |
function filterItems(type) { | |
filterButtons.forEach(button => { | |
if (button.getAttribute('data-filter') === type) { | |
button.classList.remove('bg-indigo-100', 'dark:bg-indigo-900', 'text-indigo-800', 'dark:text-indigo-200'); | |
button.classList.add('bg-indigo-600', 'text-white'); | |
} else { | |
button.classList.remove('bg-indigo-600', 'text-white'); | |
button.classList.add('bg-indigo-100', 'dark:bg-indigo-900', 'text-indigo-800', 'dark:text-indigo-200'); | |
} | |
}); | |
renderItems(type); | |
} | |
// Render items | |
function renderItems(filter = 'all') { | |
if (!currentUser) return; | |
let filteredItems = items; | |
if (filter !== 'all') { | |
filteredItems = items.filter(item => item.type === filter); | |
} | |
if (filteredItems.length === 0) { | |
itemsGrid.innerHTML = `<div class="col-span-full text-center py-8 text-gray-500">Nenhum item encontrado</div>`; | |
return; | |
} | |
itemsGrid.innerHTML = ''; | |
filteredItems.forEach(item => { | |
const itemCard = document.createElement('div'); | |
itemCard.className = 'rounded-lg overflow-hidden shadow-md transition-all hover:shadow-lg card-light dark:card-dark'; | |
let typeBadge = ''; | |
let badgeColor = ''; | |
switch (item.type) { | |
case 'protein': | |
typeBadge = 'Proteína'; | |
badgeColor = 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200'; | |
break; | |
case 'side': | |
typeBadge = 'Acompanhamento'; | |
badgeColor = 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200'; | |
break; | |
case 'dessert': | |
typeBadge = 'Sobremesa'; | |
badgeColor = 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200'; | |
break; | |
} | |
const status = item.quantity > 0 ? | |
`<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200"> | |
Disponível (${item.quantity}) | |
</span>` : | |
`<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200"> | |
Indisponível | |
</span>`; | |
itemCard.innerHTML = ` | |
<div class="p-4"> | |
<div class="flex justify-between items-start"> | |
<div> | |
<h3 class="text-lg font-semibold dark:text-white">${item.name}</h3> | |
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${badgeColor} mt-1"> | |
${typeBadge} | |
</span> | |
</div> | |
${status} | |
</div> | |
${item.image ? ` | |
<div class="mt-3"> | |
<img src="${item.image}" alt="${item.name}" class="w-full h-32 object-cover rounded-md"> | |
</div> | |
` : ''} | |
<div class="mt-3"> | |
<p class="text-sm text-gray-600 dark:text-gray-300">${item.description || 'Sem descrição'}</p> | |
</div> | |
<div class="mt-4 flex justify-between items-center"> | |
<span class="text-sm font-medium dark:text-white">${item.calories} kcal</span> | |
<div class="flex space-x-2"> | |
<button onclick="editItem('${item.id}')" class="text-indigo-600 hover:text-indigo-900 dark:text-indigo-400 dark:hover:text-indigo-300"> | |
<i class="fas fa-edit"></i> | |
</button> | |
<button onclick="deleteItem('${item.id}')" class="text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-300"> | |
<i class="fas fa-trash"></i> | |
</button> | |
</div> | |
</div> | |
</div> | |
`; | |
itemsGrid.appendChild(itemCard); | |
}); | |
} | |
// Edit item (global function) | |
window.editItem = function(id) { | |
const item = items.find(item => item.id === id); | |
if (item) { | |
showItemModal(item); | |
} | |
}; | |
// Show dish modal | |
function showDishModal(dish = null) { | |
if (!currentUser) return; | |
dishForm.reset(); | |
totalCalories.textContent = '0 kcal'; | |
// Load protein items | |
proteinItems.innerHTML = ''; | |
const proteinItemsList = items.filter(item => item.type === 'protein'); | |
proteinItemsList.forEach(item => { | |
const itemElement = document.createElement('div'); | |
itemElement.className = 'flex items-center'; | |
itemElement.innerHTML = ` | |
<input type="checkbox" id="item-${item.id}" value="${item.id}" class="item-checkbox h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded dark:bg-gray-700 dark:border-gray-600" data-calories="${item.calories}" data-type="protein"> | |
<label for="item-${item.id}" class="ml-2 block text-sm text-gray-700 dark:text-gray-300">${item.name} (${item.calories} kcal)</label> | |
`; | |
proteinItems.appendChild(itemElement); | |
}); | |
// Load side items | |
sideItems.innerHTML = ''; | |
const sideItemsList = items.filter(item => item.type === 'side'); | |
sideItemsList.forEach(item => { | |
const itemElement = document.createElement('div'); | |
itemElement.className = 'flex items-center'; | |
itemElement.innerHTML = ` | |
<input type="checkbox" id="item-${item.id}" value="${item.id}" class="item-checkbox h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded dark:bg-gray-700 dark:border-gray-600" data-calories="${item.calories}" data-type="side"> | |
<label for="item-${item.id}" class="ml-2 block text-sm text-gray-700 dark:text-gray-300">${item.name} (${item.calories} kcal)</label> | |
`; | |
sideItems.appendChild(itemElement); | |
}); | |
// Load dessert items | |
dessertItems.innerHTML = ''; | |
const dessertItemsList = items.filter(item => item.type === 'dessert'); | |
dessertItemsList.forEach(item => { | |
const itemElement = document.createElement('div'); | |
itemElement.className = 'flex items-center'; | |
itemElement.innerHTML = ` | |
<input type="checkbox" id="item-${item.id}" value="${item.id}" class="item-checkbox h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded dark:bg-gray-700 dark:border-gray-600" data-calories="${item.calories}" data-type="dessert"> | |
<label for="item-${item.id}" class="ml-2 block text-sm text-gray-700 dark:text-gray-300">${item.name} (${item.calories} kcal)</label> | |
`; | |
dessertItems.appendChild(itemElement); | |
}); | |
if (dish) { | |
document.getElementById('dishId').value = dish.id; | |
document.getElementById('dishName').value = dish.name; | |
document.getElementById('dishDescription').value = dish.description || ''; | |
// Check selected items | |
dish.items.forEach(itemId => { | |
const checkbox = document.querySelector(`#item-${itemId}`); | |
if (checkbox) { | |
checkbox.checked = true; | |
} | |
}); | |
calculateTotalCalories(); | |
document.querySelector('#dishModal h3').textContent = 'Editar Prato'; | |
} else { | |
document.getElementById('dishId').value = ''; | |
document.querySelector('#dishModal h3').textContent = 'Novo Prato'; | |
} | |
// Add event listeners to checkboxes | |
document.querySelectorAll('.item-checkbox').forEach(checkbox => { | |
checkbox.addEventListener('change', calculateTotalCalories); | |
}); | |
dishModal.classList.remove('hidden'); | |
} | |
// Calculate total calories for dish | |
function calculateTotalCalories() { | |
let total = 0; | |
document.querySelectorAll('.item-checkbox:checked').forEach(checkbox => { | |
total += parseInt(checkbox.getAttribute('data-calories')); | |
}); | |
totalCalories.textContent = `${total} kcal`; | |
} | |
// Save dish | |
function saveDish() { | |
const id = document.getElementById('dishId').value; | |
const name = document.getElementById('dishName').value.trim(); | |
const description = document.getElementById('dishDescription').value.trim(); | |
const selectedItems = []; | |
document.querySelectorAll('.item-checkbox:checked').forEach(checkbox => { | |
selectedItems.push(checkbox.value); | |
}); | |
if (!name || selectedItems.length === 0) return; | |
// Calculate total calories | |
let calories = 0; | |
selectedItems.forEach(itemId => { | |
const item = items.find(i => i.id === itemId); | |
if (item) { | |
calories += item.calories; | |
} | |
}); | |
const dishData = { | |
id: id || Date.now().toString(), | |
name, | |
description, | |
items: selectedItems, | |
calories | |
}; | |
if (id) { | |
// Update existing dish | |
const index = dishes.findIndex(dish => dish.id === id); | |
if (index !== -1) { | |
dishes[index] = dishData; | |
} | |
} else { | |
// Add new dish | |
dishes.push(dishData); | |
} | |
saveUserData(); | |
renderDishes(); | |
dishModal.classList.add('hidden'); | |
} | |
// Delete dish | |
function deleteDish(id) { | |
if (!confirm('Tem certeza que deseja excluir este prato?')) return; | |
dishes = dishes.filter(dish => dish.id !== id); | |
saveUserData(); | |
renderDishes(); | |
} | |
// Render dishes | |
function renderDishes() { | |
if (!currentUser) return; | |
if (dishes.length === 0) { | |
dishesGrid.innerHTML = '<div class="col-span-full text-center py-8 text-gray-500">Nenhum prato cadastrado</div>'; | |
return; | |
} | |
dishesGrid.innerHTML = ''; | |
dishes.forEach(dish => { | |
const dishCard = document.createElement('div'); | |
dishCard.className = 'rounded-lg overflow-hidden shadow-md transition-all hover:shadow-lg card-light dark:card-dark'; | |
// Get dish items details | |
const dishItems = []; | |
dish.items.forEach(itemId => { | |
const item = items.find(i => i.id === itemId); | |
if (item) { | |
dishItems.push(item); | |
} | |
}); | |
// Group items by type | |
const proteins = dishItems.filter(item => item.type === 'protein'); | |
const sides = dishItems.filter(item => item.type === 'side'); | |
const desserts = dishItems.filter(item => item.type === 'dessert'); | |
dishCard.innerHTML = ` | |
<div class="p-4"> | |
<div class="flex justify-between items-start"> | |
<h3 class="text-lg font-semibold dark:text-white">${dish.name}</h3> | |
<span class="text-sm font-medium dark:text-white">${dish.calories} kcal</span> | |
</div> | |
<div class="mt-3"> | |
<p class="text-sm text-gray-600 dark:text-gray-300">${dish.description || 'Sem descrição'}</p> | |
</div> | |
<div class="mt-4 space-y-2"> | |
${proteins.length > 0 ? ` | |
<div> | |
<h4 class="text-sm font-medium dark:text-white">Proteínas</h4> | |
<ul class="text-xs text-gray-600 dark:text-gray-400 mt-1"> | |
${proteins.map(item => `<li>• ${item.name} (${item.calories} kcal)</li>`).join('')} | |
</ul> | |
</div> | |
` : ''} | |
${sides.length > 0 ? ` | |
<div> | |
<h4 class="text-sm font-medium dark:text-white">Acompanhamentos</h4> | |
<ul class="text-xs text-gray-600 dark:text-gray-400 mt-1"> | |
${sides.map(item => `<li>• ${item.name} (${item.calories} kcal)</li>`).join('')} | |
</ul> | |
</div> | |
` : ''} | |
${desserts.length > 0 ? ` | |
<div> | |
<h4 class="text-sm font-medium dark:text-white">Sobremesas</h4> | |
<ul class="text-xs text-gray-600 dark:text-gray-400 mt-1"> | |
${desserts.map(item => `<li>• ${item.name} (${item.calories} kcal)</li>`).join('')} | |
</ul> | |
</div> | |
` : ''} | |
</div> | |
<div class="mt-4 flex justify-end space-x-2"> | |
<button onclick="editDish('${dish.id}')" class="text-indigo-600 hover:text-indigo-900 dark:text-indigo-400 dark:hover:text-indigo-300"> | |
<i class="fas fa-edit"></i> | |
</button> | |
<button onclick="deleteDish('${dish.id}')" class="text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-300"> | |
<i class="fas fa-trash"></i> | |
</button> | |
</div> | |
</div> | |
`; | |
dishesGrid.appendChild(dishCard); | |
}); | |
} | |
// Edit dish (global function) | |
window.editDish = function(id) { | |
const dish = dishes.find(dish => dish.id === id); | |
if (dish) { | |
showDishModal(dish); | |
} | |
}; | |
// Generate weekly menu | |
function generateWeeklyMenu() { | |
if (!currentUser || dishes.length === 0) return; | |
const days = ['Segunda-feira', 'Terça-feira', 'Quarta-feira', 'Quinta-feira', 'Sexta-feira', 'Sábado', 'Domingo']; | |
weeklyMenu = days.map(day => { | |
// Randomly select dishes for lunch and dinner | |
const availableDishes = [...dishes]; | |
const lunchDish = availableDishes[Math.floor(Math.random() * availableDishes.length)]; | |
// Remove lunch dish from available dishes for dinner | |
const dinnerDishes = availableDishes.filter(dish => dish.id !== lunchDish.id); | |
const dinnerDish = dinnerDishes.length > 0 ? | |
dinnerDishes[Math.floor(Math.random() * dinnerDishes.length)] : | |
lunchDish; | |
return { | |
day, | |
lunch: lunchDish.id, | |
dinner: dinnerDish.id | |
}; | |
}); | |
currentUser.weeklyMenu = weeklyMenu; | |
saveUserData(); | |
renderWeeklyMenu(); | |
} | |
// Render weekly menu | |
function renderWeeklyMenu() { | |
if (!currentUser) return; | |
menuTableBody.innerHTML = ''; | |
if (weeklyMenu.length === 0) { | |
menuTableBody.innerHTML = ` | |
<tr> | |
<td colspan="4" class="px-6 py-4 text-center text-gray-500 dark:text-gray-400"> | |
Nenhum cardápio semanal gerado. Clique em "Gerar Cardápio" para criar um. | |
</td> | |
</tr> | |
`; | |
return; | |
} | |
weeklyMenu.forEach((menuDay, index) => { | |
const lunchDish = dishes.find(dish => dish.id === menuDay.lunch); | |
const dinnerDish = dishes.find(dish => dish.id === menuDay.dinner); | |
const row = document.createElement('tr'); | |
row.className = index % 2 === 0 ? 'bg-white dark:bg-gray-800' : 'bg-gray-50 dark:bg-gray-700'; | |
row.innerHTML = ` | |
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium dark:text-white"> | |
${menuDay.day} | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-700 dark:text-gray-300"> | |
${lunchDish ? `${lunchDish.name} (${lunchDish.calories} kcal)` : 'N/A'} | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-700 dark:text-gray-300"> | |
${dinnerDish ? `${dinnerDish.name} (${dinnerDish.calories} kcal)` : 'N/A'} | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> | |
<button onclick="editMenuDay(${index})" class="text-indigo-600 hover:text-indigo-900 dark:text-indigo-400 dark:hover:text-indigo-300 mr-2"> | |
<i class="fas fa-edit"></i> | |
</button> | |
</td> | |
`; | |
menuTableBody.appendChild(row); | |
}); | |
} | |
// Edit menu day (global function) | |
window.editMenuDay = function(index) { | |
if (!currentUser || !weeklyMenu[index]) return; | |
// Create a modal to edit the menu day | |
const modal = document.createElement('div'); | |
modal.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50'; | |
modal.innerHTML = ` | |
<div class="bg-white dark:bg-gray-800 rounded-lg p-6 w-full max-w-md"> | |
<div class="flex justify-between items-center mb-4"> | |
<h3 class="text-xl font-semibold dark:text-white">Editar ${weeklyMenu[index].day}</h3> | |
<button id="closeMenuModal" class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"> | |
<i class="fas fa-times"></i> | |
</button> | |
</div> | |
<div class="mb-4"> | |
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Almoço</label> | |
<select id="lunchDish" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:text-white"> | |
${dishes.map(dish => ` | |
<option value="${dish.id}" ${dish.id === weeklyMenu[index].lunch ? 'selected' : ''}> | |
${dish.name} (${dish.calories} kcal) | |
</option> | |
`).join('')} | |
</select> | |
</div> | |
<div class="mb-4"> | |
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Jantar</label> | |
<select id="dinnerDish" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:text-white"> | |
${dishes.map(dish => ` | |
<option value="${dish.id}" ${dish.id === weeklyMenu[index].dinner ? 'selected' : ''}> | |
${dish.name} (${dish.calories} kcal) | |
</option> | |
`).join('')} | |
</select> | |
</div> | |
<div class="flex justify-end space-x-3"> | |
<button id="cancelMenuEdit" class="px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-md text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">Cancelar</button> | |
<button id="saveMenuEdit" class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700">Salvar</button> | |
</div> | |
</div> | |
`; | |
document.body.appendChild(modal); | |
// Add event listeners | |
document.getElementById('closeMenuModal').addEventListener('click', () => { | |
document.body.removeChild(modal); | |
}); | |
document.getElementById('cancelMenuEdit').addEventListener('click', () => { | |
document.body.removeChild(modal); | |
}); | |
document.getElementById('saveMenuEdit').addEventListener('click', () => { | |
const lunchDish = document.getElementById('lunchDish').value; | |
const dinnerDish = document.getElementById('dinnerDish').value; | |
weeklyMenu[index].lunch = lunchDish; | |
weeklyMenu[index].dinner = dinnerDish; | |
currentUser.weeklyMenu = weeklyMenu; | |
saveUserData(); | |
renderWeeklyMenu(); | |
document.body.removeChild(modal); | |
}); | |
}; | |
// Render report | |
function renderReport() { | |
if (!currentUser) return; | |
// Set date range (current week) | |
const today = new Date(); | |
const startOfWeek = new Date(today); | |
startOfWeek.setDate(today.getDate() - today.getDay() + 1); // Monday | |
const endOfWeek = new Date(startOfWeek); | |
endOfWeek.setDate(startOfWeek.getDate() + 6); // Sunday | |
const options = { day: '2-digit', month: '2-digit', year: 'numeric' }; | |
document.getElementById('reportDateRange').textContent = | |
`Semana de ${startOfWeek.toLocaleDateString('pt-BR', options)} a ${endOfWeek.toLocaleDateString('pt-BR', options)}`; | |
// Calculate total calories for the week | |
let weeklyCalories = 0; | |
if (weeklyMenu.length > 0) { | |
weeklyMenu.forEach(day => { | |
const lunchDish = dishes.find(dish => dish.id === day.lunch); | |
const dinnerDish = dishes.find(dish => dish.id === day.dinner); | |
if (lunchDish) weeklyCalories += lunchDish.calories; | |
if (dinnerDish) weeklyCalories += dinnerDish.calories; | |
}); | |
} | |
document.getElementById('reportTotalCalories').textContent = `${weeklyCalories} kcal`; | |
// Render weekly menu | |
const weeklyMenuReport = document.getElementById('weeklyMenuReport'); | |
weeklyMenuReport.innerHTML = ''; | |
if (weeklyMenu.length === 0) { | |
weeklyMenuReport.innerHTML = '<p class="text-gray-500 dark:text-gray-400">Nenhum cardápio semanal gerado.</p>'; | |
} else { | |
weeklyMenu.forEach(day => { | |
const lunchDish = dishes.find(dish => dish.id === day.lunch); | |
const dinnerDish = dishes.find(dish => dish.id === day.dinner); | |
const dayElement = document.createElement('div'); | |
dayElement.className = 'border-b pb-4 dark:border-gray-700'; | |
dayElement.innerHTML = ` | |
<h5 class="font-medium dark:text-white mb-2">${day.day}</h5> | |
<div class="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
<div class="bg-gray-50 dark:bg-gray-700 p-3 rounded-lg"> | |
<h6 class="font-medium dark:text-white">Almoço</h6> | |
<p class="text-sm ${lunchDish ? 'text-gray-600 dark:text-gray-300' : 'text-gray-400 dark:text-gray-500'}"> | |
${lunchDish ? `${lunchDish.name} (${lunchDish.calories} kcal)` : 'Nenhum prato selecionado'} | |
</p> | |
</div> | |
<div class="bg-gray-50 dark:bg-gray-700 p-3 rounded-lg"> | |
<h6 class="font-medium dark:text-white">Jantar</h6> | |
<p class="text-sm ${dinnerDish ? 'text-gray-600 dark:text-gray-300' : 'text-gray-400 dark:text-gray-500'}"> | |
${dinnerDish ? `${dinnerDish.name} (${dinnerDish.calories} kcal)` : 'Nenhum prato selecionado'} | |
</p> | |
</div> | |
</div> | |
`; | |
weeklyMenuReport.appendChild(dayElement); | |
}); | |
} | |
// Render inventory report | |
const inventoryReport = document.getElementById('inventoryReport'); | |
inventoryReport.innerHTML = ''; | |
if (items.length === 0) { | |
inventoryReport.innerHTML = ` | |
<tr> | |
<td colspan="4" class="px-6 py-4 text-center text-gray-500 dark:text-gray-400"> | |
Nenhum item cadastrado. | |
</td> | |
</tr> | |
`; | |
} else { | |
items.forEach(item => { | |
let typeName = ''; | |
switch (item.type) { | |
case 'protein': typeName = 'Proteína'; break; | |
case 'side': typeName = 'Acompanhamento'; break; | |
case 'dessert': typeName = 'Sobremesa'; break; | |
} | |
const status = item.quantity > 0 ? | |
`<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200"> | |
Disponível | |
</span>` : | |
`<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200"> | |
Indisponível | |
</span>`; | |
const row = document.createElement('tr'); | |
row.innerHTML = ` | |
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium dark:text-white"> | |
${item.name} | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400"> | |
${typeName} | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400"> | |
${item.quantity} | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400"> | |
${status} | |
</td> | |
`; | |
inventoryReport.appendChild(row); | |
}); | |
} | |
} | |
// Print report | |
function printReport() { | |
const printWindow = window.open('', '', 'width=800,height=600'); | |
printWindow.document.write(` | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Relatório Semanal - MenuMaster</title> | |
<style> | |
body { font-family: Arial, sans-serif; margin: 20px; } | |
h1, h2, h3, h4 { color: #333; } | |
.header { display: flex; justify-content: space-between; margin-bottom: 20px; } | |
.menu-day { margin-bottom: 15px; border-bottom: 1px solid #eee; padding-bottom: 10px; } | |
.menu-meal { margin-bottom: 5px; } | |
table { width: 100%; border-collapse: collapse; margin-top: 20px; } | |
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; } | |
th { background-color: #f2f2f2; } | |
.available { color: green; } | |
.unavailable { color: red; } | |
.total-calories { font-size: 1.2em; font-weight: bold; margin: 20px 0; } | |
</style> | |
</head> | |
<body> | |
<div class="header"> | |
<h1>Relatório Semanal - MenuMaster</h1> | |
<div>${document.getElementById('reportDateRange').textContent}</div> | |
</div> | |
<div class="total-calories"> | |
Total de Calorias: ${document.getElementById('reportTotalCalories').textContent} | |
</div> | |
<h2>Cardápio Semanal</h2> | |
${document.getElementById('weeklyMenuReport').innerHTML} | |
<h2>Estoque de Ingredientes</h2> | |
${document.getElementById('inventoryReport').outerHTML} | |
<script> | |
window.onload = function() { | |
window.print(); | |
setTimeout(function() { | |
window.close(); | |
}, 500); | |
}; | |
</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=wallaceblaia/receita" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body> | |
</html> |