233 lines
7.1 KiB
JavaScript
233 lines
7.1 KiB
JavaScript
import { PureComponent } from 'react';
|
|
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 Stack from '@mui/material/Stack';
|
|
import Table from '@mui/material/Table';
|
|
import TableBody from '@mui/material/TableBody';
|
|
import TableCell from '@mui/material/TableCell';
|
|
import TableContainer from '@mui/material/TableContainer';
|
|
import TableHead from '@mui/material/TableHead';
|
|
import TableRow from '@mui/material/TableRow';
|
|
import Typography from '@mui/material/Typography';
|
|
import DeleteOutlinedIcon from '@mui/icons-material/DeleteOutlined';
|
|
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
|
|
import { enqueueSnackbar } from 'notistack';
|
|
import BackendError from '../BackendError';
|
|
import { PanelTile, PanelTileGrid } from '../panels';
|
|
import { StorageManagerContext } from '../../api/storageManagerContext';
|
|
import { backendErrorFrom } from '../../utils/backendError';
|
|
import { notifyError } from '../../utils/notify';
|
|
import withNavigate from '../withNavigate';
|
|
import {
|
|
formatDate,
|
|
loadParties,
|
|
manifestConfig,
|
|
numberDisplay,
|
|
partyDisplayName,
|
|
sortDocs,
|
|
} from './manifestConfig';
|
|
|
|
class ManifestList extends PureComponent {
|
|
static contextType = StorageManagerContext;
|
|
|
|
constructor(props) {
|
|
super(props);
|
|
this.cfg = manifestConfig(props.kind);
|
|
this.state = {
|
|
status: 'loading',
|
|
error: null,
|
|
docs: [],
|
|
partiesById: {},
|
|
busyId: null,
|
|
};
|
|
this._cancelled = false;
|
|
this.handleRetry = this.handleRetry.bind(this);
|
|
this.handleCreate = this.handleCreate.bind(this);
|
|
}
|
|
|
|
componentDidMount() {
|
|
this._cancelled = false;
|
|
void this.load();
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
this._cancelled = true;
|
|
}
|
|
|
|
async load() {
|
|
const cfg = this.cfg;
|
|
this.setState({ status: 'loading', error: null });
|
|
try {
|
|
const [docs, parties] = await Promise.all([
|
|
cfg.listPromise(this.context),
|
|
loadParties(this.props.kind, this.context),
|
|
]);
|
|
if (this._cancelled) return;
|
|
this.setState({
|
|
status: 'ready',
|
|
docs: sortDocs(docs),
|
|
partiesById: cfg.partiesByIdFromList(parties),
|
|
});
|
|
} catch (err) {
|
|
if (this._cancelled) return;
|
|
this.setState({ status: 'failed', error: backendErrorFrom(err) });
|
|
}
|
|
}
|
|
|
|
handleRetry() {
|
|
void this.load();
|
|
}
|
|
|
|
/** Same path as AppShell sub-nav: live useNavigate via withNavigate. */
|
|
go(to) {
|
|
const { navigate } = this.props;
|
|
if (typeof navigate === 'function') {
|
|
navigate(to);
|
|
return;
|
|
}
|
|
window.location.assign(to);
|
|
}
|
|
|
|
handleCreate() {
|
|
this.go(this.cfg.createPath);
|
|
}
|
|
|
|
handleEdit(doc) {
|
|
this.go(this.cfg.editPath(doc.id));
|
|
}
|
|
|
|
async handleDelete(doc) {
|
|
if (this.state.busyId != null) return;
|
|
if (!window.confirm(this.cfg.confirmDelete(doc.number))) return;
|
|
|
|
const cfg = this.cfg;
|
|
this.setState({ busyId: doc.id });
|
|
try {
|
|
await this.context[cfg.api].delete(doc.id);
|
|
const list = await this.context[cfg.updateCache]();
|
|
if (this._cancelled) return;
|
|
this.setState({
|
|
docs: sortDocs(list),
|
|
busyId: null,
|
|
});
|
|
enqueueSnackbar(cfg.deletedMsg, { variant: 'success' });
|
|
} catch (err) {
|
|
if (this._cancelled) return;
|
|
notifyError(err);
|
|
this.setState({ busyId: null });
|
|
}
|
|
}
|
|
|
|
partyName(partyId) {
|
|
if (partyId == null) return null;
|
|
return partyDisplayName(
|
|
this.props.kind,
|
|
this.state.partiesById[partyId],
|
|
);
|
|
}
|
|
|
|
render() {
|
|
const { status, error, docs, busyId } = this.state;
|
|
const cfg = this.cfg;
|
|
const partyField = cfg.partyField;
|
|
|
|
if (status === 'loading') {
|
|
return (
|
|
<Box sx={{ display: 'flex', justifyContent: 'center', py: 6 }}>
|
|
<CircularProgress />
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
if (status === 'failed') {
|
|
return <BackendError error={error} onRetry={this.handleRetry} />;
|
|
}
|
|
|
|
return (
|
|
<PanelTileGrid>
|
|
<PanelTile wide={3} minHeight={240} color={cfg.listColor}>
|
|
<Stack spacing={2}>
|
|
<Box sx={{ display: 'flex', justifyContent: 'flex-end' }}>
|
|
<Button
|
|
type="button"
|
|
variant="contained"
|
|
size="small"
|
|
onClick={this.handleCreate}
|
|
>
|
|
{cfg.createButton}
|
|
</Button>
|
|
</Box>
|
|
|
|
{docs.length === 0 ? (
|
|
<Typography variant="body2" color="text.secondary">
|
|
{cfg.emptyList}
|
|
</Typography>
|
|
) : (
|
|
<TableContainer>
|
|
<Table size="small">
|
|
<TableHead>
|
|
<TableRow>
|
|
<TableCell>Nummer</TableCell>
|
|
{cfg.externalNumber ? (
|
|
<TableCell>Referenz</TableCell>
|
|
) : null}
|
|
<TableCell>{cfg.dateLabel}</TableCell>
|
|
<TableCell>{cfg.partyLabel}</TableCell>
|
|
<TableCell padding="checkbox" />
|
|
</TableRow>
|
|
</TableHead>
|
|
<TableBody>
|
|
{docs.map((row) => (
|
|
<TableRow
|
|
key={row.id}
|
|
hover
|
|
sx={{ cursor: 'pointer' }}
|
|
onClick={() => this.handleEdit(row)}
|
|
>
|
|
<TableCell>{row.number}</TableCell>
|
|
{cfg.externalNumber ? (
|
|
<TableCell>
|
|
{numberDisplay(row.externalNumber)}
|
|
</TableCell>
|
|
) : null}
|
|
<TableCell>{formatDate(row.date)}</TableCell>
|
|
<TableCell>{this.partyName(row[partyField])}</TableCell>
|
|
<TableCell
|
|
padding="checkbox"
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
<IconButton
|
|
type="button"
|
|
size="small"
|
|
aria-label={`„${row.number}“ bearbeiten`}
|
|
onClick={() => this.handleEdit(row)}
|
|
>
|
|
<EditOutlinedIcon fontSize="small" />
|
|
</IconButton>
|
|
<IconButton
|
|
type="button"
|
|
size="small"
|
|
aria-label={`„${row.number}“ löschen`}
|
|
disabled={busyId != null}
|
|
onClick={() => void this.handleDelete(row)}
|
|
>
|
|
<DeleteOutlinedIcon fontSize="small" />
|
|
</IconButton>
|
|
</TableCell>
|
|
</TableRow>
|
|
))}
|
|
</TableBody>
|
|
</Table>
|
|
</TableContainer>
|
|
)}
|
|
</Stack>
|
|
</PanelTile>
|
|
</PanelTileGrid>
|
|
);
|
|
}
|
|
}
|
|
|
|
export default withNavigate(ManifestList);
|