Refaktorisierungs-Optionen und Fix für Zahlen-Parsing

This commit is contained in:
2026-06-06 20:02:23 +02:00
parent b5b7866395
commit 2629ef1f12
4 changed files with 191 additions and 25 deletions

163
REFACTOR.md Normal file
View File

@@ -0,0 +1,163 @@
# Refactoring-Optionen
## 1. `prepareValue` reparieren + tote `parseNumber`-Funktion entfernen
**Dateien:** `hugo/static/js/converter.js`
`prepareValue()` macht `input.replace(/,/g, '.')`. Bei deutscher Eingabe `1.000,50` wird
daraus der ungültige String `1.000.50` `new Decimal()` wirft einen Fehler und die
Reaktivität bricht. Die Funktion `parseNumber()` (wird nirgends verwendet) würde das
korrekt parsen.
**Vorschlag:** Logik von `parseNumber` in `prepareValue` übernehmen, `parseNumber`
löschen.
**Effekt:** Bugfix + Tote-Code-Eliminierung.
---
## 2. Überzähliges schließendes `</div>` in Conversion-Seite entfernen
**Datei:** `hugo/layouts/conversion/single.html`
Vor dem abschließenden `{{ end }}` steht ein `</div>` ohne zugehöriges öffnendes
Element in dieser Template-Datei. Das erzeugt invalides HTML.
**Effekt:** HTML-Korrektheit, weniger Fragilität.
---
## 3. Toten `intermediate`-Engine-Zweig aus Formel-Template entfernen
**Datei:** `hugo/layouts/partials/conversion-formula.html`
Keine JSON-Datendatei verwendet `"conversion_engine": "intermediate"`. Der komplette
`else if eq .Params.engine "intermediate"`-Block wird niemals ausgeführt.
**Effekt:** Reduziert Template-Logik um ~20 Zeilen toten Code.
---
## 4. Engine-Partial dynamisch statt per `if/else` laden
**Datei:** `hugo/layouts/conversion/single.html`
Statt dreier verschachtelter `if/else` zur Auswahl des Data-Partials:
```html
{{ if eq $engine "currencies" }} ... {{ else if eq $engine "temperatures" }} ... {{ else }} ... {{ end }}
```
reicht ein dynamischer Partial-Aufruf:
```html
{{ partial (printf "conversion/%s-data" $engine) . }}
```
**Effekt:** Entfaltet 3 Ebenen verschachtelte Template-Logik.
---
## 5. SVG-Ladepattern in eigenes Partial extrahieren
**Dateien:** `hugo/layouts/index.html`, `hugo/layouts/404.html`,
`hugo/layouts/partials/unit-dropdown.html`
Diese drei Dateien wiederholen identisch:
```html
{{ $svgPath := printf "img/%s.svg" $data.slug }}
{{ $svgFile := readFile (printf "static/%s" $svgPath) }}
{{ if $svgFile }}{{ $svgFile | safeHTML }}{{ else }}<span></span>{{ end }}
```
**Vorschlag:** `hugo/layouts/partials/icon.html` mit `(dict "slug" $data.slug)`.
**Effekt:** DRY, Änderungen am Icon-Loading zentral.
---
## 6. Wiederholte Select-Optionen-Logik in Partial auslagern
**Datei:** `hugo/layouts/conversion/single.html`
Die Dropdowns für „von“ und „in“ enthalten nahezu identische `range`-Schleifen mit
nur leicht unterschiedlichen Variablen.
**Vorschlag:** `hugo/layouts/partials/unit-options.html` via Parameter
`(dict "mode" "from" ...)` steuern.
**Effekt:** Weniger Redundanz, geringere Fehleranfälligkeit bei Änderungen.
---
## 7. `category-url.html` entminifizieren
**Datei:** `hugo/layouts/partials/category-url.html`
Aktuell 1 Zeile, 260+ Zeichen ohne Umbrüche. Das ist die komplexeste Logik der
Seite (Finden des ersten und zweiten verfügbaren Units), aber völlig unlesbar.
**Effekt:** Deutlich bessere mentale Parsbarkeit.
---
## 8. URL-Wert-Normalisierung deduplizieren + `0`-Inkonsistenz fixen
**Datei:** `hugo/static/js/converter.js`
`updateUrl` und `navigate` wiederholen die Komma-zu-Punkt-Ersetzung. Zudem verhält
sich `updateUrl("0")` anders als `navigate(url, "0")` (ersteres setzt `?v=1`,
letzteres `?v=0`).
**Vorschlag:** Eine gemeinsame Funktion `normalizeUrlValue(value)` extrahieren.
**Effekt:** DRY + Konsistenzfix.
---
## 9. Redundantes `new Decimal(v)` in linear-data entfernen
**Datei:** `hugo/layouts/partials/conversion/linear-data.html`
`convert: v => new Decimal(v).times(...)` `v` kommt bereits als `Decimal` aus
`prepareValue`. Temperaturen und Währungen nutzen `v` direkt.
**Effekt:** Konsistentes Verhalten über alle Engines.
---
## 10. `_content.gotmpl` in Teil-Partials splitten
**Datei:** `hugo/content/_content.gotmpl`
Die Datei ist 140+ Zeilen lang und mischt drei Konzerne:
Währungs-Page-Generierung, Linear-Page-Generierung und Temperaturen-Page-Generierung.
**Vorschlag:**
`hugo/layouts/partials/content-generators/currencies.html`,
`linear.html`, `temperatures.html`.
**Effekt:** Besseres Verständnis des Bauprozesses, leichteres Testen einzelner
Generatoren.
---
## 11. Stille Fehler bei Währungs-Fetch im Build loggen
**Datei:** `hugo/layouts/partials/available-currencies.html`
Der Fehlerfall von `resources.GetRemote` wird abgefangen, aber nicht geloggt
(`{{ with .Err }}{{ end }}`). `_content.gotmpl` macht das besser mit `warnf`.
**Effekt:** Einfacheres Debuggen bei Build-Zeit-Fehlern.
---
## Nicht empfohlen
- **SEO-Text-Partials vereinheitlichen:** 155 Dateien unter `partials/texts/`.
Die sind inhaltsgetrieben, keine Code-Redundanz.
- **Alle `conversion_engine` Defaults aus JSON entfernen:** Theoretisch
defaultbar, aber die explizite Deklaration in jeder Datei ist defensiver und
selbsterklärend.

