Merge pull request #713 from mirumee/fix/no-warehouses-in-variant-creator

Add no warehouses info when working with stock quantities
This commit is contained in:
Dawid Tarasiuk 2020-10-08 12:42:10 +02:00 committed by GitHub
commit 46cadf0c48
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 5374 additions and 279 deletions

View file

@ -40,6 +40,8 @@ All notable, unreleased changes to this project will be documented in this file.
- Update schema with PositiveDecimal type - #695 by @AlicjaSzu - Update schema with PositiveDecimal type - #695 by @AlicjaSzu
- Add error info when fetching taxes - #701 by @dominik-zeglen - Add error info when fetching taxes - #701 by @dominik-zeglen
- Fix return to previous page on screen size change - #710 by @orzechdev - Fix return to previous page on screen size change - #710 by @orzechdev
- Fix updating order details on address change #711 - by @orzechdev
- Add no warehouses info when working with stock quantities #713 - by @orzechdev
- Add variants reordering possibility - #716 by @orzechdev - Add variants reordering possibility - #716 by @orzechdev
- Fix avatar change button - #719 by @orzechdev - Fix avatar change button - #719 by @orzechdev
- Add slug field to product, collection, category & page details (update and create) - #720 by @mmarkusik - Add slug field to product, collection, category & page details (update and create) - #720 by @mmarkusik
@ -104,7 +106,6 @@ All notable, unreleased changes to this project will be documented in this file.
- Update product stock management to newest design - #515 by @dominik-zeglen - Update product stock management to newest design - #515 by @dominik-zeglen
- Handle untracked products - #523 by @dominik-zeglen - Handle untracked products - #523 by @dominik-zeglen
- Display correct error if there were no graphql errors - #525 by @dominik-zeglen - Display correct error if there were no graphql errors - #525 by @dominik-zeglen
- Fix updating order details on address change #711 - by @orzechdev
## 2.0.0 ## 2.0.0

View file

@ -302,6 +302,10 @@
"context": "variant stock, header", "context": "variant stock, header",
"string": "Stock" "string": "Stock"
}, },
"productVariantCreatorWarehouseSectionDescription": {
"context": "no warehouses info",
"string": "There are no warehouses set up for your store. You can configure variants without providing stock quantities."
},
"productVariantCreatorWarehouseSectionHeader": { "productVariantCreatorWarehouseSectionHeader": {
"context": "header", "context": "header",
"string": "Warehouses" "string": "Warehouses"
@ -310,6 +314,14 @@
"context": "optional field", "context": "optional field",
"string": "Optional" "string": "Optional"
}, },
"productVariantWarehouseSectionDescription": {
"context": "no warehouses info",
"string": "There are no warehouses set up for your store. To add stock quantity to the variant please <a>configure a warehouse</a>"
},
"productWarehouseSectionDescription": {
"context": "no warehouses info",
"string": "There are no warehouses set up for your store. To add stock quantity to the product please <a>configure a warehouse</a>"
},
"saleDetailsPageCategoriesQuantity": { "saleDetailsPageCategoriesQuantity": {
"context": "number of categories", "context": "number of categories",
"string": "Categories ({quantity})" "string": "Categories ({quantity})"

View file

@ -101,6 +101,7 @@ interface ProductCreatePageProps {
fetchCategories: (data: string) => void; fetchCategories: (data: string) => void;
fetchCollections: (data: string) => void; fetchCollections: (data: string) => void;
fetchProductTypes: (data: string) => void; fetchProductTypes: (data: string) => void;
onWarehouseConfigure: () => void;
onBack?(); onBack?();
onSubmit?(data: ProductCreatePageSubmitData); onSubmit?(data: ProductCreatePageSubmitData);
} }
@ -124,7 +125,8 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
onBack, onBack,
fetchProductTypes, fetchProductTypes,
weightUnit, weightUnit,
onSubmit onSubmit,
onWarehouseConfigure
}: ProductCreatePageProps) => { }: ProductCreatePageProps) => {
const intl = useIntl(); const intl = useIntl();
const localizeDate = useDateLocalize(); const localizeDate = useDateLocalize();
@ -297,6 +299,7 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
<ProductStocks <ProductStocks
data={data} data={data}
disabled={disabled} disabled={disabled}
hasVariants={false}
onFormDataChange={change} onFormDataChange={change}
errors={errors} errors={errors}
stocks={stocks} stocks={stocks}
@ -320,6 +323,7 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
triggerChange(); triggerChange();
removeStock(id); removeStock(id);
}} }}
onWarehouseConfigure={onWarehouseConfigure}
/> />
<CardSpacer /> <CardSpacer />
</> </>

View file

