diff --git a/docker-compose.yml b/docker-compose.yml index 8d00032..54a6511 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ services: web: build: . - image: umrechner:v1.0.5 + image: umrechner:v1.0.6 restart: unless-stopped environment: - POCKETBASE_URL={$POCKETBASE_URL} diff --git a/hugo/content/_content.gotmpl b/hugo/content/_content.gotmpl index 21a8a30..e2cd8dd 100644 --- a/hugo/content/_content.gotmpl +++ b/hugo/content/_content.gotmpl @@ -1,5 +1,5 @@ {{/* Generischer Content Adapter fuer Einheitsumrechner */}} -{{/* Erzeugt alle from-to Permutationsseiten fuer linear, intermediate und runtime */}} +{{/* Erzeugt alle from-to Permutationsseiten fuer linear, temperatures und currencies */}} {{ $pbBase := getenv "POCKETBASE_URL" | default site.Params.pocketbase_url }} {{ $pbUrl := printf "%s/api/collections/currencies/records" $pbBase }} @@ -35,7 +35,7 @@ {{ $categorySlug := $categoryData.slug }} {{ $units := $categoryData.units }} - {{ if eq $engine "runtime" }} + {{ if eq $engine "currencies" }} {{ if not $pbOk }}{{ continue }}{{ end }} {{ $pbCurrencies := slice }} {{ range $record := $pbData }} @@ -76,7 +76,7 @@ "to_name" $toName "slug" $slug "description" $description - "engine" "runtime" + "engine" "currencies" "rate" $rate "rates_updated" $latestUpdate }} @@ -121,11 +121,21 @@ "from_factor" $fromData.factor "to_factor" $toData.factor ) }} - {{ else if eq $engine "intermediate" }} + {{ else if eq $engine "temperatures" }} + {{ $convertFormulas := $categoryData.convertFormulas }} + {{ $formula := "" }} + {{ with index $convertFormulas $fromUnit }} + {{ $formula = index . $toUnit }} + {{ end }} + {{ $displayFormulas := $categoryData.formulas }} + {{ $displayFormula := "" }} + {{ with index $displayFormulas $fromUnit }} + {{ $displayFormula = index . $toUnit }} + {{ end }} {{ $params = merge $params (dict - "engine" "intermediate" - "from_unit" $fromUnit - "to_unit" $toUnit + "engine" "temperatures" + "convertFormula" $formula + "displayFormula" $displayFormula ) }} {{ end }} diff --git a/hugo/data/temperaturen.json b/hugo/data/temperaturen.json index 686720f..5ecf28e 100644 --- a/hugo/data/temperaturen.json +++ b/hugo/data/temperaturen.json @@ -8,20 +8,7 @@ "rankine", "reaumur" ], - "conversion_engine": "intermediate", - "intermediate_unit": "celsius", - "to_intermediate": { - "fahrenheit": "(v - 32) * 5/9", - "kelvin": "v - 273.15", - "rankine": "(v - 491.67) * 5/9", - "reaumur": "v * 5/4" - }, - "from_intermediate": { - "fahrenheit": "v * 9/5 + 32", - "kelvin": "v + 273.15", - "rankine": "(v + 273.15) * 9/5", - "reaumur": "v * 4/5" - }, + "conversion_engine": "temperatures", "units": { "celsius": { "name": "Celsius" @@ -38,5 +25,69 @@ "reaumur": { "name": "Réaumur" } + }, + "formulas": { + "celsius": { + "fahrenheit": "Fahrenheit = Celsius × 9/5 + 32", + "kelvin": "Kelvin = Celsius + 273,15", + "rankine": "Rankine = (Celsius + 273,15) × 9/5", + "reaumur": "Réaumur = Celsius × 4/5" + }, + "fahrenheit": { + "celsius": "Celsius = (Fahrenheit − 32) × 5/9", + "kelvin": "Kelvin = (Fahrenheit − 32) × 5/9 + 273,15", + "rankine": "Rankine = Fahrenheit + 459,67", + "reaumur": "Réaumur = (Fahrenheit − 32) × 4/9" + }, + "kelvin": { + "celsius": "Celsius = Kelvin − 273,15", + "fahrenheit": "Fahrenheit = (Kelvin − 273,15) × 9/5 + 32", + "rankine": "Rankine = Kelvin × 9/5", + "reaumur": "Réaumur = (Kelvin − 273,15) × 4/5" + }, + "rankine": { + "celsius": "Celsius = (Rankine − 491,67) × 5/9", + "fahrenheit": "Fahrenheit = Rankine − 459,67", + "kelvin": "Kelvin = Rankine × 5/9", + "reaumur": "Réaumur = (Rankine − 491,67) × 4/9" + }, + "reaumur": { + "celsius": "Celsius = Réaumur × 5/4", + "fahrenheit": "Fahrenheit = Réaumur × 9/4 + 32", + "kelvin": "Kelvin = Réaumur × 5/4 + 273,15", + "rankine": "Rankine = (Réaumur × 5/4 + 273,15) × 9/5" + } + }, + "convertFormulas": { + "celsius": { + "fahrenheit": "v => v.times(9).dividedBy(5).plus(32)", + "kelvin": "v => v.plus(273.15)", + "rankine": "v => v.plus(273.15).times(9).dividedBy(5)", + "reaumur": "v => v.times(4).dividedBy(5)" + }, + "fahrenheit": { + "celsius": "v => v.minus(32).times(5).dividedBy(9)", + "kelvin": "v => v.minus(32).times(5).dividedBy(9).plus(273.15)", + "rankine": "v => v.plus(459.67)", + "reaumur": "v => v.minus(32).times(4).dividedBy(9)" + }, + "kelvin": { + "celsius": "v => v.minus(273.15)", + "fahrenheit": "v => v.minus(273.15).times(9).dividedBy(5).plus(32)", + "rankine": "v => v.times(9).dividedBy(5)", + "reaumur": "v => v.minus(273.15).times(4).dividedBy(5)" + }, + "rankine": { + "celsius": "v => v.minus(491.67).times(5).dividedBy(9)", + "fahrenheit": "v => v.minus(459.67)", + "kelvin": "v => v.times(5).dividedBy(9)", + "reaumur": "v => v.minus(491.67).times(4).dividedBy(9)" + }, + "reaumur": { + "celsius": "v => v.times(5).dividedBy(4)", + "fahrenheit": "v => v.times(9).dividedBy(4).plus(32)", + "kelvin": "v => v.times(5).dividedBy(4).plus(273.15)", + "rankine": "v => v.times(5).dividedBy(4).plus(273.15).times(9).dividedBy(5)" + } } } diff --git a/hugo/data/waehrungen.json b/hugo/data/waehrungen.json index 4a62ac1..40bdb63 100644 --- a/hugo/data/waehrungen.json +++ b/hugo/data/waehrungen.json @@ -33,7 +33,7 @@ "php", "egp" ], - "conversion_engine": "runtime", + "conversion_engine": "currencies", "units": { "eur": { "name": "Euro" diff --git a/hugo/layouts/conversion/single.html b/hugo/layouts/conversion/single.html index f43c10c..c2397bb 100644 --- a/hugo/layouts/conversion/single.html +++ b/hugo/layouts/conversion/single.html @@ -1,36 +1,21 @@ {{ define "main" }} {{ $catData := index hugo.Data .Params.category }} - {{ $config := "" }} - {{ if eq .Params.engine "linear" }} - {{ $config = dict "fromFactor" .Params.from_factor - "toFactor" .Params.to_factor | jsonify }} - {{ else if eq .Params.engine "intermediate" }} - {{ $toExpr := index $catData.to_intermediate - .Params.from | default "v" }} - {{ $fromExpr := index $catData.from_intermediate - .Params.to | default "v" }} - {{ $config = printf - "{ toIntermediate: v => %s, fromIntermediate: v => %s }" - $toExpr $fromExpr }} - {{ else if eq .Params.engine "runtime" }} - {{ $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) }} -
+ {{ $engine := .Params.engine | default "linear" }} + + {{ if eq $engine "currencies" }} + {{ partial "conversion/currencies-data" . }} + {{ else if eq $engine "temperatures" }} + {{ partial "conversion/temperatures-data" . }} + {{ else }} + {{ partial "conversion/linear-data" . }} + {{ end }}
+ dark:bg-gray-700 dark:text-grey-200">

@@ -40,18 +25,13 @@
- -
{{ range $catData.unitOrder }} {{ $unitData := index $availableUnits . }} @@ -98,8 +78,8 @@
@@ -166,13 +145,8 @@ -

@@ -188,5 +162,3 @@

{{ end }} - - diff --git a/hugo/layouts/partials/conversion-formula.html b/hugo/layouts/partials/conversion-formula.html index 05cccb2..a749d63 100644 --- a/hugo/layouts/partials/conversion-formula.html +++ b/hugo/layouts/partials/conversion-formula.html @@ -48,24 +48,32 @@ {{ replace $fromI "v" $intermediateName 1 }}

{{ end }} -{{ else if eq .Params.engine "runtime" }} +{{ else if eq .Params.engine "temperatures" }}

Die Umrechnung von {{ $fromName }} in {{ $toName }} erfolgt mittels dieser Formel:

-
+

+ {{ .Params.displayFormula }} +

+{{ else if eq .Params.engine "currencies" }} +

+ Die Umrechnung von {{ $fromName }} in {{ $toName }} + erfolgt mittels dieser Formel: +

+

+ x-show="rate >= 1"> + + prettyNumber(rate)">

+ x-show="rate < 1"> + + prettyNumber(1 / rate)">

-
+
{{ $rate := float .Params.rate }} {{ if ge $rate 1 }}

diff --git a/hugo/layouts/partials/conversion/currencies-data.html b/hugo/layouts/partials/conversion/currencies-data.html new file mode 100644 index 0000000..ae71ca6 --- /dev/null +++ b/hugo/layouts/partials/conversion/currencies-data.html @@ -0,0 +1,40 @@ +

diff --git a/hugo/layouts/partials/conversion/linear-data.html b/hugo/layouts/partials/conversion/linear-data.html new file mode 100644 index 0000000..8c24f24 --- /dev/null +++ b/hugo/layouts/partials/conversion/linear-data.html @@ -0,0 +1,9 @@ +
diff --git a/hugo/layouts/partials/conversion/temperatures-data.html b/hugo/layouts/partials/conversion/temperatures-data.html new file mode 100644 index 0000000..c486572 --- /dev/null +++ b/hugo/layouts/partials/conversion/temperatures-data.html @@ -0,0 +1,9 @@ +
diff --git a/hugo/static/js/converter.js b/hugo/static/js/converter.js index 5d97e22..4f96797 100644 --- a/hugo/static/js/converter.js +++ b/hugo/static/js/converter.js @@ -1,107 +1,5 @@ 'use strict'; -/** - * Alpine.js navigation helpers for conversion page. - * Handles navigation via hidden AJAX link. - */ -function navActions() { - return { - navigate(url) { - this.$refs.ajaxLink.href = url; - this.$nextTick(() => this.$refs.ajaxLink.click()); - }, - navigateWithValue(url) { - const el = document.getElementById('value'); - const val = el ? el.value.replace(/,/g, '.') : '1'; - this.$refs.ajaxLink.href = url + '?v=' + val; - this.$nextTick(() => this.$refs.ajaxLink.click()); - }, - navigateWithResult(url) { - const el = document.getElementById('result'); - const val = el ? el.value.replace(/,/g, '.') : ''; - if (val) { - this.$refs.ajaxLink.href = url + '?v=' + val; - } else { - this.$refs.ajaxLink.href = url; - } - this.$nextTick(() => this.$refs.ajaxLink.click()); - } - }; -} - -/** - * Factory function for Alpine.js converter components. - * @param {string} engine - 'linear', 'intermediate' or 'runtime' - * @param {object} config - Engine-specific configuration - * @returns {object} Alpine component data - */ -function createConverter(engine, config) { - return { - inputValue: '1', - result: '', - rates: {}, - ratesError: '', - ratesUpdated: '', - currentRate: 0, - async 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, '.'); - } - }); - if (typeof config.init === 'function') { - await config.init.call(this); - } - this.calculate(); - }, - calculate() { - try { - const normalized = parseNumber(this.inputValue); - if (!normalized || isNaN(normalized)) { - this.result = ''; - return; - } - const value = new Decimal(normalized); - let rawResult; - 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( - config.toFactor); - rawResult = value.times(fromFactor) - .dividedBy(toFactor); - } else if (engine === 'intermediate') { - let v = value.toNumber(); - v = config.toIntermediate(v); - v = config.fromIntermediate(v); - rawResult = new Decimal(v); - } else { - this.result = ''; - return; - } - this.result = prettyNumber(rawResult); - const normalizedInputValue = this.inputValue - .replace(/,/g, '.'); - if (normalizedInputValue - && normalizedInputValue !== '0') { - history.replaceState( - null, '', '?v=' + normalizedInputValue); - } - } catch (e) { - this.result = ''; - } - } - }; -} - /** * Formats a number as plain digits with dot as decimal separator. * @param {Decimal|string|number} num @@ -126,7 +24,7 @@ function prettyNumber(num, minPrecision, maxPrecision) { // Larger numbers need fewer decimal places for readable output; // smaller numbers get more to stay meaningful. let precision = maxPrecision; - if (absVal.gte(1000000)) { + if (absVal.gte(10000)) { precision = 2; } else if (absVal.gte(1000)) { precision = 4; @@ -160,3 +58,39 @@ function parseNumber(input) { } return input.replace(/,/g, '.') || '0'; } + +/** + * Prepares input value for conversion. + * Returns a Decimal, or 1 if input is empty. + * @param {string} input - User input from field + * @returns {Decimal} Prepared value + */ +function prepareValue(input) { + if (!input || input.trim() === '') { + return new Decimal(1); + } + return new Decimal(input.replace(/,/g, '.')); +} + +/** + * Updates the URL with the current input value. + * @param {string} value - The input value + */ +function updateUrl(value) { + const normalized = value ? value.replace(/,/g, '.') : ''; + if (normalized && normalized !== '0') { + history.replaceState(null, '', '?v=' + normalized); + } else { + history.replaceState(null, '', '?v=1'); + } +} + +/** + * Navigates to a new URL with the current input value as parameter. + * @param {string} url - Target URL + * @param {string} value - Current input value + */ +function navigate(url, value) { + const normalized = value ? value.replace(/,/g, '.') : '1'; + window.location.href = url + '?v=' + normalized; +}