autotracker / index.html
Noyer145's picture
Add 2 files
19beacf verified
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Car Expense Manager</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>
.chart-container {
height: 300px;
position: relative;
}
.fade-in {
animation: fadeIn 0.5s ease-in-out;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.custom-scrollbar::-webkit-scrollbar {
width: 6px;
height: 6px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 10px;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: #888;
border-radius: 10px;
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background: #555;
}
</style>
</head>
<body class="bg-gray-100 min-h-screen">
<div class="container mx-auto px-4 py-8">
<header class="mb-10">
<h1 class="text-4xl font-bold text-center text-indigo-800 mb-2">Car Expense Manager</h1>
<p class="text-center text-gray-600">Organiza y controla los gastos de tus veh铆culos</p>
</header>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
<!-- Panel izquierdo - Formularios -->
<div class="lg:col-span-1 space-y-6">
<!-- Formulario para agregar auto -->
<div class="bg-white rounded-xl shadow-md p-6 fade-in">
<h2 class="text-xl font-semibold text-gray-800 mb-4 flex items-center">
<i class="fas fa-car mr-2 text-indigo-600"></i> Agregar Veh铆culo
</h2>
<form id="addCarForm" class="space-y-4">
<div>
<label for="carBrand" class="block text-sm font-medium text-gray-700">Marca</label>
<input type="text" id="carBrand" required class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 p-2 border">
</div>
<div>
<label for="carModel" class="block text-sm font-medium text-gray-700">Modelo</label>
<input type="text" id="carModel" required class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 p-2 border">
</div>
<div>
<label for="carYear" class="block text-sm font-medium text-gray-700">A帽o</label>
<input type="number" id="carYear" min="1900" max="2099" required class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 p-2 border">
</div>
<div>
<label for="carPlate" class="block text-sm font-medium text-gray-700">Placa</label>
<input type="text" id="carPlate" required class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 p-2 border uppercase">
</div>
<button type="submit" class="w-full bg-indigo-600 text-white py-2 px-4 rounded-md hover:bg-indigo-700 transition duration-200 flex items-center justify-center">
<i class="fas fa-plus-circle mr-2"></i> Agregar Veh铆culo
</button>
</form>
</div>
<!-- Formulario para agregar gasto -->
<div class="bg-white rounded-xl shadow-md p-6 fade-in">
<h2 class="text-xl font-semibold text-gray-800 mb-4 flex items-center">
<i class="fas fa-file-invoice-dollar mr-2 text-indigo-600"></i> Registrar Gasto
</h2>
<form id="addExpenseForm" class="space-y-4">
<div>
<label for="expenseCar" class="block text-sm font-medium text-gray-700">Veh铆culo</label>
<select id="expenseCar" required class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 p-2 border">
<option value="" disabled selected>Selecciona un veh铆culo</option>
</select>
</div>
<div>
<label for="expenseType" class="block text-sm font-medium text-gray-700">Tipo de gasto</label>
<select id="expenseType" required class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 p-2 border">
<option value="" disabled selected>Selecciona un tipo</option>
<option value="Mantenimiento">Mantenimiento</option>
<option value="Reparaci贸n">Reparaci贸n</option>
<option value="Documentos">Documentos</option>
<option value="Seguro">Seguro</option>
<option value="Combustible">Combustible</option>
<option value="Otro">Otro</option>
</select>
</div>
<div>
<label for="expenseAmount" class="block text-sm font-medium text-gray-700">Monto ($)</label>
<input type="number" id="expenseAmount" min="0" step="0.01" required class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 p-2 border">
</div>
<div>
<label for="expenseDate" class="block text-sm font-medium text-gray-700">Fecha</label>
<input type="date" id="expenseDate" required class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 p-2 border">
</div>
<div>
<label for="expenseDescription" class="block text-sm font-medium text-gray-700">Descripci贸n</label>
<textarea id="expenseDescription" rows="2" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 p-2 border"></textarea>
</div>
<button type="submit" class="w-full bg-indigo-600 text-white py-2 px-4 rounded-md hover:bg-indigo-700 transition duration-200 flex items-center justify-center">
<i class="fas fa-save mr-2"></i> Guardar Gasto
</button>
</form>
</div>
</div>
<!-- Panel derecho - Visualizaci贸n de datos -->
<div class="lg:col-span-2 space-y-6">
<!-- Filtros y resumen -->
<div class="bg-white rounded-xl shadow-md p-6 fade-in">
<div class="flex flex-col md:flex-row md:items-center md:justify-between mb-6">
<h2 class="text-xl font-semibold text-gray-800 flex items-center">
<i class="fas fa-chart-pie mr-2 text-indigo-600"></i> Resumen de Gastos
</h2>
<div class="flex flex-col sm:flex-row gap-3 mt-4 md:mt-0">
<select id="filterCar" class="rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 p-2 border text-sm">
<option value="all">Todos los veh铆culos</option>
</select>
<select id="filterYear" class="rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 p-2 border text-sm">
<option value="all">Todos los a帽os</option>
</select>
<select id="filterType" class="rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 p-2 border text-sm">
<option value="all">Todos los tipos</option>
<option value="Mantenimiento">Mantenimiento</option>
<option value="Reparaci贸n">Reparaci贸n</option>
<option value="Documentos">Documentos</option>
<option value="Seguro">Seguro</option>
<option value="Combustible">Combustible</option>
<option value="Otro">Otro</option>
</select>
</div>
</div>
<!-- Tarjetas de resumen -->
<div class="grid grid-cols-1 sm:grid-cols-3 gap-4 mb-6">
<div class="bg-indigo-50 rounded-lg p-4 border border-indigo-100">
<p class="text-sm text-indigo-800 font-medium">Total Gastado</p>
<p id="totalSpent" class="text-2xl font-bold text-indigo-600">$0.00</p>
</div>
<div class="bg-green-50 rounded-lg p-4 border border-green-100">
<p class="text-sm text-green-800 font-medium">Veh铆culos Registrados</p>
<p id="totalCars" class="text-2xl font-bold text-green-600">0</p>
</div>
<div class="bg-purple-50 rounded-lg p-4 border border-purple-100">
<p class="text-sm text-purple-800 font-medium">Gastos Registrados</p>
<p id="totalExpenses" class="text-2xl font-bold text-purple-600">0</p>
</div>
</div>
<!-- Gr谩fico (simulado) -->
<div class="bg-gray-50 rounded-lg p-4 border border-gray-200 mb-6">
<div class="flex justify-between items-center mb-3">
<h3 class="font-medium text-gray-700">Distribuci贸n de Gastos</h3>
<div class="flex items-center text-sm">
<span class="w-3 h-3 rounded-full bg-indigo-500 mr-1"></span>
<span id="chartLegend" class="text-gray-600">Selecciona filtros</span>
</div>
</div>
<div class="chart-container">
<div id="expenseChart" class="h-full flex items-end justify-around">
<!-- Las barras del gr谩fico se generar谩n din谩micamente -->
<div class="text-center text-xs text-gray-500">
No hay datos para mostrar
</div>
</div>
</div>
</div>
</div>
<!-- Lista de gastos -->
<div class="bg-white rounded-xl shadow-md p-6 fade-in">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-semibold text-gray-800 flex items-center">
<i class="fas fa-receipt mr-2 text-indigo-600"></i> Historial de Gastos
</h2>
<button id="exportBtn" class="text-sm bg-gray-100 hover:bg-gray-200 text-gray-800 py-1 px-3 rounded-md flex items-center transition duration-200">
<i class="fas fa-download mr-1"></i> Exportar
</button>
</div>
<div class="overflow-x-auto custom-scrollbar">
<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">Fecha</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Veh铆culo</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Tipo</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Descripci贸n</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Monto</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="expensesTableBody" class="bg-white divide-y divide-gray-200">
<tr>
<td colspan="6" class="px-6 py-4 text-center text-sm text-gray-500">No hay gastos registrados</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- Modal para confirmar eliminaci贸n -->
<div id="confirmModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-lg p-6 max-w-md w-full mx-4">
<div class="flex items-center mb-4">
<div class="bg-red-100 p-3 rounded-full mr-3">
<i class="fas fa-exclamation-triangle text-red-600"></i>
</div>
<h3 class="text-lg font-medium text-gray-900">Confirmar eliminaci贸n</h3>
</div>
<p class="text-gray-600 mb-6">驴Est谩s seguro que deseas eliminar este gasto? Esta acci贸n no se puede deshacer.</p>
<div class="flex justify-end space-x-3">
<button id="cancelDelete" class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50">Cancelar</button>
<button id="confirmDelete" class="px-4 py-2 bg-red-600 text-white rounded-md hover:bg-red-700">Eliminar</button>
</div>
</div>
</div>
<script>
// Datos iniciales (simulando una base de datos)
let cars = JSON.parse(localStorage.getItem('cars')) || [];
let expenses = JSON.parse(localStorage.getItem('expenses')) || [];
let expenseToDelete = null;
// Elementos del DOM
const addCarForm = document.getElementById('addCarForm');
const addExpenseForm = document.getElementById('addExpenseForm');
const expenseCarSelect = document.getElementById('expenseCar');
const filterCarSelect = document.getElementById('filterCar');
const filterYearSelect = document.getElementById('filterYear');
const filterTypeSelect = document.getElementById('filterType');
const expensesTableBody = document.getElementById('expensesTableBody');
const totalSpentElement = document.getElementById('totalSpent');
const totalCarsElement = document.getElementById('totalCars');
const totalExpensesElement = document.getElementById('totalExpenses');
const expenseChart = document.getElementById('expenseChart');
const chartLegend = document.getElementById('chartLegend');
const confirmModal = document.getElementById('confirmModal');
const cancelDeleteBtn = document.getElementById('cancelDelete');
const confirmDeleteBtn = document.getElementById('confirmDelete');
const exportBtn = document.getElementById('exportBtn');
// Inicializar la aplicaci贸n
document.addEventListener('DOMContentLoaded', function() {
updateCarSelects();
updateYearFilter();
renderExpenses();
updateSummary();
renderChart();
// Configurar fecha actual como predeterminada
const today = new Date().toISOString().split('T')[0];
document.getElementById('expenseDate').value = today;
});
// Event listeners
addCarForm.addEventListener('submit', function(e) {
e.preventDefault();
addCar();
});
addExpenseForm.addEventListener('submit', function(e) {
e.preventDefault();
addExpense();
});
[filterCarSelect, filterYearSelect, filterTypeSelect].forEach(filter => {
filter.addEventListener('change', function() {
renderExpenses();
updateSummary();
renderChart();
});
});
cancelDeleteBtn.addEventListener('click', function() {
confirmModal.classList.add('hidden');
});
confirmDeleteBtn.addEventListener('click', function() {
if (expenseToDelete !== null) {
deleteExpense(expenseToDelete);
confirmModal.classList.add('hidden');
expenseToDelete = null;
}
});
exportBtn.addEventListener('click', exportData);
// Funciones principales
function addCar() {
const brand = document.getElementById('carBrand').value.trim();
const model = document.getElementById('carModel').value.trim();
const year = document.getElementById('carYear').value;
const plate = document.getElementById('carPlate').value.trim().toUpperCase();
const newCar = {
id: Date.now(),
brand,
model,
year: parseInt(year),
plate
};
cars.push(newCar);
saveData();
updateCarSelects();
document.getElementById('addCarForm').reset();
// Mostrar notificaci贸n
showNotification('Veh铆culo agregado correctamente', 'success');
}
function addExpense() {
const carId = parseInt(document.getElementById('expenseCar').value);
const type = document.getElementById('expenseType').value;
const amount = parseFloat(document.getElementById('expenseAmount').value);
const date = document.getElementById('expenseDate').value;
const description = document.getElementById('expenseDescription').value.trim();
const car = cars.find(c => c.id === carId);
if (!car) return;
const newExpense = {
id: Date.now(),
carId,
carBrand: car.brand,
carModel: car.model,
carPlate: car.plate,
type,
amount,
date,
description,
createdAt: new Date().toISOString()
};
expenses.push(newExpense);
saveData();
renderExpenses();
updateSummary();
renderChart();
document.getElementById('addExpenseForm').reset();
// Mostrar notificaci贸n
showNotification('Gasto registrado correctamente', 'success');
}
function deleteExpense(expenseId) {
expenses = expenses.filter(expense => expense.id !== expenseId);
saveData();
renderExpenses();
updateSummary();
renderChart();
// Mostrar notificaci贸n
showNotification('Gasto eliminado correctamente', 'success');
}
function updateCarSelects() {
// Limpiar selects
expenseCarSelect.innerHTML = '<option value="" disabled selected>Selecciona un veh铆culo</option>';
filterCarSelect.innerHTML = '<option value="all">Todos los veh铆culos</option>';
// Llenar selects con los veh铆culos disponibles
cars.forEach(car => {
const option1 = document.createElement('option');
option1.value = car.id;
option1.textContent = `${car.brand} ${car.model} (${car.plate})`;
expenseCarSelect.appendChild(option1);
const option2 = document.createElement('option');
option2.value = car.id;
option2.textContent = `${car.brand} ${car.model} (${car.plate})`;
filterCarSelect.appendChild(option2);
});
// Actualizar contador de veh铆culos
totalCarsElement.textContent = cars.length;
}
function updateYearFilter() {
// Obtener a帽os 煤nicos de los gastos
const years = [...new Set(expenses.map(expense => expense.date.split('-')[0]))];
// Ordenar a帽os de m谩s reciente a m谩s antiguo
years.sort((a, b) => b - a);
// Limpiar y llenar el select
filterYearSelect.innerHTML = '<option value="all">Todos los a帽os</option>';
years.forEach(year => {
const option = document.createElement('option');
option.value = year;
option.textContent = year;
filterYearSelect.appendChild(option);
});
}
function renderExpenses() {
const filteredExpenses = filterExpenses();
// Limpiar tabla
expensesTableBody.innerHTML = '';
if (filteredExpenses.length === 0) {
expensesTableBody.innerHTML = `
<tr>
<td colspan="6" class="px-6 py-4 text-center text-sm text-gray-500">No hay gastos que coincidan con los filtros</td>
</tr>
`;
return;
}
// Ordenar gastos por fecha (m谩s reciente primero)
filteredExpenses.sort((a, b) => new Date(b.date) - new Date(a.date));
// Llenar tabla con los gastos filtrados
filteredExpenses.forEach(expense => {
const row = document.createElement('tr');
row.className = 'hover:bg-gray-50';
// Formatear fecha
const date = new Date(expense.date);
const formattedDate = date.toLocaleDateString('es-ES', {
day: '2-digit',
month: '2-digit',
year: 'numeric'
});
// Determinar color seg煤n el tipo de gasto
let typeColor = 'gray';
switch(expense.type) {
case 'Mantenimiento': typeColor = 'blue'; break;
case 'Reparaci贸n': typeColor = 'red'; break;
case 'Documentos': typeColor = 'green'; break;
case 'Seguro': typeColor = 'purple'; break;
case 'Combustible': typeColor = 'orange'; break;
case 'Otro': typeColor = 'gray'; break;
}
row.innerHTML = `
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${formattedDate}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">${expense.carBrand} ${expense.carModel}</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-${typeColor}-100 text-${typeColor}-800">
${expense.type}
</span>
</td>
<td class="px-6 py-4 text-sm text-gray-500 max-w-xs truncate">${expense.description || 'Sin descripci贸n'}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">$${expense.amount.toFixed(2)}</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<button class="text-red-600 hover:text-red-900 delete-btn" data-id="${expense.id}">
<i class="fas fa-trash-alt"></i>
</button>
</td>
`;
expensesTableBody.appendChild(row);
});
// Agregar event listeners a los botones de eliminar
document.querySelectorAll('.delete-btn').forEach(btn => {
btn.addEventListener('click', function() {
expenseToDelete = parseInt(this.getAttribute('data-id'));
confirmModal.classList.remove('hidden');
});
});
// Actualizar contador de gastos
totalExpensesElement.textContent = filteredExpenses.length;
}
function filterExpenses() {
const carId = filterCarSelect.value === 'all' ? null : parseInt(filterCarSelect.value);
const year = filterYearSelect.value === 'all' ? null : filterYearSelect.value;
const type = filterTypeSelect.value === 'all' ? null : filterTypeSelect.value;
return expenses.filter(expense => {
// Filtrar por veh铆culo
if (carId !== null && expense.carId !== carId) return false;
// Filtrar por a帽o
if (year !== null && !expense.date.startsWith(year)) return false;
// Filtrar por tipo
if (type !== null && expense.type !== type) return false;
return true;
});
}
function updateSummary() {
const filteredExpenses = filterExpenses();
// Calcular total gastado
const total = filteredExpenses.reduce((sum, expense) => sum + expense.amount, 0);
totalSpentElement.textContent = `$${total.toFixed(2)}`;
// Actualizar leyenda del gr谩fico
let legendText = 'Todos los gastos';
if (filterCarSelect.value !== 'all') {
const selectedCar = cars.find(c => c.id === parseInt(filterCarSelect.value));
legendText = `${selectedCar.brand} ${selectedCar.model}`;
}
if (filterYearSelect.value !== 'all') {
legendText += ` - A帽o ${filterYearSelect.value}`;
}
if (filterTypeSelect.value !== 'all') {
legendText += ` - ${filterTypeSelect.value}`;
}
chartLegend.textContent = legendText;
}
function renderChart() {
const filteredExpenses = filterExpenses();
// Limpiar gr谩fico
expenseChart.innerHTML = '';
if (filteredExpenses.length === 0) {
expenseChart.innerHTML = '<div class="text-center text-xs text-gray-500">No hay datos para mostrar</div>';
return;
}
// Agrupar gastos por tipo si no hay filtro de tipo, o por mes si hay filtro de tipo
let data = {};
let isGroupedByType = filterTypeSelect.value === 'all';
if (isGroupedByType) {
// Agrupar por tipo de gasto
filteredExpenses.forEach(expense => {
if (!data[expense.type]) {
data[expense.type] = 0;
}
data[expense.type] += expense.amount;
});
} else {
// Agrupar por mes
filteredExpenses.forEach(expense => {
const month = expense.date.substring(0, 7); // Formato YYYY-MM
if (!data[month]) {
data[month] = 0;
}
data[month] += expense.amount;
});
}
// Ordenar los datos
const sortedData = Object.entries(data).sort((a, b) => {
if (isGroupedByType) return a[0].localeCompare(b[0]);
return a[0].localeCompare(b[0]);
});
// Calcular el m谩ximo valor para escalar las barras
const maxValue = Math.max(...Object.values(data));
// Generar las barras del gr谩fico
sortedData.forEach(([label, value]) => {
const barHeight = maxValue > 0 ? (value / maxValue * 100) : 0;
const barColor = getRandomColor();
const barContainer = document.createElement('div');
barContainer.className = 'flex flex-col items-center w-full';
// Formatear el label para meses
let displayLabel = label;
if (!isGroupedByType) {
const [year, month] = label.split('-');
displayLabel = new Date(year, month-1).toLocaleDateString('es-ES', { month: 'short' });
}
barContainer.innerHTML = `
<div class="w-8 bg-${barColor}-500 rounded-t-md hover:bg-${barColor}-600 transition duration-200" style="height: ${barHeight}%"></div>
<div class="text-xs text-gray-500 mt-1 text-center">${displayLabel}</div>
<div class="text-xs font-medium mt-1">$${value.toFixed(2)}</div>
`;
expenseChart.appendChild(barContainer);
});
}
function getRandomColor() {
const colors = ['indigo', 'blue', 'green', 'yellow', 'red', 'purple', 'pink', 'orange'];
return colors[Math.floor(Math.random() * colors.length)];
}
function saveData() {
localStorage.setItem('cars', JSON.stringify(cars));
localStorage.setItem('expenses', JSON.stringify(expenses));
}
function exportData() {
const filteredExpenses = filterExpenses();
if (filteredExpenses.length === 0) {
showNotification('No hay datos para exportar', 'warning');
return;
}
// Crear contenido CSV
let csvContent = "Fecha,Veh铆culo,Tipo,Descripci贸n,Monto\n";
filteredExpenses.forEach(expense => {
const date = new Date(expense.date).toLocaleDateString('es-ES');
const carInfo = `${expense.carBrand} ${expense.carModel} (${expense.carPlate})`;
const description = expense.description ? `"${expense.description.replace(/"/g, '""')}"` : '';
csvContent += `${date},${carInfo},${expense.type},${description},${expense.amount.toFixed(2)}\n`;
});
// Crear y descargar archivo
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.setAttribute('href', url);
link.setAttribute('download', `gastos_vehiculos_${new Date().toISOString().slice(0, 10)}.csv`);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
// Mostrar notificaci贸n
showNotification('Datos exportados correctamente', 'success');
}
function showNotification(message, type) {
const notification = document.createElement('div');
notification.className = `fixed bottom-4 right-4 px-4 py-2 rounded-md shadow-md text-white ${
type === 'success' ? 'bg-green-500' :
type === 'error' ? 'bg-red-500' :
'bg-yellow-500'
} flex items-center`;
notification.innerHTML = `
<i class="fas ${
type === 'success' ? 'fa-check-circle' :
type === 'error' ? 'fa-exclamation-circle' :
'fa-exclamation-triangle'
} mr-2"></i>
<span>${message}</span>
`;
document.body.appendChild(notification);
// Desvanecer y eliminar la notificaci贸n despu茅s de 3 segundos
setTimeout(() => {
notification.style.opacity = '0';
notification.style.transition = 'opacity 0.5s ease-out';
setTimeout(() => {
notification.remove();
}, 500);
}, 3000);
}
</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=Noyer145/autotracker" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>