crear apartado facturas
This commit is contained in:
parent
3241c77646
commit
6408d3325b
|
|
@ -50,7 +50,6 @@ export function InvoiceItem(invoice) {
|
||||||
${getStatusText(invoice.status)}
|
${getStatusText(invoice.status)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="invoice-id">${invoice.id}</div>
|
|
||||||
<div class="invoice-client">
|
<div class="invoice-client">
|
||||||
<span class="client-icon">👤</span>
|
<span class="client-icon">👤</span>
|
||||||
Cliente #${invoice.clientId}
|
Cliente #${invoice.clientId}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,12 @@ export function renderFacturasPage() {
|
||||||
const container = document.createElement('div');
|
const container = document.createElement('div');
|
||||||
container.className = 'facturas-page';
|
container.className = 'facturas-page';
|
||||||
|
|
||||||
|
// Estado de paginación
|
||||||
|
let currentPage = 1;
|
||||||
|
let totalPages = 1;
|
||||||
|
const pageSize = 20;
|
||||||
|
let allInvoices = []; // Almacenar todas las facturas
|
||||||
|
|
||||||
container.innerHTML = /*html*/`
|
container.innerHTML = /*html*/`
|
||||||
<div class="facturas-header">
|
<div class="facturas-header">
|
||||||
<h1>Facturas</h1>
|
<h1>Facturas</h1>
|
||||||
|
|
@ -29,7 +35,6 @@ export function renderFacturasPage() {
|
||||||
</div>
|
</div>
|
||||||
<div class="invoice-number">Número</div>
|
<div class="invoice-number">Número</div>
|
||||||
<div class="invoice-status">Estado</div>
|
<div class="invoice-status">Estado</div>
|
||||||
<div class="invoice-id">ID</div>
|
|
||||||
<div class="invoice-client">Cliente</div>
|
<div class="invoice-client">Cliente</div>
|
||||||
<div class="invoice-date">Fecha</div>
|
<div class="invoice-date">Fecha</div>
|
||||||
<div class="invoice-total">Total</div>
|
<div class="invoice-total">Total</div>
|
||||||
|
|
@ -40,42 +45,81 @@ export function renderFacturasPage() {
|
||||||
<div class="loading">Cargando facturas...</div>
|
<div class="loading">Cargando facturas...</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="pagination">
|
||||||
|
<button class="btn-pagination btn-prev">← Anterior</button>
|
||||||
|
<div class="pagination-info">
|
||||||
|
<span class="current-page">1</span> / <span class="total-pages">1</span>
|
||||||
|
</div>
|
||||||
|
<button class="btn-pagination btn-next">Siguiente →</button>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// Función para cargar las facturas
|
// Función para renderizar la página actual
|
||||||
async function loadInvoices() {
|
function renderPage(page = 1) {
|
||||||
|
const invoicesList = container.querySelector('.invoices-list');
|
||||||
|
|
||||||
|
currentPage = page;
|
||||||
|
totalPages = Math.ceil(allInvoices.length / pageSize);
|
||||||
|
|
||||||
|
// Calcular índices para la página
|
||||||
|
const startIndex = (page - 1) * pageSize;
|
||||||
|
const endIndex = Math.min(startIndex + pageSize, allInvoices.length);
|
||||||
|
const invoices = allInvoices.slice(startIndex, endIndex);
|
||||||
|
|
||||||
|
console.log(`Página ${currentPage}/${totalPages}, Facturas en esta página: ${invoices.length}, Total: ${allInvoices.length}`);
|
||||||
|
|
||||||
|
// Actualizar UI de paginación
|
||||||
|
updatePaginationUI();
|
||||||
|
|
||||||
|
// Limpiar lista
|
||||||
|
invoicesList.innerHTML = '';
|
||||||
|
|
||||||
|
// Si no hay facturas
|
||||||
|
if (invoices.length === 0) {
|
||||||
|
invoicesList.innerHTML = '<div class="no-invoices">No hay facturas disponibles</div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Renderizar cada factura
|
||||||
|
invoices.forEach(invoice => {
|
||||||
|
const invoiceItem = InvoiceItem(invoice);
|
||||||
|
invoicesList.appendChild(invoiceItem);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Función para cargar todas las facturas
|
||||||
|
async function loadAllInvoices() {
|
||||||
const invoicesList = container.querySelector('.invoices-list');
|
const invoicesList = container.querySelector('.invoices-list');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
invoicesList.innerHTML = '<div class="loading">Cargando facturas...</div>';
|
||||||
|
|
||||||
const token = localStorage.getItem('token');
|
const token = localStorage.getItem('token');
|
||||||
const response = await fetch(`${import.meta.env.VITE_API_BASE_URL}/api/Invoices`, {
|
const response = await fetch(
|
||||||
method: 'GET',
|
`${import.meta.env.VITE_API_BASE_URL}/api/Invoices`,
|
||||||
headers: {
|
{
|
||||||
'Authorization': `Bearer ${token}`,
|
method: 'GET',
|
||||||
'Content-Type': 'application/json'
|
headers: {
|
||||||
|
'Authorization': `Bearer ${token}`,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Error al cargar facturas');
|
throw new Error('Error al cargar facturas');
|
||||||
}
|
}
|
||||||
|
|
||||||
const invoices = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
// Limpiar lista
|
// Manejo de respuesta - obtener todas las facturas
|
||||||
invoicesList.innerHTML = '';
|
allInvoices = Array.isArray(data) ? data : data.data || data.invoices || [];
|
||||||
|
|
||||||
// Si no hay facturas
|
console.log(`Total de facturas cargadas: ${allInvoices.length}`);
|
||||||
if (invoices.length === 0) {
|
|
||||||
invoicesList.innerHTML = '<div class="no-invoices">No hay facturas disponibles</div>';
|
// Renderizar la primera página
|
||||||
return;
|
renderPage(1);
|
||||||
}
|
|
||||||
|
|
||||||
// Renderizar cada factura
|
|
||||||
invoices.forEach(invoice => {
|
|
||||||
const invoiceItem = InvoiceItem(invoice);
|
|
||||||
invoicesList.appendChild(invoiceItem);
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error al cargar facturas:', error);
|
console.error('Error al cargar facturas:', error);
|
||||||
|
|
@ -83,6 +127,21 @@ export function renderFacturasPage() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Función para actualizar la UI de paginación
|
||||||
|
function updatePaginationUI() {
|
||||||
|
const currentPageSpan = container.querySelector('.current-page');
|
||||||
|
const totalPagesSpan = container.querySelector('.total-pages');
|
||||||
|
const btnPrev = container.querySelector('.btn-prev');
|
||||||
|
const btnNext = container.querySelector('.btn-next');
|
||||||
|
|
||||||
|
currentPageSpan.textContent = currentPage;
|
||||||
|
totalPagesSpan.textContent = totalPages;
|
||||||
|
|
||||||
|
// Deshabilitar botones según corresponda
|
||||||
|
btnPrev.disabled = currentPage === 1;
|
||||||
|
btnNext.disabled = currentPage === totalPages;
|
||||||
|
}
|
||||||
|
|
||||||
// Event listeners
|
// Event listeners
|
||||||
const newInvoiceBtn = container.querySelector('.btn-new-invoice');
|
const newInvoiceBtn = container.querySelector('.btn-new-invoice');
|
||||||
newInvoiceBtn.addEventListener('click', () => {
|
newInvoiceBtn.addEventListener('click', () => {
|
||||||
|
|
@ -121,8 +180,31 @@ export function renderFacturasPage() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Cargar facturas al montar el componente
|
// Event listeners de paginación
|
||||||
loadInvoices();
|
const btnPrev = container.querySelector('.btn-prev');
|
||||||
|
const btnNext = container.querySelector('.btn-next');
|
||||||
|
|
||||||
|
btnPrev.addEventListener('click', () => {
|
||||||
|
if (currentPage > 1) {
|
||||||
|
renderPage(currentPage - 1);
|
||||||
|
scrollToTop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
btnNext.addEventListener('click', () => {
|
||||||
|
if (currentPage < totalPages) {
|
||||||
|
renderPage(currentPage + 1);
|
||||||
|
scrollToTop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Función para scroll al inicio
|
||||||
|
function scrollToTop() {
|
||||||
|
container.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cargar todas las facturas al montar el componente
|
||||||
|
loadAllInvoices();
|
||||||
|
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -229,22 +229,22 @@ button:focus-visible {
|
||||||
|
|
||||||
.status-pending {
|
.status-pending {
|
||||||
background-color: #fef3c7;
|
background-color: #fef3c7;
|
||||||
color: #d97706;
|
color: #000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-paid {
|
.status-paid {
|
||||||
background-color: #d1fae5;
|
background-color: #d1fae5;
|
||||||
color: #059669;
|
color: #000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-overdue {
|
.status-overdue {
|
||||||
background-color: #fee2e2;
|
background-color: #fee2e2;
|
||||||
color: #dc2626;
|
color: #000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-draft {
|
.status-draft {
|
||||||
background-color: #f1f5f9;
|
background-color: #f1f5f9;
|
||||||
color: #64748b;
|
color: #000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.amount-display {
|
.amount-display {
|
||||||
|
|
@ -480,6 +480,7 @@ button:focus-visible {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
font-size: 0.95rem;
|
font-size: 0.95rem;
|
||||||
background: var(--card-bg);
|
background: var(--card-bg);
|
||||||
|
color: #000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-input {
|
.search-input {
|
||||||
|
|
@ -502,7 +503,7 @@ button:focus-visible {
|
||||||
.invoices-header,
|
.invoices-header,
|
||||||
.invoice-item {
|
.invoice-item {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 40px 150px 120px 60px 150px 120px 120px 120px 80px;
|
grid-template-columns: 40px 150px 120px 120px 120px 120px 120px 120px 80px;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
padding: 1rem 1.5rem;
|
padding: 1rem 1.5rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
@ -552,27 +553,27 @@ button:focus-visible {
|
||||||
|
|
||||||
.status-draft {
|
.status-draft {
|
||||||
background: #f3f4f6;
|
background: #f3f4f6;
|
||||||
color: #6b7280;
|
color: #000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-validated {
|
.status-validated {
|
||||||
background: #dbeafe;
|
background: #dbeafe;
|
||||||
color: #1d4ed8;
|
color: #000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-paid {
|
.status-paid {
|
||||||
background: #d1fae5;
|
background: #d1fae5;
|
||||||
color: #059669;
|
color: #000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-unpaid {
|
.status-unpaid {
|
||||||
background: #fee2e2;
|
background: #fee2e2;
|
||||||
color: #dc2626;
|
color: #000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-canceled {
|
.status-canceled {
|
||||||
background: #f3f4f6;
|
background: #f3f4f6;
|
||||||
color: #374151;
|
color: #000000;
|
||||||
text-decoration: line-through;
|
text-decoration: line-through;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -633,7 +634,85 @@ button:focus-visible {
|
||||||
color: var(--danger);
|
color: var(--danger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Pagination Styles */
|
||||||
|
.pagination {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2rem;
|
||||||
|
margin-top: 2rem;
|
||||||
|
padding: 1.5rem;
|
||||||
|
background: var(--card-bg);
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-info {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 1rem;
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-info .current-page,
|
||||||
|
.pagination-info .total-pages {
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--primary);
|
||||||
|
min-width: 30px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-pagination {
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
background-color: var(--primary);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: all 0.2s;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-pagination:hover:not(:disabled) {
|
||||||
|
background-color: var(--primary-hover);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-pagination:disabled {
|
||||||
|
background-color: #cbd5e1;
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-pagination:active:not(:disabled) {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
/* Responsive */
|
/* Responsive */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.pagination {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-pagination {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-info {
|
||||||
|
order: 3;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 1400px) {
|
@media (max-width: 1400px) {
|
||||||
.invoices-header,
|
.invoices-header,
|
||||||
.invoice-item {
|
.invoice-item {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue