dsadas
All checks were successful
Deploy / deploy-staging (push) Successful in 5s
Deploy / deploy-production (push) Has been skipped

This commit is contained in:
2026-05-04 03:03:30 +02:00
parent c81e89dc3f
commit c3ba24e939
4 changed files with 318 additions and 298 deletions

View File

@@ -1,27 +1,36 @@
#fx-rates-app { #fx-rates-app,
padding: 1rem 0 2rem; #fx-rates-currencies {
}
.fx-stack {
display: grid; display: grid;
gap: 1rem; gap: 16px;
} }
.fx-card { .fx-submenu-row {
background: var(--panel-bg, #fff); display: flex;
border: 1px solid rgba(15, 23, 42, 0.12); gap: 14px;
border-radius: 8px; justify-content: space-between;
padding: 1rem; align-items: center;
flex-wrap: wrap;
} }
.fx-card h1, .fx-section-head {
.fx-card h2 { display: flex;
justify-content: space-between;
gap: 16px;
align-items: flex-start;
flex-wrap: wrap;
}
.fx-section-head h2,
.section-box h2,
.card-box h2 {
margin: 0 0 0.5rem; margin: 0 0 0.5rem;
} }
.fx-card p { .fx-section-head p,
.section-box p,
.card-box p {
margin: 0 0 0.75rem; margin: 0 0 0.75rem;
color: #5b6573; color: var(--muted);
} }
.fx-card-head { .fx-card-head {
@@ -38,40 +47,6 @@
flex-wrap: wrap; flex-wrap: wrap;
} }
.fx-button {
appearance: none;
border: 1px solid #d0d7e2;
background: #fff;
color: #1c2734;
border-radius: 8px;
padding: 0.7rem 1rem;
cursor: pointer;
}
.fx-button--primary {
background: #1c2734;
color: #fff;
border-color: #1c2734;
}
.fx-button--ghost {
background: #fff4fb;
border-color: #f5b7d7;
color: #ff8a00;
}
.fx-button--accent {
background: linear-gradient(90deg, #ff006a 0%, #ff9e00 100%);
color: #111827;
border-color: transparent;
font-weight: 700;
}
.fx-button[disabled] {
opacity: 0.6;
cursor: wait;
}
.fx-form-grid { .fx-form-grid {
display: grid; display: grid;
gap: 0.75rem; gap: 0.75rem;
@@ -90,29 +65,30 @@
.fx-form-grid span, .fx-form-grid span,
.fx-block span { .fx-block span {
font-size: 0.9rem; font-size: 0.9rem;
color: #5b6573; color: var(--muted);
} }
.fx-form-grid input, .fx-form-grid input,
.fx-form-grid select, .fx-form-grid select,
.fx-block input { .fx-block input {
width: 100%; width: 100%;
border: 1px solid #d0d7e2; border: 1px solid var(--line);
border-radius: 8px; border-radius: 12px;
padding: 0.7rem 0.8rem; padding: 0.7rem 0.8rem;
background: var(--surface-strong);
color: var(--text);
} }
.fx-message { .fx-message {
margin-bottom: 0.9rem; color: var(--text);
color: #1c2734;
} }
.fx-message.is-error { .fx-message.is-error {
color: #b42318; color: #d92d20;
} }
.fx-message.is-success { .fx-message.is-success {
color: #027a48; color: color-mix(in srgb, var(--accent-green) 78%, var(--text));
} }
.fx-table-wrap { .fx-table-wrap {
@@ -127,7 +103,7 @@
.fx-table th, .fx-table th,
.fx-table td { .fx-table td {
text-align: left; text-align: left;
border-bottom: 1px solid #eef2f6; border-bottom: 1px solid var(--line);
padding: 0.65rem 0.4rem; padding: 0.65rem 0.4rem;
} }
@@ -141,13 +117,13 @@
min-height: 1.5rem; min-height: 1.5rem;
font-size: 1rem; font-size: 1rem;
font-weight: 700; font-weight: 700;
color: #1c2734; color: var(--text);
} }
.fx-card-meta { .fx-card-meta {
display: grid; display: grid;
gap: 0.35rem; gap: 0.35rem;
color: #5b6573; color: var(--muted);
font-size: 0.95rem; font-size: 0.95rem;
text-align: right; text-align: right;
} }
@@ -156,7 +132,6 @@
display: flex; display: flex;
gap: 0.75rem; gap: 0.75rem;
flex-wrap: wrap; flex-wrap: wrap;
margin-bottom: 1rem;
} }
.fx-action-row form { .fx-action-row form {
@@ -175,22 +150,27 @@
display: grid; display: grid;
gap: 1rem; gap: 1rem;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
margin: 1rem 0 1.25rem;
} }
.fx-mini-card { .fx-card-grid {
display: grid; display: grid;
gap: 0.2rem; gap: 16px;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
} }
.fx-mini-label, .fx-mini-label,
.fx-field-label { .fx-field-label {
font-size: 0.9rem; font-size: 0.9rem;
color: #6a7383; color: var(--muted);
letter-spacing: 0.18em; letter-spacing: 0.18em;
text-transform: uppercase; text-transform: uppercase;
} }
.fx-card-value {
font-size: 1.2rem;
font-weight: 700;
}
.fx-currency-selection-row { .fx-currency-selection-row {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
@@ -216,11 +196,11 @@
.fx-input, .fx-input,
.fx-select { .fx-select {
width: 100%; width: 100%;
border: 1px solid #d0d7e2; border: 1px solid var(--line);
border-radius: 18px; border-radius: 18px;
padding: 0.8rem 1rem; padding: 0.8rem 1rem;
background: #fff; background: var(--surface-strong);
color: #1c2734; color: var(--text);
} }
.fx-field { .fx-field {
@@ -234,11 +214,11 @@
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: 0.6rem; gap: 0.6rem;
border: 1px solid #d0d7e2; border: 1px solid var(--line);
border-radius: 999px; border-radius: 999px;
padding: 0.7rem 1rem; padding: 0.7rem 1rem;
background: #fff; background: var(--surface-strong);
color: #1c2734; color: var(--text);
} }
.fx-token { .fx-token {
@@ -247,12 +227,12 @@
.fx-token:hover, .fx-token:hover,
.fx-suggestion:hover { .fx-suggestion:hover {
border-color: rgba(255, 158, 0, 0.45); border-color: color-mix(in srgb, var(--brand-accent-3) 45%, transparent);
background: rgba(255, 244, 251, 0.9); background: color-mix(in srgb, var(--brand-accent) 6%, var(--surface-strong));
} }
.fx-token-close { .fx-token-close {
color: #ff9e00; color: var(--brand-accent-3);
font-weight: 700; font-weight: 700;
text-transform: uppercase; text-transform: uppercase;
} }
@@ -269,11 +249,11 @@
} }
.fx-suggestion strong { .fx-suggestion strong {
color: #111827; color: var(--text);
} }
.fx-text { .fx-text {
color: #5b6573; color: var(--muted);
} }
.fx-history-date { .fx-history-date {
@@ -285,10 +265,10 @@
.fx-info-button { .fx-info-button {
width: 1.4rem; width: 1.4rem;
height: 1.4rem; height: 1.4rem;
border: 1px solid #d0d7e2; border: 1px solid var(--line);
border-radius: 999px; border-radius: 999px;
background: #fff; background: var(--surface-strong);
color: #5b6573; color: var(--muted);
font-size: 0.78rem; font-size: 0.78rem;
font-weight: 700; font-weight: 700;
line-height: 1; line-height: 1;
@@ -304,6 +284,11 @@
flex-direction: column; flex-direction: column;
} }
.fx-submenu-row,
.fx-card-head {
align-items: flex-start;
}
.fx-currency-search { .fx-currency-search {
flex: 1 1 auto; flex: 1 1 auto;
width: 100%; width: 100%;

View File

@@ -1,6 +1,6 @@
{ {
"eyebrow": "Modul", "eyebrow": "Modul",
"title": "FX-Rates", "title": "Waehrungskurse",
"description": "Zentrale Verwaltung fuer Waehrungskurse, Snapshots und FX-API-Abrufe.", "description": "Zentrale Verwaltung fuer Waehrungskurse, Snapshots und FX-API-Abrufe.",
"actions": [ "actions": [
{ "label": "Setup", "href": "/modules/setup/fx-rates", "variant": "secondary" } { "label": "Setup", "href": "/modules/setup/fx-rates", "variant": "secondary" }

View File

@@ -117,47 +117,65 @@ $tabs = [
['label' => 'Waehrungen', 'href' => '/module/fx-rates/currencies', 'active' => true], ['label' => 'Waehrungen', 'href' => '/module/fx-rates/currencies', 'active' => true],
]; ];
?> ?>
<?= module_shell_header('fx-rates', [
'title' => 'Waehrungskurse',
'tabs' => $tabs,
'actions' => [
['label' => 'Setup', 'href' => '/modules/setup/fx-rates', 'variant' => 'secondary', 'size' => 'sm'],
],
]) ?>
<div id="fx-rates-currencies" data-page='<?= e(is_string($currencyPageData) ? $currencyPageData : '{}') ?>'> <div id="fx-rates-currencies" data-page='<?= e(is_string($currencyPageData) ? $currencyPageData : '{}') ?>'>
<div class="fx-stack"> <div class="submenu-box">
<div class="fx-card"> <div class="fx-submenu-row">
<nav class="module-tabs" aria-label="Unterseiten von Waehrungskurse">
<?php foreach ($tabs as $tab): ?>
<a
class="<?= !empty($tab['active']) ? 'module-button module-button--tab-active' : 'module-button module-button--tab' ?>"
href="<?= e((string) $tab['href']) ?>"
><?= e((string) $tab['label']) ?></a>
<?php endforeach; ?>
</nav>
<div class="module-submenu-actions">
<a class="module-button module-button--secondary module-button--small" href="/modules/setup/fx-rates">Setup</a>
</div>
</div>
</div>
<?php if ($notice !== ''): ?> <?php if ($notice !== ''): ?>
<section class="section-box">
<div class="fx-message is-success"><?= e($notice) ?></div> <div class="fx-message is-success"><?= e($notice) ?></div>
</section>
<?php elseif ($error !== ''): ?> <?php elseif ($error !== ''): ?>
<section class="section-box">
<div class="fx-message is-error"><?= e($error) ?></div> <div class="fx-message is-error"><?= e($error) ?></div>
</section>
<?php endif; ?> <?php endif; ?>
<section class="section-box">
<div class="fx-section-head">
<div>
<h2>Waehrungs-Update</h2> <h2>Waehrungs-Update</h2>
<p>Auswahl wird in den FX-Rates-Einstellungen gespeichert und steht damit auf Handy und Desktop gleich zur Verfuegung.</p> <p>Auswahl wird in den Waehrungskurs-Einstellungen gespeichert und steht damit auf Handy und Desktop gleich zur Verfuegung.</p>
</div>
</div>
<div class="fx-action-row"> <div class="fx-action-row">
<form method="post"> <form method="post">
<input type="hidden" name="fx_action" value="refresh_rates"> <input type="hidden" name="fx_action" value="refresh_rates">
<button type="submit" class="fx-button fx-button--accent">Alle Wechselkurse aktualisieren</button> <button type="submit" class="module-button module-button--primary">Alle Wechselkurse aktualisieren</button>
</form> </form>
<form method="post"> <form method="post">
<input type="hidden" name="fx_action" value="sync_catalog"> <input type="hidden" name="fx_action" value="sync_catalog">
<button type="submit" class="fx-button fx-button--ghost">Waehrungskatalog sync</button> <button type="submit" class="module-button module-button--ghost">Waehrungskatalog sync</button>
</form> </form>
</div> </div>
</section>
<div class="fx-mini-grid"> <div class="fx-card-grid">
<div class="fx-mini-card"> <section class="card-box">
<div class="fx-mini-label">Fiat</div> <div class="fx-mini-label">Fiat</div>
<div><?= e((string) $fiatCount) ?> Waehrungen</div> <div class="fx-card-value"><?= e((string) $fiatCount) ?> Waehrungen</div>
</div> </section>
<div class="fx-mini-card"> <section class="card-box">
<div class="fx-mini-label">Krypto</div> <div class="fx-mini-label">Krypto</div>
<div><?= e((string) $cryptoCount) ?> Waehrungen</div> <div class="fx-card-value"><?= e((string) $cryptoCount) ?> Waehrungen</div>
</div> </section>
</div> </div>
<section class="section-box">
<div class="fx-field-label">Bevorzugte Waehrungen fuer Anzeige</div> <div class="fx-field-label">Bevorzugte Waehrungen fuer Anzeige</div>
<div class="fx-currency-selection-row"> <div class="fx-currency-selection-row">
<div class="fx-token-list fx-token-list--inline" data-fx-token-list></div> <div class="fx-token-list fx-token-list--inline" data-fx-token-list></div>
@@ -177,12 +195,12 @@ $tabs = [
<input type="hidden" name="fx_action" value="save_selection"> <input type="hidden" name="fx_action" value="save_selection">
<input type="hidden" name="display_base_currency" value="<?= e($displayBaseCurrency) ?>" data-fx-display-base-hidden> <input type="hidden" name="display_base_currency" value="<?= e($displayBaseCurrency) ?>" data-fx-display-base-hidden>
<div data-fx-hidden-preferred></div> <div data-fx-hidden-preferred></div>
<button type="submit" class="fx-button fx-button--ghost">Auswahl speichern</button> <button type="submit" class="module-button module-button--ghost">Auswahl speichern</button>
</form> </form>
</div> </div>
</div> </section>
<div class="fx-card"> <section class="section-box">
<div class="fx-card-head"> <div class="fx-card-head">
<div> <div>
<h2>Letzte 15 Kurs-Uploads</h2> <h2>Letzte 15 Kurs-Uploads</h2>
@@ -265,7 +283,5 @@ $tabs = [
</tbody> </tbody>
</table> </table>
</div> </div>
</section>
</div> </div>
</div>
</div>
<?= module_shell_footer() ?>

View File

@@ -58,30 +58,51 @@ $pageData = json_encode([
'preferred_currencies' => $preferredCurrencies, 'preferred_currencies' => $preferredCurrencies,
'recent_fetches' => $recentFetches, 'recent_fetches' => $recentFetches,
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$tabs = [
['label' => 'Ueberblick', 'href' => '/module/fx-rates', 'active' => true],
['label' => 'Waehrungen', 'href' => '/module/fx-rates/currencies'],
];
?> ?>
<?= module_shell_header('fx-rates', [
'title' => 'Waehrungskurse',
'actions' => [
['label' => 'Setup', 'href' => '/modules/setup/fx-rates', 'variant' => 'secondary', 'size' => 'sm'],
['label' => 'Aktuelle Kurse abrufen', 'href' => '/module/fx-rates?refresh=1', 'variant' => 'secondary', 'size' => 'sm'],
],
]) ?>
<div id="fx-rates-app" data-page='<?= e(is_string($pageData) ? $pageData : '{}') ?>'> <div id="fx-rates-app" data-page='<?= e(is_string($pageData) ? $pageData : '{}') ?>'>
<div class="fx-stack"> <div class="submenu-box">
<div class="fx-card"> <div class="fx-submenu-row">
<nav class="module-tabs" aria-label="Unterseiten von Waehrungskurse">
<?php foreach ($tabs as $tab): ?>
<a
class="<?= !empty($tab['active']) ? 'module-button module-button--tab-active' : 'module-button module-button--tab' ?>"
href="<?= e((string) $tab['href']) ?>"
><?= e((string) $tab['label']) ?></a>
<?php endforeach; ?>
</nav>
<div class="module-submenu-actions">
<a class="module-button module-button--secondary module-button--small" href="/modules/setup/fx-rates">Setup</a>
<a class="module-button module-button--secondary module-button--small" href="/module/fx-rates?refresh=1">Aktuelle Kurse abrufen</a>
</div>
</div>
</div>
<?php if ($notice !== ''): ?> <?php if ($notice !== ''): ?>
<section class="section-box">
<div class="fx-message is-success"><?= e($notice) ?></div> <div class="fx-message is-success"><?= e($notice) ?></div>
</section>
<?php elseif ($error !== ''): ?> <?php elseif ($error !== ''): ?>
<section class="section-box">
<div class="fx-message is-error"><?= e($error) ?></div> <div class="fx-message is-error"><?= e($error) ?></div>
</section>
<?php endif; ?> <?php endif; ?>
<section class="section-box">
<div class="fx-section-head">
<div>
<h2>Umrechnung</h2>
<p>Umrechnung auf Basis des letzten verfuegbaren Kurses zwischen den bevorzugten Waehrungen.</p>
</div>
</div>
<p class="fx-api-note"> <p class="fx-api-note">
API-Self-Describe-Endpoint: API-Self-Describe-Endpoint:
<a href="<?= e($apiDescribeUrl) ?>" target="_blank" rel="noopener noreferrer"><?= e($apiDescribeUrl) ?></a> <a href="<?= e($apiDescribeUrl) ?>" target="_blank" rel="noopener noreferrer"><?= e($apiDescribeUrl) ?></a>
</p> </p>
<h2>Umrechnung</h2>
<p>Umrechnung auf Basis des letzten verfuegbaren Kurses zwischen den bevorzugten Waehrungen.</p>
<div class="fx-form-grid"> <div class="fx-form-grid">
<label> <label>
<span>Quellwaehrung</span> <span>Quellwaehrung</span>
@@ -105,9 +126,9 @@ $pageData = json_encode([
</label> </label>
</div> </div>
<div class="fx-convert-result" data-bind="convert-result">Noch keine Umrechnung berechnet.</div> <div class="fx-convert-result" data-bind="convert-result">Noch keine Umrechnung berechnet.</div>
</div> </section>
<div class="fx-card"> <section class="section-box">
<div class="fx-card-head"> <div class="fx-card-head">
<div> <div>
<h2>Kursverlauf</h2> <h2>Kursverlauf</h2>
@@ -131,7 +152,5 @@ $pageData = json_encode([
</tbody> </tbody>
</table> </table>
</div> </div>
</section>
</div> </div>
</div>
</div>
<?= module_shell_footer() ?>