invoice-system/public/js/views/settings-view.js

182 lines
7.0 KiB
JavaScript

/**
* settings-view.js — Logo upload, QBO import, QBO connection test
* Extracted from app.js
*/
let currentLogoFile = null;
export async function checkCurrentLogo() {
try {
const response = await fetch('/api/logo-info');
if (response.ok) {
const data = await response.json();
if (data.hasLogo) {
document.getElementById('logo-preview').classList.remove('hidden');
document.getElementById('logo-image').src = data.logoPath + '?t=' + Date.now();
}
}
} catch (error) {
console.error('Error checking logo:', error);
}
}
export async function uploadLogo() {
if (!currentLogoFile) {
alert('Please select a file first');
return;
}
const formData = new FormData();
formData.append('logo', currentLogoFile);
const statusDiv = document.getElementById('upload-status');
statusDiv.innerHTML = '<p class="text-blue-600">Uploading...</p>';
try {
const response = await fetch('/api/upload-logo', {
method: 'POST',
body: formData
});
if (response.ok) {
const data = await response.json();
statusDiv.innerHTML = '<p class="text-green-600">✓ Logo uploaded successfully!</p>';
document.getElementById('logo-preview').classList.remove('hidden');
document.getElementById('logo-image').src = data.path + '?t=' + Date.now();
document.getElementById('upload-btn').disabled = true;
currentLogoFile = null;
document.getElementById('logo-filename').textContent = '';
document.getElementById('logo-upload').value = '';
} else {
const error = await response.json();
statusDiv.innerHTML = `<p class="text-red-600">✗ Error: ${error.error}</p>`;
}
} catch (error) {
console.error('Upload error:', error);
statusDiv.innerHTML = '<p class="text-red-600">✗ Upload failed</p>';
}
}
export function initSettingsView() {
const logoUpload = document.getElementById('logo-upload');
if (logoUpload) {
logoUpload.addEventListener('change', (e) => {
const file = e.target.files[0];
if (file) {
currentLogoFile = file;
document.getElementById('logo-filename').textContent = file.name;
document.getElementById('upload-btn').disabled = false;
}
});
}
}
export async function checkQboOverdue() {
const btn = document.querySelector('button[onclick="checkQboOverdue()"]');
const resultDiv = document.getElementById('qbo-result');
const tbody = document.getElementById('qbo-result-list');
const originalText = btn.innerHTML;
btn.innerHTML = '⏳ Connecting to QBO...';
btn.disabled = true;
resultDiv.classList.add('hidden');
tbody.innerHTML = '';
try {
const response = await fetch('/api/qbo/overdue');
const invoices = await response.json();
if (response.ok) {
resultDiv.classList.remove('hidden');
if (invoices.length === 0) {
tbody.innerHTML = '<tr><td colspan="4" class="px-4 py-4 text-center text-gray-500">✅ Good news! No overdue invoices found older than 30 days.</td></tr>';
} else {
tbody.innerHTML = invoices.map(inv => `
<tr>
<td class="px-4 py-2 font-medium text-gray-900">${inv.DocNumber || '(No Num)'}</td>
<td class="px-4 py-2 text-gray-600">${inv.CustomerRef?.name || 'Unknown'}</td>
<td class="px-4 py-2 text-red-600 font-medium">${inv.DueDate}</td>
<td class="px-4 py-2 text-right font-bold text-gray-800">$${inv.Balance}</td>
</tr>
`).join('');
}
alert(`Success! Connection working. Found ${invoices.length} overdue invoices.`);
} else {
throw new Error(invoices.error || 'Unknown error');
}
} catch (error) {
console.error('QBO Test Error:', error);
alert('❌ Connection Test Failed: ' + error.message);
tbody.innerHTML = `<tr><td colspan="4" class="px-4 py-4 text-center text-red-600">Error: ${error.message}</td></tr>`;
resultDiv.classList.remove('hidden');
} finally {
btn.innerHTML = originalText;
btn.disabled = false;
}
}
export async function importFromQBO() {
if (!confirm(
'Alle unbezahlten Rechnungen aus QBO importieren?\n\n' +
'• Bereits importierte werden übersprungen\n' +
'• Nur Kunden die lokal verknüpft sind\n\n' +
'Fortfahren?'
)) return;
const btn = document.querySelector('button[onclick="importFromQBO()"]');
const resultDiv = document.getElementById('qbo-import-result');
const originalText = btn.innerHTML;
btn.innerHTML = '⏳ Importiere aus QBO...';
btn.disabled = true;
resultDiv.classList.add('hidden');
try {
const response = await fetch('/api/qbo/import-unpaid', { method: 'POST' });
const result = await response.json();
resultDiv.classList.remove('hidden');
if (response.ok) {
let html = `<div class="p-4 rounded-lg ${result.imported > 0 ? 'bg-green-50 border border-green-200' : 'bg-blue-50 border border-blue-200'}">`;
html += `<p class="font-semibold text-gray-800 mb-2">Import abgeschlossen</p>`;
html += `<ul class="text-sm text-gray-700 space-y-1">`;
html += `<li>✅ <strong>${result.imported}</strong> Rechnungen importiert</li>`;
if (result.skipped > 0) {
html += `<li>⏭️ <strong>${result.skipped}</strong> bereits vorhanden (übersprungen)</li>`;
}
if (result.skippedNoCustomer > 0) {
html += `<li>⚠️ <strong>${result.skippedNoCustomer}</strong> übersprungen — Kunde nicht verknüpft:</li>`;
html += `<li class="ml-4 text-xs text-gray-500">${result.skippedCustomerNames.join(', ')}</li>`;
}
html += `</ul></div>`;
resultDiv.innerHTML = html;
if (result.imported > 0 && window.invoiceView) {
window.invoiceView.loadInvoices();
}
} else {
resultDiv.innerHTML = `<div class="p-4 bg-red-50 border border-red-200 rounded-lg">
<p class="font-semibold text-red-800">Import fehlgeschlagen</p>
<p class="text-sm text-red-600 mt-1">${result.error}</p>
</div>`;
}
} catch (error) {
console.error('Import Error:', error);
resultDiv.classList.remove('hidden');
resultDiv.innerHTML = `<div class="p-4 bg-red-50 border border-red-200 rounded-lg">
<p class="text-red-600">Netzwerkfehler beim Import.</p>
</div>`;
} finally {
btn.innerHTML = originalText;
btn.disabled = false;
}
}
// Expose for onclick handlers
window.uploadLogo = uploadLogo;
window.checkQboOverdue = checkQboOverdue;
window.importFromQBO = importFromQBO;