diff --git a/src/api/shipment.js b/src/api/shipment.js
index 7b7b22f..66e4207 100644
--- a/src/api/shipment.js
+++ b/src/api/shipment.js
@@ -8,7 +8,7 @@ export const shipment = {
// #region GET /api/shipment — list
list(params = {}) {
const q = new URLSearchParams();
- if (params.offset != null) q.set('offset', String(params.offset));
+ if (params.offset) q.set('offset', String(params.offset));
if (params.limit != null) q.set('limit', String(params.limit));
if (params.search) q.set('search', String(params.search));
if (params.state) q.set('state', String(params.state));
diff --git a/src/automats/shipmentAutomat.js b/src/automats/shipmentAutomat.js
index 373cbaf..0be4777 100644
--- a/src/automats/shipmentAutomat.js
+++ b/src/automats/shipmentAutomat.js
@@ -47,6 +47,7 @@ import orderAutomat from './orderAutomat';
* @property {string} filter
* @property {string} stateFilter
* @property {number} seekMonths
+ * @property {number} seekYears
* @property {'idle'|'loading'|'ready'|'failed'} status
* @property {Error|string|null} error
*/
@@ -58,6 +59,7 @@ const initialState = {
filter: '',
stateFilter: '',
seekMonths: 0,
+ seekYears: 0,
status: 'idle',
error: null,
};
@@ -69,7 +71,6 @@ let debounceTimer = null;
// #region fetch helper
async function fetchShipments(state) {
const params = {
- offset: 0,
limit: 100,
};
if (state.filter && state.filter.trim()) {
@@ -78,8 +79,9 @@ async function fetchShipments(state) {
if (state.stateFilter) {
params.state = state.stateFilter;
}
- if (state.seekMonths) {
- params.seekMonths = state.seekMonths;
+ const totalMonths = (state.seekMonths || 0) + (state.seekYears || 0) * 12;
+ if (totalMonths > 0) {
+ params.seekMonths = totalMonths;
}
const res = await shipmentApi.list(params);
@@ -164,7 +166,22 @@ const actions = {
},
setSeekMonths(months) {
- automat.setState({ seekMonths: months });
+ automat.setState({ seekMonths: months, seekYears: 0 });
+ void automat.actions.load().catch(() => {});
+ },
+
+ addSeekMonth() {
+ automat.setState((s) => ({ seekMonths: (s.seekMonths ?? 0) + 1 }));
+ void automat.actions.load().catch(() => {});
+ },
+
+ addSeekYear() {
+ automat.setState((s) => ({ seekYears: (s.seekYears ?? 0) + 1 }));
+ void automat.actions.load().catch(() => {});
+ },
+
+ resetNegativeSeek() {
+ automat.setState({ seekMonths: 0, seekYears: 0 });
void automat.actions.load().catch(() => {});
},
// #endregion
@@ -205,16 +222,31 @@ const actions = {
void orderAutomat.actions.reload().catch(() => {});
},
- reset() {
- if (debounceTimer) clearTimeout(debounceTimer);
- inflightLoad = null;
- automat.setState({ ...initialState });
- },
- // #endregion
-};
+ reset() {
+ if (debounceTimer) clearTimeout(debounceTimer);
+ inflightLoad = null;
+ automat.setState({ ...initialState });
+ },
+ // #endregion
+ };
automat = new Automat(initialState, actions, { name: 'shipment' });
+// MARK: filter key & items helpers
+export function getFilterKey(state = automat?.state || initialState) {
+ return `${state.filter || ''}|${state.stateFilter || ''}|${state.seekMonths || 0}|${state.seekYears || 0}`;
+}
+
+export function getItems() {
+ if (automat.state.status === 'idle') {
+ void automat.actions.load().catch(() => {});
+ }
+ return automat.state.items;
+}
+
+automat.getFilterKey = getFilterKey;
+automat.getItems = getItems;
+
// #region authAutomat synchronization
let lastCompanyId = null;
authAutomat.subscribe((auth) => {
diff --git a/src/components/AppShell.jsx b/src/components/AppShell.jsx
index a493e07..f9595b2 100644
--- a/src/components/AppShell.jsx
+++ b/src/components/AppShell.jsx
@@ -171,10 +171,12 @@ class AppSidebar extends PureComponent {
)}
diff --git a/src/components/manifest/ShipmentList.jsx b/src/components/manifest/ShipmentList.jsx
index a690a0c..7023c07 100644
--- a/src/components/manifest/ShipmentList.jsx
+++ b/src/components/manifest/ShipmentList.jsx
@@ -1,4 +1,4 @@
-import { PureComponent } from 'react';
+import { PureComponent, createRef } from 'react';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Chip from '@mui/material/Chip';
@@ -9,9 +9,8 @@ import TextField from '@mui/material/TextField';
import Typography from '@mui/material/Typography';
import MenuItem from '@mui/material/MenuItem';
import Stack from '@mui/material/Stack';
-import ContentCopyIcon from '@mui/icons-material/ContentCopy';
+import Skeleton from '@mui/material/Skeleton';
import LocalShippingIcon from '@mui/icons-material/LocalShipping';
-import AddIcon from '@mui/icons-material/Add';
import DeleteOutlinedIcon from '@mui/icons-material/DeleteOutlined';
import CheckCircleOutlineOutlinedIcon from '@mui/icons-material/CheckCircleOutlineOutlined';
import CloseIcon from '@mui/icons-material/Close';
@@ -19,14 +18,17 @@ import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
import Menu from '@mui/material/Menu';
import { enqueueSnackbar } from 'notistack';
import SingleLineTextEdit from 'dashgui/SingleLineTextEdit';
+import FillSpace from 'dashgui/FillSpace';
import { PanelTile, PanelTileGrid } from 'dashgui/panels';
import BackendError from '../BackendError';
import AppDialog from '../AppDialog';
import shipmentAutomat from '../../automats/shipmentAutomat';
import orderAutomat from '../../automats/orderAutomat';
+import numberAutomat from '../../automats/numberAutomat';
import { shipment as shipmentApi } from '../../api';
import { backendErrorFrom } from '../../utils/backendError';
import { notifyError } from '../../utils/notify';
+import { getMaxDocumentNumber } from '../../utils/documentNumber';
import withNavigate from '../withNavigate';
import ShipmentDetailDialog from './ShipmentDetailDialog';
import {
@@ -38,9 +40,7 @@ import {
} from './manifestConfig';
import {
GREEN_PANEL,
- SURFACE_BASE,
SURFACE_RAISED,
- WHITE,
} from '../../colors';
const cfg = manifestConfig('order');
@@ -119,63 +119,46 @@ class NewShipmentModal extends PureComponent {
}
async handleSubmit() {
- const { orders = [], onClose, onSuccess } = this.props;
const { orderId, date, externalNumber, lineAmounts } = this.state;
- const order = orders.find((o) => o.id === Number(orderId));
- if (!order) return;
-
- const linesToShip = [];
- for (const [sku, amtStr] of Object.entries(lineAmounts)) {
- const amt = Number(amtStr);
- if (amt > 0) {
- const orderLine = order.lines?.find((l) => l.sku === sku);
- linesToShip.push({
- sku,
- amount: amt,
- name: orderLine?.name ?? '',
- pu: orderLine?.pu ?? 1,
- net: orderLine?.net ?? 0,
- rebate: orderLine?.rebate ?? 0,
- taxrate: orderLine?.taxrate ?? 0,
- });
- }
+ if (!orderId) {
+ enqueueSnackbar('Bitte wählen Sie einen Auftrag aus.', { variant: 'warning' });
+ return;
}
- if (linesToShip.length === 0) {
- enqueueSnackbar('Mindestens ein Artikel muss eine Liefermenge > 0 haben.', { variant: 'warning' });
+ const lines = Object.entries(lineAmounts)
+ .filter(([, amt]) => Number(amt) > 0)
+ .map(([sku, amt]) => ({ sku, amount: Number(amt) }));
+
+ if (lines.length === 0) {
+ enqueueSnackbar('Mindestens eine Position muss mit Menge > 0 geliefert werden.', { variant: 'warning' });
return;
}
this.setState({ saving: true });
try {
- const res = await shipmentApi.create(order.id, {
+ const res = await shipmentApi.create(orderId, {
date,
externalNumber: externalNumber.trim() || undefined,
- lines: linesToShip,
+ lines,
});
- enqueueSnackbar(`Lieferschein ${res.shipment?.number || ''} erfolgreich angelegt!`, { variant: 'success' });
- await Promise.all([
- orderAutomat.actions.reload(),
- shipmentAutomat.actions.reload(),
- ]);
- onSuccess?.(res.shipment);
- onClose();
+
+ enqueueSnackbar(res.message || 'Lieferschein erfolgreich erstellt.', { variant: 'success' });
+ void orderAutomat.actions.reload().catch(() => {});
+ void shipmentAutomat.actions.reload().catch(() => {});
+
+ this.props.onSuccess?.(res.shipment);
+ this.props.onClose?.();
} catch (err) {
notifyError(err);
+ } finally {
this.setState({ saving: false });
}
}
render() {
- const { open, onClose, orders, partiesById } = this.props;
+ const { open, onClose, orders = [], partiesById = {} } = this.props;
const { orderId, date, externalNumber, lineAmounts, saving } = this.state;
- const openOrders = orders.filter((o) => {
- const ordQty = Number(o.orderedQuantity) || 0;
- const shpQty = Number(o.shippedQuantity) || 0;
- return ordQty === 0 || shpQty < ordQty;
- });
-
const currentOrder = orders.find((o) => o.id === Number(orderId));
const lines = Array.isArray(currentOrder?.lines) ? currentOrder.lines : [];
@@ -187,32 +170,29 @@ class NewShipmentModal extends PureComponent {
}
return (
-
+
-
-
- Neuen Lieferschein erstellen (Shipment Manifest)
-
-
-
-
-
-
-
+
this.handleOrderChange(e.target.value)}
fullWidth
>
- {openOrders.map((o) => {
- const party = partiesById[o.entityId];
- const partyName = partyDisplayName('order', party) || `Kunde #${o.entityId}`;
+ {orders.map((o) => {
+ const customer = partiesById[o.entityId];
+ const custName = partyDisplayName('order', customer) || (o.entityId ? `Kunde #${o.entityId}` : '');
return (
);
})}
@@ -389,141 +369,255 @@ class DeliveryStateMenu extends PureComponent {
}
}
-// MARK:
+// Dummy shipment generator with max digits for layout testing and probe measurement
+export function createDummyTestShipment() {
+ const maxOrderNumber = getMaxDocumentNumber('order');
+ return {
+ id: 999999,
+ number: `${maxOrderNumber}-L-99`,
+ externalNumber: 'REF-999999',
+ date: '2030-10-30T12:00:00Z',
+ orderNumber: maxOrderNumber,
+ customerName: 'Kunde',
+ positions: 999,
+ totalQuantity: 999,
+ packageCount: 9,
+ carrier: 'DHL',
+ trackingCode: '00340434161094042578',
+ state: 'delivered',
+ };
+}
+
const rowGridSx = {
display: 'grid',
- gridTemplateColumns: '170px 105px 130px 1.4fr 120px 160px 110px 48px',
- alignItems: 'center',
+ gridTemplateColumns: 'max-content max-content max-content 1fr max-content max-content max-content auto',
+ gridTemplateRows: '0px 1fr',
+ rowGap: 0,
columnGap: 1.5,
+ alignItems: 'center',
width: '100%',
- px: 2,
- py: 1.1,
boxSizing: 'border-box',
};
+const measuringCellSx = {
+ visibility: 'hidden',
+ height: 0,
+ boxSizing: 'border-box',
+ pointerEvents: 'none',
+ textTransform: 'none',
+ letterSpacing: 'normal',
+};
+
+const headerCellSx = {
+ color: 'text.secondary',
+ typography: 'caption',
+ fontWeight: 600,
+ textTransform: 'uppercase',
+ letterSpacing: '0.04em',
+ userSelect: 'none',
+};
+
+function renderInvisibleDummyRow(dummy) {
+ const d = dummy || createDummyTestShipment();
+ return (
+ <>
+
+ {d.number}
+ {d.externalNumber || 'Nr. / Ref.'}
+
+
+ {d.date ? formatDate(d.date) : '–'}
+
+
+ {d.orderNumber}
+
+
+ {d.customerName}
+
+
+
+
+
+
+
+
+ {d.trackingCode}
+
+
+
+
+
+ Geliefert
+
+
+ }
+ sx={{ fontWeight: 600, height: 24 }}
+ />
+
+
+
+
+ >
+ );
+}
+
+/**
+ * Ambient context for `ShipmentRow`: FillSpace renders rows as bare elements
+ * (`
`), so the list publishes callbacks and party lookup here.
+ */
+const rowContext = {
+ partiesById: {},
+ busyId: null,
+ onOpen: null,
+ onDelete: null,
+ headerItem: null,
+};
+
+// MARK:
class ShipmentRow extends PureComponent {
+ partyName(partyId) {
+ if (partyId == null) return '–';
+ const party = rowContext.partiesById[partyId];
+ return partyDisplayName('order', party) || `Kunde #${partyId}`;
+ }
+
render() {
- const { shipment, onOpen, onDelete, disabled } = this.props;
- if (!shipment) return null;
+ const { item: shipment, disabled } = this.props;
+ const dummy = this.props.headerItem || rowContext.headerItem;
+
+ // Data-free probe (FillSpace measures the row height with `item` undefined).
+ if (!shipment) {
+ if (disabled) {
+ return (
+
+
+
+ );
+ }
+ return (
+
+ {renderInvisibleDummyRow(dummy)}
+
+
+
+
+
+
+
+
+
+ );
+ }
+
+ const { busyId, onOpen, onDelete } = rowContext;
+ const isBusy = busyId === shipment.id;
+ const canDelete = shipment.state === 'new';
return (
onOpen(shipment)}
- sx={{
- p: 0,
- borderBottom: '1px solid',
- borderColor: 'divider',
- bgcolor: WHITE,
- '&:hover': {
- bgcolor: 'action.hover',
- },
- }}
+ divider
+ sx={{ ...rowGridSx, height: 64, minHeight: 64, maxHeight: 64, px: 2, py: 0 }}
+ onClick={disabled ? undefined : () => onOpen?.(shipment)}
+ disabled={disabled}
>
-
- {/* 1. Lieferschein-Nr & Ref */}
-
-
- {shipment.number}
-
- {shipment.externalNumber ? (
-
- {numberDisplay(shipment.externalNumber)}
-
- ) : null}
-
+ {renderInvisibleDummyRow(dummy)}
- {/* 2. Datum */}
+ {/* 1. Lieferschein-Nr & Ref */}
+
+
+ {shipment.number}
+
+
+ {numberDisplay(shipment.externalNumber)}
+
+
+
+ {/* 2. Datum */}
+
{shipment.date ? formatDate(shipment.date) : '–'}
+
- {/* 3. Bezugsauftrag */}
-
- {shipment.orderNumber ? (
-
- {shipment.orderNumber}
-
- ) : (
-
- –
-
- )}
-
+ {/* 3. Bezugsauftrag */}
+
+ {shipment.orderNumber ? (
+
+ {shipment.orderNumber}
+
+ ) : (
+
+ –
+
+ )}
+
- {/* 4. Kunde */}
-
- {shipment.customerName || (shipment.entityId ? `Kunde #${shipment.entityId}` : '–')}
+ {/* 4. Kunde */}
+
+
+ {shipment.customerName || this.partyName(shipment.entityId)}
+
- {/* 5. Positionen & Menge */}
-
-
- {shipment.positions || 0} Pos.
-
-
- {shipment.totalQuantity || 0} Stk
-
-
+ {/* 5. Positionen */}
+
+
+
- {/* 6. Paket & Tracking */}
-
- {shipment.carrier || shipment.trackingCode ? (
-
- {shipment.carrier && (
-
- )}
- {shipment.trackingCode && (
- {
- e.stopPropagation();
- navigator.clipboard.writeText(shipment.trackingCode);
- enqueueSnackbar('Tracking-Code kopiert!', { variant: 'info' });
- }}
- title="Tracking-Code kopieren"
- >
-
- {shipment.trackingCode}
-
-
-
- )}
-
- ) : (
-
- {shipment.packageCount ? `${shipment.packageCount} Paket(e)` : 'Keine Pakete'}
+ {/* 6. Paket / Tracking */}
+
+ {shipment.trackingCode ? (
+
+ {shipment.carrier && (
+
+ )}
+
+ {shipment.trackingCode}
- )}
-
+
+ ) : shipment.packageCount > 0 ? (
+
+ {shipment.packageCount} {shipment.packageCount === 1 ? 'Paket' : 'Pakete'}
+
+ ) : (
+
+ –
+
+ )}
+
- {/* 7. Status */}
-
-
-
+ {/* 7. Status */}
+
+
+
- {/* 8. Aktionen */}
-
- {shipment.state === 'new' && (
- {
- e.stopPropagation();
- onDelete(shipment);
- }}
- sx={{ p: 0.5, color: 'text.secondary', '&:hover': { color: 'error.main' } }}
- >
-
-
- )}
-
+ {/* 8. Aktionen (Löschen) */}
+
+ {canDelete ? (
+ {
+ e.stopPropagation();
+ onDelete?.(shipment);
+ }}
+ onMouseDown={(e) => e.stopPropagation()}
+ sx={{ width: 40, height: 40 }}
+ >
+
+
+ ) : (
+
+ )}
);
@@ -532,12 +626,42 @@ class ShipmentRow extends PureComponent {
// MARK:
class ShipmentSearchField extends PureComponent {
+ constructor(props) {
+ super(props);
+ const s = shipmentAutomat.state;
+ this.state = {
+ input: s.filter ?? '',
+ stateFilter: s.stateFilter ?? '',
+ seekMonths: s.seekMonths ?? 0,
+ seekYears: s.seekYears ?? 0,
+ };
+ }
+
+ componentDidMount() {
+ this.unsub = shipmentAutomat.subscribe(this, (state) => ({
+ input: state.filter ?? '',
+ stateFilter: state.stateFilter ?? '',
+ seekMonths: state.seekMonths ?? 0,
+ seekYears: state.seekYears ?? 0,
+ }));
+ }
+
+ componentWillUnmount() {
+ this.unsub?.();
+ }
+
render() {
- const { input, stateFilter, onInputChange, onStateFilterChange, onSeekChange, onCreate, seekMonths } = this.props;
+ const { inputRef, onCreate } = this.props;
+ const { input, stateFilter, seekMonths, seekYears } = this.state;
+ const monthActive = seekMonths > 0;
+ const monthLabel = !monthActive ? '-1 Monat' : `-${seekMonths} Monat${seekMonths > 1 ? 'e' : ''}`;
+ const yearActive = seekYears > 0;
+ const yearLabel = !yearActive ? '-1 Jahr' : `-${seekYears} Jahr${seekYears > 1 ? 'e' : ''}`;
return (
- {/* Top row: search & create */}
-
+ {/* Top row: search field & create button */}
+
shipmentAutomat.actions.setFilter(next)}
/>
- } onClick={onCreate}>
+
- {/* Filter Bar */}
-
+ {/* Filter Bar: Status chips & Negativeseek (-1 Month, -1 Year, Reset) */}
+
+ {/* Left: Status chips */}
-
- Status:
-
onStateFilterChange('')}
- sx={{ fontWeight: 600, cursor: 'pointer' }}
+ onClick={() => shipmentAutomat.actions.setStateFilter('')}
+ sx={{ fontWeight: 600, cursor: 'pointer', height: 26 }}
/>
onStateFilterChange('new')}
- sx={{ fontWeight: 600, cursor: 'pointer' }}
+ onClick={() => shipmentAutomat.actions.setStateFilter('new')}
+ sx={{ fontWeight: 600, cursor: 'pointer', height: 26 }}
/>
onStateFilterChange('delivered')}
- sx={{ fontWeight: 600, cursor: 'pointer' }}
+ onClick={() => shipmentAutomat.actions.setStateFilter('delivered')}
+ sx={{ fontWeight: 600, cursor: 'pointer', height: 26 }}
/>
onStateFilterChange('returned')}
- sx={{ fontWeight: 600, cursor: 'pointer' }}
+ onClick={() => shipmentAutomat.actions.setStateFilter('returned')}
+ sx={{ fontWeight: 600, cursor: 'pointer', height: 26 }}
/>
- {/* Date Seek Cutoff */}
-
-
-
-
+
+
+
+
+
+
@@ -652,22 +838,18 @@ class ShipmentSearchField extends PureComponent {
export class ShipmentList extends PureComponent {
constructor(props) {
super(props);
- const s = shipmentAutomat.state;
+ this.headerItem = createDummyTestShipment();
this.state = {
- items: s.items,
- total: s.total,
- unfilteredTotal: s.unfilteredTotal,
- filter: s.filter,
- stateFilter: s.stateFilter,
- seekMonths: s.seekMonths,
- status: s.status,
- error: s.error ? backendErrorFrom(s.error) : null,
+ items: shipmentAutomat.getItems(),
+ unfilteredTotal: shipmentAutomat.state.unfilteredTotal ?? 0,
+ filterKey: shipmentAutomat.getFilterKey?.() ?? '',
orders: orderAutomat.state?.items || [],
partiesById: {},
selectedShipment: null,
newShipmentModalOpen: false,
busyId: null,
};
+ this.searchRef = createRef();
this._cancelled = false;
this.handleRetry = this.handleRetry.bind(this);
this.handleOpenShipment = this.handleOpenShipment.bind(this);
@@ -679,19 +861,20 @@ export class ShipmentList extends PureComponent {
this._cancelled = false;
this.unsub = shipmentAutomat.subscribe(this, (state) => ({
items: state.items,
- total: state.total,
- unfilteredTotal: state.unfilteredTotal,
- filter: state.filter,
- stateFilter: state.stateFilter,
- seekMonths: state.seekMonths,
- status: state.status,
- error: state.error ? backendErrorFrom(state.error) : null,
+ unfilteredTotal: state.unfilteredTotal ?? 0,
+ filterKey: shipmentAutomat.getFilterKey(state),
}));
this.unsubOrder = orderAutomat.subscribe(this, (state) => ({
orders: state.items || [],
}));
+ this.unsubNumber = numberAutomat.subscribe(this, () => {
+ this.headerItem = createDummyTestShipment();
+ this.forceUpdate();
+ });
+ void numberAutomat.actions.ensureLoaded().catch(() => {});
+
void shipmentAutomat.actions.load().catch(() => {});
void orderAutomat.actions.load().catch(() => {});
void this.loadParties();
@@ -720,6 +903,7 @@ export class ShipmentList extends PureComponent {
this._cancelled = true;
this.unsub?.();
this.unsubOrder?.();
+ this.unsubNumber?.();
}
async loadParties() {
@@ -746,6 +930,7 @@ export class ShipmentList extends PureComponent {
}
async handleDeleteShipment(sh) {
+ if (this.state.busyId != null) return;
if (!window.confirm(`Lieferschein „${sh.number}“ wirklich löschen?`)) return;
this.setState({ busyId: sh.id });
try {
@@ -761,13 +946,8 @@ export class ShipmentList extends PureComponent {
render() {
const {
items,
- total,
unfilteredTotal,
- filter,
- stateFilter,
- seekMonths,
- status,
- error,
+ filterKey,
orders,
partiesById,
selectedShipment,
@@ -775,113 +955,87 @@ export class ShipmentList extends PureComponent {
busyId,
} = this.state;
- if (items === null && status === 'failed') {
+ rowContext.partiesById = partiesById;
+ rowContext.busyId = busyId;
+ rowContext.onOpen = this.handleOpenShipment;
+ rowContext.onDelete = this.handleDeleteShipment;
+ rowContext.headerItem = this.headerItem;
+
+ if (items === null && shipmentAutomat.state.status === 'failed') {
return (
-
-
-
-
-
+ <>
+ this.setState({ newShipmentModalOpen: true })} />
+
+
+
+
+
+ >
);
}
- const shipmentList = Array.isArray(items) ? items : [];
-
return (
<>
shipmentAutomat.actions.setFilter(val)}
- onStateFilterChange={(st) => shipmentAutomat.actions.setStateFilter(st)}
- onSeekChange={(m) => shipmentAutomat.actions.setSeekMonths(m)}
+ inputRef={this.searchRef}
onCreate={() => this.setState({ newShipmentModalOpen: true })}
/>
-
- {/* Table Header */}
-
-
- Lieferschein / Ref
-
-
- Datum
-
-
- Auftrag
-
-
- Kunde
-
-
- Positionen
-
-
- Paket / Tracking
-
-
- Status
-
-
-
-
- {/* Rows */}
- {items === null && status === 'loading' ? (
-
-
-
- ) : shipmentList.length === 0 ? (
-
-
-
- Keine Lieferscheine gefunden.
-
-
- {filter || stateFilter ? 'Passen Sie die Filter an oder erstellen Sie einen neuen Lieferschein.' : 'Erstellen Sie den ersten Lieferschein aus einem Auftrag.'}
-
-
- ) : (
-
- {shipmentList.map((sh) => (
-
- ))}
+
+
+ {renderInvisibleDummyRow(this.headerItem)}
+ Lieferschein / Ref
+ Datum
+ Auftrag
+ Kunde
+ Pos.
+ Paket / Tracking
+ Status
+
+
+ }
+ />
+ {items === null && (
+
+
)}
-
- {/* Footer Summary */}
-
-
- {shipmentList.length} von {total} {total === 1 ? 'Lieferschein' : 'Lieferscheinen'} angezeigt
- {unfilteredTotal > total ? ` (gefiltert von ${unfilteredTotal})` : ''}
-
-