Do not allow negative values and display errors
This commit is contained in:
parent
b0a416e0fa
commit
7aebb67cfd
9 changed files with 68 additions and 51 deletions
|
@ -74,6 +74,10 @@ export const PriceField: React.FC<PriceFieldProps> = props => {
|
|||
) : (
|
||||
<span />
|
||||
),
|
||||
inputProps: {
|
||||
min: 0,
|
||||
...InputProps?.inputProps
|
||||
},
|
||||
type: "number"
|
||||
}}
|
||||
name={name}
|
||||
|
|
|
@ -62,11 +62,6 @@ const ProductPricing: React.FC<ProductPricingProps> = props => {
|
|||
value={data.basePrice}
|
||||
currencySymbol={currency}
|
||||
onChange={handlePriceChange}
|
||||
InputProps={{
|
||||
inputProps: {
|
||||
min: 0
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
|
|
|
@ -6,6 +6,7 @@ import CardTitle from "@saleor/components/CardTitle";
|
|||
import Grid from "@saleor/components/Grid";
|
||||
import { ProductErrorFragment } from "@saleor/fragments/types/ProductErrorFragment";
|
||||
import { getFormErrors, getProductErrorMessage } from "@saleor/utils/errors";
|
||||
import createNonNegativeValueChangeHandler from "@saleor/utils/handlers/nonNegativeValueChangeHandler";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
|
@ -25,6 +26,7 @@ const ProductShipping: React.FC<ProductShippingProps> = props => {
|
|||
const intl = useIntl();
|
||||
|
||||
const formErrors = getFormErrors(["weight"], errors);
|
||||
const handleChange = createNonNegativeValueChangeHandler(onChange);
|
||||
|
||||
return (
|
||||
<Card>
|
||||
|
@ -46,7 +48,7 @@ const ProductShipping: React.FC<ProductShippingProps> = props => {
|
|||
helperText={getProductErrorMessage(formErrors.weight, intl)}
|
||||
name="weight"
|
||||
value={data.weight}
|
||||
onChange={onChange}
|
||||
onChange={handleChange}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
|
|
|
@ -29,6 +29,7 @@ import { FormsetAtomicData, FormsetChange } from "@saleor/hooks/useFormset";
|
|||
import { renderCollection } from "@saleor/misc";
|
||||
import { ICONBUTTON_SIZE } from "@saleor/theme";
|
||||
import { getFormErrors, getProductErrorMessage } from "@saleor/utils/errors";
|
||||
import createNonNegativeValueChangeHandler from "@saleor/utils/handlers/nonNegativeValueChangeHandler";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
|
@ -238,33 +239,41 @@ const ProductStocks: React.FC<ProductStocksProps> = ({
|
|||
</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}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colAction}>
|
||||
<IconButton
|
||||
color="primary"
|
||||
onClick={() => onWarehouseStockDelete(stock.id)}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
{renderCollection(stocks, stock => {
|
||||
const handleQuantityChange = createNonNegativeValueChangeHandler(
|
||||
event => onChange(stock.id, event.target.value)
|
||||
);
|
||||
|
||||
return (
|
||||
<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={handleQuantityChange}
|
||||
value={stock.value}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colAction}>
|
||||
<IconButton
|
||||
color="primary"
|
||||
onClick={() => onWarehouseStockDelete(stock.id)}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
{warehousesToAssign.length > 0 && (
|
||||
<TableRow>
|
||||
<TableCell colSpan={2}>
|
||||
|
|
|
@ -96,7 +96,7 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
|
|||
|
||||
const initialForm: ProductVariantCreatePageFormData = {
|
||||
costPrice: "",
|
||||
images: maybe(() => product.images.map(image => image.id)),
|
||||
images: product?.images.map(image => image.id),
|
||||
metadata: [],
|
||||
price: "",
|
||||
privateMetadata: [],
|
||||
|
@ -148,10 +148,9 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
|
|||
/>
|
||||
<CardSpacer />
|
||||
<ProductVariantPrice
|
||||
data={data}
|
||||
errors={errors}
|
||||
price={data.price}
|
||||
currencySymbol={currencySymbol}
|
||||
costPrice={data.costPrice}
|
||||
loading={disabled}
|
||||
onChange={change}
|
||||
/>
|
||||
|
|
|
@ -131,12 +131,12 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
|
|||
);
|
||||
|
||||
const initialForm: ProductVariantPageFormData = {
|
||||
costPrice: maybe(() => variant.costPrice.amount.toString(), ""),
|
||||
costPrice: variant?.costPrice?.amount.toString() || "",
|
||||
metadata: variant?.metadata?.map(mapMetadataItemToInput),
|
||||
price: maybe(() => variant.price.amount.toString(), ""),
|
||||
price: variant?.price?.amount.toString() || "",
|
||||
privateMetadata: variant?.privateMetadata?.map(mapMetadataItemToInput),
|
||||
sku: maybe(() => variant.sku, ""),
|
||||
trackInventory: variant?.trackInventory,
|
||||
sku: variant?.sku || "",
|
||||
trackInventory: !!variant?.trackInventory,
|
||||
weight: variant?.weight?.value.toString() || ""
|
||||
};
|
||||
|
||||
|
@ -218,7 +218,7 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
|
|||
<CardSpacer />
|
||||
<ProductVariantPrice
|
||||
errors={errors}
|
||||
price={data.price}
|
||||
data={data}
|
||||
currencySymbol={
|
||||
variant && variant.price
|
||||
? variant.price.currency
|
||||
|
@ -226,7 +226,6 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
|
|||
? variant.costPrice.currency
|
||||
: ""
|
||||
}
|
||||
costPrice={data.costPrice}
|
||||
loading={loading}
|
||||
onChange={change}
|
||||
/>
|
||||
|
|
|
@ -22,20 +22,19 @@ const useStyles = makeStyles(
|
|||
|
||||
interface ProductVariantPriceProps {
|
||||
currencySymbol?: string;
|
||||
price?: string;
|
||||
costPrice?: string;
|
||||
data: Record<"price" | "costPrice", string>;
|
||||
errors: ProductErrorFragment[];
|
||||
loading?: boolean;
|
||||
onChange(event: any);
|
||||
}
|
||||
|
||||
const ProductVariantPrice: React.FC<ProductVariantPriceProps> = props => {
|
||||
const { currencySymbol, costPrice, errors, price, loading, onChange } = props;
|
||||
const { currencySymbol, data, errors, loading, onChange } = props;
|
||||
|
||||
const classes = useStyles(props);
|
||||
const intl = useIntl();
|
||||
|
||||
const formErrors = getFormErrors(["price", "cost_price"], errors);
|
||||
const formErrors = getFormErrors(["price", "costPrice"], errors);
|
||||
|
||||
const handlePriceChange = createNonNegativeValueChangeHandler(onChange);
|
||||
|
||||
|
@ -52,11 +51,12 @@ const ProductVariantPrice: React.FC<ProductVariantPriceProps> = props => {
|
|||
<div>
|
||||
<PriceField
|
||||
error={!!formErrors.price}
|
||||
hint={getProductErrorMessage(formErrors.price, intl)}
|
||||
name="price"
|
||||
label={intl.formatMessage({
|
||||
defaultMessage: "Price"
|
||||
})}
|
||||
value={price}
|
||||
value={data.price}
|
||||
currencySymbol={currencySymbol}
|
||||
onChange={handlePriceChange}
|
||||
disabled={loading}
|
||||
|
@ -69,20 +69,20 @@ const ProductVariantPrice: React.FC<ProductVariantPriceProps> = props => {
|
|||
</div>
|
||||
<div>
|
||||
<PriceField
|
||||
error={!!formErrors.cost_price}
|
||||
error={!!formErrors.costPrice}
|
||||
name="costPrice"
|
||||
label={intl.formatMessage({
|
||||
defaultMessage: "Cost price"
|
||||
})}
|
||||
hint={
|
||||
getProductErrorMessage(formErrors.cost_price, intl) ||
|
||||
getProductErrorMessage(formErrors.costPrice, intl) ||
|
||||
intl.formatMessage({
|
||||
defaultMessage: "Optional",
|
||||
description: "optional field",
|
||||
id: "productVariantPriceOptionalCostPriceField"
|
||||
})
|
||||
}
|
||||
value={costPrice}
|
||||
value={data.costPrice}
|
||||
currencySymbol={currencySymbol}
|
||||
onChange={handlePriceChange}
|
||||
disabled={loading}
|
||||
|
|
|
@ -238,7 +238,7 @@ export function mapFormsetStockToStockInput(
|
|||
stock: FormsetAtomicData<null, string>
|
||||
): StockInput {
|
||||
return {
|
||||
quantity: parseInt(stock.value, 10),
|
||||
quantity: parseInt(stock.value, 10) || 0,
|
||||
warehouse: stock.id
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7178,6 +7178,7 @@ exports[`Storyshots Generics / Price input disabled 1`] = `
|
|||
aria-invalid="false"
|
||||
class="MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id MuiInputBase-inputAdornedEnd-id MuiOutlinedInput-inputAdornedEnd-id"
|
||||
disabled=""
|
||||
min="0"
|
||||
name="price"
|
||||
type="number"
|
||||
/>
|
||||
|
@ -7223,6 +7224,7 @@ exports[`Storyshots Generics / Price input with currency symbol 1`] = `
|
|||
<input
|
||||
aria-invalid="false"
|
||||
class="MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-inputAdornedEnd-id MuiOutlinedInput-inputAdornedEnd-id"
|
||||
min="0"
|
||||
name="price"
|
||||
type="number"
|
||||
/>
|
||||
|
@ -7276,6 +7278,7 @@ exports[`Storyshots Generics / Price input with hint 1`] = `
|
|||
<input
|
||||
aria-invalid="false"
|
||||
class="MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-inputAdornedEnd-id MuiOutlinedInput-inputAdornedEnd-id"
|
||||
min="0"
|
||||
name="price"
|
||||
type="number"
|
||||
/>
|
||||
|
@ -7332,6 +7335,7 @@ exports[`Storyshots Generics / Price input with label 1`] = `
|
|||
<input
|
||||
aria-invalid="false"
|
||||
class="MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-inputAdornedEnd-id MuiOutlinedInput-inputAdornedEnd-id"
|
||||
min="0"
|
||||
name="price"
|
||||
type="number"
|
||||
/>
|
||||
|
@ -7383,6 +7387,7 @@ exports[`Storyshots Generics / Price input with label and hint 1`] = `
|
|||
<input
|
||||
aria-invalid="false"
|
||||
class="MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-inputAdornedEnd-id MuiOutlinedInput-inputAdornedEnd-id"
|
||||
min="0"
|
||||
name="price"
|
||||
type="number"
|
||||
/>
|
||||
|
@ -7433,6 +7438,7 @@ exports[`Storyshots Generics / Price input with no value 1`] = `
|
|||
<input
|
||||
aria-invalid="false"
|
||||
class="MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-inputAdornedEnd-id MuiOutlinedInput-inputAdornedEnd-id"
|
||||
min="0"
|
||||
name="price"
|
||||
type="number"
|
||||
/>
|
||||
|
@ -7478,6 +7484,7 @@ exports[`Storyshots Generics / Price input with value 1`] = `
|
|||
<input
|
||||
aria-invalid="false"
|
||||
class="MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-inputAdornedEnd-id MuiOutlinedInput-inputAdornedEnd-id"
|
||||
min="0"
|
||||
name="price"
|
||||
type="number"
|
||||
value="30"
|
||||
|
@ -7530,6 +7537,7 @@ exports[`Storyshots Generics / Price input with value, label, currency symbol an
|
|||
<input
|
||||
aria-invalid="true"
|
||||
class="MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-inputAdornedEnd-id MuiOutlinedInput-inputAdornedEnd-id"
|
||||
min="0"
|
||||
name="price"
|
||||
type="number"
|
||||
value="30"
|
||||
|
@ -7595,6 +7603,7 @@ exports[`Storyshots Generics / Price input with value, label, currency symbol an
|
|||
<input
|
||||
aria-invalid="false"
|
||||
class="MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-inputAdornedEnd-id MuiOutlinedInput-inputAdornedEnd-id"
|
||||
min="0"
|
||||
name="price"
|
||||
type="number"
|
||||
value="30"
|
||||
|
|
Loading…
Reference in a new issue