Compare commits
2 Commits
9dff578c15
...
ec56a231e9
| Author | SHA1 | Date | |
|---|---|---|---|
| ec56a231e9 | |||
| 04a29ae8cd |
@@ -59,8 +59,6 @@
|
||||
{{ $description := printf
|
||||
"Konvertieren Sie %s in %s. Einfache und schnelle Umrechnung mit Einheitsumrechner."
|
||||
$fromName $toName }}
|
||||
{{ $fromCurrency := $fromCode }}
|
||||
{{ $toCurrency := $toCode }}
|
||||
{{ $fromRate := 1 }}
|
||||
{{ if ne $fromCode "eur" }}
|
||||
{{ $fromRate = index $pbRates $fromCode }}
|
||||
@@ -79,8 +77,6 @@
|
||||
"slug" $slug
|
||||
"description" $description
|
||||
"engine" "runtime"
|
||||
"currency_from" $fromCurrency
|
||||
"currency_to" $toCurrency
|
||||
"rate" $rate
|
||||
"rates_updated" $latestUpdate
|
||||
}}
|
||||
|
||||
@@ -13,12 +13,20 @@
|
||||
"{ toIntermediate: v => %s, fromIntermediate: v => %s }"
|
||||
$toExpr $fromExpr }}
|
||||
{{ else if eq .Params.engine "runtime" }}
|
||||
{{ $config = dict "fromCurrency" .Params.from
|
||||
"toCurrency" .Params.to | jsonify }}
|
||||
{{ $fromCode := .Params.from }}
|
||||
{{ $toCode := .Params.to }}
|
||||
{{ $config = printf
|
||||
"{ init: async function () { const pbUrl = document.querySelector('meta[name=\\'pocketbase-url\\']')?.content || 'https://www.alphabreed.com'; try { const response = await fetch(`${pbUrl}/api/collections/currencies/records`); if (!response.ok) { this.ratesError = 'Wechselkurse konnten nicht geladen werden.'; return; } const data = await response.json(); let latestUpdate = ''; for (const item of data.items || []) { this.rates[item.id] = item.rate; if (!latestUpdate || item.updated > latestUpdate) { latestUpdate = item.updated; } } const fromRate = '%s' === 'eur' ? 1 : this.rates['%s']; const toRate = '%s' === 'eur' ? 1 : this.rates['%s']; if (fromRate && toRate) { this.currentRate = parseFloat(toRate) / parseFloat(fromRate); } if (latestUpdate) { this.ratesUpdated = new Date(latestUpdate).toLocaleString('de-DE', { day: '2-digit', month: 'long', year: 'numeric', hour: '2-digit', minute: '2-digit' }); } } catch (e) { this.ratesError = 'Wechselkurse konnten nicht geladen werden.'; } }, convert: function (value) { const fromRate = '%s' === 'eur' ? 1 : this.rates['%s']; const toRate = '%s' === 'eur' ? 1 : this.rates['%s']; if (!fromRate || !toRate) { return null; } return value.times(new Decimal(toRate)).dividedBy(new Decimal(fromRate)); } }"
|
||||
$fromCode $fromCode $toCode $toCode
|
||||
$fromCode $fromCode $toCode $toCode }}
|
||||
{{ end }}
|
||||
{{ $availableUnits := partial "available-units.html"
|
||||
(dict "category" .Params.category "units" $catData.units) }}
|
||||
|
||||
<div x-data="createConverter('{{ .Params.engine }}',
|
||||
{{ $config | safeJS }})"
|
||||
x-init="init()">
|
||||
|
||||
<div id="conversionform"
|
||||
class="bg-white p-8 rounded-lg shadow-md w-full
|
||||
dark:bg-gray-700 dark:text-grey-200"
|
||||
@@ -66,11 +74,13 @@
|
||||
|
||||
<div class="flex-1 w-full">
|
||||
<label for="from"
|
||||
class="block text-sm font-medium opacity-75">
|
||||
class="block text-sm font-medium
|
||||
opacity-75">
|
||||
Umrechnen von:
|
||||
</label>
|
||||
<select id="from" name="from"
|
||||
@change="navigateWithValue($event.target.value)"
|
||||
@change="navigateWithValue(
|
||||
$event.target.value)"
|
||||
class="select mt-1 block w-full">
|
||||
{{ range $unit, $unitData := $availableUnits }}
|
||||
{{ if ne $unit $.Params.to }}
|
||||
@@ -109,11 +119,13 @@
|
||||
|
||||
<div class="flex-1 w-full">
|
||||
<label for="to"
|
||||
class="block text-sm font-medium opacity-75">
|
||||
class="block text-sm font-medium
|
||||
opacity-75">
|
||||
Umrechnen in:
|
||||
</label>
|
||||
<select id="to" name="to"
|
||||
@change="navigateWithValue($event.target.value)"
|
||||
@change="navigateWithValue(
|
||||
$event.target.value)"
|
||||
class="select mt-1 block w-full">
|
||||
{{ range $unit, $unitData := $availableUnits }}
|
||||
{{ if ne $unit $.Params.from }}
|
||||
@@ -132,15 +144,10 @@
|
||||
|
||||
</div>
|
||||
|
||||
<div x-data="createConverter('{{ .Params.engine }}',
|
||||
{{ $config | safeJS }})"
|
||||
x-init="init()">
|
||||
|
||||
<div class="flex flex-col sm:flex-row gap-4 mt-4">
|
||||
<div class="flex-1">
|
||||
<label for="value"
|
||||
class="block text-sm font-medium
|
||||
opacity-75">
|
||||
class="block text-sm font-medium opacity-75">
|
||||
Wert in {{ .Params.from_name }}:
|
||||
</label>
|
||||
<input type="text" id="value"
|
||||
@@ -152,8 +159,7 @@
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<label for="result"
|
||||
class="block text-sm font-medium
|
||||
opacity-75">
|
||||
class="block text-sm font-medium opacity-75">
|
||||
Ergebnis in {{ .Params.to_name }}:
|
||||
</label>
|
||||
<input type="text" id="result"
|
||||
@@ -170,8 +176,6 @@
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="formula" class="mt-4">
|
||||
{{ partial "conversion-formula.html" . }}
|
||||
</div>
|
||||
@@ -179,6 +183,8 @@
|
||||
{{ partial "conversion-seo-texts.html" . }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{{ end }}
|
||||
|
||||
|
||||
|
||||
@@ -49,11 +49,24 @@
|
||||
</p>
|
||||
{{ end }}
|
||||
{{ else if eq .Params.engine "runtime" }}
|
||||
{{ $rate := float .Params.rate }}
|
||||
<p class="mb-4 mt-8">
|
||||
Die Umrechnung von {{ $fromName }} in {{ $toName }}
|
||||
erfolgt mittels dieser Formel:
|
||||
</p>
|
||||
<div x-show="currentRate > 0">
|
||||
<p class="text-lg text-primary dark:text-primary-light"
|
||||
x-show="currentRate >= 1">
|
||||
<b x-text="'{{ $toName }} = {{ $fromName }} × '
|
||||
+ prettyNumber(currentRate)"></b>
|
||||
</p>
|
||||
<p class="text-lg text-primary dark:text-primary-light"
|
||||
x-show="currentRate < 1">
|
||||
<b x-text="'{{ $toName }} = {{ $fromName }} / '
|
||||
+ prettyNumber(1 / currentRate)"></b>
|
||||
</p>
|
||||
</div>
|
||||
<div x-show="!currentRate">
|
||||
{{ $rate := float .Params.rate }}
|
||||
{{ if ge $rate 1 }}
|
||||
<p class="text-lg text-primary dark:text-primary-light">
|
||||
<b>{{ $toName }} = {{ $fromName }} ×
|
||||
@@ -66,10 +79,10 @@
|
||||
{{ printf "%.12g" $invRate }}</b>
|
||||
</p>
|
||||
{{ end }}
|
||||
{{ with .Params.rates_updated }}
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 mt-2">
|
||||
</div>
|
||||
<p x-show="ratesUpdated"
|
||||
class="text-sm text-gray-600 dark:text-gray-400 mt-2">
|
||||
Wechselkurs zuletzt aktualisiert:
|
||||
{{ time.Format "2. Januar 2006, 15:04" . }}
|
||||
<span x-text="ratesUpdated"></span>
|
||||
</p>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
@@ -36,14 +36,14 @@ function navActions() {
|
||||
* @returns {object} Alpine component data
|
||||
*/
|
||||
function createConverter(engine, config) {
|
||||
if (engine === 'runtime') {
|
||||
return {
|
||||
inputValue: '1',
|
||||
result: '',
|
||||
rates: {},
|
||||
ratesError: '',
|
||||
ratesUpdated: '',
|
||||
currentRate: 0,
|
||||
async init() {
|
||||
await this.loadRates();
|
||||
const params = new URLSearchParams(
|
||||
window.location.search);
|
||||
const valueParam = params.get('v');
|
||||
@@ -55,84 +55,9 @@ function createConverter(engine, config) {
|
||||
this.inputValue = val.replace(/,/g, '.');
|
||||
}
|
||||
});
|
||||
this.calculate();
|
||||
},
|
||||
async loadRates() {
|
||||
const pbUrl = document.querySelector(
|
||||
'meta[name="pocketbase-url"]')?.content
|
||||
|| 'https://www.alphabreed.com';
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${pbUrl}/api/collections/currencies/records`);
|
||||
if (!response.ok) {
|
||||
this.ratesError =
|
||||
'Wechselkurse konnten nicht geladen werden.';
|
||||
return;
|
||||
if (typeof config.init === 'function') {
|
||||
await config.init.call(this);
|
||||
}
|
||||
const data = await response.json();
|
||||
for (const item of data.items || []) {
|
||||
this.rates[item.id] = item.rate;
|
||||
}
|
||||
} catch (e) {
|
||||
this.ratesError =
|
||||
'Wechselkurse konnten nicht geladen werden.';
|
||||
}
|
||||
},
|
||||
calculate() {
|
||||
try {
|
||||
this.result = '';
|
||||
if (Object.keys(this.rates).length === 0) {
|
||||
return;
|
||||
}
|
||||
const normalized = parseNumber(
|
||||
this.inputValue);
|
||||
if (!normalized || isNaN(normalized)) {
|
||||
return;
|
||||
}
|
||||
const fromCode = config.fromCurrency || '';
|
||||
const toCode = config.toCurrency || '';
|
||||
const fromRate = fromCode === 'eur'
|
||||
? 1 : this.rates[fromCode];
|
||||
const toRate = toCode === 'eur'
|
||||
? 1 : this.rates[toCode];
|
||||
if (!fromRate || !toRate) {
|
||||
return;
|
||||
}
|
||||
const value = new Decimal(normalized);
|
||||
const rate = new Decimal(toRate)
|
||||
.dividedBy(new Decimal(fromRate));
|
||||
const rawResult = value.times(rate);
|
||||
this.result = prettyNumber(rawResult);
|
||||
const normalizedInputValue = this.inputValue
|
||||
.replace(/,/g, '.');
|
||||
if (normalizedInputValue
|
||||
&& normalizedInputValue !== '0') {
|
||||
history.replaceState(
|
||||
null, '', '?v=' + normalizedInputValue);
|
||||
}
|
||||
} catch (e) {
|
||||
this.result = '';
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
inputValue: '1',
|
||||
result: '',
|
||||
ratesError: '',
|
||||
init() {
|
||||
const params = new URLSearchParams(
|
||||
window.location.search);
|
||||
const valueParam = params.get('v');
|
||||
if (valueParam) {
|
||||
this.inputValue = valueParam.replace(/,/g, '.');
|
||||
}
|
||||
this.$watch('inputValue', val => {
|
||||
if (val && val.indexOf(',') !== -1) {
|
||||
this.inputValue = val.replace(/,/g, '.');
|
||||
}
|
||||
});
|
||||
this.calculate();
|
||||
},
|
||||
calculate() {
|
||||
@@ -144,7 +69,9 @@ function createConverter(engine, config) {
|
||||
}
|
||||
const value = new Decimal(normalized);
|
||||
let rawResult;
|
||||
if (engine === 'linear') {
|
||||
if (typeof config.convert === 'function') {
|
||||
rawResult = config.convert.call(this, value);
|
||||
} else if (engine === 'linear') {
|
||||
const fromFactor = new Decimal(
|
||||
config.fromFactor);
|
||||
const toFactor = new Decimal(
|
||||
@@ -185,7 +112,16 @@ function createConverter(engine, config) {
|
||||
function prettyNumber(num, minPrecision, maxPrecision) {
|
||||
minPrecision = minPrecision || 4;
|
||||
maxPrecision = maxPrecision || 10;
|
||||
const d = new Decimal(num);
|
||||
const val = num instanceof Decimal ? num.toNumber() : Number(num);
|
||||
if (!Number.isFinite(val)) {
|
||||
return '';
|
||||
}
|
||||
let d;
|
||||
try {
|
||||
d = new Decimal(num);
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
const absVal = d.abs();
|
||||
// Larger numbers need fewer decimal places for readable output;
|
||||
// smaller numbers get more to stay meaningful.
|
||||
|
||||
Reference in New Issue
Block a user