test / index.html
fakesisalg's picture
Add 3 files
a93de95 verified
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sistema de Inventario</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.sidebar {
transition: all 0.3s;
}
@media (max-width: 768px) {
.sidebar {
transform: translateX(-100%);
}
.sidebar.active {
transform: translateX(0);
}
}
.product-card:hover {
transform: translateY(-2px);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
}
</style>
</head>
<body class="bg-gray-100 font-sans">
<!-- Login Screen -->
<div id="login-screen" class="fixed inset-0 flex items-center justify-center bg-gray-900 bg-opacity-75 z-50">
<div class="bg-white rounded-lg shadow-xl p-8 w-full max-w-md">
<div class="text-center mb-8">
<i class="fas fa-boxes text-4xl text-blue-600 mb-4"></i>
<h1 class="text-3xl font-bold text-gray-800">Sistema de Inventario</h1>
<p class="text-gray-600 mt-2">Ingrese sus credenciales para acceder</p>
</div>
<form id="login-form" class="space-y-6">
<div>
<label for="username" class="block text-sm font-medium text-gray-700 mb-1">Usuario</label>
<input type="text" id="username" name="username" required
class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<div>
<label for="password" class="block text-sm font-medium text-gray-700 mb-1">Contraseña</label>
<input type="password" id="password" name="password" required
class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<div>
<button type="submit"
class="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 transition duration-300 flex items-center justify-center">
<i class="fas fa-sign-in-alt mr-2"></i> Iniciar Sesión
</button>
</div>
<div class="text-center text-sm text-gray-500">
<p>Usuario demo: admin / pass123 (rol admin)</p>
<p>Usuario demo: user / pass123 (rol usuario)</p>
</div>
</form>
</div>
</div>
<!-- Main App (hidden until login) -->
<div id="app-container" class="hidden">
<!-- Sidebar -->
<div class="sidebar fixed inset-y-0 left-0 bg-gray-800 text-white w-64 z-40">
<div class="p-4 flex items-center border-b border-gray-700">
<i class="fas fa-boxes text-2xl text-blue-400 mr-3"></i>
<h1 class="text-xl font-bold">Inventario</h1>
<button id="close-sidebar" class="ml-auto md:hidden">
<i class="fas fa-times"></i>
</button>
</div>
<nav class="p-4">
<div class="mb-8">
<p class="text-gray-400 uppercase text-xs font-bold mb-4">Menú Principal</p>
<a href="#" class="flex items-center py-2 px-3 bg-gray-700 rounded-md text-white mb-2">
<i class="fas fa-tachometer-alt mr-3"></i> Dashboard
</a>
<a href="#" id="nav-products" class="flex items-center py-2 px-3 hover:bg-gray-700 rounded-md text-gray-300 hover:text-white mb-2">
<i class="fas fa-boxes mr-3"></i> Productos
</a>
<a href="#" id="nav-add-product" class="flex items-center py-2 px-3 hover:bg-gray-700 rounded-md text-gray-300 hover:text-white mb-2">
<i class="fas fa-plus-circle mr-3"></i> Agregar Producto
</a>
</div>
<div>
<p class="text-gray-400 uppercase text-xs font-bold mb-4">Usuario</p>
<a href="#" id="logout-btn" class="flex items-center py-2 px-3 hover:bg-gray-700 rounded-md text-gray-300 hover:text-white">
<i class="fas fa-sign-out-alt mr-3"></i> Cerrar Sesión
</a>
</div>
</nav>
</div>
<!-- Main Content -->
<div class="ml-0 md:ml-64 transition-all duration-300">
<!-- Header -->
<header class="bg-white shadow-sm py-4 px-6 flex items-center justify-between sticky top-0 z-30">
<button id="menu-toggle" class="md:hidden text-gray-600">
<i class="fas fa-bars text-xl"></i>
</button>
<div class="flex items-center">
<div class="mr-4">
<span id="user-role-badge" class="px-3 py-1 rounded-full text-xs font-semibold"></span>
</div>
<div class="relative">
<button id="user-menu-btn" class="flex items-center focus:outline-none">
<span id="username-display" class="mr-2 font-medium"></span>
<i class="fas fa-user-circle text-2xl text-gray-600"></i>
</button>
</div>
</div>
</header>
<!-- Dashboard Content -->
<main class="p-6">
<!-- Dashboard Overview -->
<div id="dashboard-view">
<div class="mb-8">
<h2 class="text-2xl font-bold text-gray-800 mb-6">Resumen del Inventario</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="p-3 rounded-full bg-blue-100 text-blue-600 mr-4">
<i class="fas fa-boxes text-xl"></i>
</div>
<div>
<p class="text-gray-500 text-sm">Productos totales</p>
<h3 class="text-2xl font-bold" id="total-products">0</h3>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="p-3 rounded-full bg-green-100 text-green-600 mr-4">
<i class="fas fa-box-open text-xl"></i>
</div>
<div>
<p class="text-gray-500 text-sm">Stock disponible</p>
<h3 class="text-2xl font-bold" id="total-stock">0</h3>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="p-3 rounded-full bg-purple-100 text-purple-600 mr-4">
<i class="fas fa-tags text-xl"></i>
</div>
<div>
<p class="text-gray-500 text-sm">Valor total</p>
<h3 class="text-2xl font-bold" id="total-value">$0</h3>
</div>
</div>
</div>
</div>
</div>
<div>
<h2 class="text-2xl font-bold text-gray-800 mb-6">Productos con bajo stock</h2>
<div class="bg-white rounded-lg shadow overflow-hidden">
<div id="low-stock-products" class="divide-y divide-gray-200">
<!-- Low stock products will be loaded here -->
<div class="p-4 text-center text-gray-500">
<i class="fas fa-spinner fa-spin mr-2"></i> Cargando productos...
</div>
</div>
</div>
</div>
</div>
<!-- Products List View -->
<div id="products-view" class="hidden">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-bold text-gray-800">Lista de Productos</h2>
<button id="add-product-btn" class="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition duration-300 flex items-center">
<i class="fas fa-plus mr-2"></i> Agregar Producto
</button>
</div>
<div class="bg-white rounded-lg shadow overflow-hidden">
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Nombre</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Categoría</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Precio</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Cantidad</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Acciones</th>
</tr>
</thead>
<tbody id="products-table-body" class="bg-white divide-y divide-gray-200">
<!-- Products will be loaded here -->
<tr>
<td colspan="5" class="px-6 py-4 text-center text-gray-500">
<i class="fas fa-spinner fa-spin mr-2"></i> Cargando productos...
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- Add/Edit Product Form -->
<div id="product-form-view" class="hidden">
<div class="mb-6">
<h2 class="text-2xl font-bold text-gray-800" id="form-title">Agregar Nuevo Producto</h2>
</div>
<div class="bg-white rounded-lg shadow p-6">
<form id="product-form" class="space-y-6">
<input type="hidden" id="product-id">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label for="product-name" class="block text-sm font-medium text-gray-700 mb-1">Nombre del Producto *</label>
<input type="text" id="product-name" name="product-name" required
class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<div>
<label for="product-category" class="block text-sm font-medium text-gray-700 mb-1">Categoría *</label>
<select id="product-category" name="product-category" required
class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
<option value="">Seleccione una categoría</option>
<option value="Electrónicos">Electrónicos</option>
<option value="Ropa">Ropa</option>
<option value="Alimentos">Alimentos</option>
<option value="Hogar">Hogar</option>
<option value="Oficina">Oficina</option>
</select>
</div>
<div>
<label for="product-price" class="block text-sm font-medium text-gray-700 mb-1">Precio *</label>
<div class="relative">
<span class="absolute inset-y-0 left-0 flex items-center pl-3 text-gray-500">$</span>
<input type="number" id="product-price" name="product-price" step="0.01" min="0" required
class="w-full pl-8 px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
</div>
<div>
<label for="product-quantity" class="block text-sm font-medium text-gray-700 mb-1">Cantidad *</label>
<input type="number" id="product-quantity" name="product-quantity" min="0" required
class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
</div>
<div>
<label for="product-description" class="block text-sm font-medium text-gray-700 mb-1">Descripción</label>
<textarea id="product-description" name="product-description" rows="3"
class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"></textarea>
</div>
<div class="flex justify-end space-x-4">
<button type="button" id="cancel-form-btn" class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50 transition duration-300">
Cancelar
</button>
<button type="submit" class="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition duration-300">
Guardar Producto
</button>
</div>
</form>
</div>
</div>
</main>
</div>
<!-- Edit Product Modal -->
<div id="edit-product-modal" class="fixed inset-0 bg-gray-900 bg-opacity-75 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-lg shadow-xl p-6 w-full max-w-md">
<div class="flex justify-between items-center mb-4">
<h3 class="text-xl font-bold text-gray-800">Editar Producto</h3>
<button id="close-edit-modal" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<form id="edit-product-form" class="space-y-4">
<input type="hidden" id="edit-product-id">
<div>
<label for="edit-product-name" class="block text-sm font-medium text-gray-700 mb-1">Nombre</label>
<input type="text" id="edit-product-name" class="w-full px-4 py-2 border border-gray-300 rounded-md">
</div>
<div>
<label for="edit-product-price" class="block text-sm font-medium text-gray-700 mb-1">Precio</label>
<input type="number" id="edit-product-price" step="0.01" class="w-full px-4 py-2 border border-gray-300 rounded-md">
</div>
<div>
<label for="edit-product-quantity" class="block text-sm font-medium text-gray-700 mb-1">Cantidad</label>
<input type="number" id="edit-product-quantity" class="w-full px-4 py-2 border border-gray-300 rounded-md">
</div>
<div class="flex justify-end space-x-3 pt-4">
<button type="button" id="cancel-edit-btn" class="px-4 py-2 border border-gray-300 rounded-md">
Cancelar
</button>
<button type="submit" class="bg-blue-600 text-white px-4 py-2 rounded-md">
Guardar Cambios
</button>
</div>
</form>
</div>
</div>
<!-- Delete Confirmation Modal -->
<div id="delete-modal" class="fixed inset-0 bg-gray-900 bg-opacity-75 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-lg shadow-xl p-6 w-full max-w-md">
<div class="flex justify-between items-center mb-4">
<h3 class="text-xl font-bold text-gray-800">Confirmar Eliminación</h3>
<button id="close-delete-modal" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<p class="text-gray-700 mb-6">¿Estás seguro de que deseas eliminar este producto? Esta acción no se puede deshacer.</p>
<div class="flex justify-end space-x-3">
<button type="button" id="cancel-delete-btn" class="px-4 py-2 border border-gray-300 rounded-md">
Cancelar
</button>
<button type="button" id="confirm-delete-btn" class="bg-red-600 text-white px-4 py-2 rounded-md">
Eliminar
</button>
</div>
</div>
</div>
</div>
<script>
// Mock database
const mockDatabase = {
users: [
{ id: 1, username: 'admin', password: 'pass123', role: 'admin', name: 'Administrador' },
{ id: 2, username: 'user', password: 'pass123', role: 'user', name: 'Usuario Regular' }
],
products: [
{ id: 1, name: 'Laptop HP', category: 'Electrónicos', price: 1200, quantity: 15, description: 'Laptop HP con 16GB RAM y 512GB SSD' },
{ id: 2, name: 'Smartphone Samsung', category: 'Electrónicos', price: 800, quantity: 25, description: 'Smartphone Samsung Galaxy S21' },
{ id: 3, name: 'Camisa de algodón', category: 'Ropa', price: 25, quantity: 50, description: 'Camisa 100% algodón talla M' },
{ id: 4, name: 'Arroz 5kg', category: 'Alimentos', price: 10, quantity: 40, description: 'Arroz blanco grano largo 5kg' },
{ id: 5, name: 'Silla de oficina', category: 'Oficina', price: 150, quantity: 10, description: 'Silla ergonómica para oficina' },
{ id: 6, name: 'Monitor 24"', category: 'Electrónicos', price: 180, quantity: 2, description: 'Monitor LED 24 pulgadas Full HD' },
{ id: 7, name: 'Juego de sábanas', category: 'Hogar', price: 45, quantity: 15, description: 'Juego de sábanas de algodón king size' }
]
};
// App state
const state = {
currentUser: null,
currentView: 'dashboard',
products: [],
productToDelete: null,
productToEdit: null
};
// DOM Elements
const loginScreen = document.getElementById('login-screen');
const appContainer = document.getElementById('app-container');
const loginForm = document.getElementById('login-form');
const usernameInput = document.getElementById('username');
const passwordInput = document.getElementById('password');
const logoutBtn = document.getElementById('logout-btn');
const usernameDisplay = document.getElementById('username-display');
const userRoleBadge = document.getElementById('user-role-badge');
const menuToggle = document.getElementById('menu-toggle');
const closeSidebar = document.getElementById('close-sidebar');
const sidebar = document.querySelector('.sidebar');
const navProducts = document.getElementById('nav-products');
const navAddProduct = document.getElementById('nav-add-product');
const dashboardView = document.getElementById('dashboard-view');
const productsView = document.getElementById('products-view');
const productFormView = document.getElementById('product-form-view');
const productsTableBody = document.getElementById('products-table-body');
const addProductBtn = document.getElementById('add-product-btn');
const productForm = document.getElementById('product-form');
const productIdInput = document.getElementById('product-id');
const productNameInput = document.getElementById('product-name');
const productCategoryInput = document.getElementById('product-category');
const productPriceInput = document.getElementById('product-price');
const productQuantityInput = document.getElementById('product-quantity');
const productDescriptionInput = document.getElementById('product-description');
const cancelFormBtn = document.getElementById('cancel-form-btn');
const formTitle = document.getElementById('form-title');
const totalProducts = document.getElementById('total-products');
const totalStock = document.getElementById('total-stock');
const totalValue = document.getElementById('total-value');
const lowStockProducts = document.getElementById('low-stock-products');
const editProductModal = document.getElementById('edit-product-modal');
const closeEditModal = document.getElementById('close-edit-modal');
const cancelEditBtn = document.getElementById('cancel-edit-btn');
const editProductForm = document.getElementById('edit-product-form');
const editProductId = document.getElementById('edit-product-id');
const editProductName = document.getElementById('edit-product-name');
const editProductPrice = document.getElementById('edit-product-price');
const editProductQuantity = document.getElementById('edit-product-quantity');
const deleteModal = document.getElementById('delete-modal');
const closeDeleteModal = document.getElementById('close-delete-modal');
const cancelDeleteBtn = document.getElementById('cancel-delete-btn');
const confirmDeleteBtn = document.getElementById('confirm-delete-btn');
// Event Listeners
document.addEventListener('DOMContentLoaded', () => {
// Initialize mock data
state.products = [...mockDatabase.products];
// Check if user is already logged in (for demo purposes)
const loggedInUser = localStorage.getItem('inventoryUser');
if (loggedInUser) {
state.currentUser = JSON.parse(loggedInUser);
handleSuccessfulLogin();
}
});
loginForm.addEventListener('submit', (e) => {
e.preventDefault();
const username = usernameInput.value.trim();
const password = passwordInput.value.trim();
// Validate credentials
const user = mockDatabase.users.find(u => u.username === username && u.password === password);
if (user) {
state.currentUser = user;
localStorage.setItem('inventoryUser', JSON.stringify(user));
handleSuccessfulLogin();
} else {
alert('Credenciales incorrectas. Intente nuevamente.');
}
});
logoutBtn.addEventListener('click', () => {
state.currentUser = null;
localStorage.removeItem('inventoryUser');
loginScreen.classList.remove('hidden');
appContainer.classList.add('hidden');
usernameInput.value = '';
passwordInput.value = '';
});
menuToggle.addEventListener('click', () => {
sidebar.classList.add('active');
});
closeSidebar.addEventListener('click', () => {
sidebar.classList.remove('active');
});
navProducts.addEventListener('click', (e) => {
e.preventDefault();
showProductsView();
});
navAddProduct.addEventListener('click', (e) => {
e.preventDefault();
showAddProductForm();
});
addProductBtn.addEventListener('click', () => {
showAddProductForm();
});
productForm.addEventListener('submit', (e) => {
e.preventDefault();
const productData = {
name: productNameInput.value.trim(),
category: productCategoryInput.value,
price: parseFloat(productPriceInput.value),
quantity: parseInt(productQuantityInput.value),
description: productDescriptionInput.value.trim()
};
if (productIdInput.value) {
// Edit existing product
const productId = parseInt(productIdInput.value);
const productIndex = state.products.findIndex(p => p.id === productId);
if (productIndex !== -1) {
state.products[productIndex] = {
...state.products[productIndex],
...productData
};
alert('Producto actualizado correctamente');
}
} else {
// Add new product
const newProduct = {
id: state.products.length > 0 ? Math.max(...state.products.map(p => p.id)) + 1 : 1,
...productData
};
state.products.push(newProduct);
alert('Producto agregado correctamente');
}
// Reset form and show products view
productForm.reset();
showProductsView();
updateDashboardStats();
});
cancelFormBtn.addEventListener('click', () => {
productForm.reset();
showProductsView();
});
// Handle edit product modal
document.addEventListener('click', (e) => {
if (e.target.classList.contains('edit-product-btn')) {
const productId = parseInt(e.target.dataset.id);
const product = state.products.find(p => p.id === productId);
if (product) {
state.productToEdit = product;
showEditProductModal(product);
}
}
if (e.target.classList.contains('delete-product-btn')) {
const productId = parseInt(e.target.dataset.id);
const product = state.products.find(p => p.id === productId);
if (product) {
state.productToDelete = product;
showDeleteModal();
}
}
});
editProductForm.addEventListener('submit', (e) => {
e.preventDefault();
if (state.productToEdit) {
const productIndex = state.products.findIndex(p => p.id === state.productToEdit.id);
if (productIndex !== -1) {
state.products[productIndex] = {
...state.products[productIndex],
name: editProductName.value.trim(),
price: parseFloat(editProductPrice.value),
quantity: parseInt(editProductQuantity.value)
};
alert('Producto actualizado correctamente');
hideEditProductModal();
showProductsView();
updateDashboardStats();
}
}
});
closeEditModal.addEventListener('click', hideEditProductModal);
cancelEditBtn.addEventListener('click', hideEditProductModal);
confirmDeleteBtn.addEventListener('click', () => {
if (state.productToDelete) {
state.products = state.products.filter(p => p.id !== state.productToDelete.id);
alert('Producto eliminado correctamente');
hideDeleteModal();
showProductsView();
updateDashboardStats();
}
});
closeDeleteModal.addEventListener('click', hideDeleteModal);
cancelDeleteBtn.addEventListener('click', hideDeleteModal);
// Functions
function handleSuccessfulLogin() {
loginScreen.classList.add('hidden');
appContainer.classList.remove('hidden');
// Update UI with user info
usernameDisplay.textContent = state.currentUser.name;
if (state.currentUser.role === 'admin') {
userRoleBadge.textContent = 'Administrador';
userRoleBadge.classList.add('bg-purple-100', 'text-purple-800');
} else {
userRoleBadge.textContent = 'Usuario';
userRoleBadge.classList.add('bg-blue-100', 'text-blue-800');
}
// Load initial view
showDashboardView();
updateDashboardStats();
}
function showDashboardView() {
state.currentView = 'dashboard';
dashboardView.classList.remove('hidden');
productsView.classList.add('hidden');
productFormView.classList.add('hidden');
// Update active nav item
document.querySelectorAll('nav a').forEach(link => {
link.classList.remove('bg-gray-700', 'text-white');
link.classList.add('text-gray-300', 'hover:bg-gray-700', 'hover:text-white');
});
document.querySelector('nav a:first-child').classList.add('bg-gray-700', 'text-white');
document.querySelector('nav a:first-child').classList.remove('text-gray-300', 'hover:bg-gray-700', 'hover:text-white');
}
function showProductsView() {
state.currentView = 'products';
dashboardView.classList.add('hidden');
productsView.classList.remove('hidden');
productFormView.classList.add('hidden');
// Update active nav item
document.querySelectorAll('nav a').forEach(link => {
link.classList.remove('bg-gray-700', 'text-white');
link.classList.add('text-gray-300', 'hover:bg-gray-700', 'hover:text-white');
});
navProducts.classList.add('bg-gray-700', 'text-white');
navProducts.classList.remove('text-gray-300', 'hover:bg-gray-700', 'hover:text-white');
// Render products table
renderProductsTable();
}
function showAddProductForm() {
state.currentView = 'add-product';
dashboardView.classList.add('hidden');
productsView.classList.add('hidden');
productFormView.classList.remove('hidden');
// Update form title and reset
formTitle.textContent = 'Agregar Nuevo Producto';
productForm.reset();
productIdInput.value = '';
// Update active nav item
document.querySelectorAll('nav a').forEach(link => {
link.classList.remove('bg-gray-700', 'text-white');
link.classList.add('text-gray-300', 'hover:bg-gray-700', 'hover:text-white');
});
navAddProduct.classList.add('bg-gray-700', 'text-white');
navAddProduct.classList.remove('text-gray-300', 'hover:bg-gray-700', 'hover:text-white');
}
function showEditProductForm(product) {
state.currentView = 'edit-product';
dashboardView.classList.add('hidden');
productsView.classList.add('hidden');
productFormView.classList.remove('hidden');
// Update form title and fill with product data
formTitle.textContent = 'Editar Producto';
productIdInput.value = product.id;
productNameInput.value = product.name;
productCategoryInput.value = product.category;
productPriceInput.value = product.price;
productQuantityInput.value = product.quantity;
productDescriptionInput.value = product.description || '';
}
function renderProductsTable() {
if (state.products.length === 0) {
productsTableBody.innerHTML = `
<tr>
<td colspan="5" class="px-6 py-4 text-center text-gray-500">
No hay productos registrados
</td>
</tr>
`;
return;
}
let html = '';
state.products.forEach(product => {
const lowStockClass = product.quantity < 5 ? 'text-red-600 font-semibold' : '';
html += `
<tr class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<div class="text-sm font-medium text-gray-900">${product.name}</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm text-gray-500">${product.category}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm text-gray-900">$${product.price.toFixed(2)}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm ${lowStockClass}">${product.quantity}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
${state.currentUser.role === 'admin' ? `
<button class="edit-product-btn text-blue-600 hover:text-blue-900 mr-4" data-id="${product.id}">
<i class="fas fa-edit"></i>
</button>
<button class="delete-product-btn text-red-600 hover:text-red-900" data-id="${product.id}">
<i class="fas fa-trash-alt"></i>
</button>
` : ''}
</td>
</tr>
`;
});
productsTableBody.innerHTML = html;
}
function updateDashboardStats() {
// Update total products
totalProducts.textContent = state.products.length;
// Update total stock
const totalStockCount = state.products.reduce((sum, product) => sum + product.quantity, 0);
totalStock.textContent = totalStockCount;
// Update total value
const totalValueAmount = state.products.reduce((sum, product) => sum + (product.price * product.quantity), 0);
totalValue.textContent = `$${totalValueAmount.toFixed(2)}`;
// Update low stock products
const lowStockItems = state.products.filter(p => p.quantity < 5);
if (lowStockItems.length === 0) {
lowStockProducts.innerHTML = `
<div class="p-4 text-center text-gray-500">
No hay productos con bajo stock
</div>
`;
} else {
let html = '';
lowStockItems.forEach(product => {
html += `
<div class="p-4 flex items-center justify-between hover:bg-gray-50">
<div>
<h4 class="font-medium text-gray-900">${product.name}</h4>
<p class="text-sm text-gray-500">${product.category}</p>
</div>
<div class="text-red-600 font-semibold">
${product.quantity} en stock
</div>
</div>
`;
});
lowStockProducts.innerHTML = html;
}
}
function showEditProductModal(product) {
editProductId.value = product.id;
editProductName.value = product.name;
editProductPrice.value = product.price;
editProductQuantity.value = product.quantity;
editProductModal.classList.remove('hidden');
}
function hideEditProductModal() {
editProductModal.classList.add('hidden');
state.productToEdit = null;
}
function showDeleteModal() {
deleteModal.classList.remove('hidden');
}
function hideDeleteModal() {
deleteModal.classList.add('hidden');
state.productToDelete = null;
}
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=fakesisalg/test" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>