diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index bd84c6a79..e6b9c5cc2 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -1483,6 +1483,9 @@ "context": "section header", "string": "Eligible Categories" }, + "AcMzwj": { + "string": "Menu items" + }, "AdmPca": { "context": "error message", "string": "Max value cannot be less than min value" @@ -2556,6 +2559,10 @@ "context": "payment status", "string": "Partially paid" }, + "IOshTA": { + "context": "header", + "string": "Translation MenuItem \"{menuItemName}\" - {languageCode}" + }, "IUeGzv": { "context": "plugin name", "string": "Plugin Name" diff --git a/src/fragments/translations.ts b/src/fragments/translations.ts index c3104e37b..f92eeb539 100644 --- a/src/fragments/translations.ts +++ b/src/fragments/translations.ts @@ -312,3 +312,19 @@ export const attributeValueTranslatableContentFragment = gql` } } `; + +export const menuItemTranslationFragment = gql` + fragment MenuItemTranslation on MenuItemTranslatableContent { + translation(languageCode: $language) { + id + language { + language + } + name + } + menuItem { + id + name + } + } +`; diff --git a/src/graphql/hooks.generated.ts b/src/graphql/hooks.generated.ts index bd141bebb..90ea95187 100644 --- a/src/graphql/hooks.generated.ts +++ b/src/graphql/hooks.generated.ts @@ -2657,6 +2657,21 @@ export const AttributeValueTranslatableContentFragmentDoc = gql` } } ${AttributeChoicesTranslationFragmentDoc}`; +export const MenuItemTranslationFragmentDoc = gql` + fragment MenuItemTranslation on MenuItemTranslatableContent { + translation(languageCode: $language) { + id + language { + language + } + name + } + menuItem { + id + name + } +} + `; export const WarehouseWithShippingFragmentDoc = gql` fragment WarehouseWithShipping on Warehouse { ...Warehouse @@ -15239,6 +15254,55 @@ export function useUpdateShippingMethodTranslationsMutation(baseOptions?: Apollo export type UpdateShippingMethodTranslationsMutationHookResult = ReturnType; export type UpdateShippingMethodTranslationsMutationResult = Apollo.MutationResult; export type UpdateShippingMethodTranslationsMutationOptions = Apollo.BaseMutationOptions; +export const UpdateMenuItemTranslationsDocument = gql` + mutation UpdateMenuItemTranslations($id: ID!, $input: NameTranslationInput!, $language: LanguageCodeEnum!) { + menuItemTranslate(id: $id, input: $input, languageCode: $language) { + errors { + field + message + } + menuItem { + id + name + translation(languageCode: $language) { + id + language { + language + } + name + } + } + } +} + `; +export type UpdateMenuItemTranslationsMutationFn = Apollo.MutationFunction; + +/** + * __useUpdateMenuItemTranslationsMutation__ + * + * To run a mutation, you first call `useUpdateMenuItemTranslationsMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useUpdateMenuItemTranslationsMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [updateMenuItemTranslationsMutation, { data, loading, error }] = useUpdateMenuItemTranslationsMutation({ + * variables: { + * id: // value for 'id' + * input: // value for 'input' + * language: // value for 'language' + * }, + * }); + */ +export function useUpdateMenuItemTranslationsMutation(baseOptions?: ApolloReactHooks.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return ApolloReactHooks.useMutation(UpdateMenuItemTranslationsDocument, options); + } +export type UpdateMenuItemTranslationsMutationHookResult = ReturnType; +export type UpdateMenuItemTranslationsMutationResult = Apollo.MutationResult; +export type UpdateMenuItemTranslationsMutationOptions = Apollo.BaseMutationOptions; export const CategoryTranslationsDocument = gql` query CategoryTranslations($language: LanguageCodeEnum!, $first: Int, $after: String, $last: Int, $before: String) { translations( @@ -15663,6 +15727,59 @@ export function useShippingMethodTranslationsLazyQuery(baseOptions?: ApolloReact export type ShippingMethodTranslationsQueryHookResult = ReturnType; export type ShippingMethodTranslationsLazyQueryHookResult = ReturnType; export type ShippingMethodTranslationsQueryResult = Apollo.QueryResult; +export const MenuItemTranslationsDocument = gql` + query MenuItemTranslations($language: LanguageCodeEnum!, $first: Int, $after: String, $last: Int, $before: String) { + translations( + kind: MENU_ITEM + before: $before + after: $after + first: $first + last: $last + ) { + edges { + node { + ...MenuItemTranslation + } + } + pageInfo { + ...PageInfo + } + } +} + ${MenuItemTranslationFragmentDoc} +${PageInfoFragmentDoc}`; + +/** + * __useMenuItemTranslationsQuery__ + * + * To run a query within a React component, call `useMenuItemTranslationsQuery` and pass it any options that fit your needs. + * When your component renders, `useMenuItemTranslationsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useMenuItemTranslationsQuery({ + * variables: { + * language: // value for 'language' + * first: // value for 'first' + * after: // value for 'after' + * last: // value for 'last' + * before: // value for 'before' + * }, + * }); + */ +export function useMenuItemTranslationsQuery(baseOptions: ApolloReactHooks.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return ApolloReactHooks.useQuery(MenuItemTranslationsDocument, options); + } +export function useMenuItemTranslationsLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return ApolloReactHooks.useLazyQuery(MenuItemTranslationsDocument, options); + } +export type MenuItemTranslationsQueryHookResult = ReturnType; +export type MenuItemTranslationsLazyQueryHookResult = ReturnType; +export type MenuItemTranslationsQueryResult = Apollo.QueryResult; export const ProductTranslationDetailsDocument = gql` query ProductTranslationDetails($id: ID!, $language: LanguageCodeEnum!) { translation(kind: PRODUCT, id: $id) { @@ -16031,6 +16148,42 @@ export function useShippingMethodTranslationDetailsLazyQuery(baseOptions?: Apoll export type ShippingMethodTranslationDetailsQueryHookResult = ReturnType; export type ShippingMethodTranslationDetailsLazyQueryHookResult = ReturnType; export type ShippingMethodTranslationDetailsQueryResult = Apollo.QueryResult; +export const MenuItemTranslationDetailsDocument = gql` + query MenuItemTranslationDetails($id: ID!, $language: LanguageCodeEnum!) { + translation(kind: MENU_ITEM, id: $id) { + ...MenuItemTranslation + } +} + ${MenuItemTranslationFragmentDoc}`; + +/** + * __useMenuItemTranslationDetailsQuery__ + * + * To run a query within a React component, call `useMenuItemTranslationDetailsQuery` and pass it any options that fit your needs. + * When your component renders, `useMenuItemTranslationDetailsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useMenuItemTranslationDetailsQuery({ + * variables: { + * id: // value for 'id' + * language: // value for 'language' + * }, + * }); + */ +export function useMenuItemTranslationDetailsQuery(baseOptions: ApolloReactHooks.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return ApolloReactHooks.useQuery(MenuItemTranslationDetailsDocument, options); + } +export function useMenuItemTranslationDetailsLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return ApolloReactHooks.useLazyQuery(MenuItemTranslationDetailsDocument, options); + } +export type MenuItemTranslationDetailsQueryHookResult = ReturnType; +export type MenuItemTranslationDetailsLazyQueryHookResult = ReturnType; +export type MenuItemTranslationDetailsQueryResult = Apollo.QueryResult; export const UpdateMetadataDocument = gql` mutation UpdateMetadata($id: ID!, $input: [MetadataInput!]!, $keysToDelete: [String!]!) { updateMetadata(id: $id, input: $input) { diff --git a/src/graphql/types.generated.ts b/src/graphql/types.generated.ts index 2d5bed7ef..5c6ea3c47 100644 --- a/src/graphql/types.generated.ts +++ b/src/graphql/types.generated.ts @@ -6568,6 +6568,8 @@ export type AttributeTranslationDetailsFragment = { __typename: 'AttributeTransl export type AttributeValueTranslatableContentFragment = { __typename: 'AttributeTranslatableContent', translation: { __typename: 'AttributeTranslation', id: string, name: string } | null, attribute: { __typename: 'Attribute', id: string, name: string | null, inputType: AttributeInputTypeEnum | null, choices: { __typename: 'AttributeValueCountableConnection', pageInfo: { __typename: 'PageInfo', endCursor: string | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor: string | null }, edges: Array<{ __typename: 'AttributeValueCountableEdge', cursor: string, node: { __typename: 'AttributeValue', id: string, name: string | null, richText: any | null, inputType: AttributeInputTypeEnum | null, translation: { __typename: 'AttributeValueTranslation', id: string, name: string, richText: any | null } | null } }> } | null } | null }; +export type MenuItemTranslationFragment = { __typename: 'MenuItemTranslatableContent', translation: { __typename: 'MenuItemTranslation', id: string, name: string, language: { __typename: 'LanguageDisplay', language: string } } | null, menuItem: { __typename: 'MenuItem', id: string, name: string } | null }; + export type WarehouseFragment = { __typename: 'Warehouse', id: string, name: string }; export type WarehouseWithShippingFragment = { __typename: 'Warehouse', id: string, name: string, shippingZones: { __typename: 'ShippingZoneCountableConnection', edges: Array<{ __typename: 'ShippingZoneCountableEdge', node: { __typename: 'ShippingZone', id: string, name: string } }> } }; @@ -8203,6 +8205,15 @@ export type UpdateShippingMethodTranslationsMutationVariables = Exact<{ export type UpdateShippingMethodTranslationsMutation = { __typename: 'Mutation', shippingPriceTranslate: { __typename: 'ShippingPriceTranslate', errors: Array<{ __typename: 'TranslationError', code: TranslationErrorCode, field: string | null, message: string | null }>, shippingMethod: { __typename: 'ShippingMethodType', id: string, name: string, description: any | null, translation: { __typename: 'ShippingMethodTranslation', id: string, name: string | null, description: any | null, language: { __typename: 'LanguageDisplay', language: string } } | null } | null } | null }; +export type UpdateMenuItemTranslationsMutationVariables = Exact<{ + id: Scalars['ID']; + input: NameTranslationInput; + language: LanguageCodeEnum; +}>; + + +export type UpdateMenuItemTranslationsMutation = { __typename: 'Mutation', menuItemTranslate: { __typename: 'MenuItemTranslate', errors: Array<{ __typename: 'TranslationError', field: string | null, message: string | null }>, menuItem: { __typename: 'MenuItem', id: string, name: string, translation: { __typename: 'MenuItemTranslation', id: string, name: string, language: { __typename: 'LanguageDisplay', language: string } } | null } | null } | null }; + export type CategoryTranslationsQueryVariables = Exact<{ language: LanguageCodeEnum; first?: InputMaybe; @@ -8291,6 +8302,17 @@ export type ShippingMethodTranslationsQueryVariables = Exact<{ export type ShippingMethodTranslationsQuery = { __typename: 'Query', translations: { __typename: 'TranslatableItemConnection', edges: Array<{ __typename: 'TranslatableItemEdge', node: { __typename: 'ProductTranslatableContent' } | { __typename: 'CollectionTranslatableContent' } | { __typename: 'CategoryTranslatableContent' } | { __typename: 'AttributeTranslatableContent' } | { __typename: 'AttributeValueTranslatableContent' } | { __typename: 'ProductVariantTranslatableContent' } | { __typename: 'PageTranslatableContent' } | { __typename: 'ShippingMethodTranslatableContent', id: string, name: string, description: any | null, shippingMethod: { __typename: 'ShippingMethodType', id: string } | null, translation: { __typename: 'ShippingMethodTranslation', id: string, name: string | null, description: any | null, language: { __typename: 'LanguageDisplay', code: LanguageCodeEnum, language: string } } | null } | { __typename: 'SaleTranslatableContent' } | { __typename: 'VoucherTranslatableContent' } | { __typename: 'MenuItemTranslatableContent' } }>, pageInfo: { __typename: 'PageInfo', endCursor: string | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor: string | null } } | null }; +export type MenuItemTranslationsQueryVariables = Exact<{ + language: LanguageCodeEnum; + first?: InputMaybe; + after?: InputMaybe; + last?: InputMaybe; + before?: InputMaybe; +}>; + + +export type MenuItemTranslationsQuery = { __typename: 'Query', translations: { __typename: 'TranslatableItemConnection', edges: Array<{ __typename: 'TranslatableItemEdge', node: { __typename: 'ProductTranslatableContent' } | { __typename: 'CollectionTranslatableContent' } | { __typename: 'CategoryTranslatableContent' } | { __typename: 'AttributeTranslatableContent' } | { __typename: 'AttributeValueTranslatableContent' } | { __typename: 'ProductVariantTranslatableContent' } | { __typename: 'PageTranslatableContent' } | { __typename: 'ShippingMethodTranslatableContent' } | { __typename: 'SaleTranslatableContent' } | { __typename: 'VoucherTranslatableContent' } | { __typename: 'MenuItemTranslatableContent', translation: { __typename: 'MenuItemTranslation', id: string, name: string, language: { __typename: 'LanguageDisplay', language: string } } | null, menuItem: { __typename: 'MenuItem', id: string, name: string } | null } }>, pageInfo: { __typename: 'PageInfo', endCursor: string | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor: string | null } } | null }; + export type ProductTranslationDetailsQueryVariables = Exact<{ id: Scalars['ID']; language: LanguageCodeEnum; @@ -8374,6 +8396,14 @@ export type ShippingMethodTranslationDetailsQueryVariables = Exact<{ export type ShippingMethodTranslationDetailsQuery = { __typename: 'Query', translation: { __typename: 'ProductTranslatableContent' } | { __typename: 'CollectionTranslatableContent' } | { __typename: 'CategoryTranslatableContent' } | { __typename: 'AttributeTranslatableContent' } | { __typename: 'AttributeValueTranslatableContent' } | { __typename: 'ProductVariantTranslatableContent' } | { __typename: 'PageTranslatableContent' } | { __typename: 'ShippingMethodTranslatableContent', id: string, name: string, description: any | null, shippingMethod: { __typename: 'ShippingMethodType', id: string } | null, translation: { __typename: 'ShippingMethodTranslation', id: string, name: string | null, description: any | null, language: { __typename: 'LanguageDisplay', code: LanguageCodeEnum, language: string } } | null } | { __typename: 'SaleTranslatableContent' } | { __typename: 'VoucherTranslatableContent' } | { __typename: 'MenuItemTranslatableContent' } | null }; +export type MenuItemTranslationDetailsQueryVariables = Exact<{ + id: Scalars['ID']; + language: LanguageCodeEnum; +}>; + + +export type MenuItemTranslationDetailsQuery = { __typename: 'Query', translation: { __typename: 'ProductTranslatableContent' } | { __typename: 'CollectionTranslatableContent' } | { __typename: 'CategoryTranslatableContent' } | { __typename: 'AttributeTranslatableContent' } | { __typename: 'AttributeValueTranslatableContent' } | { __typename: 'ProductVariantTranslatableContent' } | { __typename: 'PageTranslatableContent' } | { __typename: 'ShippingMethodTranslatableContent' } | { __typename: 'SaleTranslatableContent' } | { __typename: 'VoucherTranslatableContent' } | { __typename: 'MenuItemTranslatableContent', translation: { __typename: 'MenuItemTranslation', id: string, name: string, language: { __typename: 'LanguageDisplay', language: string } } | null, menuItem: { __typename: 'MenuItem', id: string, name: string } | null } | null }; + export type UpdateMetadataMutationVariables = Exact<{ id: Scalars['ID']; input: Array | MetadataInput; diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index b7ac5d1eb..ed27ca32e 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -253558,6 +253558,18 @@ exports[`Storyshots Views / Translations / Entity list default 1`] = ` Shipping methods + diff --git a/src/storybook/stories/translations/TranslationsEntitiesListPage.tsx b/src/storybook/stories/translations/TranslationsEntitiesListPage.tsx index a23f0ffb3..27ced42b5 100644 --- a/src/storybook/stories/translations/TranslationsEntitiesListPage.tsx +++ b/src/storybook/stories/translations/TranslationsEntitiesListPage.tsx @@ -24,6 +24,7 @@ const props: TranslationsEntitiesListPageProps = { onSalesTabClick: () => undefined, onShippingMethodsTabClick: () => undefined, onVouchersTabClick: () => undefined, + onMenuItemsTabClick: () => undefined, }, language: { __typename: "LanguageDisplay", diff --git a/src/translations/components/TranslationsEntitiesListPage/TranslationsEntitiesListPage.tsx b/src/translations/components/TranslationsEntitiesListPage/TranslationsEntitiesListPage.tsx index 9bb3d67a8..2d918f6d3 100644 --- a/src/translations/components/TranslationsEntitiesListPage/TranslationsEntitiesListPage.tsx +++ b/src/translations/components/TranslationsEntitiesListPage/TranslationsEntitiesListPage.tsx @@ -26,6 +26,7 @@ export interface TranslationsEntitiesFilters { onPagesTabClick: () => void; onAttributesTabClick: () => void; onShippingMethodsTabClick: () => void; + onMenuItemsTabClick: () => void; } export type TranslationsEntitiesListFilterTab = keyof typeof TranslatableEntities; @@ -39,6 +40,7 @@ const tabs: TranslationsEntitiesListFilterTab[] = [ "pages", "attributes", "shippingMethods", + "menuItems", ]; const TranslationsEntitiesListPage: React.FC = props => { @@ -126,6 +128,13 @@ const TranslationsEntitiesListPage: React.FC })} onClick={filters.onShippingMethodsTabClick} /> + {children} diff --git a/src/translations/components/TranslationsMenuItemPage/TranslationsMenuItemPage.tsx b/src/translations/components/TranslationsMenuItemPage/TranslationsMenuItemPage.tsx new file mode 100644 index 000000000..e45395045 --- /dev/null +++ b/src/translations/components/TranslationsMenuItemPage/TranslationsMenuItemPage.tsx @@ -0,0 +1,104 @@ +import { Backlink } from "@saleor/components/Backlink"; +import Container from "@saleor/components/Container"; +import LanguageSwitch from "@saleor/components/LanguageSwitch"; +import PageHeader from "@saleor/components/PageHeader"; +import { LanguageCodeEnum, MenuItemTranslationFragment } from "@saleor/graphql"; +import { commonMessages, sectionNames } from "@saleor/intl"; +import { getStringOrPlaceholder } from "@saleor/misc"; +import { + TranslationInputFieldName, + TranslationsEntitiesPageProps, +} from "@saleor/translations/types"; +import { + languageEntitiesUrl, + languageEntityUrl, + TranslatableEntities, +} from "@saleor/translations/urls"; +import React from "react"; +import { useIntl } from "react-intl"; + +import TranslationFields from "../TranslationFields"; + +export interface TranslationsMenuItemPageProps + extends TranslationsEntitiesPageProps { + data: MenuItemTranslationFragment; +} + +const TranslationsMenuItemPage: React.FC = ({ + translationId, + activeField, + disabled, + languageCode, + languages, + data, + saveButtonState, + onDiscard, + onEdit, + onSubmit, +}) => { + const intl = useIntl(); + + return ( + + + {intl.formatMessage(sectionNames.translations)} + + + + languageEntityUrl( + lang, + TranslatableEntities.menuItems, + translationId, + ) + } + /> + + + + ); +}; +TranslationsMenuItemPage.displayName = "TranslationsMenuItemPage"; +export default TranslationsMenuItemPage; diff --git a/src/translations/components/TranslationsMenuItemPage/index.ts b/src/translations/components/TranslationsMenuItemPage/index.ts new file mode 100644 index 000000000..e3bebef41 --- /dev/null +++ b/src/translations/components/TranslationsMenuItemPage/index.ts @@ -0,0 +1,2 @@ +export { default } from "./TranslationsMenuItemPage"; +export * from "./TranslationsMenuItemPage"; diff --git a/src/translations/index.tsx b/src/translations/index.tsx index a839b23d2..26a813e27 100644 --- a/src/translations/index.tsx +++ b/src/translations/index.tsx @@ -23,6 +23,7 @@ import TranslationsCollectionsComponent, { } from "./views/TranslationsCollections"; import TranslationsEntitiesComponent from "./views/TranslationsEntities"; import TranslationsLanguageList from "./views/TranslationsLanguageList"; +import TranslationsMenuItemComponent from "./views/TranslationsMenuItem"; import TranslationsPagesComponent, { TranslationsPagesQueryParams, } from "./views/TranslationsPages"; @@ -212,6 +213,22 @@ const TranslationsShippingMethod: React.FC = ({ /> ); }; +const TranslationsMenuItem: React.FC = ({ + location, + match, +}) => { + const qs = parseQs(location.search.substr(1)); + const params: TranslationsShippingMethodQueryParams = { + activeField: qs.activeField, + }; + return ( + + ); +}; const TranslationsRouter: React.FC = () => { const intl = useIntl(); @@ -313,6 +330,15 @@ const TranslationsRouter: React.FC = () => { )} component={TranslationsShippingMethod} /> + ); diff --git a/src/translations/mutations.ts b/src/translations/mutations.ts index 42131cee3..81153ee07 100644 --- a/src/translations/mutations.ts +++ b/src/translations/mutations.ts @@ -261,3 +261,29 @@ export const updateShippingMethodTranslations = gql` } } `; + +export const updateMethodItemTranslations = gql` + mutation UpdateMenuItemTranslations( + $id: ID! + $input: NameTranslationInput! + $language: LanguageCodeEnum! + ) { + menuItemTranslate(id: $id, input: $input, languageCode: $language) { + errors { + field + message + } + menuItem { + id + name + translation(languageCode: $language) { + id + language { + language + } + name + } + } + } + } +`; diff --git a/src/translations/queries.ts b/src/translations/queries.ts index 0e6774bd1..671577617 100644 --- a/src/translations/queries.ts +++ b/src/translations/queries.ts @@ -216,6 +216,33 @@ export const shippingMethodTranslations = gql` } `; +export const menuItemTranslations = gql` + query MenuItemTranslations( + $language: LanguageCodeEnum! + $first: Int + $after: String + $last: Int + $before: String + ) { + translations( + kind: MENU_ITEM + before: $before + after: $after + first: $first + last: $last + ) { + edges { + node { + ...MenuItemTranslation + } + } + pageInfo { + ...PageInfo + } + } + } +`; + export const productTranslationDetails = gql` query ProductTranslationDetails($id: ID!, $language: LanguageCodeEnum!) { translation(kind: PRODUCT, id: $id) { @@ -313,3 +340,11 @@ export const shippingMethodTranslationDetails = gql` } } `; + +export const menuItemTranslationDetails = gql` + query MenuItemTranslationDetails($id: ID!, $language: LanguageCodeEnum!) { + translation(kind: MENU_ITEM, id: $id) { + ...MenuItemTranslation + } + } +`; diff --git a/src/translations/urls.ts b/src/translations/urls.ts index f7353fe1d..e16ad88cb 100644 --- a/src/translations/urls.ts +++ b/src/translations/urls.ts @@ -14,6 +14,7 @@ export enum TranslatableEntities { pages = "pages", attributes = "attributes", shippingMethods = "shippingMethods", + menuItems = "menuItems", } const translationsSection = "/translations/"; diff --git a/src/translations/views/EntityLists/TranslationsMenuItemList.tsx b/src/translations/views/EntityLists/TranslationsMenuItemList.tsx new file mode 100644 index 000000000..60a2d034e --- /dev/null +++ b/src/translations/views/EntityLists/TranslationsMenuItemList.tsx @@ -0,0 +1,56 @@ +import { useMenuItemTranslationsQuery } from "@saleor/graphql"; +import usePaginator, { PaginatorContext } from "@saleor/hooks/usePaginator"; +import TranslationsEntitiesList from "@saleor/translations/components/TranslationsEntitiesList"; +import { + languageEntityUrl, + TranslatableEntities, +} from "@saleor/translations/urls"; +import { mapEdgesToItems } from "@saleor/utils/maps"; +import React from "react"; + +import { TranslationsEntityListProps } from "./types"; +import { sumCompleted } from "./utils"; + +const TranslationsMenuItemList: React.FC = ({ + params, + variables, +}) => { + const { data, loading } = useMenuItemTranslationsQuery({ + displayLoader: true, + variables, + }); + + const paginationValues = usePaginator({ + pageInfo: data?.translations?.pageInfo, + paginationState: variables, + queryString: params, + }); + + return ( + + + node.__typename === "MenuItemTranslatableContent" && { + completion: { + current: sumCompleted([node.translation?.name]), + max: 1, + }, + id: node?.menuItem.id, + name: node?.menuItem.name, + }, + )} + getRowHref={id => + languageEntityUrl( + variables.language, + TranslatableEntities.menuItems, + id, + ) + } + /> + + ); +}; + +export default TranslationsMenuItemList; diff --git a/src/translations/views/TranslationsEntities.tsx b/src/translations/views/TranslationsEntities.tsx index d6e5aff7e..c68ad1356 100644 --- a/src/translations/views/TranslationsEntities.tsx +++ b/src/translations/views/TranslationsEntities.tsx @@ -11,6 +11,7 @@ import { LanguageEntitiesUrlQueryParams, TranslatableEntities } from "../urls"; import TranslationsAttributeList from "./EntityLists/TranslationsAttributeList"; import TranslationsCategoryList from "./EntityLists/TranslationsCategoryList"; import TranslationsCollectionList from "./EntityLists/TranslationsCollectionList"; +import TranslationsMenuItemList from "./EntityLists/TranslationsMenuItemList"; import TranslationsPageList from "./EntityLists/TranslationsPageList"; import TranslationsProductList from "./EntityLists/TranslationsProductList"; import TranslationsSaleList from "./EntityLists/TranslationsSaleList"; @@ -96,6 +97,13 @@ const TranslationsEntities: React.FC = ({ tab: TranslatableEntities.vouchers, }), ), + onMenuItemsTabClick: () => + navigate( + "?" + + stringifyQs({ + tab: TranslatableEntities.menuItems, + }), + ), }; const lang = maybe(() => shop.languages.find(languageFromList => languageFromList.code === language), @@ -138,6 +146,8 @@ const TranslationsEntities: React.FC = ({ params={params} variables={queryVariables} /> + ) : params.tab === "menuItems" ? ( + ) : null} ); diff --git a/src/translations/views/TranslationsMenuItem.tsx b/src/translations/views/TranslationsMenuItem.tsx new file mode 100644 index 000000000..2d7105e2b --- /dev/null +++ b/src/translations/views/TranslationsMenuItem.tsx @@ -0,0 +1,107 @@ +import { + LanguageCodeEnum, + useMenuItemTranslationDetailsQuery, + useUpdateMenuItemTranslationsMutation, +} from "@saleor/graphql"; +import useNavigator from "@saleor/hooks/useNavigator"; +import useNotifier from "@saleor/hooks/useNotifier"; +import useShop from "@saleor/hooks/useShop"; +import { commonMessages } from "@saleor/intl"; +import { extractMutationErrors } from "@saleor/misc"; +import { stringifyQs } from "@saleor/utils/urls"; +import React from "react"; +import { useIntl } from "react-intl"; + +import TranslationsMenuItemPage from "../components/TranslationsMenuItemPage"; +import { TranslationField, TranslationInputFieldName } from "../types"; +import { getParsedTranslationInputData } from "../utils"; + +export interface TranslationsMenuItemQueryParams { + activeField: string; +} +export interface TranslationsMenuItemProps { + id: string; + languageCode: LanguageCodeEnum; + params: TranslationsMenuItemQueryParams; +} + +const TranslationsMenuItem: React.FC = ({ + id, + languageCode, + params, +}) => { + const navigate = useNavigator(); + const notify = useNotifier(); + const shop = useShop(); + const intl = useIntl(); + + const menuItemTranslations = useMenuItemTranslationDetailsQuery({ + variables: { id, language: languageCode }, + }); + + const [ + updateTranslations, + updateTranslationsOpts, + ] = useUpdateMenuItemTranslationsMutation({ + onCompleted: data => { + if (data.menuItemTranslate.errors.length === 0) { + menuItemTranslations.refetch(); + notify({ + status: "success", + text: intl.formatMessage(commonMessages.savedChanges), + }); + navigate("?", { replace: true }); + } + }, + }); + + const onEdit = (field: string) => + navigate( + "?" + + stringifyQs({ + activeField: field, + }), + { replace: true }, + ); + + const onDiscard = () => { + navigate("?", { replace: true }); + }; + + const handleSubmit = ( + { name: fieldName }: TranslationField, + data: string, + ) => + extractMutationErrors( + updateTranslations({ + variables: { + id, + input: getParsedTranslationInputData({ fieldName, data }), + language: languageCode, + }, + }), + ); + + const translation = menuItemTranslations?.data?.translation; + + return ( + + ); +}; +TranslationsMenuItem.displayName = "TranslationsMenuItem"; +export default TranslationsMenuItem;