diff --git a/build-and-push-gitea.sh b/build-and-push-gitea.sh index ee33b30..72d1e0e 100755 --- a/build-and-push-gitea.sh +++ b/build-and-push-gitea.sh @@ -1,49 +1,73 @@ #!/usr/bin/env bash set -Eeuo pipefail -ROOT="$(cd "$(dirname "$0")" && pwd)" +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REGISTRY="${GITEA_REGISTRY:-${1:-}}" IMAGE_PATH="${GITEA_IMAGE_PATH:-${2:-}}" TAG="${IMAGE_TAG:-${3:-latest}}" +SECOND_TAG="${4:-}" PLATFORM="${PLATFORM:-linux/amd64}" -if [[ -z "$REGISTRY" || -z "$IMAGE_PATH" ]]; then - cat < [tag] + ./build-and-push-gitea.sh [tag] [zweiter-tag] Beispiel: - $0 git.example.de meinuser/fixfin latest + ./build-and-push-gitea.sh git.d-razz.de michael/fixfin V3.3 latest -Alternativ per Umgebungsvariablen: - GITEA_REGISTRY=git.example.de \\ - GITEA_IMAGE_PATH=meinuser/fixfin \\ - IMAGE_TAG=latest \\ - $0 - -Vor dem ersten Push anmelden: - sudo docker login -USAGE +Einmalige Registry-Anmeldung: + docker login git.d-razz.de -u michael +EOF exit 1 fi -command -v docker >/dev/null 2>&1 || { echo "Docker fehlt."; exit 1; } -docker buildx version >/dev/null 2>&1 || { echo "Docker Buildx fehlt."; exit 1; } +command -v docker >/dev/null 2>&1 || { + echo "FEHLER: Docker fehlt." + exit 1 +} + +docker info >/dev/null 2>&1 || { + echo "FEHLER: Docker ist für den aktuellen Benutzer nicht erreichbar." + echo "Teste: docker ps" + exit 1 +} + +docker buildx version >/dev/null 2>&1 || { + echo "FEHLER: Docker Buildx fehlt." + exit 1 +} FULL_IMAGE="${REGISTRY}/${IMAGE_PATH}:${TAG}" +BUILD_ARGS=( + --platform "${PLATFORM}" + --tag "${FULL_IMAGE}" +) + +if [[ -n "${SECOND_TAG}" ]]; then + BUILD_ARGS+=(--tag "${REGISTRY}/${IMAGE_PATH}:${SECOND_TAG}") +fi + +BUILD_ARGS+=(--push .) + +echo echo ">>> Build + Push" echo "Image: ${FULL_IMAGE}" +if [[ -n "${SECOND_TAG}" ]]; then + echo "Zusatz: ${REGISTRY}/${IMAGE_PATH}:${SECOND_TAG}" +fi echo "Platform: ${PLATFORM}" echo -cd "$ROOT" -docker buildx build \ - --platform "$PLATFORM" \ - --tag "$FULL_IMAGE" \ - --push \ - . +cd "${ROOT}" + +docker buildx build "${BUILD_ARGS[@]}" echo -echo "Fertig: ${FULL_IMAGE}" +echo "Fertig:" +echo " ${FULL_IMAGE}" +if [[ -n "${SECOND_TAG}" ]]; then + echo " ${REGISTRY}/${IMAGE_PATH}:${SECOND_TAG}" +fi diff --git a/package.json b/package.json index 3cfb96b..6bb8c52 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fixfin", - "version": "1.3.0", + "version": "1.3.1", "private": true, "type": "module", "scripts": { diff --git a/release.sh b/release.sh index c6fb573..ebab148 100755 --- a/release.sh +++ b/release.sh @@ -11,22 +11,23 @@ Verwendung: Beispiele: ./release.sh 3.3 + ./release.sh v3.3 ./release.sh V3.3 ./release.sh 3.3.1 Ablauf: 1. git add -A 2. git commit -m "V" - 3. git push über origin/SSH - 4. build-and-push-gitea.sh - 5. Docker-Image mit Versionstag pushen - 6. denselben Stand zusätzlich als :latest markieren + 3. git push origin + 4. Docker-Build + Push nach Gitea + 5. Versionstag + latest Voraussetzungen: - - origin zeigt per SSH auf Gitea, z.B. - git@git.d-razz.de:mboehmlaender/fixfin.git - - Docker-Registry einmalig für root angemeldet: - sudo docker login git.d-razz.de + - Git-Push funktioniert über deinen konfigurierten origin-Remote. + - Docker kann als aktueller Benutzer ausgeführt werden: + docker ps + - Einmalig bei der Gitea Registry anmelden: + docker login git.d-razz.de -u michael EOF } @@ -37,13 +38,12 @@ if [[ -z "${VERSION_RAW}" ]]; then exit 1 fi -# V/v am Anfang entfernen und anschließend einheitlich als Vx.x verwenden. VERSION="${VERSION_RAW#V}" VERSION="${VERSION#v}" if [[ ! "${VERSION}" =~ ^[0-9]+\.[0-9]+(\.[0-9]+)?$ ]]; then echo "FEHLER: Ungültige Version '${VERSION_RAW}'." - echo "Erwartet z.B. 3.3, V3.3 oder 3.3.1" + echo "Erwartet z.B. 3.3, v3.3, V3.3 oder 3.3.1" exit 1 fi @@ -55,6 +55,23 @@ command -v git >/dev/null 2>&1 || { exit 1 } +command -v docker >/dev/null 2>&1 || { + echo "FEHLER: docker wurde nicht gefunden." + exit 1 +} + +docker info >/dev/null 2>&1 || { + echo "FEHLER: Docker ist für den aktuellen Benutzer nicht erreichbar." + echo + echo "Teste:" + echo " docker ps" + echo + echo "Falls Permission denied:" + echo " sudo usermod -aG docker $USER" + echo " newgrp docker" + exit 1 +} + [[ -x "${BUILD_SCRIPT}" ]] || { echo "FEHLER: ${BUILD_SCRIPT} fehlt oder ist nicht ausführbar." echo "Fix:" @@ -76,30 +93,30 @@ if [[ -z "${REMOTE}" ]]; then exit 1 fi -# Registry-Host und owner/repo automatisch aus einem SSH-Remote ableiten. -# Unterstützt: -# git@host:owner/repo.git -# ssh://git@host/owner/repo.git +# owner/repo + Registry aus SSH- oder HTTPS-Remote ableiten. +# Unterstützt z.B.: +# git@git.d-razz.de:michael/fixfin.git +# ssh://git@git.d-razz.de/michael/fixfin.git +# https://git.d-razz.de/michael/fixfin.git if [[ "${REMOTE}" =~ ^git@([^:]+):(.+)$ ]]; then REGISTRY="${BASH_REMATCH[1]}" IMAGE_PATH="${BASH_REMATCH[2]}" elif [[ "${REMOTE}" =~ ^ssh://git@([^/]+)/(.+)$ ]]; then REGISTRY="${BASH_REMATCH[1]}" IMAGE_PATH="${BASH_REMATCH[2]}" +elif [[ "${REMOTE}" =~ ^https?://([^/]+)/(.+)$ ]]; then + REGISTRY="${BASH_REMATCH[1]}" + IMAGE_PATH="${BASH_REMATCH[2]}" else - echo "FEHLER: origin ist kein unterstützter SSH-Remote:" + echo "FEHLER: origin konnte nicht ausgewertet werden:" echo " ${REMOTE}" - echo - echo "Erwartet z.B.:" - echo " git@git.d-razz.de:mboehmlaender/fixfin.git" exit 1 fi IMAGE_PATH="${IMAGE_PATH%.git}" -# Sicherheitsprüfung if [[ ! "${IMAGE_PATH}" =~ ^[^/]+/[^/]+$ ]]; then - echo "FEHLER: owner/repo konnte aus dem Remote nicht sicher ermittelt werden:" + echo "FEHLER: owner/repo konnte nicht sicher ermittelt werden:" echo " ${IMAGE_PATH}" exit 1 fi @@ -120,10 +137,10 @@ echo "Repository : ${REMOTE}" echo "Branch : ${BRANCH}" echo "Registry : ${REGISTRY}" echo "Image : ${REGISTRY}/${IMAGE_PATH}" -echo "Tag : ${IMAGE_TAG} + latest" +echo "Tags : ${IMAGE_TAG}, latest" echo -echo ">>> Prüfe SSH-Zugriff auf origin ..." +echo ">>> Prüfe Git-Remote ..." git ls-remote origin HEAD >/dev/null echo ">>> Stage Änderungen ..." @@ -139,28 +156,16 @@ fi echo ">>> Commit ${COMMIT_MESSAGE} ..." git commit -m "${COMMIT_MESSAGE}" -echo ">>> Git Push über SSH ..." +echo ">>> Git Push ..." git push origin "${BRANCH}" echo -echo ">>> Docker Registry Authentifizierung prüfen ..." -# sudo wird hier einmal initialisiert. Docker-Registry-Credentials liegen -# danach dauerhaft in der Docker-Konfiguration des root-Benutzers, sofern -# zuvor 'sudo docker login ' ausgeführt wurde. -sudo -v - -echo -echo ">>> Build + Push ${IMAGE_TAG} ..." -sudo "${BUILD_SCRIPT}" \ +echo ">>> Docker Build + Push ..." +"${BUILD_SCRIPT}" \ "${REGISTRY}" \ "${IMAGE_PATH}" \ - "${IMAGE_TAG}" - -echo -echo ">>> Markiere denselben Image-Stand zusätzlich als latest ..." -sudo docker buildx imagetools create \ - --tag "${REGISTRY}/${IMAGE_PATH}:latest" \ - "${REGISTRY}/${IMAGE_PATH}:${IMAGE_TAG}" + "${IMAGE_TAG}" \ + "latest" echo echo "============================================================" @@ -173,4 +178,3 @@ echo echo "Docker:" echo " ${REGISTRY}/${IMAGE_PATH}:${IMAGE_TAG}" echo " ${REGISTRY}/${IMAGE_PATH}:latest" -echo diff --git a/src/main.jsx b/src/main.jsx index ce54d96..5ebb0f5 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -129,7 +129,7 @@ function Dashboard({totals,onToggle}){
Periodische Kosten in Monats-Saldo einrechnenJährliche, halbjährliche und vierteljährliche Beträge werden auf einen Monatswert heruntergerechnet.
=0} negative={totals.balance<0} featured/>
{!totals.includePeriodic&&totals.periodicExpenseReserve>0&&
Periodische Ausgaben von rechnerisch {formatMoney(totals.periodicExpenseReserve)} / Monat sind derzeit nicht im Saldo enthalten.
} -

