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
@@ -1,6 +1,6 @@
import { useEffect, useState, useCallback } from 'react';
import { Modal, Form, Select, DatePicker, Input, Spin, message, Row, Col, Button, Space, Divider, Tag } from 'antd';
import { PlusOutlined, EyeOutlined, SearchOutlined } from '@ant-design/icons';
import { PlusOutlined, EyeOutlined, SearchOutlined, ExportOutlined } from '@ant-design/icons';
import dayjs from 'dayjs';
import { posteingangApi } from '../api/posteingang';
import type { DocumentRequirement, PosteingangDocument, Kontonummer } from '../api/posteingang';
@@ -11,6 +11,7 @@ import type { PaperlessDocType, PaperlessCorrespondent, PaperlessTag } from '../
import { getEnv } from '../utils/env';
import { AuthIframe, openAuthUrl } from '../utils/auth-resource';
import DocumentSearchModal from './DocumentSearchModal';
import { useIsMobile } from '../hooks/useIsMobile';
const { Option } = Select;
@@ -26,6 +27,7 @@ interface Props {
export default function DocumentEditModal({ documentId, document, open, onClose, onSave, isPosteingang = true, hasNextDocument = true }: Props) {
const [form] = Form.useForm();
const isMobile = useIsMobile();
const [loading, setLoading] = useState(false);
const [saving, setSaving] = useState(false);
@@ -349,8 +351,8 @@ export default function DocumentEditModal({ documentId, document, open, onClose,
title={`Dokument bearbeiten (${document?.title || ''})`}
open={open && !kontonummerMissing}
onCancel={() => onClose(false)}
width={1400}
style={{ top: 20 }}
width={{ xs: '100vw', md: 900, xl: 1400 }}
style={isMobile ? {} : { top: 20 }}
footer={
hasNextDocument ? [
<Button key="cancel" onClick={() => onClose(false)}>Abbrechen</Button>,
@@ -363,8 +365,12 @@ export default function DocumentEditModal({ documentId, document, open, onClose,
}
>
<Spin spinning={loading}>
<Row gutter={16} style={{ height: '75vh', overflow: 'hidden' }}>
<Col span={10} style={{ overflowY: 'auto', paddingRight: '1rem', borderRight: '1px solid #f0f0f0' }}>
<Row gutter={[16, 16]} style={isMobile ? {} : { height: '75vh', overflow: 'hidden' }}>
<Col
xs={24}
lg={10}
style={isMobile ? {} : { overflowY: 'auto', paddingRight: '1rem', borderRight: '1px solid #f0f0f0' }}
>
<Form form={form} layout="vertical" disabled={saving}>
<Form.Item name="mandant" label="Mandant" rules={[{ required: true, message: 'Wähle einen Mandanten' }]}>
@@ -556,10 +562,20 @@ export default function DocumentEditModal({ documentId, document, open, onClose,
})}
</Form>
</Col>
<Col span={14} style={{ height: '100%' }}>
<Col xs={24} lg={14} style={isMobile ? {} : { height: '100%' }}>
{isMobile && (
<Button
icon={<ExportOutlined />}
block
style={{ marginBottom: 8 }}
onClick={() => openAuthUrl(`${getEnv('VITE_API_URL')}/api/paperless/inbox/pdf/${documentId}`)}
>
PDF in neuem Tab öffnen
</Button>
)}
<AuthIframe
src={`${getEnv('VITE_API_URL')}/api/paperless/inbox/pdf/${documentId}#toolbar=0`}
style={{ width: '100%', height: '100%' }}
style={{ width: '100%', height: isMobile ? '50vh' : '100%' }}
title="PDF Preview"
/>
</Col>