diff --git a/modules/fx-rates/assets/fx-rates.css b/modules/fx-rates/assets/fx-rates.css
index c16282a..60e8e0d 100644
--- a/modules/fx-rates/assets/fx-rates.css
+++ b/modules/fx-rates/assets/fx-rates.css
@@ -138,3 +138,26 @@
font-size: 0.95rem;
text-align: right;
}
+
+.fx-history-date {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.45rem;
+}
+
+.fx-info-button {
+ width: 1.4rem;
+ height: 1.4rem;
+ border: 1px solid #d0d7e2;
+ border-radius: 999px;
+ background: #fff;
+ color: #5b6573;
+ font-size: 0.78rem;
+ font-weight: 700;
+ line-height: 1;
+ cursor: help;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ padding: 0;
+}
diff --git a/modules/fx-rates/assets/fx-rates.js b/modules/fx-rates/assets/fx-rates.js
index 5cae1c0..5137cb7 100644
--- a/modules/fx-rates/assets/fx-rates.js
+++ b/modules/fx-rates/assets/fx-rates.js
@@ -7,10 +7,8 @@
const page = JSON.parse(root.dataset.page || '{}');
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"]'),
convertTo: root.querySelector('select[name="convert_to"]'),
@@ -99,51 +97,12 @@
});
};
- const renderSnapshot = (snapshot) => {
- const rates = snapshot && snapshot.rates ? snapshot.rates : null;
- const entries = rates ? Object.entries(rates) : [];
- const visibleCurrencies = preferredCurrencies.length
- ? new Set(preferredCurrencies)
- : null;
- if (!nodes.ratesBody) {
- return;
- }
-
- const filteredEntries = visibleCurrencies
- ? entries.filter(([code]) => visibleCurrencies.has(String(code || '').trim().toUpperCase()))
- : entries;
-
- if (!filteredEntries.length) {
- nodes.ratesBody.innerHTML = '
| Noch keine Wechselkurse fuer die ausgewaehlten Waehrungen gespeichert. |
';
- return;
- }
-
- nodes.ratesBody.innerHTML = filteredEntries.map(([code, rate]) => {
- const formatted = typeof rate === 'number'
- ? rate.toLocaleString('de-DE', { maximumFractionDigits: 8 })
- : 'n/a';
- return `| ${code} | ${formatted} |
`;
- }).join('');
- };
-
- const renderFetches = (fetches) => {
- if (!nodes.fetchesBody) {
- return;
- }
- const entries = Array.isArray(fetches) ? fetches : [];
- if (!entries.length) {
- nodes.fetchesBody.innerHTML = '| Noch keine Abrufe vorhanden. |
';
- return;
- }
- nodes.fetchesBody.innerHTML = entries.map((entry) => `
-
- | ${entry?.fetched_at_display || entry?.fetched_at || ''} |
- ${entry?.base_currency || ''} |
- ${entry?.provider || ''} |
- ${entry?.trigger_source_label || entry?.trigger_source || ''} |
-
- `).join('');
- };
+ const escapeHtml = (value) => String(value || '')
+ .replaceAll('&', '&')
+ .replaceAll('<', '<')
+ .replaceAll('>', '>')
+ .replaceAll('"', '"')
+ .replaceAll("'", ''');
const renderHistory = (rows, currencies) => {
if (!nodes.historyHead || !nodes.historyBody) {
@@ -172,7 +131,19 @@
nodes.historyBody.innerHTML = entries.map((entry) => `
- | ${entry.label} |
+
+
+ ${entry.label}
+ ${entry.fetch ? `
+
+ ` : ''}
+
+ |
${series.map((currency) => {
const value = entry.rates?.[currency];
if (typeof value !== 'number' || !Number.isFinite(value)) {
@@ -201,21 +172,6 @@
return payload.data;
};
- const loadLatest = async () => {
- const base = String(
- settings.display_base_currency || settings.default_base_currency || 'EUR'
- ).trim().toUpperCase();
- const query = new URLSearchParams();
- query.set('base', base);
- if (preferredCurrencies.length) {
- query.set('symbols', preferredCurrencies.join(','));
- }
-
- const data = await request(`/latest?${query.toString()}`);
- renderSnapshot(data);
- return data;
- };
-
const loadHistory = async () => {
const base = String(
settings.display_base_currency || settings.default_base_currency || 'EUR'
@@ -236,7 +192,21 @@
return { currency, rows: Array.isArray(rows) ? rows : [] };
}));
+ const recentFetches = Array.isArray(page.recent_fetches) ? page.recent_fetches : [];
const byDate = new Map();
+ recentFetches.forEach((fetch) => {
+ const key = String(fetch?.fetched_at || '').trim();
+ if (!key || byDate.has(key)) {
+ return;
+ }
+ byDate.set(key, {
+ sortKey: key,
+ label: fetch?.fetched_at_display || fetch?.fetched_at || key,
+ fetch,
+ rates: base !== '' ? { [base]: 1 } : {},
+ });
+ });
+
histories.forEach(({ currency, rows }) => {
rows.forEach((row) => {
const key = String(row?.fetched_at || row?.rate_date || '').trim();
@@ -247,6 +217,7 @@
byDate.set(key, {
sortKey: key,
label: row?.fetched_at_display || row?.fetched_at || row?.rate_date || key,
+ fetch: recentFetches.find((fetch) => String(fetch?.fetched_at || '').trim() === key) || null,
rates: base !== '' ? { [base]: 1 } : {},
});
}
@@ -258,7 +229,8 @@
});
const mergedRows = Array.from(byDate.values())
- .sort((left, right) => String(left.sortKey).localeCompare(String(right.sortKey)));
+ .sort((left, right) => String(right.sortKey).localeCompare(String(left.sortKey)))
+ .slice(0, 15);
renderHistory(mergedRows, selectedCurrencies);
};
@@ -305,10 +277,8 @@
});
});
- renderFetches(page.recent_fetches || []);
bindManualRefreshAction();
- loadLatest().catch(() => {});
loadHistory().catch(() => {
renderHistory([], preferredCurrencies);
});
diff --git a/modules/fx-rates/pages/index.php b/modules/fx-rates/pages/index.php
index 601aaa7..04a87e5 100644
--- a/modules/fx-rates/pages/index.php
+++ b/modules/fx-rates/pages/index.php
@@ -51,7 +51,7 @@ if ((string) ($_GET['refresh'] ?? '') === '1') {
}
$latest = $service->latestStatus();
-$recentFetches = $service->recentFetches(12);
+$recentFetches = $service->recentFetches(15);
$pageData = json_encode([
'settings' => $settings,
'latest' => $latest,
@@ -110,33 +110,14 @@ $pageData = json_encode([
-
Letzte Kurse
-
Letzter gespeicherter Snapshot, umgerechnet auf = e((string) ($settings['display_base_currency'] ?? $settings['default_base_currency'] ?? 'EUR')) ?>.
+
Kursverlauf
+
Neueste Abrufe zuerst. Verlauf der bevorzugten Waehrungen relativ zur Anzeige-Basiswaehrung.
-
-
-
-
- | Waehrung |
- Kurs |
-
-
-
- | Noch keine Daten geladen. |
-
-
-
-
-
-
-
Kursverlauf
-
Chronologischer Verlauf der bevorzugten Waehrungen relativ zur Anzeige-Basiswaehrung.
@@ -151,36 +132,6 @@ $pageData = json_encode([
-
-
-
Letzte Abrufe
-
-
-
-
- | Datum |
- Basis |
- Provider |
- Ausloeser |
-
-
-
-
- | Noch keine Abrufe vorhanden. |
-
-
-
- | = e((string) ($fetch['fetched_at_display'] ?? $fetch['fetched_at'] ?? '')) ?> |
- = e((string) ($fetch['base_currency'] ?? '')) ?> |
- = e((string) ($fetch['provider'] ?? '')) ?> |
- = e((string) ($fetch['trigger_source_label'] ?? $fetch['trigger_source'] ?? '')) ?> |
-
-
-
-
-
-
-
= module_shell_footer() ?>