ns commit

This commit is contained in:
Алекс 2026-02-19 17:25:55 +01:00
parent 68043beb07
commit a9981c03a8
4 changed files with 157 additions and 13 deletions

View File

@ -109,7 +109,7 @@ export function InvoiceModal(invoiceId, onClose, onUpdate) {
</div> </div>
<div class="form-group"> <div class="form-group">
<label>Fecha</label> <label><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="vertical-align: -2px; margin-right: 4px;"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>Fecha</label>
<input <input
type="date" type="date"
name="date" name="date"
@ -121,7 +121,7 @@ export function InvoiceModal(invoiceId, onClose, onUpdate) {
<div class="form-row"> <div class="form-row">
<div class="form-group"> <div class="form-group">
<label>Fecha de Vencimiento</label> <label><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="vertical-align: -2px; margin-right: 4px;"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>Fecha de Vencimiento</label>
<input <input
type="date" type="date"
name="expireDate" name="expireDate"
@ -143,15 +143,15 @@ export function InvoiceModal(invoiceId, onClose, onUpdate) {
<div class="form-row"> <div class="form-row">
<div class="form-group full-width"> <div class="form-group full-width">
<label>Nota Pública</label> <label><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="vertical-align: -2px; margin-right: 4px;"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>Nota Pública</label>
<textarea name="note_public" rows="3">${invoice.notePublic || ''}</textarea> <textarea name="note_public" rows="3" placeholder="Visible para el cliente" ${!canEdit ? 'disabled' : ''}>${invoice.notePublic || ''}</textarea>
</div> </div>
</div> </div>
<div class="form-row"> <div class="form-row">
<div class="form-group full-width"> <div class="form-group full-width">
<label>Nota Privada</label> <label><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="vertical-align: -2px; margin-right: 4px;"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>Nota Privada</label>
<textarea name="note_private" rows="3">${invoice.notePrivate || ''}</textarea> <textarea name="note_private" rows="3" placeholder="Solo uso interno" ${!canEdit ? 'disabled' : ''}>${invoice.notePrivate || ''}</textarea>
</div> </div>
</div> </div>
</div> </div>

View File

@ -25,24 +25,34 @@ export function renderCreateInvoicePage() {
</div> </div>
<div class="form-field-compact"> <div class="form-field-compact">
<label>Fecha</label> <label><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="vertical-align: -2px; margin-right: 4px;"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>Fecha</label>
<input type="date" id="date" name="date" /> <div class="date-input-wrapper">
<input type="date" id="date" name="date" min="2000-01-01" max="2100-12-31" />
<button type="button" class="date-picker-btn" data-target="date" title="Seleccionar fecha">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
</button>
</div>
</div> </div>
<div class="form-field-compact"> <div class="form-field-compact">
<label>Vencimiento</label> <label><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="vertical-align: -2px; margin-right: 4px;"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>Vencimiento</label>
<input type="date" id="expireDate" name="expireDate" /> <div class="date-input-wrapper">
<input type="date" id="expireDate" name="expireDate" min="2000-01-01" max="2100-12-31" />
<button type="button" class="date-picker-btn" data-target="expireDate" title="Seleccionar fecha">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
</button>
</div>
</div> </div>
</div> </div>
<div class="notes-grid-compact"> <div class="notes-grid-compact">
<div class="form-field-compact"> <div class="form-field-compact">
<label>Nota pública</label> <label><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="vertical-align: -2px; margin-right: 4px;"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>Nota pública</label>
<textarea id="notePublic" name="notePublic" rows="2" placeholder="Visible para el cliente (opcional)"></textarea> <textarea id="notePublic" name="notePublic" rows="2" placeholder="Visible para el cliente (opcional)"></textarea>
</div> </div>
<div class="form-field-compact"> <div class="form-field-compact">
<label>Nota privada</label> <label><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="vertical-align: -2px; margin-right: 4px;"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>Nota privada</label>
<textarea id="notePrivate" name="notePrivate" rows="2" placeholder="Interna (opcional)"></textarea> <textarea id="notePrivate" name="notePrivate" rows="2" placeholder="Interna (opcional)"></textarea>
</div> </div>
</div> </div>
@ -100,6 +110,14 @@ export function renderCreateInvoicePage() {
expireDate.setDate(expireDate.getDate() + 30); expireDate.setDate(expireDate.getDate() + 30);
container.querySelector('#expireDate').value = expireDate.toISOString().split('T')[0]; container.querySelector('#expireDate').value = expireDate.toISOString().split('T')[0];
// Event listeners para botones de calendario
container.querySelectorAll('.date-picker-btn').forEach(btn => {
btn.addEventListener('click', () => {
const targetInput = container.querySelector(`#${btn.dataset.target}`);
if (targetInput) targetInput.showPicker();
});
});
let lineCounter = 0; let lineCounter = 0;
function createInvoiceLine() { function createInvoiceLine() {
@ -204,6 +222,41 @@ export function renderCreateInvoicePage() {
return; return;
} }
// Validar fechas coherentes
const dateValue = container.querySelector('#date').value;
const expireDateValue = container.querySelector('#expireDate').value;
function isValidDate(dateStr) {
if (!dateStr) return false;
const parts = dateStr.split('-');
if (parts.length !== 3) return false;
const year = parseInt(parts[0]);
const month = parseInt(parts[1]);
const day = parseInt(parts[2]);
if (year < 2000 || year > 2100) return false;
if (month < 1 || month > 12) return false;
if (day < 1 || day > 31) return false;
const d = new Date(year, month - 1, day);
return d.getFullYear() === year && d.getMonth() === month - 1 && d.getDate() === day;
}
if (!isValidDate(dateValue)) {
alert('La fecha de factura no es válida. Introduce una fecha entre los años 2000 y 2100.');
return;
}
if (!isValidDate(expireDateValue)) {
alert('La fecha de vencimiento no es válida. Introduce una fecha entre los años 2000 y 2100.');
return;
}
const dateObj = new Date(dateValue);
const expireDateObj = new Date(expireDateValue);
if (expireDateObj < dateObj) {
alert('La fecha de vencimiento no puede ser anterior a la fecha de factura.');
return;
}
const formData = { const formData = {
clientId: clientIdValue, clientId: clientIdValue,
date: container.querySelector('#date').value + 'T00:00:00', date: container.querySelector('#date').value + 'T00:00:00',

View File

@ -144,7 +144,7 @@ export function renderFacturasPage() {
// Manejo de respuesta - obtener todas las facturas // Manejo de respuesta - obtener todas las facturas
allInvoices = Array.isArray(data) ? data : data.data || data.invoices || []; allInvoices = Array.isArray(data) ? data : data.data || data.invoices || [];
filteredInvoices = [...allInvoices]; filteredInvoices = [...allInvoices].reverse();
console.log(`Total de facturas cargadas: ${allInvoices.length}`); console.log(`Total de facturas cargadas: ${allInvoices.length}`);

View File

@ -721,6 +721,57 @@ button:disabled {
color: var(--text-primary); color: var(--text-primary);
} }
/* Notes column */
.invoice-notes {
max-width: 200px;
}
.notes-preview {
display: flex;
flex-direction: column;
gap: 4px;
}
.note-badge {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 0.75rem;
padding: 2px 8px;
border-radius: 12px;
line-height: 1.4;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: default;
}
.note-public {
background: rgba(37, 99, 235, 0.08);
color: var(--primary);
border: 1px solid rgba(37, 99, 235, 0.15);
}
.note-public svg {
flex-shrink: 0;
}
.note-private {
background: rgba(245, 158, 11, 0.08);
color: #b45309;
border: 1px solid rgba(245, 158, 11, 0.15);
}
.note-private svg {
flex-shrink: 0;
}
.no-notes {
color: var(--text-secondary);
font-size: 0.875rem;
}
.invoice-actions { .invoice-actions {
display: flex; display: flex;
gap: var(--space-1); gap: var(--space-1);
@ -939,6 +990,46 @@ button:disabled {
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.12); box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.12);
} }
/* Date input wrapper */
.date-input-wrapper {
position: relative;
display: flex;
align-items: stretch;
}
.date-input-wrapper input[type="date"] {
flex: 1;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-right: none;
}
.date-input-wrapper input[type="date"]:focus {
z-index: 1;
}
.date-picker-btn {
display: flex;
align-items: center;
justify-content: center;
padding: 0 0.75rem;
background: var(--primary);
color: white;
border: 1px solid var(--primary);
border-top-right-radius: 6px;
border-bottom-right-radius: 6px;
cursor: pointer;
transition: background 0.2s;
}
.date-picker-btn:hover {
background: var(--primary-hover, #1d4ed8);
}
.date-picker-btn:active {
transform: scale(0.96);
}
.lines-section-compact { .lines-section-compact {
flex: 1; flex: 1;
display: flex; display: flex;