diff --git a/CHANGELOG.md b/CHANGELOG.md index 93754a5dd..e4329c6da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,12 +10,14 @@ All notable, unreleased changes to this project will be documented in this file. - Fix invalid values in channel picker - #2313 by @orzechdev - Fix missing metadata and payment balance on unconfirmed orders - #2314 by @orzechdev - Fix exit form dialog false positive - #2311 by @orzechdev +- Enable save button on channel pages - #2328 by @orzechdev - Handle form errors before product creation - #2299 by @orzechdev - Fix no product error on unconfirmed order lines - #2324 by @orzechdev - Enable save button on discount pages - #2319 by @orzechdev - Enable save button on page pages - #2325 by @orzechdev - Fix pagination errors on voucher and sale pages - #2317 by @orzechdev + ## 3.4 - Added links instead of imperative navigation with onClick - #1969 by @taniotanio7 diff --git a/src/channels/components/ChannelForm/ChannelForm.tsx b/src/channels/components/ChannelForm/ChannelForm.tsx index a1f9d5ccf..cab4b1655 100644 --- a/src/channels/components/ChannelForm/ChannelForm.tsx +++ b/src/channels/components/ChannelForm/ChannelForm.tsx @@ -72,10 +72,11 @@ export const ChannelForm: React.FC = ({ const intl = useIntl(); const [copied, copy] = useClipboard(); const formErrors = getFormErrors( - ["name", "slug", "currencyCode"], + ["name", "slug", "currencyCode", "defaultCountry"], errors, ); - const classes = useStyles({}); + const classes = useStyles(); + return ( <> diff --git a/src/channels/pages/ChannelDetailsPage/ChannelDetailsPage.tsx b/src/channels/pages/ChannelDetailsPage/ChannelDetailsPage.tsx index 80177087c..0d30bddaf 100644 --- a/src/channels/pages/ChannelDetailsPage/ChannelDetailsPage.tsx +++ b/src/channels/pages/ChannelDetailsPage/ChannelDetailsPage.tsx @@ -2,6 +2,7 @@ import ChannelAllocationStrategy from "@saleor/channels/components/ChannelAlloca import ShippingZones from "@saleor/channels/components/ShippingZones"; import Warehouses from "@saleor/channels/components/Warehouses"; import { channelsListUrl } from "@saleor/channels/urls"; +import { validateChannelFormData } from "@saleor/channels/validation"; import CardSpacer from "@saleor/components/CardSpacer"; import Form from "@saleor/components/Form"; import Grid from "@saleor/components/Grid"; @@ -41,7 +42,9 @@ import { } from "./handlers"; import { ChannelShippingZones, ChannelWarehouses } from "./types"; -export interface ChannelDetailsPageProps { +export interface ChannelDetailsPageProps< + TErrors extends ChannelErrorFragment[] +> { channel?: ChannelDetailsFragment; currencyCodes?: SingleAutocompleteChoiceType[]; disabled: boolean; @@ -58,13 +61,13 @@ export interface ChannelDetailsPageProps { allWarehousesCount: number; countries: CountryFragment[]; onDelete?: () => void; - onSubmit: (data: FormData) => SubmitPromise; + onSubmit: (data: FormData) => SubmitPromise; updateChannelStatus?: () => void; searchShippingZones: (query: string) => void; searchWarehouses: (query: string) => void; } -const ChannelDetailsPage = function({ +const ChannelDetailsPage = function({ channel, currencyCodes, disabled, @@ -88,6 +91,10 @@ const ChannelDetailsPage = function({ }: ChannelDetailsPageProps) { const navigate = useNavigator(); + const [validationErrors, setValidationErrors] = useState< + ChannelErrorFragment[] + >([]); + const [selectedCurrencyCode, setSelectedCurrencyCode] = useState(""); const [ selectedCountryDisplayName, @@ -133,25 +140,21 @@ const ChannelDetailsPage = function({ !warehousesToDisplay.some(({ id }) => id === searchedWarehouseId), ); - const checkIfSaveIsDisabled = (data: FormData) => { - const isValid = - !!data.name && - !!data.slug && - !!data.currencyCode && - !!data.defaultCountry && - data.name.trim().length > 0; + const handleSubmit = async (data: FormData) => { + const errors = validateChannelFormData(data); - return disabled || !isValid; + setValidationErrors(errors); + + if (errors.length) { + return errors; + } + + return onSubmit(data); }; return ( -
- {({ change, data, submit, set, isSaveDisabled, triggerChange }) => { + + {({ change, data, submit, set, triggerChange }) => { const handleCurrencyCodeSelect = createSingleAutocompleteSelectHandler( change, setSelectedCurrencyCode, @@ -188,6 +191,8 @@ const ChannelDetailsPage = function({ ); const reorderWarehouse = createWarehouseReorderHandler(data, set); + const allErrors = [...errors, ...validationErrors]; + return ( <> @@ -202,7 +207,7 @@ const ChannelDetailsPage = function({ onChange={change} onCurrencyCodeChange={handleCurrencyCodeSelect} onDefaultCountryChange={handleDefaultCountrySelect} - errors={errors} + errors={allErrors} />
@@ -267,7 +272,7 @@ const ChannelDetailsPage = function({ onSubmit={submit} onDelete={onDelete} state={saveButtonBarState} - disabled={isSaveDisabled} + disabled={disabled} /> ); diff --git a/src/channels/validation.ts b/src/channels/validation.ts new file mode 100644 index 000000000..018bd83ea --- /dev/null +++ b/src/channels/validation.ts @@ -0,0 +1,32 @@ +import { ChannelErrorCode, ChannelErrorFragment } from "@saleor/graphql"; + +import { FormData } from "./components/ChannelForm"; + +const createEmptyRequiredError = (field: string): ChannelErrorFragment => ({ + __typename: "ChannelError", + code: ChannelErrorCode.REQUIRED, + field, + message: null, +}); + +export const validateChannelFormData = (data: FormData) => { + let errors: ChannelErrorFragment[] = []; + + if (!data.name) { + errors = [...errors, createEmptyRequiredError("name")]; + } + + if (!data.slug) { + errors = [...errors, createEmptyRequiredError("slug")]; + } + + if (!data.currencyCode) { + errors = [...errors, createEmptyRequiredError("currencyCode")]; + } + + if (!data.defaultCountry) { + errors = [...errors, createEmptyRequiredError("defaultCountry")]; + } + + return errors; +}; diff --git a/src/channels/views/ChannelCreate/ChannelCreate.tsx b/src/channels/views/ChannelCreate/ChannelCreate.tsx index f351a3fa6..0cff2dba5 100644 --- a/src/channels/views/ChannelCreate/ChannelCreate.tsx +++ b/src/channels/views/ChannelCreate/ChannelCreate.tsx @@ -46,7 +46,10 @@ export const ChannelCreateView = ({}) => { }, }); - const [reorderChannelWarehouses] = useChannelReorderWarehousesMutation({ + const [ + reorderChannelWarehouses, + reorderChannelWarehousesOpts, + ] = useChannelReorderWarehousesMutation({ onCompleted: data => { const errors = data.channelReorderWarehouses.errors; if (errors.length) { @@ -173,6 +176,7 @@ export const ChannelCreateView = ({}) => { )} disabled={ createChannelOpts.loading || + reorderChannelWarehousesOpts.loading || shippingZonesCountLoading || warehousesCountLoading }