@ -21,6 +21,7 @@ import CardTitle from "@saleor/components/CardTitle";
import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
import FormSpacer from "@saleor/components/FormSpacer"; import FormSpacer from "@saleor/components/FormSpacer";
import Hr from "@saleor/components/Hr"; import Hr from "@saleor/components/Hr";
import Link from "@saleor/components/Link";
import { ProductErrorFragment } from "@saleor/fragments/types/ProductErrorFragment"; import { ProductErrorFragment } from "@saleor/fragments/types/ProductErrorFragment";
import { WarehouseFragment } from "@saleor/fragments/types/WarehouseFragment"; import { WarehouseFragment } from "@saleor/fragments/types/WarehouseFragment";
import { FormChange } from "@saleor/hooks/useForm"; import { FormChange } from "@saleor/hooks/useForm";
@ -41,12 +42,14 @@ export interface ProductStocksProps {
data: ProductStockFormData; data: ProductStockFormData;
disabled: boolean; disabled: boolean;
errors: ProductErrorFragment[]; errors: ProductErrorFragment[];
hasVariants: boolean;
stocks: ProductStockInput[]; stocks: ProductStockInput[];
warehouses: WarehouseFragment[]; warehouses: WarehouseFragment[];
onChange: FormsetChange; onChange: FormsetChange;
onFormDataChange: FormChange; onFormDataChange: FormChange;
onWarehouseStockAdd: (warehouseId: string) => void; onWarehouseStockAdd: (warehouseId: string) => void;
onWarehouseStockDelete: (warehouseId: string) => void; onWarehouseStockDelete: (warehouseId: string) => void;
onWarehouseConfigure: () => void;
} }
const useStyles = makeStyles( const useStyles = makeStyles(
@ -75,6 +78,9 @@ const useStyles = makeStyles(
marginBottom: theme.spacing(2) marginBottom: theme.spacing(2)
} }
}, },
noWarehouseInfo: {
marginTop: theme.spacing()
},
paper: { paper: {
padding: theme.spacing(2) padding: theme.spacing(2)
}, },
@ -105,13 +111,15 @@ const useStyles = makeStyles(
const ProductStocks: React.FC<ProductStocksProps> = ({ const ProductStocks: React.FC<ProductStocksProps> = ({
data, data,
disabled, disabled,
hasVariants,
errors, errors,
stocks, stocks,
warehouses, warehouses,
onChange, onChange,
onFormDataChange, onFormDataChange,
onWarehouseStockAdd, onWarehouseStockAdd,
onWarehouseStockDelete onWarehouseStockDelete,
onWarehouseConfigure
}) => { }) => {
const classes = useStyles({}); const classes = useStyles({});
const intl = useIntl(); const intl = useIntl();
@ -178,108 +186,143 @@ const ProductStocks: React.FC<ProductStocksProps> = ({
</span> </span>
</div> </div>
</Typography> </Typography>
</CardContent> {!warehouses.length && (
<Table> <Typography color="textSecondary" className={classes.noWarehouseInfo}>
<TableHead> {hasVariants ? (
<TableRow> <>
<TableCell className={classes.colName}> <FormattedMessage
<FormattedMessage defaultMessage="There are no warehouses set up for your store. To add stock quantity to the variant please <a>configure a warehouse</a>"
defaultMessage="Warehouse Name" description="no warehouses info"
description="tabel column header" id="productVariantWarehouseSectionDescription"
/> values={{
</TableCell> a: chunks => (
<TableCell className={classes.colQuantity}> <Link onClick={onWarehouseConfigure}>{chunks}</Link>
<FormattedMessage )
defaultMessage="Quantity Available"
description="tabel column header"
/>
</TableCell>
<TableCell className={classes.colAction} />
</TableRow>
</TableHead>
<TableBody>
{renderCollection(stocks, stock => (
<TableRow key={stock.id}>
<TableCell className={classes.colName}>{stock.label}</TableCell>
<TableCell className={classes.colQuantity}>
<TextField
className={classes.inputComponent}
disabled={disabled}
fullWidth
inputProps={{
className: classes.input,
min: 0,
type: "number"
}} }}
onChange={event => onChange(stock.id, event.target.value)} />
value={stock.value} </>
) : (
<>
<FormattedMessage
defaultMessage="There are no warehouses set up for your store. To add stock quantity to the product please <a>configure a warehouse</a>"
description="no warehouses info"
id="productWarehouseSectionDescription"
values={{
a: chunks => (
<Link onClick={onWarehouseConfigure}>{chunks}</Link>
)
}}
/>
</>
)}
</Typography>
)}
</CardContent>
{warehouses.length > 0 && (
<Table>
<TableHead>
<TableRow>
<TableCell className={classes.colName}>
<FormattedMessage
defaultMessage="Warehouse Name"
description="tabel column header"
/> />
</TableCell> </TableCell>
<TableCell className={classes.colAction}> <TableCell className={classes.colQuantity}>
<IconButton <FormattedMessage
color="primary" defaultMessage="Quantity Available"
onClick={() => onWarehouseStockDelete(stock.id)} description="tabel column header"
> />
<DeleteIcon />
</IconButton>
</TableCell> </TableCell>
<TableCell className={classes.colAction} />
</TableRow> </TableRow>
))} </TableHead>
{warehousesToAssign.length > 0 && ( <TableBody>
<TableRow> {renderCollection(stocks, stock => (
<TableCell colSpan={2}> <TableRow key={stock.id}>
<Typography variant="body2"> <TableCell className={classes.colName}>{stock.label}</TableCell>
<FormattedMessage <TableCell className={classes.colQuantity}>
defaultMessage="Assign Warehouse" <TextField
description="button" className={classes.inputComponent}
disabled={disabled}
fullWidth
inputProps={{
className: classes.input,
min: 0,
type: "number"
}}
onChange={event => onChange(stock.id, event.target.value)}
value={stock.value}
/> />
</Typography> </TableCell>
</TableCell> <TableCell className={classes.colAction}>
<TableCell className={classes.colAction}> <IconButton
<ClickAwayListener onClickAway={() => setExpansionState(false)}> color="primary"
<div ref={anchor}> onClick={() => onWarehouseStockDelete(stock.id)}
<IconButton >
color="primary" <DeleteIcon />
onClick={() => setExpansionState(!isExpanded)} </IconButton>
> </TableCell>
<AddIcon /> </TableRow>
</IconButton> ))}
<Popper {warehousesToAssign.length > 0 && (
className={classes.popper} <TableRow>
open={isExpanded} <TableCell colSpan={2}>
anchorEl={anchor.current} <Typography variant="body2">
transition <FormattedMessage
placement="top-end" defaultMessage="Assign Warehouse"
> description="button"
{({ TransitionProps }) => ( />
<Grow </Typography>
{...TransitionProps} </TableCell>
style={{ <TableCell className={classes.colAction}>
transformOrigin: "right top" <ClickAwayListener
}} onClickAway={() => setExpansionState(false)}
> >
<Paper className={classes.paper}> <div ref={anchor}>
{warehousesToAssign.map(warehouse => ( <IconButton
<MenuItem color="primary"
className={classes.menuItem} onClick={() => setExpansionState(!isExpanded)}
onClick={() => >
onWarehouseStockAdd(warehouse.id) <AddIcon />
} </IconButton>
> <Popper
{warehouse.name} className={classes.popper}
</MenuItem> open={isExpanded}
))} anchorEl={anchor.current}
</Paper> transition
</Grow> placement="top-end"
)} >
</Popper> {({ TransitionProps }) => (
</div> <Grow
</ClickAwayListener> {...TransitionProps}
</TableCell> style={{
</TableRow> transformOrigin: "right top"
)} }}
</TableBody> >
</Table> <Paper className={classes.paper}>
{warehousesToAssign.map(warehouse => (
<MenuItem
className={classes.menuItem}
onClick={() =>
onWarehouseStockAdd(warehouse.id)
}
>
{warehouse.name}
</MenuItem>
))}
</Paper>
</Grow>
)}
</Popper>
</div>
</ClickAwayListener>
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
)}
</Card> </Card>
); );
}; };

View file

@ -87,6 +87,7 @@ export interface ProductUpdatePageProps extends ListActions {
onSubmit?(data: ProductUpdatePageSubmitData); onSubmit?(data: ProductUpdatePageSubmitData);
onVariantAdd?(); onVariantAdd?();
onSetDefaultVariant(); onSetDefaultVariant();
onWarehouseConfigure();
} }
export interface ProductUpdatePageSubmitData extends ProductUpdatePageFormData { export interface ProductUpdatePageSubmitData extends ProductUpdatePageFormData {
@ -128,6 +129,7 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
onSetDefaultVariant, onSetDefaultVariant,
onVariantShow, onVariantShow,
onVariantReorder, onVariantReorder,
onWarehouseConfigure,
isChecked, isChecked,
selected, selected,
toggle, toggle,
@ -346,6 +348,7 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
<ProductStocks <ProductStocks
data={data} data={data}
disabled={disabled} disabled={disabled}
hasVariants={false}
errors={errors} errors={errors}
stocks={stocks} stocks={stocks}
warehouses={warehouses} warehouses={warehouses}
@ -369,6 +372,7 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
triggerChange(); triggerChange();
removeStock(id); removeStock(id);
}} }}
onWarehouseConfigure={onWarehouseConfigure}
/> />
</> </>
)} )}

