sotre
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { useState } from 'react';
|
||||
import { Component } from 'react';
|
||||
import { EditablePanelTile } from '../../panels';
|
||||
import SingleLineTextEdit from '../../SingleLineTextEdit';
|
||||
import { useStorageManager } from '../../../api/storageManagerContext';
|
||||
import { StorageManagerContext } from '../../../api/storageManagerContext';
|
||||
import { requireObject } from '../../../utils/apiShape';
|
||||
import { PANEL_TILE_KEYS } from '../../../theme';
|
||||
import { getCategoryBreadcrumbs } from '../../../store/categorySlice';
|
||||
@@ -45,6 +45,13 @@ const pillsContainerSx = {
|
||||
mt: 'auto',
|
||||
};
|
||||
|
||||
const columnSx = {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flex: 1,
|
||||
alignSelf: 'stretch',
|
||||
};
|
||||
|
||||
function draftDirty(draft, saved) {
|
||||
const nameDiff = draft.name.trim() !== String(saved.name ?? '').trim();
|
||||
const a = draft.categoryIds ?? [];
|
||||
@@ -70,61 +77,156 @@ function buildCategoryLabel(categories, categoryId, rootCategoryId) {
|
||||
return crumbs.map((c) => c.name).join(' | ');
|
||||
}
|
||||
|
||||
function CategoryPills({
|
||||
categoryLabels,
|
||||
categoryIds,
|
||||
categories,
|
||||
rootCategoryId,
|
||||
sku,
|
||||
editMode,
|
||||
onCategoryIdsChange,
|
||||
}) {
|
||||
const [pickerOpen, setPickerOpen] = useState(false);
|
||||
function categoryLabels(categoryIds, categories, rootCategoryId) {
|
||||
return (categoryIds ?? [])
|
||||
.map((id) => ({ id, label: buildCategoryLabel(categories, id, rootCategoryId) }))
|
||||
.filter((c) => c.label);
|
||||
}
|
||||
|
||||
const handleRemoveCategory = (removeId) => {
|
||||
class CategoryPills extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { pickerOpen: false };
|
||||
}
|
||||
|
||||
handleRemoveCategory = (removeId) => {
|
||||
const { categoryIds, onCategoryIdsChange } = this.props;
|
||||
const remaining = categoryIds.filter((id) => String(id) !== String(removeId));
|
||||
onCategoryIdsChange?.(remaining);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box sx={pillsContainerSx}>
|
||||
{categoryLabels.map(({ id, label }) => (
|
||||
<Chip
|
||||
key={id}
|
||||
label={label}
|
||||
size="small"
|
||||
variant="outlined"
|
||||
onDelete={editMode && categoryIds.length > 1 ? () => handleRemoveCategory(id) : undefined}
|
||||
/>
|
||||
))}
|
||||
{editMode && (
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => setPickerOpen(true)}
|
||||
sx={{ ml: 0.5, width: 24, height: 24 }}
|
||||
>
|
||||
<AddIcon fontSize="small" />
|
||||
</IconButton>
|
||||
)}
|
||||
</Box>
|
||||
<CategoryPickerDialog
|
||||
key={sku}
|
||||
open={pickerOpen}
|
||||
onClose={() => setPickerOpen(false)}
|
||||
categories={categories}
|
||||
rootCategoryId={rootCategoryId}
|
||||
currentCategoryIds={categoryIds}
|
||||
onSelect={onCategoryIdsChange}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
handleOpenPicker = () => {
|
||||
this.setState({ pickerOpen: true });
|
||||
};
|
||||
|
||||
handleClosePicker = () => {
|
||||
this.setState({ pickerOpen: false });
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
categoryLabels: labels,
|
||||
categoryIds,
|
||||
categories,
|
||||
rootCategoryId,
|
||||
sku,
|
||||
editMode,
|
||||
onCategoryIdsChange,
|
||||
} = this.props;
|
||||
const { pickerOpen } = this.state;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box sx={pillsContainerSx}>
|
||||
{labels.map(({ id, label }) => (
|
||||
<Chip
|
||||
key={id}
|
||||
label={label}
|
||||
size="small"
|
||||
variant="outlined"
|
||||
onDelete={
|
||||
editMode && categoryIds.length > 1
|
||||
? () => this.handleRemoveCategory(id)
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
))}
|
||||
{editMode ? (
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={this.handleOpenPicker}
|
||||
sx={{ ml: 0.5, width: 24, height: 24 }}
|
||||
>
|
||||
<AddIcon fontSize="small" />
|
||||
</IconButton>
|
||||
) : null}
|
||||
</Box>
|
||||
<CategoryPickerDialog
|
||||
key={sku}
|
||||
open={pickerOpen}
|
||||
onClose={this.handleClosePicker}
|
||||
categories={categories}
|
||||
rootCategoryId={rootCategoryId}
|
||||
currentCategoryIds={categoryIds}
|
||||
onSelect={onCategoryIdsChange}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default function ArticleViewName({ article: articleData, categories, rootCategoryId, onArticleUpdated }) {
|
||||
const store = useStorageManager();
|
||||
class NameDisplay extends Component {
|
||||
render() {
|
||||
const { name, categoryIds, categories, rootCategoryId, sku } = this.props;
|
||||
const labels = categoryLabels(categoryIds, categories, rootCategoryId);
|
||||
return (
|
||||
<Box sx={columnSx}>
|
||||
<Box sx={{ flex: 1, display: 'flex', alignItems: 'center' }}>
|
||||
<SingleLineTextEdit
|
||||
label="Name"
|
||||
value={name}
|
||||
readOnly
|
||||
onChange={() => {}}
|
||||
sx={articleNameFieldHiddenSx}
|
||||
/>
|
||||
</Box>
|
||||
{labels.length > 0 ? (
|
||||
<CategoryPills
|
||||
categoryLabels={labels}
|
||||
categoryIds={categoryIds}
|
||||
categories={categories}
|
||||
rootCategoryId={rootCategoryId}
|
||||
sku={sku}
|
||||
editMode={false}
|
||||
/>
|
||||
) : null}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const handleSave = async (draft) => {
|
||||
class NameEdit extends Component {
|
||||
render() {
|
||||
const {
|
||||
draft,
|
||||
onChange,
|
||||
inputRef,
|
||||
categories,
|
||||
rootCategoryId,
|
||||
sku,
|
||||
} = this.props;
|
||||
const labels = categoryLabels(draft.categoryIds, categories, rootCategoryId);
|
||||
return (
|
||||
<Box sx={columnSx}>
|
||||
<Box sx={{ flex: 1, display: 'flex', alignItems: 'center' }}>
|
||||
<SingleLineTextEdit
|
||||
inputRef={inputRef}
|
||||
label="Name"
|
||||
value={draft.name}
|
||||
onChange={(name) => onChange({ ...draft, name })}
|
||||
sx={articleNameFieldSx}
|
||||
/>
|
||||
</Box>
|
||||
<CategoryPills
|
||||
categoryLabels={labels}
|
||||
categoryIds={draft.categoryIds}
|
||||
categories={categories}
|
||||
rootCategoryId={rootCategoryId}
|
||||
sku={sku}
|
||||
editMode
|
||||
onCategoryIdsChange={(categoryIds) => onChange({ ...draft, categoryIds })}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default class ArticleViewName extends Component {
|
||||
static contextType = StorageManagerContext;
|
||||
|
||||
handleSave = async (draft) => {
|
||||
const { article: articleData, onArticleUpdated } = this.props;
|
||||
const store = this.context;
|
||||
const name = draft.name.trim();
|
||||
const categoryIds = draft.categoryIds ?? [];
|
||||
|
||||
@@ -138,73 +240,38 @@ export default function ArticleViewName({ article: articleData, categories, root
|
||||
onArticleUpdated?.(nameRes.article);
|
||||
};
|
||||
|
||||
const savedCategoryIds = articleData.categoryIds ?? [];
|
||||
render() {
|
||||
const { article: articleData, categories, rootCategoryId } = this.props;
|
||||
const savedCategoryIds = articleData.categoryIds ?? [];
|
||||
const value = { name: articleData.name ?? '', categoryIds: [...savedCategoryIds] };
|
||||
|
||||
return (
|
||||
<EditablePanelTile
|
||||
wide={3}
|
||||
tileKey={PANEL_TILE_KEYS.name}
|
||||
minHeight={0}
|
||||
sx={headerTileSx}
|
||||
value={{ name: articleData.name ?? '', categoryIds: [...savedCategoryIds] }}
|
||||
onSave={handleSave}
|
||||
isDirty={draftDirty}
|
||||
canSave={draftValid}
|
||||
renderDisplay={({ name, categoryIds }) => {
|
||||
const labels = (categoryIds ?? [])
|
||||
.map((id) => ({ id, label: buildCategoryLabel(categories, id, rootCategoryId) }))
|
||||
.filter((c) => c.label);
|
||||
return (
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', flex: 1, alignSelf: 'stretch' }}>
|
||||
<Box sx={{ flex: 1, display: 'flex', alignItems: 'center' }}>
|
||||
<SingleLineTextEdit
|
||||
label="Name"
|
||||
value={name}
|
||||
readOnly
|
||||
onChange={() => {}}
|
||||
sx={articleNameFieldHiddenSx}
|
||||
/>
|
||||
</Box>
|
||||
{labels.length > 0 && (
|
||||
<CategoryPills
|
||||
categoryLabels={labels}
|
||||
categoryIds={categoryIds}
|
||||
categories={categories}
|
||||
rootCategoryId={rootCategoryId}
|
||||
sku={articleData.sku}
|
||||
editMode={false}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}}
|
||||
renderEdit={(draft, onChange, inputRef) => {
|
||||
const labels = (draft.categoryIds ?? [])
|
||||
.map((id) => ({ id, label: buildCategoryLabel(categories, id, rootCategoryId) }))
|
||||
.filter((c) => c.label);
|
||||
return (
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', flex: 1, alignSelf: 'stretch' }}>
|
||||
<Box sx={{ flex: 1, display: 'flex', alignItems: 'center' }}>
|
||||
<SingleLineTextEdit
|
||||
inputRef={inputRef}
|
||||
label="Name"
|
||||
value={draft.name}
|
||||
onChange={(name) => onChange({ ...draft, name })}
|
||||
sx={articleNameFieldSx}
|
||||
/>
|
||||
</Box>
|
||||
<CategoryPills
|
||||
categoryLabels={labels}
|
||||
categoryIds={draft.categoryIds}
|
||||
categories={categories}
|
||||
rootCategoryId={rootCategoryId}
|
||||
sku={articleData.sku}
|
||||
editMode={true}
|
||||
onCategoryIdsChange={(categoryIds) => onChange({ ...draft, categoryIds })}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
return (
|
||||
<EditablePanelTile
|
||||
wide={3}
|
||||
tileKey={PANEL_TILE_KEYS.name}
|
||||
minHeight={0}
|
||||
sx={headerTileSx}
|
||||
value={value}
|
||||
onSave={this.handleSave}
|
||||
isDirty={draftDirty}
|
||||
canSave={draftValid}
|
||||
display={
|
||||
<NameDisplay
|
||||
name={value.name}
|
||||
categoryIds={value.categoryIds}
|
||||
categories={categories}
|
||||
rootCategoryId={rootCategoryId}
|
||||
sku={articleData.sku}
|
||||
/>
|
||||
}
|
||||
edit={
|
||||
<NameEdit
|
||||
categories={categories}
|
||||
rootCategoryId={rootCategoryId}
|
||||
sku={articleData.sku}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user