feat: add mobile-responsive layout and card views across all pages
Build and Push Multi-Platform Images / build-and-push (push) Successful in 39s
Build and Push Multi-Platform Images / build-and-push (push) Successful in 39s
- New useIsMobile hook and MobileCardList component - AppLayout: hamburger menu + Drawer navigation on mobile - All list pages (Inbox, Posteingang, Manuell, Mail, Freigabe, Zahlung, TaskLog) show card layout on mobile instead of tables - CSS: responsive modal height, horizontal table scroll, text-size-adjust - Backend: Agrarmonitor polling fixes, Zahlung service improvements, IMAP folder service extended, misc controller fixes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -25,8 +25,14 @@ export class ImapFolderService {
|
||||
@Cron('0 3 * * *', { timeZone: 'Europe/Berlin' })
|
||||
async cleanupImportedEmails(): Promise<void> {
|
||||
if (!this.configService.get<string>('IMAP_HOST')) return;
|
||||
const importedFolder = this.configService.get<string>('IMAP_IMPORTED_FOLDER', 'importiert');
|
||||
const trashFolder = this.configService.get<string>('IMAP_TRASH_FOLDER', 'Trash');
|
||||
const importedFolder = this.configService.get<string>(
|
||||
'IMAP_IMPORTED_FOLDER',
|
||||
'importiert',
|
||||
);
|
||||
const trashFolder = this.configService.get<string>(
|
||||
'IMAP_TRASH_FOLDER',
|
||||
'Trash',
|
||||
);
|
||||
const client = this.createClient();
|
||||
try {
|
||||
await client.connect();
|
||||
@@ -39,10 +45,14 @@ export class ImapFolderService {
|
||||
const oldUids = await client.search({ before: cutoff }, { uid: true });
|
||||
if (Array.isArray(oldUids) && oldUids.length > 0) {
|
||||
await client.messageMove(oldUids, trashFolder, { uid: true });
|
||||
this.logger.log(`${oldUids.length} alte E-Mail(s) aus "${importedFolder}" in "${trashFolder}" verschoben.`);
|
||||
this.logger.log(
|
||||
`${oldUids.length} alte E-Mail(s) aus "${importedFolder}" in "${trashFolder}" verschoben.`,
|
||||
);
|
||||
}
|
||||
} catch (err: any) {
|
||||
this.logger.warn(`Bereinigung "${importedFolder}" nicht möglich: ${err.message}`);
|
||||
this.logger.warn(
|
||||
`Bereinigung "${importedFolder}" nicht möglich: ${err.message}`,
|
||||
);
|
||||
}
|
||||
|
||||
// Papierkorb leeren
|
||||
@@ -51,10 +61,14 @@ export class ImapFolderService {
|
||||
const trashUids = await client.search({ all: true }, { uid: true });
|
||||
if (Array.isArray(trashUids) && trashUids.length > 0) {
|
||||
await client.messageDelete(trashUids, { uid: true });
|
||||
this.logger.log(`${trashUids.length} E-Mail(s) aus "${trashFolder}" gelöscht.`);
|
||||
this.logger.log(
|
||||
`${trashUids.length} E-Mail(s) aus "${trashFolder}" gelöscht.`,
|
||||
);
|
||||
}
|
||||
} catch (err: any) {
|
||||
this.logger.warn(`Papierkorb "${trashFolder}" konnte nicht geleert werden: ${err.message}`);
|
||||
this.logger.warn(
|
||||
`Papierkorb "${trashFolder}" konnte nicht geleert werden: ${err.message}`,
|
||||
);
|
||||
}
|
||||
} catch (err: any) {
|
||||
this.logger.error(`IMAP-Cleanup fehlgeschlagen: ${err.message}`);
|
||||
@@ -64,9 +78,13 @@ export class ImapFolderService {
|
||||
}
|
||||
|
||||
async moveProcessedInboxToImportiert(messageIds: string[]): Promise<number> {
|
||||
if (!this.configService.get<string>('IMAP_HOST') || messageIds.length === 0) return 0;
|
||||
if (!this.configService.get<string>('IMAP_HOST') || messageIds.length === 0)
|
||||
return 0;
|
||||
|
||||
const importedFolder = this.configService.get<string>('IMAP_IMPORTED_FOLDER', 'importiert');
|
||||
const importedFolder = this.configService.get<string>(
|
||||
'IMAP_IMPORTED_FOLDER',
|
||||
'importiert',
|
||||
);
|
||||
const client = this.createClient();
|
||||
let movedCount = 0;
|
||||
|
||||
@@ -74,7 +92,7 @@ export class ImapFolderService {
|
||||
await client.connect();
|
||||
|
||||
const mailboxes = await client.list();
|
||||
if (!mailboxes.some(m => m.path === importedFolder)) {
|
||||
if (!mailboxes.some((m) => m.path === importedFolder)) {
|
||||
await client.mailboxCreate(importedFolder);
|
||||
this.logger.log(`IMAP-Ordner "${importedFolder}" erstellt.`);
|
||||
}
|
||||
@@ -86,7 +104,10 @@ export class ImapFolderService {
|
||||
const idSet = new Set(messageIds.map(normalize));
|
||||
const uidsToMove: number[] = [];
|
||||
|
||||
for await (const msg of client.fetch('1:*', { uid: true, envelope: true })) {
|
||||
for await (const msg of client.fetch('1:*', {
|
||||
uid: true,
|
||||
envelope: true,
|
||||
})) {
|
||||
const msgId = msg.envelope?.messageId;
|
||||
if (msgId && idSet.has(normalize(msgId))) {
|
||||
uidsToMove.push(msg.uid);
|
||||
@@ -96,10 +117,14 @@ export class ImapFolderService {
|
||||
if (uidsToMove.length > 0) {
|
||||
await client.messageMove(uidsToMove, importedFolder, { uid: true });
|
||||
movedCount = uidsToMove.length;
|
||||
this.logger.log(`${movedCount} E-Mail(s) aus INBOX → "${importedFolder}" verschoben.`);
|
||||
this.logger.log(
|
||||
`${movedCount} E-Mail(s) aus INBOX → "${importedFolder}" verschoben.`,
|
||||
);
|
||||
}
|
||||
} catch (err: any) {
|
||||
this.logger.error(`moveProcessedInboxToImportiert fehlgeschlagen: ${err.message}`);
|
||||
this.logger.error(
|
||||
`moveProcessedInboxToImportiert fehlgeschlagen: ${err.message}`,
|
||||
);
|
||||
} finally {
|
||||
await client.logout().catch(() => {});
|
||||
}
|
||||
@@ -110,24 +135,34 @@ export class ImapFolderService {
|
||||
async moveToImportiert(messageId: string): Promise<void> {
|
||||
if (!this.configService.get<string>('IMAP_HOST')) return;
|
||||
|
||||
const importedFolder = this.configService.get<string>('IMAP_IMPORTED_FOLDER', 'importiert');
|
||||
const importedFolder = this.configService.get<string>(
|
||||
'IMAP_IMPORTED_FOLDER',
|
||||
'importiert',
|
||||
);
|
||||
const client = this.createClient();
|
||||
try {
|
||||
await client.connect();
|
||||
|
||||
const mailboxes = await client.list();
|
||||
if (!mailboxes.some(m => m.path === importedFolder)) {
|
||||
if (!mailboxes.some((m) => m.path === importedFolder)) {
|
||||
await client.mailboxCreate(importedFolder);
|
||||
this.logger.log(`IMAP-Ordner "${importedFolder}" erstellt.`);
|
||||
}
|
||||
|
||||
await client.mailboxOpen('INBOX');
|
||||
const uids = await client.search({ header: { 'message-id': messageId } }, { uid: true });
|
||||
const uids = await client.search(
|
||||
{ header: { 'message-id': messageId } },
|
||||
{ uid: true },
|
||||
);
|
||||
if (Array.isArray(uids) && uids.length > 0) {
|
||||
await client.messageMove(uids, importedFolder, { uid: true });
|
||||
this.logger.log(`E-Mail ${messageId} → "${importedFolder}" verschoben.`);
|
||||
this.logger.log(
|
||||
`E-Mail ${messageId} → "${importedFolder}" verschoben.`,
|
||||
);
|
||||
} else {
|
||||
this.logger.warn(`E-Mail ${messageId} nicht in INBOX gefunden (bereits verschoben?).`);
|
||||
this.logger.warn(
|
||||
`E-Mail ${messageId} nicht in INBOX gefunden (bereits verschoben?).`,
|
||||
);
|
||||
}
|
||||
} catch (err: any) {
|
||||
this.logger.error(`IMAP moveToImportiert fehlgeschlagen: ${err.message}`);
|
||||
|
||||
Reference in New Issue
Block a user