diff --git a/modules/fx-rates/assets/fx-rates.js b/modules/fx-rates/assets/fx-rates.js index ad61718..84e8213 100644 --- a/modules/fx-rates/assets/fx-rates.js +++ b/modules/fx-rates/assets/fx-rates.js @@ -8,6 +8,8 @@ const settings = page.settings || {}; const nodes = { ratesBody: root.querySelector('[data-bind="rates-body"]'), + historyHead: root.querySelector('[data-bind="history-head"]'), + historyBody: root.querySelector('[data-bind="history-body"]'), fetchesBody: root.querySelector('[data-bind="fetches-body"]'), convertResult: root.querySelector('[data-bind="convert-result"]'), convertFrom: root.querySelector('select[name="convert_from"]'), @@ -59,6 +61,45 @@ `).join(''); }; + const renderHistory = (rows, currencies) => { + if (!nodes.historyHead || !nodes.historyBody) { + return; + } + + const series = Array.isArray(currencies) ? currencies : []; + if (!series.length) { + nodes.historyHead.innerHTML = 'DatumKurse'; + nodes.historyBody.innerHTML = 'Keine bevorzugten Waehrungen fuer den Verlauf vorhanden.'; + return; + } + + nodes.historyHead.innerHTML = ` + + Datum + ${series.map((currency) => `${currency}`).join('')} + + `; + + const entries = Array.isArray(rows) ? rows : []; + if (!entries.length) { + nodes.historyBody.innerHTML = `Noch keine Verlaufsdaten vorhanden.`; + return; + } + + nodes.historyBody.innerHTML = entries.map((entry) => ` + + ${entry.label} + ${series.map((currency) => { + const value = entry.rates?.[currency]; + if (typeof value !== 'number' || !Number.isFinite(value)) { + return '–'; + } + return `${value.toLocaleString('de-DE', { maximumFractionDigits: 8 })}`; + }).join('')} + + `).join(''); + }; + const request = async (path, options = {}) => { const response = await fetch(`${apiBase}${path}`, { credentials: 'same-origin', @@ -91,6 +132,50 @@ return data; }; + const loadHistory = async () => { + const base = String( + settings.display_base_currency || settings.default_base_currency || 'EUR' + ).trim().toUpperCase(); + const historyCurrencies = preferredCurrencies.filter((currency) => currency !== base); + + if (!historyCurrencies.length) { + renderHistory([], []); + return; + } + + const histories = await Promise.all(historyCurrencies.map(async (currency) => { + const query = new URLSearchParams({ from: base, to: currency, limit: '20' }); + const rows = await request(`/history?${query.toString()}`); + return { currency, rows: Array.isArray(rows) ? rows : [] }; + })); + + const byDate = new Map(); + histories.forEach(({ currency, rows }) => { + rows.forEach((row) => { + const key = String(row?.fetched_at || row?.rate_date || '').trim(); + if (!key) { + return; + } + if (!byDate.has(key)) { + byDate.set(key, { + sortKey: key, + label: row?.fetched_at_display || row?.fetched_at || row?.rate_date || key, + rates: {}, + }); + } + const entry = byDate.get(key); + if (entry && typeof row?.rate === 'number' && Number.isFinite(row.rate)) { + entry.rates[currency] = row.rate; + } + }); + }); + + const mergedRows = Array.from(byDate.values()) + .sort((left, right) => String(left.sortKey).localeCompare(String(right.sortKey))); + + renderHistory(mergedRows, historyCurrencies); + }; + const calculateConversion = async () => { if (!nodes.convertFrom || !nodes.convertTo || !nodes.convertAmount || !nodes.convertResult) { return; @@ -136,5 +221,10 @@ renderFetches(page.recent_fetches || []); loadLatest().catch(() => {}); + loadHistory().catch(() => { + renderHistory([], preferredCurrencies.filter((currency) => currency !== String( + settings.display_base_currency || settings.default_base_currency || 'EUR' + ).trim().toUpperCase())); + }); calculateConversion().catch(() => {}); })(); diff --git a/modules/fx-rates/pages/index.php b/modules/fx-rates/pages/index.php index fbf7138..043dbc7 100644 --- a/modules/fx-rates/pages/index.php +++ b/modules/fx-rates/pages/index.php @@ -87,11 +87,12 @@ $pageData = json_encode([

Letzte Kurse

-

Letzter gespeicherter Snapshot fuer .

+

Letzter gespeicherter Snapshot, umgerechnet auf .

Letzter Abruf:
-
Basis:
+
Anzeige-Basis:
+
Snapshot-Basis:
@@ -109,6 +110,24 @@ $pageData = json_encode([
+
+

Kursverlauf

+

Chronologischer Verlauf der bevorzugten Waehrungen relativ zur Anzeige-Basiswaehrung.

+
+ + + + + + + + + + +
DatumKurse
Noch keine Verlaufsdaten geladen.
+
+
+

Letzte Abrufe