View file

@ -58,6 +58,7 @@ interface ProductVariantCreatePageProps {
onSubmit: (data: ProductVariantCreatePageSubmitData) => void; onSubmit: (data: ProductVariantCreatePageSubmitData) => void;
onVariantClick: (variantId: string) => void; onVariantClick: (variantId: string) => void;
onVariantReorder: ReorderAction; onVariantReorder: ReorderAction;
onWarehouseConfigure: () => void;
} }
const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
@ -72,7 +73,8 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
onBack, onBack,
onSubmit, onSubmit,
onVariantClick, onVariantClick,
onVariantReorder onVariantReorder,
onWarehouseConfigure
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const attributeInput = React.useMemo( const attributeInput = React.useMemo(
@ -165,6 +167,7 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
<ProductStocks <ProductStocks
data={data} data={data}
disabled={disabled} disabled={disabled}
hasVariants={true}
onFormDataChange={change} onFormDataChange={change}
errors={errors} errors={errors}
stocks={stocks} stocks={stocks}
@ -187,6 +190,7 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
triggerChange(); triggerChange();
removeStock(id); removeStock(id);
}} }}
onWarehouseConfigure={onWarehouseConfigure}
/> />
<CardSpacer /> <CardSpacer />
<Metadata data={data} onChange={changeMetadata} /> <Metadata data={data} onChange={changeMetadata} />

