feat: add mobile-responsive layout and card views across all pages
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:
2026-07-14 09:22:58 +02:00
parent 0765d14d3b
commit 7076eef57b
28 changed files with 1081 additions and 462 deletions
@@ -20,7 +20,11 @@ export class ZahlungService {
private readonly paperlessService: PaperlessService,
) {}
async getZahlungDocuments(page: number, pageSize: number, filter: ZahlungFilter) {
async getZahlungDocuments(
page: number,
pageSize: number,
filter: ZahlungFilter,
) {
const docTypes = await this.documentTypeRepo.find({
where: { FreigabeErforderlich: true as any },
});
@@ -44,7 +48,10 @@ export class ZahlungService {
const result = await this.paperlessService.getDocuments(params);
allDocs = result.results ?? [];
} catch (err: any) {
this.logger.warn('Fehler beim Laden der Belege für Zahlung', err?.message);
this.logger.warn(
'Fehler beim Laden der Belege für Zahlung',
err?.message,
);
return { count: 0, results: [] };
}
@@ -73,9 +80,12 @@ export class ZahlungService {
private getCfValue(doc: any, fieldId: number): string | null {
const cf = (doc.custom_fields ?? []).find((f: any) => f.field === fieldId);
if (!cf || cf.value === null || cf.value === undefined || cf.value === '') return null;
if (!cf || cf.value === null || cf.value === undefined || cf.value === '')
return null;
if (typeof cf.value === 'object') {
return String(cf.value?.id ?? cf.value?.value ?? cf.value?.label ?? '') || null;
return (
String(cf.value?.id ?? cf.value?.value ?? cf.value?.label ?? '') || null
);
}
return String(cf.value);
}
@@ -91,20 +101,24 @@ export class ZahlungService {
}
const customFields: any[] = [...(doc.custom_fields ?? [])];
const existing = customFields.find((f: any) => f.field === ZAHLUNG_FIELD_ID);
const existing = customFields.find(
(f: any) => f.field === ZAHLUNG_FIELD_ID,
);
if (existing) {
existing.value = value;
} else if (value !== null && value !== '') {
customFields.push({ field: ZAHLUNG_FIELD_ID, value });
}
await this.paperlessService.updateDocument(documentId, { custom_fields: customFields });
await this.paperlessService.updateDocument(documentId, {
custom_fields: customFields,
});
return { success: true };
}
async getZahlungOptions(): Promise<{ id: string; label: string }[]> {
const fields = await this.paperlessService.getCustomFields();
const field = (fields as any[]).find((f: any) => f.id === ZAHLUNG_FIELD_ID);
const field = fields.find((f: any) => f.id === ZAHLUNG_FIELD_ID);
if (!field) return [];
const rawOptions: any[] = field.extra_data?.select_options ?? [];