Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/shipping/components/OrderWeight/OrderWeight.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-strict-ignore
import { DashboardCard } from "@dashboard/components/Card";
import ControlledCheckbox from "@dashboard/components/ControlledCheckbox";
import VerticalSpacer from "@dashboard/components/VerticalSpacer";
Expand Down
6 changes: 4 additions & 2 deletions src/shipping/components/PricingCard/PricingCard.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-strict-ignore
import { ChannelShippingData } from "@dashboard/channels/utils";
import { DashboardCard } from "@dashboard/components/Card";
import PriceField from "@dashboard/components/PriceField";
Expand Down Expand Up @@ -32,7 +31,10 @@ const numberOfColumns = 2;
const PricingCard = ({ channels, disabled, errors, onChange }: PricingCardProps) => {
const classes = useStyles({});
const intl = useIntl();
const formErrors = getFormChannelErrors(["price"], errors);
const formErrors = getFormChannelErrors(
["price"],
errors.map(err => ({ ...err, channels: err.channels || [] })),
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this mapping ensures channels is always defined, creating a new array of error objects with spread operations for every render is inefficient. Since ShippingChannelsErrorFragment should guarantee channels is defined (based on GraphQL schema), a type assertion would be more efficient. If the schema truly allows nullable channels, consider memoizing this transformation with useMemo:

const formErrors = useMemo(
  () => getFormChannelErrors(
    ["price"],
    errors.map(err => ({ ...err, channels: err.channels || [] })),
  ),
  [errors]
);
Suggested change
errors.map(err => ({ ...err, channels: err.channels || [] })),
errors as (ShippingChannelsErrorFragment & { channels: NonNullable<ShippingChannelsErrorFragment["channels"]> })[],

Copilot uses AI. Check for mistakes.
);

return (
<DashboardCard>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-strict-ignore
import { Button } from "@dashboard/components/Button";
import { DashboardCard } from "@dashboard/components/Card";
import Checkbox from "@dashboard/components/Checkbox";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-strict-ignore
import { DashboardCard } from "@dashboard/components/Card";
import CardSpacer from "@dashboard/components/CardSpacer";
import RichTextEditor from "@dashboard/components/RichTextEditor";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-strict-ignore
import { FormChange } from "@dashboard/hooks/useForm";

export function createCountryChangeHandler(selectedCountries: string[], change: FormChange) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-strict-ignore
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
import CardSpacer from "@dashboard/components/CardSpacer";
import { ConfirmButtonTransitionState } from "@dashboard/components/ConfirmButton";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-strict-ignore
import { useUser } from "@dashboard/auth";
import { hasPermission } from "@dashboard/auth/misc";
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
Expand Down
7 changes: 3 additions & 4 deletions src/shipping/components/ShippingZoneDetailsPage/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-strict-ignore
import { ShippingZoneQuery } from "@dashboard/graphql";
import { mapMetadataItemToInput } from "@dashboard/utils/maps";

Expand All @@ -8,11 +7,11 @@ export const getInitialFormData = (
shippingZone?: ShippingZoneQuery["shippingZone"],
): ShippingZoneUpdateFormData => ({
description: shippingZone?.description || "",
metadata: shippingZone?.metadata.map(mapMetadataItemToInput),
metadata: shippingZone?.metadata.map(mapMetadataItemToInput) || [],
name: shippingZone?.name || "",
privateMetadata: shippingZone?.privateMetadata.map(mapMetadataItemToInput),
privateMetadata: shippingZone?.privateMetadata.map(mapMetadataItemToInput) || [],
warehouses:
shippingZone?.warehouses?.map(warehouse => ({ label: warehouse.name, value: warehouse.id })) ||
[],
channels: shippingZone?.channels.map(({ id, name }) => ({ label: name, value: id })) || [],
channels: shippingZone?.channels?.map(({ id, name }) => ({ label: name, value: id })) || [],
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-strict-ignore
import { Button } from "@dashboard/components/Button";
import { DashboardCard } from "@dashboard/components/Card";
import RadioGroupField from "@dashboard/components/RadioGroupField";
Expand All @@ -19,7 +18,9 @@ interface ShippingZonePostalCodesProps {
initialExpanded?: boolean;
postalCodes: ShippingMethodTypeFragment["postalCodeRules"] | undefined;
onPostalCodeInclusionChange: (inclusion: PostalCodeRuleInclusionTypeEnum) => void;
onPostalCodeDelete: (code: ShippingMethodTypeFragment["postalCodeRules"][0]) => void;
onPostalCodeDelete: (
code: NonNullable<ShippingMethodTypeFragment["postalCodeRules"]>[number],
) => void;
onPostalCodeRangeAdd: () => void;
}

Expand Down Expand Up @@ -59,15 +60,17 @@ const ShippingZonePostalCodes = ({
onPostalCodeRangeAdd,
}: ShippingZonePostalCodesProps) => {
const [expanded, setExpanded] = React.useState(initialExpanded);
const [inclusionType, setInclusionType] = React.useState(null);
const [inclusionType, setInclusionType] = React.useState<string | null>(null);
const intl = useIntl();
const classes = useStyles({});
const getInclusionType = () => {
if (inclusionType) {
return inclusionType;
}

return postalCodes[0]?.inclusionType || PostalCodeRuleInclusionTypeEnum.EXCLUDE;
return (
(postalCodes && postalCodes[0]?.inclusionType) || PostalCodeRuleInclusionTypeEnum.EXCLUDE
);
};
const onInclusionRadioChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const value = event.target.value;
Expand All @@ -80,7 +83,15 @@ const ShippingZonePostalCodes = ({
onPostalCodeInclusionChange(postalType);
};
const getPostalCodeRangeLabel = (
postalCodeRange: ShippingMethodTypeFragment["postalCodeRules"][0],
postalCodeRange:
| {
__typename: "ShippingMethodPostalCodeRule";
id: string;
inclusionType: PostalCodeRuleInclusionTypeEnum | null;
start: string | null;
end: string | null;
}
| undefined,
) => {
if (!postalCodeRange?.start) {
return <Skeleton />;
Expand Down Expand Up @@ -171,7 +182,7 @@ const ShippingZonePostalCodes = ({
<TableHead>
<TableRowLink>
<TableCell>
{postalCodes === undefined ? (
{postalCodes == null ? (
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Inconsistent spacing in null check. The comparison operator should be == null (without extra space) for consistency with typical JavaScript/TypeScript style. While this doesn't affect functionality, it's inconsistent with standard formatting.

Suggested change
{postalCodes == null ? (
{postalCodes==null ? (

Copilot uses AI. Check for mistakes.
<Skeleton className={classes.skeleton} />
) : (
<Text size={2} fontWeight="light">
Expand Down Expand Up @@ -200,7 +211,7 @@ const ShippingZonePostalCodes = ({
{expanded && (
<TableBody>
{renderCollection(
postalCodes,
postalCodes || undefined,
postalCodeRange => (
<TableRowLink key={postalCodeRange?.id} data-test-id="assigned-postal-codes-rows">
<TableCell>{getPostalCodeRangeLabel(postalCodeRange)}</TableCell>
Expand All @@ -209,7 +220,7 @@ const ShippingZonePostalCodes = ({
disabled={disabled}
color="primary"
variant="secondary"
onClick={() => onPostalCodeDelete(postalCodeRange)}
onClick={() => postalCodeRange && onPostalCodeDelete(postalCodeRange)}
data-test-id={"delete-postal-code-" + postalCodeRange?.id}
>
<DeleteIcon
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-strict-ignore
import { DashboardCard } from "@dashboard/components/Card";
import IconButtonTableCell from "@dashboard/components/IconButtonTableCell";
import Money from "@dashboard/components/Money";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-strict-ignore
import { ChannelShippingData } from "@dashboard/channels/utils";
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
import CardSpacer from "@dashboard/components/CardSpacer";
Expand Down Expand Up @@ -192,7 +191,7 @@ const ShippingZoneRatesCreatePage = ({
<DetailPageLayout.RightSidebar>
<ChannelsAvailabilityCard
managePermissions={[PermissionEnum.MANAGE_SHIPPING]}
allChannelsCount={allChannelsCount}
allChannelsCount={allChannelsCount || 0}
channelsList={data.channelListings}
openModal={openChannelsModal}
/>
Expand Down
11 changes: 7 additions & 4 deletions src/shipping/errors.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-strict-ignore
import { ShippingErrorCode, ShippingErrorFragment } from "@dashboard/graphql";
import getShippingErrorMessage from "@dashboard/utils/errors/shipping";
import { defineMessages, IntlShape } from "react-intl";
Expand All @@ -22,15 +21,19 @@ const messages = defineMessages({
});

export function getShippingWeightRateErrorMessage(
err: ShippingErrorFragment,
err: ShippingErrorFragment | undefined,
intl: IntlShape,
): string {
switch (err?.code) {
if (!err) {
return "";
}

switch (err.code) {
case ShippingErrorCode.MAX_LESS_THAN_MIN:
return intl.formatMessage(messages.weight);
case ShippingErrorCode.INVALID:
return intl.formatMessage(messages.invalid);
default:
getShippingErrorMessage(err, intl);
return getShippingErrorMessage(err, intl) || "";
}
}
30 changes: 20 additions & 10 deletions src/shipping/handlers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-strict-ignore
import { ChannelShippingData } from "@dashboard/channels/utils";
import {
CountryFragment,
Expand Down Expand Up @@ -48,7 +47,7 @@ export const createChannelsChangeHandler =
};

const getPostalCodeRulesToAdd = (rules: ShippingMethodTypeFragment["postalCodeRules"]) =>
rules
(rules || [])
.filter(code => !code.id || code.id === "0")
.map(
code =>
Expand Down Expand Up @@ -77,7 +76,7 @@ function getCreateShippingPriceRateVariables(
name: data.name,
shippingZone: id,
type: ShippingMethodTypeEnum.PRICE,
description: getParsedDataForJsonStringField(data.description),
description: data.description ? getParsedDataForJsonStringField(data.description) : null,
taxClass: data.taxClassId,
},
};
Expand Down Expand Up @@ -107,7 +106,7 @@ function getCreateShippingWeightRateVariables(
name: data.name,
shippingZone: id,
type: ShippingMethodTypeEnum.WEIGHT,
description: getParsedDataForJsonStringField(data.description),
description: data.description ? getParsedDataForJsonStringField(data.description) : null,
taxClass: data.taxClassId,
},
};
Expand All @@ -130,13 +129,14 @@ export function getUpdateShippingPriceRateVariables(
addPostalCodeRules: postalCodeRules,
deletePostalCodeRules,
inclusionType:
addPostalCodeRules[0]?.inclusionType || PostalCodeRuleInclusionTypeEnum.EXCLUDE,
(addPostalCodeRules && addPostalCodeRules[0]?.inclusionType) ||
PostalCodeRuleInclusionTypeEnum.EXCLUDE,
maximumDeliveryDays: parsedMaxDays,
minimumDeliveryDays: parsedMinDays,
name: data.name,
shippingZone: id,
type: ShippingMethodTypeEnum.PRICE,
description: getParsedDataForJsonStringField(data.description),
description: data.description ? getParsedDataForJsonStringField(data.description) : null,
taxClass: data.taxClassId,
},
};
Expand All @@ -162,15 +162,16 @@ export function getUpdateShippingWeightRateVariables(
addPostalCodeRules: postalCodeRules,
deletePostalCodeRules,
inclusionType:
addPostalCodeRules[0]?.inclusionType || PostalCodeRuleInclusionTypeEnum.EXCLUDE,
(addPostalCodeRules && addPostalCodeRules[0]?.inclusionType) ||
PostalCodeRuleInclusionTypeEnum.EXCLUDE,
maximumDeliveryDays: parsedMaxDays,
maximumOrderWeight: isWeightSet ? parsedMaxValue : null,
minimumDeliveryDays: parsedMinDays,
minimumOrderWeight: isWeightSet ? parsedMinValue : null,
name: data.name,
shippingZone: id,
type: ShippingMethodTypeEnum.WEIGHT,
description: getParsedDataForJsonStringField(data.description),
description: data.description ? getParsedDataForJsonStringField(data.description) : null,
taxClass: data.taxClassId,
},
};
Expand Down Expand Up @@ -221,12 +222,21 @@ export function useShippingRateCreator(
const response = await createBaseShippingRate({
variables: getVariables(data, shippingZoneId, postalCodes, inclusionType),
});

if (!response.data?.shippingPriceCreate) {
throw new Error("Failed to create shipping rate");
}
Comment on lines +226 to +228
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Throwing an error here will cause an unhandled exception. The function createShippingRate is called through useHandleFormSubmit, which doesn't have try-catch error handling. This will crash the application instead of showing a user-friendly error. Consider returning an error array consistent with the pattern used when createErrors.length > 0 (line 232-234), or add proper error handling in the call chain.

Copilot uses AI. Check for mistakes.

const createErrors = response.data.shippingPriceCreate.errors;

if (createErrors.length > 0) {
return createErrors;
}

if (!response.data.shippingPriceCreate.shippingMethod) {
throw new Error("Failed to create shipping method");
}
Comment on lines +236 to +238
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Throwing an error here will cause an unhandled exception. The function createShippingRate is called through useHandleFormSubmit, which doesn't have try-catch error handling. This will crash the application instead of showing a user-friendly error. Consider returning an error array consistent with the pattern used elsewhere in this function, or add proper error handling in the call chain.

Copilot uses AI. Check for mistakes.

const rateId = response.data.shippingPriceCreate.shippingMethod.id;
const errors = await extractMutationErrors(
updateShippingMethodChannelListing({
Expand Down Expand Up @@ -263,9 +273,9 @@ export function useShippingRateCreator(
const called = createBaseShippingRateOpts.called || updateShippingMethodChannelListingOpts.called;
const loading =
createBaseShippingRateOpts.loading || updateShippingMethodChannelListingOpts.loading;
const errors = [...(createBaseShippingRateOpts.data?.shippingPriceCreate.errors || [])];
const errors = [...(createBaseShippingRateOpts.data?.shippingPriceCreate?.errors || [])];
const channelErrors =
updateShippingMethodChannelListingOpts.data?.shippingMethodChannelListingUpdate.errors || [];
updateShippingMethodChannelListingOpts.data?.shippingMethodChannelListingUpdate?.errors || [];

return {
channelErrors,
Expand Down
27 changes: 17 additions & 10 deletions src/shipping/views/RateCreate.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
// @ts-strict-ignore
import { createSortedShippingChannels } from "@dashboard/channels/utils";
import ChannelsAvailabilityDialog from "@dashboard/components/ChannelsAvailabilityDialog";
import { WindowTitle } from "@dashboard/components/WindowTitle";
import { PostalCodeRuleInclusionTypeEnum, useShippingZoneChannelsQuery } from "@dashboard/graphql";
import {
PostalCodeRuleInclusionTypeEnum,
ShippingMethodWithPostalCodesFragment,
useShippingZoneChannelsQuery,
} from "@dashboard/graphql";
import useChannels from "@dashboard/hooks/useChannels";
import useNavigator from "@dashboard/hooks/useNavigator";
import { sectionNames } from "@dashboard/intl";
Expand Down Expand Up @@ -68,22 +71,24 @@ const RateCreate = ({ id, params }: RateCreateProps) => {
});
const { channelErrors, createShippingRate, errors, status } = useShippingRateCreator(
id,
params.type,
state.postalCodeRules,
state.inclusionType,
params.type!,
state.postalCodeRules || [],
state.inclusionType!,
Comment on lines +74 to +76
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a non-null assertion (params.type!) on optional URL parameters is unsafe. If params.type is undefined, this will cause a runtime error. Consider adding proper validation:

if (!params.type) {
  // Handle missing type parameter
  return null; // or navigate back
}
const { channelErrors, createShippingRate, errors, status } = useShippingRateCreator(
  id,
  params.type,
  state.postalCodeRules || [],
  state.inclusionType!,
);

Copilot uses AI. Check for mistakes.
);
const onPostalCodeAssign = (rule: MinMax) => {
if (state.postalCodeRules.filter(getPostalCodeRuleByMinMax(rule)).length > 0) {
const postalCodeRules = state.postalCodeRules || [];

if (postalCodeRules.filter(getPostalCodeRuleByMinMax(rule)).length > 0) {
closeModal();

return;
}

const newCode = getRuleObject(rule, state.inclusionType);
const newCode = getRuleObject(rule, state.inclusionType!);
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a non-null assertion (state.inclusionType!) is unsafe. The inclusionType in the reducer state is marked as optional (inclusionType?: PostalCodeRuleInclusionTypeEnum). While it's initialized with a default value in line 68, the state could potentially be updated with undefined. Consider using nullish coalescing for safety:

const newCode = getRuleObject(rule, state.inclusionType ?? PostalCodeRuleInclusionTypeEnum.EXCLUDE);
Suggested change
const newCode = getRuleObject(rule, state.inclusionType!);
const newCode = getRuleObject(rule, state.inclusionType ?? PostalCodeRuleInclusionTypeEnum.EXCLUDE);

Copilot uses AI. Check for mistakes.

dispatch({
havePostalCodesChanged: true,
postalCodeRules: [...state.postalCodeRules, newCode],
postalCodeRules: [...postalCodeRules, newCode],
});
closeModal();
};
Expand All @@ -93,10 +98,12 @@ const RateCreate = ({ id, params }: RateCreateProps) => {
postalCodeRules: [],
});
};
const onPostalCodeUnassign = code => {
const onPostalCodeUnassign = (
code: NonNullable<ShippingMethodWithPostalCodesFragment["postalCodeRules"]>[number],
) => {
dispatch({
havePostalCodesChanged: true,
postalCodeRules: filterPostalCodes(state.postalCodeRules, code),
postalCodeRules: filterPostalCodes(state.postalCodeRules || [], code),
});
};

Expand Down
5 changes: 4 additions & 1 deletion src/shipping/views/RateUpdate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,10 @@ const RateUpdate = ({ id, rateId, params }: RateUpdateProps) => {
} else {
dispatch({
havePostalCodesChanged: true,
postalCodeRules: filterPostalCodes(state.postalCodeRules, code),
postalCodeRules: filterPostalCodes(
state.postalCodeRules!,
code as NonNullable<ShippingMethodWithPostalCodesFragment["postalCodeRules"]>[number],
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type assertion here is redundant. The function filterPostalCodes already expects and returns the correct type PostalCodeRule[]. The code parameter can be passed directly without the type assertion:

postalCodeRules: filterPostalCodes(state.postalCodeRules!, code),
Suggested change
code as NonNullable<ShippingMethodWithPostalCodesFragment["postalCodeRules"]>[number],
code,

Copilot uses AI. Check for mistakes.
),
});
}
};
Expand Down
5 changes: 2 additions & 3 deletions src/shipping/views/ShippingZoneCreate.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-strict-ignore
import { useCreateShippingZoneMutation, useShopCountriesQuery } from "@dashboard/graphql";
import useNavigator from "@dashboard/hooks/useNavigator";
import useNotifier from "@dashboard/hooks/useNotifier";
Expand Down Expand Up @@ -27,7 +26,7 @@ const ShippingZoneCreate = () => {
});
const [createShippingZone, createShippingZoneOpts] = useCreateShippingZoneMutation({
onCompleted: data => {
if (data.shippingZoneCreate.errors.length === 0) {
if (data.shippingZoneCreate?.errors.length === 0 && data.shippingZoneCreate?.shippingZone) {
notify({
status: "success",
text: intl.formatMessage(commonMessages.savedChanges),
Expand All @@ -50,7 +49,7 @@ const ShippingZoneCreate = () => {
countries={shop?.countries || []}
restWorldCountries={mapCountriesToCountriesCodes(restWorldCountries?.shop?.countries) || []}
disabled={createShippingZoneOpts.loading}
errors={createShippingZoneOpts.data?.shippingZoneCreate.errors || []}
errors={createShippingZoneOpts.data?.shippingZoneCreate?.errors || []}
onSubmit={handleSubmit}
saveButtonBarState={createShippingZoneOpts.status}
/>
Expand Down
1 change: 0 additions & 1 deletion src/shipping/views/ShippingZoneDetails/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-strict-ignore
import ActionDialog from "@dashboard/components/ActionDialog";
import useAppChannel from "@dashboard/components/AppLayout/AppChannelContext";
import NotFoundPage from "@dashboard/components/NotFoundPage";
Expand Down
Loading
Loading