import { PureComponent, createRef } from 'react';
import Autocomplete from '@mui/material/Autocomplete';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import CircularProgress from '@mui/material/CircularProgress';
import Stack from '@mui/material/Stack';
import TextField from '@mui/material/TextField';
import Chip from '@mui/material/Chip';
import UploadFileIcon from '@mui/icons-material/UploadFile';
import Inventory2OutlinedIcon from '@mui/icons-material/Inventory2Outlined';
import CheckCircleOutlineOutlinedIcon from '@mui/icons-material/CheckCircleOutlineOutlined';
import SingleLineTextEdit from 'dashgui/SingleLineTextEdit';
import { PanelTile } from 'dashgui/panels';
import FileDropzone from '../FileDropzone';
import AttachmentList from './AttachmentList';
import { hasArticleLines, manifestConfig, partyDisplayName } from './manifestConfig';
import { notifyErrorMessage } from '../../utils/notify';
class ManifestHeader extends PureComponent {
constructor(props) {
super(props);
this.fileInputRef = createRef();
this.handlePartyChange = this.handlePartyChange.bind(this);
this.handleUploadClick = this.handleUploadClick.bind(this);
this.handleFileChange = this.handleFileChange.bind(this);
this.handleFileDrop = this.handleFileDrop.bind(this);
}
handlePartyChange(_event, option) {
this.props.onPartyChange(option?.id ?? null);
}
hasArticles() {
return Boolean(
this.props.hasArticles ??
(Array.isArray(this.props.lines) && hasArticleLines(this.props.lines)),
);
}
handleUploadClick() {
if (this.hasArticles()) {
notifyErrorMessage(
'Datei-Upload nicht möglich, solange Artikel vorhanden sind. Bitte entfernen Sie zuerst alle Zeilen.',
);
return;
}
this.fileInputRef.current?.click();
}
handleFileDrop(file) {
if (this.hasArticles()) {
notifyErrorMessage(
'Datei-Upload nicht möglich, solange Artikel vorhanden sind. Bitte entfernen Sie zuerst alle Zeilen.',
);
return;
}
this.props.onPdfUpload?.(file);
}
handleFileChange(event) {
const file = event.target.files?.[0];
event.target.value = '';
if (!file) return;
if (this.hasArticles()) {
notifyErrorMessage(
'Datei-Upload nicht möglich, solange Artikel vorhanden sind. Bitte entfernen Sie zuerst alle Zeilen.',
);
return;
}
this.props.onPdfUpload?.(file);
}
render() {
const {
kind,
headerColor,
dateLabel,
partyLabel,
parties,
partiesLoading,
partyId,
date,
number,
externalNumber,
saving,
pdfUploading,
attachments,
onPdfUpload,
onPdfRescan,
onPdfDelete,
onPdfClick,
onAttachmentClick,
onDateChange,
onNumberChange,
onExternalNumberChange,
onSave,
actionsColor,
docId,
ledgerSummary,
onOpenBookDialog,
} = this.props;
const hasArticles = this.hasArticles();
// Delivery: number is server-allocated/read-only; the caller reference
// lives in the editable externalNumber field.
const cfg = manifestConfig(kind);
const useExternalNumber = Boolean(cfg.externalNumber);
const selected =
parties.find((row) => row.id === partyId) ?? null;
const isDateEmpty = !date || String(date).trim() === '';
const isPartyEmpty = partyId == null || partyId === '';
return (
<>
{/* Tile 1: 3-wide Kopfdaten Tile */}
onDateChange(e.target.value)}
autoComplete="off"
slotProps={{
inputLabel: { shrink: true },
htmlInput: { spellCheck: false },
}}
sx={{
flex: { xs: '1 1 100%', sm: '1 1 150px' },
minWidth: { sm: 130 },
}}
/>
partyDisplayName(kind, option)}
isOptionEqualToValue={(option, value) => option.id === value.id}
onChange={this.handlePartyChange}
sx={{
flex: { xs: '1 1 100%', sm: '2 1 200px' },
minWidth: { sm: 160 },
}}
renderInput={(params) => (
)}
/>
{useExternalNumber ? (
) : null}
{docId != null && kind === 'delivery' && ledgerSummary && (
) : (
)
}
label={
ledgerSummary.status === 'booked'
? `Lager: Gebucht (${ledgerSummary.bookedPositions}/${ledgerSummary.totalPositions})`
: ledgerSummary.status === 'partial'
? `Lager: Teilweise (${ledgerSummary.bookedPositions}/${ledgerSummary.totalPositions})`
: ledgerSummary.status === 'empty'
? 'Lager: Keine Pos.'
: `Lager: Offen (${ledgerSummary.totalPositions} Pos.)`
}
color={
ledgerSummary.status === 'booked'
? 'success'
: ledgerSummary.status === 'partial'
? 'warning'
: 'default'
}
variant="outlined"
onClick={onOpenBookDialog}
sx={{
cursor: onOpenBookDialog ? 'pointer' : 'default',
fontWeight: 600,
height: 32,
px: 0.5,
}}
/>
)}
{/* Tile 2: 1-wide Action Tile (4th tile in row) with Speichern & Datei hochladen */}
{docId != null && kind === 'delivery' && onOpenBookDialog && (
) : (
)
}
disabled={saving || pdfUploading}
onClick={onOpenBookDialog}
sx={{
height: 40,
fontWeight: 700,
fontSize: '0.8125rem',
whiteSpace: 'nowrap',
}}
>
{ledgerSummary?.status === 'booked'
? 'Lager gebucht'
: ledgerSummary?.status === 'partial'
? 'Rest einbuchen'
: 'Ins Lager buchen'}
)}
{onPdfUpload ? (
{({ isDragging }) => (
: }
disabled={saving || pdfUploading}
onClick={this.handleUploadClick}
title={
hasArticles
? 'Datei-Upload nicht möglich, solange Artikel vorhanden sind'
: 'PDF, TXT oder CSV hochladen'
}
sx={{
whiteSpace: 'nowrap',
height: 40,
width: '100%',
px: 1.5,
boxSizing: 'border-box',
bgcolor: isDragging ? 'action.hover' : 'transparent',
borderColor: isDragging
? 'primary.main'
: hasArticles
? 'action.disabled'
: undefined,
color: hasArticles ? 'text.disabled' : undefined,
opacity: hasArticles ? 0.6 : 1,
cursor: hasArticles ? 'not-allowed' : 'pointer',
transition: 'background-color 0.15s, border-color 0.15s, opacity 0.15s',
}}
>
{pdfUploading
? 'Wird analysiert…'
: isDragging
? 'Datei hier ablegen'
: 'Datei hochladen'}
)}
) : null}
{onPdfUpload ? (
) : null}
>
);
}
}
export default ManifestHeader;