import { PureComponent, createRef } from 'react'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; import CircularProgress from '@mui/material/CircularProgress'; import List from '@mui/material/List'; import Typography from '@mui/material/Typography'; import Sortable from 'sortablejs'; 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 companyAutomat from '../../automats/companyAutomat'; import { CarrierItem } from '../../components/carrier'; import { GREEN_PANEL } from '../../colors'; class VersandartenSearchField extends PureComponent { constructor(props) { super(props); const initial = companyAutomat.state.carrierFilter || ''; this.state = { isAdmin: companyAutomat.state.current?.role === 'admin', input: initial, submitting: false, }; this.handleSubmit = this.handleSubmit.bind(this); } componentDidMount() { this.unsub = companyAutomat.subscribe(this, (state) => ({ isAdmin: state.current?.role === 'admin', input: state.carrierFilter, })); } componentWillUnmount() { this.unsub?.(); } onInputChange(input) { this.setState({ input }); companyAutomat.actions.filterCarriers(input); } async handleSubmit() { if (!this.state.isAdmin) return; const name = this.state.input.trim(); if (!name || this.state.submitting) return; this.setState({ submitting: true }); try { const row = await companyAutomat.actions.createCarrier({ name, hasPackages: true, isReverse: false, }); enqueueSnackbar(`Versandart „${row.name || name}“ angelegt.`, { variant: 'success' }); this.setState({ input: '', submitting: false }); companyAutomat.actions.filterCarriers(''); } catch (err) { this.setState({ submitting: false }); notifyError(err); } } render() { const { inputRef } = this.props; const { isAdmin, input, submitting } = this.state; const canCreate = isAdmin && Boolean(input.trim()) && !submitting; return ( this.onInputChange(next)} onKeyDown={(event) => { if (event.key === 'Enter') { event.preventDefault(); void this.handleSubmit(); } }} /> ); } } class AuftragVersandartenPage extends PureComponent { constructor(props) { super(props); const initialCarriers = companyAutomat.state.filteredCarriers ?? companyAutomat.state.carriers ?? []; this.state = { isAdmin: companyAutomat.state.current?.role === 'admin', status: companyAutomat.state.carriersStatus === 'ready' ? 'ready' : 'loading', error: companyAutomat.state.carriersError ? backendErrorFrom(companyAutomat.state.carriersError) : null, carriers: initialCarriers, filter: companyAutomat.state.carrierFilter ?? '', }; this.searchRef = createRef(); this.listRef = createRef(); this._sortable = null; this._cancelled = false; this.handleRetry = this.handleRetry.bind(this); this._initSortable = this._initSortable.bind(this); this._destroySortable = this._destroySortable.bind(this); } componentDidMount() { this._cancelled = false; this.unsub = companyAutomat.subscribe(this, (state) => ({ isAdmin: state.current?.role === 'admin', carriers: state.filteredCarriers ?? state.carriers, filter: state.carrierFilter ?? '', status: state.carriersStatus, error: state.carriersError ? backendErrorFrom(state.carriersError) : null, })); if (this.state.status === 'ready') { this._initSortable(); } } componentDidUpdate(prevProps, prevState) { if ( prevState.isAdmin !== this.state.isAdmin || prevState.status !== this.state.status || prevState.carriers !== this.state.carriers || prevState.filter !== this.state.filter ) { if (this.state.status === 'ready') { this._initSortable(); } } } componentWillUnmount() { this._cancelled = true; this.unsub?.(); this._destroySortable(); } _initSortable() { this._destroySortable(); const el = this.listRef.current; if (!el) return; const isFiltered = Boolean(this.state.filter?.trim()); this._sortable = Sortable.create(el, { handle: '.drag-handle', animation: 150, ghostClass: 'sortable-ghost', chosenClass: 'sortable-chosen', disabled: !this.state.isAdmin || isFiltered, onEnd: (evt) => { const { oldIndex, newIndex } = evt; if (oldIndex === newIndex) return; this.setState( (prev) => { const next = [...prev.carriers]; const [moved] = next.splice(oldIndex, 1); next.splice(newIndex, 0, moved); return { carriers: next }; }, () => { const ids = this.state.carriers.map((c) => c.id); void this._persistOrder(ids); } ); }, }); } _destroySortable() { this._sortable?.destroy(); this._sortable = null; } async _persistOrder(carrierIds) { try { await companyAutomat.actions.reorderCarriers(carrierIds); } catch (err) { if (this._cancelled) return; notifyError(err); void companyAutomat.actions.reloadCarriers(); } } handleRetry() { void companyAutomat.actions.loadCarriers().catch((err) => notifyError(err)); } render() { const { isAdmin, status, error, carriers, filter } = this.state; if (status === 'loading' || status === 'idle') { return ( ); } if (status === 'failed') { return ; } const trimmedFilter = filter?.trim() ?? ''; return ( <> {carriers.length === 0 ? ( {trimmedFilter ? `Keine Treffer für „${trimmedFilter}“.` : 'Noch keine Versandarten angelegt.'} ) : ( carriers.map((c) => ( )) )} ); } } export { CarrierItem, VersandartenSearchField }; export default withNavigate(AuftragVersandartenPage);