„inbox“ war doppelt belegt (/api/inbox = Scanner, /api/paperless/inbox = Paperless-Posteingang) und passte nicht zur Manuell-Seite, die dieselben Routen nutzt. Umbenannt (ohne Alias, nur das eigene Frontend nutzt sie): PUT paperless/inbox/:id → PUT paperless/documents/:id GET paperless/inbox/pdf/:id → GET paperless/documents/:id/pdf GET paperless/inbox/preview/:id → GET paperless/documents/:id/preview GET paperless/inbox/:id/erechnung → GET paperless/documents/:id/erechnung Dabei: - Dokumentsuche (GET documents) wieder für den Posteingang (VIEW_INBOX) statt nur Admin – die Verknüpfungssuche im Bearbeiten-Dialog nutzt sie; die Einschränkung aus dem vorigen Commit hatte sie gebrochen. - Suche und GET documents/:id liefern nur noch id, Titel, ASN, Datum (kein OCR-Volltext, keine Statusfelder). - documents/:id/pdf auch für VIEW_SCANNER (Duplikatvergleich im Scanner). - E-Rechnungs-Vorschlag prüft wie das Speichern pro Dokument (Tag 1 bzw. Manuell-Tag); Vorschau/PDF bewusst nicht (Suchergebnisse, Duplikate). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
182 lines
6.0 KiB
TypeScript
182 lines
6.0 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
import { Table, Popover, Button, Space, message, Tooltip, Typography } from 'antd';
|
|
import { AuthImage } from '../utils/auth-resource';
|
|
import { ReloadOutlined } from '@ant-design/icons';
|
|
|
|
const { Title, Text } = Typography;
|
|
import dayjs from 'dayjs';
|
|
import { posteingangApi } from '../api/posteingang';
|
|
import type { PosteingangDocument } from '../api/posteingang';
|
|
import DocumentEditModal from '../components/DocumentEditModal';
|
|
import { getEnv } from '../utils/env';
|
|
import { useIsMobile } from '../hooks/useIsMobile';
|
|
import MobileCardList from '../components/MobileCardList';
|
|
|
|
export default function PosteingangPage() {
|
|
const isMobile = useIsMobile();
|
|
const [data, setData] = useState<PosteingangDocument[]>([]);
|
|
const [loading, setLoading] = useState(false);
|
|
const [editModalOpen, setEditModalOpen] = useState(false);
|
|
const [selectedDoc, setSelectedDoc] = useState<PosteingangDocument | null>(null);
|
|
|
|
const fetchData = async () => {
|
|
setLoading(true);
|
|
try {
|
|
const docs = await posteingangApi.getList();
|
|
setData(docs || []);
|
|
} catch (e) {
|
|
message.error("Posteingang konnte nicht geladen werden.");
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
fetchData();
|
|
// Refresh interval every 30 seconds
|
|
const interval = setInterval(() => {
|
|
fetchData();
|
|
}, 30000);
|
|
return () => clearInterval(interval);
|
|
}, []);
|
|
|
|
const handleEdit = (doc: PosteingangDocument) => {
|
|
setSelectedDoc(doc);
|
|
setEditModalOpen(true);
|
|
};
|
|
|
|
const handleCloseModal = (openNext?: boolean) => {
|
|
setEditModalOpen(false);
|
|
if (openNext && data.length > 0) {
|
|
const currentIndex = data.findIndex(d => d.id === selectedDoc?.id);
|
|
const nextIndex = currentIndex !== -1 && currentIndex + 1 < data.length ? currentIndex + 1 : 0;
|
|
const nextDoc = data[nextIndex];
|
|
setTimeout(() => {
|
|
setSelectedDoc(nextDoc);
|
|
setEditModalOpen(true);
|
|
}, 300);
|
|
} else {
|
|
setSelectedDoc(null);
|
|
}
|
|
};
|
|
|
|
const popoverContent = (id: number) => (
|
|
<AuthImage
|
|
src={`${getEnv('VITE_API_URL')}/api/paperless/documents/${id}/preview`}
|
|
width={400}
|
|
/>
|
|
);
|
|
|
|
const getEingangsdatum = (record: PosteingangDocument) => {
|
|
const cf = record.customFields?.find((f) => f.field === 9);
|
|
return cf?.value ? dayjs(cf.value).format('DD.MM.YYYY') : '-';
|
|
};
|
|
|
|
const columns = [
|
|
{
|
|
title: 'Vorschau',
|
|
key: 'preview',
|
|
width: 150,
|
|
render: (_: any, record: PosteingangDocument) => (
|
|
<Popover content={popoverContent(record.id)} placement="right" trigger="hover">
|
|
<AuthImage
|
|
src={`${getEnv('VITE_API_URL')}/api/paperless/documents/${record.id}/preview`}
|
|
width={100}
|
|
style={{ border: '1px solid #d9d9d9', objectFit: 'contain' }}
|
|
/>
|
|
</Popover>
|
|
),
|
|
},
|
|
{
|
|
title: 'Titel',
|
|
dataIndex: 'title',
|
|
key: 'title',
|
|
width: '35%',
|
|
},
|
|
{
|
|
title: 'Eingangsdatum',
|
|
key: 'eingangsdatum',
|
|
render: (_: any, record: PosteingangDocument) => getEingangsdatum(record),
|
|
},
|
|
{
|
|
title: 'Importiert am',
|
|
dataIndex: 'added',
|
|
key: 'added',
|
|
render: (text: string) => dayjs(text).format('DD.MM.YYYY HH:mm'),
|
|
},
|
|
{
|
|
title: 'Aktionen',
|
|
key: 'action',
|
|
width: 150,
|
|
render: (_: any, record: PosteingangDocument) => (
|
|
<Button type="primary" onClick={() => handleEdit(record)}>
|
|
Bearbeiten
|
|
</Button>
|
|
),
|
|
},
|
|
];
|
|
|
|
return (
|
|
<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} />
|
|
</Tooltip>
|
|
</Space>
|
|
</div>
|
|
{isMobile ? (
|
|
<MobileCardList<PosteingangDocument>
|
|
dataSource={data}
|
|
rowKey="id"
|
|
loading={loading}
|
|
pagination={{ pageSize: 20, showTotal: (t) => `${t} Dokumente` }}
|
|
renderCard={(record) => (
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
|
<div style={{ display: 'flex', gap: 12 }}>
|
|
<AuthImage
|
|
src={`${getEnv('VITE_API_URL')}/api/paperless/documents/${record.id}/preview`}
|
|
width={72}
|
|
style={{ border: '1px solid #d9d9d9', objectFit: 'contain', flexShrink: 0 }}
|
|
/>
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 4, minWidth: 0 }}>
|
|
<Text strong>{record.title || '—'}</Text>
|
|
<Text type="secondary" style={{ fontSize: 13 }}>
|
|
Eingangsdatum: {getEingangsdatum(record)}
|
|
</Text>
|
|
<Text type="secondary" style={{ fontSize: 13 }}>
|
|
Importiert am: {dayjs(record.added).format('DD.MM.YYYY HH:mm')}
|
|
</Text>
|
|
</div>
|
|
</div>
|
|
<Button type="primary" block onClick={() => handleEdit(record)}>
|
|
Bearbeiten
|
|
</Button>
|
|
</div>
|
|
)}
|
|
/>
|
|
) : (
|
|
<Table
|
|
columns={columns}
|
|
dataSource={data}
|
|
rowKey="id"
|
|
loading={loading}
|
|
pagination={{ pageSize: 20, showSizeChanger: true, showTotal: (t) => `${t} Dokumente` }}
|
|
locale={{ emptyText: 'Keine Einträge vorhanden' }}
|
|
/>
|
|
)}
|
|
|
|
<DocumentEditModal
|
|
documentId={selectedDoc?.id || null}
|
|
document={selectedDoc}
|
|
open={editModalOpen}
|
|
onClose={handleCloseModal}
|
|
onSave={fetchData}
|
|
isPosteingang={true}
|
|
hasNextDocument={data.length > 1}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|