feat: Postfach zeigt Takt und Abrufzeitpunkt in der Uebersicht

Der Takt des automatischen Abrufs gab es laengst – aber nur im
Bearbeiten-Formular. In der Uebersicht war deshalb nicht zu sehen, ob
ueberhaupt automatisch abgerufen wird, und es sah aus, als gaebe es die
Einstellung nicht. Bei der Bankanbindung steht sie direkt in der Karte; jetzt
auch hier: "alle 15 Minuten" oder "nur auf Knopfdruck".

Und "zuletzt abgerufen" stand ohne Zeitpunkt da, sagte also nichts. Jetzt mit
Datum und Uhrzeit – die Uhrzeit ist noetig, weil der Abruf meist am selben Tag
lief.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Guido
2026-08-27 13:06:00 +02:00
co-authored by Claude Opus 5
parent 60b8178c57
commit 1084cf7c17
4 changed files with 31 additions and 5 deletions
+11
View File
@@ -32,6 +32,17 @@ export function formatDate(iso, locale = "de") {
return new Intl.DateTimeFormat(locale === "en" ? "en-GB" : "de-DE").format(d); return new Intl.DateTimeFormat(locale === "en" ? "en-GB" : "de-DE").format(d);
} }
/** Datum MIT Uhrzeit – für Zeitpunkte, bei denen der Tag allein nichts sagt
* ("zuletzt abgerufen" am selben Tag). */
export function formatDateTime(iso, locale = "de") {
if (!iso) return "";
const d = new Date(iso);
if (Number.isNaN(d.getTime())) return String(iso);
return new Intl.DateTimeFormat(locale === "en" ? "en-GB" : "de-DE", {
dateStyle: "short", timeStyle: "short",
}).format(d);
}
/** Eingabetext ("12,34" oder "12.34") in Ganzzahl-Cent; null bei leer/ungültig. */ /** Eingabetext ("12,34" oder "12.34") in Ganzzahl-Cent; null bei leer/ungültig. */
export function parseAmountToCents(str) { export function parseAmountToCents(str) {
if (str == null) return null; if (str == null) return null;
+4 -1
View File
@@ -573,7 +573,10 @@
"deleteSender": "Zuordnung entfernen", "deleteSender": "Zuordnung entfernen",
"deleteSenderConfirm": "Zuordnung „{{text}}“ entfernen? Bereits erfasste Zahlungen bleiben.", "deleteSenderConfirm": "Zuordnung „{{text}}“ entfernen? Bereits erfasste Zahlungen bleiben.",
"anyCurrency": "jede Währung", "anyCurrency": "jede Währung",
"fetchOlder": "Älteres nachholen" "fetchOlder": "Älteres nachholen",
"every": "alle {{n}} Minuten",
"manualOnly": "nur auf Knopfdruck",
"lastPollAt": "zuletzt abgerufen: {{when}}"
}, },
"bank": { "bank": {
"title": "Bankkonten Anbindung", "title": "Bankkonten Anbindung",
+4 -1
View File
@@ -573,7 +573,10 @@
"deleteSender": "Remove mapping", "deleteSender": "Remove mapping",
"deleteSenderConfirm": "Remove mapping “{{text}}”? Payments already captured stay.", "deleteSenderConfirm": "Remove mapping “{{text}}”? Payments already captured stay.",
"anyCurrency": "any currency", "anyCurrency": "any currency",
"fetchOlder": "Catch up on older" "fetchOlder": "Catch up on older",
"every": "every {{n}} minutes",
"manualOnly": "only on demand",
"lastPollAt": "last fetched: {{when}}"
}, },
"bank": { "bank": {
"title": "Bank account connection", "title": "Bank account connection",
+12 -3
View File
@@ -1,7 +1,7 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { api } from "../../api"; import { api } from "../../api";
import { formatDate } from "../../format"; import { formatDate, formatDateTime } from "../../format";
import { useToast } from "../../toast"; import { useToast } from "../../toast";
import { useConfirm } from "../../confirm"; import { useConfirm } from "../../confirm";
import Icon from "../../components/Icon"; import Icon from "../../components/Icon";
@@ -145,11 +145,20 @@ export default function Postfaecher() {
{quellen.map((q) => ( {quellen.map((q) => (
<div key={q.id} className="mail-source"> <div key={q.id} className="mail-source">
<div><strong>{q.name || q.host}</strong></div> <div><strong>{q.name || q.host}</strong></div>
<div className="muted small">{q.username} · {q.folder} · {q.security.toUpperCase()}</div> <div className="muted small">
{q.username} · {q.folder} · {q.security.toUpperCase()} ·{" "}
{/* Der Takt stand nur im Bearbeiten-Formular. Ob überhaupt
automatisch abgerufen wird, gehört in die Übersicht. */}
{q.is_active && q.poll_minutes
? t("mailbox.every", { n: q.poll_minutes })
: t("mailbox.manualOnly")}
</div>
<div className="muted small"> <div className="muted small">
{q.last_error {q.last_error
? `${t("mailbox.lastError")}: ${q.last_error}` ? `${t("mailbox.lastError")}: ${q.last_error}`
: q.last_poll_at ? t("mailbox.lastPoll") : t("mailbox.neverPolled")} : q.last_poll_at
? t("mailbox.lastPollAt", { when: formatDateTime(q.last_poll_at) })
: t("mailbox.neverPolled")}
</div> </div>
<div style={{ marginTop: "var(--sp-2)" }}> <div style={{ marginTop: "var(--sp-2)" }}>
{(absender[q.id] || []).length === 0 ? ( {(absender[q.id] || []).length === 0 ? (