fix: IMAP-Konto bearbeiten + Löschen auch bei sync_running
- Store: UpdateCredentials() — Zugangsdaten + Passwort neu verschlüsseln,
setzt status='idle', error_msg='', sync_running=false zurück
- Handler: PATCH /api/imap/{id} unterstützt nun Credential-Update
(name/host/username vorhanden = Credential-Update, sonst sync_interval)
- Frontend: "Bearbeiten"-Button öffnet Edit-Dialog mit allen Feldern;
Passwort-Feld leer = unverändertes Passwort
- Frontend: Löschen-Button nicht mehr durch sync_running blockiert
(nur noch bei status=running gesperrt)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+108
-1
@@ -12,6 +12,7 @@ import {
|
||||
getImapProgress,
|
||||
triggerImapSync,
|
||||
updateImapInterval,
|
||||
updateImapAccount,
|
||||
type ImapAccount,
|
||||
type ImapFolder,
|
||||
} from "@/lib/api";
|
||||
@@ -50,6 +51,17 @@ export default function ImapPage() {
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [deleteConfirm, setDeleteConfirm] = useState<number | null>(null);
|
||||
|
||||
// Edit state
|
||||
const [editAccount, setEditAccount] = useState<ImapAccount | null>(null);
|
||||
const [editName, setEditName] = useState("");
|
||||
const [editHost, setEditHost] = useState("");
|
||||
const [editPort, setEditPort] = useState("993");
|
||||
const [editTls, setEditTls] = useState("ssl");
|
||||
const [editUsername, setEditUsername] = useState("");
|
||||
const [editPassword, setEditPassword] = useState("");
|
||||
const [editSaving, setEditSaving] = useState(false);
|
||||
const [editError, setEditError] = useState("");
|
||||
|
||||
// Form state
|
||||
const [formName, setFormName] = useState("");
|
||||
const [formHost, setFormHost] = useState("");
|
||||
@@ -209,6 +221,43 @@ export default function ImapPage() {
|
||||
setDeleteConfirm(null);
|
||||
}
|
||||
|
||||
function openEdit(acc: ImapAccount) {
|
||||
setEditAccount(acc);
|
||||
setEditName(acc.name);
|
||||
setEditHost(acc.host);
|
||||
setEditPort(String(acc.port));
|
||||
setEditTls(acc.tls);
|
||||
setEditUsername(acc.username);
|
||||
setEditPassword("");
|
||||
setEditError("");
|
||||
}
|
||||
|
||||
async function handleEditSave() {
|
||||
if (!editAccount) return;
|
||||
if (!editName || !editHost || !editUsername) {
|
||||
setEditError("Name, Host und Benutzername sind Pflichtfelder.");
|
||||
return;
|
||||
}
|
||||
setEditSaving(true);
|
||||
setEditError("");
|
||||
try {
|
||||
const updated = await updateImapAccount(editAccount.id, {
|
||||
name: editName,
|
||||
host: editHost,
|
||||
port: parseInt(editPort, 10),
|
||||
tls: editTls,
|
||||
username: editUsername,
|
||||
password: editPassword || undefined,
|
||||
});
|
||||
setAccounts((prev) => prev.map((a) => (a.id === updated.id ? updated : a)));
|
||||
setEditAccount(null);
|
||||
} catch (err) {
|
||||
setEditError(err instanceof Error ? err.message : String(err));
|
||||
} finally {
|
||||
setEditSaving(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSyncNow(id: number) {
|
||||
try {
|
||||
const updated = await triggerImapSync(id);
|
||||
@@ -407,10 +456,17 @@ export default function ImapPage() {
|
||||
>
|
||||
Sync jetzt
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => openEdit(acc)}
|
||||
>
|
||||
Bearbeiten
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="destructive"
|
||||
disabled={acc.status === "running" || acc.sync_running}
|
||||
disabled={acc.status === "running"}
|
||||
onClick={() => setDeleteConfirm(acc.id)}
|
||||
>
|
||||
Loeschen
|
||||
@@ -572,6 +628,57 @@ export default function ImapPage() {
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{/* Edit Account Dialog */}
|
||||
<Dialog open={editAccount !== null} onOpenChange={(open) => { if (!open) setEditAccount(null); }}>
|
||||
<DialogContent className="max-w-lg">
|
||||
<DialogHeader>
|
||||
<DialogTitle>IMAP-Konto bearbeiten</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-1">
|
||||
<Label>Name</Label>
|
||||
<Input value={editName} onChange={(e) => setEditName(e.target.value)} placeholder="z.B. Firmen-Mail" />
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="space-y-1">
|
||||
<Label>Host</Label>
|
||||
<Input value={editHost} onChange={(e) => setEditHost(e.target.value)} placeholder="imap.example.com" />
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<Label>Port</Label>
|
||||
<Input value={editPort} onChange={(e) => setEditPort(e.target.value)} type="number" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<Label>TLS</Label>
|
||||
<Select value={editTls} onValueChange={setEditTls}>
|
||||
<SelectTrigger><SelectValue /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="ssl">SSL/TLS</SelectItem>
|
||||
<SelectItem value="starttls">STARTTLS</SelectItem>
|
||||
<SelectItem value="none">Keine</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<Label>Benutzername</Label>
|
||||
<Input value={editUsername} onChange={(e) => setEditUsername(e.target.value)} placeholder="user@example.com" />
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<Label>Passwort <span className="text-muted-foreground text-xs">(leer lassen = unveraendert)</span></Label>
|
||||
<Input value={editPassword} onChange={(e) => setEditPassword(e.target.value)} type="password" placeholder="Neues Passwort eingeben" />
|
||||
</div>
|
||||
{editError && <p className="text-sm text-destructive">{editError}</p>}
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setEditAccount(null)}>Abbrechen</Button>
|
||||
<Button onClick={handleEditSave} disabled={editSaving}>
|
||||
{editSaving ? "Speichert..." : "Speichern"}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{/* Delete Confirmation Dialog */}
|
||||
<Dialog
|
||||
open={deleteConfirm !== null}
|
||||
|
||||
@@ -392,6 +392,16 @@ export async function updateImapInterval(id: number, intervalMin: number): Promi
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateImapAccount(
|
||||
id: number,
|
||||
data: { name: string; host: string; port: number; tls: string; username: string; password?: string }
|
||||
): Promise<ImapAccount> {
|
||||
return request<ImapAccount>(`/api/imap/${id}`, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
}
|
||||
|
||||
// ── POP3 ──────────────────────────────────────────────────────────────────
|
||||
|
||||
export interface Pop3Account {
|
||||
|
||||
Reference in New Issue
Block a user