Metadata settings in tax classes (#2680)

* feat: added metadata settings to tax classes

* test: update test snapshots

* refactor: update taxes error handling

* refactor: add TaxClassBase fragment

* refactor: update tax classes initial values definition
This commit is contained in:
Dawid 2022-11-30 17:18:44 +01:00 committed by GitHub
parent 689568694e
commit f2af4a1b23
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 679 additions and 159 deletions

View file

@ -489,40 +489,46 @@ export const shippingPriceTranslateErrorFragment = gql`
`;
export const taxConfigurationUpdateError = gql`
fragment TaxConfigurationUpdateErrorFragment on TaxConfigurationUpdateError {
fragment TaxConfigurationUpdateError on TaxConfigurationUpdateError {
field
code
message
}
`;
export const taxCountryConfigurationUpdateError = gql`
fragment TaxCountryConfigurationUpdateErrorFragment on TaxCountryConfigurationUpdateError {
fragment TaxCountryConfigurationUpdateError on TaxCountryConfigurationUpdateError {
field
code
message
}
`;
export const taxCountryConfigurationDeleteError = gql`
fragment TaxCountryConfigurationDeleteErrorFragment on TaxCountryConfigurationDeleteError {
fragment TaxCountryConfigurationDeleteError on TaxCountryConfigurationDeleteError {
field
code
message
}
`;
export const taxClassUpdateError = gql`
fragment TaxClassUpdateErrorFragment on TaxClassUpdateError {
fragment TaxClassUpdateError on TaxClassUpdateError {
field
code
message
}
`;
export const taxClassCreateError = gql`
fragment TaxClassCreateErrorFragment on TaxClassCreateError {
fragment TaxClassCreateError on TaxClassCreateError {
field
code
message
}
`;
export const taxClassDeleteError = gql`
fragment TaxClassDeleteErrorFragment on TaxClassDeleteError {
fragment TaxClassDeleteError on TaxClassDeleteError {
field
code
message
}
`;

View file

@ -69,12 +69,19 @@ export const taxRateFragment = gql`
}
`;
export const taxClassFragment = gql`
fragment TaxClass on TaxClass {
export const taxClassBaseFragment = gql`
fragment TaxClassBase on TaxClass {
id
name
}
`;
export const taxClassFragment = gql`
fragment TaxClass on TaxClass {
...TaxClassBase
countries {
...TaxRate
}
...Metadata
}
`;

View file

@ -972,40 +972,46 @@ export const ShippingPriceTranslateErrorFragmentFragmentDoc = gql`
message
}
`;
export const TaxConfigurationUpdateErrorFragmentFragmentDoc = gql`
fragment TaxConfigurationUpdateErrorFragment on TaxConfigurationUpdateError {
export const TaxConfigurationUpdateErrorFragmentDoc = gql`
fragment TaxConfigurationUpdateError on TaxConfigurationUpdateError {
field
code
message
}
`;
export const TaxCountryConfigurationUpdateErrorFragmentFragmentDoc = gql`
fragment TaxCountryConfigurationUpdateErrorFragment on TaxCountryConfigurationUpdateError {
export const TaxCountryConfigurationUpdateErrorFragmentDoc = gql`
fragment TaxCountryConfigurationUpdateError on TaxCountryConfigurationUpdateError {
field
code
message
}
`;
export const TaxCountryConfigurationDeleteErrorFragmentFragmentDoc = gql`
fragment TaxCountryConfigurationDeleteErrorFragment on TaxCountryConfigurationDeleteError {
export const TaxCountryConfigurationDeleteErrorFragmentDoc = gql`
fragment TaxCountryConfigurationDeleteError on TaxCountryConfigurationDeleteError {
field
code
message
}
`;
export const TaxClassUpdateErrorFragmentFragmentDoc = gql`
fragment TaxClassUpdateErrorFragment on TaxClassUpdateError {
export const TaxClassUpdateErrorFragmentDoc = gql`
fragment TaxClassUpdateError on TaxClassUpdateError {
field
code
message
}
`;
export const TaxClassCreateErrorFragmentFragmentDoc = gql`
fragment TaxClassCreateErrorFragment on TaxClassCreateError {
export const TaxClassCreateErrorFragmentDoc = gql`
fragment TaxClassCreateError on TaxClassCreateError {
field
code
message
}
`;
export const TaxClassDeleteErrorFragmentFragmentDoc = gql`
fragment TaxClassDeleteErrorFragment on TaxClassDeleteError {
export const TaxClassDeleteErrorFragmentDoc = gql`
fragment TaxClassDeleteError on TaxClassDeleteError {
field
code
message
}
`;
export const GiftCardsSettingsFragmentDoc = gql`
@ -2440,6 +2446,12 @@ export const TaxCountryConfigurationFragmentDoc = gql`
}
}
${CountryWithCodeFragmentDoc}`;
export const TaxClassBaseFragmentDoc = gql`
fragment TaxClassBase on TaxClass {
id
name
}
`;
export const TaxRateFragmentDoc = gql`
fragment TaxRate on TaxClassCountryRate {
country {
@ -2450,13 +2462,15 @@ export const TaxRateFragmentDoc = gql`
${CountryWithCodeFragmentDoc}`;
export const TaxClassFragmentDoc = gql`
fragment TaxClass on TaxClass {
id
name
...TaxClassBase
countries {
...TaxRate
}
...Metadata
}
${TaxRateFragmentDoc}`;
${TaxClassBaseFragmentDoc}
${TaxRateFragmentDoc}
${MetadataFragmentDoc}`;
export const TimePeriodFragmentDoc = gql`
fragment TimePeriod on TimePeriod {
amount
@ -14767,14 +14781,14 @@ export const TaxConfigurationUpdateDocument = gql`
mutation TaxConfigurationUpdate($id: ID!, $input: TaxConfigurationUpdateInput!) {
taxConfigurationUpdate(id: $id, input: $input) {
errors {
...TaxConfigurationUpdateErrorFragment
...TaxConfigurationUpdateError
}
taxConfiguration {
...TaxConfiguration
}
}
}
${TaxConfigurationUpdateErrorFragmentFragmentDoc}
${TaxConfigurationUpdateErrorFragmentDoc}
${TaxConfigurationFragmentDoc}`;
export type TaxConfigurationUpdateMutationFn = Apollo.MutationFunction<Types.TaxConfigurationUpdateMutation, Types.TaxConfigurationUpdateMutationVariables>;
@ -14810,14 +14824,14 @@ export const TaxCountryConfigurationUpdateDocument = gql`
updateTaxClassRates: $updateTaxClassRates
) {
errors {
...TaxCountryConfigurationUpdateErrorFragment
...TaxCountryConfigurationUpdateError
}
taxCountryConfiguration {
...TaxCountryConfiguration
}
}
}
${TaxCountryConfigurationUpdateErrorFragmentFragmentDoc}
${TaxCountryConfigurationUpdateErrorFragmentDoc}
${TaxCountryConfigurationFragmentDoc}`;
export type TaxCountryConfigurationUpdateMutationFn = Apollo.MutationFunction<Types.TaxCountryConfigurationUpdateMutation, Types.TaxCountryConfigurationUpdateMutationVariables>;
@ -14850,14 +14864,14 @@ export const TaxCountryConfigurationDeleteDocument = gql`
mutation TaxCountryConfigurationDelete($countryCode: CountryCode!) {
taxCountryConfigurationDelete(countryCode: $countryCode) {
errors {
...TaxCountryConfigurationDeleteErrorFragment
...TaxCountryConfigurationDeleteError
}
taxCountryConfiguration {
...TaxCountryConfiguration
}
}
}
${TaxCountryConfigurationDeleteErrorFragmentFragmentDoc}
${TaxCountryConfigurationDeleteErrorFragmentDoc}
${TaxCountryConfigurationFragmentDoc}`;
export type TaxCountryConfigurationDeleteMutationFn = Apollo.MutationFunction<Types.TaxCountryConfigurationDeleteMutation, Types.TaxCountryConfigurationDeleteMutationVariables>;
@ -14889,14 +14903,14 @@ export const TaxClassUpdateDocument = gql`
mutation TaxClassUpdate($id: ID!, $input: TaxClassUpdateInput!) {
taxClassUpdate(id: $id, input: $input) {
errors {
...TaxClassUpdateErrorFragment
...TaxClassUpdateError
}
taxClass {
...TaxClass
}
}
}
${TaxClassUpdateErrorFragmentFragmentDoc}
${TaxClassUpdateErrorFragmentDoc}
${TaxClassFragmentDoc}`;
export type TaxClassUpdateMutationFn = Apollo.MutationFunction<Types.TaxClassUpdateMutation, Types.TaxClassUpdateMutationVariables>;
@ -14929,14 +14943,14 @@ export const TaxClassCreateDocument = gql`
mutation TaxClassCreate($input: TaxClassCreateInput!) {
taxClassCreate(input: $input) {
errors {
...TaxClassCreateErrorFragment
...TaxClassCreateError
}
taxClass {
...TaxClass
}
}
}
${TaxClassCreateErrorFragmentFragmentDoc}
${TaxClassCreateErrorFragmentDoc}
${TaxClassFragmentDoc}`;
export type TaxClassCreateMutationFn = Apollo.MutationFunction<Types.TaxClassCreateMutation, Types.TaxClassCreateMutationVariables>;
@ -14968,11 +14982,11 @@ export const TaxClassDeleteDocument = gql`
mutation TaxClassDelete($id: ID!) {
taxClassDelete(id: $id) {
errors {
...TaxClassDeleteErrorFragment
...TaxClassDeleteError
}
}
}
${TaxClassDeleteErrorFragmentFragmentDoc}`;
${TaxClassDeleteErrorFragmentDoc}`;
export type TaxClassDeleteMutationFn = Apollo.MutationFunction<Types.TaxClassDeleteMutation, Types.TaxClassDeleteMutationVariables>;
/**

View file

@ -7189,17 +7189,17 @@ export type AttributeValueTranslateErrorFragmentFragment = { __typename: 'Transl
export type ShippingPriceTranslateErrorFragmentFragment = { __typename: 'TranslationError', code: TranslationErrorCode, field: string | null, message: string | null };
export type TaxConfigurationUpdateErrorFragmentFragment = { __typename: 'TaxConfigurationUpdateError', field: string | null, code: TaxConfigurationUpdateErrorCode };
export type TaxConfigurationUpdateErrorFragment = { __typename: 'TaxConfigurationUpdateError', field: string | null, code: TaxConfigurationUpdateErrorCode, message: string | null };
export type TaxCountryConfigurationUpdateErrorFragmentFragment = { __typename: 'TaxCountryConfigurationUpdateError', field: string | null, code: TaxCountryConfigurationUpdateErrorCode };
export type TaxCountryConfigurationUpdateErrorFragment = { __typename: 'TaxCountryConfigurationUpdateError', field: string | null, code: TaxCountryConfigurationUpdateErrorCode, message: string | null };
export type TaxCountryConfigurationDeleteErrorFragmentFragment = { __typename: 'TaxCountryConfigurationDeleteError', field: string | null, code: TaxCountryConfigurationDeleteErrorCode };
export type TaxCountryConfigurationDeleteErrorFragment = { __typename: 'TaxCountryConfigurationDeleteError', field: string | null, code: TaxCountryConfigurationDeleteErrorCode, message: string | null };
export type TaxClassUpdateErrorFragmentFragment = { __typename: 'TaxClassUpdateError', field: string | null, code: TaxClassUpdateErrorCode };
export type TaxClassUpdateErrorFragment = { __typename: 'TaxClassUpdateError', field: string | null, code: TaxClassUpdateErrorCode, message: string | null };
export type TaxClassCreateErrorFragmentFragment = { __typename: 'TaxClassCreateError', field: string | null, code: TaxClassCreateErrorCode };
export type TaxClassCreateErrorFragment = { __typename: 'TaxClassCreateError', field: string | null, code: TaxClassCreateErrorCode, message: string | null };
export type TaxClassDeleteErrorFragmentFragment = { __typename: 'TaxClassDeleteError', field: string | null, code: TaxClassDeleteErrorCode };
export type TaxClassDeleteErrorFragment = { __typename: 'TaxClassDeleteError', field: string | null, code: TaxClassDeleteErrorCode, message: string | null };
export type FileFragment = { __typename: 'File', url: string, contentType: string | null };
@ -7409,7 +7409,9 @@ export type TaxCountryConfigurationFragment = { __typename: 'TaxCountryConfigura
export type TaxRateFragment = { __typename: 'TaxClassCountryRate', rate: number, country: { __typename: 'CountryDisplay', country: string, code: string } };
export type TaxClassFragment = { __typename: 'TaxClass', id: string, name: string, countries: Array<{ __typename: 'TaxClassCountryRate', rate: number, country: { __typename: 'CountryDisplay', country: string, code: string } }> };
export type TaxClassBaseFragment = { __typename: 'TaxClass', id: string, name: string };
export type TaxClassFragment = { __typename: 'TaxClass', id: string, name: string, countries: Array<{ __typename: 'TaxClassCountryRate', rate: number, country: { __typename: 'CountryDisplay', country: string, code: string } }>, metadata: Array<{ __typename: 'MetadataItem', key: string, value: string }>, privateMetadata: Array<{ __typename: 'MetadataItem', key: string, value: string }> };
export type TimePeriodFragment = { __typename: 'TimePeriod', amount: number, type: TimePeriodTypeEnum };
@ -8975,7 +8977,7 @@ export type TaxConfigurationUpdateMutationVariables = Exact<{
}>;
export type TaxConfigurationUpdateMutation = { __typename: 'Mutation', taxConfigurationUpdate: { __typename: 'TaxConfigurationUpdate', errors: Array<{ __typename: 'TaxConfigurationUpdateError', field: string | null, code: TaxConfigurationUpdateErrorCode }>, taxConfiguration: { __typename: 'TaxConfiguration', id: string, displayGrossPrices: boolean, pricesEnteredWithTax: boolean, chargeTaxes: boolean, taxCalculationStrategy: TaxCalculationStrategy | null, channel: { __typename: 'Channel', id: string, name: string }, countries: Array<{ __typename: 'TaxConfigurationPerCountry', chargeTaxes: boolean, taxCalculationStrategy: TaxCalculationStrategy | null, displayGrossPrices: boolean, country: { __typename: 'CountryDisplay', country: string, code: string } }> } | null } | null };
export type TaxConfigurationUpdateMutation = { __typename: 'Mutation', taxConfigurationUpdate: { __typename: 'TaxConfigurationUpdate', errors: Array<{ __typename: 'TaxConfigurationUpdateError', field: string | null, code: TaxConfigurationUpdateErrorCode, message: string | null }>, taxConfiguration: { __typename: 'TaxConfiguration', id: string, displayGrossPrices: boolean, pricesEnteredWithTax: boolean, chargeTaxes: boolean, taxCalculationStrategy: TaxCalculationStrategy | null, channel: { __typename: 'Channel', id: string, name: string }, countries: Array<{ __typename: 'TaxConfigurationPerCountry', chargeTaxes: boolean, taxCalculationStrategy: TaxCalculationStrategy | null, displayGrossPrices: boolean, country: { __typename: 'CountryDisplay', country: string, code: string } }> } | null } | null };
export type TaxCountryConfigurationUpdateMutationVariables = Exact<{
countryCode: CountryCode;
@ -8983,14 +8985,14 @@ export type TaxCountryConfigurationUpdateMutationVariables = Exact<{
}>;
export type TaxCountryConfigurationUpdateMutation = { __typename: 'Mutation', taxCountryConfigurationUpdate: { __typename: 'TaxCountryConfigurationUpdate', errors: Array<{ __typename: 'TaxCountryConfigurationUpdateError', field: string | null, code: TaxCountryConfigurationUpdateErrorCode }>, taxCountryConfiguration: { __typename: 'TaxCountryConfiguration', country: { __typename: 'CountryDisplay', country: string, code: string }, taxClassCountryRates: Array<{ __typename: 'TaxClassCountryRate', rate: number, taxClass: { __typename: 'TaxClass', id: string, name: string } | null }> } | null } | null };
export type TaxCountryConfigurationUpdateMutation = { __typename: 'Mutation', taxCountryConfigurationUpdate: { __typename: 'TaxCountryConfigurationUpdate', errors: Array<{ __typename: 'TaxCountryConfigurationUpdateError', field: string | null, code: TaxCountryConfigurationUpdateErrorCode, message: string | null }>, taxCountryConfiguration: { __typename: 'TaxCountryConfiguration', country: { __typename: 'CountryDisplay', country: string, code: string }, taxClassCountryRates: Array<{ __typename: 'TaxClassCountryRate', rate: number, taxClass: { __typename: 'TaxClass', id: string, name: string } | null }> } | null } | null };
export type TaxCountryConfigurationDeleteMutationVariables = Exact<{
countryCode: CountryCode;
}>;
export type TaxCountryConfigurationDeleteMutation = { __typename: 'Mutation', taxCountryConfigurationDelete: { __typename: 'TaxCountryConfigurationDelete', errors: Array<{ __typename: 'TaxCountryConfigurationDeleteError', field: string | null, code: TaxCountryConfigurationDeleteErrorCode }>, taxCountryConfiguration: { __typename: 'TaxCountryConfiguration', country: { __typename: 'CountryDisplay', country: string, code: string }, taxClassCountryRates: Array<{ __typename: 'TaxClassCountryRate', rate: number, taxClass: { __typename: 'TaxClass', id: string, name: string } | null }> } | null } | null };
export type TaxCountryConfigurationDeleteMutation = { __typename: 'Mutation', taxCountryConfigurationDelete: { __typename: 'TaxCountryConfigurationDelete', errors: Array<{ __typename: 'TaxCountryConfigurationDeleteError', field: string | null, code: TaxCountryConfigurationDeleteErrorCode, message: string | null }>, taxCountryConfiguration: { __typename: 'TaxCountryConfiguration', country: { __typename: 'CountryDisplay', country: string, code: string }, taxClassCountryRates: Array<{ __typename: 'TaxClassCountryRate', rate: number, taxClass: { __typename: 'TaxClass', id: string, name: string } | null }> } | null } | null };
export type TaxClassUpdateMutationVariables = Exact<{
id: Scalars['ID'];
@ -8998,21 +9000,21 @@ export type TaxClassUpdateMutationVariables = Exact<{
}>;
export type TaxClassUpdateMutation = { __typename: 'Mutation', taxClassUpdate: { __typename: 'TaxClassUpdate', errors: Array<{ __typename: 'TaxClassUpdateError', field: string | null, code: TaxClassUpdateErrorCode }>, taxClass: { __typename: 'TaxClass', id: string, name: string, countries: Array<{ __typename: 'TaxClassCountryRate', rate: number, country: { __typename: 'CountryDisplay', country: string, code: string } }> } | null } | null };
export type TaxClassUpdateMutation = { __typename: 'Mutation', taxClassUpdate: { __typename: 'TaxClassUpdate', errors: Array<{ __typename: 'TaxClassUpdateError', field: string | null, code: TaxClassUpdateErrorCode, message: string | null }>, taxClass: { __typename: 'TaxClass', id: string, name: string, countries: Array<{ __typename: 'TaxClassCountryRate', rate: number, country: { __typename: 'CountryDisplay', country: string, code: string } }>, metadata: Array<{ __typename: 'MetadataItem', key: string, value: string }>, privateMetadata: Array<{ __typename: 'MetadataItem', key: string, value: string }> } | null } | null };
export type TaxClassCreateMutationVariables = Exact<{
input: TaxClassCreateInput;
}>;
export type TaxClassCreateMutation = { __typename: 'Mutation', taxClassCreate: { __typename: 'TaxClassCreate', errors: Array<{ __typename: 'TaxClassCreateError', field: string | null, code: TaxClassCreateErrorCode }>, taxClass: { __typename: 'TaxClass', id: string, name: string, countries: Array<{ __typename: 'TaxClassCountryRate', rate: number, country: { __typename: 'CountryDisplay', country: string, code: string } }> } | null } | null };
export type TaxClassCreateMutation = { __typename: 'Mutation', taxClassCreate: { __typename: 'TaxClassCreate', errors: Array<{ __typename: 'TaxClassCreateError', field: string | null, code: TaxClassCreateErrorCode, message: string | null }>, taxClass: { __typename: 'TaxClass', id: string, name: string, countries: Array<{ __typename: 'TaxClassCountryRate', rate: number, country: { __typename: 'CountryDisplay', country: string, code: string } }>, metadata: Array<{ __typename: 'MetadataItem', key: string, value: string }>, privateMetadata: Array<{ __typename: 'MetadataItem', key: string, value: string }> } | null } | null };
export type TaxClassDeleteMutationVariables = Exact<{
id: Scalars['ID'];
}>;
export type TaxClassDeleteMutation = { __typename: 'Mutation', taxClassDelete: { __typename: 'TaxClassDelete', errors: Array<{ __typename: 'TaxClassDeleteError', field: string | null, code: TaxClassDeleteErrorCode }> } | null };
export type TaxClassDeleteMutation = { __typename: 'Mutation', taxClassDelete: { __typename: 'TaxClassDelete', errors: Array<{ __typename: 'TaxClassDeleteError', field: string | null, code: TaxClassDeleteErrorCode, message: string | null }> } | null };
export type TaxConfigurationsListQueryVariables = Exact<{
before?: InputMaybe<Scalars['String']>;
@ -9040,7 +9042,7 @@ export type TaxClassesListQueryVariables = Exact<{
}>;
export type TaxClassesListQuery = { __typename: 'Query', taxClasses: { __typename: 'TaxClassCountableConnection', edges: Array<{ __typename: 'TaxClassCountableEdge', node: { __typename: 'TaxClass', id: string, name: string, countries: Array<{ __typename: 'TaxClassCountryRate', rate: number, country: { __typename: 'CountryDisplay', country: string, code: string } }> } }> } | null };
export type TaxClassesListQuery = { __typename: 'Query', taxClasses: { __typename: 'TaxClassCountableConnection', edges: Array<{ __typename: 'TaxClassCountableEdge', node: { __typename: 'TaxClass', id: string, name: string, countries: Array<{ __typename: 'TaxClassCountryRate', rate: number, country: { __typename: 'CountryDisplay', country: string, code: string } }>, metadata: Array<{ __typename: 'MetadataItem', key: string, value: string }>, privateMetadata: Array<{ __typename: 'MetadataItem', key: string, value: string }> } }> } | null };
export type TaxClassAssignQueryVariables = Exact<{
first?: InputMaybe<Scalars['Int']>;

View file

@ -8,7 +8,7 @@ import PageHeader from "@saleor/components/PageHeader";
import Savebar from "@saleor/components/Savebar";
import {
ProductTypeKindEnum,
TaxClassFragment,
TaxClassBaseFragment,
WeightUnitsEnum,
} from "@saleor/graphql";
import { SubmitPromise } from "@saleor/hooks/useForm";
@ -44,7 +44,7 @@ export interface ProductTypeCreatePageProps {
disabled: boolean;
pageTitle: string;
saveButtonBarState: ConfirmButtonTransitionState;
taxClasses: Array<Omit<TaxClassFragment, "countries">>;
taxClasses: TaxClassBaseFragment[];
kind: ProductTypeKindEnum;
onChangeKind: (kind: ProductTypeKindEnum) => void;
onSubmit: (data: ProductTypeForm) => SubmitPromise<any[]>;

View file

@ -12,7 +12,7 @@ import {
ProductAttributeType,
ProductTypeDetailsQuery,
ProductTypeKindEnum,
TaxClassFragment,
TaxClassBaseFragment,
WeightUnitsEnum,
} from "@saleor/graphql";
import { SubmitPromise } from "@saleor/hooks/useForm";
@ -64,7 +64,7 @@ export interface ProductTypeDetailsPageProps {
pageTitle: string;
productAttributeList: ListActions;
saveButtonBarState: ConfirmButtonTransitionState;
taxClasses: Array<Omit<TaxClassFragment, "countries">>;
taxClasses: TaxClassBaseFragment[];
variantAttributeList: ListActions;
onAttributeAdd: (type: ProductAttributeType) => void;
onAttributeReorder: (event: ReorderEvent, type: ProductAttributeType) => void;

View file

@ -1,7 +1,7 @@
import { Card, CardContent } from "@material-ui/core";
import CardTitle from "@saleor/components/CardTitle";
import SingleAutocompleteSelectField from "@saleor/components/SingleAutocompleteSelectField";
import { TaxClassFragment } from "@saleor/graphql";
import { TaxClassBaseFragment } from "@saleor/graphql";
import { sectionNames } from "@saleor/intl";
import { makeStyles } from "@saleor/macaw-ui";
import { taxesMessages } from "@saleor/taxes/messages";
@ -16,7 +16,7 @@ interface ProductTypeTaxesProps {
taxClassId: string;
};
taxClassDisplayName: string;
taxClasses: Array<Omit<TaxClassFragment, "countries">>;
taxClasses: TaxClassBaseFragment[];
disabled: boolean;
onChange: (event: React.ChangeEvent<any>) => void;
onFetchMore: FetchMoreProps;

View file

@ -1,4 +1,4 @@
import { ProductTypeKindEnum, TaxClassFragment } from "@saleor/graphql";
import { ProductTypeKindEnum, TaxClassBaseFragment } from "@saleor/graphql";
import { ChangeEvent, FormChange } from "@saleor/hooks/useForm";
export const makeProductTypeKindChangeHandler = (
@ -12,7 +12,7 @@ export const makeProductTypeKindChangeHandler = (
export function handleTaxClassChange(
event: ChangeEvent,
taxClasses: Array<Omit<TaxClassFragment, "countries">>,
taxClasses: TaxClassBaseFragment[],
formChange: FormChange,
displayChange: (name: string) => void,
) {

View file

@ -28,7 +28,7 @@ import {
SearchProductsQuery,
SearchProductTypesQuery,
SearchWarehousesQuery,
TaxClassFragment,
TaxClassBaseFragment,
} from "@saleor/graphql";
import useNavigator from "@saleor/hooks/useNavigator";
import useStateFromProps from "@saleor/hooks/useStateFromProps";
@ -75,7 +75,7 @@ interface ProductCreatePageProps {
saveButtonBarState: ConfirmButtonTransitionState;
weightUnit: string;
warehouses: RelayToFlat<SearchWarehousesQuery["search"]>;
taxClasses: Array<Omit<TaxClassFragment, "countries">>;
taxClasses: TaxClassBaseFragment[];
fetchMoreTaxClasses: FetchMoreProps;
selectedProductType?: ProductTypeQuery["productType"];
fetchCategories: (data: string) => void;

View file

@ -1,7 +1,7 @@
import { Card, CardContent } from "@material-ui/core";
import CardTitle from "@saleor/components/CardTitle";
import SingleAutocompleteSelectField from "@saleor/components/SingleAutocompleteSelectField";
import { TaxClassFragment } from "@saleor/graphql";
import { TaxClassBaseFragment } from "@saleor/graphql";
import { sectionNames } from "@saleor/intl";
import { makeStyles } from "@saleor/macaw-ui";
import { taxesMessages } from "@saleor/taxes/messages";
@ -14,7 +14,7 @@ import { ProductCreateFormData } from "../ProductCreatePage";
interface ProductTaxesProps {
value: string;
taxClassDisplayName: string;
taxClasses: Array<Omit<TaxClassFragment, "countries">>;
taxClasses: TaxClassBaseFragment[];
disabled: boolean;
onChange: (event: React.ChangeEvent<any>) => void;
onFetchMore: FetchMoreProps;

View file

@ -35,7 +35,7 @@ import {
SearchCollectionsQuery,
SearchPagesQuery,
SearchProductsQuery,
TaxClassFragment,
TaxClassBaseFragment,
WarehouseFragment,
} from "@saleor/graphql";
import { SubmitPromise } from "@saleor/hooks/useForm";
@ -89,7 +89,7 @@ export interface ProductUpdatePageProps {
header: string;
saveButtonBarState: ConfirmButtonTransitionState;
warehouses: WarehouseFragment[];
taxClasses: Array<Omit<TaxClassFragment, "countries">>;
taxClasses: TaxClassBaseFragment[];
fetchMoreTaxClasses: FetchMoreProps;
referencePages?: RelayToFlat<SearchPagesQuery["search"]>;
referenceProducts?: RelayToFlat<SearchProductsQuery["search"]>;

View file

@ -1,7 +1,7 @@
import { Card, CardContent } from "@material-ui/core";
import CardTitle from "@saleor/components/CardTitle";
import SingleAutocompleteSelectField from "@saleor/components/SingleAutocompleteSelectField";
import { TaxClassFragment } from "@saleor/graphql";
import { TaxClassBaseFragment } from "@saleor/graphql";
import { sectionNames } from "@saleor/intl";
import { makeStyles } from "@saleor/macaw-ui";
import { taxesMessages } from "@saleor/taxes/messages";
@ -14,7 +14,7 @@ import { ShippingZoneRateUpdateFormData } from "../ShippingZoneRatesPage/types";
interface ShippingMethodTaxesProps {
value: string;
taxClassDisplayName: string;
taxClasses: Array<Omit<TaxClassFragment, "countries">>;
taxClasses: TaxClassBaseFragment[];
disabled: boolean;
onChange: (event: React.ChangeEvent<any>) => void;
onFetchMore: FetchMoreProps;

View file

@ -14,7 +14,7 @@ import {
ShippingErrorFragment,
ShippingMethodTypeEnum,
ShippingMethodTypeFragment,
TaxClassFragment,
TaxClassBaseFragment,
} from "@saleor/graphql";
import useForm, { SubmitPromise } from "@saleor/hooks/useForm";
import useHandleFormSubmit from "@saleor/hooks/useHandleFormSubmit";
@ -56,7 +56,7 @@ export interface ShippingZoneRatesCreatePageProps extends WithFormId {
onChannelsChange: (data: ChannelShippingData[]) => void;
openChannelsModal: () => void;
variant: ShippingMethodTypeEnum;
taxClasses: Array<Omit<TaxClassFragment, "countries">>;
taxClasses: TaxClassBaseFragment[];
fetchMoreTaxClasses: FetchMoreProps;
}

View file

@ -16,7 +16,7 @@ import {
ShippingMethodTypeEnum,
ShippingMethodTypeFragment,
ShippingZoneQuery,
TaxClassFragment,
TaxClassBaseFragment,
} from "@saleor/graphql";
import useForm, { SubmitPromise } from "@saleor/hooks/useForm";
import useHandleFormSubmit from "@saleor/hooks/useHandleFormSubmit";
@ -70,7 +70,7 @@ export interface ShippingZoneRatesPageProps
onProductAssign: () => void;
onProductUnassign: (ids: string[]) => void;
variant: ShippingMethodTypeEnum;
taxClasses: Array<Omit<TaxClassFragment, "countries">>;
taxClasses: TaxClassBaseFragment[];
fetchMoreTaxClasses: FetchMoreProps;
}

View file

@ -251291,6 +251291,172 @@ exports[`Storyshots Views / Taxes / Tax classes view default 1`] = `
</li>
</section>
</div>
<div
class="VerticalSpacer-container-id VerticalSpacer-container-id"
/>
<div
class="MuiPaper-root-id MuiCard-root-id MuiPaper-elevation0-id MuiPaper-rounded-id"
data-test-expanded="true"
data-test-id="metadata-editor"
data-test-is-private="false"
>
<div
class="MuiCardHeader-root-id Metadata-header-id"
>
<div
class="MuiCardHeader-content-id"
>
<span
class="MuiTypography-root-id MuiCardHeader-title-id MuiTypography-h5-id MuiTypography-displayBlock-id"
>
Metadata
<button
class="MuiButtonBase-root-id IconButton-secondary-id Metadata-expandBtn-id Metadata-rotate-id"
data-test-id="expand"
tabindex="0"
type="button"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root-id MacawIcon-root-id"
fill="none"
focusable="false"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m8 10.5 4 4 4-4"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.5"
/>
<circle
cx="12"
cy="12"
r="10.25"
stroke="currentColor"
stroke-width="1.5"
/>
</svg>
</button>
</span>
</div>
</div>
<div
class="MuiCardContent-root-id Metadata-content-id"
/>
<div
class="MuiCardContent-root-id Metadata-emptyContainer-id"
>
<div
class="MuiTypography-root-id MuiTypography-body2-id MuiTypography-colorTextSecondary-id"
>
No metadata created for this element. Use the button below to add new metadata field.
</div>
</div>
<div
class="MuiCardActions-root-id Metadata-actions-id MuiCardActions-spacing-id"
>
<button
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-outlined-id Button-buttonDefault-id MuiButton-outlinedPrimary-id"
data-test-id="add-field"
tabindex="0"
type="button"
>
<span
class="MuiButton-label-id"
>
Add Field
</span>
</button>
</div>
</div>
<div
class="CardSpacer-spacer-id"
/>
<div
class="MuiPaper-root-id MuiCard-root-id MuiPaper-elevation0-id MuiPaper-rounded-id"
data-test-expanded="true"
data-test-id="metadata-editor"
data-test-is-private="true"
>
<div
class="MuiCardHeader-root-id Metadata-header-id"
>
<div
class="MuiCardHeader-content-id"
>
<span
class="MuiTypography-root-id MuiCardHeader-title-id MuiTypography-h5-id MuiTypography-displayBlock-id"
>
Private Metadata
<button
class="MuiButtonBase-root-id IconButton-secondary-id Metadata-expandBtn-id Metadata-rotate-id"
data-test-id="expand"
tabindex="0"
type="button"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root-id MacawIcon-root-id"
fill="none"
focusable="false"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m8 10.5 4 4 4-4"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.5"
/>
<circle
cx="12"
cy="12"
r="10.25"
stroke="currentColor"
stroke-width="1.5"
/>
</svg>
</button>
</span>
</div>
</div>
<div
class="MuiCardContent-root-id Metadata-content-id"
/>
<div
class="MuiCardContent-root-id Metadata-emptyContainer-id"
>
<div
class="MuiTypography-root-id MuiTypography-body2-id MuiTypography-colorTextSecondary-id"
>
No metadata created for this element. Use the button below to add new metadata field.
</div>
</div>
<div
class="MuiCardActions-root-id Metadata-actions-id MuiCardActions-spacing-id"
>
<button
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-outlined-id Button-buttonDefault-id MuiButton-outlinedPrimary-id"
data-test-id="add-field"
tabindex="0"
type="button"
>
<span
class="MuiButton-label-id"
>
Add Field
</span>
</button>
</div>
</div>
</div>
</div>
</div>
@ -251599,6 +251765,136 @@ exports[`Storyshots Views / Taxes / Tax classes view loading 1`] = `
/>
</section>
</div>
<div
class="VerticalSpacer-container-id VerticalSpacer-container-id"
/>
<div
class="MuiPaper-root-id MuiCard-root-id MuiPaper-elevation0-id MuiPaper-rounded-id"
data-test-expanded="true"
data-test-id="metadata-editor"
data-test-is-private="false"
>
<div
class="MuiCardHeader-root-id Metadata-header-id"
>
<div
class="MuiCardHeader-content-id"
>
<span
class="MuiTypography-root-id MuiCardHeader-title-id MuiTypography-h5-id MuiTypography-displayBlock-id"
>
Metadata
<button
class="MuiButtonBase-root-id IconButton-secondary-id Metadata-expandBtn-id Metadata-rotate-id"
data-test-id="expand"
tabindex="0"
type="button"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root-id MacawIcon-root-id"
fill="none"
focusable="false"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m8 10.5 4 4 4-4"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.5"
/>
<circle
cx="12"
cy="12"
r="10.25"
stroke="currentColor"
stroke-width="1.5"
/>
</svg>
</button>
</span>
</div>
</div>
<div
class="MuiCardContent-root-id"
>
<span
class="Skeleton-skeleton-id"
data-test-id="skeleton"
>
</span>
</div>
</div>
<div
class="CardSpacer-spacer-id"
/>
<div
class="MuiPaper-root-id MuiCard-root-id MuiPaper-elevation0-id MuiPaper-rounded-id"
data-test-expanded="true"
data-test-id="metadata-editor"
data-test-is-private="true"
>
<div
class="MuiCardHeader-root-id Metadata-header-id"
>
<div
class="MuiCardHeader-content-id"
>
<span
class="MuiTypography-root-id MuiCardHeader-title-id MuiTypography-h5-id MuiTypography-displayBlock-id"
>
Private Metadata
<button
class="MuiButtonBase-root-id IconButton-secondary-id Metadata-expandBtn-id Metadata-rotate-id"
data-test-id="expand"
tabindex="0"
type="button"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root-id MacawIcon-root-id"
fill="none"
focusable="false"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m8 10.5 4 4 4-4"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.5"
/>
<circle
cx="12"
cy="12"
r="10.25"
stroke="currentColor"
stroke-width="1.5"
/>
</svg>
</button>
</span>
</div>
</div>
<div
class="MuiCardContent-root-id"
>
<span
class="Skeleton-skeleton-id"
data-test-id="skeleton"
>
</span>
</div>
</div>
</div>
</div>
</div>

View file

@ -166,6 +166,8 @@ export const taxClasses: TaxClassFragment[] = [
rate: 0.15,
},
],
metadata: [],
privateMetadata: [],
},
{
__typename: "TaxClass",
@ -191,5 +193,7 @@ export const taxClasses: TaxClassFragment[] = [
rate: 0.0,
},
],
metadata: [],
privateMetadata: [],
},
];

View file

@ -7,7 +7,7 @@ export const taxConfigurationUpdate = gql`
) {
taxConfigurationUpdate(id: $id, input: $input) {
errors {
...TaxConfigurationUpdateErrorFragment
...TaxConfigurationUpdateError
}
taxConfiguration {
...TaxConfiguration
@ -26,7 +26,7 @@ export const taxCountryConfigurationUpdate = gql`
updateTaxClassRates: $updateTaxClassRates
) {
errors {
...TaxCountryConfigurationUpdateErrorFragment
...TaxCountryConfigurationUpdateError
}
taxCountryConfiguration {
...TaxCountryConfiguration
@ -39,7 +39,7 @@ export const taxCountryConfigurationDelete = gql`
mutation TaxCountryConfigurationDelete($countryCode: CountryCode!) {
taxCountryConfigurationDelete(countryCode: $countryCode) {
errors {
...TaxCountryConfigurationDeleteErrorFragment
...TaxCountryConfigurationDeleteError
}
taxCountryConfiguration {
...TaxCountryConfiguration
@ -52,7 +52,7 @@ export const taxClassUpdate = gql`
mutation TaxClassUpdate($id: ID!, $input: TaxClassUpdateInput!) {
taxClassUpdate(id: $id, input: $input) {
errors {
...TaxClassUpdateErrorFragment
...TaxClassUpdateError
}
taxClass {
...TaxClass
@ -65,7 +65,7 @@ export const taxClassCreate = gql`
mutation TaxClassCreate($input: TaxClassCreateInput!) {
taxClassCreate(input: $input) {
errors {
...TaxClassCreateErrorFragment
...TaxClassCreateError
}
taxClass {
...TaxClass
@ -78,7 +78,7 @@ export const taxClassDelete = gql`
mutation TaxClassDelete($id: ID!) {
taxClassDelete(id: $id) {
errors {
...TaxClassDeleteErrorFragment
...TaxClassDeleteError
}
}
}

View file

@ -9,15 +9,12 @@ import VerticalSpacer from "@saleor/apps/components/VerticalSpacer";
import CardTitle from "@saleor/components/CardTitle";
import Container from "@saleor/components/Container";
import Grid from "@saleor/components/Grid";
import Metadata from "@saleor/components/Metadata";
import PageHeader from "@saleor/components/PageHeader";
import Savebar from "@saleor/components/Savebar";
import Skeleton from "@saleor/components/Skeleton";
import { configurationMenuUrl } from "@saleor/configuration";
import {
TaxClassCreateInput,
TaxClassFragment,
TaxClassUpdateInput,
} from "@saleor/graphql";
import { TaxClassFragment } from "@saleor/graphql";
import { SubmitPromise } from "@saleor/hooks/useForm";
import useNavigator from "@saleor/hooks/useNavigator";
import { sectionNames } from "@saleor/intl";
@ -34,8 +31,11 @@ import {
import { parseQuery } from "@saleor/orders/components/OrderCustomerAddressesEditDialog/utils";
import { getById } from "@saleor/orders/components/OrderReturnPage/utils";
import { taxesMessages } from "@saleor/taxes/messages";
import { TaxClassesPageFormData } from "@saleor/taxes/types";
import { useAutofocus } from "@saleor/taxes/utils/useAutofocus";
import { isLastElement } from "@saleor/taxes/utils/utils";
import { getFormErrors } from "@saleor/utils/errors";
import getTaxesErrorMessage from "@saleor/utils/errors/taxes";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
@ -52,8 +52,8 @@ interface TaxClassesPageProps {
disabled: boolean;
onCreateNewButtonClick: () => void;
onTaxClassDelete: (id: string) => SubmitPromise;
onTaxClassCreate: (input: TaxClassCreateInput) => SubmitPromise;
onTaxClassUpdate: (id: string, input: TaxClassUpdateInput) => SubmitPromise;
onTaxClassCreate: (data: TaxClassesPageFormData) => SubmitPromise;
onTaxClassUpdate: (data: TaxClassesPageFormData) => SubmitPromise;
}
export const TaxClassesPage: React.FC<TaxClassesPageProps> = props => {
@ -90,11 +90,13 @@ export const TaxClassesPage: React.FC<TaxClassesPageProps> = props => {
onTaxClassUpdate={onTaxClassUpdate}
disabled={disabled}
>
{({ data, handlers, submit, change }) => {
{({ data, validationErrors, handlers, submit, change }) => {
const filteredRates = data.updateTaxClassRates.filter(
rate => rate.label.search(new RegExp(parseQuery(query), "i")) >= 0,
);
const formErrors = getFormErrors(["name"], validationErrors);
return (
<Container>
<PageHeader title={intl.formatMessage(sectionNames.taxes)} />
@ -140,6 +142,8 @@ export const TaxClassesPage: React.FC<TaxClassesPageProps> = props => {
fullWidth
inputProps={{ className: classes.namePadding }}
inputRef={nameInputRef}
error={!!formErrors.name}
helperText={getTaxesErrorMessage(formErrors.name, intl)}
/>
</CardContent>
</Card>
@ -238,6 +242,8 @@ export const TaxClassesPage: React.FC<TaxClassesPageProps> = props => {
</>
)}
</Card>
<VerticalSpacer spacing={3} />
<Metadata data={data} onChange={handlers.changeMetadata} />
</div>
)}
</Grid>

View file

@ -1,56 +1,58 @@
import { useExitFormDialog } from "@saleor/components/Form/useExitFormDialog";
import {
CountryCode,
TaxClassCreateInput,
TaxClassFragment,
TaxClassRateInput,
TaxClassUpdateInput,
} from "@saleor/graphql";
import { TaxClassFragment } from "@saleor/graphql";
import useForm, { FormChange, SubmitPromise } from "@saleor/hooks/useForm";
import useFormset, { FormsetData } from "@saleor/hooks/useFormset";
import useFormset from "@saleor/hooks/useFormset";
import useHandleFormSubmit from "@saleor/hooks/useHandleFormSubmit";
import React from "react";
import { TaxClassesPageFormData } from "@saleor/taxes/types";
import { getTaxClassInitialFormData } from "@saleor/taxes/utils/data";
import { validateTaxClassFormData } from "@saleor/taxes/utils/validation";
import { TaxClassError } from "@saleor/utils/errors/taxes";
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
import React, { useState } from "react";
export interface TaxClassesPageFormData {
updateTaxClassRates: FormsetData<TaxClassRateInput>;
name?: string;
interface TaxClassesFormHandlers {
handleRateChange: (id: string, value: string) => void;
changeMetadata: FormChange;
}
export interface UseTaxClassesFormResult {
validationErrors: TaxClassError[];
data: TaxClassesPageFormData;
submit: () => SubmitPromise;
submit: () => SubmitPromise<TaxClassError[]>;
change: FormChange;
handlers: { handleRateChange: (id: string, value: string) => void };
handlers: TaxClassesFormHandlers;
}
interface TaxClassesFormProps {
children: (props: any) => React.ReactNode;
children: (props: UseTaxClassesFormResult) => React.ReactNode;
taxClass: TaxClassFragment | undefined;
onTaxClassCreate: (data: TaxClassCreateInput) => SubmitPromise;
onTaxClassUpdate: (id: string, data: TaxClassUpdateInput) => SubmitPromise;
onTaxClassCreate: (
data: TaxClassesPageFormData,
) => SubmitPromise<TaxClassError[]>;
onTaxClassUpdate: (
data: TaxClassesPageFormData,
) => SubmitPromise<TaxClassError[]>;
disabled: boolean;
}
function useTaxClassesForm(
taxClass: TaxClassFragment,
onTaxClassCreate,
onTaxClassUpdate,
disabled,
onTaxClassCreate: (
data: TaxClassesPageFormData,
) => SubmitPromise<TaxClassError[]>,
onTaxClassUpdate: (
data: TaxClassesPageFormData,
) => SubmitPromise<TaxClassError[]>,
disabled: boolean,
): UseTaxClassesFormResult {
// Initial
const initialFormData: TaxClassUpdateInput = {
name: taxClass?.name ?? "",
};
const isNewTaxClass = taxClass?.id === "new";
const initialFormsetData = taxClass?.countries
.map(item => ({
id: item.country.code,
label: item.country.country,
value: item.rate?.toString() ?? "",
data: null,
}))
.sort((a, b) => a.label.localeCompare(b.label));
const initialFormData = getTaxClassInitialFormData(taxClass);
const initialFormsetData = initialFormData.updateTaxClassRates;
const formset = useFormset(initialFormsetData);
const { formId, triggerChange, data, handleChange } = useForm(
initialFormData,
@ -60,42 +62,41 @@ function useTaxClassesForm(
},
);
const [validationErrors, setValidationErrors] = useState<TaxClassError[]>([]);
if (isNewTaxClass) {
triggerChange();
}
const formset = useFormset(initialFormsetData);
// Handlers
const handleRateChange = (id: string, value: string) => {
triggerChange();
formset.change(id, value);
};
const {
makeChangeHandler: makeMetadataChangeHandler,
} = useMetadataChangeTrigger();
const changeMetadata = makeMetadataChangeHandler(handleChange);
// Submit
const submitData = {
name: data.name,
[isNewTaxClass
? "createCountryRates"
: "updateCountryRates"]: formset.data.flatMap(item => {
const { id, value } = item;
if (!value && isNewTaxClass) {
return [];
}
const parsedRate = parseFloat(value);
return {
rate: parsedRate,
countryCode: id as CountryCode,
};
}),
};
const handleSubmit = async (data: TaxClassUpdateInput) => {
const errors = isNewTaxClass
? await onTaxClassCreate(data)
: await onTaxClassUpdate(taxClass.id, data);
const handleSubmit = async (data: TaxClassesPageFormData) => {
const errors = validateTaxClassFormData(data);
return errors;
setValidationErrors(errors);
if (errors.length) {
return errors;
}
if (isNewTaxClass) {
return onTaxClassCreate(data);
}
return onTaxClassUpdate(data);
};
const handleFormSubmit = useHandleFormSubmit({
@ -103,7 +104,11 @@ function useTaxClassesForm(
onSubmit: handleSubmit,
});
const submit = () => handleFormSubmit(submitData);
const submit = () =>
handleFormSubmit({
...data,
updateTaxClassRates: formset.data,
});
// Exit form util
@ -118,8 +123,9 @@ function useTaxClassesForm(
setIsSubmitDisabled(disabled);
return {
validationErrors,
data: { ...data, updateTaxClassRates: formset.data },
handlers: { handleRateChange },
handlers: { handleRateChange, changeMetadata },
change: handleChange,
submit,
};

9
src/taxes/types.ts Normal file
View file

@ -0,0 +1,9 @@
import { MetadataFormData } from "@saleor/components/Metadata";
import { TaxClassRateInput } from "@saleor/graphql";
import { FormsetData } from "@saleor/hooks/useFormset";
export interface TaxClassesPageFormData extends MetadataFormData {
id: string;
updateTaxClassRates: FormsetData<TaxClassRateInput>;
name?: string;
}

59
src/taxes/utils/data.ts Normal file
View file

@ -0,0 +1,59 @@
import {
CountryCode,
CountryRateInput,
TaxClassCreateInput,
TaxClassFragment,
TaxClassUpdateInput,
} from "@saleor/graphql";
import { FormsetAtomicData } from "@saleor/hooks/useFormset";
import { mapMetadataItemToInput } from "@saleor/utils/maps";
import { TaxClassesPageFormData } from "../types";
export const getTaxClassInitialFormData = (
taxClass?: TaxClassFragment,
): TaxClassesPageFormData => {
const initialCountries = taxClass?.countries
.map(item => ({
id: item.country.code,
label: item.country.country,
value: item.rate?.toString() ?? "",
data: null,
}))
.sort((a, b) => a.label.localeCompare(b.label));
return {
id: taxClass?.id,
name: taxClass?.name ?? "",
metadata: taxClass?.metadata?.map(mapMetadataItemToInput),
privateMetadata: taxClass?.privateMetadata?.map(mapMetadataItemToInput),
updateTaxClassRates: initialCountries,
};
};
const createCountryRateInput = ({
id,
value,
}: FormsetAtomicData): CountryRateInput => ({
countryCode: id as CountryCode,
rate: parseFloat(value),
});
export const createTaxClassCreateInput = (
data: TaxClassesPageFormData,
): TaxClassCreateInput => ({
name: data.name,
createCountryRates: data.updateTaxClassRates.flatMap(item => {
if (!item.value) {
return [];
}
return createCountryRateInput(item);
}),
});
export const createTaxClassUpdateInput = (
data: TaxClassesPageFormData,
): TaxClassUpdateInput => ({
name: data.name,
updateCountryRates: data.updateTaxClassRates.flatMap(createCountryRateInput),
});

View file

@ -1,9 +1,9 @@
import { TaxClassFragment, useTaxClassAssignQuery } from "@saleor/graphql";
import { TaxClassBaseFragment, useTaxClassAssignQuery } from "@saleor/graphql";
import { FetchMoreProps } from "@saleor/types";
import { mapEdgesToItems } from "@saleor/utils/maps";
interface UseTaxClassFetchMoreHookResult {
taxClasses: Array<Omit<TaxClassFragment, "countries">>;
taxClasses: TaxClassBaseFragment[];
fetchMoreTaxClasses: FetchMoreProps;
}

View file

@ -0,0 +1,21 @@
import { CommonError, CommonErrorCode } from "@saleor/utils/errors/common";
import { TaxClassesPageFormData } from "../types";
export const createEmptyRequiredError = (
field: string,
): CommonError<CommonErrorCode> => ({
code: CommonErrorCode.REQUIRED,
field,
message: null,
});
export const validateTaxClassFormData = (data: TaxClassesPageFormData) => {
let errors: Array<CommonError<CommonErrorCode>> = [];
if (!data.name) {
errors = [...errors, createEmptyRequiredError("name")];
}
return errors;
};

View file

@ -1,23 +1,33 @@
import {
TaxClassCreateInput,
TaxClassCreateErrorFragment,
TaxClassFragment,
TaxClassUpdateInput,
useTaxClassCreateMutation,
useTaxClassDeleteMutation,
useTaxClassesListQuery,
useTaxClassUpdateMutation,
useTaxCountriesListQuery,
useUpdateMetadataMutation,
useUpdatePrivateMetadataMutation,
} from "@saleor/graphql";
import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier";
import { commonMessages } from "@saleor/intl";
import createMetadataCreateHandler, {
CreateMetadataHandlerFunctionResult,
} from "@saleor/utils/handlers/metadataCreateHandler";
import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler";
import { mapEdgesToItems } from "@saleor/utils/maps";
import React from "react";
import { useIntl } from "react-intl";
import { taxesMessages } from "../messages";
import TaxClassesPage from "../pages/TaxClassesPage";
import { TaxClassesPageFormData } from "../types";
import { taxClassesListUrl, TaxTab, taxTabPath } from "../urls";
import {
createTaxClassCreateInput,
createTaxClassUpdateInput,
} from "../utils/data";
import { useTaxUrlRedirect } from "../utils/useTaxUrlRedirect";
import { mapUndefinedCountriesToTaxClasses } from "../utils/utils";
@ -40,12 +50,17 @@ export const TaxClassesList: React.FC<TaxClassesListProps> = ({ id }) => {
id: "new",
name: intl.formatMessage(taxesMessages.newTaxClass),
countries: [],
metadata: [],
privateMetadata: [],
}),
[intl],
);
const isNewTaxClass = id === "new";
const [updateMetadata] = useUpdateMetadataMutation({});
const [updatePrivateMetadata] = useUpdatePrivateMetadataMutation({});
const [taxClassDeleteMutation] = useTaxClassDeleteMutation({
onCompleted: data => {
const errors = data?.taxClassDelete?.errors;
@ -89,15 +104,23 @@ export const TaxClassesList: React.FC<TaxClassesListProps> = ({ id }) => {
},
});
const handleCreateTaxClass = async (input: TaxClassCreateInput) => {
const createTaxClass = async (
data: TaxClassesPageFormData,
): Promise<CreateMetadataHandlerFunctionResult<
TaxClassCreateErrorFragment
>> => {
const res = await taxClassCreateMutation({
variables: {
input,
input: createTaxClassCreateInput(data),
},
});
refetch();
navigate(res?.data?.taxClassCreate?.taxClass?.id);
return res;
const taxClassCreate = res?.data?.taxClassCreate;
return {
id: taxClassCreate?.taxClass?.id,
errors: taxClassCreate?.errors,
};
};
const handleDeleteTaxClass = async (id: string) => {
@ -114,14 +137,17 @@ export const TaxClassesList: React.FC<TaxClassesListProps> = ({ id }) => {
}
};
const handleUpdateTaxClass = async (id: string, input: TaxClassUpdateInput) =>
taxClassUpdateMutation({
const updateTaxClass = async (data: TaxClassesPageFormData) => {
const res = await taxClassUpdateMutation({
variables: {
id,
input,
id: data.id,
input: createTaxClassUpdateInput(data),
},
});
return res?.data?.taxClassUpdate?.errors || [];
};
const { data, refetch } = useTaxClassesListQuery({
variables: { first: 100 },
});
@ -152,10 +178,34 @@ export const TaxClassesList: React.FC<TaxClassesListProps> = ({ id }) => {
newTaxClass,
]);
const savebarState =
id === "new"
? taxClassCreateMutationState.status
: taxClassUpdateMutationState.status;
const selectedTaxClass = React.useMemo(() => {
if (isNewTaxClass) {
return newTaxClass;
}
return taxClasses?.find(taxClass => taxClass.id === id);
}, [id, isNewTaxClass, newTaxClass, taxClasses]);
const handleCreateTaxClass = createMetadataCreateHandler(
createTaxClass,
updateMetadata,
updatePrivateMetadata,
id => {
refetch();
navigate(id);
},
);
const handleUpdateTaxClass = createMetadataUpdateHandler(
selectedTaxClass,
updateTaxClass,
variables => updateMetadata({ variables }),
variables => updatePrivateMetadata({ variables }),
);
const savebarState = isNewTaxClass
? taxClassCreateMutationState.status
: taxClassUpdateMutationState.status;
useTaxUrlRedirect({
id,

View file

@ -49,6 +49,7 @@ const messages = defineMessages({
interface ErrorFragment {
code: AccountErrorCode | SetPasswordData["errors"][number]["code"];
field: string | null;
}
function getAccountErrorMessage(err: ErrorFragment, intl: IntlShape): string {

View file

@ -16,11 +16,18 @@ const commonErrorMessages = defineMessages({
},
});
type CommonErrorCode = "GRAPHQL_ERROR" | "INVALID" | "REQUIRED";
export const CommonErrorCode = {
GRAPHQL_ERROR: "GRAPHQL_ERROR",
INVALID: "INVALID",
REQUIRED: "REQUIRED",
} as const;
interface CommonError<ErrorCode> {
export type CommonErrorCode = typeof CommonErrorCode[keyof typeof CommonErrorCode];
export interface CommonError<ErrorCode> {
code: ErrorCode | CommonErrorCode;
field?: string | null;
field: string | null;
message?: string | null;
}
export function getCommonFormFieldErrorMessage<ErrorCode>(

27
src/utils/errors/taxes.ts Normal file
View file

@ -0,0 +1,27 @@
import {
TaxClassCreateErrorFragment,
TaxClassDeleteErrorFragment,
TaxClassUpdateErrorFragment,
} from "@saleor/graphql";
import { IntlShape } from "react-intl";
import {
CommonError,
CommonErrorCode,
getCommonFormFieldErrorMessage,
} from "./common";
export type TaxClassError =
| TaxClassUpdateErrorFragment
| TaxClassCreateErrorFragment
| TaxClassDeleteErrorFragment
| CommonError<CommonErrorCode>;
function getTaxesErrorMessage(
err: Omit<TaxClassError, "__typename"> | undefined,
intl: IntlShape,
): string {
return getCommonFormFieldErrorMessage(err, intl);
}
export default getTaxesErrorMessage;

View file

@ -15,6 +15,7 @@ function createMetadataCreateHandler<T extends MetadataFormData, TError>(
create: (data: T) => Promise<CreateMetadataHandlerFunctionResult<TError>>,
setMetadata: UpdateMetadataMutationFn,
setPrivateMetadata: UpdatePrivateMetadataMutationFn,
onComplete?: (id: string) => void,
) {
return async (data: T) => {
const { id, errors } = await create(data);
@ -60,6 +61,10 @@ function createMetadataCreateHandler<T extends MetadataFormData, TError>(
}
}
if (onComplete) {
onComplete(id);
}
return [];
};
}