base-de-datos / index.html
styvencm's picture
necesito que crees una app que comience con un dashboard y permita ver dos opciones, la primera será registrarse, es donde usuario pondra todos sus datos, nombre, apellido y edad luego quedarán guardados, la segunda opcióndel dashboard debe ser poder ver la base de datos de quienes sehan registrado. La app debe tener un diseño modeno e intuitivo - Initial Deployment
a596e49 verified
Raw
History Blame Contribute Delete
28.1 kB
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>User Management App</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>
.fade-in {
animation: fadeIn 0.3s ease-in-out;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.sidebar {
transition: all 0.3s ease;
}
@media (max-width: 768px) {
.sidebar {
transform: translateX(-100%);
position: fixed;
z-index: 50;
height: 100vh;
}
.sidebar.open {
transform: translateX(0);
}
}
</style>
</head>
<body class="bg-gray-100 font-sans">
<!-- Mobile menu button -->
<div class="md:hidden fixed top-4 left-4 z-50">
<button id="menuBtn" class="p-2 rounded-md bg-indigo-600 text-white">
<i class="fas fa-bars"></i>
</button>
</div>
<!-- Sidebar -->
<div id="sidebar" class="sidebar w-64 bg-white shadow-lg fixed h-full">
<div class="p-4 flex items-center space-x-3 border-b border-gray-200">
<div class="w-10 h-10 rounded-full bg-indigo-100 flex items-center justify-center">
<i class="fas fa-user-cog text-indigo-600"></i>
</div>
<h1 class="text-xl font-bold text-gray-800">User Manager</h1>
</div>
<nav class="p-4">
<ul class="space-y-2">
<li>
<button id="dashboardBtn" class="w-full flex items-center space-x-3 p-3 rounded-lg bg-indigo-50 text-indigo-700">
<i class="fas fa-tachometer-alt"></i>
<span>Dashboard</span>
</button>
</li>
<li>
<button id="registerBtn" class="w-full flex items-center space-x-3 p-3 rounded-lg hover:bg-gray-100 text-gray-700">
<i class="fas fa-user-plus"></i>
<span>Registrar Usuario</span>
</button>
</li>
<li>
<button id="databaseBtn" class="w-full flex items-center space-x-3 p-3 rounded-lg hover:bg-gray-100 text-gray-700">
<i class="fas fa-database"></i>
<span>Base de Datos</span>
</button>
</li>
</ul>
</nav>
<div class="absolute bottom-0 w-full p-4 border-t border-gray-200">
<button class="w-full flex items-center space-x-3 p-3 rounded-lg hover:bg-gray-100 text-gray-700">
<i class="fas fa-sign-out-alt"></i>
<span>Cerrar Sesión</span>
</button>
</div>
</div>
<!-- Main Content -->
<div class="md:ml-64 min-h-screen">
<!-- Header -->
<header class="bg-white shadow-sm p-4">
<div class="flex justify-between items-center">
<h2 class="text-xl font-semibold text-gray-800" id="pageTitle">Dashboard</h2>
<div class="flex items-center space-x-4">
<div class="relative">
<i class="fas fa-bell text-gray-500 hover:text-indigo-600 cursor-pointer"></i>
<span class="absolute -top-1 -right-1 w-2 h-2 bg-red-500 rounded-full"></span>
</div>
<div class="flex items-center space-x-2">
<div class="w-8 h-8 rounded-full bg-indigo-100 flex items-center justify-center">
<i class="fas fa-user text-indigo-600 text-sm"></i>
</div>
<span class="text-sm font-medium">Admin</span>
</div>
</div>
</div>
</header>
<!-- Content Area -->
<main class="p-6">
<!-- Dashboard Content -->
<div id="dashboardContent" class="fade-in">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
<div class="bg-white rounded-xl shadow-md p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-gray-500">Usuarios registrados</p>
<h3 class="text-3xl font-bold" id="totalUsers">0</h3>
</div>
<div class="w-12 h-12 rounded-full bg-indigo-100 flex items-center justify-center">
<i class="fas fa-users text-indigo-600"></i>
</div>
</div>
</div>
<div class="bg-white rounded-xl shadow-md p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-gray-500">Registros hoy</p>
<h3 class="text-3xl font-bold" id="todayUsers">0</h3>
</div>
<div class="w-12 h-12 rounded-full bg-green-100 flex items-center justify-center">
<i class="fas fa-calendar-day text-green-600"></i>
</div>
</div>
</div>
</div>
<div class="bg-white rounded-xl shadow-md p-6">
<h3 class="text-xl font-semibold mb-4">Acciones rápidas</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<button id="quickRegisterBtn" class="flex items-center space-x-3 p-4 border border-gray-200 rounded-lg hover:border-indigo-300 hover:bg-indigo-50 transition-all">
<div class="w-10 h-10 rounded-full bg-indigo-100 flex items-center justify-center">
<i class="fas fa-user-plus text-indigo-600"></i>
</div>
<div>
<h4 class="font-medium">Registrar nuevo usuario</h4>
<p class="text-sm text-gray-500">Agrega un usuario a la base de datos</p>
</div>
</button>
<button id="quickDatabaseBtn" class="flex items-center space-x-3 p-4 border border-gray-200 rounded-lg hover:border-indigo-300 hover:bg-indigo-50 transition-all">
<div class="w-10 h-10 rounded-full bg-purple-100 flex items-center justify-center">
<i class="fas fa-database text-purple-600"></i>
</div>
<div>
<h4 class="font-medium">Ver base de datos</h4>
<p class="text-sm text-gray-500">Consulta todos los usuarios registrados</p>
</div>
</button>
</div>
</div>
</div>
<!-- Register Form Content -->
<div id="registerContent" class="hidden fade-in">
<div class="bg-white rounded-xl shadow-md p-6 max-w-2xl mx-auto">
<div class="flex items-center justify-between mb-6">
<h3 class="text-xl font-semibold">Registro de Usuario</h3>
<div class="w-10 h-10 rounded-full bg-indigo-100 flex items-center justify-center">
<i class="fas fa-user-plus text-indigo-600"></i>
</div>
</div>
<form id="userForm" class="space-y-4">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label for="firstName" class="block text-sm font-medium text-gray-700 mb-1">Nombre</label>
<input type="text" id="firstName" required class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-200 focus:border-indigo-500 outline-none transition">
</div>
<div>
<label for="lastName" class="block text-sm font-medium text-gray-700 mb-1">Apellido</label>
<input type="text" id="lastName" required class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-200 focus:border-indigo-500 outline-none transition">
</div>
</div>
<div>
<label for="age" class="block text-sm font-medium text-gray-700 mb-1">Edad</label>
<input type="number" id="age" min="1" max="120" required class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-200 focus:border-indigo-500 outline-none transition">
</div>
<div>
<label for="email" class="block text-sm font-medium text-gray-700 mb-1">Correo Electrónico</label>
<input type="email" id="email" required class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-200 focus:border-indigo-500 outline-none transition">
</div>
<div class="pt-4">
<button type="submit" class="w-full bg-indigo-600 text-white py-2 px-4 rounded-lg hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition">
Registrar Usuario
</button>
</div>
</form>
</div>
</div>
<!-- Database Content -->
<div id="databaseContent" class="hidden fade-in">
<div class="bg-white rounded-xl shadow-md p-6">
<div class="flex items-center justify-between mb-6">
<h3 class="text-xl font-semibold">Base de Datos de Usuarios</h3>
<div class="flex items-center space-x-3">
<div class="relative">
<input type="text" id="searchInput" placeholder="Buscar..." class="pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-200 focus:border-indigo-500 outline-none transition">
<i class="fas fa-search absolute left-3 top-3 text-gray-400"></i>
</div>
<button class="p-2 rounded-lg bg-gray-100 hover:bg-gray-200">
<i class="fas fa-filter text-gray-600"></i>
</button>
</div>
</div>
<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">Apellido</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Edad</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Email</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Fecha Registro</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="usersTableBody" class="bg-white divide-y divide-gray-200">
<!-- Users will be inserted here dynamically -->
<tr>
<td colspan="6" class="px-6 py-4 text-center text-gray-500">No hay usuarios registrados</td>
</tr>
</tbody>
</table>
</div>
<div class="flex items-center justify-between mt-4">
<div class="text-sm text-gray-500">
Mostrando <span id="showingFrom">0</span> a <span id="showingTo">0</span> de <span id="totalRecords">0</span> registros
</div>
<div class="flex space-x-2">
<button id="prevPage" disabled class="px-3 py-1 border rounded-lg text-gray-500 bg-gray-100 cursor-not-allowed">Anterior</button>
<button id="nextPage" disabled class="px-3 py-1 border rounded-lg text-gray-500 bg-gray-100 cursor-not-allowed">Siguiente</button>
</div>
</div>
</div>
</div>
</main>
</div>
<!-- Toast Notification -->
<div id="toast" class="fixed bottom-4 right-4 hidden">
<div class="bg-green-500 text-white px-4 py-2 rounded-lg shadow-lg flex items-center space-x-2">
<i class="fas fa-check-circle"></i>
<span id="toastMessage">Usuario registrado exitosamente!</span>
</div>
</div>
<script>
// Local storage for users database
let usersDB = JSON.parse(localStorage.getItem('usersDB')) || [];
// DOM Elements
const menuBtn = document.getElementById('menuBtn');
const sidebar = document.getElementById('sidebar');
const pageTitle = document.getElementById('pageTitle');
// Content areas
const dashboardContent = document.getElementById('dashboardContent');
const registerContent = document.getElementById('registerContent');
const databaseContent = document.getElementById('databaseContent');
// Navigation buttons
const dashboardBtn = document.getElementById('dashboardBtn');
const registerBtn = document.getElementById('registerBtn');
const databaseBtn = document.getElementById('databaseBtn');
const quickRegisterBtn = document.getElementById('quickRegisterBtn');
const quickDatabaseBtn = document.getElementById('quickDatabaseBtn');
// Form elements
const userForm = document.getElementById('userForm');
// Database elements
const usersTableBody = document.getElementById('usersTableBody');
const searchInput = document.getElementById('searchInput');
const prevPage = document.getElementById('prevPage');
const nextPage = document.getElementById('nextPage');
const showingFrom = document.getElementById('showingFrom');
const showingTo = document.getElementById('showingTo');
const totalRecords = document.getElementById('totalRecords');
// Dashboard counters
const totalUsers = document.getElementById('totalUsers');
const todayUsers = document.getElementById('todayUsers');
// Toast
const toast = document.getElementById('toast');
const toastMessage = document.getElementById('toastMessage');
// Pagination
let currentPage = 1;
const usersPerPage = 5;
// Initialize app
document.addEventListener('DOMContentLoaded', () => {
updateDashboardCounters();
showDashboard();
// Mobile menu toggle
menuBtn.addEventListener('click', () => {
sidebar.classList.toggle('open');
});
// Navigation
dashboardBtn.addEventListener('click', showDashboard);
registerBtn.addEventListener('click', showRegister);
databaseBtn.addEventListener('click', showDatabase);
quickRegisterBtn.addEventListener('click', showRegister);
quickDatabaseBtn.addEventListener('click', showDatabase);
// Form submission
userForm.addEventListener('submit', registerUser);
// Database search
searchInput.addEventListener('input', filterUsers);
// Pagination
prevPage.addEventListener('click', () => {
if (currentPage > 1) {
currentPage--;
renderUsersTable();
}
});
nextPage.addEventListener('click', () => {
const totalPages = Math.ceil(usersDB.length / usersPerPage);
if (currentPage < totalPages) {
currentPage++;
renderUsersTable();
}
});
});
function showDashboard() {
dashboardContent.classList.remove('hidden');
registerContent.classList.add('hidden');
databaseContent.classList.add('hidden');
// Update active button in sidebar
dashboardBtn.classList.add('bg-indigo-50', 'text-indigo-700');
dashboardBtn.classList.remove('hover:bg-gray-100', 'text-gray-700');
registerBtn.classList.remove('bg-indigo-50', 'text-indigo-700');
registerBtn.classList.add('hover:bg-gray-100', 'text-gray-700');
databaseBtn.classList.remove('bg-indigo-50', 'text-indigo-700');
databaseBtn.classList.add('hover:bg-gray-100', 'text-gray-700');
pageTitle.textContent = 'Dashboard';
// Close mobile menu if open
sidebar.classList.remove('open');
// Update counters
updateDashboardCounters();
}
function showRegister() {
dashboardContent.classList.add('hidden');
registerContent.classList.remove('hidden');
databaseContent.classList.add('hidden');
// Update active button in sidebar
dashboardBtn.classList.remove('bg-indigo-50', 'text-indigo-700');
dashboardBtn.classList.add('hover:bg-gray-100', 'text-gray-700');
registerBtn.classList.add('bg-indigo-50', 'text-indigo-700');
registerBtn.classList.remove('hover:bg-gray-100', 'text-gray-700');
databaseBtn.classList.remove('bg-indigo-50', 'text-indigo-700');
databaseBtn.classList.add('hover:bg-gray-100', 'text-gray-700');
pageTitle.textContent = 'Registrar Usuario';
// Close mobile menu if open
sidebar.classList.remove('open');
// Reset form
userForm.reset();
}
function showDatabase() {
dashboardContent.classList.add('hidden');
registerContent.classList.add('hidden');
databaseContent.classList.remove('hidden');
// Update active button in sidebar
dashboardBtn.classList.remove('bg-indigo-50', 'text-indigo-700');
dashboardBtn.classList.add('hover:bg-gray-100', 'text-gray-700');
registerBtn.classList.remove('bg-indigo-50', 'text-indigo-700');
registerBtn.classList.add('hover:bg-gray-100', 'text-gray-700');
databaseBtn.classList.add('bg-indigo-50', 'text-indigo-700');
databaseBtn.classList.remove('hover:bg-gray-100', 'text-gray-700');
pageTitle.textContent = 'Base de Datos';
// Close mobile menu if open
sidebar.classList.remove('open');
// Render users table
currentPage = 1;
renderUsersTable();
}
function registerUser(e) {
e.preventDefault();
const firstName = document.getElementById('firstName').value;
const lastName = document.getElementById('lastName').value;
const age = document.getElementById('age').value;
const email = document.getElementById('email').value;
const newUser = {
id: Date.now(),
firstName,
lastName,
age,
email,
registrationDate: new Date().toLocaleDateString('es-ES')
};
usersDB.push(newUser);
localStorage.setItem('usersDB', JSON.stringify(usersDB));
// Show success toast
showToast('Usuario registrado exitosamente!');
// Reset form
userForm.reset();
// Update dashboard counters
updateDashboardCounters();
// If on database view, refresh the table
if (!databaseContent.classList.contains('hidden')) {
currentPage = 1;
renderUsersTable();
}
}
function renderUsersTable(filteredUsers = null) {
const usersToDisplay = filteredUsers || usersDB;
const startIndex = (currentPage - 1) * usersPerPage;
const endIndex = Math.min(startIndex + usersPerPage, usersToDisplay.length);
const paginatedUsers = usersToDisplay.slice(startIndex, endIndex);
// Clear table
usersTableBody.innerHTML = '';
if (paginatedUsers.length === 0) {
const row = document.createElement('tr');
row.innerHTML = `
<td colspan="6" class="px-6 py-4 text-center text-gray-500">No se encontraron usuarios</td>
`;
usersTableBody.appendChild(row);
} else {
paginatedUsers.forEach(user => {
const row = document.createElement('tr');
row.className = 'hover:bg-gray-50';
row.innerHTML = `
<td class="px-6 py-4 whitespace-nowrap">${user.firstName}</td>
<td class="px-6 py-4 whitespace-nowrap">${user.lastName}</td>
<td class="px-6 py-4 whitespace-nowrap">${user.age}</td>
<td class="px-6 py-4 whitespace-nowrap">${user.email}</td>
<td class="px-6 py-4 whitespace-nowrap">${user.registrationDate}</td>
<td class="px-6 py-4 whitespace-nowrap">
<button onclick="deleteUser(${user.id})" class="text-red-500 hover:text-red-700">
<i class="fas fa-trash-alt"></i>
</button>
</td>
`;
usersTableBody.appendChild(row);
});
}
// Update pagination info
showingFrom.textContent = usersToDisplay.length > 0 ? startIndex + 1 : 0;
showingTo.textContent = endIndex;
totalRecords.textContent = usersToDisplay.length;
// Update pagination buttons
prevPage.disabled = currentPage === 1;
nextPage.disabled = endIndex >= usersToDisplay.length;
if (prevPage.disabled) {
prevPage.classList.add('bg-gray-100', 'cursor-not-allowed');
prevPage.classList.remove('bg-white', 'hover:bg-gray-50', 'cursor-pointer');
} else {
prevPage.classList.remove('bg-gray-100', 'cursor-not-allowed');
prevPage.classList.add('bg-white', 'hover:bg-gray-50', 'cursor-pointer');
}
if (nextPage.disabled) {
nextPage.classList.add('bg-gray-100', 'cursor-not-allowed');
nextPage.classList.remove('bg-white', 'hover:bg-gray-50', 'cursor-pointer');
} else {
nextPage.classList.remove('bg-gray-100', 'cursor-not-allowed');
nextPage.classList.add('bg-white', 'hover:bg-gray-50', 'cursor-pointer');
}
}
function filterUsers() {
const searchTerm = searchInput.value.toLowerCase();
if (searchTerm === '') {
renderUsersTable();
return;
}
const filteredUsers = usersDB.filter(user =>
user.firstName.toLowerCase().includes(searchTerm) ||
user.lastName.toLowerCase().includes(searchTerm) ||
user.email.toLowerCase().includes(searchTerm) ||
user.age.toString().includes(searchTerm)
);
currentPage = 1;
renderUsersTable(filteredUsers);
}
function deleteUser(userId) {
if (confirm('¿Estás seguro de que deseas eliminar este usuario?')) {
usersDB = usersDB.filter(user => user.id !== userId);
localStorage.setItem('usersDB', JSON.stringify(usersDB));
// Update dashboard counters
updateDashboardCounters();
// Re-render table
renderUsersTable();
// Show toast
showToast('Usuario eliminado correctamente');
}
}
function updateDashboardCounters() {
totalUsers.textContent = usersDB.length;
const today = new Date().toLocaleDateString('es-ES');
const todayRegistrations = usersDB.filter(user => user.registrationDate === today).length;
todayUsers.textContent = todayRegistrations;
}
function showToast(message) {
toastMessage.textContent = message;
toast.classList.remove('hidden');
setTimeout(() => {
toast.classList.add('hidden');
}, 3000);
}
// Close sidebar when clicking outside on mobile
document.addEventListener('click', (e) => {
if (window.innerWidth <= 768 && !sidebar.contains(e.target) && e.target !== menuBtn) {
sidebar.classList.remove('open');
}
});
</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=styvencm/base-de-datos" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>