View file

@ -140,6 +140,17 @@ storiesOf(
step={ProductVariantCreatorStep.prices} step={ProductVariantCreatorStep.prices}
warehouses={[props.warehouses[0]]} warehouses={[props.warehouses[0]]}
/> />
))
.add("ship when no warehouses", () => (
<ProductVariantCreatorContent
{...props}
data={{
...data,
warehouses: []
}}
step={ProductVariantCreatorStep.prices}
warehouses={[]}
/>
)); ));
storiesOf("Views / Products / Create multiple variants / summary", module) storiesOf("Views / Products / Create multiple variants / summary", module)

View file

@ -123,192 +123,206 @@ const ProductVariantCreatorStock: React.FC<ProductVariantCreatorStockProps> = pr
})} })}
/> />
<CardContent> <CardContent>
{warehouses.length > 1 && ( {!warehouses.length ? (
<Typography color="textSecondary">
<FormattedMessage
defaultMessage="There are no warehouses set up for your store. You can configure variants without providing stock quantities."
description="no warehouses info"
id="productVariantCreatorWarehouseSectionDescription"
/>
</Typography>
) : (
<> <>
<Typography className={classes.warehouseHeader} variant="h5"> {warehouses.length > 1 && (
<FormattedMessage <>
defaultMessage="Warehouses" <Typography className={classes.warehouseHeader} variant="h5">
description="header" <FormattedMessage
id="productVariantCreatorWarehouseSectionHeader" defaultMessage="Warehouses"
/> description="header"
</Typography> id="productVariantCreatorWarehouseSectionHeader"
<Typography className={classes.warehouseSubheader}>
<FormattedMessage
defaultMessage="Based on your selections we will create {numberOfProducts} products. Use this step to customize price and stocks for your new products"
values={{
numberOfProducts: data.attributes.reduce(
(acc, attr) => acc + attr.values.length,
0
)
}}
/>
</Typography>
<div className={classes.warehouseContainer}>
{warehouses.map(warehouse => (
<ControlledCheckbox
checked={isSelected(
warehouse.id,
data.warehouses,
(a, b) => a === b
)}
name={`warehouse:${warehouse.id}`}
label={warehouse.name}
onChange={() => onWarehouseToggle(warehouse.id)}
key={warehouse.id}
/>
))}
</div>
<CardSpacer />
<Hr />
<CardSpacer />
</>
)}
<Typography className={classes.stockHeader} variant="h5">
<FormattedMessage
defaultMessage="Stock"
description="variant stock, header"
id="productVariantCreatorStockSectionHeader"
/>
</Typography>
<RadioGroup value={data.stock.mode}>
<FormControlLabel
value="all"
control={<Radio color="primary" />}
label={intl.formatMessage({
defaultMessage: "Apply single stock to all SKUs"
})}
onChange={() => onApplyToAllChange("all")}
/>
{data.stock.mode === "all" && (
<div className={classes.stockContainer}>
{data.warehouses.map((warehouseId, warehouseIndex) => (
<div key={warehouseId}>
<Typography className={classes.warehouseName}>
{
warehouses.find(warehouse => warehouse.id === warehouseId)
.name
}
</Typography>
<TextField
fullWidth
inputProps={{
min: 0,
type: "number"
}}
label={intl.formatMessage({
defaultMessage: "Stock",
id: "productVariantCreatePricesStockInputLabel"
})}
value={data.stock.value[warehouseIndex]}
onChange={event =>
onApplyToAllStockChange(
parseInt(event.target.value, 10),
warehouseIndex
)
}
/> />
</Typography>
<Typography className={classes.warehouseSubheader}>
<FormattedMessage
defaultMessage="Based on your selections we will create {numberOfProducts} products. Use this step to customize price and stocks for your new products"
values={{
numberOfProducts: data.attributes.reduce(
(acc, attr) => acc + attr.values.length,
0
)
}}
/>
</Typography>
<div className={classes.warehouseContainer}>
{warehouses.map(warehouse => (
<ControlledCheckbox
checked={isSelected(
warehouse.id,
data.warehouses,
(a, b) => a === b
)}
name={`warehouse:${warehouse.id}`}
label={warehouse.name}
onChange={() => onWarehouseToggle(warehouse.id)}
key={warehouse.id}
/>
))}
</div> </div>
))} <CardSpacer />
</div> <Hr />
)} <CardSpacer />
<FormSpacer /> </>
<FormControlLabel )}
value="attribute" <Typography className={classes.stockHeader} variant="h5">
control={<Radio color="primary" />} <FormattedMessage
label={intl.formatMessage({ defaultMessage="Stock"
defaultMessage: "Apply unique stock by attribute to each SKU" description="variant stock, header"
})} id="productVariantCreatorStockSectionHeader"
onChange={() => onApplyToAllChange("attribute")}
/>
{data.stock.mode === "attribute" && (
<>
<FormSpacer />
<SingleSelectField
className={classes.shortInput}
choices={attributeChoices}
label={intl.formatMessage({
defaultMessage: "Select Attribute",
description: "variant attribute"
})}
value={data.stock.attribute}
onChange={event => onAttributeSelect(event.target.value)}
/> />
{stockAttributeValues && ( </Typography>
<> <RadioGroup value={data.stock.mode}>
<Hr className={classes.hrAttribute} /> <FormControlLabel
<FormSpacer /> value="all"
<div className={classes.attributeStockScroll}> control={<Radio color="primary" />}
<div className={classes.attributeStockContainer}> label={intl.formatMessage({
<div /> defaultMessage: "Apply single stock to all SKUs"
{data.stock.attribute && })}
data.warehouses.map(warehouseId => ( onChange={() => onApplyToAllChange("all")}
<Typography />
className={classes.warehouseName} {data.stock.mode === "all" && (
key={warehouseId} <div className={classes.stockContainer}>
> {data.warehouses.map((warehouseId, warehouseIndex) => (
{ <div key={warehouseId}>
warehouses.find( <Typography className={classes.warehouseName}>
warehouse => warehouse.id === warehouseId {
).name warehouses.find(
} warehouse => warehouse.id === warehouseId
</Typography> ).name
))} }
{stockAttributeValues.map(attributeValue => ( </Typography>
<React.Fragment key={attributeValue.id}> <TextField
<Typography>{attributeValue.name}</Typography> fullWidth
{data.warehouses.map( inputProps={{
(warehouseId, warehouseIndex) => ( min: 0,
<TextField type: "number"
fullWidth }}
inputProps={{ label={intl.formatMessage({
min: 0, defaultMessage: "Stock",
type: "number" id: "productVariantCreatePricesStockInputLabel"
}} })}
label={intl.formatMessage({ value={data.stock.value[warehouseIndex]}
defaultMessage: "Stock", onChange={event =>
id: onApplyToAllStockChange(
"productVariantCreatePricesStockInputLabel" parseInt(event.target.value, 10),
})} warehouseIndex
value={ )
data.stock.values.find( }
value => value.slug === attributeValue.slug />
).value[warehouseIndex]
}
onChange={event =>
onAttributeValueChange(
attributeValue.slug,
parseInt(event.target.value, 10),
warehouseIndex
)
}
key={warehouseId}
/>
)
)}
</React.Fragment>
))}
</div> </div>
</div> ))}
</div>
)}
<FormSpacer />
<FormControlLabel
value="attribute"
control={<Radio color="primary" />}
label={intl.formatMessage({
defaultMessage: "Apply unique stock by attribute to each SKU"
})}
onChange={() => onApplyToAllChange("attribute")}
/>
{data.stock.mode === "attribute" && (
<>
<FormSpacer />
<SingleSelectField
className={classes.shortInput}
choices={attributeChoices}
label={intl.formatMessage({
defaultMessage: "Select Attribute",
description: "variant attribute"
})}
value={data.stock.attribute}
onChange={event => onAttributeSelect(event.target.value)}
/>
{stockAttributeValues && (
<>
<Hr className={classes.hrAttribute} />
<FormSpacer />
<div className={classes.attributeStockScroll}>
<div className={classes.attributeStockContainer}>
<div />
{data.stock.attribute &&
data.warehouses.map(warehouseId => (
<Typography
className={classes.warehouseName}
key={warehouseId}
>
{
warehouses.find(
warehouse => warehouse.id === warehouseId
).name
}
</Typography>
))}
{stockAttributeValues.map(attributeValue => (
<React.Fragment key={attributeValue.id}>
<Typography>{attributeValue.name}</Typography>
{data.warehouses.map(
(warehouseId, warehouseIndex) => (
<TextField
fullWidth
inputProps={{
min: 0,
type: "number"
}}
label={intl.formatMessage({
defaultMessage: "Stock",
id:
"productVariantCreatePricesStockInputLabel"
})}
value={
data.stock.values.find(
value =>
value.slug === attributeValue.slug
).value[warehouseIndex]
}
onChange={event =>
onAttributeValueChange(
attributeValue.slug,
parseInt(event.target.value, 10),
warehouseIndex
)
}
key={warehouseId}
/>
)
)}
</React.Fragment>
))}
</div>
</div>
</>
)}
</>
)}
{data.stock.mode === "attribute" && !!data.stock.attribute && (
<>
<FormSpacer />
<Hr />
</> </>
)} )}
</>
)}
{data.stock.mode === "attribute" && !!data.stock.attribute && (
<>
<FormSpacer /> <FormSpacer />
<Hr /> <FormControlLabel
</> value="skip"
)} control={<Radio color="primary" />}
<FormSpacer /> label={intl.formatMessage({
<FormControlLabel defaultMessage: "Skip stock for now"
value="skip" })}
control={<Radio color="primary" />} onChange={() => onApplyToAllChange("skip")}
label={intl.formatMessage({ />
defaultMessage: "Skip stock for now" </RadioGroup>
})} </>
onChange={() => onApplyToAllChange("skip")} )}
/>
</RadioGroup>
</CardContent> </CardContent>
</Card> </Card>
); );

