diff --git a/src/automats/deliveryEditorAutomat.js b/src/automats/deliveryEditorAutomat.js index ff4621e..71914c9 100644 --- a/src/automats/deliveryEditorAutomat.js +++ b/src/automats/deliveryEditorAutomat.js @@ -375,6 +375,24 @@ export function createDeliveryEditorAutomat(options = {}) { automat.actions.removeLineitem(payload); }, + /** + * Moves a lineitem from one index to another. + * @param {number} fromIndex Source index. + * @param {number} toIndex Target index. + * @returns {void} + */ + moveLineitem(fromIndex, toIndex) { + const currentLines = automat.state.lines ?? []; + if (fromIndex < 0 || fromIndex >= currentLines.length || toIndex < 0 || toIndex >= currentLines.length) return; + const nextLines = [...currentLines]; + const [item] = nextLines.splice(fromIndex, 1); + nextLines.splice(toIndex, 0, item); + automat.setState({ + lines: nextLines, + lineitems: nextLines, + }); + }, + /** Clears all lines from the editor. @returns {void} */ clearLineitems() { automat.setState({ diff --git a/src/automats/manifestDraftAutomat.js b/src/automats/manifestDraftAutomat.js index c5feea7..743f611 100644 --- a/src/automats/manifestDraftAutomat.js +++ b/src/automats/manifestDraftAutomat.js @@ -284,6 +284,17 @@ export function getManifestDirtyDetails(current, baseline) { if (curLines.length !== baseLines.length) { fields.lines = true; + } else { + for (let i = 0; i < curLines.length; i++) { + const cl = curLines[i]; + const bl = baseLines[i]; + const clIdentifier = cl?.key ?? (cl?.id != null ? `id-${cl.id}` : null); + const blIdentifier = bl?.key ?? (bl?.id != null ? `id-${bl.id}` : null); + if (clIdentifier && blIdentifier && clIdentifier !== blIdentifier) { + fields.lines = true; + break; + } + } } for (let i = 0; i < curLines.length; i++) { diff --git a/src/components/manifest/ManifestForm.jsx b/src/components/manifest/ManifestForm.jsx index fb0c8b9..fce6a22 100644 --- a/src/components/manifest/ManifestForm.jsx +++ b/src/components/manifest/ManifestForm.jsx @@ -96,6 +96,7 @@ class ManifestForm extends PureComponent { this.handleCloseArticleSearch = this.handleCloseArticleSearch.bind(this); this.handleAssignArticleForLine = this.handleAssignArticleForLine.bind(this); this.handleRemoveLine = this.handleRemoveLine.bind(this); + this.handleMoveLine = this.handleMoveLine.bind(this); this.handleRemoveAllLines = this.handleRemoveAllLines.bind(this); this.handleSave = this.handleSave.bind(this); this.handlePdfUpload = this.handlePdfUpload.bind(this); @@ -701,6 +702,19 @@ class ManifestForm extends PureComponent { }); } + handleMoveLine(key, delta) { + this.setState((prev) => { + const fromIndex = prev.lines.findIndex((l) => l.key === key); + if (fromIndex < 0) return null; + const toIndex = fromIndex + delta; + if (toIndex < 0 || toIndex >= prev.lines.length) return null; + const next = [...prev.lines]; + const [item] = next.splice(fromIndex, 1); + next.splice(toIndex, 0, item); + return { lines: next, isDirty: true }; + }); + } + handleRemoveAllLines() { this.setState({ lines: [] }); } @@ -998,6 +1012,7 @@ class ManifestForm extends PureComponent { onAddArticleLine={this.handleAddArticleLine} onSearchArticle={this.handleOpenArticleSearchForLine} onRemoveLine={this.handleRemoveLine} + onMoveLine={this.handleMoveLine} onRemoveAllLines={this.handleRemoveAllLines} onSave={() => void this.handleSave()} /> diff --git a/src/components/manifest/ManifestFormLine.jsx b/src/components/manifest/ManifestFormLine.jsx index 57fee87..decb278 100644 --- a/src/components/manifest/ManifestFormLine.jsx +++ b/src/components/manifest/ManifestFormLine.jsx @@ -11,6 +11,8 @@ import TextField from '@mui/material/TextField'; import Tooltip from '@mui/material/Tooltip'; import Typography from '@mui/material/Typography'; import DeleteOutlinedIcon from '@mui/icons-material/DeleteOutlined'; +import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward'; +import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward'; import HelpOutlineIcon from '@mui/icons-material/HelpOutlineOutlined'; import Inventory2OutlinedIcon from '@mui/icons-material/Inventory2Outlined'; import CheckCircleOutlineOutlinedIcon from '@mui/icons-material/CheckCircleOutlineOutlined'; @@ -40,6 +42,8 @@ class ManifestFormLine extends PureComponent { fallbackSuppliers: [], }; this.handleRemove = this.handleRemove.bind(this); + this.handleMoveUp = this.handleMoveUp.bind(this); + this.handleMoveDown = this.handleMoveDown.bind(this); this.handleBlur = this.handleBlur.bind(this); this.handleCreateArticle = this.handleCreateArticle.bind(this); this.handleSearchArticle = this.handleSearchArticle.bind(this); @@ -121,6 +125,14 @@ class ManifestFormLine extends PureComponent { this.props.onRemove(this.props.line.key); } + handleMoveUp() { + this.props.onMove?.(this.props.line.key, -1); + } + + handleMoveDown() { + this.props.onMove?.(this.props.line.key, 1); + } + handleBlur(e, columnId) { if (columnId === 'sku' && this.props.onSkuBlur) { this.props.onSkuBlur(this.props.line.key, e.target.value); @@ -971,15 +983,62 @@ class ManifestFormLine extends PureComponent { {this.renderField(col, colIdx, columns.length)} ))} - - - - + + + {this.props.onMove && ( + <> + + + + + + + + + + + + + + + + )} + + + + + + + + diff --git a/src/components/manifest/ManifestLines.jsx b/src/components/manifest/ManifestLines.jsx index c13c42d..7b2c1a2 100644 --- a/src/components/manifest/ManifestLines.jsx +++ b/src/components/manifest/ManifestLines.jsx @@ -28,6 +28,7 @@ class ManifestLines extends PureComponent { onLineChange, onAddLine, onRemoveLine, + onMoveLine, onRemoveAllLines, columns = LINE_COLUMNS, supplierArticlesMap = {}, @@ -98,7 +99,7 @@ class ManifestLines extends PureComponent { {col.label} ))} - + @@ -138,6 +139,9 @@ class ManifestLines extends PureComponent { supplierArticleBusy={Boolean(busySupplierArticleLineKeys[lineKey])} onChange={onLineChange} onRemove={onRemoveLine} + onMove={onMoveLine} + isFirstLine={index === 0} + isLastLine={index === lines.length - 1} onSkuBlur={onSkuBlur} onCreateArticle={onCreateArticle} onSearchArticle={onSearchArticle} diff --git a/src/routes/lieferung/neu.jsx b/src/routes/lieferung/neu.jsx index f5d7a41..535aa03 100644 --- a/src/routes/lieferung/neu.jsx +++ b/src/routes/lieferung/neu.jsx @@ -99,6 +99,7 @@ class LieferungNeuPage extends PureComponent { this.handleAddLine = this.handleAddLine.bind(this); this.handleAddArticleLine = this.handleAddArticleLine.bind(this); this.handleRemoveLine = this.handleRemoveLine.bind(this); + this.handleMoveLine = this.handleMoveLine.bind(this); this.handleRemoveAllLines = this.handleRemoveAllLines.bind(this); this.handleSave = this.handleSave.bind(this); this.handlePdfUpload = this.handlePdfUpload.bind(this); @@ -541,6 +542,13 @@ class LieferungNeuPage extends PureComponent { this.automat.actions.removeLineitem(index >= 0 ? index : key); } + handleMoveLine(key, delta) { + const fromIndex = this.findLineIndex(key); + if (fromIndex < 0) return; + const toIndex = fromIndex + delta; + this.automat.actions.moveLineitem(fromIndex, toIndex); + } + handleRemoveAllLines() { this.automat.actions.clearLineitems(); } @@ -1340,6 +1348,7 @@ class LieferungNeuPage extends PureComponent { onAddLine={this.handleAddLine} onAddArticleLine={this.handleAddArticleLine} onRemoveLine={this.handleRemoveLine} + onMoveLine={this.handleMoveLine} onRemoveAllLines={this.handleRemoveAllLines} onSave={() => void this.handleSave()} onCreateArticle={this.handleCreateArticleForLine}