Show article history in a table via GET /article/{sku}/history.

This commit is contained in:
seb
2026-08-05 09:35:47 +02:00
parent debd246012
commit 60e6858fd5
10 changed files with 297 additions and 96 deletions

View File

@@ -0,0 +1,12 @@
import { Component } from 'react';
import Typography from '@mui/material/Typography';
export default class ArticleAttributesTab extends Component {
render() {
return (
<Typography variant="body2" color="text.secondary">
Attribute und Merkmale
</Typography>
);
}
}

View File

@@ -0,0 +1,12 @@
import { Component } from 'react';
import Typography from '@mui/material/Typography';
export default class ArticleCompoundTab extends Component {
render() {
return (
<Typography variant="body2" color="text.secondary">
Compound Editor
</Typography>
);
}
}

View File

@@ -0,0 +1,71 @@
import { Component } from 'react';
import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody';
import TableCell from '@mui/material/TableCell';
import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import { StorageManagerContext } from '../../../api/storageManagerContext';
import { notifyError } from '../../../utils/notify';
function formatDate(iso) {
return new Date(iso).toLocaleString('de-DE');
}
export default class ArticleHistoryTab extends Component {
static contextType = StorageManagerContext;
constructor(props) {
super(props);
this.state = { history: null };
}
componentDidMount() {
void this.context.article
.history(this.props.sku)
.then((res) => this.setState({ history: res.history }))
.catch(notifyError);
}
render() {
const { history } = this.state;
if (history == null) return null;
return (
<Table
size="small"
sx={{
'& td': { borderBottom: 'none', bgcolor: 'transparent' },
'& th': { bgcolor: 'transparent' },
}}
>
<TableHead>
<TableRow>
<TableCell
sx={{
borderBottom: '1px solid rgba(0, 0, 0, 0.35)',
width: '1%',
whiteSpace: 'nowrap',
}}
>
Datum
</TableCell>
<TableCell
sx={{ borderBottom: '1px solid rgba(0, 0, 0, 0.35)' }}
/>
</TableRow>
</TableHead>
<TableBody>
{history.map((entry) => (
<TableRow key={entry.id}>
<TableCell
sx={{ verticalAlign: 'top', whiteSpace: 'nowrap', width: '1%' }}
>
{formatDate(entry.date)}
</TableCell>
<TableCell sx={{ whiteSpace: 'pre-wrap' }}>{entry.text}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
);
}
}

View File

@@ -0,0 +1,12 @@
import { Component } from 'react';
import Typography from '@mui/material/Typography';
export default class ArticleLabelTab extends Component {
render() {
return (
<Typography variant="body2" color="text.secondary">
Label
</Typography>
);
}
}

View File

@@ -0,0 +1,33 @@
import { Component } from 'react';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
function PanelValue({ label, children }) {
if (children == null || children === '') return null;
return (
<Box sx={{ mb: 1 }}>
{label ? (
<Typography variant="caption" color="text.secondary" display="block">
{label}
</Typography>
) : null}
<Typography variant="body2" component="div">
{children}
</Typography>
</Box>
);
}
export default class ArticleSeoTab extends Component {
render() {
const { article: articleData } = this.props;
return (
<>
<PanelValue label="Slug">{articleData.slug}</PanelValue>
<PanelValue label="SEO-Titel">{articleData.seoTitle}</PanelValue>
<PanelValue label="SEO-Beschreibung">{articleData.seoDescription}</PanelValue>
<PanelValue label="Beschreibung">{articleData.description}</PanelValue>
</>
);
}
}

View File

@@ -0,0 +1,51 @@
import { Component } from 'react';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import DeleteIcon from '@mui/icons-material/Delete';
import { StorageManagerContext } from '../../../api/storageManagerContext';
import { notifyError } from '../../../utils/notify';
export default class ArticleSettingsTab extends Component {
static contextType = StorageManagerContext;
constructor(props) {
super(props);
this.state = { busy: false };
this.handleDeleteArticle = this.handleDeleteArticle.bind(this);
}
async handleDeleteArticle() {
const { article: articleData, onArticleDeleted } = this.props;
if (this.state.busy) return;
if (!window.confirm(`Artikel „${articleData.name ?? articleData.sku}“ wirklich löschen?`)) {
return;
}
this.setState({ busy: true });
try {
await this.context.article.delete(articleData.sku);
onArticleDeleted?.(articleData);
} catch (err) {
notifyError(err);
this.setState({ busy: false });
}
}
render() {
const { busy } = this.state;
return (
<Box>
<Button
type="button"
color="error"
variant="contained"
startIcon={<DeleteIcon />}
disabled={busy}
onClick={() => void this.handleDeleteArticle()}
>
Artikel löschen
</Button>
</Box>
);
}
}

View File

@@ -0,0 +1,12 @@
import { Component } from 'react';
import Typography from '@mui/material/Typography';
export default class ArticleSupplierTab extends Component {
render() {
return (
<Typography variant="body2" color="text.secondary">
Supplier
</Typography>
);
}
}