Skip to content

Commit 15eced3

Browse files
Allow changing useLegacyShippingZoneStockAvailability shop settings (#6536)
Co-authored-by: Lukasz Ostrowski <lukasz.ostrowski@saleor.io>
1 parent 71222e4 commit 15eced3

9 files changed

Lines changed: 125 additions & 4 deletions

File tree

.changeset/quick-items-change.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"saleor-dashboard": patch
3+
---
4+
5+
Added a "Stock availability" toggle in Site Settings to control `useLegacyShippingZoneStockAvailability`.
6+

locale/defaultMessages.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1472,6 +1472,10 @@
14721472
"6Y1YDn": {
14731473
"string": "Updated extension permissions"
14741474
},
1475+
"6ZnMfQ": {
1476+
"context": "card header and checkbox label",
1477+
"string": "Use legacy shipping zone stock availability"
1478+
},
14751479
"6ZubLQ": {
14761480
"string": "Manage available refunds reasons"
14771481
},
@@ -9710,6 +9714,10 @@
97109714
"context": "order history message",
97119715
"string": "Invoice was sent to customer by {sentBy}"
97129716
},
9717+
"qeAmFa": {
9718+
"context": "section description",
9719+
"string": "When enabled, stock availability is filtered by shipping zones and the destination address (legacy behavior). When disabled, it is determined only by the direct warehouse-channel link. <a>Learn more</a>."
9720+
},
97139721
"qf8OtW": {
97149722
"string": "Continue with Saleor Cloud"
97159723
},
@@ -10496,6 +10504,10 @@
1049610504
"v3WWK+": {
1049710505
"string": "Status is invalid"
1049810506
},
10507+
"v5yLSE": {
10508+
"context": "section title",
10509+
"string": "Stock availability"
10510+
},
1049910511
"v8UngX": {
1050010512
"string": "Search warehouses..."
1050110513
},
@@ -10872,6 +10884,10 @@
1087210884
"context": "dialog header",
1087310885
"string": "Cancel order #{orderNumber}"
1087410886
},
10887+
"wmybDi": {
10888+
"context": "intro to webhook list",
10889+
"string": "When disabled, the following channel-scoped stock webhooks become available:"
10890+
},
1087510891
"wn3di2": {
1087610892
"string": "This password is too commonly used"
1087710893
},

src/fragments/shop.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export const shopFragment = gql`
5757
limitQuantityPerCheckout
5858
enableAccountConfirmationByEmail
5959
useLegacyUpdateWebhookEmission
60+
useLegacyShippingZoneStockAvailability
6061
preserveAllAddressFields
6162
passwordLoginMode
6263
}

src/graphql/hooks.generated.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3407,6 +3407,7 @@ export const ShopFragmentDoc = gql`
34073407
limitQuantityPerCheckout
34083408
enableAccountConfirmationByEmail
34093409
useLegacyUpdateWebhookEmission
3410+
useLegacyShippingZoneStockAvailability
34103411
preserveAllAddressFields
34113412
passwordLoginMode
34123413
}