View file

@ -70,6 +70,7 @@ interface ProductVariantPageProps {
onImageSelect(id: string); onImageSelect(id: string);
onVariantClick(variantId: string); onVariantClick(variantId: string);
onSetDefaultVariant(); onSetDefaultVariant();
onWarehouseConfigure();
} }
const ProductVariantPage: React.FC<ProductVariantPageProps> = ({ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
@ -88,7 +89,8 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
onSubmit, onSubmit,
onVariantClick, onVariantClick,
onVariantReorder, onVariantReorder,
onSetDefaultVariant onSetDefaultVariant,
onWarehouseConfigure
}) => { }) => {
const attributeInput = React.useMemo( const attributeInput = React.useMemo(
() => getAttributeInputFromVariant(variant), () => getAttributeInputFromVariant(variant),
@ -240,6 +242,7 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
<ProductStocks <ProductStocks
data={data} data={data}
disabled={loading} disabled={loading}
hasVariants={true}
errors={errors} errors={errors}
stocks={stocks} stocks={stocks}
warehouses={warehouses} warehouses={warehouses}
@ -263,6 +266,7 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
triggerChange(); triggerChange();
removeStock(id); removeStock(id);
}} }}
onWarehouseConfigure={onWarehouseConfigure}
/> />
<CardSpacer /> <CardSpacer />
<Metadata data={data} onChange={changeMetadata} /> <Metadata data={data} onChange={changeMetadata} />

