147 lines
5.2 KiB
JavaScript
147 lines
5.2 KiB
JavaScript
(() => {
|
|
const root = document.getElementById('fx-rates-app');
|
|
if (!root) {
|
|
return;
|
|
}
|
|
|
|
const page = JSON.parse(root.dataset.page || '{}');
|
|
const settings = page.settings || {};
|
|
const nodes = {
|
|
message: root.querySelector('[data-bind="message"]'),
|
|
lastFetch: root.querySelector('[data-bind="last-fetch"]'),
|
|
defaultBase: root.querySelector('[data-bind="default-base"]'),
|
|
displayBase: root.querySelector('[data-bind="display-base"]'),
|
|
ratesBody: root.querySelector('[data-bind="rates-body"]'),
|
|
defaultBaseInput: root.querySelector('input[name="default_base_currency"]'),
|
|
displayBaseInput: root.querySelector('input[name="display_base_currency"]'),
|
|
preferredCurrenciesInput: root.querySelector('input[name="preferred_currencies"]'),
|
|
};
|
|
const apiBase = '/api/fx-rates/v1';
|
|
|
|
const setMessage = (text, type = '') => {
|
|
if (!nodes.message) {
|
|
return;
|
|
}
|
|
nodes.message.textContent = text || '';
|
|
nodes.message.className = `fx-message${type ? ` is-${type}` : ''}`;
|
|
};
|
|
|
|
const setLoading = (state) => {
|
|
root.querySelectorAll('button[data-action]').forEach((button) => {
|
|
button.disabled = state;
|
|
});
|
|
};
|
|
|
|
const parsePreferredCurrencies = () => String(nodes.preferredCurrenciesInput?.value || '')
|
|
.split(/[\s,;]+/)
|
|
.map((item) => item.trim().toUpperCase())
|
|
.filter(Boolean);
|
|
|
|
const renderSnapshot = (snapshot) => {
|
|
const rates = snapshot && snapshot.rates ? snapshot.rates : null;
|
|
const entries = rates ? Object.entries(rates) : [];
|
|
if (!nodes.ratesBody) {
|
|
return;
|
|
}
|
|
|
|
if (!entries.length) {
|
|
nodes.ratesBody.innerHTML = '<tr><td colspan="2">Noch keine Wechselkurse fuer die ausgewaehlten Waehrungen gespeichert.</td></tr>';
|
|
return;
|
|
}
|
|
|
|
nodes.ratesBody.innerHTML = entries.map(([code, rate]) => {
|
|
const formatted = typeof rate === 'number'
|
|
? rate.toLocaleString('de-DE', { maximumFractionDigits: 8 })
|
|
: 'n/a';
|
|
return `<tr><td>${code}</td><td>${formatted}</td></tr>`;
|
|
}).join('');
|
|
};
|
|
|
|
const request = async (path, options = {}) => {
|
|
const response = await fetch(`${apiBase}${path}`, {
|
|
credentials: 'same-origin',
|
|
headers: {
|
|
Accept: 'application/json',
|
|
...(options.body ? { 'Content-Type': 'application/json' } : {}),
|
|
...(options.headers || {}),
|
|
},
|
|
...options,
|
|
});
|
|
const payload = await response.json().catch(() => ({}));
|
|
if (!response.ok) {
|
|
throw new Error(payload?.context?.message || payload?.error || `HTTP ${response.status}`);
|
|
}
|
|
return payload.data;
|
|
};
|
|
|
|
const loadLatest = async () => {
|
|
const base = String(
|
|
nodes.displayBaseInput?.value || settings.display_base_currency || settings.default_base_currency || 'EUR'
|
|
).trim().toUpperCase();
|
|
const preferred = parsePreferredCurrencies();
|
|
const query = new URLSearchParams();
|
|
query.set('base', base);
|
|
if (preferred.length) {
|
|
query.set('symbols', preferred.join(','));
|
|
}
|
|
|
|
const data = await request(`/latest?${query.toString()}`);
|
|
renderSnapshot(data);
|
|
if (nodes.lastFetch) {
|
|
nodes.lastFetch.textContent = data?.fetched_at || 'noch keiner';
|
|
}
|
|
if (nodes.displayBase) {
|
|
nodes.displayBase.textContent = base;
|
|
}
|
|
return data;
|
|
};
|
|
|
|
root.querySelector('[data-action="refresh-rates"]')?.addEventListener('click', async () => {
|
|
try {
|
|
setLoading(true);
|
|
setMessage('Ich rufe jetzt die aktuellen Kurse ab.');
|
|
const payload = {
|
|
force: true,
|
|
base: String(nodes.defaultBaseInput?.value || settings.default_base_currency || 'EUR').trim().toUpperCase(),
|
|
currencies: parsePreferredCurrencies(),
|
|
};
|
|
const data = await request('/refresh', { method: 'POST', body: JSON.stringify(payload) });
|
|
setMessage(`Aktuelle Kurse gespeichert. ${data?.updated_count || 0} Werte aktualisiert.`, 'success');
|
|
await loadLatest();
|
|
} catch (error) {
|
|
setMessage(error.message || 'Kurse konnten nicht aktualisiert werden.', 'error');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
});
|
|
|
|
root.querySelector('[data-action="save-settings"]')?.addEventListener('click', async () => {
|
|
try {
|
|
setLoading(true);
|
|
setMessage('Ich speichere jetzt die Waehrungs-Auswahl.');
|
|
const payload = {
|
|
default_base_currency: String(nodes.defaultBaseInput?.value || '').trim().toUpperCase(),
|
|
display_base_currency: String(nodes.displayBaseInput?.value || '').trim().toUpperCase(),
|
|
preferred_currencies: parsePreferredCurrencies(),
|
|
};
|
|
const data = await request('/settings', { method: 'PUT', body: JSON.stringify(payload) });
|
|
if (nodes.defaultBase) {
|
|
nodes.defaultBase.textContent = data?.default_base_currency || '';
|
|
}
|
|
if (nodes.displayBase) {
|
|
nodes.displayBase.textContent = data?.display_base_currency || '';
|
|
}
|
|
setMessage('Waehrungs-Auswahl gespeichert.', 'success');
|
|
await loadLatest();
|
|
} catch (error) {
|
|
setMessage(error.message || 'Waehrungs-Auswahl konnte nicht gespeichert werden.', 'error');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
});
|
|
|
|
loadLatest().catch((error) => {
|
|
setMessage(error.message || 'Letzter Snapshot konnte nicht geladen werden.', 'error');
|
|
});
|
|
})();
|