Add order expiration (#3843)

* Add TTL expiration & migrate some components in channel details

* Migrate button

* Update fixtures

* Add changeset

* Typo & lint

* Reorder UI elements

* Set custom height to match old input

* Add explanatory comment

* test - created test for expired orderes functionality in channels, fixed validation test which checks using same slug twice when creating channel

* Fix create page

* Fix lint issue

* Fix error handling double notification

* tests added: creating channel with expiration days for orders, creating channel with expiration days for orders with boundary conditions exciding requirements

* function name typeExpirationDate changed to typeExpirationDays

---------

Co-authored-by: wojteknowacki <wojciech.nowacki@saleor.io>
This commit is contained in:
Michał Droń 2023-07-12 14:04:50 +02:00 committed by GitHub
parent 66976d547b
commit b386cf060f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 321 additions and 182 deletions

View file

@ -0,0 +1,5 @@
---
"saleor-dashboard": minor
---
Add order exipration TTL field to channel settings

View file

@ -8,6 +8,7 @@ import { CHANNELS_SELECTORS } from "../../../elements/channels/channels-selector
import { SELECT_CHANNELS_TO_ASSIGN } from "../../../elements/channels/select-channels-to-assign"; import { SELECT_CHANNELS_TO_ASSIGN } from "../../../elements/channels/select-channels-to-assign";
import { HEADER_SELECTORS } from "../../../elements/header/header-selectors"; import { HEADER_SELECTORS } from "../../../elements/header/header-selectors";
import { BUTTON_SELECTORS } from "../../../elements/shared/button-selectors"; import { BUTTON_SELECTORS } from "../../../elements/shared/button-selectors";
import { MESSAGES } from "../../../fixtures/";
import { productDetailsUrl, urlList } from "../../../fixtures/urlList"; import { productDetailsUrl, urlList } from "../../../fixtures/urlList";
import { ONE_PERMISSION_USERS } from "../../../fixtures/users"; import { ONE_PERMISSION_USERS } from "../../../fixtures/users";
import { createChannel } from "../../../support/api/requests/Channels"; import { createChannel } from "../../../support/api/requests/Channels";
@ -17,7 +18,11 @@ import {
getShippingZone, getShippingZone,
} from "../../../support/api/requests/ShippingMethod"; } from "../../../support/api/requests/ShippingMethod";
import { createWarehouse as createWarehouseViaApi } from "../../../support/api/requests/Warehouse"; import { createWarehouse as createWarehouseViaApi } from "../../../support/api/requests/Warehouse";
import { createChannelByView } from "../../../support/pages/channelsPage"; import {
createChannelByView,
setChannelRequiredFields,
typeExpirationDays,
} from "../../../support/pages/channelsPage";
describe("Channels", () => { describe("Channels", () => {
const channelStartsWith = `CyChannels`; const channelStartsWith = `CyChannels`;
@ -91,6 +96,65 @@ describe("Channels", () => {
}); });
}, },
); );
it(
"should create new channel with expired orders functionality set. TC: SALEOR_0713",
{ tags: ["@channel", "@allEnv", "@stable"] },
() => {
const randomChannel = `${channelStartsWith} ${faker.datatype.number()}`;
const orderExpiresAfter = 120;
cy.addAliasToGraphRequest("Channels");
cy.addAliasToGraphRequest("ChannelCreate");
cy.visit(urlList.channels);
cy.waitForRequestAndCheckIfNoErrors("@Channels");
setChannelRequiredFields({ name: randomChannel, currency });
typeExpirationDays(orderExpiresAfter);
cy.clickConfirmButton();
cy.waitForRequestAndCheckIfNoErrors("@ChannelCreate").then(
channelCreate => {
expect(
channelCreate.response.body.data.channelCreate.channel
.orderSettings,
).to.have.property("deleteExpiredOrdersAfter", orderExpiresAfter);
cy.get(CHANNELS_SELECTORS.orderExpirationInput)
.invoke("val")
.should("contain", orderExpiresAfter.toString());
},
);
},
);
it(
"should not be able to create new channel with expired orders functionality with values outside boundary conditions. TC: SALEOR_0714",
{ tags: ["@channel", "@allEnv"] },
() => {
const randomChannel = `${channelStartsWith} ${faker.datatype.number()}`;
const underBoundaryConditions = 0;
const overBoundaryConditions = 121;
cy.addAliasToGraphRequest("Channels");
cy.addAliasToGraphRequest("ChannelCreate");
cy.visit(urlList.channels);
cy.waitForRequestAndCheckIfNoErrors("@Channels");
setChannelRequiredFields({ name: randomChannel, currency });
typeExpirationDays(underBoundaryConditions);
cy.clickConfirmButton();
cy.wait("@ChannelCreate").then(createChannelResponse => {
cy.log(createChannelResponse);
expect(
createChannelResponse.response.body.data.channelCreate.errors.length,
).to.eq(1);
cy.confirmationErrorMessageShouldAppear();
});
typeExpirationDays(overBoundaryConditions);
cy.clickConfirmButton();
cy.wait("@ChannelCreate").then(createChannelResponse => {
cy.log(createChannelResponse);
expect(
createChannelResponse.response.body.data.channelCreate.errors.length,
).to.eq(1);
cy.confirmationErrorMessageShouldAppear();
});
},
);
it( it(
"should create channel with shippingZone and warehouse TC: SALEOR_0712", "should create channel with shippingZone and warehouse TC: SALEOR_0712",
{ tags: ["@channel", "@allEnv"] }, { tags: ["@channel", "@allEnv"] },
@ -101,7 +165,6 @@ describe("Channels", () => {
const randomChannel = `${channelStartsWith} ${faker.datatype.number()}`; const randomChannel = `${channelStartsWith} ${faker.datatype.number()}`;
cy.addAliasToGraphRequest("Channels"); cy.addAliasToGraphRequest("Channels");
cy.visit(urlList.channels); cy.visit(urlList.channels);
cy.expectSkeletonIsVisible();
cy.wait("@Channels"); cy.wait("@Channels");
createChannelByView({ createChannelByView({
name: randomChannel, name: randomChannel,
@ -120,7 +183,7 @@ describe("Channels", () => {
); );
it( it(
"should validate slug name. TC: SALEOR_0703", "should validate that creating channels with same slug name as other is not possible. TC: SALEOR_0703",
{ tags: ["@channel", "@allEnv", "@stable"] }, { tags: ["@channel", "@allEnv", "@stable"] },
() => { () => {
const randomChannel = `${channelStartsWith} ${faker.datatype.number()}`; const randomChannel = `${channelStartsWith} ${faker.datatype.number()}`;
@ -133,8 +196,10 @@ describe("Channels", () => {
cy.visit(urlList.channels); cy.visit(urlList.channels);
cy.expectSkeletonIsVisible(); cy.expectSkeletonIsVisible();
createChannelByView({ name: randomChannel, currency }); createChannelByView({ name: randomChannel, currency });
cy.get(ADD_CHANNEL_FORM_SELECTORS.slugValidationMessage).should( cy.confirmationErrorMessageShouldAppear();
"be.visible", cy.get(ADD_CHANNEL_FORM_SELECTORS.generalInformationSection).should(
"contain.text",
MESSAGES.slugMustBeUnique,
); );
}, },
); );

View file

@ -15,4 +15,5 @@ export const ADD_CHANNEL_FORM_SELECTORS = {
warehouseAutocompleteSelect: warehouseAutocompleteSelect:
"[data-test-id='warehouse-auto-complete-select']", "[data-test-id='warehouse-auto-complete-select']",
countryAutocompleteInput: '[data-test-id="country-select-input"]', countryAutocompleteInput: '[data-test-id="country-select-input"]',
generalInformationSection: '[data-test-id="general-information"]',
}; };

View file

@ -1,5 +1,6 @@
export const CHANNELS_SELECTORS = { export const CHANNELS_SELECTORS = {
createChannelButton: "[data-test-id='add-channel']", createChannelButton: "[data-test-id='add-channel']",
channelsTable: "[class='MuiTableBody-root']", channelsTable: "[class='MuiTableBody-root']",
channelName: "[data-test-id='name']" channelName: "[data-test-id='name']",
orderExpirationInput: "[data-test-id='delete-expired-order-input']",
}; };

View file

@ -2,4 +2,5 @@ export const MESSAGES = {
noProductFound: "No products found", noProductFound: "No products found",
confirmProductsDeletion: "Are you sure you want to delete 2 products?", confirmProductsDeletion: "Are you sure you want to delete 2 products?",
invalidEmailAddress: "Enter a valid email address.", invalidEmailAddress: "Enter a valid email address.",
slugMustBeUnique: "Slug must be unique",
}; };

View file

@ -16,13 +16,32 @@ export function createChannelByView({
defaultCountry = "Poland", defaultCountry = "Poland",
warehouse, warehouse,
}) { }) {
cy.addAliasToGraphRequest("Channel") setChannelRequiredFields({
.get(CHANNELS_SELECTORS.createChannelButton) name,
.click() currency,
.get(ADD_CHANNEL_FORM_SELECTORS.channelName) slug,
.type(name) shippingZone,
defaultCountry,
warehouse,
});
cy.get(ADD_CHANNEL_FORM_SELECTORS.saveButton).click();
}
export function setChannelRequiredFields({
name,
currency,
slug = name,
shippingZone,
defaultCountry = "Poland",
warehouse,
}) {
cy.addAliasToGraphRequest("Channel");
clickCreateChannelButton();
cy.get(ADD_CHANNEL_FORM_SELECTORS.channelName)
.click({ force: true })
.type(name, { force: true })
.get(ADD_CHANNEL_FORM_SELECTORS.slug) .get(ADD_CHANNEL_FORM_SELECTORS.slug)
.type(slug) .click({ force: true })
.type(slug, { force: true })
.get(ADD_CHANNEL_FORM_SELECTORS.currency) .get(ADD_CHANNEL_FORM_SELECTORS.currency)
.click(); .click();
cy.get(ADD_CHANNEL_FORM_SELECTORS.currency).type(currency); cy.get(ADD_CHANNEL_FORM_SELECTORS.currency).type(currency);
@ -43,7 +62,6 @@ export function createChannelByView({
if (warehouse) { if (warehouse) {
addWarehouse(warehouse); addWarehouse(warehouse);
} }
cy.get(ADD_CHANNEL_FORM_SELECTORS.saveButton).click();
} }
export function addShippingZone(shippingZone) { export function addShippingZone(shippingZone) {
@ -57,6 +75,16 @@ export function addShippingZone(shippingZone) {
shippingZone, shippingZone,
); );
} }
export function typeExpirationDays(expirationDays) {
cy.get(CHANNELS_SELECTORS.orderExpirationInput)
.click({ force: true })
.clear()
.type(expirationDays);
}
export function clickCreateChannelButton() {
return cy.get(CHANNELS_SELECTORS.createChannelButton).click();
}
export function addWarehouse(warehouse) { export function addWarehouse(warehouse) {
cy.get(BUTTON_SELECTORS.expandIcon) cy.get(BUTTON_SELECTORS.expandIcon)

View file

@ -4411,6 +4411,10 @@
"context": "page header, edit view", "context": "page header, edit view",
"string": "Edit grant refund" "string": "Edit grant refund"
}, },
"U+79k0": {
"context": "order expiration card description",
"string": "The time in days after expired orders will be deleted. Allowed range between 1 and 120."
},
"U1eJIw": { "U1eJIw": {
"context": "order history message", "context": "order history message",
"string": "Products were added to an order" "string": "Products were added to an order"
@ -5269,10 +5273,6 @@
"context": "header", "context": "header",
"string": "Password" "string": "Password"
}, },
"ZhaXLU": {
"context": "button",
"string": "Copy"
},
"ZhqH8J": { "ZhqH8J": {
"context": "button", "context": "button",
"string": "Copy headers" "string": "Copy headers"
@ -6659,6 +6659,10 @@
"kTr2o8": { "kTr2o8": {
"string": "Attribute name" "string": "Attribute name"
}, },
"kVKTwC": {
"context": "order expiration card title",
"string": "Order expiration"
},
"kVOslW": { "kVOslW": {
"context": "reason for discount label", "context": "reason for discount label",
"string": "Reason for discount" "string": "Reason for discount"

View file

@ -3,8 +3,7 @@ import {
ChannelShippingZones, ChannelShippingZones,
ChannelWarehouses, ChannelWarehouses,
} from "@dashboard/channels/pages/ChannelDetailsPage/types"; } from "@dashboard/channels/pages/ChannelDetailsPage/types";
import CardSpacer from "@dashboard/components/CardSpacer"; import { DashboardCard } from "@dashboard/components/Card";
import CardTitle from "@dashboard/components/CardTitle";
import ControlledSwitch from "@dashboard/components/ControlledSwitch"; import ControlledSwitch from "@dashboard/components/ControlledSwitch";
import FormSpacer from "@dashboard/components/FormSpacer"; import FormSpacer from "@dashboard/components/FormSpacer";
import Link from "@dashboard/components/Link"; import Link from "@dashboard/components/Link";
@ -23,18 +22,11 @@ import { ChangeEvent, FormChange } from "@dashboard/hooks/useForm";
import { commonMessages } from "@dashboard/intl"; import { commonMessages } from "@dashboard/intl";
import { getFormErrors } from "@dashboard/utils/errors"; import { getFormErrors } from "@dashboard/utils/errors";
import getChannelsErrorMessage from "@dashboard/utils/errors/channels"; import getChannelsErrorMessage from "@dashboard/utils/errors/channels";
import { import { Box, Button, CopyIcon, Input, Text } from "@saleor/macaw-ui/next";
Card,
CardContent,
InputAdornment,
TextField,
Typography,
} from "@material-ui/core";
import { Box } from "@saleor/macaw-ui/next";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
import { useStyles } from "../styles"; import { messages } from "./messages";
import { ExtendedFormHelperTextProps } from "./types"; import { ExtendedFormHelperTextProps } from "./types";
export interface FormData extends StockSettingsInput { export interface FormData extends StockSettingsInput {
@ -49,6 +41,7 @@ export interface FormData extends StockSettingsInput {
warehousesToDisplay: ChannelWarehouses; warehousesToDisplay: ChannelWarehouses;
defaultCountry: CountryCode; defaultCountry: CountryCode;
markAsPaidStrategy: MarkAsPaidStrategyEnum; markAsPaidStrategy: MarkAsPaidStrategyEnum;
deleteExpiredOrdersAfter: number;
} }
export interface ChannelFormProps { export interface ChannelFormProps {
@ -79,91 +72,62 @@ export const ChannelForm: React.FC<ChannelFormProps> = ({
onMarkAsPaidStrategyChange, onMarkAsPaidStrategyChange,
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const [copied, copy] = useClipboard(); const [, copy] = useClipboard();
const formErrors = getFormErrors<keyof FormData, ChannelErrorFragment>( const formErrors = getFormErrors<keyof FormData, ChannelErrorFragment>(
["name", "slug", "currencyCode", "defaultCountry"], [
"name",
"slug",
"currencyCode",
"defaultCountry",
"deleteExpiredOrdersAfter",
],
errors, errors,
); );
const classes = useStyles();
return ( return (
<> <>
<Card> <DashboardCard>
<CardTitle <DashboardCard.Title>
title={intl.formatMessage(commonMessages.generalInformations)} {intl.formatMessage(commonMessages.generalInformations)}
/> </DashboardCard.Title>
<CardContent> <DashboardCard.Content data-test-id="general-information">
<TextField <Input
error={!!formErrors.name} error={!!formErrors.name}
helperText={getChannelsErrorMessage(formErrors?.name, intl)} helperText={getChannelsErrorMessage(formErrors?.name, intl)}
disabled={disabled} disabled={disabled}
fullWidth label={intl.formatMessage(messages.channelName)}
label={intl.formatMessage({
id: "UymotP",
defaultMessage: "Channel name",
description: "channel name",
})}
name="name" name="name"
value={data.name} value={data.name}
onChange={onChange} onChange={onChange}
/> />
<FormSpacer /> <FormSpacer />
<TextField <Input
error={!!formErrors.slug} error={!!formErrors.slug}
helperText={getChannelsErrorMessage(formErrors?.slug, intl)} helperText={getChannelsErrorMessage(formErrors?.slug, intl)}
disabled={disabled} disabled={disabled}
fullWidth label={intl.formatMessage(messages.channelSlug)}
FormHelperTextProps={
{
"data-test-id": "slug-text-input-helper-text",
} as ExtendedFormHelperTextProps
}
label={intl.formatMessage({
id: "74Zo/H",
defaultMessage: "Slug",
description: "channel slug",
})}
name="slug" name="slug"
value={data.slug} value={data.slug}
onChange={onChange} onChange={onChange}
InputProps={{ endAdornment={
endAdornment: ( <Button
<InputAdornment variant="tertiary"
className={classes.copyBtn} onClick={() => copy(data.slug)}
position="end" textTransform="uppercase"
disableTypography icon={<CopyIcon />}
onClick={() => copy(data.slug)} />
> }
{copied ? (
<FormattedMessage
id="r86alc"
defaultMessage="Copied"
description="button"
/>
) : (
<FormattedMessage
id="ZhaXLU"
defaultMessage="Copy"
description="button"
/>
)}
</InputAdornment>
),
}}
/> />
<FormSpacer /> </DashboardCard.Content>
</CardContent> </DashboardCard>
</Card> <Box display="grid" __gridTemplateColumns="2fr 1fr" rowGap={2}>
<CardSpacer /> <Text variant="heading" margin={6}>
<Card> <FormattedMessage {...messages.channelSettings} />
<CardTitle </Text>
title={intl.formatMessage({ <Text variant="heading" margin={6}>
id: "3y4r+z", <FormattedMessage {...messages.orderExpiration} />
defaultMessage: "Channel Settings", </Text>
description: "channel settings", <Box paddingX={6}>
})}
/>
<CardContent>
{currencyCodes ? ( {currencyCodes ? (
<SingleAutocompleteSelectField <SingleAutocompleteSelectField
data-test-id="channel-currency-select-input" data-test-id="channel-currency-select-input"
@ -179,11 +143,7 @@ export const ChannelForm: React.FC<ChannelFormProps> = ({
intl, intl,
)} )}
disabled={disabled} disabled={disabled}
label={intl.formatMessage({ label={intl.formatMessage(messages.channelCurrency)}
id: "9Sz0By",
defaultMessage: "Currency",
description: "channel currency",
})}
choices={currencyCodes} choices={currencyCodes}
name="currencyCode" name="currencyCode"
displayValue={selectedCurrencyCode} displayValue={selectedCurrencyCode}
@ -191,18 +151,18 @@ export const ChannelForm: React.FC<ChannelFormProps> = ({
onChange={onCurrencyCodeChange} onChange={onCurrencyCodeChange}
/> />
) : ( ) : (
<> <Box display="flex" flexDirection="column">
<Typography variant="caption" className={classes.label}> <Text variant="caption">
<FormattedMessage <FormattedMessage {...messages.selectedCurrency} />
id="39yi8w" </Text>
defaultMessage="Selected currency" <Text>{data.currencyCode}</Text>
description="selected currency" </Box>
/>
</Typography>
<Typography>{data.currencyCode}</Typography>
</>
)} )}
<FormSpacer /> </Box>
<Text variant="caption" paddingX={6}>
<FormattedMessage {...messages.orderExpirationDescription} />
</Text>
<Box paddingX={6}>
<SingleAutocompleteSelectField <SingleAutocompleteSelectField
data-test-id="country-select-input" data-test-id="country-select-input"
error={!!formErrors.defaultCountry} error={!!formErrors.defaultCountry}
@ -216,73 +176,82 @@ export const ChannelForm: React.FC<ChannelFormProps> = ({
intl, intl,
)} )}
disabled={disabled} disabled={disabled}
label={intl.formatMessage({ label={intl.formatMessage(messages.defaultCountry)}
id: "tV+Dcm",
defaultMessage: "Default country",
})}
choices={countries} choices={countries}
name="defaultCountry" name="defaultCountry"
displayValue={selectedCountryDisplayName} displayValue={selectedCountryDisplayName}
value={data.defaultCountry} value={data.defaultCountry}
onChange={onDefaultCountryChange} onChange={onDefaultCountryChange}
/> />
<FormSpacer /> </Box>
<Box display="flex" gap={1.5} alignItems="center"> <Box paddingX={6}>
<ControlledSwitch <Input
data-test-id="order-settings-mark-as-paid" name="deleteExpiredOrdersAfter"
disabled={disabled} data-test-id="delete-expired-order-input"
checked={ value={data.deleteExpiredOrdersAfter}
data.markAsPaidStrategy === error={!!formErrors.deleteExpiredOrdersAfter}
MarkAsPaidStrategyEnum.TRANSACTION_FLOW type="number"
} label="TTL"
onChange={onMarkAsPaidStrategyChange} onChange={onChange}
name="markAsPaidStrategy" min={0}
label={ max={120}
<span> // TODO: Should be removed after single autocomplete
<FormattedMessage // select is migrated to macaw inputs
defaultMessage='"Mark as paid" feature creates a' __height={12.5}
id="MDOw8D" />
/>{" "} </Box>
<Link <Box display="flex" gap={1.5} alignItems="center" paddingX={6}>
href="https://docs.saleor.io/docs/3.x/developer/payments#processing-a-payment-with-payment-app" <ControlledSwitch
target="_blank" data-test-id="order-settings-mark-as-paid"
rel="noopener noreferer" disabled={disabled}
> checked={
<FormattedMessage data.markAsPaidStrategy ===
defaultMessage="Transaction" MarkAsPaidStrategyEnum.TRANSACTION_FLOW
id="1+ROfp" }
/> onChange={onMarkAsPaidStrategyChange}
</Link>{" "} name="markAsPaidStrategy"
<FormattedMessage label={
defaultMessage="- used by Payment Apps" <span>
id="Fqe4aB" <FormattedMessage
/> defaultMessage='"Mark as paid" feature creates a'
</span> id="MDOw8D"
} />{" "}
secondLabel={ <Link
<span> href="https://docs.saleor.io/docs/3.x/developer/payments#processing-a-payment-with-payment-app"
<FormattedMessage target="_blank"
defaultMessage="If left unchecked it creates a" rel="noopener noreferer"
id="hHv0ih" >
/>{" "} <FormattedMessage defaultMessage="Transaction" id="1+ROfp" />
<Link </Link>{" "}
href="https://docs.saleor.io/docs/3.x/developer/payments#payment-plugin" <FormattedMessage
target="_blank" defaultMessage="- used by Payment Apps"
rel="noopener noreferer" id="Fqe4aB"
> />
<FormattedMessage defaultMessage="Payment" id="NmK6zy" /> </span>
</Link>{" "} }
<FormattedMessage secondLabel={
defaultMessage="- used by Payment Plugins" <span>
id="50lR2F" <FormattedMessage
/> defaultMessage="If left unchecked it creates a"
</span> id="hHv0ih"
} />{" "}
/> <Link
<PreviewPill /> href="https://docs.saleor.io/docs/3.x/developer/payments#payment-plugin"
</Box> target="_blank"
</CardContent> rel="noopener noreferer"
</Card> >
<FormattedMessage defaultMessage="Payment" id="NmK6zy" />
</Link>{" "}
<FormattedMessage
defaultMessage="- used by Payment Plugins"
id="50lR2F"
/>
</span>
}
/>
<PreviewPill />
</Box>
</Box>
</> </>
); );
}; };

View file

@ -0,0 +1,44 @@
import { defineMessages } from "react-intl";
export const messages = defineMessages({
channelName: {
id: "UymotP",
defaultMessage: "Channel name",
description: "channel name",
},
channelSlug: {
id: "74Zo/H",
defaultMessage: "Slug",
description: "channel slug",
},
channelSettings: {
id: "3y4r+z",
defaultMessage: "Channel Settings",
description: "channel settings",
},
channelCurrency: {
id: "9Sz0By",
defaultMessage: "Currency",
description: "channel currency",
},
selectedCurrency: {
id: "39yi8w",
defaultMessage: "Selected currency",
description: "selected currency",
},
defaultCountry: {
id: "tV+Dcm",
defaultMessage: "Default country",
},
orderExpiration: {
id: "kVKTwC",
defaultMessage: "Order expiration",
description: "order expiration card title",
},
orderExpirationDescription: {
id: 'U+79k0',
defaultMessage:
"The time in days after expired orders will be deleted. Allowed range between 1 and 120.",
description: "order expiration card description",
},
});

View file

@ -1,6 +1,6 @@
import { Button } from "@dashboard/components/Button";
import CardTitle from "@dashboard/components/CardTitle"; import CardTitle from "@dashboard/components/CardTitle";
import { Card, CardContent, Typography } from "@material-ui/core"; import { Card, CardContent, Typography } from "@material-ui/core";
import { Button } from "@saleor/macaw-ui/next";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
@ -53,9 +53,10 @@ export const ChannelStatus: React.FC<ChannelStatusProps> = ({
)} )}
</Typography> </Typography>
<Button <Button
className={classes.activeBtn} variant="secondary"
disabled={disabled} disabled={disabled}
onClick={() => updateChannelStatus()} onClick={() => updateChannelStatus()}
marginTop={2}
> >
{isActive ? ( {isActive ? (
<FormattedMessage <FormattedMessage

View file

@ -50,6 +50,7 @@ export const channelsList: ChannelDetailsFragment[] = [
], ],
orderSettings: { orderSettings: {
markAsPaidStrategy: MarkAsPaidStrategyEnum.TRANSACTION_FLOW, markAsPaidStrategy: MarkAsPaidStrategyEnum.TRANSACTION_FLOW,
deleteExpiredOrdersAfter: 60,
__typename: "OrderSettings", __typename: "OrderSettings",
}, },
}, },
@ -84,6 +85,7 @@ export const channelsList: ChannelDetailsFragment[] = [
], ],
orderSettings: { orderSettings: {
markAsPaidStrategy: MarkAsPaidStrategyEnum.TRANSACTION_FLOW, markAsPaidStrategy: MarkAsPaidStrategyEnum.TRANSACTION_FLOW,
deleteExpiredOrdersAfter: 60,
__typename: "OrderSettings", __typename: "OrderSettings",
}, },
}, },
@ -118,6 +120,7 @@ export const channelsList: ChannelDetailsFragment[] = [
], ],
orderSettings: { orderSettings: {
markAsPaidStrategy: MarkAsPaidStrategyEnum.TRANSACTION_FLOW, markAsPaidStrategy: MarkAsPaidStrategyEnum.TRANSACTION_FLOW,
deleteExpiredOrdersAfter: 60,
__typename: "OrderSettings", __typename: "OrderSettings",
}, },
}, },
@ -152,6 +155,7 @@ export const channelsList: ChannelDetailsFragment[] = [
], ],
orderSettings: { orderSettings: {
markAsPaidStrategy: MarkAsPaidStrategyEnum.TRANSACTION_FLOW, markAsPaidStrategy: MarkAsPaidStrategyEnum.TRANSACTION_FLOW,
deleteExpiredOrdersAfter: 60,
__typename: "OrderSettings", __typename: "OrderSettings",
}, },
}, },
@ -186,6 +190,7 @@ export const channelsList: ChannelDetailsFragment[] = [
], ],
orderSettings: { orderSettings: {
markAsPaidStrategy: MarkAsPaidStrategyEnum.TRANSACTION_FLOW, markAsPaidStrategy: MarkAsPaidStrategyEnum.TRANSACTION_FLOW,
deleteExpiredOrdersAfter: 60,
__typename: "OrderSettings", __typename: "OrderSettings",
}, },
}, },
@ -220,6 +225,7 @@ export const channelsList: ChannelDetailsFragment[] = [
], ],
orderSettings: { orderSettings: {
markAsPaidStrategy: MarkAsPaidStrategyEnum.TRANSACTION_FLOW, markAsPaidStrategy: MarkAsPaidStrategyEnum.TRANSACTION_FLOW,
deleteExpiredOrdersAfter: 60,
__typename: "OrderSettings", __typename: "OrderSettings",
}, },
}, },
@ -254,6 +260,7 @@ export const channelsList: ChannelDetailsFragment[] = [
], ],
orderSettings: { orderSettings: {
markAsPaidStrategy: MarkAsPaidStrategyEnum.TRANSACTION_FLOW, markAsPaidStrategy: MarkAsPaidStrategyEnum.TRANSACTION_FLOW,
deleteExpiredOrdersAfter: 60,
__typename: "OrderSettings", __typename: "OrderSettings",
}, },
}, },
@ -290,6 +297,7 @@ export const channel: ChannelDetailsFragment = {
], ],
orderSettings: { orderSettings: {
markAsPaidStrategy: MarkAsPaidStrategyEnum.TRANSACTION_FLOW, markAsPaidStrategy: MarkAsPaidStrategyEnum.TRANSACTION_FLOW,
deleteExpiredOrdersAfter: 60,
__typename: "OrderSettings", __typename: "OrderSettings",
}, },
}; };

