Add text attribute for product and page translations (#1276)

* initial commit

* Refactor translation components

* Wire up products

* Update types

* add logic for withChoices flag

* refactoring

* fixing errors

* change in intl.ts

* amended Changelog

* fix formatting

* fixing stuff

* Update translations

* remove unused declaration

* add changes

* Fixes

* Add newline

* Update displayName

* Update snapshots

Co-authored-by: Jakub Majorek <majorek.jakub@gmail.com>
This commit is contained in:
Kamil Pastuszka 2021-08-09 16:59:12 +02:00 committed by GitHub
parent 8d2021675e
commit 034eea0dcd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 781 additions and 298 deletions

View file

@ -73,6 +73,7 @@ All notable, unreleased changes to this project will be documented in this file.
- Add Metadata for Sale & Voucher - #7653 by @piotrgrundas - Add Metadata for Sale & Voucher - #7653 by @piotrgrundas
- Add variant create options dialog - #1238 by @orzechdev - Add variant create options dialog - #1238 by @orzechdev
- Fix for errors on changing channel availability - #1264 by @krzysztofwolski - Fix for errors on changing channel availability - #1264 by @krzysztofwolski
- Add text attribute for product and page translations - #1276 by @kamilpastuszka
# 2.11.1 # 2.11.1

View file

@ -6668,6 +6668,9 @@
"context": "independent of any particular day, eg. 11:35", "context": "independent of any particular day, eg. 11:35",
"string": "Time" "string": "Time"
}, },
"src_dot_translationAttributes": {
"string": "Attributes"
},
"src_dot_translations": { "src_dot_translations": {
"context": "translations section name", "context": "translations section name",
"string": "Translations" "string": "Translations"
@ -6688,6 +6691,21 @@
"src_dot_translations_dot_components_dot_TranslationFields_dot_3793796047": { "src_dot_translations_dot_components_dot_TranslationFields_dot_3793796047": {
"string": "No translation yet" "string": "No translation yet"
}, },
"src_dot_translations_dot_components_dot_TranslationsAttributesPage_dot_1567737068": {
"context": "attribute values",
"string": "Value {number}"
},
"src_dot_translations_dot_components_dot_TranslationsAttributesPage_dot_2642976392": {
"string": "Attribute Name"
},
"src_dot_translations_dot_components_dot_TranslationsAttributesPage_dot_884093025": {
"context": "header",
"string": "Translation Attribute \"{attribute}\" - {languageCode}"
},
"src_dot_translations_dot_components_dot_TranslationsAttributesPage_dot_values": {
"context": "section name",
"string": "Values"
},
"src_dot_translations_dot_components_dot_TranslationsCategoriesPage_dot_1214235329": { "src_dot_translations_dot_components_dot_TranslationsCategoriesPage_dot_1214235329": {
"string": "Category Name" "string": "Category Name"
}, },
@ -6790,28 +6808,13 @@
"src_dot_translations_dot_components_dot_TranslationsPagesPage_dot_3468022343": { "src_dot_translations_dot_components_dot_TranslationsPagesPage_dot_3468022343": {
"string": "Search Engine Preview" "string": "Search Engine Preview"
}, },
"src_dot_translations_dot_components_dot_TranslationsPagesPage_dot_4165966072": {
"context": "attribute list",
"string": "Attribute {number}"
},
"src_dot_translations_dot_components_dot_TranslationsPagesPage_dot_432157284": { "src_dot_translations_dot_components_dot_TranslationsPagesPage_dot_432157284": {
"string": "Page Title" "string": "Page Title"
}, },
"src_dot_translations_dot_components_dot_TranslationsProductTypesPage_dot_1567737068": {
"context": "attribute values",
"string": "Value {number}"
},
"src_dot_translations_dot_components_dot_TranslationsProductTypesPage_dot_2642976392": {
"string": "Attribute Name"
},
"src_dot_translations_dot_components_dot_TranslationsProductTypesPage_dot_3495336243": {
"context": "attribute richtext value",
"string": "Text"
},
"src_dot_translations_dot_components_dot_TranslationsProductTypesPage_dot_884093025": {
"context": "header",
"string": "Translation Attribute \"{attribute}\" - {languageCode}"
},
"src_dot_translations_dot_components_dot_TranslationsProductTypesPage_dot_values": {
"context": "section name",
"string": "Values"
},
"src_dot_translations_dot_components_dot_TranslationsProductsPage_dot_1406947243": { "src_dot_translations_dot_components_dot_TranslationsProductsPage_dot_1406947243": {
"string": "Search Engine Description" "string": "Search Engine Description"
}, },
@ -6831,6 +6834,10 @@
"src_dot_translations_dot_components_dot_TranslationsProductsPage_dot_3468022343": { "src_dot_translations_dot_components_dot_TranslationsProductsPage_dot_3468022343": {
"string": "Search Engine Preview" "string": "Search Engine Preview"
}, },
"src_dot_translations_dot_components_dot_TranslationsProductsPage_dot_4165966072": {
"context": "attribute list",
"string": "Attribute {number}"
},
"src_dot_translations_dot_components_dot_TranslationsSalesPage_dot_3731955064": { "src_dot_translations_dot_components_dot_TranslationsSalesPage_dot_3731955064": {
"context": "header", "context": "header",
"string": "Translation Sale \"{saleName}\" - {languageCode}" "string": "Translation Sale \"{saleName}\" - {languageCode}"

View file

@ -44,6 +44,7 @@ export const collectionTranslationFragment = gql`
} }
} }
`; `;
export const productTranslationFragment = gql` export const productTranslationFragment = gql`
fragment ProductTranslationFragment on ProductTranslatableContent { fragment ProductTranslationFragment on ProductTranslatableContent {
product { product {
@ -55,14 +56,31 @@ export const productTranslationFragment = gql`
} }
translation(languageCode: $language) { translation(languageCode: $language) {
id id
seoTitle
seoDescription
name
description description
language { language {
code code
language language
} }
}
attributeValues {
id
name name
seoDescription richText
seoTitle attributeValue {
id
}
translation(languageCode: $language) {
id
name
richText
language {
code
language
}
}
} }
} }
`; `;
@ -139,6 +157,23 @@ export const pageTranslationFragment = gql`
language language
} }
} }
attributeValues {
id
name
richText
attributeValue {
id
}
translation(languageCode: $language) {
id
name
richText
language {
code
language
}
}
}
} }
`; `;
export const pageTranslatableFragment = gql` export const pageTranslatableFragment = gql`
@ -148,7 +183,6 @@ export const pageTranslatableFragment = gql`
seoDescription seoDescription
seoTitle seoTitle
title title
translation(languageCode: $language) { translation(languageCode: $language) {
id id
content content
@ -188,6 +222,8 @@ export const attributeChoicesTranslationFragment = gql`
export const attributeTranslationFragment = gql` export const attributeTranslationFragment = gql`
fragment AttributeTranslationFragment on AttributeTranslatableContent { fragment AttributeTranslationFragment on AttributeTranslatableContent {
id
name
translation(languageCode: $language) { translation(languageCode: $language) {
id id
name name
@ -203,6 +239,30 @@ export const attributeTranslationFragment = gql`
export const attributeTranslationDetailsFragment = gql` export const attributeTranslationDetailsFragment = gql`
${attributeChoicesTranslationFragment} ${attributeChoicesTranslationFragment}
fragment AttributeTranslationDetailsFragment on AttributeTranslatableContent { fragment AttributeTranslationDetailsFragment on AttributeTranslatableContent {
translation(languageCode: $language) {
id
name
}
attribute {
id
name
inputType
withChoices
choices(
first: $firstValues
after: $afterValues
last: $lastValues
before: $beforeValues
) {
...AttributeChoicesTranslationFragment
}
}
}
`;
export const attributeValueTranslatableContentFragment = gql`
${attributeChoicesTranslationFragment}
fragment AttributeValueTranslatableContentFragment on AttributeTranslatableContent {
translation(languageCode: $language) { translation(languageCode: $language) {
id id
name name

View file

@ -56,6 +56,7 @@ export interface AttributeTranslationDetailsFragment_attribute {
id: string; id: string;
name: string | null; name: string | null;
inputType: AttributeInputTypeEnum | null; inputType: AttributeInputTypeEnum | null;
withChoices: boolean;
choices: AttributeTranslationDetailsFragment_attribute_choices | null; choices: AttributeTranslationDetailsFragment_attribute_choices | null;
} }

View file

@ -24,6 +24,8 @@ export interface AttributeTranslationFragment_attribute {
export interface AttributeTranslationFragment { export interface AttributeTranslationFragment {
__typename: "AttributeTranslatableContent"; __typename: "AttributeTranslatableContent";
id: string;
name: string;
translation: AttributeTranslationFragment_translation | null; translation: AttributeTranslationFragment_translation | null;
attribute: AttributeTranslationFragment_attribute | null; attribute: AttributeTranslationFragment_attribute | null;
} }

View file

@ -0,0 +1,66 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { AttributeInputTypeEnum } from "./../../types/globalTypes";
// ====================================================
// GraphQL fragment: AttributeValueTranslatableContentFragment
// ====================================================
export interface AttributeValueTranslatableContentFragment_translation {
__typename: "AttributeTranslation";
id: string;
name: string;
}
export interface AttributeValueTranslatableContentFragment_attribute_choices_pageInfo {
__typename: "PageInfo";
endCursor: string | null;
hasNextPage: boolean;
hasPreviousPage: boolean;
startCursor: string | null;
}
export interface AttributeValueTranslatableContentFragment_attribute_choices_edges_node_translation {
__typename: "AttributeValueTranslation";
id: string;
name: string;
richText: any | null;
}
export interface AttributeValueTranslatableContentFragment_attribute_choices_edges_node {
__typename: "AttributeValue";
id: string;
name: string | null;
richText: any | null;
inputType: AttributeInputTypeEnum | null;
translation: AttributeValueTranslatableContentFragment_attribute_choices_edges_node_translation | null;
}
export interface AttributeValueTranslatableContentFragment_attribute_choices_edges {
__typename: "AttributeValueCountableEdge";
cursor: string;
node: AttributeValueTranslatableContentFragment_attribute_choices_edges_node;
}
export interface AttributeValueTranslatableContentFragment_attribute_choices {
__typename: "AttributeValueCountableConnection";
pageInfo: AttributeValueTranslatableContentFragment_attribute_choices_pageInfo;
edges: AttributeValueTranslatableContentFragment_attribute_choices_edges[];
}
export interface AttributeValueTranslatableContentFragment_attribute {
__typename: "Attribute";
id: string;
name: string | null;
inputType: AttributeInputTypeEnum | null;
choices: AttributeValueTranslatableContentFragment_attribute_choices | null;
}
export interface AttributeValueTranslatableContentFragment {
__typename: "AttributeTranslatableContent";
translation: AttributeValueTranslatableContentFragment_translation | null;
attribute: AttributeValueTranslatableContentFragment_attribute | null;
}

View file

@ -34,8 +34,37 @@ export interface PageTranslationFragment_translation {
language: PageTranslationFragment_translation_language; language: PageTranslationFragment_translation_language;
} }
export interface PageTranslationFragment_attributeValues_attributeValue {
__typename: "AttributeValue";
id: string;
}
export interface PageTranslationFragment_attributeValues_translation_language {
__typename: "LanguageDisplay";
code: LanguageCodeEnum;
language: string;
}
export interface PageTranslationFragment_attributeValues_translation {
__typename: "AttributeValueTranslation";
id: string;
name: string;
richText: any | null;
language: PageTranslationFragment_attributeValues_translation_language;
}
export interface PageTranslationFragment_attributeValues {
__typename: "AttributeValueTranslatableContent";
id: string;
name: string;
richText: any | null;
attributeValue: PageTranslationFragment_attributeValues_attributeValue | null;
translation: PageTranslationFragment_attributeValues_translation | null;
}
export interface PageTranslationFragment { export interface PageTranslationFragment {
__typename: "PageTranslatableContent"; __typename: "PageTranslatableContent";
page: PageTranslationFragment_page | null; page: PageTranslationFragment_page | null;
translation: PageTranslationFragment_translation | null; translation: PageTranslationFragment_translation | null;
attributeValues: PageTranslationFragment_attributeValues[];
} }

View file

@ -27,15 +27,44 @@ export interface ProductTranslationFragment_translation_language {
export interface ProductTranslationFragment_translation { export interface ProductTranslationFragment_translation {
__typename: "ProductTranslation"; __typename: "ProductTranslation";
id: string; id: string;
seoTitle: string | null;
seoDescription: string | null;
name: string | null;
description: any | null; description: any | null;
language: ProductTranslationFragment_translation_language; language: ProductTranslationFragment_translation_language;
name: string | null; }
seoDescription: string | null;
seoTitle: string | null; export interface ProductTranslationFragment_attributeValues_attributeValue {
__typename: "AttributeValue";
id: string;
}
export interface ProductTranslationFragment_attributeValues_translation_language {
__typename: "LanguageDisplay";
code: LanguageCodeEnum;
language: string;
}
export interface ProductTranslationFragment_attributeValues_translation {
__typename: "AttributeValueTranslation";
id: string;
name: string;
richText: any | null;
language: ProductTranslationFragment_attributeValues_translation_language;
}
export interface ProductTranslationFragment_attributeValues {
__typename: "AttributeValueTranslatableContent";
id: string;
name: string;
richText: any | null;
attributeValue: ProductTranslationFragment_attributeValues_attributeValue | null;
translation: ProductTranslationFragment_attributeValues_translation | null;
} }
export interface ProductTranslationFragment { export interface ProductTranslationFragment {
__typename: "ProductTranslatableContent"; __typename: "ProductTranslatableContent";
product: ProductTranslationFragment_product | null; product: ProductTranslationFragment_product | null;
translation: ProductTranslationFragment_translation | null; translation: ProductTranslationFragment_translation | null;
attributeValues: ProductTranslationFragment_attributeValues[];
} }

View file

@ -99,6 +99,9 @@ export const commonMessages = defineMessages({
summary: { summary: {
defaultMessage: "Summary" defaultMessage: "Summary"
}, },
translationAttributes: {
defaultMessage: "Attributes"
},
unauthorizedDashboardAccess: { unauthorizedDashboardAccess: {
defaultMessage: "Only staff users can access the dashboard" defaultMessage: "Only staff users can access the dashboard"
}, },

View file

@ -18,7 +18,7 @@ const props: TranslationsEntitiesListPageProps = {
onCategoriesTabClick: () => undefined, onCategoriesTabClick: () => undefined,
onCollectionsTabClick: () => undefined, onCollectionsTabClick: () => undefined,
onPagesTabClick: () => undefined, onPagesTabClick: () => undefined,
onProductTypesTabClick: () => undefined, onAttributesTabClick: () => undefined,
onProductsTabClick: () => undefined, onProductsTabClick: () => undefined,
onSalesTabClick: () => undefined, onSalesTabClick: () => undefined,
onShippingMethodsTabClick: () => undefined, onShippingMethodsTabClick: () => undefined,

View file

@ -15,6 +15,7 @@ import Skeleton from "@saleor/components/Skeleton";
import TablePagination from "@saleor/components/TablePagination"; import TablePagination from "@saleor/components/TablePagination";
import { buttonMessages } from "@saleor/intl"; import { buttonMessages } from "@saleor/intl";
import { makeStyles } from "@saleor/macaw-ui"; import { makeStyles } from "@saleor/macaw-ui";
import { TranslationField } from "@saleor/translations/types";
import { ListProps } from "@saleor/types"; import { ListProps } from "@saleor/types";
import classNames from "classnames"; import classNames from "classnames";
import React from "react"; import React from "react";
@ -24,14 +25,6 @@ import TranslationFieldsLong from "./TranslationFieldsLong";
import TranslationFieldsRich from "./TranslationFieldsRich"; import TranslationFieldsRich from "./TranslationFieldsRich";
import TranslationFieldsShort from "./TranslationFieldsShort"; import TranslationFieldsShort from "./TranslationFieldsShort";
export interface TranslationField {
displayName: string;
name: string;
translation: string;
type: "short" | "long" | "rich";
value: string;
}
type Pagination = Pick< type Pagination = Pick<
ListProps, ListProps,
Exclude<keyof ListProps, "onRowClick" | "disabled"> Exclude<keyof ListProps, "onRowClick" | "disabled">
@ -47,7 +40,7 @@ export interface TranslationFieldsProps {
pagination?: Pagination; pagination?: Pagination;
onEdit: (field: string) => void; onEdit: (field: string) => void;
onDiscard: () => void; onDiscard: () => void;
onSubmit: (field: string, data: string | OutputData) => void; onSubmit: (field: TranslationField, data: string | OutputData) => void;
} }
const useStyles = makeStyles( const useStyles = makeStyles(
@ -215,7 +208,7 @@ const TranslationFields: React.FC<TranslationFieldsProps> = props => {
initial={field.translation} initial={field.translation}
saveButtonState={saveButtonState} saveButtonState={saveButtonState}
onDiscard={onDiscard} onDiscard={onDiscard}
onSubmit={data => onSubmit(field.name, data)} onSubmit={data => onSubmit(field, data)}
/> />
) : field.type === "long" ? ( ) : field.type === "long" ? (
<TranslationFieldsLong <TranslationFieldsLong
@ -224,7 +217,7 @@ const TranslationFields: React.FC<TranslationFieldsProps> = props => {
initial={field.translation} initial={field.translation}
saveButtonState={saveButtonState} saveButtonState={saveButtonState}
onDiscard={onDiscard} onDiscard={onDiscard}
onSubmit={data => onSubmit(field.name, data)} onSubmit={data => onSubmit(field, data)}
/> />
) : ( ) : (
<TranslationFieldsRich <TranslationFieldsRich
@ -233,7 +226,7 @@ const TranslationFields: React.FC<TranslationFieldsProps> = props => {
initial={field.translation} initial={field.translation}
saveButtonState={saveButtonState} saveButtonState={saveButtonState}
onDiscard={onDiscard} onDiscard={onDiscard}
onSubmit={data => onSubmit(field.name, data)} onSubmit={data => onSubmit(field, data)}
/> />
) )
) : ( ) : (

View file

@ -7,16 +7,16 @@ import { AttributeTranslationDetailsFragment } from "@saleor/fragments/types/Att
import { commonMessages, sectionNames } from "@saleor/intl"; import { commonMessages, sectionNames } from "@saleor/intl";
import { Backlink } from "@saleor/macaw-ui"; import { Backlink } from "@saleor/macaw-ui";
import { getStringOrPlaceholder } from "@saleor/misc"; import { getStringOrPlaceholder } from "@saleor/misc";
import { TranslationsEntitiesPageProps } from "@saleor/translations/types"; import {
TranslationField,
TranslationsEntitiesPageProps
} from "@saleor/translations/types";
import { ListSettings } from "@saleor/types"; import { ListSettings } from "@saleor/types";
import React from "react"; import React from "react";
import { defineMessages, useIntl } from "react-intl"; import { defineMessages, useIntl } from "react-intl";
import { import { LanguageCodeEnum } from "../../../types/globalTypes";
AttributeInputTypeEnum, import TranslationFields from "../TranslationFields";
LanguageCodeEnum
} from "../../../types/globalTypes";
import TranslationFields, { TranslationField } from "../TranslationFields";
export const messages = defineMessages({ export const messages = defineMessages({
values: { values: {
@ -25,7 +25,7 @@ export const messages = defineMessages({
} }
}); });
export interface TranslationsProductTypesPageProps export interface TranslationsAttributesPageProps
extends TranslationsEntitiesPageProps { extends TranslationsEntitiesPageProps {
data: AttributeTranslationDetailsFragment; data: AttributeTranslationDetailsFragment;
settings?: ListSettings; settings?: ListSettings;
@ -44,7 +44,7 @@ export const fieldNames = {
richTextValue: "attributeRichTextValue" richTextValue: "attributeRichTextValue"
}; };
const TranslationsProductTypesPage: React.FC<TranslationsProductTypesPageProps> = ({ const TranslationsAttributesPage: React.FC<TranslationsAttributesPageProps> = ({
activeField, activeField,
disabled, disabled,
languages, languages,
@ -64,6 +64,8 @@ const TranslationsProductTypesPage: React.FC<TranslationsProductTypesPageProps>
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const withChoices = data?.attribute?.withChoices;
return ( return (
<Container> <Container>
<Backlink onClick={onBack}> <Backlink onClick={onBack}>
@ -110,64 +112,50 @@ const TranslationsProductTypesPage: React.FC<TranslationsProductTypesPageProps>
onSubmit={onSubmit} onSubmit={onSubmit}
/> />
<CardSpacer /> <CardSpacer />
<TranslationFields {data?.attribute?.choices.edges.length > 0 && withChoices && (
activeField={activeField} <TranslationFields
disabled={disabled} activeField={activeField}
initialState={true} disabled={disabled}
title={intl.formatMessage(messages.values)} initialState={true}
fields={ title={intl.formatMessage(messages.values)}
data?.attribute?.choices?.edges?.map( fields={
({ node: attributeValue }, attributeValueIndex) => { data?.attribute?.choices?.edges?.map(
const isRichText = ({ node: attributeValue }, attributeValueIndex) => {
attributeValue?.inputType === AttributeInputTypeEnum.RICH_TEXT; const displayName = intl.formatMessage(
const displayName = isRichText {
? intl.formatMessage({ defaultMessage: "Value {number}",
defaultMessage: "Text", description: "attribute values"
description: "attribute richtext value" },
}) {
: intl.formatMessage( number: attributeValueIndex + 1
{ }
defaultMessage: "Value {number}", );
description: "attribute values"
},
{
number: attributeValueIndex + 1
}
);
return { return {
displayName, displayName,
name: `${ name: `${fieldNames.value}:${attributeValue.id}`,
isRichText ? fieldNames.richTextValue : fieldNames.value translation: attributeValue?.translation?.name || null,
}:${attributeValue.id}`, type: "short" as TranslationField["type"],
translation: value: attributeValue?.name
(isRichText };
? attributeValue?.translation?.richText }
: attributeValue?.translation?.name) || null, ) || []
type: (isRichText }
? "rich" saveButtonState={saveButtonState}
: "short") as TranslationField["type"], pagination={{
value: isRichText settings,
? attributeValue?.richText onUpdateListSettings,
: attributeValue?.name pageInfo,
}; onNextPage,
} onPreviousPage
) || [] }}
} onEdit={onEdit}
saveButtonState={saveButtonState} onDiscard={onDiscard}
pagination={{ onSubmit={onSubmit}
settings, />
onUpdateListSettings, )}
pageInfo,
onNextPage,
onPreviousPage
}}
onEdit={onEdit}
onDiscard={onDiscard}
onSubmit={onSubmit}
/>
</Container> </Container>
); );
}; };
TranslationsProductTypesPage.displayName = "TranslationsProductTypesPage"; TranslationsAttributesPage.displayName = "TranslationsAttributesPage";
export default TranslationsProductTypesPage; export default TranslationsAttributesPage;

View file

@ -0,0 +1,2 @@
export { default } from "./TranslationsAttributesPage";
export * from "./TranslationsAttributesPage";

View file

@ -25,7 +25,7 @@ export interface TranslationsEntitiesFilters {
onSalesTabClick: () => void; onSalesTabClick: () => void;
onVouchersTabClick: () => void; onVouchersTabClick: () => void;
onPagesTabClick: () => void; onPagesTabClick: () => void;
onProductTypesTabClick: () => void; onAttributesTabClick: () => void;
onShippingMethodsTabClick: () => void; onShippingMethodsTabClick: () => void;
} }
@ -38,7 +38,7 @@ const tabs: TranslationsEntitiesListFilterTab[] = [
"sales", "sales",
"vouchers", "vouchers",
"pages", "pages",
"productTypes", "attributes",
"shippingMethods" "shippingMethods"
]; ];
@ -109,7 +109,7 @@ const TranslationsEntitiesListPage: React.FC<TranslationsEntitiesListPageProps>
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Attributes" defaultMessage: "Attributes"
})} })}
onClick={filters.onProductTypesTabClick} onClick={filters.onAttributesTabClick}
/> />
<FilterTab <FilterTab
label={intl.formatMessage({ label={intl.formatMessage({

View file

@ -19,6 +19,7 @@ import TranslationFields from "../TranslationFields";
export interface TranslationsPagesPageProps export interface TranslationsPagesPageProps
extends TranslationsEntitiesPageProps { extends TranslationsEntitiesPageProps {
data: PageTranslationFragment; data: PageTranslationFragment;
onAttributeValueSubmit: TranslationsEntitiesPageProps["onSubmit"];
} }
const TranslationsPagesPage: React.FC<TranslationsPagesPageProps> = ({ const TranslationsPagesPage: React.FC<TranslationsPagesPageProps> = ({
@ -32,7 +33,8 @@ const TranslationsPagesPage: React.FC<TranslationsPagesPageProps> = ({
onDiscard, onDiscard,
onEdit, onEdit,
onLanguageChange, onLanguageChange,
onSubmit onSubmit,
onAttributeValueSubmit
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
@ -90,6 +92,7 @@ const TranslationsPagesPage: React.FC<TranslationsPagesPageProps> = ({
onDiscard={onDiscard} onDiscard={onDiscard}
onSubmit={onSubmit} onSubmit={onSubmit}
/> />
<CardSpacer /> <CardSpacer />
<TranslationFields <TranslationFields
activeField={activeField} activeField={activeField}
@ -123,6 +126,40 @@ const TranslationsPagesPage: React.FC<TranslationsPagesPageProps> = ({
onDiscard={onDiscard} onDiscard={onDiscard}
onSubmit={onSubmit} onSubmit={onSubmit}
/> />
<CardSpacer />
{data?.attributeValues?.length > 0 && (
<>
<TranslationFields
activeField={activeField}
disabled={disabled}
initialState={true}
title={intl.formatMessage(commonMessages.translationAttributes)}
fields={
data.attributeValues.map((attrVal, i) => ({
id: attrVal.attributeValue.id,
displayName: intl.formatMessage(
{
defaultMessage: "Attribute {number}",
description: "attribute list"
},
{
number: i + 1
}
),
name: attrVal?.name,
translation: attrVal?.translation?.richText || null,
type: "rich" as "rich",
value: attrVal?.richText
})) || []
}
saveButtonState={saveButtonState}
onEdit={onEdit}
onDiscard={onDiscard}
onSubmit={onAttributeValueSubmit}
/>
<CardSpacer />
</>
)}
</Container> </Container>
); );
}; };

View file

@ -1,2 +0,0 @@
export { default } from "./TranslationsProductTypesPage";
export * from "./TranslationsProductTypesPage";

View file

@ -19,6 +19,7 @@ import TranslationFields from "../TranslationFields";
export interface TranslationsProductsPageProps export interface TranslationsProductsPageProps
extends TranslationsEntitiesPageProps { extends TranslationsEntitiesPageProps {
data: ProductTranslationFragment; data: ProductTranslationFragment;
onAttributeValueSubmit: TranslationsEntitiesPageProps["onSubmit"];
} }
const TranslationsProductsPage: React.FC<TranslationsProductsPageProps> = ({ const TranslationsProductsPage: React.FC<TranslationsProductsPageProps> = ({
@ -32,7 +33,8 @@ const TranslationsProductsPage: React.FC<TranslationsProductsPageProps> = ({
onDiscard, onDiscard,
onEdit, onEdit,
onLanguageChange, onLanguageChange,
onSubmit onSubmit,
onAttributeValueSubmit
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
@ -123,6 +125,40 @@ const TranslationsProductsPage: React.FC<TranslationsProductsPageProps> = ({
onDiscard={onDiscard} onDiscard={onDiscard}
onSubmit={onSubmit} onSubmit={onSubmit}
/> />
<CardSpacer />
{data?.attributeValues?.length > 0 && (
<>
<TranslationFields
activeField={activeField}
disabled={disabled}
initialState={true}
title={intl.formatMessage(commonMessages.translationAttributes)}
fields={
data.attributeValues.map((attrVal, i) => ({
id: attrVal.attributeValue.id,
displayName: intl.formatMessage(
{
defaultMessage: "Attribute {number}",
description: "attribute list"
},
{
number: i + 1
}
),
name: attrVal?.name,
translation: attrVal?.translation?.richText || null,
type: "rich" as "rich",
value: attrVal?.richText
})) || []
}
saveButtonState={saveButtonState}
onEdit={onEdit}
onDiscard={onDiscard}
onSubmit={onAttributeValueSubmit}
/>
<CardSpacer />
</>
)}
</Container> </Container>
); );
}; };

View file

@ -12,6 +12,9 @@ import {
languageListPath, languageListPath,
TranslatableEntities TranslatableEntities
} from "./urls"; } from "./urls";
import TranslationsAttributesComponent, {
TranslationsAttributesQueryParams
} from "./views/TranslationsAttributes";
import TranslationsCategoriesComponent, { import TranslationsCategoriesComponent, {
TranslationsCategoriesQueryParams TranslationsCategoriesQueryParams
} from "./views/TranslationsCategories"; } from "./views/TranslationsCategories";
@ -26,9 +29,6 @@ import TranslationsPagesComponent, {
import TranslationsProductsComponent, { import TranslationsProductsComponent, {
TranslationsProductsQueryParams TranslationsProductsQueryParams
} from "./views/TranslationsProducts"; } from "./views/TranslationsProducts";
import TranslationsProductTypesComponent, {
TranslationsProductTypesQueryParams
} from "./views/TranslationsProductTypes";
import TranslationsSaleComponent, { import TranslationsSaleComponent, {
TranslationsSalesQueryParams TranslationsSalesQueryParams
} from "./views/TranslationsSales"; } from "./views/TranslationsSales";
@ -155,16 +155,16 @@ const TranslationsPages: React.FC<TranslationsEntityRouteProps> = ({
/> />
); );
}; };
const TranslationsProductTypes: React.FC<TranslationsEntityRouteProps> = ({ const TranslationsAttributes: React.FC<TranslationsEntityRouteProps> = ({
location, location,
match match
}) => { }) => {
const qs = parseQs(location.search.substr(1)); const qs = parseQs(location.search.substr(1));
const params: TranslationsProductTypesQueryParams = { const params: TranslationsAttributesQueryParams = {
activeField: qs.activeField activeField: qs.activeField
}; };
return ( return (
<TranslationsProductTypesComponent <TranslationsAttributesComponent
id={decodeURIComponent(match.params.id)} id={decodeURIComponent(match.params.id)}
languageCode={LanguageCodeEnum[match.params.languageCode]} languageCode={LanguageCodeEnum[match.params.languageCode]}
params={params} params={params}
@ -263,10 +263,10 @@ const TranslationsRouter: React.FC = () => {
exact exact
path={languageEntityPath( path={languageEntityPath(
":languageCode", ":languageCode",
TranslatableEntities.productTypes, TranslatableEntities.attributes,
":id" ":id"
)} )}
component={TranslationsProductTypes} component={TranslationsAttributes}
/> />
<Route <Route
exact exact

View file

@ -276,6 +276,7 @@ const updateAttributeValueTranslations = gql`
attributeValue { attributeValue {
id id
name name
richText
translation(languageCode: $language) { translation(languageCode: $language) {
id id
name name

View file

@ -2,6 +2,31 @@ import { OutputData } from "@editorjs/editorjs";
import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
import { ShopInfo_shop_languages } from "@saleor/components/Shop/types/ShopInfo"; import { ShopInfo_shop_languages } from "@saleor/components/Shop/types/ShopInfo";
export enum TranslationInputFieldName {
description = "description",
name = "name",
seoDescription = "seoDescription",
seoTitle = "seoTitle",
richText = "richText"
}
export enum PageTranslationInputFieldName {
content = "content",
title = "title",
seoDescription = "seoDescription",
seoTitle = "seoTitle",
richText = "richText"
}
export interface TranslationField<T extends string = string> {
id?: string;
displayName: string;
name: T;
translation: string;
type: "short" | "long" | "rich";
value: string;
}
export interface TranslationsEntitiesPageProps { export interface TranslationsEntitiesPageProps {
activeField: string; activeField: string;
disabled: boolean; disabled: boolean;
@ -12,19 +37,5 @@ export interface TranslationsEntitiesPageProps {
onEdit: (field: string) => void; onEdit: (field: string) => void;
onDiscard: () => void; onDiscard: () => void;
onLanguageChange: (lang: string) => void; onLanguageChange: (lang: string) => void;
onSubmit: (field: string, data: string | OutputData) => void; onSubmit: (field: TranslationField, data: string | OutputData) => void;
}
export enum TranslationInputFieldName {
description = "description",
name = "name",
seoDescription = "seoDescription",
seoTitle = "seoTitle"
}
export enum PageTranslationInputFieldName {
content = "content",
title = "title",
seoDescription = "seoDescription",
seoTitle = "seoTitle"
} }

View file

@ -60,6 +60,7 @@ export interface AttributeTranslationDetails_translation_AttributeTranslatableCo
id: string; id: string;
name: string | null; name: string | null;
inputType: AttributeInputTypeEnum | null; inputType: AttributeInputTypeEnum | null;
withChoices: boolean;
choices: AttributeTranslationDetails_translation_AttributeTranslatableContent_attribute_choices | null; choices: AttributeTranslationDetails_translation_AttributeTranslatableContent_attribute_choices | null;
} }

View file

@ -28,6 +28,8 @@ export interface AttributeTranslations_translations_edges_node_AttributeTranslat
export interface AttributeTranslations_translations_edges_node_AttributeTranslatableContent { export interface AttributeTranslations_translations_edges_node_AttributeTranslatableContent {
__typename: "AttributeTranslatableContent"; __typename: "AttributeTranslatableContent";
id: string;
name: string;
translation: AttributeTranslations_translations_edges_node_AttributeTranslatableContent_translation | null; translation: AttributeTranslations_translations_edges_node_AttributeTranslatableContent_translation | null;
attribute: AttributeTranslations_translations_edges_node_AttributeTranslatableContent_attribute | null; attribute: AttributeTranslations_translations_edges_node_AttributeTranslatableContent_attribute | null;
} }

View file

@ -38,10 +38,39 @@ export interface PageTranslationDetails_translation_PageTranslatableContent_tran
language: PageTranslationDetails_translation_PageTranslatableContent_translation_language; language: PageTranslationDetails_translation_PageTranslatableContent_translation_language;
} }
export interface PageTranslationDetails_translation_PageTranslatableContent_attributeValues_attributeValue {
__typename: "AttributeValue";
id: string;
}
export interface PageTranslationDetails_translation_PageTranslatableContent_attributeValues_translation_language {
__typename: "LanguageDisplay";
code: LanguageCodeEnum;
language: string;
}
export interface PageTranslationDetails_translation_PageTranslatableContent_attributeValues_translation {
__typename: "AttributeValueTranslation";
id: string;
name: string;
richText: any | null;
language: PageTranslationDetails_translation_PageTranslatableContent_attributeValues_translation_language;
}
export interface PageTranslationDetails_translation_PageTranslatableContent_attributeValues {
__typename: "AttributeValueTranslatableContent";
id: string;
name: string;
richText: any | null;
attributeValue: PageTranslationDetails_translation_PageTranslatableContent_attributeValues_attributeValue | null;
translation: PageTranslationDetails_translation_PageTranslatableContent_attributeValues_translation | null;
}
export interface PageTranslationDetails_translation_PageTranslatableContent { export interface PageTranslationDetails_translation_PageTranslatableContent {
__typename: "PageTranslatableContent"; __typename: "PageTranslatableContent";
page: PageTranslationDetails_translation_PageTranslatableContent_page | null; page: PageTranslationDetails_translation_PageTranslatableContent_page | null;
translation: PageTranslationDetails_translation_PageTranslatableContent_translation | null; translation: PageTranslationDetails_translation_PageTranslatableContent_translation | null;
attributeValues: PageTranslationDetails_translation_PageTranslatableContent_attributeValues[];
} }
export type PageTranslationDetails_translation = PageTranslationDetails_translation_ProductTranslatableContent | PageTranslationDetails_translation_PageTranslatableContent; export type PageTranslationDetails_translation = PageTranslationDetails_translation_ProductTranslatableContent | PageTranslationDetails_translation_PageTranslatableContent;

View file

@ -38,10 +38,39 @@ export interface PageTranslations_translations_edges_node_PageTranslatableConten
language: PageTranslations_translations_edges_node_PageTranslatableContent_translation_language; language: PageTranslations_translations_edges_node_PageTranslatableContent_translation_language;
} }
export interface PageTranslations_translations_edges_node_PageTranslatableContent_attributeValues_attributeValue {
__typename: "AttributeValue";
id: string;
}
export interface PageTranslations_translations_edges_node_PageTranslatableContent_attributeValues_translation_language {
__typename: "LanguageDisplay";
code: LanguageCodeEnum;
language: string;
}
export interface PageTranslations_translations_edges_node_PageTranslatableContent_attributeValues_translation {
__typename: "AttributeValueTranslation";
id: string;
name: string;
richText: any | null;
language: PageTranslations_translations_edges_node_PageTranslatableContent_attributeValues_translation_language;
}
export interface PageTranslations_translations_edges_node_PageTranslatableContent_attributeValues {
__typename: "AttributeValueTranslatableContent";
id: string;
name: string;
richText: any | null;
attributeValue: PageTranslations_translations_edges_node_PageTranslatableContent_attributeValues_attributeValue | null;
translation: PageTranslations_translations_edges_node_PageTranslatableContent_attributeValues_translation | null;
}
export interface PageTranslations_translations_edges_node_PageTranslatableContent { export interface PageTranslations_translations_edges_node_PageTranslatableContent {
__typename: "PageTranslatableContent"; __typename: "PageTranslatableContent";
page: PageTranslations_translations_edges_node_PageTranslatableContent_page | null; page: PageTranslations_translations_edges_node_PageTranslatableContent_page | null;
translation: PageTranslations_translations_edges_node_PageTranslatableContent_translation | null; translation: PageTranslations_translations_edges_node_PageTranslatableContent_translation | null;
attributeValues: PageTranslations_translations_edges_node_PageTranslatableContent_attributeValues[];
} }
export type PageTranslations_translations_edges_node = PageTranslations_translations_edges_node_ProductTranslatableContent | PageTranslations_translations_edges_node_PageTranslatableContent; export type PageTranslations_translations_edges_node = PageTranslations_translations_edges_node_ProductTranslatableContent | PageTranslations_translations_edges_node_PageTranslatableContent;

View file

@ -31,17 +31,46 @@ export interface ProductTranslationDetails_translation_ProductTranslatableConten
export interface ProductTranslationDetails_translation_ProductTranslatableContent_translation { export interface ProductTranslationDetails_translation_ProductTranslatableContent_translation {
__typename: "ProductTranslation"; __typename: "ProductTranslation";
id: string; id: string;
seoTitle: string | null;
seoDescription: string | null;
name: string | null;
description: any | null; description: any | null;
language: ProductTranslationDetails_translation_ProductTranslatableContent_translation_language; language: ProductTranslationDetails_translation_ProductTranslatableContent_translation_language;
name: string | null; }
seoDescription: string | null;
seoTitle: string | null; export interface ProductTranslationDetails_translation_ProductTranslatableContent_attributeValues_attributeValue {
__typename: "AttributeValue";
id: string;
}
export interface ProductTranslationDetails_translation_ProductTranslatableContent_attributeValues_translation_language {
__typename: "LanguageDisplay";
code: LanguageCodeEnum;
language: string;
}
export interface ProductTranslationDetails_translation_ProductTranslatableContent_attributeValues_translation {
__typename: "AttributeValueTranslation";
id: string;
name: string;
richText: any | null;
language: ProductTranslationDetails_translation_ProductTranslatableContent_attributeValues_translation_language;
}
export interface ProductTranslationDetails_translation_ProductTranslatableContent_attributeValues {
__typename: "AttributeValueTranslatableContent";
id: string;
name: string;
richText: any | null;
attributeValue: ProductTranslationDetails_translation_ProductTranslatableContent_attributeValues_attributeValue | null;
translation: ProductTranslationDetails_translation_ProductTranslatableContent_attributeValues_translation | null;
} }
export interface ProductTranslationDetails_translation_ProductTranslatableContent { export interface ProductTranslationDetails_translation_ProductTranslatableContent {
__typename: "ProductTranslatableContent"; __typename: "ProductTranslatableContent";
product: ProductTranslationDetails_translation_ProductTranslatableContent_product | null; product: ProductTranslationDetails_translation_ProductTranslatableContent_product | null;
translation: ProductTranslationDetails_translation_ProductTranslatableContent_translation | null; translation: ProductTranslationDetails_translation_ProductTranslatableContent_translation | null;
attributeValues: ProductTranslationDetails_translation_ProductTranslatableContent_attributeValues[];
} }
export type ProductTranslationDetails_translation = ProductTranslationDetails_translation_CollectionTranslatableContent | ProductTranslationDetails_translation_ProductTranslatableContent; export type ProductTranslationDetails_translation = ProductTranslationDetails_translation_CollectionTranslatableContent | ProductTranslationDetails_translation_ProductTranslatableContent;

View file

@ -31,17 +31,46 @@ export interface ProductTranslations_translations_edges_node_ProductTranslatable
export interface ProductTranslations_translations_edges_node_ProductTranslatableContent_translation { export interface ProductTranslations_translations_edges_node_ProductTranslatableContent_translation {
__typename: "ProductTranslation"; __typename: "ProductTranslation";
id: string; id: string;
seoTitle: string | null;
seoDescription: string | null;
name: string | null;
description: any | null; description: any | null;
language: ProductTranslations_translations_edges_node_ProductTranslatableContent_translation_language; language: ProductTranslations_translations_edges_node_ProductTranslatableContent_translation_language;
name: string | null; }
seoDescription: string | null;
seoTitle: string | null; export interface ProductTranslations_translations_edges_node_ProductTranslatableContent_attributeValues_attributeValue {
__typename: "AttributeValue";
id: string;
}
export interface ProductTranslations_translations_edges_node_ProductTranslatableContent_attributeValues_translation_language {
__typename: "LanguageDisplay";
code: LanguageCodeEnum;
language: string;
}
export interface ProductTranslations_translations_edges_node_ProductTranslatableContent_attributeValues_translation {
__typename: "AttributeValueTranslation";
id: string;
name: string;
richText: any | null;
language: ProductTranslations_translations_edges_node_ProductTranslatableContent_attributeValues_translation_language;
}
export interface ProductTranslations_translations_edges_node_ProductTranslatableContent_attributeValues {
__typename: "AttributeValueTranslatableContent";
id: string;
name: string;
richText: any | null;
attributeValue: ProductTranslations_translations_edges_node_ProductTranslatableContent_attributeValues_attributeValue | null;
translation: ProductTranslations_translations_edges_node_ProductTranslatableContent_attributeValues_translation | null;
} }
export interface ProductTranslations_translations_edges_node_ProductTranslatableContent { export interface ProductTranslations_translations_edges_node_ProductTranslatableContent {
__typename: "ProductTranslatableContent"; __typename: "ProductTranslatableContent";
product: ProductTranslations_translations_edges_node_ProductTranslatableContent_product | null; product: ProductTranslations_translations_edges_node_ProductTranslatableContent_product | null;
translation: ProductTranslations_translations_edges_node_ProductTranslatableContent_translation | null; translation: ProductTranslations_translations_edges_node_ProductTranslatableContent_translation | null;
attributeValues: ProductTranslations_translations_edges_node_ProductTranslatableContent_attributeValues[];
} }
export type ProductTranslations_translations_edges_node = ProductTranslations_translations_edges_node_CollectionTranslatableContent | ProductTranslations_translations_edges_node_ProductTranslatableContent; export type ProductTranslations_translations_edges_node = ProductTranslations_translations_edges_node_CollectionTranslatableContent | ProductTranslations_translations_edges_node_ProductTranslatableContent;

View file

@ -26,6 +26,7 @@ export interface UpdateAttributeValueTranslations_attributeValueTranslate_attrib
__typename: "AttributeValue"; __typename: "AttributeValue";
id: string; id: string;
name: string | null; name: string | null;
richText: any | null;
translation: UpdateAttributeValueTranslations_attributeValueTranslate_attributeValue_translation | null; translation: UpdateAttributeValueTranslations_attributeValueTranslate_attributeValue_translation | null;
} }

View file

@ -40,10 +40,39 @@ export interface UpdatePageTranslations_pageTranslate_page_translation {
language: UpdatePageTranslations_pageTranslate_page_translation_language; language: UpdatePageTranslations_pageTranslate_page_translation_language;
} }
export interface UpdatePageTranslations_pageTranslate_page_attributeValues_attributeValue {
__typename: "AttributeValue";
id: string;
}
export interface UpdatePageTranslations_pageTranslate_page_attributeValues_translation_language {
__typename: "LanguageDisplay";
code: LanguageCodeEnum;
language: string;
}
export interface UpdatePageTranslations_pageTranslate_page_attributeValues_translation {
__typename: "AttributeValueTranslation";
id: string;
name: string;
richText: any | null;
language: UpdatePageTranslations_pageTranslate_page_attributeValues_translation_language;
}
export interface UpdatePageTranslations_pageTranslate_page_attributeValues {
__typename: "AttributeValueTranslatableContent";
id: string;
name: string;
richText: any | null;
attributeValue: UpdatePageTranslations_pageTranslate_page_attributeValues_attributeValue | null;
translation: UpdatePageTranslations_pageTranslate_page_attributeValues_translation | null;
}
export interface UpdatePageTranslations_pageTranslate_page { export interface UpdatePageTranslations_pageTranslate_page {
__typename: "PageTranslatableContent"; __typename: "PageTranslatableContent";
page: UpdatePageTranslations_pageTranslate_page_page | null; page: UpdatePageTranslations_pageTranslate_page_page | null;
translation: UpdatePageTranslations_pageTranslate_page_translation | null; translation: UpdatePageTranslations_pageTranslate_page_translation | null;
attributeValues: UpdatePageTranslations_pageTranslate_page_attributeValues[];
} }
export interface UpdatePageTranslations_pageTranslate { export interface UpdatePageTranslations_pageTranslate {

View file

@ -11,7 +11,7 @@ export enum TranslatableEntities {
sales = "sales", sales = "sales",
vouchers = "vouchers", vouchers = "vouchers",
pages = "pages", pages = "pages",
productTypes = "productTypes", attributes = "attributes",
shippingMethods = "shippingMethods" shippingMethods = "shippingMethods"
} }

View file

@ -14,14 +14,15 @@ import { useIntl } from "react-intl";
import { getMutationState, maybe } from "../../misc"; import { getMutationState, maybe } from "../../misc";
import { LanguageCodeEnum } from "../../types/globalTypes"; import { LanguageCodeEnum } from "../../types/globalTypes";
import TranslationsProductTypesPage, { import TranslationsAttributesPage, {
fieldNames fieldNames
} from "../components/TranslationsProductTypesPage"; } from "../components/TranslationsAttributesPage";
import { import {
TypedUpdateAttributeTranslations, TypedUpdateAttributeTranslations,
TypedUpdateAttributeValueTranslations TypedUpdateAttributeValueTranslations
} from "../mutations"; } from "../mutations";
import { useAttributeTranslationDetails } from "../queries"; import { useAttributeTranslationDetails } from "../queries";
import { TranslationField } from "../types";
import { UpdateAttributeTranslations } from "../types/UpdateAttributeTranslations"; import { UpdateAttributeTranslations } from "../types/UpdateAttributeTranslations";
import { UpdateAttributeValueTranslations } from "../types/UpdateAttributeValueTranslations"; import { UpdateAttributeValueTranslations } from "../types/UpdateAttributeValueTranslations";
import { import {
@ -30,16 +31,16 @@ import {
TranslatableEntities TranslatableEntities
} from "../urls"; } from "../urls";
export interface TranslationsProductTypesQueryParams extends Pagination { export interface TranslationsAttributesQueryParams extends Pagination {
activeField: string; activeField: string;
} }
export interface TranslationsProductTypesProps { export interface TranslationsAttributesProps {
id: string; id: string;
languageCode: LanguageCodeEnum; languageCode: LanguageCodeEnum;
params: TranslationsProductTypesQueryParams; params: TranslationsAttributesQueryParams;
} }
const TranslationsProductTypes: React.FC<TranslationsProductTypesProps> = ({ const TranslationsAttributes: React.FC<TranslationsAttributesProps> = ({
id, id,
languageCode, languageCode,
params params
@ -121,8 +122,11 @@ const TranslationsProductTypes: React.FC<TranslationsProductTypesProps> = ({
updateAttributeValueTranslations, updateAttributeValueTranslations,
updateAttributeValueTranslationsOpts updateAttributeValueTranslationsOpts
) => { ) => {
const handleSubmit = (field: string, data: string | OutputData) => { const handleSubmit = (
const [fieldName, fieldId] = field.split(":"); { name }: TranslationField,
data: string | OutputData
) => {
const [fieldName, fieldId] = name.split(":");
if (fieldName === fieldNames.attribute) { if (fieldName === fieldNames.attribute) {
updateAttributeTranslations({ updateAttributeTranslations({
@ -168,7 +172,7 @@ const TranslationsProductTypes: React.FC<TranslationsProductTypesProps> = ({
); );
return ( return (
<TranslationsProductTypesPage <TranslationsAttributesPage
activeField={params.activeField} activeField={params.activeField}
disabled={ disabled={
attributeTranslations.loading || attributeTranslations.loading ||
@ -181,7 +185,7 @@ const TranslationsProductTypes: React.FC<TranslationsProductTypesProps> = ({
onBack={() => onBack={() =>
navigate( navigate(
languageEntitiesUrl(languageCode, { languageEntitiesUrl(languageCode, {
tab: TranslatableEntities.productTypes tab: TranslatableEntities.attributes
}) })
) )
} }
@ -189,11 +193,7 @@ const TranslationsProductTypes: React.FC<TranslationsProductTypesProps> = ({
onDiscard={onDiscard} onDiscard={onDiscard}
onLanguageChange={lang => onLanguageChange={lang =>
navigate( navigate(
languageEntityUrl( languageEntityUrl(lang, TranslatableEntities.attributes, id)
lang,
TranslatableEntities.productTypes,
id
)
) )
} }
onSubmit={handleSubmit} onSubmit={handleSubmit}
@ -211,5 +211,5 @@ const TranslationsProductTypes: React.FC<TranslationsProductTypesProps> = ({
</TypedUpdateAttributeTranslations> </TypedUpdateAttributeTranslations>
); );
}; };
TranslationsProductTypes.displayName = "TranslationsProductTypes"; TranslationsAttributes.displayName = "TranslationsAttributes";
export default TranslationsProductTypes; export default TranslationsAttributes;

View file

@ -11,7 +11,7 @@ import { LanguageCodeEnum } from "../../types/globalTypes";
import TranslationsCategoriesPage from "../components/TranslationsCategoriesPage"; import TranslationsCategoriesPage from "../components/TranslationsCategoriesPage";
import { TypedUpdateCategoryTranslations } from "../mutations"; import { TypedUpdateCategoryTranslations } from "../mutations";
import { useCategoryTranslationDetails } from "../queries"; import { useCategoryTranslationDetails } from "../queries";
import { TranslationInputFieldName } from "../types"; import { TranslationField, TranslationInputFieldName } from "../types";
import { UpdateCategoryTranslations } from "../types/UpdateCategoryTranslations"; import { UpdateCategoryTranslations } from "../types/UpdateCategoryTranslations";
import { import {
languageEntitiesUrl, languageEntitiesUrl,
@ -69,13 +69,16 @@ const TranslationsCategories: React.FC<TranslationsCategoriesProps> = ({
<TypedUpdateCategoryTranslations onCompleted={onUpdate}> <TypedUpdateCategoryTranslations onCompleted={onUpdate}>
{(updateTranslations, updateTranslationsOpts) => { {(updateTranslations, updateTranslationsOpts) => {
const handleSubmit = ( const handleSubmit = (
fieldName: TranslationInputFieldName, { name: fieldName }: TranslationField<TranslationInputFieldName>,
data: string | OutputData data: string | OutputData
) => { ) => {
updateTranslations({ updateTranslations({
variables: { variables: {
id, id,
input: getParsedTranslationInputData({ data, fieldName }), input: getParsedTranslationInputData({
data,
fieldName
}),
language: languageCode language: languageCode
} }
}); });

View file

@ -11,7 +11,7 @@ import { LanguageCodeEnum } from "../../types/globalTypes";
import TranslationsCollectionsPage from "../components/TranslationsCollectionsPage"; import TranslationsCollectionsPage from "../components/TranslationsCollectionsPage";
import { TypedUpdateCollectionTranslations } from "../mutations"; import { TypedUpdateCollectionTranslations } from "../mutations";
import { useCollectionTranslationDetails } from "../queries"; import { useCollectionTranslationDetails } from "../queries";
import { TranslationInputFieldName } from "../types"; import { TranslationField, TranslationInputFieldName } from "../types";
import { UpdateCollectionTranslations } from "../types/UpdateCollectionTranslations"; import { UpdateCollectionTranslations } from "../types/UpdateCollectionTranslations";
import { import {
languageEntitiesUrl, languageEntitiesUrl,
@ -70,13 +70,16 @@ const TranslationsCollections: React.FC<TranslationsCollectionsProps> = ({
<TypedUpdateCollectionTranslations onCompleted={onUpdate}> <TypedUpdateCollectionTranslations onCompleted={onUpdate}>
{(updateTranslations, updateTranslationsOpts) => { {(updateTranslations, updateTranslationsOpts) => {
const handleSubmit = ( const handleSubmit = (
fieldName: TranslationInputFieldName, { name: fieldName }: TranslationField<TranslationInputFieldName>,
data: string data: string
) => { ) => {
updateTranslations({ updateTranslations({
variables: { variables: {
id, id,
input: getParsedTranslationInputData({ data, fieldName }), input: getParsedTranslationInputData({
data,
fieldName
}),
language: languageCode language: languageCode
} }
}); });

View file

@ -76,11 +76,11 @@ const TranslationsEntities: React.FC<TranslationsEntitiesProps> = ({
tab: TranslatableEntities.pages tab: TranslatableEntities.pages
}) })
), ),
onProductTypesTabClick: () => onAttributesTabClick: () =>
navigate( navigate(
"?" + "?" +
stringifyQs({ stringifyQs({
tab: TranslatableEntities.productTypes tab: TranslatableEntities.attributes
}) })
), ),
onProductsTabClick: () => onProductsTabClick: () =>
@ -380,7 +380,7 @@ const TranslationsEntities: React.FC<TranslationsEntitiesProps> = ({
); );
}} }}
</TypedPageTranslations> </TypedPageTranslations>
) : params.tab === "productTypes" ? ( ) : params.tab === "attributes" ? (
<TypedAttributeTranslations variables={queryVariables}> <TypedAttributeTranslations variables={queryVariables}>
{({ data, loading }) => { {({ data, loading }) => {
const { loadNextPage, loadPreviousPage, pageInfo } = paginate( const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
@ -403,7 +403,7 @@ const TranslationsEntities: React.FC<TranslationsEntitiesProps> = ({
navigate( navigate(
languageEntityUrl( languageEntityUrl(
language, language,
TranslatableEntities.productTypes, TranslatableEntities.attributes,
id id
) )
) )

View file

@ -1,3 +1,4 @@
import { OutputData } from "@editorjs/editorjs";
import useNavigator from "@saleor/hooks/useNavigator"; import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier"; import useNotifier from "@saleor/hooks/useNotifier";
import useShop from "@saleor/hooks/useShop"; import useShop from "@saleor/hooks/useShop";
@ -8,10 +9,12 @@ import { useIntl } from "react-intl";
import { LanguageCodeEnum } from "../../types/globalTypes"; import { LanguageCodeEnum } from "../../types/globalTypes";
import TranslationsPagesPage from "../components/TranslationsPagesPage"; import TranslationsPagesPage from "../components/TranslationsPagesPage";
import { TypedUpdatePageTranslations } from "../mutations"; import {
TypedUpdateAttributeValueTranslations,
TypedUpdatePageTranslations
} from "../mutations";
import { usePageTranslationDetails } from "../queries"; import { usePageTranslationDetails } from "../queries";
import { PageTranslationInputFieldName } from "../types"; import { PageTranslationInputFieldName, TranslationField } from "../types";
import { UpdatePageTranslations } from "../types/UpdatePageTranslations";
import { import {
languageEntitiesUrl, languageEntitiesUrl,
languageEntityUrl, languageEntityUrl,
@ -50,8 +53,9 @@ const TranslationsPages: React.FC<TranslationsPagesProps> = ({
}), }),
true true
); );
const onUpdate = (data: UpdatePageTranslations) => {
if (data.pageTranslate.errors.length === 0) { const onUpdate = (errors: unknown[]) => {
if (errors.length === 0) {
pageTranslations.refetch(); pageTranslations.refetch();
notify({ notify({
status: "success", status: "success",
@ -60,57 +64,88 @@ const TranslationsPages: React.FC<TranslationsPagesProps> = ({
navigate("?", true); navigate("?", true);
} }
}; };
const onDiscard = () => { const onDiscard = () => {
navigate("?", true); navigate("?", true);
}; };
return ( return (
<TypedUpdatePageTranslations onCompleted={onUpdate}> <TypedUpdatePageTranslations
{(updateTranslations, updateTranslationsOpts) => { onCompleted={data => onUpdate(data.pageTranslate.errors)}
const handleSubmit = ( >
fieldName: PageTranslationInputFieldName, {(updateTranslations, updateTranslationsOpts) => (
data: string <TypedUpdateAttributeValueTranslations
) => { onCompleted={data => onUpdate(data.attributeValueTranslate.errors)}
updateTranslations({ >
variables: { {updateAttributeValueTranslations => {
id, const handleSubmit = (
input: getParsedTranslationInputData({ data, fieldName }), {
language: languageCode name: fieldName
} }: TranslationField<PageTranslationInputFieldName>,
}); data: string | any
}; ) => {
const translation = pageTranslations?.data?.translation; updateTranslations({
variables: {
id,
input: getParsedTranslationInputData({
data,
fieldName
}),
language: languageCode
}
});
};
return ( const handleAttributeValueSubmit = (
<TranslationsPagesPage { id }: TranslationField<PageTranslationInputFieldName>,
activeField={params.activeField} data: OutputData
disabled={ ) => {
pageTranslations.loading || updateTranslationsOpts.loading updateAttributeValueTranslations({
} variables: {
languageCode={languageCode} id,
languages={shop?.languages || []} input: { richText: JSON.stringify(data) },
saveButtonState={updateTranslationsOpts.status} language: languageCode
onBack={() => }
navigate( });
languageEntitiesUrl(languageCode, { };
tab: TranslatableEntities.pages
}) const translation = pageTranslations?.data?.translation;
)
} return (
onEdit={onEdit} <TranslationsPagesPage
onDiscard={onDiscard} activeField={params.activeField}
onLanguageChange={lang => disabled={
navigate(languageEntityUrl(lang, TranslatableEntities.pages, id)) pageTranslations.loading || updateTranslationsOpts.loading
} }
onSubmit={handleSubmit} languageCode={languageCode}
data={ languages={shop?.languages || []}
translation?.__typename === "PageTranslatableContent" saveButtonState={updateTranslationsOpts.status}
? translation onBack={() =>
: null navigate(
} languageEntitiesUrl(languageCode, {
/> tab: TranslatableEntities.pages
); })
}} )
}
onEdit={onEdit}
onDiscard={onDiscard}
onLanguageChange={lang =>
navigate(
languageEntityUrl(lang, TranslatableEntities.pages, id)
)
}
onSubmit={handleSubmit}
onAttributeValueSubmit={handleAttributeValueSubmit}
data={
translation?.__typename === "PageTranslatableContent"
? translation
: null
}
/>
);
}}
</TypedUpdateAttributeValueTranslations>
)}
</TypedUpdatePageTranslations> </TypedUpdatePageTranslations>
); );
}; };

View file

@ -1,3 +1,4 @@
import { OutputData } from "@editorjs/editorjs";
import useNavigator from "@saleor/hooks/useNavigator"; import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier"; import useNotifier from "@saleor/hooks/useNotifier";
import useShop from "@saleor/hooks/useShop"; import useShop from "@saleor/hooks/useShop";
@ -9,10 +10,12 @@ import { useIntl } from "react-intl";
import { maybe } from "../../misc"; import { maybe } from "../../misc";
import { LanguageCodeEnum } from "../../types/globalTypes"; import { LanguageCodeEnum } from "../../types/globalTypes";
import TranslationsProductsPage from "../components/TranslationsProductsPage"; import TranslationsProductsPage from "../components/TranslationsProductsPage";
import { TypedUpdateProductTranslations } from "../mutations"; import {
TypedUpdateAttributeValueTranslations,
TypedUpdateProductTranslations
} from "../mutations";
import { useProductTranslationDetails } from "../queries"; import { useProductTranslationDetails } from "../queries";
import { TranslationInputFieldName } from "../types"; import { TranslationField, TranslationInputFieldName } from "../types";
import { UpdateProductTranslations } from "../types/UpdateProductTranslations";
import { import {
languageEntitiesUrl, languageEntitiesUrl,
languageEntityUrl, languageEntityUrl,
@ -51,8 +54,9 @@ const TranslationsProducts: React.FC<TranslationsProductsProps> = ({
}), }),
true true
); );
const onUpdate = (data: UpdateProductTranslations) => {
if (data.productTranslate.errors.length === 0) { const onUpdate = (errors: unknown[]) => {
if (errors.length === 0) {
productTranslations.refetch(); productTranslations.refetch();
notify({ notify({
status: "success", status: "success",
@ -61,59 +65,86 @@ const TranslationsProducts: React.FC<TranslationsProductsProps> = ({
navigate("?", true); navigate("?", true);
} }
}; };
const onDiscard = () => { const onDiscard = () => {
navigate("?", true); navigate("?", true);
}; };
return ( return (
<TypedUpdateProductTranslations onCompleted={onUpdate}> <TypedUpdateProductTranslations
{(updateTranslations, updateTranslationsOpts) => { onCompleted={data => onUpdate(data.productTranslate.errors)}
const handleSubmit = ( >
fieldName: TranslationInputFieldName, {(updateTranslations, updateTranslationsOpts) => (
data: string <TypedUpdateAttributeValueTranslations
) => { onCompleted={data => onUpdate(data.attributeValueTranslate.errors)}
updateTranslations({ >
variables: { {updateAttributeValueTranslations => {
id, const handleSubmit = (
input: getParsedTranslationInputData({ data, fieldName }), { name: fieldName }: TranslationField<TranslationInputFieldName>,
language: languageCode data: string
} ) => {
}); updateTranslations({
}; variables: {
const translation = productTranslations?.data?.translation; id,
input: getParsedTranslationInputData({
data,
fieldName
}),
language: languageCode
}
});
};
return ( const handleAttributeValueSubmit = (
<TranslationsProductsPage { id }: TranslationField<TranslationInputFieldName>,
activeField={params.activeField} data: OutputData
disabled={ ) => {
productTranslations.loading || updateTranslationsOpts.loading updateAttributeValueTranslations({
} variables: {
languageCode={languageCode} id,
languages={maybe(() => shop.languages, [])} input: { richText: JSON.stringify(data) },
saveButtonState={updateTranslationsOpts.status} language: languageCode
onBack={() => }
navigate( });
languageEntitiesUrl(languageCode, { };
tab: TranslatableEntities.products
}) const translation = productTranslations?.data?.translation;
)
} return (
onEdit={onEdit} <TranslationsProductsPage
onDiscard={onDiscard} activeField={params.activeField}
onLanguageChange={lang => disabled={
navigate( productTranslations.loading || updateTranslationsOpts.loading
languageEntityUrl(lang, TranslatableEntities.products, id) }
) languageCode={languageCode}
} languages={maybe(() => shop.languages, [])}
onSubmit={handleSubmit} saveButtonState={updateTranslationsOpts.status}
data={ onBack={() =>
translation?.__typename === "ProductTranslatableContent" navigate(
? translation languageEntitiesUrl(languageCode, {
: null tab: TranslatableEntities.products
} })
/> )
); }
}} onEdit={onEdit}
onDiscard={onDiscard}
onLanguageChange={lang =>
navigate(
languageEntityUrl(lang, TranslatableEntities.products, id)
)
}
onSubmit={handleSubmit}
onAttributeValueSubmit={handleAttributeValueSubmit}
data={
translation?.__typename === "ProductTranslatableContent"
? translation
: null
}
/>
);
}}
</TypedUpdateAttributeValueTranslations>
)}
</TypedUpdateProductTranslations> </TypedUpdateProductTranslations>
); );
}; };

View file

@ -6,21 +6,18 @@ import { stringify as stringifyQs } from "qs";
import React from "react"; import React from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import { import { LanguageCodeEnum } from "../../types/globalTypes";
LanguageCodeEnum, import TranslationsSalesPage from "../components/TranslationsSalesPage";
NameTranslationInput
} from "../../types/globalTypes";
import TranslationsSalesPage, {
fieldNames
} from "../components/TranslationsSalesPage";
import { TypedUpdateSaleTranslations } from "../mutations"; import { TypedUpdateSaleTranslations } from "../mutations";
import { useSaleTranslationDetails } from "../queries"; import { useSaleTranslationDetails } from "../queries";
import { TranslationField, TranslationInputFieldName } from "../types";
import { UpdateSaleTranslations } from "../types/UpdateSaleTranslations"; import { UpdateSaleTranslations } from "../types/UpdateSaleTranslations";
import { import {
languageEntitiesUrl, languageEntitiesUrl,
languageEntityUrl, languageEntityUrl,
TranslatableEntities TranslatableEntities
} from "../urls"; } from "../urls";
import { getParsedTranslationInputData } from "../utils";
export interface TranslationsSalesQueryParams { export interface TranslationsSalesQueryParams {
activeField: string; activeField: string;
@ -70,15 +67,17 @@ const TranslationsSales: React.FC<TranslationsSalesProps> = ({
return ( return (
<TypedUpdateSaleTranslations onCompleted={onUpdate}> <TypedUpdateSaleTranslations onCompleted={onUpdate}>
{(updateTranslations, updateTranslationsOpts) => { {(updateTranslations, updateTranslationsOpts) => {
const handleSubmit = (field: string, data: string) => { const handleSubmit = (
const input: NameTranslationInput = {}; { name: fieldName }: TranslationField<TranslationInputFieldName>,
if (field === fieldNames.name) { data: string
input.name = data; ) => {
}
updateTranslations({ updateTranslations({
variables: { variables: {
id, id,
input, input: getParsedTranslationInputData({
data,
fieldName
}),
language: languageCode language: languageCode
} }
}); });

View file

@ -10,7 +10,7 @@ import { LanguageCodeEnum } from "../../types/globalTypes";
import TranslationsShippingMethodPage from "../components/TranslationsShippingMethodPage"; import TranslationsShippingMethodPage from "../components/TranslationsShippingMethodPage";
import { TypedUpdateShippingMethodTranslations } from "../mutations"; import { TypedUpdateShippingMethodTranslations } from "../mutations";
import { useShippingMethodTranslationDetails } from "../queries"; import { useShippingMethodTranslationDetails } from "../queries";
import { TranslationInputFieldName } from "../types"; import { TranslationField, TranslationInputFieldName } from "../types";
import { UpdateShippingMethodTranslations } from "../types/UpdateShippingMethodTranslations"; import { UpdateShippingMethodTranslations } from "../types/UpdateShippingMethodTranslations";
import { import {
languageEntitiesUrl, languageEntitiesUrl,
@ -68,13 +68,13 @@ const TranslationsShippingMethod: React.FC<TranslationsShippingMethodProps> = ({
<TypedUpdateShippingMethodTranslations onCompleted={onUpdate}> <TypedUpdateShippingMethodTranslations onCompleted={onUpdate}>
{(updateTranslations, updateTranslationsOpts) => { {(updateTranslations, updateTranslationsOpts) => {
const handleSubmit = ( const handleSubmit = (
field: TranslationInputFieldName, { name: fieldName }: TranslationField<TranslationInputFieldName>,
data: string data: string
) => { ) => {
updateTranslations({ updateTranslations({
variables: { variables: {
id, id,
input: getParsedTranslationInputData({ fieldName: field, data }), input: getParsedTranslationInputData({ fieldName, data }),
language: languageCode language: languageCode
} }
}); });

View file

@ -7,21 +7,18 @@ import React from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import { maybe } from "../../misc"; import { maybe } from "../../misc";
import { import { LanguageCodeEnum } from "../../types/globalTypes";
LanguageCodeEnum, import TranslationsVouchersPage from "../components/TranslationsVouchersPage";
NameTranslationInput
} from "../../types/globalTypes";
import TranslationsVouchersPage, {
fieldNames
} from "../components/TranslationsVouchersPage";
import { TypedUpdateVoucherTranslations } from "../mutations"; import { TypedUpdateVoucherTranslations } from "../mutations";
import { useVoucherTranslationDetails } from "../queries"; import { useVoucherTranslationDetails } from "../queries";
import { TranslationField, TranslationInputFieldName } from "../types";
import { UpdateVoucherTranslations } from "../types/UpdateVoucherTranslations"; import { UpdateVoucherTranslations } from "../types/UpdateVoucherTranslations";
import { import {
languageEntitiesUrl, languageEntitiesUrl,
languageEntityUrl, languageEntityUrl,
TranslatableEntities TranslatableEntities
} from "../urls"; } from "../urls";
import { getParsedTranslationInputData } from "../utils";
export interface TranslationsVouchersQueryParams { export interface TranslationsVouchersQueryParams {
activeField: string; activeField: string;
@ -71,15 +68,17 @@ const TranslationsVouchers: React.FC<TranslationsVouchersProps> = ({
return ( return (
<TypedUpdateVoucherTranslations onCompleted={onUpdate}> <TypedUpdateVoucherTranslations onCompleted={onUpdate}>
{(updateTranslations, updateTranslationsOpts) => { {(updateTranslations, updateTranslationsOpts) => {
const handleSubmit = (field: string, data: string) => { const handleSubmit = (
const input: NameTranslationInput = {}; { name: fieldName }: TranslationField<TranslationInputFieldName>,
if (field === fieldNames.name) { data: string
input.name = data; ) => {
}
updateTranslations({ updateTranslations({
variables: { variables: {
id, id,
input, input: getParsedTranslationInputData({
data,
fieldName
}),
language: languageCode language: languageCode
} }
}); });