src/graphql/types.generated.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11789,7 +11789,7 @@ export type LimitInfoFragment = { __typename: 'Limits', channels?: number | null
1178911789

1179011790
export type ShopLimitFragment = { __typename: 'Shop', limits: { __typename: 'LimitInfo', currentUsage: { __typename: 'Limits', channels?: number | null, orders?: number | null, productVariants?: number | null, staffUsers?: number | null, warehouses?: number | null }, allowedUsage: { __typename: 'Limits', channels?: number | null, orders?: number | null, productVariants?: number | null, staffUsers?: number | null, warehouses?: number | null } } };
1179111791

11792-
export type ShopFragment = { __typename: 'Shop', customerSetPasswordUrl: string | null, defaultMailSenderAddress: string | null, defaultMailSenderName: string | null, description: string | null, name: string, reserveStockDurationAnonymousUser: number | null, reserveStockDurationAuthenticatedUser: number | null, limitQuantityPerCheckout: number | null, enableAccountConfirmationByEmail: boolean | null, useLegacyUpdateWebhookEmission: boolean | null, preserveAllAddressFields: boolean, passwordLoginMode: PasswordLoginModeEnum, companyAddress: { __typename: 'Address', city: string, cityArea: string, companyName: string, countryArea: string, firstName: string, id: string, lastName: string, phone: string | null, postalCode: string, streetAddress1: string, streetAddress2: string, country: { __typename: 'CountryDisplay', code: string, country: string } } | null, countries: Array<{ __typename: 'CountryDisplay', code: string, country: string }>, domain: { __typename: 'Domain', host: string } };
11792+
export type ShopFragment = { __typename: 'Shop', customerSetPasswordUrl: string | null, defaultMailSenderAddress: string | null, defaultMailSenderName: string | null, description: string | null, name: string, reserveStockDurationAnonymousUser: number | null, reserveStockDurationAuthenticatedUser: number | null, limitQuantityPerCheckout: number | null, enableAccountConfirmationByEmail: boolean | null, useLegacyUpdateWebhookEmission: boolean | null, useLegacyShippingZoneStockAvailability: boolean, preserveAllAddressFields: boolean, passwordLoginMode: PasswordLoginModeEnum, companyAddress: { __typename: 'Address', city: string, cityArea: string, companyName: string, countryArea: string, firstName: string, id: string, lastName: string, phone: string | null, postalCode: string, streetAddress1: string, streetAddress2: string, country: { __typename: 'CountryDisplay', code: string, country: string } } | null, countries: Array<{ __typename: 'CountryDisplay', code: string, country: string }>, domain: { __typename: 'Domain', host: string } };
1179311793

1179411794
export type StaffMemberFragment = { __typename: 'User', id: string, email: string, firstName: string, isActive: boolean, lastName: string };
1179511795

@@ -13432,7 +13432,7 @@ export type ShopSettingsUpdateMutationVariables = Exact<{
1343213432
}>;
1343313433

1343413434

13435-
export type ShopSettingsUpdateMutation = { __typename: 'Mutation', shopSettingsUpdate: { __typename: 'ShopSettingsUpdate', errors: Array<{ __typename: 'ShopError', code: ShopErrorCode, field: string | null, message: string | null }>, shop: { __typename: 'Shop', customerSetPasswordUrl: string | null, defaultMailSenderAddress: string | null, defaultMailSenderName: string | null, description: string | null, name: string, reserveStockDurationAnonymousUser: number | null, reserveStockDurationAuthenticatedUser: number | null, limitQuantityPerCheckout: number | null, enableAccountConfirmationByEmail: boolean | null, useLegacyUpdateWebhookEmission: boolean | null, preserveAllAddressFields: boolean, passwordLoginMode: PasswordLoginModeEnum, companyAddress: { __typename: 'Address', city: string, cityArea: string, companyName: string, countryArea: string, firstName: string, id: string, lastName: string, phone: string | null, postalCode: string, streetAddress1: string, streetAddress2: string, country: { __typename: 'CountryDisplay', code: string, country: string } } | null, countries: Array<{ __typename: 'CountryDisplay', code: string, country: string }>, domain: { __typename: 'Domain', host: string } } | null } | null, shopAddressUpdate: { __typename: 'ShopAddressUpdate', errors: Array<{ __typename: 'ShopError', code: ShopErrorCode, field: string | null, message: string | null }>, shop: { __typename: 'Shop', companyAddress: { __typename: 'Address', city: string, cityArea: string, companyName: string, countryArea: string, firstName: string, id: string, lastName: string, phone: string | null, postalCode: string, streetAddress1: string, streetAddress2: string, country: { __typename: 'CountryDisplay', code: string, country: string } } | null } | null } | null };
13435+
export type ShopSettingsUpdateMutation = { __typename: 'Mutation', shopSettingsUpdate: { __typename: 'ShopSettingsUpdate', errors: Array<{ __typename: 'ShopError', code: ShopErrorCode, field: string | null, message: string | null }>, shop: { __typename: 'Shop', customerSetPasswordUrl: string | null, defaultMailSenderAddress: string | null, defaultMailSenderName: string | null, description: string | null, name: string, reserveStockDurationAnonymousUser: number | null, reserveStockDurationAuthenticatedUser: number | null, limitQuantityPerCheckout: number | null, enableAccountConfirmationByEmail: boolean | null, useLegacyUpdateWebhookEmission: boolean | null, useLegacyShippingZoneStockAvailability: boolean, preserveAllAddressFields: boolean, passwordLoginMode: PasswordLoginModeEnum, companyAddress: { __typename: 'Address', city: string, cityArea: string, companyName: string, countryArea: string, firstName: string, id: string, lastName: string, phone: string | null, postalCode: string, streetAddress1: string, streetAddress2: string, country: { __typename: 'CountryDisplay', code: string, country: string } } | null, countries: Array<{ __typename: 'CountryDisplay', code: string, country: string }>, domain: { __typename: 'Domain', host: string } } | null } | null, shopAddressUpdate: { __typename: 'ShopAddressUpdate', errors: Array<{ __typename: 'ShopError', code: ShopErrorCode, field: string | null, message: string | null }>, shop: { __typename: 'Shop', companyAddress: { __typename: 'Address', city: string, cityArea: string, companyName: string, countryArea: string, firstName: string, id: string, lastName: string, phone: string | null, postalCode: string, streetAddress1: string, streetAddress2: string, country: { __typename: 'CountryDisplay', code: string, country: string } } | null } | null } | null };
1343613436

1343713437
export type RefundSettingsUpdateMutationVariables = Exact<{
1343813438
refundSettingsInput: RefundSettingsUpdateInput;
@@ -13449,7 +13449,7 @@ export type RefundReasonReferenceClearMutation = { __typename: 'Mutation', refun
1344913449
export type SiteSettingsQueryVariables = Exact<{ [key: string]: never; }>;
1345013450

1345113451

13452-
export type SiteSettingsQuery = { __typename: 'Query', shop: { __typename: 'Shop', customerSetPasswordUrl: string | null, defaultMailSenderAddress: string | null, defaultMailSenderName: string | null, description: string | null, name: string, reserveStockDurationAnonymousUser: number | null, reserveStockDurationAuthenticatedUser: number | null, limitQuantityPerCheckout: number | null, enableAccountConfirmationByEmail: boolean | null, useLegacyUpdateWebhookEmission: boolean | null, preserveAllAddressFields: boolean, passwordLoginMode: PasswordLoginModeEnum, companyAddress: { __typename: 'Address', city: string, cityArea: string, companyName: string, countryArea: string, firstName: string, id: string, lastName: string, phone: string | null, postalCode: string, streetAddress1: string, streetAddress2: string, country: { __typename: 'CountryDisplay', code: string, country: string } } | null, countries: Array<{ __typename: 'CountryDisplay', code: string, country: string }>, domain: { __typename: 'Domain', host: string } } };
13452+
export type SiteSettingsQuery = { __typename: 'Query', shop: { __typename: 'Shop', customerSetPasswordUrl: string | null, defaultMailSenderAddress: string | null, defaultMailSenderName: string | null, description: string | null, name: string, reserveStockDurationAnonymousUser: number | null, reserveStockDurationAuthenticatedUser: number | null, limitQuantityPerCheckout: number | null, enableAccountConfirmationByEmail: boolean | null, useLegacyUpdateWebhookEmission: boolean | null, useLegacyShippingZoneStockAvailability: boolean, preserveAllAddressFields: boolean, passwordLoginMode: PasswordLoginModeEnum, companyAddress: { __typename: 'Address', city: string, cityArea: string, companyName: string, countryArea: string, firstName: string, id: string, lastName: string, phone: string | null, postalCode: string, streetAddress1: string, streetAddress2: string, country: { __typename: 'CountryDisplay', code: string, country: string } } | null, countries: Array<{ __typename: 'CountryDisplay', code: string, country: string }>, domain: { __typename: 'Domain', host: string } } };
1345313453

1345413454
export type StaffMemberAddMutationVariables = Exact<{
1345513455
input: StaffCreateInput;

src/siteSettings/components/SiteSettingsPage/SiteSettingsPage.tsx

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@ import CompanyAddressInput from "@dashboard/components/CompanyAddressInput";
66
import { type ConfirmButtonTransitionState } from "@dashboard/components/ConfirmButton";
77
import Form from "@dashboard/components/Form";
88
import { DetailPageLayout } from "@dashboard/components/Layouts";
9+
import { Link } from "@dashboard/components/Link";
910
import PageSectionHeader from "@dashboard/components/PageSectionHeader";
1011
import { Savebar } from "@dashboard/components/Savebar";
12+
import VerticalSpacer from "@dashboard/components/VerticalSpacer";
1113
import { configurationMenuUrl } from "@dashboard/configuration/urls";
1214
import {
1315
type PasswordLoginModeEnum,
1416
type ShopErrorFragment,
1517
type SiteSettingsQuery,
18+
WebhookEventTypeAsyncEnum,
1619
} from "@dashboard/graphql";
1720
import useAddressValidation from "@dashboard/hooks/useAddressValidation";
1821
import { type SubmitPromise } from "@dashboard/hooks/useForm";
@@ -22,12 +25,22 @@ import { commonMessages } from "@dashboard/intl";
2225
import createSingleAutocompleteSelectHandler from "@dashboard/utils/handlers/singleAutocompleteSelectChangeHandler";
2326
import { mapCountriesToChoices } from "@dashboard/utils/maps";
2427
import { Box, Checkbox, Divider, Text } from "@saleor/macaw-ui-next";
25-
import { useIntl } from "react-intl";
28+
import { FormattedMessage, useIntl } from "react-intl";
2629

2730
import SiteCheckoutSettingsCard from "../SiteCheckoutSettingsCard";
2831
import { SitePasswordLoginCard } from "../SitePasswordLoginCard/SitePasswordLoginCard";
2932
import { messages } from "./messages";
3033

34+
const stockAvailabilityWebhooks = [
35+
WebhookEventTypeAsyncEnum.PRODUCT_VARIANT_OUT_OF_STOCK_IN_CHANNEL,
36+
WebhookEventTypeAsyncEnum.PRODUCT_VARIANT_BACK_IN_STOCK_IN_CHANNEL,
37+
WebhookEventTypeAsyncEnum.PRODUCT_VARIANT_OUT_OF_STOCK_FOR_CLICK_AND_COLLECT,
38+
WebhookEventTypeAsyncEnum.PRODUCT_VARIANT_BACK_IN_STOCK_FOR_CLICK_AND_COLLECT,
39+
];
40+
41+
const stockAvailabilityDocsUrl =
42+
"https://docs.saleor.io/developer/stock/overview#legacy-stock-availability";
43+
3144
interface SiteSettingsPageAddressFormData {
3245
city: string;
3346
companyName: string;
@@ -46,6 +59,7 @@ export interface SiteSettingsPageFormData extends SiteSettingsPageAddressFormDat
4659
limitQuantityPerCheckout: number;
4760
emailConfirmation: boolean;
4861
useLegacyUpdateWebhookEmission: boolean;
62+
useLegacyShippingZoneStockAvailability: boolean;
4963
preserveAllAddressFields: boolean;
5064
passwordLoginMode: PasswordLoginModeEnum;
5165
}
@@ -101,6 +115,7 @@ const SiteSettingsPage = (props: SiteSettingsPageProps) => {
101115
limitQuantityPerCheckout: shop?.limitQuantityPerCheckout ?? 0,
102116
emailConfirmation: shop?.enableAccountConfirmationByEmail ?? false,
103117
useLegacyUpdateWebhookEmission: shop?.useLegacyUpdateWebhookEmission ?? true,
118+
useLegacyShippingZoneStockAvailability: shop?.useLegacyShippingZoneStockAvailability ?? true,
104119
preserveAllAddressFields: shop?.preserveAllAddressFields ?? false,
105120
passwordLoginMode: shop?.passwordLoginMode,
106121
};
@@ -133,6 +148,9 @@ const SiteSettingsPage = (props: SiteSettingsPageProps) => {
133148
const handlePreserveAddressFieldsChange = isEnabled => {
134149
change({ target: { name: "preserveAllAddressFields", value: isEnabled } });
135150
};
151+
const handleLegacyStockAvailabilityChange = isEnabled => {
152+
change({ target: { name: "useLegacyShippingZoneStockAvailability", value: isEnabled } });
153+
};
136154

137155
return (
138156
<DetailPageLayout gridTemplateColumns={1}>
@@ -251,6 +269,62 @@ const SiteSettingsPage = (props: SiteSettingsPageProps) => {
251269
</DashboardCard>
252270
</Box>
253271
<Divider />
272+
<Box
273+
display="grid"
274+
__gridTemplateColumns="1fr 3fr"
275+
paddingLeft={6}
276+
paddingBottom={8}
277+
>
278+
<Box paddingTop={6}>
279+
<Text size={3} fontWeight="bold" lineHeight={2}>
280+
{intl.formatMessage(messages.sectionStockAvailabilityTitle)}
281+
</Text>
282+
<VerticalSpacer />
283+
<Text size={3} fontWeight="regular">
284+
<FormattedMessage
285+
{...messages.sectionStockAvailabilityDescription}
286+
values={{
287+
a: chunks => (
288+
<Link href={stockAvailabilityDocsUrl} target="_blank">
289+
{chunks}
290+
</Link>
291+
),
292+
}}
293+
/>
294+
</Text>
295+
</Box>
296+
<DashboardCard>
297+
<DashboardCard.Header>
298+
<DashboardCard.Title>
299+
{intl.formatMessage(messages.sectionStockAvailabilityHeader)}
300+
</DashboardCard.Title>
301+
</DashboardCard.Header>
302+
<DashboardCard.Content>
303+
<Box display="flex" flexDirection="column" gap={3}>
304+
<Checkbox
305+
data-test-id="legacy-shipping-zone-stock-availability-checkbox"
306+
checked={data.useLegacyShippingZoneStockAvailability}
307+
onCheckedChange={handleLegacyStockAvailabilityChange}
308+
>
309+
<Text>{intl.formatMessage(messages.sectionStockAvailabilityHeader)}</Text>
310+
</Checkbox>
311+
<Box display="flex" flexDirection="column" gap={1}>
312+
<Text size={2} color="default2">
313+
{intl.formatMessage(messages.sectionStockAvailabilityWebhooksIntro)}
314+
</Text>
315+
<Box as="ul" margin={0} paddingLeft={5}>
316+
{stockAvailabilityWebhooks.map(name => (
317+
<Box as="li" key={name}>
318+
<Text size={2}>{name}</Text>
319+
</Box>
320+
))}
321+
</Box>
322+
</Box>
323+
</Box>
324+
</DashboardCard.Content>
325+
</DashboardCard>
326+
</Box>
327+
<Divider />
254328
<Box
255329
display="grid"
256330
__gridTemplateColumns="1fr 3fr"

src/siteSettings/components/SiteSettingsPage/messages.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,25 @@ export const messages = defineMessages({
8888
"Controls whether users can authenticate using password-based login. You can allow it for everyone, restrict it to customers only, or disable it entirely.",
8989
description: "section description",
9090
},
91+
sectionStockAvailabilityTitle: {
92+
id: "v5yLSE",
93+
defaultMessage: "Stock availability",
94+
description: "section title",
95+
},
96+
sectionStockAvailabilityDescription: {
97+
id: "qeAmFa",
98+
defaultMessage:
99+
"When enabled, stock availability is filtered by shipping zones and the destination address (legacy behavior). When disabled, it is determined only by the direct warehouse-channel link. <a>Learn more</a>.",
100+
description: "section description",
101+
},
102+
sectionStockAvailabilityHeader: {
103+
id: "6ZnMfQ",
104+
defaultMessage: "Use legacy shipping zone stock availability",
105+
description: "card header and checkbox label",
106+
},
107+
sectionStockAvailabilityWebhooksIntro: {
108+
id: "wmybDi",
109+
defaultMessage: "When disabled, the following channel-scoped stock webhooks become available:",
110+
description: "intro to webhook list",
111+
},
91112
});

src/siteSettings/fixtures.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export const shop: SiteSettingsQuery["shop"] = {
4242
limitQuantityPerCheckout: 50,
4343
enableAccountConfirmationByEmail: true,
4444
useLegacyUpdateWebhookEmission: true,
45+
useLegacyShippingZoneStockAvailability: true,
4546
preserveAllAddressFields: false,
4647
passwordLoginMode: PasswordLoginModeEnum.ENABLED,
4748
};

src/siteSettings/views/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ const SiteSettings = () => {
7272
enableAccountConfirmationByEmail: data.emailConfirmation,
7373
limitQuantityPerCheckout: data.limitQuantityPerCheckout || null,
7474
useLegacyUpdateWebhookEmission: data.useLegacyUpdateWebhookEmission,
75+
useLegacyShippingZoneStockAvailability: data.useLegacyShippingZoneStockAvailability,
7576
preserveAllAddressFields: data.preserveAllAddressFields,
7677
passwordLoginMode: data.passwordLoginMode,
7778
};

0 commit comments

Comments
 (0)