View file

@ -126,6 +126,7 @@ const ChannelDetailsPage = function <TErrors extends ChannelErrorFragment[]>({
shippingZonesToDisplay: channelShippingZones, shippingZonesToDisplay: channelShippingZones,
warehousesToDisplay: channelWarehouses, warehousesToDisplay: channelWarehouses,
markAsPaidStrategy: orderSettings?.markAsPaidStrategy, markAsPaidStrategy: orderSettings?.markAsPaidStrategy,
deleteExpiredOrdersAfter: orderSettings?.deleteExpiredOrdersAfter,
}; };
const getFilteredShippingZonesChoices = ( const getFilteredShippingZonesChoices = (

View file

@ -11,8 +11,8 @@ import {
import { getSearchFetchMoreProps } from "@dashboard/hooks/makeTopLevelSearch/utils"; import { getSearchFetchMoreProps } from "@dashboard/hooks/makeTopLevelSearch/utils";
import useNavigator from "@dashboard/hooks/useNavigator"; import useNavigator from "@dashboard/hooks/useNavigator";
import useNotifier from "@dashboard/hooks/useNotifier"; import useNotifier from "@dashboard/hooks/useNotifier";
import { getDefaultNotifierSuccessErrorData } from "@dashboard/hooks/useNotifier/utils";
import useShop from "@dashboard/hooks/useShop"; import useShop from "@dashboard/hooks/useShop";
import { commonMessages } from "@dashboard/intl";
import { extractMutationErrors } from "@dashboard/misc"; import { extractMutationErrors } from "@dashboard/misc";
import getChannelsErrorMessage from "@dashboard/utils/errors/channels"; import getChannelsErrorMessage from "@dashboard/utils/errors/channels";
import currencyCodes from "currency-codes"; import currencyCodes from "currency-codes";
@ -25,7 +25,7 @@ import { calculateItemsOrderMoves } from "../ChannelDetails/handlers";
import { useShippingZones } from "../ChannelDetails/useShippingZones"; import { useShippingZones } from "../ChannelDetails/useShippingZones";
import { useWarehouses } from "../ChannelDetails/useWarehouses"; import { useWarehouses } from "../ChannelDetails/useWarehouses";
export const ChannelCreateView = ({}) => { export const ChannelCreateView = () => {
const navigate = useNavigator(); const navigate = useNavigator();
const notify = useNotifier(); const notify = useNotifier();
const intl = useIntl(); const intl = useIntl();
@ -40,7 +40,12 @@ export const ChannelCreateView = ({}) => {
const [createChannel, createChannelOpts] = useChannelCreateMutation({ const [createChannel, createChannelOpts] = useChannelCreateMutation({
onCompleted: ({ channelCreate: { errors } }: ChannelCreateMutation) => { onCompleted: ({ channelCreate: { errors } }: ChannelCreateMutation) => {
notify(getDefaultNotifierSuccessErrorData(errors, intl)); if (!errors.length) {
notify({
status: "success",
text: intl.formatMessage(commonMessages.savedChanges),
});
}
}, },
}); });
@ -66,6 +71,7 @@ export const ChannelCreateView = ({}) => {
slug, slug,
defaultCountry, defaultCountry,
markAsPaidStrategy, markAsPaidStrategy,
deleteExpiredOrdersAfter,
}: FormData) => { }: FormData) => {
const input: ChannelCreateInput = { const input: ChannelCreateInput = {
defaultCountry, defaultCountry,
@ -79,6 +85,7 @@ export const ChannelCreateView = ({}) => {
}, },
orderSettings: { orderSettings: {
markAsPaidStrategy, markAsPaidStrategy,
deleteExpiredOrdersAfter,
}, },
}; };

View file

@ -116,6 +116,7 @@ export const ChannelDetails: React.FC<ChannelDetailsProps> = ({
defaultCountry, defaultCountry,
allocationStrategy, allocationStrategy,
markAsPaidStrategy, markAsPaidStrategy,
deleteExpiredOrdersAfter,
}: FormData) => { }: FormData) => {
const updateChannelMutation = updateChannel({ const updateChannelMutation = updateChannel({
variables: { variables: {
@ -133,6 +134,7 @@ export const ChannelDetails: React.FC<ChannelDetailsProps> = ({
}, },
orderSettings: { orderSettings: {
markAsPaidStrategy, markAsPaidStrategy,
deleteExpiredOrdersAfter,
}, },
}, },
}, },

View file

@ -34,6 +34,7 @@ export const channelDetailsFragment = gql`
} }
orderSettings { orderSettings {
markAsPaidStrategy markAsPaidStrategy
deleteExpiredOrdersAfter
} }
} }
`; `;

View file

@ -254,6 +254,7 @@ export const ChannelDetailsFragmentDoc = gql`
} }
orderSettings { orderSettings {
markAsPaidStrategy markAsPaidStrategy
deleteExpiredOrdersAfter
} }
} }
${ChannelFragmentDoc} ${ChannelFragmentDoc}

View file

@ -8255,7 +8255,7 @@ export type ChannelCreateMutationVariables = Exact<{
}>; }>;
export type ChannelCreateMutation = { __typename: 'Mutation', channelCreate: { __typename: 'ChannelCreate', channel: { __typename: 'Channel', hasOrders: boolean, id: string, isActive: boolean, name: string, slug: string, currencyCode: string, warehouses: Array<{ __typename: 'Warehouse', id: string, name: string }>, orderSettings: { __typename: 'OrderSettings', markAsPaidStrategy: MarkAsPaidStrategyEnum }, defaultCountry: { __typename: 'CountryDisplay', code: string, country: string }, stockSettings: { __typename: 'StockSettings', allocationStrategy: AllocationStrategyEnum } } | null, errors: Array<{ __typename: 'ChannelError', code: ChannelErrorCode, field: string | null, message: string | null }> } | null }; export type ChannelCreateMutation = { __typename: 'Mutation', channelCreate: { __typename: 'ChannelCreate', channel: { __typename: 'Channel', hasOrders: boolean, id: string, isActive: boolean, name: string, slug: string, currencyCode: string, warehouses: Array<{ __typename: 'Warehouse', id: string, name: string }>, orderSettings: { __typename: 'OrderSettings', markAsPaidStrategy: MarkAsPaidStrategyEnum, deleteExpiredOrdersAfter: any }, defaultCountry: { __typename: 'CountryDisplay', code: string, country: string }, stockSettings: { __typename: 'StockSettings', allocationStrategy: AllocationStrategyEnum } } | null, errors: Array<{ __typename: 'ChannelError', code: ChannelErrorCode, field: string | null, message: string | null }> } | null };
export type ChannelUpdateMutationVariables = Exact<{ export type ChannelUpdateMutationVariables = Exact<{
id: Scalars['ID']; id: Scalars['ID'];
@ -8263,7 +8263,7 @@ export type ChannelUpdateMutationVariables = Exact<{
}>; }>;
export type ChannelUpdateMutation = { __typename: 'Mutation', channelUpdate: { __typename: 'ChannelUpdate', channel: { __typename: 'Channel', hasOrders: boolean, id: string, isActive: boolean, name: string, slug: string, currencyCode: string, warehouses: Array<{ __typename: 'Warehouse', id: string, name: string }>, orderSettings: { __typename: 'OrderSettings', markAsPaidStrategy: MarkAsPaidStrategyEnum }, defaultCountry: { __typename: 'CountryDisplay', code: string, country: string }, stockSettings: { __typename: 'StockSettings', allocationStrategy: AllocationStrategyEnum } } | null, errors: Array<{ __typename: 'ChannelError', code: ChannelErrorCode, field: string | null, message: string | null }> } | null }; export type ChannelUpdateMutation = { __typename: 'Mutation', channelUpdate: { __typename: 'ChannelUpdate', channel: { __typename: 'Channel', hasOrders: boolean, id: string, isActive: boolean, name: string, slug: string, currencyCode: string, warehouses: Array<{ __typename: 'Warehouse', id: string, name: string }>, orderSettings: { __typename: 'OrderSettings', markAsPaidStrategy: MarkAsPaidStrategyEnum, deleteExpiredOrdersAfter: any }, defaultCountry: { __typename: 'CountryDisplay', code: string, country: string }, stockSettings: { __typename: 'StockSettings', allocationStrategy: AllocationStrategyEnum } } | null, errors: Array<{ __typename: 'ChannelError', code: ChannelErrorCode, field: string | null, message: string | null }> } | null };
export type ChannelDeleteMutationVariables = Exact<{ export type ChannelDeleteMutationVariables = Exact<{
id: Scalars['ID']; id: Scalars['ID'];
@ -8278,14 +8278,14 @@ export type ChannelActivateMutationVariables = Exact<{
}>; }>;
export type ChannelActivateMutation = { __typename: 'Mutation', channelActivate: { __typename: 'ChannelActivate', channel: { __typename: 'Channel', hasOrders: boolean, id: string, isActive: boolean, name: string, slug: string, currencyCode: string, warehouses: Array<{ __typename: 'Warehouse', id: string, name: string }>, orderSettings: { __typename: 'OrderSettings', markAsPaidStrategy: MarkAsPaidStrategyEnum }, defaultCountry: { __typename: 'CountryDisplay', code: string, country: string }, stockSettings: { __typename: 'StockSettings', allocationStrategy: AllocationStrategyEnum } } | null, errors: Array<{ __typename: 'ChannelError', code: ChannelErrorCode, field: string | null, message: string | null }> } | null }; export type ChannelActivateMutation = { __typename: 'Mutation', channelActivate: { __typename: 'ChannelActivate', channel: { __typename: 'Channel', hasOrders: boolean, id: string, isActive: boolean, name: string, slug: string, currencyCode: string, warehouses: Array<{ __typename: 'Warehouse', id: string, name: string }>, orderSettings: { __typename: 'OrderSettings', markAsPaidStrategy: MarkAsPaidStrategyEnum, deleteExpiredOrdersAfter: any }, defaultCountry: { __typename: 'CountryDisplay', code: string, country: string }, stockSettings: { __typename: 'StockSettings', allocationStrategy: AllocationStrategyEnum } } | null, errors: Array<{ __typename: 'ChannelError', code: ChannelErrorCode, field: string | null, message: string | null }> } | null };
export type ChannelDeactivateMutationVariables = Exact<{ export type ChannelDeactivateMutationVariables = Exact<{
id: Scalars['ID']; id: Scalars['ID'];
}>; }>;
export type ChannelDeactivateMutation = { __typename: 'Mutation', channelDeactivate: { __typename: 'ChannelDeactivate', channel: { __typename: 'Channel', hasOrders: boolean, id: string, isActive: boolean, name: string, slug: string, currencyCode: string, warehouses: Array<{ __typename: 'Warehouse', id: string, name: string }>, orderSettings: { __typename: 'OrderSettings', markAsPaidStrategy: MarkAsPaidStrategyEnum }, defaultCountry: { __typename: 'CountryDisplay', code: string, country: string }, stockSettings: { __typename: 'StockSettings', allocationStrategy: AllocationStrategyEnum } } | null, errors: Array<{ __typename: 'ChannelError', code: ChannelErrorCode, field: string | null, message: string | null }> } | null }; export type ChannelDeactivateMutation = { __typename: 'Mutation', channelDeactivate: { __typename: 'ChannelDeactivate', channel: { __typename: 'Channel', hasOrders: boolean, id: string, isActive: boolean, name: string, slug: string, currencyCode: string, warehouses: Array<{ __typename: 'Warehouse', id: string, name: string }>, orderSettings: { __typename: 'OrderSettings', markAsPaidStrategy: MarkAsPaidStrategyEnum, deleteExpiredOrdersAfter: any }, defaultCountry: { __typename: 'CountryDisplay', code: string, country: string }, stockSettings: { __typename: 'StockSettings', allocationStrategy: AllocationStrategyEnum } } | null, errors: Array<{ __typename: 'ChannelError', code: ChannelErrorCode, field: string | null, message: string | null }> } | null };
export type ChannelReorderWarehousesMutationVariables = Exact<{ export type ChannelReorderWarehousesMutationVariables = Exact<{
channelId: Scalars['ID']; channelId: Scalars['ID'];
@ -8293,7 +8293,7 @@ export type ChannelReorderWarehousesMutationVariables = Exact<{
}>; }>;
export type ChannelReorderWarehousesMutation = { __typename: 'Mutation', channelReorderWarehouses: { __typename: 'ChannelReorderWarehouses', channel: { __typename: 'Channel', hasOrders: boolean, id: string, isActive: boolean, name: string, slug: string, currencyCode: string, warehouses: Array<{ __typename: 'Warehouse', id: string, name: string }>, orderSettings: { __typename: 'OrderSettings', markAsPaidStrategy: MarkAsPaidStrategyEnum }, defaultCountry: { __typename: 'CountryDisplay', code: string, country: string }, stockSettings: { __typename: 'StockSettings', allocationStrategy: AllocationStrategyEnum } } | null, errors: Array<{ __typename: 'ChannelError', code: ChannelErrorCode, field: string | null, message: string | null }> } | null }; export type ChannelReorderWarehousesMutation = { __typename: 'Mutation', channelReorderWarehouses: { __typename: 'ChannelReorderWarehouses', channel: { __typename: 'Channel', hasOrders: boolean, id: string, isActive: boolean, name: string, slug: string, currencyCode: string, warehouses: Array<{ __typename: 'Warehouse', id: string, name: string }>, orderSettings: { __typename: 'OrderSettings', markAsPaidStrategy: MarkAsPaidStrategyEnum, deleteExpiredOrdersAfter: any }, defaultCountry: { __typename: 'CountryDisplay', code: string, country: string }, stockSettings: { __typename: 'StockSettings', allocationStrategy: AllocationStrategyEnum } } | null, errors: Array<{ __typename: 'ChannelError', code: ChannelErrorCode, field: string | null, message: string | null }> } | null };
export type BaseChannelsQueryVariables = Exact<{ [key: string]: never; }>; export type BaseChannelsQueryVariables = Exact<{ [key: string]: never; }>;
@ -8303,14 +8303,14 @@ export type BaseChannelsQuery = { __typename: 'Query', channels: Array<{ __typen
export type ChannelsQueryVariables = Exact<{ [key: string]: never; }>; export type ChannelsQueryVariables = Exact<{ [key: string]: never; }>;
export type ChannelsQuery = { __typename: 'Query', channels: Array<{ __typename: 'Channel', hasOrders: boolean, id: string, isActive: boolean, name: string, slug: string, currencyCode: string, warehouses: Array<{ __typename: 'Warehouse', id: string, name: string }>, orderSettings: { __typename: 'OrderSettings', markAsPaidStrategy: MarkAsPaidStrategyEnum }, defaultCountry: { __typename: 'CountryDisplay', code: string, country: string }, stockSettings: { __typename: 'StockSettings', allocationStrategy: AllocationStrategyEnum } }> | null }; export type ChannelsQuery = { __typename: 'Query', channels: Array<{ __typename: 'Channel', hasOrders: boolean, id: string, isActive: boolean, name: string, slug: string, currencyCode: string, warehouses: Array<{ __typename: 'Warehouse', id: string, name: string }>, orderSettings: { __typename: 'OrderSettings', markAsPaidStrategy: MarkAsPaidStrategyEnum, deleteExpiredOrdersAfter: any }, defaultCountry: { __typename: 'CountryDisplay', code: string, country: string }, stockSettings: { __typename: 'StockSettings', allocationStrategy: AllocationStrategyEnum } }> | null };
export type ChannelQueryVariables = Exact<{ export type ChannelQueryVariables = Exact<{
id: Scalars['ID']; id: Scalars['ID'];
}>; }>;
export type ChannelQuery = { __typename: 'Query', channel: { __typename: 'Channel', hasOrders: boolean, id: string, isActive: boolean, name: string, slug: string, currencyCode: string, warehouses: Array<{ __typename: 'Warehouse', id: string, name: string }>, orderSettings: { __typename: 'OrderSettings', markAsPaidStrategy: MarkAsPaidStrategyEnum }, defaultCountry: { __typename: 'CountryDisplay', code: string, country: string }, stockSettings: { __typename: 'StockSettings', allocationStrategy: AllocationStrategyEnum } } | null }; export type ChannelQuery = { __typename: 'Query', channel: { __typename: 'Channel', hasOrders: boolean, id: string, isActive: boolean, name: string, slug: string, currencyCode: string, warehouses: Array<{ __typename: 'Warehouse', id: string, name: string }>, orderSettings: { __typename: 'OrderSettings', markAsPaidStrategy: MarkAsPaidStrategyEnum, deleteExpiredOrdersAfter: any }, defaultCountry: { __typename: 'CountryDisplay', code: string, country: string }, stockSettings: { __typename: 'StockSettings', allocationStrategy: AllocationStrategyEnum } } | null };
export type CollectionUpdateMutationVariables = Exact<{ export type CollectionUpdateMutationVariables = Exact<{
id: Scalars['ID']; id: Scalars['ID'];
@ -8924,7 +8924,7 @@ export type ChannelErrorFragment = { __typename: 'ChannelError', code: ChannelEr
export type ChannelFragment = { __typename: 'Channel', id: string, isActive: boolean, name: string, slug: string, currencyCode: string, defaultCountry: { __typename: 'CountryDisplay', code: string, country: string }, stockSettings: { __typename: 'StockSettings', allocationStrategy: AllocationStrategyEnum } }; export type ChannelFragment = { __typename: 'Channel', id: string, isActive: boolean, name: string, slug: string, currencyCode: string, defaultCountry: { __typename: 'CountryDisplay', code: string, country: string }, stockSettings: { __typename: 'StockSettings', allocationStrategy: AllocationStrategyEnum } };
export type ChannelDetailsFragment = { __typename: 'Channel', hasOrders: boolean, id: string, isActive: boolean, name: string, slug: string, currencyCode: string, warehouses: Array<{ __typename: 'Warehouse', id: string, name: string }>, orderSettings: { __typename: 'OrderSettings', markAsPaidStrategy: MarkAsPaidStrategyEnum }, defaultCountry: { __typename: 'CountryDisplay', code: string, country: string }, stockSettings: { __typename: 'StockSettings', allocationStrategy: AllocationStrategyEnum } }; export type ChannelDetailsFragment = { __typename: 'Channel', hasOrders: boolean, id: string, isActive: boolean, name: string, slug: string, currencyCode: string, warehouses: Array<{ __typename: 'Warehouse', id: string, name: string }>, orderSettings: { __typename: 'OrderSettings', markAsPaidStrategy: MarkAsPaidStrategyEnum, deleteExpiredOrdersAfter: any }, defaultCountry: { __typename: 'CountryDisplay', code: string, country: string }, stockSettings: { __typename: 'StockSettings', allocationStrategy: AllocationStrategyEnum } };
export type CollectionFragment = { __typename: 'Collection', id: string, name: string, channelListings: Array<{ __typename: 'CollectionChannelListing', isPublished: boolean, publicationDate: any | null, channel: { __typename: 'Channel', id: string, name: string } }> | null }; export type CollectionFragment = { __typename: 'Collection', id: string, name: string, channelListings: Array<{ __typename: 'CollectionChannelListing', isPublished: boolean, publicationDate: any | null, channel: { __typename: 'Channel', id: string, name: string } }> | null };