Refaktorisierungs-Optionen und Fix für Zahlen-Parsing
This commit is contained in:
163
REFACTOR.md
Normal file
163
REFACTOR.md
Normal 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.
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
services:
|
services:
|
||||||
web:
|
web:
|
||||||
build: .
|
build: .
|
||||||
image: umrechner:v1.0.6
|
image: umrechner:v1.0.7
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
- POCKETBASE_URL={$POCKETBASE_URL}
|
- POCKETBASE_URL={$POCKETBASE_URL}
|
||||||
|
|||||||
@@ -79,7 +79,7 @@
|
|||||||
<div class="text-center sm:pt-6">
|
<div class="text-center sm:pt-6">
|
||||||
<button type="button"
|
<button type="button"
|
||||||
@click="navigate(
|
@click="navigate(
|
||||||
$el.dataset.swapUrl, prettyNumber(convert(prepareValue(inputValue))))"
|
$el.dataset.swapUrl, prettyNumber(convert(parseNumber(inputValue))))"
|
||||||
data-swap-url="/{{ .Params.to }}-in-{{ .Params.from }}/"
|
data-swap-url="/{{ .Params.to }}-in-{{ .Params.from }}/"
|
||||||
class="inline-flex items-center text-sm
|
class="inline-flex items-center text-sm
|
||||||
font-medium text-primary
|
font-medium text-primary
|
||||||
@@ -145,7 +145,7 @@
|
|||||||
</label>
|
</label>
|
||||||
<input type="text" id="result"
|
<input type="text" id="result"
|
||||||
disabled
|
disabled
|
||||||
:value="prettyNumber(convert(prepareValue(inputValue)))"
|
:value="prettyNumber(convert(parseNumber(inputValue)))"
|
||||||
class="textinput mt-1 block w-full">
|
class="textinput mt-1 block w-full">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -44,32 +44,35 @@ function prettyNumber(num, minPrecision, maxPrecision) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a German number string.
|
* Parses a number string for conversion.
|
||||||
* Accepts both comma and dot as decimal separator.
|
* Accepts both German (1.234,56) and US (1,234.56) notation.
|
||||||
* @param {string} input
|
* The last dot or comma is always the decimal separator;
|
||||||
* @returns {string} Normalized string with dot as decimal
|
* 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) {
|
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() === '') {
|
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, '.'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user