7076eef57b
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>
125 lines
3.6 KiB
TypeScript
125 lines
3.6 KiB
TypeScript
import { useState, useCallback, useEffect } from 'react';
|
|
import { Modal, Input, List, Typography, Space, Pagination, Spin, Button } from 'antd';
|
|
import { SearchOutlined, CheckOutlined } from '@ant-design/icons';
|
|
import { paperlessApi } from '../api/paperless';
|
|
import { getEnv } from '../utils/env';
|
|
import { AuthImage } from '../utils/auth-resource';
|
|
import dayjs from 'dayjs';
|
|
|
|
const { Text } = Typography;
|
|
|
|
interface Props {
|
|
open: boolean;
|
|
onCancel: () => void;
|
|
onSelect: (doc: any) => void;
|
|
}
|
|
|
|
export default function DocumentSearchModal({ open, onCancel, onSelect }: Props) {
|
|
const [loading, setLoading] = useState(false);
|
|
const [search, setSearch] = useState('');
|
|
const [data, setData] = useState<any[]>([]);
|
|
const [total, setTotal] = useState(0);
|
|
const [page, setPage] = useState(1);
|
|
const [pageSize] = useState(10);
|
|
|
|
const load = useCallback(async (q: string, p: number) => {
|
|
setLoading(true);
|
|
try {
|
|
const response = await paperlessApi.searchDocuments({
|
|
search: q,
|
|
page: p,
|
|
pageSize: pageSize,
|
|
});
|
|
setData(response.results);
|
|
setTotal(response.count);
|
|
} catch (e) {
|
|
console.error("Error searching documents", e);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, [pageSize]);
|
|
|
|
useEffect(() => {
|
|
if (open) {
|
|
load(search, page);
|
|
}
|
|
}, [open, page, load]); // We don't trigger on search immediately to allow 'Search' button or Enter
|
|
|
|
const handleSearch = (val: string) => {
|
|
setSearch(val);
|
|
setPage(1);
|
|
load(val, 1);
|
|
};
|
|
|
|
return (
|
|
<Modal
|
|
title="Dokument suchen"
|
|
open={open}
|
|
onCancel={onCancel}
|
|
footer={null}
|
|
width={{ xs: '100vw', md: 800 }}
|
|
style={{ top: 50 }}
|
|
>
|
|
<div style={{ marginBottom: 16 }}>
|
|
<Input.Search
|
|
placeholder="Titel oder Inhalt suchen..."
|
|
enterButton={<SearchOutlined />}
|
|
onSearch={handleSearch}
|
|
loading={loading}
|
|
allowClear
|
|
/>
|
|
</div>
|
|
|
|
<Spin spinning={loading}>
|
|
<List
|
|
itemLayout="horizontal"
|
|
dataSource={data}
|
|
renderItem={(doc) => (
|
|
<List.Item
|
|
actions={[
|
|
<Button
|
|
key="select"
|
|
type="primary"
|
|
icon={<CheckOutlined />}
|
|
onClick={() => onSelect(doc)}
|
|
>
|
|
Auswählen
|
|
</Button>
|
|
]}
|
|
>
|
|
<List.Item.Meta
|
|
avatar={
|
|
<AuthImage
|
|
src={`${getEnv('VITE_API_URL')}/api/paperless/inbox/preview/${doc.id}`}
|
|
width={80}
|
|
height={110}
|
|
style={{ objectFit: 'cover', borderRadius: 4, border: '1px solid #f0f0f0' }}
|
|
/>
|
|
}
|
|
title={doc.title}
|
|
description={
|
|
<Space direction="vertical" size={0}>
|
|
<Text type="secondary" style={{ fontSize: 12 }}>ID: {doc.id} | ASN: {doc.archive_serial_number || 'Keine'}</Text>
|
|
<Text type="secondary" style={{ fontSize: 12 }}>Erstellt: {dayjs(doc.created).format('DD.MM.YYYY')}</Text>
|
|
</Space>
|
|
}
|
|
/>
|
|
</List.Item>
|
|
)}
|
|
/>
|
|
{total > pageSize && (
|
|
<div style={{ marginTop: 16, textAlign: 'right' }}>
|
|
<Pagination
|
|
current={page}
|
|
pageSize={pageSize}
|
|
total={total}
|
|
onChange={(p) => setPage(p)}
|
|
size="small"
|
|
/>
|
|
</div>
|
|
)}
|
|
</Spin>
|
|
</Modal>
|
|
);
|
|
}
|