View file

@ -14,6 +14,7 @@ import {
usePrivateMetadataUpdate usePrivateMetadataUpdate
} from "@saleor/utils/metadata/updateMetadata"; } from "@saleor/utils/metadata/updateMetadata";
import { useWarehouseList } from "@saleor/warehouses/queries"; import { useWarehouseList } from "@saleor/warehouses/queries";
import { warehouseAddPath } from "@saleor/warehouses/urls";
import React from "react"; import React from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
@ -180,6 +181,7 @@ export const ProductCreateView: React.FC = () => {
)} )}
onBack={handleBack} onBack={handleBack}
onSubmit={handleSubmit} onSubmit={handleSubmit}
onWarehouseConfigure={() => navigate(warehouseAddPath)}
saveButtonBarState={productCreateOpts.status} saveButtonBarState={productCreateOpts.status}
fetchMoreCategories={{ fetchMoreCategories={{
hasMore: searchCategoryOpts.data?.search.pageInfo.hasNextPage, hasMore: searchCategoryOpts.data?.search.pageInfo.hasNextPage,

View file

@ -33,6 +33,7 @@ import {
usePrivateMetadataUpdate usePrivateMetadataUpdate
} from "@saleor/utils/metadata/updateMetadata"; } from "@saleor/utils/metadata/updateMetadata";
import { useWarehouseList } from "@saleor/warehouses/queries"; import { useWarehouseList } from "@saleor/warehouses/queries";
import { warehouseAddPath } from "@saleor/warehouses/urls";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
@ -304,6 +305,7 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
onDelete={() => openModal("remove")} onDelete={() => openModal("remove")}
onImageReorder={handleImageReorder} onImageReorder={handleImageReorder}
onSubmit={handleSubmit} onSubmit={handleSubmit}
onWarehouseConfigure={() => navigate(warehouseAddPath)}
onVariantAdd={handleVariantAdd} onVariantAdd={handleVariantAdd}
onVariantsAdd={() => navigate(productVariantCreatorUrl(id))} onVariantsAdd={() => navigate(productVariantCreatorUrl(id))}
onVariantShow={variantId => () => onVariantShow={variantId => () =>

View file

@ -13,6 +13,7 @@ import {
usePrivateMetadataUpdate usePrivateMetadataUpdate
} from "@saleor/utils/metadata/updateMetadata"; } from "@saleor/utils/metadata/updateMetadata";
import { useWarehouseList } from "@saleor/warehouses/queries"; import { useWarehouseList } from "@saleor/warehouses/queries";
import { warehouseAddPath } from "@saleor/warehouses/urls";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
@ -215,6 +216,7 @@ export const ProductVariant: React.FC<ProductUpdateProps> = ({
onDelete={() => openModal("remove")} onDelete={() => openModal("remove")}
onImageSelect={handleImageSelect} onImageSelect={handleImageSelect}
onSubmit={handleSubmit} onSubmit={handleSubmit}
onWarehouseConfigure={() => navigate(warehouseAddPath)}
onVariantClick={variantId => { onVariantClick={variantId => {
navigate(productVariantEditUrl(productId, variantId)); navigate(productVariantEditUrl(productId, variantId));
}} }}

View file

@ -10,6 +10,7 @@ import {
usePrivateMetadataUpdate usePrivateMetadataUpdate
} from "@saleor/utils/metadata/updateMetadata"; } from "@saleor/utils/metadata/updateMetadata";
import { useWarehouseList } from "@saleor/warehouses/queries"; import { useWarehouseList } from "@saleor/warehouses/queries";
import { warehouseAddPath } from "@saleor/warehouses/urls";
import React from "react"; import React from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
@ -142,6 +143,7 @@ export const ProductVariant: React.FC<ProductVariantCreateProps> = ({
onBack={handleBack} onBack={handleBack}
onSubmit={handleSubmit} onSubmit={handleSubmit}
onVariantClick={handleVariantClick} onVariantClick={handleVariantClick}
onWarehouseConfigure={() => navigate(warehouseAddPath)}
onVariantReorder={handleVariantReorder} onVariantReorder={handleVariantReorder}
saveButtonBarState={variantCreateResult.status} saveButtonBarState={variantCreateResult.status}
warehouses={ warehouses={

File diff suppressed because it is too large Load diff

View file

@ -35,6 +35,7 @@ storiesOf("Views / Products / Create product", module)
onSubmit={() => undefined} onSubmit={() => undefined}
saveButtonBarState="default" saveButtonBarState="default"
warehouses={warehouseList} warehouses={warehouseList}
onWarehouseConfigure={() => undefined}
taxTypes={taxTypes} taxTypes={taxTypes}
weightUnit="kg" weightUnit="kg"
/> />
@ -58,6 +59,7 @@ storiesOf("Views / Products / Create product", module)
onSubmit={() => undefined} onSubmit={() => undefined}
saveButtonBarState="default" saveButtonBarState="default"
warehouses={undefined} warehouses={undefined}
onWarehouseConfigure={() => undefined}
taxTypes={taxTypes} taxTypes={taxTypes}
weightUnit="kg" weightUnit="kg"
/> />
@ -87,6 +89,7 @@ storiesOf("Views / Products / Create product", module)
onSubmit={() => undefined} onSubmit={() => undefined}
saveButtonBarState="default" saveButtonBarState="default"
warehouses={warehouseList} warehouses={warehouseList}
onWarehouseConfigure={() => undefined}
taxTypes={taxTypes} taxTypes={taxTypes}
weightUnit="kg" weightUnit="kg"
/> />

View file

@ -39,6 +39,7 @@ const props: ProductUpdatePageProps = {
onVariantReorder: () => undefined, onVariantReorder: () => undefined,
onVariantShow: () => undefined, onVariantShow: () => undefined,
onVariantsAdd: () => undefined, onVariantsAdd: () => undefined,
onWarehouseConfigure: () => undefined,
placeholderImage, placeholderImage,
product, product,
saveButtonBarState: "default", saveButtonBarState: "default",
@ -104,6 +105,25 @@ storiesOf("Views / Products / Product edit", module)
}} }}
/> />
)) ))
.add("no stock, no variants and no warehouses", () => (
<ProductUpdatePage
{...props}
warehouses={[]}
product={{
...product,
productType: {
...product.productType,
hasVariants: false
},
variants: [
{
...product.variants[0],
stocks: []
}
]
}}
/>
))
.add("no product attributes", () => ( .add("no product attributes", () => (
<ProductUpdatePage <ProductUpdatePage
{...props} {...props}

View file

@ -26,6 +26,7 @@ storiesOf("Views / Products / Create product variant", module)
onVariantReorder={() => undefined} onVariantReorder={() => undefined}
saveButtonBarState="default" saveButtonBarState="default"
warehouses={warehouseList} warehouses={warehouseList}
onWarehouseConfigure={() => undefined}
/> />
)) ))
.add("with errors", () => ( .add("with errors", () => (
@ -58,6 +59,7 @@ storiesOf("Views / Products / Create product variant", module)
onVariantReorder={() => undefined} onVariantReorder={() => undefined}
saveButtonBarState="default" saveButtonBarState="default"
warehouses={warehouseList} warehouses={warehouseList}
onWarehouseConfigure={() => undefined}
/> />
)) ))
.add("when loading data", () => ( .add("when loading data", () => (
@ -74,6 +76,7 @@ storiesOf("Views / Products / Create product variant", module)
onVariantReorder={() => undefined} onVariantReorder={() => undefined}
saveButtonBarState="default" saveButtonBarState="default"
warehouses={warehouseList} warehouses={warehouseList}
onWarehouseConfigure={() => undefined}
/> />
)) ))
.add("add first variant", () => ( .add("add first variant", () => (
@ -93,5 +96,23 @@ storiesOf("Views / Products / Create product variant", module)
onVariantReorder={() => undefined} onVariantReorder={() => undefined}
saveButtonBarState="default" saveButtonBarState="default"
warehouses={warehouseList} warehouses={warehouseList}
onWarehouseConfigure={() => undefined}
/>
))
.add("no warehouses", () => (
<ProductVariantCreatePage
currencySymbol="USD"
weightUnit="kg"
disabled={false}
errors={[]}
header="Add variant"
product={product}
onBack={() => undefined}
onSubmit={() => undefined}
onVariantClick={undefined}
onVariantReorder={() => undefined}
saveButtonBarState="default"
warehouses={[]}
onWarehouseConfigure={() => undefined}
/> />
)); ));

View file

@ -28,6 +28,7 @@ storiesOf("Views / Products / Product variant details", module)
onVariantReorder={() => undefined} onVariantReorder={() => undefined}
saveButtonBarState="default" saveButtonBarState="default"
warehouses={warehouseList} warehouses={warehouseList}
onWarehouseConfigure={() => undefined}
/> />
)) ))
.add("when loading data", () => ( .add("when loading data", () => (
@ -47,6 +48,26 @@ storiesOf("Views / Products / Product variant details", module)
onVariantReorder={() => undefined} onVariantReorder={() => undefined}
saveButtonBarState="default" saveButtonBarState="default"
warehouses={warehouseList} warehouses={warehouseList}
onWarehouseConfigure={() => undefined}
/>
))
.add("no warehouses", () => (
<ProductVariantPage
defaultWeightUnit="kg"
header={variant.name || variant.sku}
errors={[]}
variant={variant}
onAdd={() => undefined}
onBack={() => undefined}
onDelete={undefined}
onSetDefaultVariant={() => undefined}
onImageSelect={() => undefined}
onSubmit={() => undefined}
onVariantClick={() => undefined}
onVariantReorder={() => undefined}
saveButtonBarState="default"
warehouses={[]}
onWarehouseConfigure={() => undefined}
/> />
)) ))
.add("attribute errors", () => ( .add("attribute errors", () => (
@ -82,5 +103,6 @@ storiesOf("Views / Products / Product variant details", module)
...error ...error
}))} }))}
warehouses={warehouseList} warehouses={warehouseList}
onWarehouseConfigure={() => undefined}
/> />
)); ));