feat: improve frontend accessibility, dark mode, and UX
Build and Push Multi-Platform Images / build-and-push (push) Successful in 18s

- Replace div with semantic button elements in AppLayout for accessibility
- Use Ant Design theme tokens instead of hardcoded colors in MailDetailPage
- Add loading state to refresh buttons and pagination info
- Add German empty state messages to tables
- Clean up unnecessary ConfigProvider wrappers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-21 14:45:33 +02:00
parent e9d42ce172
commit 7de22c9418
8 changed files with 43 additions and 31 deletions
+12 -5
View File
@@ -125,7 +125,7 @@ export default function AppLayout() {
}}
>
{/* Logo / Collapse-Toggle */}
<div
<button
onClick={() => setCollapsed(!collapsed)}
style={{
height: 56,
@@ -134,7 +134,11 @@ export default function AppLayout() {
justifyContent: 'center',
cursor: 'pointer',
userSelect: 'none',
border: 'none',
borderBottom: `1px solid ${isDark ? 'rgba(255,255,255,0.08)' : '#e2e4ea'}`,
background: 'transparent',
width: '100%',
padding: 0,
transition: 'background 0.2s',
}}
onMouseEnter={(e) => (e.currentTarget.style.background = isDark ? 'rgba(255,255,255,0.04)' : 'rgba(0,0,0,0.02)')}
@@ -143,7 +147,7 @@ export default function AppLayout() {
<Text strong style={{ color: logoColor, fontSize: collapsed ? 14 : 18, transition: 'font-size 0.2s' }}>
{collapsed ? 'PM' : 'Paperless'}
</Text>
</div>
</button>
{/* Navigation Menu */}
<Menu
@@ -179,7 +183,7 @@ export default function AppLayout() {
>
{/* Theme Toggle */}
<Tooltip title={isDark ? 'Light Mode' : 'Dark Mode'} placement="right">
<div
<button
onClick={toggleTheme}
style={{
display: 'flex',
@@ -190,14 +194,17 @@ export default function AppLayout() {
cursor: 'pointer',
color: subtleColor,
justifyContent: collapsed ? 'center' : 'flex-start',
transition: 'all 0.2s',
border: 'none',
background: 'transparent',
width: '100%',
transition: 'background 0.2s',
}}
onMouseEnter={(e) => (e.currentTarget.style.background = isDark ? 'rgba(255,255,255,0.08)' : '#eef1f8')}
onMouseLeave={(e) => (e.currentTarget.style.background = 'transparent')}
>
{isDark ? <SunOutlined style={{ fontSize: 16 }} /> : <MoonOutlined style={{ fontSize: 16 }} />}
{!collapsed && <Text style={{ color: subtleColor, fontSize: 13 }}>{isDark ? 'Light Mode' : 'Dark Mode'}</Text>}
</div>
</button>
</Tooltip>
{/* User Menu */}
@@ -1176,7 +1176,7 @@ export default function InboxDetailPage() {
<Button icon={<ArrowLeftOutlined />} onClick={() => navigate('/inbox')}>
Zurück
</Button>
<Title level={4} style={{ margin: 0 }}>
<Title level={3} style={{ margin: 0 }}>
{file.name}
</Title>
<SourceTag source={file.source} />
+1 -1
View File
@@ -387,7 +387,7 @@ export default function InboxPage() {
onChange={(e) => setSearch(e.target.value)}
allowClear
/>
<Button icon={<ReloadOutlined />} onClick={load}>
<Button icon={<ReloadOutlined />} onClick={load} loading={loading}>
Aktualisieren
</Button>
<Popconfirm
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import {
Card, Button, Space, Spin, Tag, Typography, Table, message, Empty, Popconfirm
Card, Button, Space, Spin, Tag, Typography, Table, message, Empty, Popconfirm, theme
} from 'antd';
import { ArrowLeftOutlined, FileTextOutlined, CloseCircleOutlined, LinkOutlined } from '@ant-design/icons';
import type { ColumnsType } from 'antd/es/table';
@@ -23,6 +23,7 @@ export default function MailDetailPage() {
const [loading, setLoading] = useState(true);
const [previewLoading, setPreviewLoading] = useState(false);
const [wizardOpen, setWizardOpen] = useState(false);
const { token } = theme.useToken();
useEffect(() => {
if (!id) return;
@@ -208,7 +209,7 @@ export default function MailDetailPage() {
{/* Rechte Seite: Anhänge + Vorschau */}
<Card size="small" styles={{ body: { padding: 0, display: 'flex', flexDirection: 'column', height: 'calc(100vh - 200px)' } }}>
<div style={{ flex: '0 0 auto', borderBottom: '1px solid #303030', maxHeight: 240, overflow: 'auto' }}>
<div style={{ flex: '0 0 auto', borderBottom: `1px solid ${token.colorBorder}`, maxHeight: 240, overflow: 'auto' }}>
<Table<EmailAttachment>
columns={columns}
dataSource={attachments}
@@ -223,7 +224,7 @@ export default function MailDetailPage() {
locale={{ emptyText: <Empty description="Keine Anhänge" image={Empty.PRESENTED_IMAGE_SIMPLE} /> }}
/>
</div>
<div style={{ flex: 1, minHeight: 0, background: '#1a1a2e' }}>
<div style={{ flex: 1, minHeight: 0, background: token.colorBgLayout }}>
{previewLoading ? (
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100%' }}>
<Spin />
@@ -240,7 +241,7 @@ export default function MailDetailPage() {
)
) : (
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100%', color: '#888' }}>
<Space vertical align="center">
<Space direction="vertical" align="center">
<FileTextOutlined style={{ fontSize: 48 }} />
<Text type="secondary">Kein Anhang ausgewählt</Text>
</Space>
@@ -219,6 +219,7 @@ export default function MailpostfachPage() {
loading={loading}
rowKey="Id"
pagination={{ pageSize: 20, showSizeChanger: true, showTotal: (t) => `${t} E-Mails` }}
locale={{ emptyText: 'Keine Einträge vorhanden' }}
onRow={(record) => ({
onClick: () => navigate(`/mailpostfach/${record.Id}`),
style: { cursor: 'pointer' },
@@ -1,5 +1,7 @@
import { useEffect, useState } from 'react';
import { Table, Popover, Button, Space, message, ConfigProvider, Tooltip } from 'antd';
import { Table, Popover, Button, Space, message, Tooltip, Typography } from 'antd';
const { Title } = Typography;
import { ReloadOutlined } from '@ant-design/icons';
import dayjs from 'dayjs';
import { posteingangApi } from '../api/posteingang';
@@ -97,7 +99,7 @@ export default function ManuellBearbeitenPage() {
render: (text: string) => dayjs(text).format('DD.MM.YYYY HH:mm'),
},
{
title: 'Aktion',
title: 'Aktionen',
key: 'action',
width: 150,
render: (_: any, record: PosteingangDocument) => (
@@ -109,10 +111,9 @@ export default function ManuellBearbeitenPage() {
];
return (
<ConfigProvider>
<div style={{ padding: '0 24px 24px' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
<h2>Manuell bearbeiten</h2>
<div>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
<Title level={3} style={{ margin: 0 }}>Manuell bearbeiten</Title>
<Space>
<Tooltip title="Manuell aktualisieren">
<Button icon={<ReloadOutlined />} onClick={fetchData} loading={loading} />
@@ -124,9 +125,10 @@ export default function ManuellBearbeitenPage() {
dataSource={data}
rowKey="id"
loading={loading}
pagination={{ pageSize: 20 }}
pagination={{ pageSize: 20, showSizeChanger: true, showTotal: (t) => `${t} Dokumente` }}
locale={{ emptyText: 'Keine Einträge vorhanden' }}
/>
<DocumentEditModal
documentId={selectedDoc?.id || null}
document={selectedDoc}
@@ -136,7 +138,6 @@ export default function ManuellBearbeitenPage() {
isPosteingang={false}
hasNextDocument={data.length > 1}
/>
</div>
</ConfigProvider>
</div>
);
}
@@ -1,7 +1,9 @@
import { useEffect, useState } from 'react';
import { Table, Popover, Button, Space, message, ConfigProvider, Tooltip } from 'antd';
import { Table, Popover, Button, Space, message, Tooltip, Typography } from 'antd';
import { AuthImage } from '../utils/auth-resource';
import { ReloadOutlined } from '@ant-design/icons';
const { Title } = Typography;
import dayjs from 'dayjs';
import { posteingangApi } from '../api/posteingang';
import type { PosteingangDocument } from '../api/posteingang';
@@ -98,7 +100,7 @@ export default function PosteingangPage() {
render: (text: string) => dayjs(text).format('DD.MM.YYYY HH:mm'),
},
{
title: 'Aktion',
title: 'Aktionen',
key: 'action',
width: 150,
render: (_: any, record: PosteingangDocument) => (
@@ -110,10 +112,9 @@ export default function PosteingangPage() {
];
return (
<ConfigProvider>
<div style={{ padding: '0 24px 24px' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
<h2>Posteingang</h2>
<div>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
<Title level={3} style={{ margin: 0 }}>Posteingang</Title>
<Space>
<Tooltip title="Manuell aktualisieren">
<Button icon={<ReloadOutlined />} onClick={fetchData} loading={loading} />
@@ -125,9 +126,10 @@ export default function PosteingangPage() {
dataSource={data}
rowKey="id"
loading={loading}
pagination={{ pageSize: 20 }}
pagination={{ pageSize: 20, showSizeChanger: true, showTotal: (t) => `${t} Dokumente` }}
locale={{ emptyText: 'Keine Einträge vorhanden' }}
/>
<DocumentEditModal
documentId={selectedDoc?.id || null}
document={selectedDoc}
@@ -137,7 +139,6 @@ export default function PosteingangPage() {
isPosteingang={true}
hasNextDocument={data.length > 1}
/>
</div>
</ConfigProvider>
</div>
);
}
@@ -161,6 +161,7 @@ export default function TaskLogPage() {
rowKey="TaskId"
loading={loading}
pagination={{ pageSize: 20 }}
locale={{ emptyText: 'Keine Einträge vorhanden' }}
/>
</div>
</ConfigProvider>