Saldo je Konto

Monatliche Fixwerte plus optional heruntergerechnete periodische Werte.

{totals.accounts.length===0?:
{totals.accounts.map(s=>

{s.account.name}

monatlicher Fix-Saldo
=0?'good-text':'bad-text'}`}>{s.balance>=0?'+':''}{formatMoney(s.balance)}
Eingänge+{formatMoney(s.income)}
Ausgänge−{formatMoney(s.expense)}
Transfers rein+{formatMoney(s.transferIn)}
Transfers raus−{formatMoney(s.transferOut)}
)}
}
+

Saldo je Konto

Monatliche Fixwerte plus optional heruntergerechnete periodische Werte.

{totals.accounts.length===0?:
{totals.accounts.map(s=>

{s.account.name}

monatlicher Fix-Saldo
=0?'good-text':'bad-text'}`}>{s.balance>=0?'+':''}{formatMoney(s.balance)}
Eingänge+{formatMoney(s.income)}
Ausgänge−{formatMoney(s.expense)}
Transfers rein+{formatMoney(s.transferIn)}
Transfers raus−{formatMoney(s.transferOut)}
)}
}
GESAMT

Monatlich nach Fixkosten übrig

Interne Transfers verändern den Gesamtsaldo nicht.

=0?'good-text':'bad-text'}`}>{formatMoney(totals.balance)}
; } diff --git a/src/styles.css b/src/styles.css index 223eb0f..4baf5da 100644 --- a/src/styles.css +++ b/src/styles.css @@ -187,7 +187,7 @@ code { color:#b8c3ff; } .year-month-list { gap:9px; } .year-month-card summary { grid-template-columns:1fr 22px; gap:9px 10px; padding:13px 12px; } .year-month-heading { grid-column:1; } - .accordion-chevron { grid-column:2; grid-row:1; align-self:center; } + .year-month-card .accordion-chevron { grid-column:2; grid-row:1; align-self:center; } .year-month-basics { grid-column:1 / -1; grid-row:2; gap:6px; } .year-month-basics div { background:#101823; border:1px solid #202c3c; border-radius:9px; padding:8px; } .year-month-basics strong { font-size:12px; } @@ -213,7 +213,7 @@ code { color:#b8c3ff; } .mobile-nav { grid-template-columns:repeat(7,minmax(50px,1fr)); } } -/* v3.2: kompakte Kontosalden als Akkordeon */ +/* v3.4: robuste Konto-Saldo-Anzeige auf der Übersicht */ .account-balance-list { display:grid; gap:10px; @@ -231,16 +231,16 @@ code { color:#b8c3ff; } .account-balance-card summary { list-style:none; cursor:pointer; - display:grid; - grid-template-columns:minmax(0,1fr) auto 22px; - gap:14px; + display:flex; + gap:12px; align-items:center; + justify-content:space-between; min-height:68px; padding:12px 15px; user-select:none; } .account-balance-card summary::-webkit-details-marker { display:none; } -.account-balance-title { min-width:0; } +.account-balance-title { min-width:0; overflow:hidden; flex:1 1 auto; } .account-balance-title h3 { margin:0; overflow:hidden; @@ -253,11 +253,21 @@ code { color:#b8c3ff; } color:var(--muted); font-size:11px; } +.account-balance-right { + flex:0 0 auto; + display:flex; + align-items:center; + justify-content:flex-end; + gap:8px; + min-width:max-content; +} .account-balance-value { + display:block; font-size:19px; font-weight:850; letter-spacing:-.035em; white-space:nowrap; + flex:0 0 auto; } .account-balance-card[open] .accordion-chevron { transform:rotate(180deg); } .account-balance-details { @@ -272,15 +282,15 @@ code { color:#b8c3ff; } @media (max-width:760px) { .account-balance-list { gap:8px; } .account-balance-card summary { - grid-template-columns:minmax(0,1fr) auto 18px; gap:8px; min-height:58px; padding:9px 10px; } .account-balance-title h3 { font-size:14px; } .account-balance-title span { font-size:9px; } - .account-balance-value { font-size:16px; } - .account-balance-card .accordion-chevron { font-size:17px; } + .account-balance-value { font-size:15px; letter-spacing:-.025em; } + .account-balance-right { gap:6px; } + .account-balance-card .accordion-chevron { font-size:17px; flex:0 0 auto; } .account-balance-details { padding:0 10px 10px; } .account-balance-details .mini-grid { gap:8px;