Do not allow negative values and display errors

This commit is contained in:
dominik-zeglen 2020-10-13 13:29:42 +02:00
parent b0a416e0fa
commit 7aebb67cfd
9 changed files with 68 additions and 51 deletions

View file

@ -74,6 +74,10 @@ export const PriceField: React.FC<PriceFieldProps> = props => {
) : (
<span />
),
inputProps: {
min: 0,
...InputProps?.inputProps
},
type: "number"
}}
name={name}

View file

@ -62,11 +62,6 @@ const ProductPricing: React.FC<ProductPricingProps> = props => {
value={data.basePrice}
currencySymbol={currency}
onChange={handlePriceChange}
InputProps={{
inputProps: {
min: 0
}
}}
/>
</div>
</CardContent>

View file

@ -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">

View file

@ -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}>

View file

@ -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}
/>

View file

@ -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}
/>

View file

@ -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}

View file

@ -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
};
}

View file

@ -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"