182 lines
7.0 KiB
JavaScript
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; |