funcionarios / index.html
JonathanCelular's picture
pode fazer um novo css e html mais bonito para meu codigo - <!DOCTYPE html> <html lang="pt-br"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Controle de Consumo de Funcionários</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.6.2/sql-wasm.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script src="https://unpkg.com/lucide@latest"></script> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" /> <style> :root { /* Tema Claro (Padrão) */ --primary-color: #4CAF50; --primary-dark: #388E3C; --secondary-color: #2196F3; --accent-color: #FFC107; --text-color: #333; --light-text-color: #666; --background-light: #f8f9fa; --background-medium: #e9ecef; --surface-color: #ffffff; --border-color: #dee2e6; --shadow-light: rgba(0, 0, 0, 0.08); --shadow-medium: rgba(0, 0, 0, 0.15); --delete-color: #dc3545; --delete-dark: #c82333; --edit-color: #2196F3; --edit-dark: #1976D2; --toggle-color: #ffc107; --toggle-dark: #e0a800; --input-focus-shadow: rgba(76, 175, 80, 0.35); } html[data-theme='dark'] { /* Tema Escuro */ --primary-color: #5cb85c; --primary-dark: #449d44; --secondary-color: #337ab7; --accent-color: #f0ad4e; --text-color: #e0e0e0; --light-text-color: #a0a0a0; --background-light: #121212; --background-medium: #1e1e1e; --surface-color: #2c2c2c; --border-color: #444444; --shadow-light: rgba(255, 255, 255, 0.05); --shadow-medium: rgba(255, 255, 255, 0.1); --delete-color: #d9534f; --delete-dark: #c9302c; --edit-color: #5bc0de; --edit-dark: #31b0d5; --toggle-color: #f0ad4e; --toggle-dark: #ec971f; --input-focus-shadow: rgba(92, 184, 92, 0.35); } html { transition: background-color 0.3s, color 0.3s; } body { font-family: 'Inter', sans-serif; padding: 30px; background-color: var(--background-light); color: var(--text-color); line-height: 1.6; margin: 0; display: flex; flex-direction: column; align-items: center; min-height: 100vh; padding-bottom: 80px; padding-top: 80px; /* Adicionado para acomodar a navbar fixa */ } h1 { color: var(--primary-dark); margin-bottom: 25px; font-size: 2.5em; font-weight: 700; text-align: center; width: 100%; max-width: 1000px; text-shadow: 1px 1px 2px rgba(0,0,0,0.05); } h2, h3 { color: var(--text-color); margin-bottom: 20px; font-size: 1.8em; font-weight: 600; border-bottom: 2px solid var(--border-color); padding-bottom: 10px; } h3 { border-bottom: none; font-size: 1.5em; margin-top: 20px; } label { display: block; margin: 18px 0 8px; font-weight: 600; color: var(--text-color); letter-spacing: 0.5px; text-shadow: 0.5px 0.5px 1px rgba(0,0,0,0.02); } input, select { padding: 14px 18px; margin-bottom: 20px; width: 100%; max-width: 480px; box-sizing: border-box; border: 1px solid var(--border-color); border-radius: 12px; font-size: 1.05em; color: var(--text-color); background-color: var(--surface-color); transition: border-color 0.3s ease, box-shadow 0.3s ease, background-color 0.3s ease; } input::placeholder { color: var(--light-text-color); opacity: 0.7; } html[data-theme='light'] input:hover, html[data-theme='light'] select:hover { background-color: #fcfcfc; border-color: #c0c0c0; } html[data-theme='dark'] input:hover, html[data-theme='dark'] select:hover { border-color: #666; } input:focus, select:focus { border-color: var(--primary-color); box-shadow: 0 0 0 4px var(--input-focus-shadow); outline: none; background-color: var(--surface-color); } button { padding: 12px 20px; background-color: var(--primary-color); color: white; border: none; border-radius: 12px; cursor: pointer; transition: background-color 0.3s ease, transform 0.2s ease, box-shadow 0.3s ease; font-weight: 600; font-size: 1em; display: inline-flex; align-items: center; justify-content: center; gap: 8px; margin-bottom: 0; box-shadow: 0 5px 15px var(--shadow-light); text-shadow: 0.5px 0.5px 1px rgba(0,0,0,0.1); background-image: linear-gradient(to bottom right, var(--primary-color), var(--primary-dark)); } i[data-lucide] { width: 1.1em; height: 1.1em; } button:hover { background-color: var(--primary-dark); transform: translateY(-4px); box-shadow: 0 8px 20px var(--shadow-medium); background-image: linear-gradient(to bottom right, var(--primary-dark), #2e7d32); } button:active { transform: translateY(0); box-shadow: 0 2px 8px var(--shadow-light); } /* ===== NOVOS BOTÕES GENÉRICOS ===== */ .btn-primary { background-color: #007bff; background-image: linear-gradient(to bottom right, #007bff, #0056b3); color: white; } .btn-primary:hover { background-color: #0056b3; background-image: linear-gradient(to bottom right, #0056b3, #004085); } .btn-secondary { background-color: transparent; background-image: none; color: var(--light-text-color); border: 1px solid var(--border-color); box-shadow: none; } .btn-secondary:hover { background-color: var(--background-medium); border-color: var(--border-color); color: var(--text-color); box-shadow: none; transform: translateY(-2px); } button.activate-btn { background-color: var(--primary-color); background-image: linear-gradient(to bottom right, var(--primary-color), var(--primary-dark)); } button.activate-btn:hover { background-image: linear-gradient(to bottom right, var(--primary-dark), #2e7d32); } button.toggle-btn { background-color: var(--toggle-color); background-image: linear-gradient(to bottom right, var(--toggle-color), var(--toggle-dark)); } button.toggle-btn:hover { background-color: var(--toggle-dark); background-image: linear-gradient(to bottom right, var(--toggle-dark), #c79100); } button.delete-btn { background-color: var(--delete-color); background-image: linear-gradient(to bottom right, var(--delete-color), var(--delete-dark)); } button.delete-btn:hover { background-color: var(--delete-dark); background-image: linear-gradient(to bottom right, var(--delete-dark), #a71d2a); } button.edit-btn { background-color: var(--edit-color); background-image: linear-gradient(to bottom right, var(--edit-color), var(--edit-dark)); } button.edit-btn:hover { background-color: var(--edit-dark); background-image: linear-gradient(to bottom right, var(--edit-dark), #1565C0); } table { width: 100%; border-collapse: separate; border-spacing: 0; margin-top: 25px; background-color: var(--surface-color); border-radius: 12px; overflow: hidden; box-shadow: 0 4px 12px var(--shadow-light); } tr { transition: background-color 0.2s ease-in-out; } th, td { border-bottom: 1px solid var(--border-color); padding: 15px 20px; text-align: left; vertical-align: middle; } td:last-child { display: flex; justify-content: flex-end; gap: 8px; } th { background-color: var(--background-medium); font-weight: 600; color: var(--light-text-color); text-transform: uppercase; font-size: 0.9em; } tr:last-child td { border-bottom: none; } td:last-child button { padding: 10px; margin-left: 0; gap: 0; } td:last-child button i { margin: 0; } html[data-theme='light'] tr:nth-child(even) { background-color: #fefefe; } html[data-theme='dark'] tr:nth-child(even) { background-color: #383838; } html[data-theme='light'] tr:hover { background-color: #f0f4f7; } html[data-theme='dark'] tr:hover { background-color: var(--primary-dark); color: white; } html[data-theme='dark'] tr:hover td { color: white; } .inactive-row { opacity: 0.6; background-color: var(--background-medium) !important; } .inactive-row td { text-decoration: line-through; } .tabs { display: flex; width: 100%; max-width: 1000px; margin-bottom: 30px; background-color: var(--background-medium); border-radius: 12px; padding: 4px; box-shadow: 0 4px 8px var(--shadow-light); flex-wrap: nowrap; overflow-x: auto; /* Propriedades para fixar a navbar */ position: fixed; top: 0; left: 50%; transform: translateX(-50%); z-index: 1000; border-radius: 0 0 12px 12px; } .tabs::-webkit-scrollbar { height: 4px; } .tabs::-webkit-scrollbar-thumb { background: var(--border-color); border-radius: 10px; } .tab-button { background-color: transparent; padding: 12px 20px; cursor: pointer; border-radius: 8px; user-select: none; transition: background-color 0.3s ease, color 0.3s ease, box-shadow 0.3s ease; font-weight: 500; color: var(--light-text-color); flex-grow: 1; flex-shrink: 0; text-align: center; border: none; margin: 0; display: inline-flex; align-items: center; justify-content: center; gap: 8px; white-space: nowrap; } .tab-button.active { background-color: var(--surface-color); color: var(--primary-color); font-weight: 700; box-shadow: 0 2px 8px var(--shadow-light); z-index: 1; } .section { display: none; background: var(--surface-color); border-radius: 12px; box-shadow: 0 6px 18px var(--shadow-medium); padding: 30px; margin-bottom: 40px; width: 100%; max-width: 1000px; box-sizing: border-box; } .section.active { display: block; } #toast { visibility: hidden; min-width: 280px; background-color: #dc3545; /* Vermelho */ color: #fff; /* Cor do texto branca */ text-align: center; border-radius: 10px; padding: 16px 25px; position: fixed; z-index: 9999; left: 50%; bottom: 40px; font-size: 1.1em; transform: translateX(-50%); opacity: 0; transition: opacity 0.6s ease, visibility 0.6s ease; box-shadow: 0 4px 12px var(--shadow-medium); pointer-events: none; } #toast.show { visibility: visible; opacity: 1; pointer-events: auto; } .modal { display: none; position: fixed; z-index: 1001; left: 0; top: 0; width: 100%; height: 100%; overflow: auto; background-color: rgba(0,0,0,0.5); justify-content: center; align-items: center; padding: 20px; } .modal-content { background-color: var(--surface-color); padding: 35px; border-radius: 15px; box-shadow: 0 8px 25px var(--shadow-medium); text-align: center; animation: fadeIn 0.4s ease-out; width: 90%; max-width: 450px; } .modal-content h3 { margin-top: 0; color: var(--primary-dark); font-size: 1.8em; margin-bottom: 15px; } .modal-content p { margin-bottom: 30px; color: var(--light-text-color); font-size: 1.1em; } .modal-buttons { display: flex; justify-content: center; gap: 20px; } .modal-buttons button { flex: 1; max-width: 180px; padding: 12px 25px; font-size: 1.05em; margin-bottom: 0; } .modal-buttons .cancel-btn { background-color: #6c757d; background-image: none; box-shadow: 0 4px 8px rgba(0,0,0,0.1); } .modal-buttons .cancel-btn:hover { background-color: #5a6268; box-shadow: 0 6px 12px rgba(0,0,0,0.2); } .modal-content.form-modal { text-align: left; } .modal-content.form-modal input, .modal-content.form-modal select { max-width: 100%; } .modal-content.form-modal .modal-buttons { margin-top: 20px; } footer { margin-top: 30px; padding: 1px; width: 100%; max-width: 1000px; text-align: center; color: var(--light-text-color); font-size: 0.9em; border-top: 1px solid var(--border-color); background-color: var(--background-medium); border-radius: 12px 12px 0 0; box-shadow: 0 -2px 8px var(--shadow-light); position: fixed; bottom: 0; left: 50%; transform: translateX(-50%); z-index: 999; } @keyframes fadeIn { from { opacity: 0; transform: translateY(-30px); } to { opacity: 1; transform: translateY(0); } } .select2-container--default .select2-selection--single { background-color: var(--surface-color); border: 1px solid var(--border-color); border-radius: 12px; height: auto; } .select2-container--default .select2-selection--single .select2-selection__rendered { color: var(--text-color); line-height: 1.6; padding: 10px 18px; } .select2-container--default .select2-selection--single .select2-selection__arrow { height: 50px; right: 10px; } .select2-container .select2-selection--single .select2-selection__rendered { padding-right: 30px; } .select2-dropdown { background-color: var(--surface-color); border: 1px solid var(--border-color); border-radius: 12px; box-shadow: 0 8px 25px var(--shadow-medium); } .select2-container--default .select2-search--dropdown .select2-search__field { background-color: var(--background-medium); border: 1px solid var(--border-color); color: var(--text-color); border-radius: 8px; padding: 8px 12px; } .select2-container--default .select2-search--dropdown .select2-search__field:focus { border-color: var(--primary-color); outline: none; } .select2-container--default .select2-results__option--highlighted[aria-selected] { background-color: var(--primary-color); color: white; } .select2-results__option { color: var(--text-color); } .select2-container--default .select2-results__option[aria-selected=true] { background-color: var(--background-medium); } .select2-container--open .select2-selection--single { border-color: var(--primary-color); box-shadow: none; } .modal-content .close-modal-btn { position: absolute; top: 10px; right: 15px; background: none; border: none; font-size: 2.5em; color: var(--light-text-color); cursor: pointer; line-height: 1; padding: 0; font-weight: 300; transition: color 0.2s ease, transform 0.2s ease; box-shadow: none; background-image: none; } .modal-content .close-modal-btn:hover { color: var(--delete-color); transform: scale(1.1); background-color: transparent; } .modal-header { text-align: center; margin-bottom: 25px; } .modal-header h3 { margin: 0; border-bottom: none; } .form-group { margin-bottom: 22px; } .form-group label { margin-top: 0; margin-bottom: 10px; } .form-row { display: flex; gap: 20px; align-items: flex-start; } .form-row .form-group { flex: 1; margin-bottom: 22px; } .quantity-input { display: flex; align-items: center; border: 1px solid var(--border-color); border-radius: 12px; overflow: hidden; transition: border-color 0.3s ease, box-shadow 0.3s ease; } .quantity-input:focus-within { border-color: var(--primary-color); box-shadow: 0 0 0 4px var(--input-focus-shadow); } .quantity-input input[type="number"] { width: 100%; text-align: center; border: none; margin-bottom: 0; box-shadow: none; padding: 14px 5px; -moz-appearance: textfield; } .quantity-input input[type="number"]::-webkit-outer-spin-button, .quantity-input input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } .quantity-btn { background-color: var(--background-medium); border: none; color: var(--text-color); cursor: pointer; font-size: 1.4em; font-weight: 600; padding: 0 18px; align-self: stretch; transition: background-color 0.3s, color 0.3s; box-shadow: none; transform: none; background-image: none; margin-bottom: 0; border-radius: 0; } .quantity-btn:hover { background-color: var(--primary-color); color: white; } .quantity-btn.minus { border-right: 1px solid var(--border-color); } .quantity-btn.plus { border-left: 1px solid var(--border-color); } .modal-content.form-modal { max-width: 550px; padding: 30px 40px; position: relative; } .modal-content.form-modal .modal-buttons { margin-top: 25px; justify-content: flex-end; gap: 15px; } .modal-content.form-modal .cancel-btn { background-color: transparent; background-image: none; color: var(--light-text-color); border: 1px solid var(--border-color); box-shadow: none; } .modal-content.form-modal .cancel-btn:hover { background-color: var(--background-medium); border-color: var(--border-color); color: var(--text-color); box-shadow: none; transform: translateY(-2px); } .filter-container { display: flex; flex-wrap: wrap; gap: 16px; align-items: flex-end; padding: 20px; margin-bottom: 20px; background-color: transparent; border: 1px solid var(--border-color); border-radius: 12px; } .filter-group { display: flex; flex-direction: column; gap: 8px; } .filter-group.search-group { flex: 1 1 300px; } .filter-group.date-group { display: flex; flex-direction: row; gap: 16px; } .filter-group label { margin: 0; font-size: 0.9em; font-weight: 500; color: var(--light-text-color); } .filter-container input, .filter-container button { margin-bottom: 0; } .input-with-icon { position: relative; display: flex; align-items: center; } .input-with-icon i { position: absolute; left: 15px; color: var(--light-text-color); width: 18px; height: 18px; } .input-with-icon input { padding-left: 55px; /* MODIFICADO: Aumentado para dar mais espaço */ } .settings-list { display: flex; flex-direction: column; gap: 16px; } .setting-item { display: flex; justify-content: space-between; align-items: center; padding: 20px; background-color: var(--background-medium); border-radius: 12px; border: 1px solid var(--border-color); flex-wrap: wrap; gap: 16px; opacity: 0; transform: translateY(10px); animation: fadeInItem 0.4s ease-out forwards; } .setting-item:nth-child(1) { animation-delay: 0.1s; } .setting-item:nth-child(2) { animation-delay: 0.2s; } .setting-item:nth-child(3) { animation-delay: 0.3s; } .setting-item:nth-child(4) { animation-delay: 0.4s; } @keyframes fadeInItem { to { opacity: 1; transform: translateY(0); } } .setting-info h3 { margin: 0 0 4px 0; font-size: 1.1em; font-weight: 600; color: var(--text-color); border: none; padding: 0; } .setting-info p { margin: 0; font-size: 0.9em; color: var(--light-text-color); } #loading-overlay { position: fixed; inset: 0; background-color: rgba(0, 0, 0, 0.5); z-index: 9999; display: flex; justify-content: center; align-items: center; } .spinner { width: 50px; height: 50px; border: 5px solid var(--surface-color); border-top-color: var(--primary-color); border-radius: 50%; animation: spin 1s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } } .dashboard-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; margin-bottom: 30px; } .dashboard-card { background-color: var(--background-medium); padding: 20px; border-radius: 12px; border: 1px solid var(--border-color); } .dashboard-card h4 { font-size: 1em; color: var(--light-text-color); margin-bottom: 8px; font-weight: 500; } .dashboard-card .value { font-size: 2em; font-weight: 700; color: var(--text-color); } .dashboard-card .subtext { font-size: 0.9em; color: var(--light-text-color); } .chart-container { height: 350px; margin-top: 30px; } .report-controls { padding: 24px; background-color: var(--background-medium); border: 1px solid var(--border-color); border-radius: 12px; display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; /* Alterado de 20px para 15px para um layout mais compacto, conforme sugestão */ align-items: center; /* ALTEADO: Alinha os itens do grid ao centro verticalmente */ margin-bottom: 30px; } .report-controls .filter-group, .report-controls .actions-group { display: flex; flex-direction: column; gap: 8px; } .report-controls .actions-group { flex-direction: row; gap: 12px; align-items: center; /* ADICIONADO: Alinha os itens (botões) dentro do actions-group verticalmente ao centro */ } .report-controls .actions-group button { flex: 1; height: 100%; /* ADICIONADO: Garante que os botões tenham a mesma altura do elemento de entrada */ margin-bottom: 0; /* Garante que não haja margin-bottom extra */ display: flex; /* Para centralizar o ícone e o texto verticalmente */ align-items: center; /* Centraliza o conteúdo (ícone e texto) */ justify-content: center; /* Centraliza o conteúdo (ícone e texto) */ box-sizing: border-box; /* Garante que padding e border sejam incluídos na altura */ } .report-controls label { margin: 0; font-size: 0.9em; font-weight: 500; } /* Ajuste para inputs dentro do report-controls para remover margin-bottom padrão */ .report-controls input, .report-controls select { margin-bottom: 0; } #relatorio .empty-state { text-align: center; padding: 60px 20px; border: 2px dashed var(--border-color); border-radius: 12px; margin-top: 30px; } #relatorio .empty-state i { font-size: 3em; color: var(--border-color); width: 1.5em; height: 1.5em; } #relatorio .empty-state h3 { margin-top: 20px; border: none; font-size: 1.2em; color: var(--text-color); } #relatorio .empty-state p { color: var(--light-text-color); max-width: 300px; margin: 8px auto 0; } /* Novas regras para o "olhinho" de senha */ .input-with-toggle-password { position: relative; width: 100%; max-width: 600px; /* Aumentado para preencher mais o modal. Ajuste este valor. */ margin: 0 auto 20px auto; } .input-with-toggle-password input { width: 100%; padding-right: 45px; margin-bottom: 0; } .toggle-password-icon { position: absolute; right: 15px; top: 50%; transform: translateY(-50%); cursor: pointer; color: var(--light-text-color); width: 20px; height: 20px; transition: color 0.2s ease; } .toggle-password-icon:hover { color: var(--text-color); } /* Adicionado para espaçamento do botão Novo Lançamento */ .section button.btn-primary:first-of-type { margin-bottom: 25px; } @media (max-width: 768px) { body { padding: 20px; padding-top: 70px; /* Adjust padding for smaller tabs */ padding-bottom: 70px; } h1 { font-size: 2em; } .tabs { padding: 2px; border-radius: 0 0 8px 8px; } .tab-button { padding: 10px 15px; font-size: 0.9em; } .section { padding: 20px; margin-bottom: 30px; } input, select, button { font-size: 0.95em; padding: 12px 15px; } button { padding: 10px 15px; } table { font-size: 0.9em; } th, td { padding: 12px 15px; } td:last-child { flex-direction: column; /* Stack action buttons */ gap: 5px; align-items: flex-end; } td:last-child button { width: fit-content; align-self: flex-end; } .modal-content { padding: 25px; width: 95%; } .modal-buttons { flex-direction: column; gap: 10px; } .modal-buttons button { max-width: 100%; } .form-row { flex-direction: column; gap: 15px; } .filter-container { flex-direction: column; align-items: stretch; padding: 15px; gap: 10px; } .filter-group.date-group { flex-direction: column; gap: 10px; } .input-with-icon input { padding-left: 45px; /* Adjust for smaller screens */ } .report-controls { grid-template-columns: 1fr; padding: 15px; gap: 15px; } .report-controls .actions-group { flex-direction: column; gap: 10px; } .dashboard-grid { grid-template-columns: 1fr; /* Stack cards on mobile */ } .dashboard-card .value { font-size: 1.8em; } .chart-container { height: 300px; /* Adjust chart height for smaller screens */ } .setting-item { flex-direction: column; align-items: flex-start; gap: 10px; padding: 15px; } .setting-action { width: 100%; display: flex; justify-content: flex-end; } #toast { min-width: unset; width: 90%; left: 5%; transform: translateX(0%); bottom: 20px; font-size: 1em; padding: 12px 20px; } footer { padding: 5px; font-size: 0.8em; } } @media print { /* Estilos de impressão mantidos */ body { padding: 0; background-color: #fff; color: #000; margin: 0; min-height: auto; } .tabs, .tab-button, .section:not(.active), .modal, #toast, #loading-overlay, footer, button, .filter-container, .report-controls { display: none !important; } .section.active { display: block !important; box-shadow: none; padding: 0; margin-bottom: 0; max-width: none; width: auto; } h2 { border-bottom: none; text-align: center; margin-bottom: 20px; font-size: 1.5em; } table { width: 100%; border-collapse: collapse; box-shadow: none; border-radius: 0; } th, td { border: 1px solid #ccc; padding: 8px; font-size: 0.9em; color: #000; background-color: #fff !important; } th { background-color: #f2f2f2 !important; font-weight: bold; } tr:nth-child(even) { background-color: #f9f9f9 !important; } td:last-child { display: table-cell; /* Revert flex for print */ text-align: left; } .dashboard-grid, .chart-container, .settings-list { display: none !important; } #relatorio { margin-top: 0; } #relatorio .empty-state { display: none !important; } } </style> </head> <body> <div class="tabs"> <div class="tab-button active" onclick="showTab(0)"><i data-lucide="layout-dashboard"></i>Visão Geral</div> <div class="tab-button" onclick="showTab(1)"><i data-lucide="users"></i>Funcionários</div> <div class="tab-button" onclick="showTab(2)"><i data-lucide="package"></i>Produtos</div> <div class="tab-button" onclick="showTab(3)"><i data-lucide="arrow-right-left"></i>Lançamentos</div> <div class="tab-button" onclick="showTab(4)"><i data-lucide="bar-chart-3"></i>Relatório</div> <div class="tab-button" onclick="showTab(5)"><i data-lucide="settings"></i>Configurações</div> </div> <div class="section active"> <h2><i data-lucide="layout-dashboard"></i> Dashboard do Mês</h2> <div class="dashboard-grid"> <div class="dashboard-card" id="card-total-mes"> <h4>Total Gasto no Mês</h4> <p class="value">R$ 0,00</p> <p class="subtext">Consumo geral</p> </div> <div class="dashboard-card" id="card-top-produto"> <h4>Produto Mais Consumido</h4> <p class="value">-</p> <p class="subtext">Item mais retirado</p> </div> <div class="dashboard-card" id="card-top-consumidor"> <h4>Top Consumidor do Mês</h4> <p class="value">-</p> <p class="subtext">Maior consumo em valor</p> </div> </div> <div class="chart-container"> <canvas id="mainDashboardChart"></canvas> </div> </div> <div class="section"> <h2><i data-lucide="users"></i> Gestão de Funcionários</h2> <button class="btn-primary" onclick="openAddFuncionarioModal()"><i data-lucide="plus"></i>Novo Funcionário</button> <div id="lista-funcionarios"></div> </div> <div class="section"> <h2><i data-lucide="package"></i> Gestão de Produtos</h2> <button class="btn-primary" onclick="openAddProdutoModal()"><i data-lucide="plus"></i>Novo Produto</button> <div id="lista-produtos"></div> </div> <div class="section"> <h2><i data-lucide="arrow-right-left"></i> Gestão de Lançamentos</h2> <button class="btn-primary" onclick="openLancamentoModal()"><i data-lucide="plus"></i>Novo Lançamento</button> <div class="filter-container"> <div class="filter-group search-group"> <label for="lanc-pesquisa">Pesquisar por nome ou observação</label> <div class="input-with-icon"> <i data-lucide="search"></i> <input type="text" id="lanc-pesquisa" placeholder="Funcionário, produto, obs..." oninput="listarLancamentos()"> </div> </div> <div class="filter-group date-group"> <div class="date-field"> <label for="lanc-filtro-de">Data de Início</label> <input type="date" id="lanc-filtro-de"> </div> <div class="date-field"> <label for="lanc-filtro-ate">Data Final</label> <input type="date" id="lanc-filtro-ate"> </div> </div> <div class="filter-group actions-group"> <button class="btn-primary" onclick="listarLancamentos(true)"><i data-lucide="filter"></i>Filtrar</button> <button class="btn-secondary" onclick="goToPreviousMonth()"><i data-lucide="chevrons-left"></i>Mês Anterior</button> <button class="btn-secondary" onclick="resetarFiltrosLancamentos()"><i data-lucide="rotate-cw"></i>Mês Atual</button> </div> </div> <div id="lista-lancamentos"></div> </div> <div class="section"> <h2><i data-lucide="bar-chart-3"></i> Relatório</h2> <div class="report-controls"> <div class="filter-group"> <label for="filtro-func">Filtrar por Funcionário</label> <select id="filtro-func"></select> </div> <div class="filter-group"> <label for="filtro-de">Período de</label> <input id="filtro-de" type="date"> </div> <div class="filter-group"> <label for="filtro-ate">Até</label> <input id="filtro-ate" type="date"> </div> <div class="actions-group"> <button class="btn-primary" onclick="gerarRelatorio()"><i data-lucide="file-text"></i>Gerar</button> <button class="btn-secondary" onclick="imprimirRelatorio()"><i data-lucide="file-down"></i>PDF</button> </div> </div> <div id="relatorio"> <div class="empty-state"> <i data-lucide="search-check"></i> <h3>Nenhum relatório gerado</h3> <p>Utilize o painel de controle acima para gerar um novo relatório.</p> </div> </div> </div> <div class="section"> <h2><i data-lucide="settings"></i> Configurações</h2> <div class="settings-list"> <div class="setting-item"> <div class="setting-info"> <h3>Alterar Tema</h3> <p>Alterne entre os modos de visualização claro e escuro.</p> </div> <div class="setting-action"> <button class="btn-secondary" id="theme-toggle-btn"><i data-lucide="sun"></i>Alterar Tema</button> </div> </div> <div class="setting-item"> <div class="setting-info"> <h3>Exportar Dados</h3> <p>Salve um backup completo de seus dados em um arquivo .sqlite.</p> </div> <div class="setting-action"> <input type="file" id="importFile" style="display:none" onchange="importarDB(event)"> <button class="btn-secondary" onclick="exportarDB()"><i data-lucide="download"></i>Exportar Banco</button> </div> </div> <div class="setting-item"> <div class="setting-info"> <h3>Importar Dados</h3> <p>Carregue um arquivo de backup .sqlite. (Atenção: isso substituirá os dados atuais).</p> </div> <div class="setting-action"> <input type="file" id="importFile" style="display:none" onchange="importarDB(event)"> <button class="btn-secondary" onclick="document.getElementById('importFile').click()"><i data-lucide="upload"></i>Importar Banco</button> </div> </div> <div class="setting-item"> <div class="setting-info"> <h3>Alterar Senha do Administrador</h3> <p>Defina uma nova senha para as ações restritas.</p> </div> <div class="setting-action"> <button class="btn-primary" onclick="openChangePasswordModal()"><i data-lucide="key-round"></i>Alterar Senha</button> </div> </div> </div> </div> <div id="loading-overlay" style="display: none;"><div class="spinner"></div></div> <div id="toast"></div> <div id="confirmationModal" class="modal"> <div class="modal-content"> <h3 id="modalTitle">Confirmação</h3> <p id="modalMessage">Você tem certeza?</p> <div class="modal-buttons"> <button class="btn-secondary" id="cancelButton"><i data-lucide="x-circle"></i>Cancelar</button><button class="delete-btn" id="confirmButton"><i data-lucide="check-circle"></i>Confirmar</button> </div> </div> </div> <div id="passwordModal" class="modal"> <div class="modal-content"> <button class="close-modal-btn" onclick="closePasswordModal()"><i data-lucide="x"></i></button> <div class="modal-header"> <h3>Acesso Restrito</h3> </div> <div class="form-group"> <label for="admin-password">Senha de Administrador:</label> <div class="input-with-toggle-password"> <input type="password" id="admin-password" placeholder="Digite a senha"> <i data-lucide="eye" class="toggle-password-icon" onclick="togglePasswordVisibility('admin-password', this)"></i> </div> </div> <div class="modal-buttons"> <button class="btn-secondary" onclick="closePasswordModal()"><i data-lucide="x"></i>Cancelar</button> <button class="btn-primary" id="confirmPasswordButton"><i data-lucide="check"></i>Confirmar</button> </div> </div> </div> <div id="changePasswordModal" class="modal"> <div class="modal-content form-modal"> <button class="close-modal-btn" onclick="closeChangePasswordModal()"><i data-lucide="x"></i></button> <div class="modal-header"> <h3>Alterar Senha do Administrador</h3> </div> <div class="form-group"> <label for="current-admin-password">Senha Atual:</label> <div class="input-with-toggle-password"> <input type="password" id="current-admin-password" placeholder="Digite a senha atual"> <i data-lucide="eye" class="toggle-password-icon" onclick="togglePasswordVisibility('current-admin-password', this)"></i> </div> </div> <div class="form-group"> <label for="new-admin-password">Nova Senha:</label> <div class="input-with-toggle-password"> <input type="password" id="new-admin-password" placeholder="Digite a nova senha"> <i data-lucide="eye" class="toggle-password-icon" onclick="togglePasswordVisibility('new-admin-password', this)"></i> </div> </div> <div class="form-group"> <label for="confirm-new-admin-password">Confirmar Nova Senha:</label> <div class="input-with-toggle-password"> <input type="password" id="confirm-new-admin-password" placeholder="Confirme a nova senha"> <i data-lucide="eye" class="toggle-password-icon" onclick="togglePasswordVisibility('confirm-new-admin-password', this)"></i> </div> </div> <div class="modal-buttons"> <button class="btn-secondary" onclick="closeChangePasswordModal()"><i data-lucide="x"></i>Cancelar</button> <button class="btn-primary" id="saveNewPasswordButton"><i data-lucide="save"></i>Salvar Nova Senha</button> </div> </div> </div> <div id="funcionarioFormModal" class="modal"> <div class="modal-content form-modal"> <h3 id="funcionarioModalTitle">Cadastrar Funcionário</h3> <input type="hidden" id="func-id"> <div class="form-group"><label for="func-nome-modal">Nome:</label> <input type="text" id="func-nome-modal"></div> <div class="form-group"><label for="func-setor-modal">Setor:</label> <input type="text" id="func-setor-modal"></div> <div class="modal-buttons"> <button class="btn-secondary" id="cancelFuncionarioButton"><i data-lucide="x"></i>Cancelar</button> <button class="btn-primary" id="saveFuncionarioButton"><i data-lucide="save"></i>Salvar</button> </div> </div> </div> <div id="produtoFormModal" class="modal"> <div class="modal-content form-modal"> <h3 id="produtoModalTitle">Cadastrar Produto</h3> <input type="hidden" id="prod-id"> <div class="form-group"><label for="prod-nome-modal">Nome:</label> <input type="text" id="prod-nome-modal"></div> <div class="form-group"><label for="prod-valor-modal">Valor:</label> <input type="number" step="0.01" id="prod-valor-modal"></div> <div class="modal-buttons"> <button class="btn-secondary" id="cancelProdutoButton"><i data-lucide="x"></i>Cancelar</button> <button class="btn-primary" id="saveProdutoButton"><i data-lucide="save"></i>Salvar</button> </div> </div> </div> <div id="lancamentoFormModal" class="modal"> <div class="modal-content form-modal"> <button class="close-modal-btn" onclick="closeLancamentoModal()"><i data-lucide="x"></i></button> <div class="modal-header"> <h3 id="lancamentoModalTitle">Lançar Consumo</h3> </div> <input type="hidden" id="cons-id"> <div class="form-group"> <label for="cons-func-modal">Funcionário:</label> <select id="cons-func-modal" style="width: 100%;"></select> </div> <div class="form-group"> <label for="cons-prod-modal">Produto:</label> <select id="cons-prod-modal" style="width: 100%;"></select> </div> <div class="form-row"> <div class="form-group"> <label for="cons-qtd-modal">Quantidade:</label> <div class="quantity-input"> <button type="button" class="quantity-btn minus">-</button> <input type="number" id="cons-qtd-modal" value="1" min="1"> <button type="button" class="quantity-btn plus">+</button> </div> </div> <div class="form-group"> <label for="cons-data-modal">Data:</label> <input type="date" id="cons-data-modal"> </div> </div> <div class="form-group"> <label for="cons-obs-modal">Observação:</label> <input type="text" id="cons-obs-modal" placeholder="Ex: Café da manhã"> </div> <div class="modal-buttons"> <button class="btn-secondary" id="cancelLancamentoButton"><i data-lucide="x"></i>Cancelar</button> <button class="btn-primary" id="saveLancamentoButton"><i data-lucide="check"></i>Lançar</button> </div> </div> </div> <footer> <p>By: JhonnyDarks - v3.0</p> </footer> </body> <script> // --- Variáveis Globais --- let db; let SQL; let currentConfirmAction = null; let mainDashboardChart = null; let currentPasswordCallback = null; let ADMIN_PASSWORD; // Não é mais uma const, será carregada/salva no localStorage // --- Funções de UI e Animação --- function showLoading() { document.getElementById('loading-overlay').style.display = 'flex'; } function hideLoading() { document.getElementById('loading-overlay').style.display = 'none'; } function applyTheme(theme) { document.documentElement.setAttribute('data-theme', theme); localStorage.setItem('theme', theme); const themeToggleBtn = document.getElementById('theme-toggle-btn'); if (themeToggleBtn) { const isDark = theme === 'dark'; const icon = isDark ? 'sun' : 'moon'; const text = isDark ? 'Mudar para Tema Claro' : 'Mudar para Tema Escuro'; themeToggleBtn.innerHTML = `<i data-lucide="${icon}"></i> ${text}`; lucide.createIcons(); } } function getFormattedDate(date) { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); return `${year}-${month}-${day}`; } function showTab(index) { document.querySelectorAll('.section').forEach((sec, i) => sec.classList.toggle('active', i === index)); document.querySelectorAll('.tab-button').forEach((tab, i) => tab.classList.toggle('active', i === index)); if (index === 0) renderDashboard(); else if (index === 1) listarFuncionarios(); else if (index === 2) listarProdutos(); else if (index === 3) { setLancamentoDateFiltersToCurrentMonth(); listarLancamentos(true); } else if (index === 4) { setDataHoje(); atualizarCombos(); } else if (index === 5) { lucide.createIcons(); } // Garante que os ícones da aba de configurações sejam atualizados } // --- Script Completo e Funcional --- function setLancamentoDateFiltersToCurrentMonth() { const hoje = new Date(); const primeiroDia = new Date(hoje.getFullYear(), hoje.getMonth(), 1); const ultimoDia = new Date(hoje.getFullYear(), hoje.getMonth() + 1, 0); document.getElementById('lanc-filtro-de').value = getFormattedDate(primeiroDia); document.getElementById('lanc-filtro-ate').value = getFormattedDate(ultimoDia); } function goToPreviousMonth() { // NOVA FUNÇÃO: Navega para o mês anterior nos filtros de lançamento const inputDe = document.getElementById('lanc-filtro-de'); const inputAte = document.getElementById('lanc-filtro-ate'); let dataAtualDe = new Date(inputDe.value + 'T00:00:00'); // Calcula o primeiro dia do mês anterior ao mês da data de início atual let primeiroDiaMesAnterior = new Date(dataAtualDe.getFullYear(), dataAtualDe.getMonth() - 1, 1); // Calcula o último dia do mês anterior ao mês da data de início atual let ultimoDiaMesAnterior = new Date(primeiroDiaMesAnterior.getFullYear(), primeiroDiaMesAnterior.getMonth() + 1, 0); inputDe.value = getFormattedDate(primeiroDiaMesAnterior); inputAte.value = getFormattedDate(ultimoDiaMesAnterior); listarLancamentos(true); // Chama a função para listar com os novos filtros de data } function showToast(msg) { const toast = document.getElementById('toast'); toast.textContent = msg; toast.classList.add('show'); setTimeout(() => toast.classList.remove('show'), 3000); } function getDataLocal() { const hoje = new Date(); const ano = hoje.getFullYear(); const mes = String(hoje.getMonth() + 1).padStart(2, '0'); const dia = String(hoje.getDate()).padStart(2, '0'); return `${ano}-${mes}-${dia}`; } function setDataHoje() { const hoje = getDataLocal(); document.getElementById('filtro-de').value = hoje; document.getElementById('filtro-ate').value = hoje; } function showConfirmationModal(title, message, onConfirm, requiresPassword = false) { const modal = document.getElementById('confirmationModal'); document.getElementById('modalTitle').textContent = title; document.getElementById('modalMessage').textContent = message; if (requiresPassword) { document.getElementById('confirmButton').onclick = () => { document.getElementById('confirmationModal').style.display = 'none'; openPasswordModal(onConfirm); }; } else { document.getElementById('confirmButton').onclick = () => { if (onConfirm) onConfirm(); document.getElementById('confirmationModal').style.display = 'none'; currentConfirmAction = null; }; } modal.style.display = 'flex'; document.getElementById('cancelButton').onclick = () => { document.getElementById('confirmationModal').style.display = 'none'; currentConfirmAction = null; }; } function openPasswordModal(callback) { document.getElementById('admin-password').value = ''; // Limpa o campo da senha document.getElementById('passwordModal').style.display = 'flex'; currentPasswordCallback = callback; // Armazena a função de callback } function closePasswordModal() { document.getElementById('passwordModal').style.display = 'none'; currentPasswordCallback = null; } function openChangePasswordModal() { document.getElementById('current-admin-password').value = ''; document.getElementById('new-admin-password').value = ''; document.getElementById('confirm-new-admin-password').value = ''; document.getElementById('changePasswordModal').style.display = 'flex'; // Recria os ícones de olho ao abrir a modal lucide.createIcons(); } function closeChangePasswordModal() { document.getElementById('changePasswordModal').style.display = 'none'; } // Função para alternar a visibilidade da senha function togglePasswordVisibility(inputId, iconElement) { const input = document.getElementById(inputId); if (input.type === "password") { input.type = "text"; iconElement.setAttribute('data-lucide', 'eye-off'); // Muda para ícone de olho cortado } else { input.type = "password"; iconElement.setAttribute('data-lucide', 'eye'); // Muda de volta para ícone de olho } // Para recriar o ícone visualmente após a mudança de data-lucide lucide.createIcons(); } function openAddFuncionarioModal() { document.getElementById('funcionarioModalTitle').textContent = 'Cadastrar Funcionário'; document.getElementById('func-id').value = ''; document.getElementById('func-nome-modal').value = ''; document.getElementById('func-setor-modal').value = ''; document.getElementById('funcionarioFormModal').style.display = 'flex'; } function editFuncionario(id, nome, setor) { document.getElementById('funcionarioModalTitle').textContent = 'Editar Funcionário'; document.getElementById('func-id').value = id; document.getElementById('func-nome-modal').value = nome; document.getElementById('func-setor-modal').value = setor; document.getElementById('funcionarioFormModal').style.display = 'flex'; } function openAddProdutoModal() { document.getElementById('produtoModalTitle').textContent = 'Cadastrar Produto'; document.getElementById('prod-id').value = ''; document.getElementById('prod-nome-modal').value = ''; document.getElementById('prod-valor-modal').value = ''; document.getElementById('produtoFormModal').style.display = 'flex'; } function editProduto(id, nome, valor) { document.getElementById('produtoModalTitle').textContent = 'Editar Produto'; document.getElementById('prod-id').value = id; document.getElementById('prod-nome-modal').value = nome; document.getElementById('prod-valor-modal').value = valor; document.getElementById('produtoFormModal').style.display = 'flex'; } function openLancamentoModal() { const modal = document.getElementById('lancamentoFormModal'); document.getElementById('lancamentoModalTitle').textContent = 'Lançar Consumo'; document.getElementById('cons-id').value = ''; document.getElementById('cons-qtd-modal').value = '1'; document.getElementById('cons-obs-modal').value = ''; document.getElementById('cons-data-modal').value = getDataLocal(); atualizarCombosLancamentoModal(); modal.style.display = 'flex'; $('#cons-func-modal').select2({ placeholder: 'Selecione um funcionário...', dropdownParent: $('#lancamentoFormModal .modal-content') }); $('#cons-prod-modal').select2({ placeholder: 'Selecione um produto...', dropdownParent: $('#lancamentoFormModal .modal-content') }); } function closeLancamentoModal() { if ($('#cons-func-modal').data('select2')) $('#cons-func-modal').select2('destroy'); if ($('#cons-prod-modal').data('select2')) $('#cons-prod-modal').select2('destroy'); document.getElementById('lancamentoFormModal').style.display = 'none'; } function atualizarCombosLancamentoModal() { const funcs = db.exec("SELECT id, nome FROM funcionarios WHERE ativo = 1 ORDER BY nome")[0]?.values || []; const prods = db.exec("SELECT id, nome FROM produtos WHERE ativo = 1 ORDER BY nome")[0]?.values || []; const selFunc = $('#cons-func-modal'); const selProd = $('#cons-prod-modal'); selFunc.empty().append(new Option('', '', true, true)); selProd.empty().append(new Option('', '', true, true)); funcs.forEach(([id, nome]) => selFunc.append(new Option(nome, id, false, false))); prods.forEach(([id, nome]) => selProd.append(new Option(nome, id, false, false))); selFunc.trigger('change'); selProd.trigger('change'); } document.addEventListener('click', function(e) { if (e.target.closest('.quantity-btn')) { const button = e.target.closest('.quantity-btn'); const wrapper = button.closest('.quantity-input'); const input = wrapper.querySelector('input[type="number"]'); if (!input) return; let currentValue = parseInt(input.value, 10); const min = parseInt(input.min, 10); if (button.classList.contains('plus')) currentValue++; else if (button.classList.contains('minus')) currentValue--; if (!isNaN(min) && currentValue < min) currentValue = min; input.value = currentValue; } }); function checkAndUpdateSchema() { try { db.exec("SELECT ativo FROM funcionarios LIMIT 1"); } catch (e) { db.run("ALTER TABLE funcionarios ADD COLUMN ativo INTEGER DEFAULT 1"); db.run("UPDATE funcionarios SET ativo = 1 WHERE ativo IS NULL"); } try { db.exec("SELECT ativo FROM produtos LIMIT 1"); } catch (e) { db.run("ALTER TABLE produtos ADD COLUMN ativo INTEGER DEFAULT 1"); db.run("UPDATE produtos SET ativo = 1 WHERE ativo IS NULL"); } salvarBanco(); } function salvarBanco() { const data = db.export(); const b64 = btoa(String.fromCharCode(...data)); localStorage.setItem('db', b64); } function atualizarCombos() { const funcs = db.exec("SELECT id, nome FROM funcionarios WHERE ativo = 1 ORDER BY nome")[0]?.values || []; const selFuncFiltro = document.getElementById('filtro-func'); if (selFuncFiltro) { selFuncFiltro.innerHTML = '<option value="">Todos os Funcionários</option>' + funcs.map(([id, nome]) => `<option value="${id}">${nome}</option>`).join(''); } } function listarFuncionarios() { showLoading(); setTimeout(() => { const result = db.exec("SELECT id, nome, setor, ativo FROM funcionarios ORDER BY nome")[0]; const container = document.getElementById('lista-funcionarios'); if (!result || !result.values.length) { container.innerHTML = '<p>Nenhum funcionário cadastrado.</p>'; hideLoading(); return; } const html = `<h3>Lista de Funcionários</h3><table><tr><th>ID</th><th>Nome</th><th>Setor</th><th>Status</th><th>Ações</th></tr>${result.values.map(r => { const [id, nome, setor, ativo] = r; const toggleButtonClass = ativo === 1 ? 'toggle-btn' : 'activate-btn'; const toggleIcon = ativo ? 'toggle-left' : 'toggle-right'; return `<tr class="${!ativo ? 'inactive-row' : ''}"> <td>${id}</td> <td>${nome}</td> <td>${setor}</td> <td>${ativo ? 'Ativo' : 'Inativo'}</td> <td> <button class="edit-btn" title="Editar" onclick="editFuncionario(${id}, '${nome.replace(/'/g, "\\'")}', '${setor.replace(/'/g, "\\'")}')"><i data-lucide="edit"></i></button> <button class="${toggleButtonClass}" title="${ativo ? 'Desativar' : 'Ativar'}" onclick="toggleFuncionarioStatus(${id}, ${ativo})"><i data-lucide="${toggleIcon}"></i></button> <button class="delete-btn" title="Excluir" onclick="showConfirmationModal('Confirmar Exclusão', 'Tem certeza? Lançamentos associados serão perdidos.', () => { db.run('DELETE FROM consumo WHERE funcionario_id = ?', [${id}]); db.run('DELETE FROM funcionarios WHERE id = ?', [${id}]); salvarBanco(); atualizarCombosLancamentoModal(); atualizarCombos(); listarFuncionarios(); listarLancamentos(true); showToast('Funcionário excluído.'); }, true)"><i data-lucide="trash-2"></i></button> </td> </tr>`; }).join('')}</table>`; container.innerHTML = html; lucide.createIcons(); hideLoading(); }, 50); } function toggleFuncionarioStatus(id, currentStatus) { const newStatus = 1 - currentStatus; db.run("UPDATE funcionarios SET ativo = ? WHERE id = ?", [newStatus, id]); salvarBanco(); showToast(`Funcionário ${newStatus === 1 ? 'ativado' : 'desativado'}.`); listarFuncionarios(); atualizarCombos(); atualizarCombosLancamentoModal(); } function listarProdutos() { showLoading(); setTimeout(() => { const result = db.exec("SELECT id, nome, valor, ativo FROM produtos ORDER BY nome")[0]; const container = document.getElementById('lista-produtos'); if (!result || !result.values.length) { container.innerHTML = '<p>Nenhum produto cadastrado.</p>'; hideLoading(); return; } const html = `<h3>Lista de Produtos</h3><table><tr><th>ID</th><th>Nome</th><th>Valor</th><th>Status</th><th>Ações</th></tr>${result.values.map(r => { const [id, nome, valor, ativo] = r; const toggleButtonClass = ativo === 1 ? 'toggle-btn' : 'activate-btn'; const toggleIcon = ativo ? 'toggle-left' : 'toggle-right'; return `<tr class="${!ativo ? 'inactive-row' : ''}"> <td>${id}</td> <td>${nome}</td> <td>R$ ${valor.toFixed(2)}</td> <td>${ativo ? 'Ativo' : 'Inativo'}</td> <td> <button class="edit-btn" title="Editar" onclick="editProduto(${id}, '${nome.replace(/'/g, "\\'")}', ${valor})"><i data-lucide="edit"></i></button> <button class="${toggleButtonClass}" title="${ativo ? 'Desativar' : 'Ativar'}" onclick="toggleProdutoStatus(${id}, ${ativo})"><i data-lucide="${toggleIcon}"></i></button> <button class="delete-btn" title="Excluir" onclick="showConfirmationModal('Confirmar Exclusão', 'Tem certeza? Lançamentos associados serão perdidos.', () => { db.run('DELETE FROM consumo WHERE produto_id = ?', [${id}]); db.run('DELETE FROM produtos WHERE id = ?', [${id}]); salvarBanco(); atualizarCombosLancamentoModal(); atualizarCombos(); listarProdutos(); listarLancamentos(true); showToast('Produto excluído.'); }, true)"><i data-lucide="trash-2"></i></button> </td> </tr>`; }).join('')}</table>`; container.innerHTML = html; lucide.createIcons(); hideLoading(); }, 50); } function toggleProdutoStatus(id, currentStatus) { const newStatus = 1 - currentStatus; db.run("UPDATE produtos SET ativo = ? WHERE id = ?", [newStatus, id]); salvarBanco(); showToast(`Produto ${newStatus === 1 ? 'ativado' : 'desativado'}.`); listarProdutos(); atualizarCombos(); atualizarCombosLancamentoModal(); } function listarLancamentos(filtrarPorData = false) { showLoading(); setTimeout(() => { const pesquisa = document.getElementById('lanc-pesquisa').value.toLowerCase(); const container = document.getElementById('lista-lancamentos'); let query = `SELECT c.id, f.nome, p.nome, c.quantidade, c.data, c.obs, p.valor FROM consumo c JOIN funcionarios f ON f.id = c.funcionario_id JOIN produtos p ON p.id = c.produto_id`; let params = []; if (filtrarPorData) { const de = document.getElementById('lanc-filtro-de').value; const ate = document.getElementById('lanc-filtro-ate').value; if (de && ate) { query += ' WHERE c.data BETWEEN ? AND ?'; params.push(de, ate); } } query += ' ORDER BY c.data DESC, c.id DESC'; try { const result = db.exec(query, params)[0]; if (!result || result.values.length === 0) { container.innerHTML = `<p>Nenhum lançamento encontrado.</p>`; hideLoading(); return; } const filteredRows = result.values.filter(r => (r[1] && r[1].toLowerCase().includes(pesquisa)) || (r[2] && r[2].toLowerCase().includes(pesquisa)) || (r[5] && r[5].toLowerCase().includes(pesquisa)) || (r[4] && r[4].toLowerCase().includes(pesquisa)) ); if (filteredRows.length === 0) { container.innerHTML = '<p>Nenhum lançamento com a pesquisa atual.</p>'; hideLoading(); return; } const html = `<h3>Lançamentos</h3><table><tr><th>ID</th><th>Funcionário</th><th>Produto</th><th>Qtd</th><th>Data</th><th>Obs</th><th>Ações</th></tr>${filteredRows.map(r => `<tr><td>${r[0]}</td><td>${r[1]}</td><td>${r[2]}</td><td>${r[3]}</td><td>${new Date(r[4] + 'T00:00:00').toLocaleDateString()}</td><td>${r[5] || ''}</td><td><button class="delete-btn" title="Excluir" onclick="showConfirmationModal('Confirmar Exclusão', 'Excluir este lançamento?', () => { db.run('DELETE FROM consumo WHERE id = ?', [${r[0]}]); salvarBanco(); listarLancamentos(true); showToast('Lançamento excluído.'); }, true)"><i data-lucide="trash-2"></i></button></td></tr>`).join('')}</table>`; container.innerHTML = html; lucide.createIcons(); } catch (e) { console.error("Erro ao listar lançamentos:", e); container.innerHTML = `<p style="color:red;">Erro ao carregar os lançamentos. Verifique o console.</p>`; } finally { hideLoading(); } }, 50); } function resetarFiltrosLancamentos() { setLancamentoDateFiltersToCurrentMonth(); document.getElementById('lanc-pesquisa').value = ''; listarLancamentos(true); } function renderDashboard() { showLoading(); setTimeout(() => { const hoje = new Date(); const primeiroDia = getFormattedDate(new Date(hoje.getFullYear(), hoje.getMonth(), 1)); const ultimoDia = getFormattedDate(new Date(hoje.getFullYear(), hoje.getMonth() + 1, 0)); let totalMes = 0; const totalQuery = db.exec(`SELECT SUM(p.valor * c.quantidade) FROM consumo c JOIN produtos p ON p.id = c.produto_id WHERE c.data BETWEEN '${primeiroDia}' AND '${ultimoDia}'`)[0]; if (totalQuery && totalQuery.values[0][0]) { totalMes = totalQuery.values[0][0]; } document.querySelector('#card-total-mes .value').textContent = `R$ ${totalMes.toFixed(2)}`; let topProduto = '-'; const topProdQuery = db.exec(`SELECT p.nome FROM consumo c JOIN produtos p ON p.id = c.produto_id WHERE c.data BETWEEN '${primeiroDia}' AND '${ultimoDia}' GROUP BY p.nome ORDER BY SUM(c.quantidade) DESC LIMIT 1`)[0]; if (topProdQuery && topProdQuery.values.length > 0) { topProduto = topProdQuery.values[0][0]; } document.querySelector('#card-top-produto .value').textContent = topProduto; let topConsumidor = '-'; const topConsumidorQuery = db.exec(`SELECT f.nome FROM consumo c JOIN funcionarios f ON f.id = c.funcionario_id JOIN produtos p ON p.id = c.produto_id WHERE c.data BETWEEN '${primeiroDia}' AND '${ultimoDia}' GROUP BY f.nome ORDER BY SUM(p.valor * c.quantidade) DESC LIMIT 1`)[0]; if (topConsumidorQuery && topConsumidorQuery.values.length > 0) { topConsumidor = topConsumidorQuery.values[0][0]; } document.querySelector('#card-top-consumidor .value').textContent = topConsumidor; const chartDataQuery = db.exec(`SELECT f.nome, SUM(p.valor * c.quantidade) as total FROM consumo c JOIN funcionarios f ON f.id = c.funcionario_id JOIN produtos p ON p.id = c.produto_id WHERE c.data BETWEEN '${primeiroDia}' AND '${ultimoDia}' GROUP BY f.nome ORDER BY total DESC LIMIT 5`)[0]; if (mainDashboardChart) mainDashboardChart.destroy(); const ctx = document.getElementById('mainDashboardChart').getContext('2d'); if (chartDataQuery && chartDataQuery.values.length > 0) { const labels = chartDataQuery.values.map(row => row[0]); const data = chartDataQuery.values.map(row => row[1]); mainDashboardChart = new Chart(ctx, { type: 'bar', data: { labels, datasets: [{ label: 'Top 5 Consumidores do Mês (R$)', data, backgroundColor: 'rgba(76, 175, 80, 0.5)', borderColor: 'rgba(76, 175, 80, 1)', borderWidth: 1 }] }, options: { scales: { y: { beginAtZero: true } }, responsive: true, maintainAspectRatio: false } }); } else { // Clear the canvas if no data to display ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); // Optionally display a message on the chart area ctx.font = "16px Inter"; ctx.fillStyle = "var(--light-text-color)"; ctx.textAlign = "center"; ctx.fillText("Nenhum dado de consumo para este mês.", ctx.canvas.width / 2, ctx.canvas.height / 2); } hideLoading(); lucide.createIcons(); }, 50); } function gerarRelatorio() { showLoading(); setTimeout(() => { const funcId = document.getElementById('filtro-func').value; const de = document.getElementById('filtro-de').value; const ate = document.getElementById('filtro-ate').value; if (!de || !ate) { showToast('Selecione as datas.'); hideLoading(); return; } const stmt = ` SELECT f.nome, p.nome, c.quantidade, p.valor, (c.quantidade * p.valor) AS total, c.data FROM consumo c JOIN funcionarios f ON f.id = c.funcionario_id JOIN produtos p ON p.id = c.produto_id WHERE c.data BETWEEN ? AND ? ${funcId ? 'AND f.id = ?' : ''} ORDER BY c.data `; const params = funcId ? [de, ate, funcId] : [de, ate]; const container = document.getElementById('relatorio'); try { const result = db.exec(stmt, params)[0]; if (!result || result.values.length === 0) { container.innerHTML = `<div class="empty-state"><i data-lucide="info"></i><h3>Nenhum registro encontrado</h3><p>Não há dados para os filtros selecionados.</p></div>`; lucide.createIcons(); hideLoading(); return; } let totalGeral = 0; const rows = result.values.map(r => { totalGeral += r[4]; // r[4] é o total (quantidade * valor) return `<tr> <td>${r[0]}</td> <td>${r[1]}</td> <td>${r[2]}</td> <td>R$ ${r[3].toFixed(2)}</td> <td>R$ ${r[4].toFixed(2)}</td> <td>${new Date(r[5] + 'T00:00:00').toLocaleDateString()}</td> </tr>`; }).join(''); const selectedFuncName = funcId ? db.exec(`SELECT nome FROM funcionarios WHERE id = ${funcId}`)[0]?.values[0][0] : 'Todos'; const reportTitle = `Relatório de Consumo (De: ${de} a ${ate}${funcId ? `, ${selectedFuncName}` : ''})`; container.innerHTML = ` <h3>${reportTitle}</h3> <table> <thead> <tr> <th>Funcionário</th> <th>Produto</th> <th>Qtd</th> <th>Valor Unit.</th> <th>Total</th> <th>Data</th> </tr> </thead> <tbody> ${rows} </tbody> <tfoot> <tr> <td colspan="4" style="text-align:right"><strong>Total:</strong></td> <td colspan="2"><strong>R$ ${totalGeral.toFixed(2)}</strong></td> </tr> </tfoot> </table> `; } catch(e) { console.error("Erro ao gerar relatório: ", e); container.innerHTML = `<div class="empty-state"><i data-lucide="alert-triangle"></i><h3>Ocorreu um erro</h3><p>Não foi possível gerar o relatório. Verifique o console.</p></div>`; lucide.createIcons(); } finally { hideLoading(); } }, 50); } async function imprimirRelatorio() { const reportElement = document.getElementById('relatorio'); if (!reportElement.querySelector('table')) { showToast("Gere um relatório antes de exportar para PDF."); return; } showLoading(); showToast("Gerando PDF, aguarde..."); // Small delay to ensure loading indicator is visible await new Promise(resolve => setTimeout(resolve, 100)); try { const tableElement = reportElement.querySelector('table'); // Temporarily set overflow to visible for html2canvas to capture full content const originalOverflow = tableElement.style.overflow; tableElement.style.overflow = 'visible'; const canvas = await html2canvas(tableElement, { scale: 2, // Aumenta a escala para melhor qualidade no PDF useCORS: true, logging: false, // ignoreElements: (element) => { // // Ignora elementos que não devem aparecer no PDF, se houver // // return element.classList.contains('no-print'); // return false; // } }); // Restore original overflow tableElement.style.overflow = originalOverflow; const imgData = canvas.toDataURL('image/png'); const { jsPDF } = window.jspdf; const pdf = new jsPDF('p', 'mm', 'a4'); // 'p' for portrait, 'mm' for millimeters, 'a4' for A4 size const imgProps = pdf.getImageProperties(imgData); const pdfWidth = pdf.internal.pageSize.getWidth() - 20; // 10mm margin on each side const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width; // Add title to PDF pdf.text(document.querySelector('#relatorio h3').textContent, 10, 10); // Add image to PDF, starting at Y=20 (after title) pdf.addImage(imgData, 'PNG', 10, 20, pdfWidth, pdfHeight); pdf.save(`relatorio_consumo_${new Date().toISOString().slice(0,10)}.pdf`); showToast("PDF gerado com sucesso!"); } catch (e) { console.error("Erro ao gerar PDF:", e); showToast("Erro ao gerar o PDF. Verifique o console."); } finally { hideLoading(); } } function exportarDB() { const data = db.export(); const blob = new Blob([data], { type: 'application/octet-stream' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `backup_consumo_${new Date().toISOString().slice(0,10)}.sqlite`; a.click(); URL.revokeObjectURL(url); showToast('Banco de dados exportado.'); } function importarDB(event) { const file = event.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = function (e) { try { const Uints = new Uint8Array(e.target.result); db = new SQL.Database(Uints); checkAndUpdateSchema(); salvarBanco(); atualizarCombos(); showTab(0); // Volta para a dashboard após importar showToast('Banco de dados importado!'); } catch (error) { console.error("Erro ao importar banco:", error); showToast('Arquivo inválido ou corrompido.'); } }; reader.readAsArrayBuffer(file); } // --- Inicialização --- initSqlJs({ locateFile: file => `https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.6.2/${file}` }).then(sqlInstance => { SQL = sqlInstance; const savedDb = localStorage.getItem('db'); if (savedDb) { const Uints = Uint8Array.from(atob(savedDb), c => c.charCodeAt(0)); db = new SQL.Database(Uints); } else { db = new SQL.Database(); db.run(` CREATE TABLE funcionarios (id INTEGER PRIMARY KEY, nome TEXT, setor TEXT, ativo INTEGER DEFAULT 1); CREATE TABLE produtos (id INTEGER PRIMARY KEY, nome TEXT, valor REAL, ativo INTEGER DEFAULT 1); CREATE TABLE consumo (id INTEGER PRIMARY KEY, funcionario_id INTEGER, produto_id INTEGER, quantidade INTEGER, data TEXT, obs TEXT); `); } checkAndUpdateSchema(); // --- CARREGAR A SENHA DE ADMINISTRADOR NO INÍCIO --- // Se a senha não existe no localStorage, define uma padrão e salva. // CUIDADO: Em produção, essa senha padrão deveria ser definida de forma mais segura. ADMIN_PASSWORD = localStorage.getItem('admin_password') || 'admin123'; localStorage.setItem('admin_password', ADMIN_PASSWORD); // Garante que a senha padrão seja salva ou atualizada // Listeners // Removido o listener direto do confirmButton, agora ele é configurado dinamicamente no showConfirmationModal document.getElementById('cancelButton').addEventListener('click', () => { document.getElementById('confirmationModal').style.display = 'none'; currentConfirmAction = null; }); // Listener para o botão de confirmar senha do passwordModal document.getElementById('confirmPasswordButton').addEventListener('click', () => { const enteredPassword = document.getElementById('admin-password').value; if (enteredPassword === ADMIN_PASSWORD) { closePasswordModal(); if (currentPasswordCallback) { currentPasswordCallback(); // Executa a ação original após a senha correta showToast("Senha correta. Ação autorizada."); } } else { showToast("Senha incorreta!"); document.getElementById('admin-password').value = ''; // Limpa o campo } }); // Listener para o botão de salvar nova senha do changePasswordModal document.getElementById('saveNewPasswordButton').addEventListener('click', () => { const currentPassword = document.getElementById('current-admin-password').value; const newPassword = document.getElementById('new-admin-password').value; const confirmNewPassword = document.getElementById('confirm-new-admin-password').value; if (currentPassword !== ADMIN_PASSWORD) { showToast("Senha atual incorreta!"); return; } if (newPassword === '') { showToast("A nova senha não pode ser vazia."); return; } if (newPassword !== confirmNewPassword) { showToast("A nova senha e a confirmação não coincidem."); return; } ADMIN_PASSWORD = newPassword; // Atualiza a variável global localStorage.setItem('admin_password', ADMIN_PASSWORD); // Salva a nova senha no localStorage showToast("Senha de administrador alterada com sucesso!"); closeChangePasswordModal(); }); document.getElementById('saveFuncionarioButton').addEventListener('click', () => { const id = document.getElementById('func-id').value; const nome = document.getElementById('func-nome-modal').value.trim(); const setor = document.getElementById('func-setor-modal').value.trim(); if (!nome || !setor) { showToast('Preencha nome e setor.'); return; } if (id) { db.run(`UPDATE funcionarios SET nome = ?, setor = ? WHERE id = ?`, [nome, setor, id]); showToast('Funcionário atualizado!'); } else { db.run(`INSERT INTO funcionarios (nome, setor, ativo) VALUES (?, ?, 1)`, [nome, setor]); showToast("Funcionário cadastrado."); } salvarBanco(); listarFuncionarios(); atualizarCombosLancamentoModal(); atualizarCombos(); // Atualiza combos de filtro de relatório document.getElementById('funcionarioFormModal').style.display = 'none'; }); document.getElementById('cancelFuncionarioButton').addEventListener('click', () => document.getElementById('funcionarioFormModal').style.display = 'none'); document.getElementById('saveProdutoButton').addEventListener('click', () => { const id = document.getElementById('prod-id').value; const nome = document.getElementById('prod-nome-modal').value.trim(); const valor = parseFloat(document.getElementById('prod-valor-modal').value); if (!nome || isNaN(valor) || valor <= 0) { showToast('Preencha nome e valor válido.'); return; } if (id) { db.run(`UPDATE produtos SET nome = ?, valor = ? WHERE id = ?`, [nome, valor, id]); showToast('Produto atualizado!'); } else { db.run(`INSERT INTO produtos (nome, valor, ativo) VALUES (?, ?, 1)`, [nome, valor]); showToast("Produto cadastrado."); } salvarBanco(); listarProdutos(); atualizarCombosLancamentoModal(); atualizarCombos(); // Atualiza combos de filtro de relatório document.getElementById('produtoFormModal').style.display = 'none'; }); document.getElementById('cancelProdutoButton').addEventListener('click', () => document.getElementById('produtoFormModal').style.display = 'none'); document.getElementById('saveLancamentoButton').addEventListener('click', () => { const funcionario_id = $('#cons-func-modal').val(); const produto_id = $('#cons-prod-modal').val(); const qtd = parseInt(document.getElementById('cons-qtd-modal').value); const data = document.getElementById('cons-data-modal').value; const obs = document.getElementById('cons-obs-modal').value.trim(); if (!funcionario_id || !produto_id || isNaN(qtd) || qtd <= 0 || !data) { showToast('Preencha todos os campos corretamente.'); return; } db.run(`INSERT INTO consumo (funcionario_id, produto_id, quantidade, data, obs) VALUES (?, ?, ?, ?, ?)`, [funcionario_id, produto_id, qtd, data, obs]); showToast("Consumo lançado."); salvarBanco(); listarLancamentos(true); closeLancamentoModal(); renderDashboard(); // Atualiza o dashboard após novo lançamento }); document.getElementById('cancelLancamentoButton').addEventListener('click', () => closeLancamentoModal()); document.getElementById('theme-toggle-btn').addEventListener('click', () => { const newTheme = document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark'; applyTheme(newTheme); }); const currentTheme = localStorage.getItem('theme') || 'light'; applyTheme(currentTheme); showTab(0); // Mostra a aba Visão Geral ao iniciar lucide.createIcons(); // Inicializa os ícones do Lucide }); </script> </html> - Initial Deployment
2338488 verified
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Controle de Consumo de Funcionários</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.6.2/sql-wasm.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://unpkg.com/lucide@latest"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet">
<style>
@layer utilities {
.scrollbar::-webkit-scrollbar {
width: 8px;
height: 8px;
}
.scrollbar::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.05);
border-radius: 10px;
}
.scrollbar::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.2);
border-radius: 10px;
}
.scrollbar::-webkit-scrollbar-thumb:hover {
background: rgba(0, 0, 0, 0.3);
}
.animate-fade-in {
animation: fadeIn 0.3s ease-out forwards;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.animate-spin {
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.animate-pulse {
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.transition-all {
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.bg-gradient-primary {
background-image: linear-gradient(to right, #4f46e5, #7c3aed);
}
.bg-gradient-success {
background-image: linear-gradient(to right, #10b981, #059669);
}
.bg-gradient-warning {
background-image: linear-gradient(to right, #f59e0b, #d97706);
}
.bg-gradient-danger {
background-image: linear-gradient(to right, #ef4444, #dc2626);
}
.bg-gradient-info {
background-image: linear-gradient(to right, #3b82f6, #2563eb);
}
}
/* Estilos para o tema escuro */
.dark {
--color-primary: 79, 70, 229;
--color-secondary: 124, 58, 237;
--color-success: 16, 185, 129;
--color-warning: 245, 158, 11;
--color-danger: 239, 68, 68;
--color-info: 59, 130, 246;
--color-text: 229, 231, 235;
--color-text-light: 156, 163, 175;
--color-bg: 17, 24, 39;
--color-bg-light: 31, 41, 55;
--color-bg-lighter: 55, 65, 81;
--color-border: 55, 65, 81;
--color-surface: 31, 41, 55;
--color-shadow: 0, 0, 0, 0.2;
}
/* Estilos para o tema claro */
:root {
--color-primary: 79, 70, 229;
--color-secondary: 124, 58, 237;
--color-success: 16, 185, 129;
--color-warning: 245, 158, 11;
--color-danger: 239, 68, 68;
--color-info: 59, 130, 246;
--color-text: 31, 41, 55;
--color-text-light: 107, 114, 128;
--color-bg: 249, 250, 251;
--color-bg-light: 243, 244, 246;
--color-bg-lighter: 229, 231, 235;
--color-border: 209, 213, 219;
--color-surface: 255, 255, 255;
--color-shadow: 0, 0, 0, 0.05;
}
body {
font-family: 'Inter', sans-serif;
background-color: rgb(var(--color-bg));
color: rgb(var(--color-text));
transition: background-color 0.3s, color 0.3s;
}
.select2-container--default .select2-selection--single {
background-color: rgb(var(--color-surface));
border: 1px solid rgb(var(--color-border));
border-radius: 0.5rem;
height: auto;
padding: 0.5rem;
}
.select2-container--default .select2-selection--single .select2-selection__rendered {
color: rgb(var(--color-text));
line-height: 1.5;
}
.select2-container--default .select2-selection--single .select2-selection__arrow {
height: 100%;
}
.select2-dropdown {
background-color: rgb(var(--color-surface));
border: 1px solid rgb(var(--color-border));
border-radius: 0.5rem;
}
.select2-container--default .select2-results__option--highlighted[aria-selected] {
background-color: rgba(var(--color-primary), 0.1);
color: rgb(var(--color-text));
}
.select2-results__option {
color: rgb(var(--color-text));
}
.select2-container--default .select2-results__option[aria-selected=true] {
background-color: rgba(var(--color-primary), 0.2);
}
.select2-container--open .select2-selection--single {
border-color: rgb(var(--color-primary));
}
/* Estilos para impressão */
@media print {
.no-print {
display: none !important;
}
body {
background-color: white !important;
color: black !important;
}
table {
width: 100% !important;
border-collapse: collapse !important;
}
th, td {
border: 1px solid #ddd !important;
padding: 8px !important;
color: black !important;
background-color: white !important;
}
th {
background-color: #f2f2f2 !important;
}
}
</style>
</head>
<body class="min-h-screen flex flex-col">
<!-- Barra de navegação superior -->
<div class="sticky top-0 z-50 bg-white dark:bg-gray-800 shadow-md dark:shadow-lg">
<div class="container mx-auto px-4 py-3 flex items-center justify-between">
<div class="flex items-center space-x-2">
<i data-lucide="shopping-bag" class="w-6 h-6 text-indigo-600 dark:text-indigo-400"></i>
<h1 class="text-xl font-bold text-gray-800 dark:text-white">Controle de Consumo</h1>
</div>
<div class="flex items-center space-x-4">
<button id="theme-toggle-btn" class="p-2 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700">
<i data-lucide="sun" class="w-5 h-5 text-gray-600 dark:text-gray-300"></i>
</button>
<span class="text-sm text-gray-500 dark:text-gray-400">v3.0</span>
</div>
</div>
<!-- Abas de navegação -->
<div class="container mx-auto px-4">
<div class="flex overflow-x-auto scrollbar pb-1">
<button onclick="showTab(0)" class="tab-button active flex-shrink-0 px-4 py-3 flex items-center space-x-2 text-sm font-medium border-b-2 border-transparent hover:border-gray-300 dark:hover:border-gray-600 transition-colors">
<i data-lucide="layout-dashboard" class="w-4 h-4"></i>
<span>Visão Geral</span>
</button>
<button onclick="showTab(1)" class="tab-button flex-shrink-0 px-4 py-3 flex items-center space-x-2 text-sm font-medium border-b-2 border-transparent hover:border-gray-300 dark:hover:border-gray-600 transition-colors">
<i data-lucide="users" class="w-4 h-4"></i>
<span>Funcionários</span>
</button>
<button onclick="showTab(2)" class="tab-button flex-shrink-0 px-4 py-3 flex items-center space-x-2 text-sm font-medium border-b-2 border-transparent hover:border-gray-300 dark:hover:border-gray-600 transition-colors">
<i data-lucide="package" class="w-4 h-4"></i>
<span>Produtos</span>
</button>
<button onclick="showTab(3)" class="tab-button flex-shrink-0 px-4 py-3 flex items-center space-x-2 text-sm font-medium border-b-2 border-transparent hover:border-gray-300 dark:hover:border-gray-600 transition-colors">
<i data-lucide="arrow-right-left" class="w-4 h-4"></i>
<span>Lançamentos</span>
</button>
<button onclick="showTab(4)" class="tab-button flex-shrink-0 px-4 py-3 flex items-center space-x-2 text-sm font-medium border-b-2 border-transparent hover:border-gray-300 dark:hover:border-gray-600 transition-colors">
<i data-lucide="bar-chart-3" class="w-4 h-4"></i>
<span>Relatório</span>
</button>
<button onclick="showTab(5)" class="tab-button flex-shrink-0 px-4 py-3 flex items-center space-x-2 text-sm font-medium border-b-2 border-transparent hover:border-gray-300 dark:hover:border-gray-600 transition-colors">
<i data-lucide="settings" class="w-4 h-4"></i>
<span>Configurações</span>
</button>
</div>
</div>
</div>
<!-- Conteúdo principal -->
<main class="flex-grow container mx-auto px-4 py-6">
<!-- Seção Visão Geral -->
<div id="dashboard-section" class="section active">
<div class="flex items-center justify-between mb-6">
<h2 class="text-2xl font-bold text-gray-800 dark:text-white flex items-center space-x-2">
<i data-lucide="layout-dashboard" class="w-6 h-6 text-indigo-600 dark:text-indigo-400"></i>
<span>Dashboard do Mês</span>
</h2>
<div class="text-sm text-gray-500 dark:text-gray-400">
<span id="current-month"></span>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-md p-6 border-l-4 border-indigo-500">
<div class="flex items-center justify-between">
<div>
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Total Gasto no Mês</p>
<p id="card-total-mes" class="text-3xl font-bold text-gray-800 dark:text-white mt-1">R$ 0,00</p>
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">Consumo geral</p>
</div>
<div class="p-3 rounded-lg bg-indigo-50 dark:bg-indigo-900/30">
<i data-lucide="dollar-sign" class="w-6 h-6 text-indigo-600 dark:text-indigo-400"></i>
</div>
</div>
</div>
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-md p-6 border-l-4 border-green-500">
<div class="flex items-center justify-between">
<div>
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Produto Mais Consumido</p>
<p id="card-top-produto" class="text-3xl font-bold text-gray-800 dark:text-white mt-1">-</p>
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">Item mais retirado</p>
</div>
<div class="p-3 rounded-lg bg-green-50 dark:bg-green-900/30">
<i data-lucide="package" class="w-6 h-6 text-green-600 dark:text-green-400"></i>
</div>
</div>
</div>
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-md p-6 border-l-4 border-blue-500">
<div class="flex items-center justify-between">
<div>
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Top Consumidor do Mês</p>
<p id="card-top-consumidor" class="text-3xl font-bold text-gray-800 dark:text-white mt-1">-</p>
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">Maior consumo em valor</p>
</div>
<div class="p-3 rounded-lg bg-blue-50 dark:bg-blue-900/30">
<i data-lucide="award" class="w-6 h-6 text-blue-600 dark:text-blue-400"></i>
</div>
</div>
</div>
</div>
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-md p-6 mb-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold text-gray-800 dark:text-white">Consumo por Funcionário</h3>
<div class="flex items-center space-x-2">
<button onclick="renderDashboard('month')" class="px-3 py-1 text-sm rounded-md bg-indigo-50 dark:bg-indigo-900/30 text-indigo-600 dark:text-indigo-400">Mês</button>
<button onclick="renderDashboard('quarter')" class="px-3 py-1 text-sm rounded-md bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300">Trimestre</button>
<button onclick="renderDashboard('year')" class="px-3 py-1 text-sm rounded-md bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300">Ano</button>
</div>
</div>
<div class="h-80">
<canvas id="mainDashboardChart"></canvas>
</div>
</div>
</div>
<!-- Seção Funcionários -->
<div id="funcionarios-section" class="section hidden">
<div class="flex items-center justify-between mb-6">
<h2 class="text-2xl font-bold text-gray-800 dark:text-white flex items-center space-x-2">
<i data-lucide="users" class="w-6 h-6 text-indigo-600 dark:text-indigo-400"></i>
<span>Gestão de Funcionários</span>
</h2>
<button onclick="openAddFuncionarioModal()" class="flex items-center space-x-2 px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg transition-colors">
<i data-lucide="plus" class="w-5 h-5"></i>
<span>Novo Funcionário</span>
</button>
</div>
<div id="lista-funcionarios" class="bg-white dark:bg-gray-800 rounded-xl shadow-md overflow-hidden">
<div class="p-4 border-b border-gray-200 dark:border-gray-700">
<div class="flex items-center justify-between">
<h3 class="text-lg font-semibold text-gray-800 dark:text-white">Lista de Funcionários</h3>
<div class="relative">
<i data-lucide="search" class="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400"></i>
<input type="text" placeholder="Pesquisar funcionário..." class="pl-10 pr-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
</div>
</div>
</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-700">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">ID</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Nome</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Setor</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Status</th>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Ações</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
<!-- Conteúdo dinâmico será inserido aqui -->
</tbody>
</table>
</div>
<div class="p-4 border-t border-gray-200 dark:border-gray-700 text-sm text-gray-500 dark:text-gray-400">
Total de <span id="total-funcionarios" class="font-medium">0</span> funcionários
</div>
</div>
</div>
<!-- Seção Produtos -->
<div id="produtos-section" class="section hidden">
<div class="flex items-center justify-between mb-6">
<h2 class="text-2xl font-bold text-gray-800 dark:text-white flex items-center space-x-2">
<i data-lucide="package" class="w-6 h-6 text-indigo-600 dark:text-indigo-400"></i>
<span>Gestão de Produtos</span>
</h2>
<button onclick="openAddProdutoModal()" class="flex items-center space-x-2 px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg transition-colors">
<i data-lucide="plus" class="w-5 h-5"></i>
<span>Novo Produto</span>
</button>
</div>
<div id="lista-produtos" class="bg-white dark:bg-gray-800 rounded-xl shadow-md overflow-hidden">
<div class="p-4 border-b border-gray-200 dark:border-gray-700">
<div class="flex items-center justify-between">
<h3 class="text-lg font-semibold text-gray-800 dark:text-white">Lista de Produtos</h3>
<div class="relative">
<i data-lucide="search" class="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400"></i>
<input type="text" placeholder="Pesquisar produto..." class="pl-10 pr-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
</div>
</div>
</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-700">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">ID</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Nome</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Valor</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Status</th>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Ações</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
<!-- Conteúdo dinâmico será inserido aqui -->
</tbody>
</table>
</div>
<div class="p-4 border-t border-gray-200 dark:border-gray-700 text-sm text-gray-500 dark:text-gray-400">
Total de <span id="total-produtos" class="font-medium">0</span> produtos
</div>
</div>
</div>
<!-- Seção Lançamentos -->
<div id="lancamentos-section" class="section hidden">
<div class="flex items-center justify-between mb-6">
<h2 class="text-2xl font-bold text-gray-800 dark:text-white flex items-center space-x-2">
<i data-lucide="arrow-right-left" class="w-6 h-6 text-indigo-600 dark:text-indigo-400"></i>
<span>Gestão de Lançamentos</span>
</h2>
<button onclick="openLancamentoModal()" class="flex items-center space-x-2 px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg transition-colors">
<i data-lucide="plus" class="w-5 h-5"></i>
<span>Novo Lançamento</span>
</button>
</div>
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-md p-4 mb-6">
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
<div class="md:col-span-2">
<label for="lanc-pesquisa" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Pesquisar</label>
<div class="relative">
<i data-lucide="search" class="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400"></i>
<input type="text" id="lanc-pesquisa" placeholder="Funcionário, produto, obs..." class="w-full pl-10 pr-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white" oninput="listarLancamentos()">
</div>
</div>
<div>
<label for="lanc-filtro-de" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Data Inicial</label>
<input type="date" id="lanc-filtro-de" class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
</div>
<div>
<label for="lanc-filtro-ate" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Data Final</label>
<input type="date" id="lanc-filtro-ate" class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
</div>
</div>
<div class="flex justify-end space-x-3 mt-4">
<button onclick="goToPreviousMonth()" class="flex items-center space-x-1 px-4 py-2 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
<i data-lucide="chevrons-left" class="w-4 h-4"></i>
<span>Mês Anterior</span>
</button>
<button onclick="resetarFiltrosLancamentos()" class="flex items-center space-x-1 px-4 py-2 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
<i data-lucide="rotate-cw" class="w-4 h-4"></i>
<span>Mês Atual</span>
</button>
<button onclick="listarLancamentos(true)" class="flex items-center space-x-1 px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg transition-colors">
<i data-lucide="filter" class="w-4 h-4"></i>
<span>Filtrar</span>
</button>
</div>
</div>
<div id="lista-lancamentos" class="bg-white dark:bg-gray-800 rounded-xl shadow-md overflow-hidden">
<div class="p-4 border-b border-gray-200 dark:border-gray-700">
<h3 class="text-lg font-semibold text-gray-800 dark:text-white">Lançamentos</h3>
</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-700">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">ID</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Funcionário</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Produto</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Qtd</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Data</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Obs</th>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Ações</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
<!-- Conteúdo dinâmico será inserido aqui -->
</tbody>
</table>
</div>
<div class="p-4 border-t border-gray-200 dark:border-gray-700 text-sm text-gray-500 dark:text-gray-400">
Total de <span id="total-lancamentos" class="font-medium">0</span> lançamentos
</div>
</div>
</div>
<!-- Seção Relatório -->
<div id="relatorio-section" class="section hidden">
<div class="flex items-center justify-between mb-6">
<h2 class="text-2xl font-bold text-gray-800 dark:text-white flex items-center space-x-2">
<i data-lucide="bar-chart-3" class="w-6 h-6 text-indigo-600 dark:text-indigo-400"></i>
<span>Relatório</span>
</h2>
</div>
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-md p-4 mb-6">
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
<div>
<label for="filtro-func" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Funcionário</label>
<select id="filtro-func" class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
<option value="">Todos</option>
</select>
</div>
<div>
<label for="filtro-de" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Data Inicial</label>
<input type="date" id="filtro-de" class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
</div>
<div>
<label for="filtro-ate" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Data Final</label>
<input type="date" id="filtro-ate" class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
</div>
<div class="flex items-end space-x-3">
<button onclick="gerarRelatorio()" class="flex-1 flex items-center justify-center space-x-1 px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg transition-colors">
<i data-lucide="file-text" class="w-4 h-4"></i>
<span>Gerar</span>
</button>
<button onclick="imprimirRelatorio()" class="flex-1 flex items-center justify-center space-x-1 px-4 py-2 bg-gray-600 hover:bg-gray-700 text-white rounded-lg transition-colors">
<i data-lucide="file-down" class="w-4 h-4"></i>
<span>PDF</span>
</button>
</div>
</div>
</div>
<div id="relatorio" class="bg-white dark:bg-gray-800 rounded-xl shadow-md overflow-hidden">
<div class="empty-state p-12 text-center">
<div class="inline-flex items-center justify-center w-16 h-16 rounded-full bg-gray-100 dark:bg-gray-700 mb-4">
<i data-lucide="search-check" class="w-8 h-8 text-gray-400"></i>
</div>
<h3 class="text-lg font-medium text-gray-800 dark:text-white mb-2">Nenhum relatório gerado</h3>
<p class="text-gray-500 dark:text-gray-400 max-w-md mx-auto">Utilize o painel de controle acima para gerar um novo relatório.</p>
</div>
</div>
</div>
<!-- Seção Configurações -->
<div id="configuracoes-section" class="section hidden">
<div class="flex items-center justify-between mb-6">
<h2 class="text-2xl font-bold text-gray-800 dark:text-white flex items-center space-x-2">
<i data-lucide="settings" class="w-6 h-6 text-indigo-600 dark:text-indigo-400"></i>
<span>Configurações</span>
</h2>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-md p-6 border border-gray-200 dark:border-gray-700">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold text-gray-800 dark:text-white">Aparência</h3>
<div class="p-2 rounded-lg bg-indigo-50 dark:bg-indigo-900/30">
<i data-lucide="palette" class="w-5 h-5 text-indigo-600 dark:text-indigo-400"></i>
</div>
</div>
<div class="space-y-4">
<div class="flex items-center justify-between">
<div>
<p class="text-sm font-medium text-gray-700 dark:text-gray-300">Tema Escuro</p>
<p class="text-xs text-gray-500 dark:text-gray-400">Alterne entre os modos claro e escuro</p>
</div>
<button id="theme-toggle-btn" class="relative inline-flex h-6 w-11 items-center rounded-full bg-gray-200 dark:bg-gray-600 transition-colors">
<span class="sr-only">Alterar tema</span>
<span class="inline-block h-4 w-4 transform rounded-full bg-white dark:bg-gray-300 transition translate-x-1 dark:translate-x-6"></span>
</button>
</div>
</div>
</div>
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-md p-6 border border-gray-200 dark:border-gray-700">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold text-gray-800 dark:text-white">Backup</h3>
<div class="p-2 rounded-lg bg-green-50 dark:bg-green-900/30">
<i data-lucide="database" class="w-5 h-5 text-green-600 dark:text-green-400"></i>
</div>
</div>
<div class="space-y-4">
<div class="flex items-center justify-between">
<div>
<p class="text-sm font-medium text-gray-700 dark:text-gray-300">Exportar Dados</p>
<p class="text-xs text-gray-500 dark:text-gray-400">Salve um backup completo dos dados</p>
</div>
<button onclick="exportarDB()" class="flex items-center space-x-1 px-3 py-1.5 text-sm bg-green-50 dark:bg-green-900/30 text-green-600 dark:text-green-400 rounded-lg hover:bg-green-100 dark:hover:bg-green-800/50 transition-colors">
<i data-lucide="download" class="w-4 h-4"></i>
<span>Exportar</span>
</button>
</div>
<div class="flex items-center justify-between">
<div>
<p class="text-sm font-medium text-gray-700 dark:text-gray-300">Importar Dados</p>
<p class="text-xs text-gray-500 dark:text-gray-400">Carregue um arquivo de backup</p>
</div>
<div>
<input type="file" id="importFile" class="hidden" onchange="importarDB(event)">
<button onclick="document.getElementById('importFile').click()" class="flex items-center space-x-1 px-3 py-1.5 text-sm bg-blue-50 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400 rounded-lg hover:bg-blue-100 dark:hover:bg-blue-800/50 transition-colors">
<i data-lucide="upload" class="w-4 h-4"></i>
<span>Importar</span>
</button>
</div>
</div>
</div>
</div>
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-md p-6 border border-gray-200 dark:border-gray-700">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold text-gray-800 dark:text-white">Segurança</h3>
<div class="p-2 rounded-lg bg-red-50 dark:bg-red-900/30">
<i data-lucide="shield" class="w-5 h-5 text-red-600 dark:text-red-400"></i>
</div>
</div>
<div class="space-y-4">
<div class="flex items-center justify-between">
<div>
<p class="text-sm font-medium text-gray-700 dark:text-gray-300">Senha de Administrador</p>
<p class="text-xs text-gray-500 dark:text-gray-400">Altere a senha para ações restritas</p>
</div>
<button onclick="openChangePasswordModal()" class="flex items-center space-x-1 px-3 py-1.5 text-sm bg-indigo-50 dark:bg-indigo-900/30 text-indigo-600 dark:text-indigo-400 rounded-lg hover:bg-indigo-100 dark:hover:bg-indigo-800/50 transition-colors">
<i data-lucide="key-round" class="w-4 h-4"></i>
<span>Alterar</span>
</button>
</div>
</div>
</div>
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-md p-6 border border-gray-200 dark:border-gray-700">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold text-gray-800 dark:text-white">Sobre</h3>
<div class="p-2 rounded-lg bg-purple-50 dark:bg-purple-900/30">
<i data-lucide="info" class="w-5 h-5 text-purple-600 dark:text-purple-400"></i>
</div>
</div>
<div class="space-y-3">
<div class="flex items-center space-x-3">
<div class="p-2 rounded-lg bg-gray-100 dark:bg-gray-700">
<i data-lucide="code" class="w-5 h-5 text-gray-600 dark:text-gray-300"></i>
</div>
<div>
<p class="text-sm font-medium text-gray-700 dark:text-gray-300">Versão</p>
<p class="text-xs text-gray-500 dark:text-gray-400">3.0.0</p>
</div>
</div>
<div class="flex items-center space-x-3">
<div class="p-2 rounded-lg bg-gray-100 dark:bg-gray-700">
<i data-lucide="user" class="w-5 h-5 text-gray-600 dark:text-gray-300"></i>
</div>
<div>
<p class="text-sm font-medium text-gray-700 dark:text-gray-300">Desenvolvedor</p>
<p class="text-xs text-gray-500 dark:text-gray-400">JhonnyDarks</p>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
<!-- Rodapé -->
<footer class="bg-white dark:bg-gray-800 border-t border-gray-200 dark:border-gray-700 py-4">
<div class="container mx-auto px-4 text-center text-sm text-gray-500 dark:text-gray-400">
<p>© 2023 Controle de Consumo. Todos os direitos reservados.</p>
</div>
</footer>
<!-- Modais -->
<div id="loading-overlay" class="fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center hidden">
<div class="bg-white dark:bg-gray-800 rounded-xl p-6 max-w-sm w-full mx-4 shadow-xl">
<div class="flex flex-col items-center">
<div class="w-12 h-12 border-4 border-indigo-500 border-t-transparent rounded-full animate-spin mb-4"></div>
<h3 class="text-lg font-medium text-gray-800 dark:text-white mb-2">Carregando...</h3>
<p class="text-gray-500 dark:text-gray-400 text-center">Por favor, aguarde enquanto processamos sua solicitação.</p>
</div>
</div>
</div>
<div id="toast" class="fixed bottom-4 left-1/2 transform -translate-x-1/2 bg-gray-800 dark:bg-gray-700 text-white px-6 py-3 rounded-lg shadow-lg hidden z-50">
<div class="flex items-center space-x-2">
<i data-lucide="info" class="w-5 h-5 text-white"></i>
<span id="toast-message">Mensagem de exemplo</span>
</div>
</div>
<!-- Modal de Confirmação -->
<div id="confirmationModal" class="fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center hidden">
<div class="bg-white dark:bg-gray-800 rounded-xl p-6 max-w-md w-full mx-4 shadow-xl">
<div class="flex justify-between items-start mb-4">
<h3 id="modalTitle" class="text-xl font-bold text-gray-800 dark:text-white">Confirmação</h3>
<button onclick="document.getElementById('confirmationModal').classList.add('hidden')" class="text-gray-400 hover:text-gray-500 dark:hover:text-gray-300">
<i data-lucide="x" class="w-6 h-6"></i>
</button>
</div>
<p id="modalMessage" class="text-gray-600 dark:text-gray-300 mb-6">Você tem certeza que deseja realizar esta ação?</p>
<div class="flex justify-end space-x-3">
<button id="cancelButton" class="px-4 py-2 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
Cancelar
</button>
<button id="confirmButton" class="px-4 py-2 bg-red-600 hover:bg-red-700 text-white rounded-lg transition-colors">
Confirmar
</button>
</div>
</div>
</div>
<!-- Modal de Senha -->
<div id="passwordModal" class="fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center hidden">
<div class="bg-white dark:bg-gray-800 rounded-xl p-6 max-w-md w-full mx-4 shadow-xl">
<div class="flex justify-between items-start mb-4">
<h3 class="text-xl font-bold text-gray-800 dark:text-white">Acesso Restrito</h3>
<button onclick="closePasswordModal()" class="text-gray-400 hover:text-gray-500 dark:hover:text-gray-300">
<i data-lucide="x" class="w-6 h-6"></i>
</button>
</div>
<p class="text-gray-600 dark:text-gray-300 mb-4">Esta ação requer autenticação. Por favor, insira a senha de administrador.</p>
<div class="mb-4">
<label for="admin-password" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Senha</label>
<div class="relative">
<input type="password" id="admin-password" placeholder="Digite a senha" class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white pr-10">
<button onclick="togglePasswordVisibility('admin-password', this)" class="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-500 dark:hover:text-gray-300">
<i data-lucide="eye" class="w-5 h-5"></i>
</button>
</div>
</div>
<div class="flex justify-end space-x-3">
<button onclick="closePasswordModal()" class="px-4 py-2 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
Cancelar
</button>
<button id="confirmPasswordButton" class="px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg transition-colors">
Confirmar
</button>
</div>
</div>
</div>
<!-- Modal de Alteração de Senha -->
<div id="changePasswordModal" class="fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center hidden">
<div class="bg-white dark:bg-gray-800 rounded-xl p-6 max-w-md w-full mx-4 shadow-xl">
<div class="flex justify-between items-start mb-4">
<h3 class="text-xl font-bold text-gray-800 dark:text-white">Alterar Senha</h3>
<button onclick="closeChangePasswordModal()" class="text-gray-400 hover:text-gray-500 dark:hover:text-gray-300">
<i data-lucide="x" class="w-6 h-6"></i>
</button>
</div>
<div class="space-y-4">
<div>
<label for="current-admin-password" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Senha Atual</label>
<div class="relative">
<input type="password" id="current-admin-password" placeholder="Digite a senha atual" class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white pr-10">
<button onclick="togglePasswordVisibility('current-admin-password', this)" class="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-500 dark:hover:text-gray-300">
<i data-lucide="eye" class="w-5 h-5"></i>
</button>
</div>
</div>
<div>
<label for="new-admin-password" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Nova Senha</label>
<div class="relative">
<input type="password" id="new-admin-password" placeholder="Digite a nova senha" class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white pr-10">
<button onclick="togglePasswordVisibility('new-admin-password', this)" class="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-500 dark:hover:text-gray-300">
<i data-lucide="eye" class="w-5 h-5"></i>
</button>
</div>
</div>
<div>
<label for="confirm-new-admin-password" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Confirmar Nova Senha</label>
<div class="relative">
<input type="password" id="confirm-new-admin-password" placeholder="Confirme a nova senha" class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white pr-10">
<button onclick="togglePasswordVisibility('confirm-new-admin-password', this)" class="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-500 dark:hover:text-gray-300">
<i data-lucide="eye" class="w-5 h-5"></i>
</button>
</div>
</div>
</div>
<div class="flex justify-end space-x-3 mt-6">
<button onclick="closeChangePasswordModal()" class="px-4 py-2 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
Cancelar
</button>
<button id="saveNewPasswordButton" class="px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg transition-colors">
Salvar
</button>
</div>
</div>
</div>
<!-- Modal de Funcionário -->
<div id="funcionarioFormModal" class="fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center hidden">
<div class="bg-white dark:bg-gray-800 rounded-xl p-6 max-w-md w-full mx-4 shadow-xl">
<div class="flex justify-between items-start mb-4">
<h3 id="funcionarioModalTitle" class="text-xl font-bold text-gray-800 dark:text-white">Cadastrar Funcionário</h3>
<button onclick="document.getElementById('funcionarioFormModal').classList.add('hidden')" class="text-gray-400 hover:text-gray-500 dark:hover:text-gray-300">
<i data-lucide="x" class="w-6 h-6"></i>
</button>
</div>
<input type="hidden" id="func-id">
<div class="space-y-4">
<div>
<label for="func-nome-modal" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Nome</label>
<input type="text" id="func-nome-modal" class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white" placeholder="Nome do funcionário">
</div>
<div>
<label for="func-setor-modal" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Setor</label>
<input type="text" id="func-setor-modal" class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white" placeholder="Setor do funcionário">
</div>
</div>
<div class="flex justify-end space-x-3 mt-6">
<button id="cancelFuncionarioButton" class="px-4 py-2 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
Cancelar
</button>
<button id="saveFuncionarioButton" class="px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg transition-colors">
Salvar
</button>
</div>
</div>
</div>
<!-- Modal de Produto -->
<div id="produtoFormModal" class="fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center hidden">
<div class="bg-white dark:bg-gray-800 rounded-xl p-6 max-w-md w-full mx-4 shadow-xl">
<div class="flex justify-between items-start mb-4">
<h3 id="produtoModalTitle" class="text-xl font-bold text-gray-800 dark:text-white">Cadastrar Produto</h3>
<button onclick="document.getElementById('produtoFormModal').classList.add('hidden')" class="text-gray-400 hover:text-gray-500 dark:hover:text-gray-300">
<i data-lucide="x" class="w-6 h-6"></i>
</button>
</div>
<input type="hidden" id="prod-id">
<div class="space-y-4">
<div>
<label for="prod-nome-modal" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Nome</label>
<input type="text" id="prod-nome-modal" class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white" placeholder="Nome do produto">
</div>
<div>
<label for="prod-valor-modal" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Valor</label>
<input type="number" step="0.01" id="prod-valor-modal" class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white" placeholder="Valor unitário">
</div>
</div>
<div class="flex justify-end space-x-3 mt-6">
<button id="cancelProdutoButton" class="px-4 py-2 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
Cancelar
</button>
<button id="saveProdutoButton" class="px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg transition-colors">
Salvar
</button>
</div>
</div>
</div>
<!-- Modal de Lançamento -->
<div id="lancamentoFormModal" class="fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center hidden">
<div class="bg-white dark:bg-gray-800 rounded-xl p-6 max-w-md w-full mx-4 shadow-xl">
<div class="flex justify-between items-start mb-4">
<h3 id="lancamentoModalTitle" class="text-xl font-bold text-gray-800 dark:text-white">Lançar Consumo</h3>
<button onclick="closeLancamentoModal()" class="text-gray-400 hover:text-gray-500 dark:hover:text-gray-300">
<i data-lucide="x" class="w-6 h-6"></i>
</button>
</div>
<input type="hidden" id="cons-id">
<div class="space-y-4">
<div>
<label for="cons-func-modal" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Funcionário</label>
<select id="cons-func-modal" class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
<option value="">Selecione um funcionário</option>
</select>
</div>
<div>
<label for="cons-prod-modal" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Produto</label>
<select id="cons-prod-modal" class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
<option value="">Selecione um produto</option>
</select>
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label for="cons-qtd-modal" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Quantidade</label>
<div class="flex items-center border border-gray-300 dark:border-gray-600 rounded-lg overflow-hidden">
<button type="button" class="quantity-btn minus px-3 py-2 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors">
<i data-lucide="minus" class="w-4 h-4"></i>
</button>
<input type="number" id="cons-qtd-modal" value="1" min="1" class="w-full px-3 py-2 text-center border-0 focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:text-white">
<button type="button" class="quantity-btn plus px-3 py-2 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors">
<i data-lucide="plus" class="w-4 h-4"></i>
</button>
</div>
</div>
<div>
<label for="cons-data-modal" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Data</label>
<input type="date" id="cons-data-modal" class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
</div>
</div>
<div>
<label for="cons-obs-modal" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Observação</label>
<input type="text" id="cons-obs-modal" class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white" placeholder="Ex: Café da manhã">
</div>
</div>
<div class="flex justify-end space-x-3 mt-6">
<button id="cancelLancamentoButton" class="px-4 py-2 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
Cancelar
</button>
<button id="saveLancamentoButton" class="px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg transition-colors">
Lançar
</button>
</div>
</div>
</div>
<script>
// --- Variáveis Globais ---
let db;
let SQL;
let currentConfirmAction = null;
let mainDashboardChart = null;
let currentPasswordCallback = null;
let ADMIN_PASSWORD; // Não é mais uma const, será carregada/salva no localStorage
// --- Funções de UI e Animação ---
function showLoading() {
document.getElementById('loading-overlay').classList.remove('hidden');
}
function hideLoading() {
document.getElementById('loading-overlay').classList.add('hidden');
}
function applyTheme(theme) {
if (theme === 'dark') {
document.documentElement.classList.add('dark');
localStorage.setItem('theme', 'dark');
document.getElementById('theme-toggle-btn').innerHTML = '<i data-lucide="sun" class="w-5 h-5"></i>';
} else {
document.documentElement.classList.remove('dark');
localStorage.setItem('theme', 'light');
document.getElementById('theme-toggle-btn').innerHTML = '<i data-lucide="moon" class="w-5 h-5"></i>';
}
lucide.createIcons();
}
function getFormattedDate(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
function showTab(index) {
// Esconde todas as seções
document.querySelectorAll('.section').forEach(sec => sec.classList.add('hidden'));
document.querySelectorAll('.tab-button').forEach(tab => tab.classList.remove('active', 'border-indigo-600', 'text-indigo-600', 'dark:text-indigo-400'));
// Mostra a seção selecionada
const sections = document.querySelectorAll('.section');
sections[index].classList.remove('hidden');
// Ativa a aba selecionada
const tabs = document.querySelectorAll('.tab-button');
tabs[index].classList.add('active', 'border-indigo-600', 'text-indigo-600', 'dark:text-indigo-400');
// Atualiza o conteúdo conforme necessário
if (index === 0) {
updateCurrentMonth();
renderDashboard();
} else if (index === 1) listarFuncionarios();
else if (index === 2) listarProdutos();
else if (index === 3) {
setLancamentoDateFiltersToCurrentMonth();
listarLancamentos(true);
} else if (index === 4) {
setDataHoje();
atualizarCombos();
}
// Atualiza ícones
lucide.createIcons();
}
function updateCurrentMonth() {
const months = ["Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"];
const currentDate = new Date();
document.getElementById('current-month').textContent = `${months[currentDate.getMonth()]} ${currentDate.getFullYear()}`;
}
function setLancamentoDateFiltersToCurrentMonth() {
const hoje = new Date();
const primeiroDia = new Date(hoje.getFullYear(), hoje.getMonth(), 1);
const ultimoDia = new Date(hoje.getFullYear(), hoje.getMonth() + 1, 0);
document.getElementById('lanc-filtro-de').value = getFormattedDate(primeiroDia);
document.getElementById('lanc-filtro-ate').value = getFormattedDate(ultimoDia);
}
function goToPreviousMonth() {
const inputDe = document.getElementById('lanc-filtro-de');
const inputAte = document.getElementById('lanc-filtro-ate');
let dataAtualDe = new Date(inputDe.value + 'T00:00:00');
// Calcula o primeiro dia do mês anterior ao mês da data de início atual
let primeiroDiaMesAnterior = new Date(dataAtualDe.getFullYear(), dataAtualDe.getMonth() - 1, 1);
// Calcula o último dia do mês anterior ao mês da data de início atual
let ultimoDiaMesAnterior = new Date(primeiroDiaMesAnterior.getFullYear(), primeiroDiaMesAnterior.getMonth() + 1, 0);
inputDe.value = getFormattedDate(primeiroDiaMesAnterior);
inputAte.value = getFormattedDate(ultimoDiaMesAnterior);
listarLancamentos(true); // Chama a função para listar com os novos filtros de data
}
function showToast(msg, type = 'info') {
const toast = document.getElementById('toast');
const toastMessage = document.getElementById('toast-message');
const toastIcon = toast.querySelector('i');
// Define a cor com base no tipo
if (type === 'error') {
toast.classList.add('bg-red-600', 'dark:bg-red-700');
toastIcon.setAttribute('data-lucide', 'alert-circle');
} else if (type === 'success') {
toast.classList.add('bg-green-600', 'dark:bg-green-700');
toastIcon.setAttribute('data-lucide', 'check-circle');
} else {
toast.classList.add('bg-indigo-600', 'dark:bg-indigo-700');
toastIcon.setAttribute('data-lucide', 'info');
}
toastMessage.textContent = msg;
toast.classList.remove('hidden');
lucide.createIcons();
setTimeout(() => {
toast.classList.add('hidden');
toast.classList.remove('bg-red-600', 'dark:bg-red-700', 'bg-green-600', 'dark:bg-green-700', 'bg-indigo-600', 'dark:bg-indigo-700');
}, 3000);
}
function getDataLocal() {
const hoje = new Date();
const ano = hoje.getFullYear();
const mes = String(hoje.getMonth() + 1).padStart(2, '0');
const dia = String(hoje.getDate()).padStart(2, '0');
return `${ano}-${mes}-${dia}`;
}
function setDataHoje() {
const hoje = getDataLocal();
document.getElementById('filtro-de').value = hoje;
document.getElementById('filtro-ate').value = hoje;
}
function showConfirmationModal(title, message, onConfirm, requiresPassword = false) {
const modal = document.getElementById('confirmationModal');
document.getElementById('modalTitle').textContent = title;
document.getElementById('modalMessage').textContent = message;
if (requiresPassword) {
document.getElementById('confirmButton').onclick = () => {
document.getElementById('confirmationModal').classList.add('hidden');
openPasswordModal(onConfirm);
};
} else {
document.getElementById('confirmButton').onclick = () => {
if (onConfirm) onConfirm();
document.getElementById('confirmationModal').classList.add('hidden');
currentConfirmAction = null;
};
}
modal.classList.remove('hidden');
document.getElementById('cancelButton').onclick = () => {
document.getElementById('confirmationModal').classList.add('hidden');
currentConfirmAction = null;
};
}
function openPasswordModal(callback) {
document.getElementById('admin-password').value = ''; // Limpa o campo da senha
document.getElementById('passwordModal').classList.remove('hidden');
currentPasswordCallback = callback; // Armazena a função de callback
}
function closePasswordModal() {
document.getElementById('passwordModal').classList.add('hidden');
currentPasswordCallback = null;
}
function openChangePasswordModal() {
document.getElementById('current-admin-password').value = '';
document.getElementById('new-admin-password').value = '';
document.getElementById('confirm-new-admin-password').value = '';
document.getElementById('changePasswordModal').classList.remove('hidden');
lucide.createIcons();
}
<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=JonathanCelular/funcionarios" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>