aasdsd
This commit is contained in:
@@ -138,3 +138,26 @@
|
|||||||
font-size: 0.95rem;
|
font-size: 0.95rem;
|
||||||
text-align: right;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,10 +7,8 @@
|
|||||||
const page = JSON.parse(root.dataset.page || '{}');
|
const page = JSON.parse(root.dataset.page || '{}');
|
||||||
const settings = page.settings || {};
|
const settings = page.settings || {};
|
||||||
const nodes = {
|
const nodes = {
|
||||||
ratesBody: root.querySelector('[data-bind="rates-body"]'),
|
|
||||||
historyHead: root.querySelector('[data-bind="history-head"]'),
|
historyHead: root.querySelector('[data-bind="history-head"]'),
|
||||||
historyBody: root.querySelector('[data-bind="history-body"]'),
|
historyBody: root.querySelector('[data-bind="history-body"]'),
|
||||||
fetchesBody: root.querySelector('[data-bind="fetches-body"]'),
|
|
||||||
convertResult: root.querySelector('[data-bind="convert-result"]'),
|
convertResult: root.querySelector('[data-bind="convert-result"]'),
|
||||||
convertFrom: root.querySelector('select[name="convert_from"]'),
|
convertFrom: root.querySelector('select[name="convert_from"]'),
|
||||||
convertTo: root.querySelector('select[name="convert_to"]'),
|
convertTo: root.querySelector('select[name="convert_to"]'),
|
||||||
@@ -99,51 +97,12 @@
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderSnapshot = (snapshot) => {
|
const escapeHtml = (value) => String(value || '')
|
||||||
const rates = snapshot && snapshot.rates ? snapshot.rates : null;
|
.replaceAll('&', '&')
|
||||||
const entries = rates ? Object.entries(rates) : [];
|
.replaceAll('<', '<')
|
||||||
const visibleCurrencies = preferredCurrencies.length
|
.replaceAll('>', '>')
|
||||||
? new Set(preferredCurrencies)
|
.replaceAll('"', '"')
|
||||||
: null;
|
.replaceAll("'", ''');
|
||||||
if (!nodes.ratesBody) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const filteredEntries = visibleCurrencies
|
|
||||||
? entries.filter(([code]) => visibleCurrencies.has(String(code || '').trim().toUpperCase()))
|
|
||||||
: entries;
|
|
||||||
|
|
||||||
if (!filteredEntries.length) {
|
|
||||||
nodes.ratesBody.innerHTML = '<tr><td colspan="2">Noch keine Wechselkurse fuer die ausgewaehlten Waehrungen gespeichert.</td></tr>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes.ratesBody.innerHTML = filteredEntries.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 renderFetches = (fetches) => {
|
|
||||||
if (!nodes.fetchesBody) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const entries = Array.isArray(fetches) ? fetches : [];
|
|
||||||
if (!entries.length) {
|
|
||||||
nodes.fetchesBody.innerHTML = '<tr><td colspan="4">Noch keine Abrufe vorhanden.</td></tr>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
nodes.fetchesBody.innerHTML = entries.map((entry) => `
|
|
||||||
<tr>
|
|
||||||
<td>${entry?.fetched_at_display || entry?.fetched_at || ''}</td>
|
|
||||||
<td>${entry?.base_currency || ''}</td>
|
|
||||||
<td>${entry?.provider || ''}</td>
|
|
||||||
<td>${entry?.trigger_source_label || entry?.trigger_source || ''}</td>
|
|
||||||
</tr>
|
|
||||||
`).join('');
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderHistory = (rows, currencies) => {
|
const renderHistory = (rows, currencies) => {
|
||||||
if (!nodes.historyHead || !nodes.historyBody) {
|
if (!nodes.historyHead || !nodes.historyBody) {
|
||||||
@@ -172,7 +131,19 @@
|
|||||||
|
|
||||||
nodes.historyBody.innerHTML = entries.map((entry) => `
|
nodes.historyBody.innerHTML = entries.map((entry) => `
|
||||||
<tr>
|
<tr>
|
||||||
<td>${entry.label}</td>
|
<td>
|
||||||
|
<div class="fx-history-date">
|
||||||
|
<span>${entry.label}</span>
|
||||||
|
${entry.fetch ? `
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="fx-info-button"
|
||||||
|
title="${escapeHtml(`Basis: ${entry.fetch.base_currency || '-'} | Provider: ${entry.fetch.provider || '-'} | Ausloeser: ${entry.fetch.trigger_source_label || entry.fetch.trigger_source || '-'}`)}"
|
||||||
|
aria-label="${escapeHtml(`Abrufinfo fuer ${entry.label}`)}"
|
||||||
|
>i</button>
|
||||||
|
` : ''}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
${series.map((currency) => {
|
${series.map((currency) => {
|
||||||
const value = entry.rates?.[currency];
|
const value = entry.rates?.[currency];
|
||||||
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
||||||
@@ -201,21 +172,6 @@
|
|||||||
return payload.data;
|
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 loadHistory = async () => {
|
||||||
const base = String(
|
const base = String(
|
||||||
settings.display_base_currency || settings.default_base_currency || 'EUR'
|
settings.display_base_currency || settings.default_base_currency || 'EUR'
|
||||||
@@ -236,7 +192,21 @@
|
|||||||
return { currency, rows: Array.isArray(rows) ? rows : [] };
|
return { currency, rows: Array.isArray(rows) ? rows : [] };
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const recentFetches = Array.isArray(page.recent_fetches) ? page.recent_fetches : [];
|
||||||
const byDate = new Map();
|
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 }) => {
|
histories.forEach(({ currency, rows }) => {
|
||||||
rows.forEach((row) => {
|
rows.forEach((row) => {
|
||||||
const key = String(row?.fetched_at || row?.rate_date || '').trim();
|
const key = String(row?.fetched_at || row?.rate_date || '').trim();
|
||||||
@@ -247,6 +217,7 @@
|
|||||||
byDate.set(key, {
|
byDate.set(key, {
|
||||||
sortKey: key,
|
sortKey: key,
|
||||||
label: row?.fetched_at_display || row?.fetched_at || row?.rate_date || 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 } : {},
|
rates: base !== '' ? { [base]: 1 } : {},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -258,7 +229,8 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
const mergedRows = Array.from(byDate.values())
|
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);
|
renderHistory(mergedRows, selectedCurrencies);
|
||||||
};
|
};
|
||||||
@@ -305,10 +277,8 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
renderFetches(page.recent_fetches || []);
|
|
||||||
bindManualRefreshAction();
|
bindManualRefreshAction();
|
||||||
|
|
||||||
loadLatest().catch(() => {});
|
|
||||||
loadHistory().catch(() => {
|
loadHistory().catch(() => {
|
||||||
renderHistory([], preferredCurrencies);
|
renderHistory([], preferredCurrencies);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ if ((string) ($_GET['refresh'] ?? '') === '1') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$latest = $service->latestStatus();
|
$latest = $service->latestStatus();
|
||||||
$recentFetches = $service->recentFetches(12);
|
$recentFetches = $service->recentFetches(15);
|
||||||
$pageData = json_encode([
|
$pageData = json_encode([
|
||||||
'settings' => $settings,
|
'settings' => $settings,
|
||||||
'latest' => $latest,
|
'latest' => $latest,
|
||||||
@@ -110,33 +110,14 @@ $pageData = json_encode([
|
|||||||
<div class="fx-card">
|
<div class="fx-card">
|
||||||
<div class="fx-card-head">
|
<div class="fx-card-head">
|
||||||
<div>
|
<div>
|
||||||
<h2>Letzte Kurse</h2>
|
<h2>Kursverlauf</h2>
|
||||||
<p>Letzter gespeicherter Snapshot, umgerechnet auf <?= e((string) ($settings['display_base_currency'] ?? $settings['default_base_currency'] ?? 'EUR')) ?>.</p>
|
<p>Neueste Abrufe zuerst. Verlauf der bevorzugten Waehrungen relativ zur Anzeige-Basiswaehrung.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="fx-card-meta">
|
<div class="fx-card-meta">
|
||||||
<div><strong>Letzter Abruf:</strong> <?= e((string) ($latest['fetched_at_display'] ?? $latest['fetched_at'] ?? 'noch keiner')) ?></div>
|
|
||||||
<div><strong>Anzeige-Basis:</strong> <?= e((string) ($settings['display_base_currency'] ?? $settings['default_base_currency'] ?? '')) ?></div>
|
<div><strong>Anzeige-Basis:</strong> <?= e((string) ($settings['display_base_currency'] ?? $settings['default_base_currency'] ?? '')) ?></div>
|
||||||
<div><strong>Snapshot-Basis:</strong> <?= e((string) ($latest['base_currency'] ?? '')) ?></div>
|
<div><strong>Letzter Abruf:</strong> <?= e((string) ($latest['fetched_at_display'] ?? $latest['fetched_at'] ?? 'noch keiner')) ?></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="fx-table-wrap">
|
|
||||||
<table class="fx-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Waehrung</th>
|
|
||||||
<th>Kurs</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody data-bind="rates-body">
|
|
||||||
<tr><td colspan="2">Noch keine Daten geladen.</td></tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="fx-card">
|
|
||||||
<h2>Kursverlauf</h2>
|
|
||||||
<p>Chronologischer Verlauf der bevorzugten Waehrungen relativ zur Anzeige-Basiswaehrung.</p>
|
|
||||||
<div class="fx-table-wrap">
|
<div class="fx-table-wrap">
|
||||||
<table class="fx-table">
|
<table class="fx-table">
|
||||||
<thead data-bind="history-head">
|
<thead data-bind="history-head">
|
||||||
@@ -151,36 +132,6 @@ $pageData = json_encode([
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="fx-card">
|
|
||||||
<h2>Letzte Abrufe</h2>
|
|
||||||
<div class="fx-table-wrap">
|
|
||||||
<table class="fx-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Datum</th>
|
|
||||||
<th>Basis</th>
|
|
||||||
<th>Provider</th>
|
|
||||||
<th>Ausloeser</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody data-bind="fetches-body">
|
|
||||||
<?php if ($recentFetches === []): ?>
|
|
||||||
<tr><td colspan="4">Noch keine Abrufe vorhanden.</td></tr>
|
|
||||||
<?php else: ?>
|
|
||||||
<?php foreach ($recentFetches as $fetch): ?>
|
|
||||||
<tr>
|
|
||||||
<td><?= e((string) ($fetch['fetched_at_display'] ?? $fetch['fetched_at'] ?? '')) ?></td>
|
|
||||||
<td><?= e((string) ($fetch['base_currency'] ?? '')) ?></td>
|
|
||||||
<td><?= e((string) ($fetch['provider'] ?? '')) ?></td>
|
|
||||||
<td><?= e((string) ($fetch['trigger_source_label'] ?? $fetch['trigger_source'] ?? '')) ?></td>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
<?php endif; ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?= module_shell_footer() ?>
|
<?= module_shell_footer() ?>
|
||||||
|
|||||||
Reference in New Issue
Block a user