import { PureComponent } from 'react'; import Alert from '@mui/material/Alert'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; import CircularProgress from '@mui/material/CircularProgress'; import IconButton from '@mui/material/IconButton'; import List from '@mui/material/List'; import ListItem from '@mui/material/ListItem'; import Stack from '@mui/material/Stack'; import Typography from '@mui/material/Typography'; import DeleteOutlinedIcon from '@mui/icons-material/DeleteOutlined'; import StorefrontIcon from '@mui/icons-material/Storefront'; import { enqueueSnackbar } from 'notistack'; import BackendError from '../../components/BackendError'; import SingleLineTextEdit from 'dashgui/SingleLineTextEdit'; import { PanelTile, PanelTileGrid } from 'dashgui/panels'; import withNavigate from '../../components/withNavigate'; import { backendErrorFrom } from '../../utils/backendError'; import { notifyError } from '../../utils/notify'; import { setLastDeliveryPath } from '../../automats/navigationAutomat'; import companyAutomat from '../../automats/companyAutomat'; import { deliveryDraftAutomat } from '../../automats/deliveryEditorAutomat'; import { GREEN_PANEL, } from '../../colors'; function sortSuppliers(suppliers) { return [...suppliers].sort((a, b) => String(a.name).localeCompare(String(b.name), 'de'), ); } function draftsFrom(suppliers) { return Object.fromEntries(suppliers.map((row) => [row.id, row.name])); } function getInitialParams(props) { const searchParams = typeof window !== 'undefined' ? new URLSearchParams(window.location.search) : null; const prefill = props.location?.state?.name ?? props.location?.state?.newName ?? searchParams?.get('name') ?? searchParams?.get('newName') ?? deliveryDraftAutomat.state.unmatchedSupplierName ?? ''; const returnTo = props.location?.state?.returnTo ?? searchParams?.get('returnTo') ?? null; return { prefill: prefill.trim(), returnTo }; } class LieferungLieferantenPage extends PureComponent { constructor(props) { super(props); const { prefill } = getInitialParams(props); const initialSuppliers = sortSuppliers(companyAutomat.state.suppliers ?? []); this.state = { status: companyAutomat.state.suppliersStatus, error: companyAutomat.state.suppliersError ? backendErrorFrom(companyAutomat.state.suppliersError) : null, suppliers: initialSuppliers, drafts: draftsFrom(initialSuppliers), newName: prefill, busyKey: null, }; this._cancelled = false; this.handleCreate = this.handleCreate.bind(this); this.handleRetry = this.handleRetry.bind(this); } componentDidMount() { const { returnTo, prefill } = getInitialParams(this.props); if (!returnTo) { setLastDeliveryPath(null); } if (prefill && !this.state.newName) { this.setState({ newName: prefill }); } this._cancelled = false; this.unsub = companyAutomat.subscribe( this, (state) => { const sorted = sortSuppliers(state.suppliers ?? []); return { status: state.suppliersStatus, error: state.suppliersError ? backendErrorFrom(state.suppliersError) : null, suppliers: sorted, drafts: draftsFrom(sorted), }; }, ['suppliers', 'suppliersStatus', 'suppliersError'], ); } componentDidUpdate(prevProps) { if (prevProps.location !== this.props.location) { const { prefill } = getInitialParams(this.props); if (prefill && prefill !== this.state.newName) { this.setState({ newName: prefill }); } } } componentWillUnmount() { this._cancelled = true; this.unsub?.(); } handleRetry() { void companyAutomat.actions.loadSuppliers().catch((err) => notifyError(err)); } async handleCreate() { const name = this.state.newName.trim(); if (!name || this.state.busyKey != null) return; const { returnTo } = getInitialParams(this.props); this.setState({ busyKey: 'new' }); try { const newSupplier = await companyAutomat.actions.createSupplier({ name }); // Assign the new supplier to the active delivery draft & clear unmatched name deliveryDraftAutomat.actions.setDeliveryEntity(newSupplier.id); deliveryDraftAutomat.actions.setUnmatchedSupplierName(null); if (this._cancelled) return; this.setState({ newName: '', busyKey: null }); enqueueSnackbar(`Lieferant „${newSupplier.name || name}“ angelegt.`, { variant: 'success' }); if (returnTo && this.props.navigate) { this.props.navigate(returnTo); } } catch (err) { if (this._cancelled) return; notifyError(err); this.setState({ busyKey: null }); } } async handleRename(supplier) { const draft = String(this.state.drafts[supplier.id] ?? '').trim(); if (!draft) { this.setState((prev) => ({ drafts: { ...prev.drafts, [supplier.id]: supplier.name }, })); return; } if (draft === supplier.name || this.state.busyKey != null) return; this.setState({ busyKey: supplier.id }); try { await companyAutomat.actions.updateSupplier(supplier.id, { name: draft }); if (this._cancelled) return; this.setState({ busyKey: null }); enqueueSnackbar('Lieferant gespeichert.', { variant: 'success' }); } catch (err) { if (this._cancelled) return; notifyError(err); this.setState((prev) => ({ busyKey: null, drafts: { ...prev.drafts, [supplier.id]: supplier.name }, })); } } async handleDelete(supplier) { if (this.state.busyKey != null) return; if (!window.confirm(`Lieferant „${supplier.name}“ wirklich löschen?`)) { return; } this.setState({ busyKey: supplier.id }); try { await companyAutomat.actions.removeSupplier(supplier.id); if (this._cancelled) return; this.setState({ busyKey: null }); enqueueSnackbar('Lieferant gelöscht.', { variant: 'success' }); } catch (err) { if (this._cancelled) return; notifyError(err); this.setState({ busyKey: null }); } } render() { const { status, error, suppliers, drafts, newName, busyKey } = this.state; const busy = busyKey != null; const { returnTo } = getInitialParams(this.props); if (status === 'loading' || status === 'idle') { return ( ); } if (status === 'failed') { return ( ); } return ( {returnTo ? ( this.props.navigate?.(returnTo)} > Zurück zur Lieferung } > {newName ? `Lieferantenname „${newName}“ aus Dokument übernommen.` : 'Neuen Lieferanten für die Lieferung anlegen.'} ) : null} {suppliers.length === 0 ? ( Noch keine Lieferanten. ) : ( suppliers.map((supplier) => ( this.setState((prev) => ({ drafts: { ...prev.drafts, [supplier.id]: next }, })) } onBlur={() => void this.handleRename(supplier)} onKeyDown={(event) => { if (event.key === 'Enter') { event.preventDefault(); event.target.blur(); } if (event.key === 'Escape') { event.preventDefault(); this.setState((prev) => ({ drafts: { ...prev.drafts, [supplier.id]: supplier.name }, })); event.target.blur(); } }} sx={{ flex: 1 }} /> void this.handleDelete(supplier)} > )) )} this.setState({ newName: next })} onKeyDown={(event) => { if (event.key === 'Enter') { event.preventDefault(); void this.handleCreate(); } }} sx={{ flex: 1 }} /> ); } } export default withNavigate(LieferungLieferantenPage);