View File

@@ -1,7 +1,7 @@
services:
web:
build: .
image: umrechner:v1.0.6
image: umrechner:v1.0.7
restart: unless-stopped
environment:
- POCKETBASE_URL={$POCKETBASE_URL}

View File

@@ -79,7 +79,7 @@
<div class="text-center sm:pt-6">
<button type="button"
@click="navigate(
$el.dataset.swapUrl, prettyNumber(convert(prepareValue(inputValue))))"
$el.dataset.swapUrl, prettyNumber(convert(parseNumber(inputValue))))"
data-swap-url="/{{ .Params.to }}-in-{{ .Params.from }}/"
class="inline-flex items-center text-sm
font-medium text-primary
@@ -145,7 +145,7 @@
</label>
<input type="text" id="result"
disabled
:value="prettyNumber(convert(prepareValue(inputValue)))"
:value="prettyNumber(convert(parseNumber(inputValue)))"
class="textinput mt-1 block w-full">
</div>
</div>

View File

@@ -44,32 +44,35 @@ function prettyNumber(num, minPrecision, maxPrecision) {
}
/**
* Parses a German number string.
* Accepts both comma and dot as decimal separator.
* @param {string} input
* @returns {string} Normalized string with dot as decimal
* Parses a number string for conversion.
* Accepts both German (1.234,56) and US (1,234.56) notation.
* The last dot or comma is always the decimal separator;
* all preceding ones are treated as thousand separators and removed.
* Invalid or empty input returns Decimal(0).
* @param {string} input - User input from field
* @returns {Decimal} Parsed Decimal value
*/
function parseNumber(input) {
if (!input) return '0';
const hasComma = input.indexOf(',') !== -1;
const hasDot = input.indexOf('.') !== -1;
if (hasComma && hasDot) {
return input.replace(/\./g, '').replace(/,/g, '.') || '0';
}
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(0);
}
const trimmed = input.trim();
const lastDot = trimmed.lastIndexOf('.');
const lastComma = trimmed.lastIndexOf(',');
const lastSep = Math.max(lastDot, lastComma);
let normalized;
if (lastSep === -1) {
normalized = trimmed;
} else {
const beforeSep = trimmed.slice(0, lastSep).replace(/[.,]/g, '');
const afterSep = trimmed.slice(lastSep + 1);
normalized = beforeSep + '.' + afterSep;
}
try {
return new Decimal(normalized);
} catch (e) {
return new Decimal(0);
}
return new Decimal(input.replace(/,/g, '.'));
}
/**