Page types (#807)
* Create attribute class selector * Use ProductAttributeType to check if product is simple or with variants * Allow attribute class selection only during its creation * Update attribute type selection translations * Show only product attributes in columns picker on product list view * Cleanups in Attribute Organization component * Create Page Types list page * Create content management section in settings * Implement page types list view * Remove unused imports from page type list * Updatte page type list style * Remove legacy code from page type list component * Update PageTypeListPage component * Create Page Types details page * Fix page type attribute reordering * Implement PageType create view * Implement PageType update view * gUpdate page type details components * Fix page type update component * Update page type components stories * Update page type errors handling * Update page type details view * Create Page Types details page * Implement PageType create view * Update product attribute assignment mutations * Add page types attribute assignment mutations * Add page types attribute assignment handling * Temporarily fix page create mutation * Update page type error messages * Remove legacy storybook page type stories * Update attribute assignment dialogs stories * Update page type details error handling * Update props for page type components * Create attribute class selector * Implement page types list view * Add page type selector on page create and details views * Add attributes list to page details views * Update page types list * Use attribute errors for attributes muatations * Save attribute values on page create and update * Update messages for page view * Update page attributes fragment * Use AttributeError in AttributeBulkDelete * Update page type and its attribute selection * Handle page types deleting * Update page types deleting messages * Handle page types attribute reorder * Fix PageOrganizeContent component types * Update graphqql types * Fix page fixture * Update messages * Update test snapshots * Pass pageTypes to PageForm * Update changelog with page type addition note * Update package-lock * Update test snapshots * Fix malformed generated type * Update messages after rebase
This commit is contained in:
parent
080f6e5b01
commit
fc02fce701
149 changed files with 11407 additions and 972 deletions
|
@ -4,6 +4,8 @@ All notable, unreleased changes to this project will be documented in this file.
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
- Add Page Types - #807 by @orzechdev
|
||||
|
||||
# 2.11.1
|
||||
|
||||
- Support multiline text in plugin configuration secret field - #829 by @karolinakuzniewicz
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
"configurationMenuNavigation": {
|
||||
"string": "Define how users can navigate through your store"
|
||||
},
|
||||
"configurationMenuPageTypes": {
|
||||
"string": "Define types of content pages used in your store"
|
||||
},
|
||||
"configurationMenuPages": {
|
||||
"string": "Manage and add additional pages"
|
||||
},
|
||||
|
@ -208,6 +211,13 @@
|
|||
"context": "vat not included in order price",
|
||||
"string": "does not apply"
|
||||
},
|
||||
"pageTypeCreateHeader": {
|
||||
"context": "window title",
|
||||
"string": "Create Page Type"
|
||||
},
|
||||
"pageTypeInputLabel": {
|
||||
"string": "Select content type"
|
||||
},
|
||||
"productExportFieldCategory": {
|
||||
"context": "product field",
|
||||
"string": "Category"
|
||||
|
@ -808,6 +818,24 @@
|
|||
"context": "attribute's label'",
|
||||
"string": "Default Label"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeOrganization_dot_205334083": {
|
||||
"string": "Define where this attribute should be used in Saleor system"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeOrganization_dot_3829816432": {
|
||||
"context": "section header",
|
||||
"string": "Organization"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeOrganization_dot_3835822980": {
|
||||
"string": "Attribute Class"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeOrganization_dot_contentAttribute": {
|
||||
"context": "attribute type",
|
||||
"string": "Content Attribute"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeOrganization_dot_productAttribute": {
|
||||
"context": "attribute type",
|
||||
"string": "Product Attribute"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributePage_dot_2855501559": {
|
||||
"context": "page title",
|
||||
"string": "Create New Attribute"
|
||||
|
@ -824,13 +852,16 @@
|
|||
"context": "attribute properties regarding dashboard",
|
||||
"string": "Dashboard Properties"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_3135366329": {
|
||||
"context": "attribute visibility in storefront",
|
||||
"string": "Public"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_3590282519": {
|
||||
"context": "attribute position in storefront filters",
|
||||
"string": "Position in faceted navigation"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_3876764312": {
|
||||
"context": "attribute",
|
||||
"string": "Visible on Product Page in Storefront"
|
||||
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_3758203740": {
|
||||
"string": "If enabled, attribute will be accessible to customers."
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_4048785456": {
|
||||
"string": "If enabled this attribute can be used as a column in product table."
|
||||
|
@ -1451,6 +1482,23 @@
|
|||
"context": "section header",
|
||||
"string": "App Status"
|
||||
},
|
||||
"src_dot_components_dot_AssignAttributeDialog_dot_2173976534": {
|
||||
"context": "button",
|
||||
"string": "Assign attributes"
|
||||
},
|
||||
"src_dot_components_dot_AssignAttributeDialog_dot_3922579741": {
|
||||
"context": "dialog header",
|
||||
"string": "Assign Attribute"
|
||||
},
|
||||
"src_dot_components_dot_AssignAttributeDialog_dot_4205644805": {
|
||||
"string": "No results found"
|
||||
},
|
||||
"src_dot_components_dot_AssignAttributeDialog_dot_524117994": {
|
||||
"string": "Search by attribute name"
|
||||
},
|
||||
"src_dot_components_dot_AssignAttributeDialog_dot_902296540": {
|
||||
"string": "Search Attributes"
|
||||
},
|
||||
"src_dot_components_dot_AssignCategoryDialog_dot_3125506097": {
|
||||
"context": "dialog header",
|
||||
"string": "Assign Category"
|
||||
|
@ -1489,6 +1537,9 @@
|
|||
"context": "dialog header",
|
||||
"string": "Assign Product"
|
||||
},
|
||||
"src_dot_components_dot_AttributeUnassignDialog_dot_2037985699": {
|
||||
"string": "Are you sure you want to unassign {attributeName} from {itemTypeName}?"
|
||||
},
|
||||
"src_dot_components_dot_AutocompleteSelectMenu_dot_2332404293": {
|
||||
"string": "No results"
|
||||
},
|
||||
|
@ -1528,6 +1579,10 @@
|
|||
"context": "product unavailability",
|
||||
"string": "Unavailable for purchase"
|
||||
},
|
||||
"src_dot_components_dot_BulkAttributeUnassignDialog_dot_3177750460": {
|
||||
"context": "unassign multiple attributes from item",
|
||||
"string": "{counter,plural,one{Are you sure you want to unassign this attribute from {itemTypeName}?} other{Are you sure you want to unassign {attributeQuantity} attributes from {itemTypeName}?}}"
|
||||
},
|
||||
"src_dot_components_dot_ChannelsAvailabilityContent_dot_1528830621": {
|
||||
"string": "Select channels you want for {contentType} to be available on"
|
||||
},
|
||||
|
@ -2076,6 +2131,9 @@
|
|||
"src_dot_configuration_dot_3140151600": {
|
||||
"string": "Staff Settings"
|
||||
},
|
||||
"src_dot_configuration_dot_3351299924": {
|
||||
"string": "Content Management"
|
||||
},
|
||||
"src_dot_configuration_dot_3655543906": {
|
||||
"string": "Product Settings"
|
||||
},
|
||||
|
@ -3699,10 +3757,136 @@
|
|||
"src_dot_orders_dot_views_dot_OrderList_dot_1738939038": {
|
||||
"string": "Order draft successfully created"
|
||||
},
|
||||
"src_dot_pageTypes": {
|
||||
"context": "page types section name",
|
||||
"string": "Page Types"
|
||||
},
|
||||
"src_dot_pageTypes_dot_components_dot_PageTypeAttributes_dot_1192828581": {
|
||||
"string": "No attributes found"
|
||||
},
|
||||
"src_dot_pageTypes_dot_components_dot_PageTypeAttributes_dot_1228425832": {
|
||||
"string": "Attribute name"
|
||||
},
|
||||
"src_dot_pageTypes_dot_components_dot_PageTypeAttributes_dot_1656462109": {
|
||||
"context": "button",
|
||||
"string": "Assign attribute"
|
||||
},
|
||||
"src_dot_pageTypes_dot_components_dot_PageTypeAttributes_dot_3478065224": {
|
||||
"context": "attribute internal name",
|
||||
"string": "Slug"
|
||||
},
|
||||
"src_dot_pageTypes_dot_components_dot_PageTypeAttributes_dot_613275198": {
|
||||
"context": "section header",
|
||||
"string": "Content Attributes"
|
||||
},
|
||||
"src_dot_pageTypes_dot_components_dot_PageTypeBulkDeleteDialog_dot_4266703515": {
|
||||
"context": "delete page types with its pages",
|
||||
"string": "{counter,plural,one{Page Type you want to delete is used by some pages. Deleting this page type will also delete those pages. Are you sure you want to delete this page type? After doing so you won’t be able to revert changes.} other{Page Types you want to delete are used by some pages. Deleting these page types will also delete those pages. Are you sure you want to delete {displayQuantity} page types? After doing so you won’t be able to revert changes.}}"
|
||||
},
|
||||
"src_dot_pageTypes_dot_components_dot_PageTypeBulkDeleteDialog_dot_69987032": {
|
||||
"context": "dialog header",
|
||||
"string": "Delete Page Types"
|
||||
},
|
||||
"src_dot_pageTypes_dot_components_dot_PageTypeBulkDeleteDialog_dot_8271141": {
|
||||
"context": "delete page types",
|
||||
"string": "{counter,plural,one{Are you sure you want to delete this page type? After doing so you won’t be able to revert changes.} other{Are you sure you want to delete {displayQuantity} page types? After doing so you won’t be able to revert changes.}}"
|
||||
},
|
||||
"src_dot_pageTypes_dot_components_dot_PageTypeCreatePage_dot_1105469372": {
|
||||
"string": "These are general information about this Content Type."
|
||||
},
|
||||
"src_dot_pageTypes_dot_components_dot_PageTypeCreatePage_dot_1509432322": {
|
||||
"context": "section header",
|
||||
"string": "Metadata"
|
||||
},
|
||||
"src_dot_pageTypes_dot_components_dot_PageTypeCreatePage_dot_4047854353": {
|
||||
"context": "header",
|
||||
"string": "Create Page Type"
|
||||
},
|
||||
"src_dot_pageTypes_dot_components_dot_PageTypeDeleteDialog_dot_2364900868": {
|
||||
"context": "delete page type with its pages",
|
||||
"string": "Page Type you want to delete is used by some pages. Deleting this page type will also delete those pages. Are you sure you want to delete {name}? After doing so you won’t be able to revert changes."
|
||||
},
|
||||
"src_dot_pageTypes_dot_components_dot_PageTypeDeleteDialog_dot_3120835055": {
|
||||
"context": "dialog header",
|
||||
"string": "Delete Page Type"
|
||||
},
|
||||
"src_dot_pageTypes_dot_components_dot_PageTypeDeleteDialog_dot_3734861990": {
|
||||
"context": "delete page type",
|
||||
"string": "Are you sure you want to delete {name}? After doing so you won’t be able to revert changes."
|
||||
},
|
||||
"src_dot_pageTypes_dot_components_dot_PageTypeDetailsPage_dot_1105469372": {
|
||||
"string": "These are general information about this Content Type."
|
||||
},
|
||||
"src_dot_pageTypes_dot_components_dot_PageTypeDetailsPage_dot_1509432322": {
|
||||
"context": "section header",
|
||||
"string": "Metadata"
|
||||
},
|
||||
"src_dot_pageTypes_dot_components_dot_PageTypeDetailsPage_dot_3466659718": {
|
||||
"string": "This list shows all attributes that will be assigned to pages that have this page type assigned."
|
||||
},
|
||||
"src_dot_pageTypes_dot_components_dot_PageTypeDetailsPage_dot_613275198": {
|
||||
"context": "section header",
|
||||
"string": "Content Attributes"
|
||||
},
|
||||
"src_dot_pageTypes_dot_components_dot_PageTypeDetails_dot_1631499902": {
|
||||
"string": "Content Type Name"
|
||||
},
|
||||
"src_dot_pageTypes_dot_components_dot_PageTypeListPage_dot_1793515137": {
|
||||
"context": "button",
|
||||
"string": "create page type"
|
||||
},
|
||||
"src_dot_pageTypes_dot_components_dot_PageTypeListPage_dot_1793828289": {
|
||||
"string": "Search Page Type"
|
||||
},
|
||||
"src_dot_pageTypes_dot_components_dot_PageTypeListPage_dot_464566131": {
|
||||
"context": "tab name",
|
||||
"string": "All Page Types"
|
||||
},
|
||||
"src_dot_pageTypes_dot_components_dot_PageTypeList_dot_1631499902": {
|
||||
"context": "page type name",
|
||||
"string": "Content Type Name"
|
||||
},
|
||||
"src_dot_pageTypes_dot_components_dot_PageTypeList_dot_2965257236": {
|
||||
"string": "No page types found"
|
||||
},
|
||||
"src_dot_pageTypes_dot_views_dot_2634056946": {
|
||||
"context": "dialog header",
|
||||
"string": "Unassign Attribute from Page Type"
|
||||
},
|
||||
"src_dot_pageTypes_dot_views_dot_3442954831": {
|
||||
"string": "Page type deleted"
|
||||
},
|
||||
"src_dot_pageTypes_dot_views_dot_870815507": {
|
||||
"context": "unassign attribute from page type, button",
|
||||
"string": "Unassign"
|
||||
},
|
||||
"src_dot_pageTypes_dot_views_dot_891131033": {
|
||||
"context": "dialog header",
|
||||
"string": "Unassign Attribute From Page Type"
|
||||
},
|
||||
"src_dot_pageTypes_dot_views_dot_98187848": {
|
||||
"string": "Successfully created page type"
|
||||
},
|
||||
"src_dot_pages": {
|
||||
"context": "pages section name",
|
||||
"string": "Pages"
|
||||
},
|
||||
"src_dot_pages_dot_components_dot_PageAttributes_dot_1071548120": {
|
||||
"context": "number of page attributes",
|
||||
"string": "{number} Attributes"
|
||||
},
|
||||
"src_dot_pages_dot_components_dot_PageAttributes_dot_1148029984": {
|
||||
"context": "attribute value",
|
||||
"string": "Value"
|
||||
},
|
||||
"src_dot_pages_dot_components_dot_PageAttributes_dot_1207761269": {
|
||||
"context": "attribute values",
|
||||
"string": "Values"
|
||||
},
|
||||
"src_dot_pages_dot_components_dot_PageAttributes_dot_4153345096": {
|
||||
"context": "page attributes, section header",
|
||||
"string": "Attributes"
|
||||
},
|
||||
"src_dot_pages_dot_components_dot_PageDetailsPage_dot_1068617485": {
|
||||
"context": "page header",
|
||||
"string": "Create Page"
|
||||
|
@ -3757,6 +3941,13 @@
|
|||
"context": "page status",
|
||||
"string": "Not Published"
|
||||
},
|
||||
"src_dot_pages_dot_components_dot_PageOrganizeContent_dot_2959504520": {
|
||||
"string": "Content type"
|
||||
},
|
||||
"src_dot_pages_dot_components_dot_PageOrganizeContent_dot_590187004": {
|
||||
"context": "section header",
|
||||
"string": "Organize Content"
|
||||
},
|
||||
"src_dot_pages_dot_views_dot_1068617485": {
|
||||
"context": "header",
|
||||
"string": "Create Page"
|
||||
|
@ -4061,30 +4252,6 @@
|
|||
"context": "product types section name",
|
||||
"string": "Product Types"
|
||||
},
|
||||
"src_dot_productTypes_dot_components_dot_AssignAttributeDialog_dot_2173976534": {
|
||||
"context": "button",
|
||||
"string": "Assign attributes"
|
||||
},
|
||||
"src_dot_productTypes_dot_components_dot_AssignAttributeDialog_dot_3922579741": {
|
||||
"context": "dialog header",
|
||||
"string": "Assign Attribute"
|
||||
},
|
||||
"src_dot_productTypes_dot_components_dot_AssignAttributeDialog_dot_4205644805": {
|
||||
"string": "No results found"
|
||||
},
|
||||
"src_dot_productTypes_dot_components_dot_AssignAttributeDialog_dot_524117994": {
|
||||
"string": "Search by attribute name"
|
||||
},
|
||||
"src_dot_productTypes_dot_components_dot_AssignAttributeDialog_dot_902296540": {
|
||||
"string": "Search Attributes"
|
||||
},
|
||||
"src_dot_productTypes_dot_components_dot_ProductTypeAttributeUnassignDialog_dot_404238501": {
|
||||
"context": "dialog header",
|
||||
"string": "Unassign Attribute From Product Type"
|
||||
},
|
||||
"src_dot_productTypes_dot_components_dot_ProductTypeAttributeUnassignDialog_dot_722498450": {
|
||||
"string": "Are you sure you want to unassign {attributeName} from {productTypeName}?"
|
||||
},
|
||||
"src_dot_productTypes_dot_components_dot_ProductTypeAttributes_dot_1192828581": {
|
||||
"string": "No attributes found"
|
||||
},
|
||||
|
@ -4107,14 +4274,6 @@
|
|||
"context": "section header",
|
||||
"string": "Variant Attributes"
|
||||
},
|
||||
"src_dot_productTypes_dot_components_dot_ProductTypeBulkAttributeUnassignDialog_dot_2646729060": {
|
||||
"context": "unassign multiple attributes from product type",
|
||||
"string": "{counter,plural,one{Are you sure you want to unassign this attribute from {productTypeName}?} other{Are you sure you want to unassign {attributeQuantity} attributes from {productTypeName}?}}"
|
||||
},
|
||||
"src_dot_productTypes_dot_components_dot_ProductTypeBulkAttributeUnassignDialog_dot_766918870": {
|
||||
"context": "dialog header",
|
||||
"string": "Unassign Attribute from Product Type"
|
||||
},
|
||||
"src_dot_productTypes_dot_components_dot_ProductTypeDeleteDialog_dot_2297471173": {
|
||||
"context": "delete product type",
|
||||
"string": "Are you sure you want to delete {name}?"
|
||||
|
@ -4216,6 +4375,14 @@
|
|||
"src_dot_productTypes_dot_views_dot_ProductTypeUpdate_dot_3512959355": {
|
||||
"string": "Product type deleted"
|
||||
},
|
||||
"src_dot_productTypes_dot_views_dot_ProductTypeUpdate_dot_404238501": {
|
||||
"context": "dialog header",
|
||||
"string": "Unassign Attribute From Product Type"
|
||||
},
|
||||
"src_dot_productTypes_dot_views_dot_ProductTypeUpdate_dot_766918870": {
|
||||
"context": "dialog header",
|
||||
"string": "Unassign Attribute from Product Type"
|
||||
},
|
||||
"src_dot_productTypes_dot_views_dot_ProductTypeUpdate_dot_870815507": {
|
||||
"context": "unassign attribute from product type, button",
|
||||
"string": "Unassign"
|
||||
|
@ -5901,7 +6068,7 @@
|
|||
},
|
||||
"src_dot_utils_dot_errors_dot_notFound": {
|
||||
"context": "error message",
|
||||
"string": "Invoice not found"
|
||||
"string": "Page not found."
|
||||
},
|
||||
"src_dot_utils_dot_errors_dot_notReady": {
|
||||
"context": "error message",
|
||||
|
|
41
package-lock.json
generated
41
package-lock.json
generated
|
@ -12580,7 +12580,8 @@
|
|||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
|
@ -12598,11 +12599,13 @@
|
|||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
|
@ -12615,15 +12618,18 @@
|
|||
},
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
|
@ -12726,7 +12732,8 @@
|
|||
},
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
|
@ -12736,6 +12743,7 @@
|
|||
"is-fullwidth-code-point": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
|
@ -12748,17 +12756,20 @@
|
|||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
|
@ -12775,6 +12786,7 @@
|
|||
"mkdirp": {
|
||||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
|
@ -12847,7 +12859,8 @@
|
|||
},
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
|
@ -12857,6 +12870,7 @@
|
|||
"once": {
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
|
@ -12932,7 +12946,8 @@
|
|||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
|
@ -12962,6 +12977,7 @@
|
|||
"string-width": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
|
@ -12979,6 +12995,7 @@
|
|||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
|
@ -13017,11 +13034,13 @@
|
|||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.3",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
329
schema.graphql
329
schema.graphql
|
@ -394,6 +394,7 @@ type Attribute implements Node & ObjectWithMetadata {
|
|||
inputType: AttributeInputTypeEnum
|
||||
name: String
|
||||
slug: String
|
||||
type: AttributeTypeEnum
|
||||
values: [AttributeValue]
|
||||
valueRequired: Boolean!
|
||||
visibleInStorefront: Boolean!
|
||||
|
@ -404,21 +405,10 @@ type Attribute implements Node & ObjectWithMetadata {
|
|||
storefrontSearchPosition: Int!
|
||||
}
|
||||
|
||||
type AttributeAssign {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
productType: ProductType
|
||||
productErrors: [ProductError!]!
|
||||
}
|
||||
|
||||
input AttributeAssignInput {
|
||||
id: ID!
|
||||
type: AttributeTypeEnum!
|
||||
}
|
||||
|
||||
type AttributeBulkDelete {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
count: Int!
|
||||
productErrors: [ProductError!]!
|
||||
attributeErrors: [AttributeError!]!
|
||||
}
|
||||
|
||||
type AttributeCountableConnection {
|
||||
|
@ -435,13 +425,14 @@ type AttributeCountableEdge {
|
|||
type AttributeCreate {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
attribute: Attribute
|
||||
productErrors: [ProductError!]!
|
||||
attributeErrors: [AttributeError!]!
|
||||
}
|
||||
|
||||
input AttributeCreateInput {
|
||||
inputType: AttributeInputTypeEnum
|
||||
name: String!
|
||||
slug: String
|
||||
type: AttributeTypeEnum!
|
||||
values: [AttributeValueCreateInput]
|
||||
valueRequired: Boolean
|
||||
isVariantOnly: Boolean
|
||||
|
@ -454,10 +445,25 @@ input AttributeCreateInput {
|
|||
|
||||
type AttributeDelete {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
productErrors: [ProductError!]!
|
||||
attributeErrors: [AttributeError!]!
|
||||
attribute: Attribute
|
||||
}
|
||||
|
||||
type AttributeError {
|
||||
field: String
|
||||
message: String
|
||||
code: AttributeErrorCode!
|
||||
}
|
||||
|
||||
enum AttributeErrorCode {
|
||||
ALREADY_EXISTS
|
||||
GRAPHQL_ERROR
|
||||
INVALID
|
||||
NOT_FOUND
|
||||
REQUIRED
|
||||
UNIQUE
|
||||
}
|
||||
|
||||
input AttributeFilterInput {
|
||||
valueRequired: Boolean
|
||||
isVariantOnly: Boolean
|
||||
|
@ -467,6 +473,7 @@ input AttributeFilterInput {
|
|||
availableInGrid: Boolean
|
||||
search: String
|
||||
ids: [ID]
|
||||
type: AttributeTypeEnum
|
||||
inCollection: ID
|
||||
inCategory: ID
|
||||
channel: String
|
||||
|
@ -486,7 +493,7 @@ enum AttributeInputTypeEnum {
|
|||
type AttributeReorderValues {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
attribute: Attribute
|
||||
productErrors: [ProductError!]!
|
||||
attributeErrors: [AttributeError!]!
|
||||
}
|
||||
|
||||
enum AttributeSortField {
|
||||
|
@ -526,20 +533,14 @@ type AttributeTranslation implements Node {
|
|||
}
|
||||
|
||||
enum AttributeTypeEnum {
|
||||
PRODUCT
|
||||
VARIANT
|
||||
}
|
||||
|
||||
type AttributeUnassign {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
productType: ProductType
|
||||
productErrors: [ProductError!]!
|
||||
PRODUCT_TYPE
|
||||
PAGE_TYPE
|
||||
}
|
||||
|
||||
type AttributeUpdate {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
attribute: Attribute
|
||||
productErrors: [ProductError!]!
|
||||
attributeErrors: [AttributeError!]!
|
||||
}
|
||||
|
||||
input AttributeUpdateInput {
|
||||
|
@ -568,13 +569,13 @@ type AttributeValue implements Node {
|
|||
type AttributeValueBulkDelete {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
count: Int!
|
||||
productErrors: [ProductError!]!
|
||||
attributeErrors: [AttributeError!]!
|
||||
}
|
||||
|
||||
type AttributeValueCreate {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
attribute: Attribute
|
||||
productErrors: [ProductError!]!
|
||||
attributeErrors: [AttributeError!]!
|
||||
attributeValue: AttributeValue
|
||||
}
|
||||
|
||||
|
@ -585,7 +586,7 @@ input AttributeValueCreateInput {
|
|||
type AttributeValueDelete {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
attribute: Attribute
|
||||
productErrors: [ProductError!]!
|
||||
attributeErrors: [AttributeError!]!
|
||||
attributeValue: AttributeValue
|
||||
}
|
||||
|
||||
|
@ -623,7 +624,7 @@ enum AttributeValueType {
|
|||
type AttributeValueUpdate {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
attribute: Attribute
|
||||
productErrors: [ProductError!]!
|
||||
attributeErrors: [AttributeError!]!
|
||||
attributeValue: AttributeValue
|
||||
}
|
||||
|
||||
|
@ -1244,6 +1245,7 @@ type CollectionTranslation implements Node {
|
|||
type CollectionUpdate {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
collectionErrors: [CollectionError!]!
|
||||
productErrors: [ProductError!]!
|
||||
collection: Collection
|
||||
}
|
||||
|
||||
|
@ -2561,19 +2563,8 @@ type Mutation {
|
|||
shippingZoneDelete(id: ID!): ShippingZoneDelete
|
||||
shippingZoneBulkDelete(ids: [ID]!): ShippingZoneBulkDelete
|
||||
shippingZoneUpdate(id: ID!, input: ShippingZoneUpdateInput!): ShippingZoneUpdate
|
||||
attributeCreate(input: AttributeCreateInput!): AttributeCreate
|
||||
attributeDelete(id: ID!): AttributeDelete
|
||||
attributeBulkDelete(ids: [ID]!): AttributeBulkDelete
|
||||
attributeAssign(operations: [AttributeAssignInput]!, productTypeId: ID!): AttributeAssign
|
||||
attributeUnassign(attributeIds: [ID]!, productTypeId: ID!): AttributeUnassign
|
||||
attributeUpdate(id: ID!, input: AttributeUpdateInput!): AttributeUpdate
|
||||
attributeTranslate(id: ID!, input: NameTranslationInput!, languageCode: LanguageCodeEnum!): AttributeTranslate
|
||||
attributeValueCreate(attribute: ID!, input: AttributeValueCreateInput!): AttributeValueCreate
|
||||
attributeValueDelete(id: ID!): AttributeValueDelete
|
||||
attributeValueBulkDelete(ids: [ID]!): AttributeValueBulkDelete
|
||||
attributeValueUpdate(id: ID!, input: AttributeValueCreateInput!): AttributeValueUpdate
|
||||
attributeValueTranslate(id: ID!, input: NameTranslationInput!, languageCode: LanguageCodeEnum!): AttributeValueTranslate
|
||||
attributeReorderValues(attributeId: ID!, moves: [ReorderInput]!): AttributeReorderValues
|
||||
productAttributeAssign(operations: [ProductAttributeAssignInput]!, productTypeId: ID!): ProductAttributeAssign
|
||||
productAttributeUnassign(attributeIds: [ID]!, productTypeId: ID!): ProductAttributeUnassign
|
||||
categoryCreate(input: CategoryInput!, parent: ID): CategoryCreate
|
||||
categoryDelete(id: ID!): CategoryDelete
|
||||
categoryBulkDelete(ids: [ID]!): CategoryBulkDelete
|
||||
|
@ -2594,6 +2585,7 @@ type Mutation {
|
|||
productUpdate(id: ID!, input: ProductInput!): ProductUpdate
|
||||
productTranslate(id: ID!, input: TranslationInput!, languageCode: LanguageCodeEnum!): ProductTranslate
|
||||
productChannelListingUpdate(id: ID!, input: ProductChannelListingUpdateInput!): ProductChannelListingUpdate
|
||||
productSetAvailabilityForPurchase(isAvailable: Boolean!, productId: ID!, startDate: Date): ProductSetAvailabilityForPurchase
|
||||
productImageCreate(input: ProductImageCreateInput!): ProductImageCreate
|
||||
productVariantReorder(moves: [ReorderInput]!, productId: ID!): ProductVariantReorder
|
||||
productImageDelete(id: ID!): ProductImageDelete
|
||||
|
@ -2605,6 +2597,10 @@ type Mutation {
|
|||
productTypeBulkDelete(ids: [ID]!): ProductTypeBulkDelete
|
||||
productTypeUpdate(id: ID!, input: ProductTypeInput!): ProductTypeUpdate
|
||||
productTypeReorderAttributes(moves: [ReorderInput]!, productTypeId: ID!, type: AttributeTypeEnum!): ProductTypeReorderAttributes
|
||||
productTypeUpdateMetadata(id: ID!, input: MetaInput!): ProductTypeUpdateMeta @deprecated(reason: "Use the `updateMetadata` mutation instead. This field will be removed after 2020-07-31.")
|
||||
productTypeClearMetadata(id: ID!, input: MetaPath!): ProductTypeClearMeta @deprecated(reason: "Use the `deleteMetadata` mutation instead. This field will be removed after 2020-07-31.")
|
||||
productTypeUpdatePrivateMetadata(id: ID!, input: MetaInput!): ProductTypeUpdatePrivateMeta @deprecated(reason: "Use the `updatePrivateMetadata` mutation instead. This field will be removed after 2020-07-31.")
|
||||
productTypeClearPrivateMetadata(id: ID!, input: MetaPath!): ProductTypeClearPrivateMeta @deprecated(reason: "Use the `deletePrivateMetadata` mutation instead. This field will be removed after 2020-07-31.")
|
||||
digitalContentCreate(input: DigitalContentUploadInput!, variantId: ID!): DigitalContentCreate
|
||||
digitalContentDelete(variantId: ID!): DigitalContentDelete
|
||||
digitalContentUpdate(input: DigitalContentInput!, variantId: ID!): DigitalContentUpdate
|
||||
|
@ -2626,12 +2622,19 @@ type Mutation {
|
|||
paymentRefund(amount: PositiveDecimal, paymentId: ID!): PaymentRefund
|
||||
paymentVoid(paymentId: ID!): PaymentVoid
|
||||
paymentInitialize(gateway: String!, paymentData: JSONString): PaymentInitialize
|
||||
pageCreate(input: PageInput!): PageCreate
|
||||
pageCreate(input: PageCreateInput!): PageCreate
|
||||
pageDelete(id: ID!): PageDelete
|
||||
pageBulkDelete(ids: [ID]!): PageBulkDelete
|
||||
pageBulkPublish(ids: [ID]!, isPublished: Boolean!): PageBulkPublish
|
||||
pageUpdate(id: ID!, input: PageInput!): PageUpdate
|
||||
pageTranslate(id: ID!, input: PageTranslationInput!, languageCode: LanguageCodeEnum!): PageTranslate
|
||||
pageTypeCreate(input: PageTypeCreateInput!): PageTypeCreate
|
||||
pageTypeUpdate(id: ID, input: PageTypeUpdateInput!): PageTypeUpdate
|
||||
pageTypeDelete(id: ID!): PageTypeDelete
|
||||
pageTypeBulkDelete(ids: [ID!]!): PageTypeBulkDelete
|
||||
pageAttributeAssign(attributeIds: [ID!]!, pageTypeId: ID!): PageAttributeAssign
|
||||
pageAttributeUnassign(attributeIds: [ID!]!, pageTypeId: ID!): PageAttributeUnassign
|
||||
pageTypeReorderAttributes(moves: [ReorderInput!]!, pageTypeId: ID!): PageTypeReorderAttributes
|
||||
draftOrderComplete(id: ID!): DraftOrderComplete
|
||||
draftOrderCreate(input: DraftOrderCreateInput!): DraftOrderCreate
|
||||
draftOrderDelete(id: ID!): DraftOrderDelete
|
||||
|
@ -2716,6 +2719,17 @@ type Mutation {
|
|||
channelDelete(id: ID!, input: ChannelDeleteInput!): ChannelDelete
|
||||
channelActivate(id: ID!): ChannelActivate
|
||||
channelDeactivate(id: ID!): ChannelDeactivate
|
||||
attributeCreate(input: AttributeCreateInput!): AttributeCreate
|
||||
attributeDelete(id: ID!): AttributeDelete
|
||||
attributeUpdate(id: ID!, input: AttributeUpdateInput!): AttributeUpdate
|
||||
attributeTranslate(id: ID!, input: NameTranslationInput!, languageCode: LanguageCodeEnum!): AttributeTranslate
|
||||
attributeBulkDelete(ids: [ID]!): AttributeBulkDelete
|
||||
attributeValueBulkDelete(ids: [ID]!): AttributeValueBulkDelete
|
||||
attributeValueCreate(attribute: ID!, input: AttributeValueCreateInput!): AttributeValueCreate
|
||||
attributeValueDelete(id: ID!): AttributeValueDelete
|
||||
attributeValueUpdate(id: ID!, input: AttributeValueCreateInput!): AttributeValueUpdate
|
||||
attributeValueTranslate(id: ID!, input: NameTranslationInput!, languageCode: LanguageCodeEnum!): AttributeValueTranslate
|
||||
attributeReorderValues(attributeId: ID!, moves: [ReorderInput]!): AttributeReorderValues
|
||||
appCreate(input: AppInput!): AppCreate
|
||||
appUpdate(id: ID!, input: AppInput!): AppUpdate
|
||||
appDelete(id: ID!): AppDelete
|
||||
|
@ -2761,6 +2775,11 @@ type Mutation {
|
|||
userAvatarUpdate(image: Upload!): UserAvatarUpdate
|
||||
userAvatarDelete: UserAvatarDelete
|
||||
userBulkSetActive(ids: [ID]!, isActive: Boolean!): UserBulkSetActive
|
||||
serviceAccountCreate(input: ServiceAccountInput!): ServiceAccountCreate @deprecated(reason: "Use the `appCreate` mutation instead. This field will be removed after 2020-07-31.")
|
||||
serviceAccountUpdate(id: ID!, input: ServiceAccountInput!): ServiceAccountUpdate @deprecated(reason: "Use the `appUpdate` mutation instead. This field will be removed after 2020-07-31.")
|
||||
serviceAccountDelete(id: ID!): ServiceAccountDelete @deprecated(reason: "Use the `appDelete` mutation instead. This field will be removed after 2020-07-31.")
|
||||
serviceAccountTokenCreate(input: ServiceAccountTokenInput!): ServiceAccountTokenCreate @deprecated(reason: "Use the `appTokenCreate` mutation instead. This field will be removed after 2020-07-31.")
|
||||
serviceAccountTokenDelete(id: ID!): ServiceAccountTokenDelete @deprecated(reason: "Use the `appTokenDelete` mutation instead. This field will be removed after 2020-07-31.")
|
||||
permissionGroupCreate(input: PermissionGroupCreateInput!): PermissionGroupCreate
|
||||
permissionGroupUpdate(id: ID!, input: PermissionGroupUpdateInput!): PermissionGroupUpdate
|
||||
permissionGroupDelete(id: ID!): PermissionGroupDelete
|
||||
|
@ -3149,10 +3168,24 @@ type Page implements Node & ObjectWithMetadata {
|
|||
publicationDate: Date
|
||||
isPublished: Boolean!
|
||||
slug: String!
|
||||
pageType: PageType!
|
||||
created: DateTime!
|
||||
privateMetadata: [MetadataItem]!
|
||||
metadata: [MetadataItem]!
|
||||
translation(languageCode: LanguageCodeEnum!): PageTranslation
|
||||
attributes: [SelectedAttribute!]!
|
||||
}
|
||||
|
||||
type PageAttributeAssign {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
pageType: PageType
|
||||
pageErrors: [PageError!]!
|
||||
}
|
||||
|
||||
type PageAttributeUnassign {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
pageType: PageType
|
||||
pageErrors: [PageError!]!
|
||||
}
|
||||
|
||||
type PageBulkDelete {
|
||||
|
@ -3184,6 +3217,18 @@ type PageCreate {
|
|||
page: Page
|
||||
}
|
||||
|
||||
input PageCreateInput {
|
||||
slug: String
|
||||
title: String
|
||||
content: String
|
||||
contentJson: JSONString
|
||||
attributes: [AttributeValueInput!]
|
||||
isPublished: Boolean
|
||||
publicationDate: String
|
||||
seo: SeoInput
|
||||
pageType: ID!
|
||||
}
|
||||
|
||||
type PageDelete {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
pageErrors: [PageError!]!
|
||||
|
@ -3194,6 +3239,7 @@ type PageError {
|
|||
field: String
|
||||
message: String
|
||||
code: PageErrorCode!
|
||||
attributes: [ID!]
|
||||
}
|
||||
|
||||
enum PageErrorCode {
|
||||
|
@ -3202,6 +3248,8 @@ enum PageErrorCode {
|
|||
NOT_FOUND
|
||||
REQUIRED
|
||||
UNIQUE
|
||||
DUPLICATED_INPUT_ITEM
|
||||
ATTRIBUTE_ALREADY_ASSIGNED
|
||||
}
|
||||
|
||||
input PageFilterInput {
|
||||
|
@ -3220,6 +3268,7 @@ input PageInput {
|
|||
title: String
|
||||
content: String
|
||||
contentJson: JSONString
|
||||
attributes: [AttributeValueInput!]
|
||||
isPublished: Boolean
|
||||
publicationDate: String
|
||||
seo: SeoInput
|
||||
|
@ -3273,6 +3322,85 @@ input PageTranslationInput {
|
|||
contentJson: JSONString
|
||||
}
|
||||
|
||||
type PageType implements Node & ObjectWithMetadata {
|
||||
id: ID!
|
||||
name: String!
|
||||
slug: String!
|
||||
privateMetadata: [MetadataItem]!
|
||||
metadata: [MetadataItem]!
|
||||
attributes: [Attribute]
|
||||
availableAttributes(filter: AttributeFilterInput, before: String, after: String, first: Int, last: Int): AttributeCountableConnection
|
||||
hasPages: Boolean
|
||||
}
|
||||
|
||||
type PageTypeBulkDelete {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
count: Int!
|
||||
pageErrors: [PageError!]!
|
||||
}
|
||||
|
||||
type PageTypeCountableConnection {
|
||||
pageInfo: PageInfo!
|
||||
edges: [PageTypeCountableEdge!]!
|
||||
totalCount: Int
|
||||
}
|
||||
|
||||
type PageTypeCountableEdge {
|
||||
node: PageType!
|
||||
cursor: String!
|
||||
}
|
||||
|
||||
type PageTypeCreate {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
pageErrors: [PageError!]!
|
||||
pageType: PageType
|
||||
}
|
||||
|
||||
input PageTypeCreateInput {
|
||||
name: String
|
||||
slug: String
|
||||
addAttributes: [ID!]
|
||||
}
|
||||
|
||||
type PageTypeDelete {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
pageErrors: [PageError!]!
|
||||
pageType: PageType
|
||||
}
|
||||
|
||||
input PageTypeFilterInput {
|
||||
search: String
|
||||
}
|
||||
|
||||
type PageTypeReorderAttributes {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
pageType: PageType
|
||||
pageErrors: [PageError!]!
|
||||
}
|
||||
|
||||
enum PageTypeSortField {
|
||||
NAME
|
||||
SLUG
|
||||
}
|
||||
|
||||
input PageTypeSortingInput {
|
||||
direction: OrderDirection!
|
||||
field: PageTypeSortField!
|
||||
}
|
||||
|
||||
type PageTypeUpdate {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
pageErrors: [PageError!]!
|
||||
pageType: PageType
|
||||
}
|
||||
|
||||
input PageTypeUpdateInput {
|
||||
name: String
|
||||
slug: String
|
||||
addAttributes: [ID!]
|
||||
removeAttributes: [ID!]
|
||||
}
|
||||
|
||||
type PageUpdate {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
pageErrors: [PageError!]!
|
||||
|
@ -3414,6 +3542,7 @@ enum PermissionEnum {
|
|||
MANAGE_MENUS
|
||||
MANAGE_ORDERS
|
||||
MANAGE_PAGES
|
||||
MANAGE_PAGE_TYPES_AND_ATTRIBUTES
|
||||
MANAGE_PRODUCTS
|
||||
MANAGE_PRODUCT_TYPES_AND_ATTRIBUTES
|
||||
MANAGE_SHIPPING
|
||||
|
@ -3586,6 +3715,28 @@ type Product implements Node & ObjectWithMetadata {
|
|||
isAvailableForPurchase: Boolean
|
||||
}
|
||||
|
||||
type ProductAttributeAssign {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
productType: ProductType
|
||||
productErrors: [ProductError!]!
|
||||
}
|
||||
|
||||
input ProductAttributeAssignInput {
|
||||
id: ID!
|
||||
type: ProductAttributeType!
|
||||
}
|
||||
|
||||
enum ProductAttributeType {
|
||||
PRODUCT
|
||||
VARIANT
|
||||
}
|
||||
|
||||
type ProductAttributeUnassign {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
productType: ProductType
|
||||
productErrors: [ProductError!]!
|
||||
}
|
||||
|
||||
type ProductBulkDelete {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
count: Int!
|
||||
|
@ -4150,8 +4301,6 @@ type Query {
|
|||
shippingZones(channel: String, before: String, after: String, first: Int, last: Int): ShippingZoneCountableConnection
|
||||
digitalContent(id: ID!): DigitalContent
|
||||
digitalContents(before: String, after: String, first: Int, last: Int): DigitalContentCountableConnection
|
||||
attributes(filter: AttributeFilterInput, sortBy: AttributeSortingInput, before: String, after: String, first: Int, last: Int): AttributeCountableConnection
|
||||
attribute(id: ID!): Attribute
|
||||
categories(filter: CategoryFilterInput, sortBy: CategorySortingInput, level: Int, before: String, after: String, first: Int, last: Int): CategoryCountableConnection
|
||||
category(id: ID, slug: String): Category
|
||||
collection(id: ID, slug: String, channel: String): Collection
|
||||
|
@ -4167,6 +4316,8 @@ type Query {
|
|||
payments(before: String, after: String, first: Int, last: Int): PaymentCountableConnection
|
||||
page(id: ID, slug: String): Page
|
||||
pages(sortBy: PageSortingInput, filter: PageFilterInput, before: String, after: String, first: Int, last: Int): PageCountableConnection
|
||||
pageType(id: ID!): PageType
|
||||
pageTypes(sortBy: PageTypeSortingInput, filter: PageTypeFilterInput, before: String, after: String, first: Int, last: Int): PageTypeCountableConnection
|
||||
homepageEvents(before: String, after: String, first: Int, last: Int): OrderEventCountableConnection
|
||||
order(id: ID!): Order
|
||||
orders(sortBy: OrderSortingInput, filter: OrderFilterInput, created: ReportingPeriod, status: OrderStatusFilter, channel: String, before: String, after: String, first: Int, last: Int): OrderCountableConnection
|
||||
|
@ -4194,6 +4345,8 @@ type Query {
|
|||
checkoutLines(before: String, after: String, first: Int, last: Int): CheckoutLineCountableConnection
|
||||
channel(id: ID): Channel
|
||||
channels: [Channel!]
|
||||
attributes(filter: AttributeFilterInput, sortBy: AttributeSortingInput, before: String, after: String, first: Int, last: Int): AttributeCountableConnection
|
||||
attribute(id: ID!): Attribute
|
||||
appsInstallations: [AppInstallation!]!
|
||||
apps(filter: AppFilterInput, sortBy: AppSortingInput, before: String, after: String, first: Int, last: Int): AppCountableConnection
|
||||
app(id: ID!): App
|
||||
|
@ -4393,6 +4546,92 @@ input SeoInput {
|
|||
description: String
|
||||
}
|
||||
|
||||
type ServiceAccount implements Node & ObjectWithMetadata {
|
||||
id: ID!
|
||||
name: String
|
||||
created: DateTime
|
||||
isActive: Boolean
|
||||
permissions: [Permission]
|
||||
tokens: [ServiceAccountToken]
|
||||
privateMetadata: [MetadataItem]!
|
||||
metadata: [MetadataItem]!
|
||||
}
|
||||
|
||||
type ServiceAccountCountableConnection {
|
||||
pageInfo: PageInfo!
|
||||
edges: [ServiceAccountCountableEdge!]!
|
||||
totalCount: Int
|
||||
}
|
||||
|
||||
type ServiceAccountCountableEdge {
|
||||
node: ServiceAccount!
|
||||
cursor: String!
|
||||
}
|
||||
|
||||
type ServiceAccountCreate {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
authToken: String
|
||||
accountErrors: [AccountError!]!
|
||||
serviceAccount: ServiceAccount
|
||||
}
|
||||
|
||||
type ServiceAccountDelete {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
accountErrors: [AccountError!]!
|
||||
serviceAccount: ServiceAccount
|
||||
}
|
||||
|
||||
input ServiceAccountFilterInput {
|
||||
search: String
|
||||
isActive: Boolean
|
||||
}
|
||||
|
||||
input ServiceAccountInput {
|
||||
name: String
|
||||
isActive: Boolean
|
||||
permissions: [PermissionEnum]
|
||||
}
|
||||
|
||||
enum ServiceAccountSortField {
|
||||
NAME
|
||||
CREATION_DATE
|
||||
}
|
||||
|
||||
input ServiceAccountSortingInput {
|
||||
direction: OrderDirection!
|
||||
field: ServiceAccountSortField!
|
||||
}
|
||||
|
||||
type ServiceAccountToken implements Node {
|
||||
name: String
|
||||
authToken: String
|
||||
id: ID!
|
||||
}
|
||||
|
||||
type ServiceAccountTokenCreate {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
authToken: String
|
||||
accountErrors: [AccountError!]!
|
||||
serviceAccountToken: ServiceAccountToken
|
||||
}
|
||||
|
||||
type ServiceAccountTokenDelete {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
accountErrors: [AccountError!]!
|
||||
serviceAccountToken: ServiceAccountToken
|
||||
}
|
||||
|
||||
input ServiceAccountTokenInput {
|
||||
name: String
|
||||
serviceAccount: ID!
|
||||
}
|
||||
|
||||
type ServiceAccountUpdate {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
accountErrors: [AccountError!]!
|
||||
serviceAccount: ServiceAccount
|
||||
}
|
||||
|
||||
type SetPassword {
|
||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||
token: String
|
||||
|
@ -5517,7 +5756,7 @@ enum WeightUnitsEnum {
|
|||
|
||||
scalar _Any
|
||||
|
||||
union _Entity = Address | User | Group | App | ProductVariant | Product | ProductType | Collection | Category | ProductImage
|
||||
union _Entity = Address | User | Group | ServiceAccount | App | ProductVariant | Product | ProductType | Collection | Category | ProductImage | PageType
|
||||
|
||||
type _Service {
|
||||
sdl: String
|
||||
|
|
|
@ -5,10 +5,11 @@ import CardTitle from "@saleor/components/CardTitle";
|
|||
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
|
||||
import FormSpacer from "@saleor/components/FormSpacer";
|
||||
import SingleSelectField from "@saleor/components/SingleSelectField";
|
||||
import { ProductErrorFragment } from "@saleor/fragments/types/ProductErrorFragment";
|
||||
import { AttributeErrorFragment } from "@saleor/fragments/types/AttributeErrorFragment";
|
||||
import { commonMessages } from "@saleor/intl";
|
||||
import { AttributeInputTypeEnum } from "@saleor/types/globalTypes";
|
||||
import { getFormErrors, getProductErrorMessage } from "@saleor/utils/errors";
|
||||
import { getFormErrors } from "@saleor/utils/errors";
|
||||
import getAttributeErrorMessage from "@saleor/utils/errors/attribute";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
import slugify from "slugify";
|
||||
|
@ -20,7 +21,7 @@ export interface AttributeDetailsProps {
|
|||
canChangeType: boolean;
|
||||
data: AttributePageFormData;
|
||||
disabled: boolean;
|
||||
errors: ProductErrorFragment[];
|
||||
errors: AttributeErrorFragment[];
|
||||
onChange: (event: React.ChangeEvent<any>) => void;
|
||||
}
|
||||
|
||||
|
@ -66,7 +67,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({
|
|||
})}
|
||||
name={"name" as keyof AttributePageFormData}
|
||||
fullWidth
|
||||
helperText={getProductErrorMessage(formErrors.name, intl)}
|
||||
helperText={getAttributeErrorMessage(formErrors.name, intl)}
|
||||
value={data.name}
|
||||
onChange={onChange}
|
||||
/>
|
||||
|
@ -97,7 +98,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({
|
|||
choices={inputTypeChoices}
|
||||
disabled={disabled || !canChangeType}
|
||||
error={!!formErrors.inputType}
|
||||
hint={getProductErrorMessage(formErrors.inputType, intl)}
|
||||
hint={getAttributeErrorMessage(formErrors.inputType, intl)}
|
||||
label={intl.formatMessage({
|
||||
defaultMessage: "Catalog Input type for Store Owner",
|
||||
description: "attribute's editor component"
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
import { makeStyles } from "@material-ui/core";
|
||||
import Card from "@material-ui/core/Card";
|
||||
import CardContent from "@material-ui/core/CardContent";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import CardTitle from "@saleor/components/CardTitle";
|
||||
import RadioGroupField from "@saleor/components/RadioGroupField";
|
||||
import { AttributeTypeEnum } from "@saleor/types/globalTypes";
|
||||
import React from "react";
|
||||
import { defineMessages, FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import { AttributePageFormData } from "../AttributePage";
|
||||
|
||||
export interface AttributeOrganizationProps {
|
||||
canChangeType: boolean;
|
||||
data: AttributePageFormData;
|
||||
disabled: boolean;
|
||||
onChange: (event: React.ChangeEvent<any>) => void;
|
||||
}
|
||||
|
||||
const messages = defineMessages({
|
||||
contentAttribute: {
|
||||
defaultMessage: "Content Attribute",
|
||||
description: "attribute type"
|
||||
},
|
||||
productAttribute: {
|
||||
defaultMessage: "Product Attribute",
|
||||
description: "attribute type"
|
||||
}
|
||||
});
|
||||
|
||||
const useStyles = makeStyles(
|
||||
theme => ({
|
||||
card: {
|
||||
overflow: "visible"
|
||||
},
|
||||
cardSubtitle: {
|
||||
fontSize: "1rem",
|
||||
marginBottom: theme.spacing(0.5)
|
||||
},
|
||||
label: {
|
||||
marginBottom: theme.spacing(0.5)
|
||||
}
|
||||
}),
|
||||
{ name: "AttributeOrganization" }
|
||||
);
|
||||
|
||||
const AttributeOrganization: React.FC<AttributeOrganizationProps> = props => {
|
||||
const { canChangeType, data, disabled, onChange } = props;
|
||||
|
||||
const classes = useStyles(props);
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardTitle
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Organization",
|
||||
description: "section header"
|
||||
})}
|
||||
/>
|
||||
<CardContent>
|
||||
{canChangeType ? (
|
||||
<RadioGroupField
|
||||
choices={[
|
||||
{
|
||||
label: intl.formatMessage(messages.productAttribute),
|
||||
value: AttributeTypeEnum.PRODUCT_TYPE
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage(messages.contentAttribute),
|
||||
value: AttributeTypeEnum.PAGE_TYPE
|
||||
}
|
||||
]}
|
||||
disabled={disabled}
|
||||
label={
|
||||
<>
|
||||
<FormattedMessage defaultMessage="Attribute Class" />
|
||||
<Typography variant="caption">
|
||||
<FormattedMessage defaultMessage="Define where this attribute should be used in Saleor system" />
|
||||
</Typography>
|
||||
</>
|
||||
}
|
||||
name={"type" as keyof FormData}
|
||||
value={data.type}
|
||||
onChange={onChange}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<Typography className={classes.label} variant="caption">
|
||||
<FormattedMessage defaultMessage="Attribute Class" />
|
||||
</Typography>
|
||||
<Typography>
|
||||
{data.type === AttributeTypeEnum.PRODUCT_TYPE
|
||||
? intl.formatMessage(messages.productAttribute)
|
||||
: intl.formatMessage(messages.contentAttribute)}
|
||||
</Typography>
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
AttributeOrganization.displayName = "AttributeOrganization";
|
||||
export default AttributeOrganization;
|
2
src/attributes/components/AttributeOrganization/index.ts
Normal file
2
src/attributes/components/AttributeOrganization/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./AttributeOrganization";
|
||||
export * from "./AttributeOrganization";
|
|
@ -12,11 +12,14 @@ import {
|
|||
AttributeDetailsFragment,
|
||||
AttributeDetailsFragment_values
|
||||
} from "@saleor/fragments/types/AttributeDetailsFragment";
|
||||
import { ProductErrorFragment } from "@saleor/fragments/types/ProductErrorFragment";
|
||||
import { AttributeErrorFragment } from "@saleor/fragments/types/AttributeErrorFragment";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { maybe } from "@saleor/misc";
|
||||
import { ReorderAction } from "@saleor/types";
|
||||
import { AttributeInputTypeEnum } from "@saleor/types/globalTypes";
|
||||
import {
|
||||
AttributeInputTypeEnum,
|
||||
AttributeTypeEnum
|
||||
} from "@saleor/types/globalTypes";
|
||||
import { mapMetadataItemToInput } from "@saleor/utils/maps";
|
||||
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
|
||||
import React from "react";
|
||||
|
@ -24,13 +27,14 @@ import { useIntl } from "react-intl";
|
|||
import slugify from "slugify";
|
||||
|
||||
import AttributeDetails from "../AttributeDetails";
|
||||
import AttributeOrganization from "../AttributeOrganization";
|
||||
import AttributeProperties from "../AttributeProperties";
|
||||
import AttributeValues from "../AttributeValues";
|
||||
|
||||
export interface AttributePageProps {
|
||||
attribute: AttributeDetailsFragment | null;
|
||||
disabled: boolean;
|
||||
errors: ProductErrorFragment[];
|
||||
errors: AttributeErrorFragment[];
|
||||
saveButtonBarState: ConfirmButtonTransitionState;
|
||||
values: AttributeDetailsFragment_values[];
|
||||
onBack: () => void;
|
||||
|
@ -43,6 +47,7 @@ export interface AttributePageProps {
|
|||
}
|
||||
|
||||
export interface AttributePageFormData extends MetadataFormData {
|
||||
type: AttributeTypeEnum;
|
||||
availableInGrid: boolean;
|
||||
filterableInDashboard: boolean;
|
||||
inputType: AttributeInputTypeEnum;
|
||||
|
@ -87,6 +92,7 @@ const AttributePage: React.FC<AttributePageProps> = ({
|
|||
privateMetadata: [],
|
||||
slug: "",
|
||||
storefrontSearchPosition: "",
|
||||
type: AttributeTypeEnum.PRODUCT_TYPE,
|
||||
valueRequired: true,
|
||||
visibleInStorefront: true
|
||||
}
|
||||
|
@ -114,6 +120,7 @@ const AttributePage: React.FC<AttributePageProps> = ({
|
|||
() => attribute.storefrontSearchPosition.toString(),
|
||||
""
|
||||
),
|
||||
type: attribute?.type || AttributeTypeEnum.PRODUCT_TYPE,
|
||||
valueRequired: maybe(() => attribute.valueRequired, true),
|
||||
visibleInStorefront: maybe(() => attribute.visibleInStorefront, true)
|
||||
};
|
||||
|
@ -125,12 +132,14 @@ const AttributePage: React.FC<AttributePageProps> = ({
|
|||
!attribute || isPrivateMetadataModified
|
||||
? data.privateMetadata
|
||||
: undefined;
|
||||
const type = attribute === null ? data.type : undefined;
|
||||
|
||||
return onSubmit({
|
||||
...data,
|
||||
metadata,
|
||||
privateMetadata,
|
||||
slug: data.slug || slugify(data.name).toLowerCase()
|
||||
slug: data.slug || slugify(data.name).toLowerCase(),
|
||||
type
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -176,6 +185,13 @@ const AttributePage: React.FC<AttributePageProps> = ({
|
|||
<Metadata data={data} onChange={changeMetadata} />
|
||||
</div>
|
||||
<div>
|
||||
<AttributeOrganization
|
||||
canChangeType={attribute === null}
|
||||
data={data}
|
||||
disabled={disabled}
|
||||
onChange={change}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<AttributeProperties
|
||||
data={data}
|
||||
errors={errors}
|
||||
|
|
|
@ -5,11 +5,14 @@ import Typography from "@material-ui/core/Typography";
|
|||
import CardSpacer from "@saleor/components/CardSpacer";
|
||||
import CardTitle from "@saleor/components/CardTitle";
|
||||
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
|
||||
import ControlledSwitch from "@saleor/components/ControlledSwitch";
|
||||
import FormSpacer from "@saleor/components/FormSpacer";
|
||||
import Hr from "@saleor/components/Hr";
|
||||
import { ProductErrorFragment } from "@saleor/fragments/types/ProductErrorFragment";
|
||||
import { AttributeErrorFragment } from "@saleor/fragments/types/AttributeErrorFragment";
|
||||
import { commonMessages } from "@saleor/intl";
|
||||
import { getFormErrors, getProductErrorMessage } from "@saleor/utils/errors";
|
||||
import { AttributeTypeEnum } from "@saleor/types/globalTypes";
|
||||
import { getFormErrors } from "@saleor/utils/errors";
|
||||
import getAttributeErrorMessage from "@saleor/utils/errors/attribute";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
|
@ -18,7 +21,7 @@ import { AttributePageFormData } from "../AttributePage";
|
|||
export interface AttributePropertiesProps {
|
||||
data: AttributePageFormData;
|
||||
disabled: boolean;
|
||||
errors: ProductErrorFragment[];
|
||||
errors: AttributeErrorFragment[];
|
||||
onChange: (event: React.ChangeEvent<any>) => void;
|
||||
}
|
||||
|
||||
|
@ -74,42 +77,54 @@ const AttributeProperties: React.FC<AttributePropertiesProps> = ({
|
|||
/>
|
||||
</Typography>
|
||||
<Hr />
|
||||
<ControlledCheckbox
|
||||
name={"filterableInStorefront" as keyof FormData}
|
||||
label={intl.formatMessage({
|
||||
defaultMessage: "Use in Faceted Navigation",
|
||||
description: "attribute is filterable in storefront"
|
||||
})}
|
||||
checked={data.filterableInStorefront}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<FormSpacer />
|
||||
{data.filterableInStorefront && (
|
||||
<TextField
|
||||
disabled={disabled}
|
||||
error={!!formErrors.storefrontSearchPosition}
|
||||
fullWidth
|
||||
helperText={getProductErrorMessage(
|
||||
formErrors.storefrontSearchPosition,
|
||||
intl
|
||||
)}
|
||||
name={"storefrontSearchPosition" as keyof AttributePageFormData}
|
||||
label={intl.formatMessage({
|
||||
defaultMessage: "Position in faceted navigation",
|
||||
description: "attribute position in storefront filters"
|
||||
})}
|
||||
value={data.storefrontSearchPosition}
|
||||
onChange={onChange}
|
||||
/>
|
||||
{data.type === AttributeTypeEnum.PRODUCT_TYPE && (
|
||||
<>
|
||||
<ControlledCheckbox
|
||||
name={"filterableInStorefront" as keyof FormData}
|
||||
label={intl.formatMessage({
|
||||
defaultMessage: "Use in Faceted Navigation",
|
||||
description: "attribute is filterable in storefront"
|
||||
})}
|
||||
checked={data.filterableInStorefront}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<FormSpacer />
|
||||
</>
|
||||
)}
|
||||
{data.filterableInStorefront &&
|
||||
data.type === AttributeTypeEnum.PRODUCT_TYPE && (
|
||||
<TextField
|
||||
disabled={disabled}
|
||||
error={!!formErrors.storefrontSearchPosition}
|
||||
fullWidth
|
||||
helperText={getAttributeErrorMessage(
|
||||
formErrors.storefrontSearchPosition,
|
||||
intl
|
||||
)}
|
||||
name={"storefrontSearchPosition" as keyof AttributePageFormData}
|
||||
label={intl.formatMessage({
|
||||
defaultMessage: "Position in faceted navigation",
|
||||
description: "attribute position in storefront filters"
|
||||
})}
|
||||
value={data.storefrontSearchPosition}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)}
|
||||
<FormSpacer />
|
||||
<ControlledCheckbox
|
||||
<ControlledSwitch
|
||||
name={"visibleInStorefront" as keyof FormData}
|
||||
label={intl.formatMessage({
|
||||
defaultMessage: "Visible on Product Page in Storefront",
|
||||
description: "attribute"
|
||||
})}
|
||||
label={
|
||||
<>
|
||||
<FormattedMessage
|
||||
defaultMessage="Public"
|
||||
description="attribute visibility in storefront"
|
||||
/>
|
||||
<Typography variant="caption">
|
||||
<FormattedMessage defaultMessage="If enabled, attribute will be accessible to customers." />
|
||||
</Typography>
|
||||
</>
|
||||
}
|
||||
checked={data.visibleInStorefront}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
|
|
|
@ -9,7 +9,7 @@ import ConfirmButton, {
|
|||
ConfirmButtonTransitionState
|
||||
} from "@saleor/components/ConfirmButton";
|
||||
import Form from "@saleor/components/Form";
|
||||
import { ProductErrorFragment } from "@saleor/fragments/types/ProductErrorFragment";
|
||||
import { AttributeErrorFragment } from "@saleor/fragments/types/AttributeErrorFragment";
|
||||
import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors";
|
||||
import { buttonMessages } from "@saleor/intl";
|
||||
import { maybe } from "@saleor/misc";
|
||||
|
@ -26,7 +26,7 @@ export interface AttributeValueEditDialogProps {
|
|||
attributeValue: AttributeDetails_attribute_values | null;
|
||||
confirmButtonState: ConfirmButtonTransitionState;
|
||||
disabled: boolean;
|
||||
errors: ProductErrorFragment[];
|
||||
errors: AttributeErrorFragment[];
|
||||
open: boolean;
|
||||
onSubmit: (data: AttributeValueEditDialogFormData) => void;
|
||||
onClose: () => void;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { ProductErrorFragment } from "@saleor/fragments/types/ProductErrorFragment";
|
||||
import { ProductErrorCode } from "@saleor/types/globalTypes";
|
||||
import { getProductErrorMessage } from "@saleor/utils/errors";
|
||||
import { AttributeErrorFragment } from "@saleor/fragments/types/AttributeErrorFragment";
|
||||
import { AttributeErrorCode } from "@saleor/types/globalTypes";
|
||||
import getAttributeErrorMessage from "@saleor/utils/errors/attribute";
|
||||
import { defineMessages, IntlShape } from "react-intl";
|
||||
|
||||
const messages = defineMessages({
|
||||
|
@ -13,25 +13,25 @@ const messages = defineMessages({
|
|||
});
|
||||
|
||||
export function getAttributeSlugErrorMessage(
|
||||
err: ProductErrorFragment,
|
||||
err: AttributeErrorFragment,
|
||||
intl: IntlShape
|
||||
): string {
|
||||
switch (err?.code) {
|
||||
case ProductErrorCode.UNIQUE:
|
||||
case AttributeErrorCode.UNIQUE:
|
||||
return intl.formatMessage(messages.attributeSlugUnique);
|
||||
default:
|
||||
return getProductErrorMessage(err, intl);
|
||||
return getAttributeErrorMessage(err, intl);
|
||||
}
|
||||
}
|
||||
|
||||
export function getAttributeValueErrorMessage(
|
||||
err: ProductErrorFragment,
|
||||
err: AttributeErrorFragment,
|
||||
intl: IntlShape
|
||||
): string {
|
||||
switch (err?.code) {
|
||||
case ProductErrorCode.ALREADY_EXISTS:
|
||||
case AttributeErrorCode.ALREADY_EXISTS:
|
||||
return intl.formatMessage(messages.attributeValueAlreadyExists);
|
||||
default:
|
||||
return getProductErrorMessage(err, intl);
|
||||
return getAttributeErrorMessage(err, intl);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import { AttributeDetailsFragment } from "@saleor/fragments/types/AttributeDetai
|
|||
import { ProductDetails_product_productType_variantAttributes } from "@saleor/products/types/ProductDetails";
|
||||
import {
|
||||
AttributeInputTypeEnum,
|
||||
AttributeTypeEnum,
|
||||
AttributeValueType
|
||||
} from "@saleor/types/globalTypes";
|
||||
|
||||
|
@ -25,6 +26,7 @@ export const attribute: AttributeDetailsFragment = {
|
|||
privateMetadata: [],
|
||||
slug: "author",
|
||||
storefrontSearchPosition: 2,
|
||||
type: AttributeTypeEnum.PRODUCT_TYPE,
|
||||
valueRequired: true,
|
||||
values: [
|
||||
{
|
||||
|
@ -55,6 +57,7 @@ export const attributes: Array<AttributeList_attributes_edges_node &
|
|||
id: "UHJvZHVjdEF0dHJpYnV0ZTo5",
|
||||
name: "Author",
|
||||
slug: "author",
|
||||
type: AttributeTypeEnum.PRODUCT_TYPE,
|
||||
values: [
|
||||
{
|
||||
__typename: "AttributeValue" as "AttributeValue",
|
||||
|
@ -86,6 +89,7 @@ export const attributes: Array<AttributeList_attributes_edges_node &
|
|||
id: "UHJvZHVjdEF0dHJpYnV0ZTo2",
|
||||
name: "Box Size",
|
||||
slug: "box-size",
|
||||
type: AttributeTypeEnum.PRODUCT_TYPE,
|
||||
values: [
|
||||
{
|
||||
__typename: "AttributeValue" as "AttributeValue",
|
||||
|
@ -135,6 +139,7 @@ export const attributes: Array<AttributeList_attributes_edges_node &
|
|||
id: "UHJvZHVjdEF0dHJpYnV0ZToz",
|
||||
name: "Brand",
|
||||
slug: "brand",
|
||||
type: AttributeTypeEnum.PRODUCT_TYPE,
|
||||
values: [
|
||||
{
|
||||
__typename: "AttributeValue" as "AttributeValue",
|
||||
|
@ -157,6 +162,7 @@ export const attributes: Array<AttributeList_attributes_edges_node &
|
|||
id: "UHJvZHVjdEF0dHJpYnV0ZTo4",
|
||||
name: "Candy Box Size",
|
||||
slug: "candy-box-size",
|
||||
type: AttributeTypeEnum.PRODUCT_TYPE,
|
||||
values: [
|
||||
{
|
||||
__typename: "AttributeValue" as "AttributeValue",
|
||||
|
@ -197,6 +203,7 @@ export const attributes: Array<AttributeList_attributes_edges_node &
|
|||
id: "UHJvZHVjdEF0dHJpYnV0ZTo1",
|
||||
name: "Coffee Genre",
|
||||
slug: "coffee-genre",
|
||||
type: AttributeTypeEnum.PRODUCT_TYPE,
|
||||
values: [
|
||||
{
|
||||
__typename: "AttributeValue" as "AttributeValue",
|
||||
|
@ -228,6 +235,7 @@ export const attributes: Array<AttributeList_attributes_edges_node &
|
|||
id: "UHJvZHVjdEF0dHJpYnV0ZToy",
|
||||
name: "Collar",
|
||||
slug: "collar",
|
||||
type: AttributeTypeEnum.PRODUCT_TYPE,
|
||||
values: [
|
||||
{
|
||||
__typename: "AttributeValue" as "AttributeValue",
|
||||
|
@ -268,6 +276,7 @@ export const attributes: Array<AttributeList_attributes_edges_node &
|
|||
id: "UHJvZHVjdEF0dHJpYnV0ZTox",
|
||||
name: "Color",
|
||||
slug: "color",
|
||||
type: AttributeTypeEnum.PRODUCT_TYPE,
|
||||
values: [
|
||||
{
|
||||
__typename: "AttributeValue" as "AttributeValue",
|
||||
|
@ -299,6 +308,7 @@ export const attributes: Array<AttributeList_attributes_edges_node &
|
|||
id: "UHJvZHVjdEF0dHJpYnV0ZToxMg==",
|
||||
name: "Cover",
|
||||
slug: "cover",
|
||||
type: AttributeTypeEnum.PRODUCT_TYPE,
|
||||
values: [
|
||||
{
|
||||
__typename: "AttributeValue" as "AttributeValue",
|
||||
|
@ -366,6 +376,7 @@ export const attributes: Array<AttributeList_attributes_edges_node &
|
|||
id: "UHJvZHVjdEF0dHJpYnV0ZTo3",
|
||||
name: "Flavor",
|
||||
slug: "flavor",
|
||||
type: AttributeTypeEnum.PRODUCT_TYPE,
|
||||
values: [
|
||||
{
|
||||
__typename: "AttributeValue" as "AttributeValue",
|
||||
|
@ -397,6 +408,7 @@ export const attributes: Array<AttributeList_attributes_edges_node &
|
|||
id: "UHJvZHVjdEF0dHJpYnV0ZToxMQ==",
|
||||
name: "Language",
|
||||
slug: "language",
|
||||
type: AttributeTypeEnum.PRODUCT_TYPE,
|
||||
values: [
|
||||
{
|
||||
__typename: "AttributeValue" as "AttributeValue",
|
||||
|
@ -428,6 +440,7 @@ export const attributes: Array<AttributeList_attributes_edges_node &
|
|||
id: "UHJvZHVjdEF0dHJpYnV0ZToxMA==",
|
||||
name: "Publisher",
|
||||
slug: "publisher",
|
||||
type: AttributeTypeEnum.PRODUCT_TYPE,
|
||||
values: [
|
||||
{
|
||||
__typename: "AttributeValue" as "AttributeValue",
|
||||
|
@ -459,6 +472,7 @@ export const attributes: Array<AttributeList_attributes_edges_node &
|
|||
id: "UHJvZHVjdEF0dHJpYnV0ZTo0",
|
||||
name: "Size",
|
||||
slug: "size",
|
||||
type: AttributeTypeEnum.PRODUCT_TYPE,
|
||||
values: [
|
||||
{
|
||||
__typename: "AttributeValue" as "AttributeValue",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { attributeDetailsFragment } from "@saleor/fragments/attributes";
|
||||
import { productErrorFragment } from "@saleor/fragments/errors";
|
||||
import { attributeErrorFragment } from "@saleor/fragments/errors";
|
||||
import makeMutation from "@saleor/hooks/makeMutation";
|
||||
import gql from "graphql-tag";
|
||||
|
||||
|
@ -37,11 +37,11 @@ import {
|
|||
} from "./types/AttributeValueUpdate";
|
||||
|
||||
const attributeBulkDelete = gql`
|
||||
${productErrorFragment}
|
||||
${attributeErrorFragment}
|
||||
mutation AttributeBulkDelete($ids: [ID!]!) {
|
||||
attributeBulkDelete(ids: $ids) {
|
||||
errors: productErrors {
|
||||
...ProductErrorFragment
|
||||
errors: attributeErrors {
|
||||
...AttributeErrorFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,11 +52,11 @@ export const useAttributeBulkDeleteMutation = makeMutation<
|
|||
>(attributeBulkDelete);
|
||||
|
||||
const attributeDelete = gql`
|
||||
${productErrorFragment}
|
||||
${attributeErrorFragment}
|
||||
mutation AttributeDelete($id: ID!) {
|
||||
attributeDelete(id: $id) {
|
||||
errors: productErrors {
|
||||
...ProductErrorFragment
|
||||
errors: attributeErrors {
|
||||
...AttributeErrorFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -68,14 +68,14 @@ export const useAttributeDeleteMutation = makeMutation<
|
|||
|
||||
export const attributeUpdateMutation = gql`
|
||||
${attributeDetailsFragment}
|
||||
${productErrorFragment}
|
||||
${attributeErrorFragment}
|
||||
mutation AttributeUpdate($id: ID!, $input: AttributeUpdateInput!) {
|
||||
attributeUpdate(id: $id, input: $input) {
|
||||
attribute {
|
||||
...AttributeDetailsFragment
|
||||
}
|
||||
errors: productErrors {
|
||||
...ProductErrorFragment
|
||||
errors: attributeErrors {
|
||||
...AttributeErrorFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -87,14 +87,14 @@ export const useAttributeUpdateMutation = makeMutation<
|
|||
|
||||
const attributeValueDelete = gql`
|
||||
${attributeDetailsFragment}
|
||||
${productErrorFragment}
|
||||
${attributeErrorFragment}
|
||||
mutation AttributeValueDelete($id: ID!) {
|
||||
attributeValueDelete(id: $id) {
|
||||
attribute {
|
||||
...AttributeDetailsFragment
|
||||
}
|
||||
errors: productErrors {
|
||||
...ProductErrorFragment
|
||||
errors: attributeErrors {
|
||||
...AttributeErrorFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -106,14 +106,14 @@ export const useAttributeValueDeleteMutation = makeMutation<
|
|||
|
||||
export const attributeValueUpdateMutation = gql`
|
||||
${attributeDetailsFragment}
|
||||
${productErrorFragment}
|
||||
${attributeErrorFragment}
|
||||
mutation AttributeValueUpdate($id: ID!, $input: AttributeValueCreateInput!) {
|
||||
attributeValueUpdate(id: $id, input: $input) {
|
||||
attribute {
|
||||
...AttributeDetailsFragment
|
||||
}
|
||||
errors: productErrors {
|
||||
...ProductErrorFragment
|
||||
errors: attributeErrors {
|
||||
...AttributeErrorFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -125,14 +125,14 @@ export const useAttributeValueUpdateMutation = makeMutation<
|
|||
|
||||
export const attributeValueCreateMutation = gql`
|
||||
${attributeDetailsFragment}
|
||||
${productErrorFragment}
|
||||
${attributeErrorFragment}
|
||||
mutation AttributeValueCreate($id: ID!, $input: AttributeValueCreateInput!) {
|
||||
attributeValueCreate(attribute: $id, input: $input) {
|
||||
attribute {
|
||||
...AttributeDetailsFragment
|
||||
}
|
||||
errors: productErrors {
|
||||
...ProductErrorFragment
|
||||
errors: attributeErrors {
|
||||
...AttributeErrorFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -144,14 +144,14 @@ export const useAttributeValueCreateMutation = makeMutation<
|
|||
|
||||
export const attributeCreateMutation = gql`
|
||||
${attributeDetailsFragment}
|
||||
${productErrorFragment}
|
||||
${attributeErrorFragment}
|
||||
mutation AttributeCreate($input: AttributeCreateInput!) {
|
||||
attributeCreate(input: $input) {
|
||||
attribute {
|
||||
...AttributeDetailsFragment
|
||||
}
|
||||
errors: productErrors {
|
||||
...ProductErrorFragment
|
||||
errors: attributeErrors {
|
||||
...AttributeErrorFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -162,7 +162,7 @@ export const useAttributeCreateMutation = makeMutation<
|
|||
>(attributeCreateMutation);
|
||||
|
||||
const attributeValueReorderMutation = gql`
|
||||
${productErrorFragment}
|
||||
${attributeErrorFragment}
|
||||
mutation AttributeValueReorder($id: ID!, $move: ReorderInput!) {
|
||||
attributeReorderValues(attributeId: $id, moves: [$move]) {
|
||||
attribute {
|
||||
|
@ -171,8 +171,8 @@ const attributeValueReorderMutation = gql`
|
|||
id
|
||||
}
|
||||
}
|
||||
errors: productErrors {
|
||||
...ProductErrorFragment
|
||||
errors: attributeErrors {
|
||||
...AttributeErrorFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,15 +2,15 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { ProductErrorCode } from "./../../types/globalTypes";
|
||||
import { AttributeErrorCode } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL mutation operation: AttributeBulkDelete
|
||||
// ====================================================
|
||||
|
||||
export interface AttributeBulkDelete_attributeBulkDelete_errors {
|
||||
__typename: "ProductError";
|
||||
code: ProductErrorCode;
|
||||
__typename: "AttributeError";
|
||||
code: AttributeErrorCode;
|
||||
field: string | null;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { AttributeCreateInput, AttributeInputTypeEnum, AttributeValueType, ProductErrorCode } from "./../../types/globalTypes";
|
||||
import { AttributeCreateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeValueType, AttributeErrorCode } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL mutation operation: AttributeCreate
|
||||
|
@ -33,6 +33,7 @@ export interface AttributeCreate_attributeCreate_attribute {
|
|||
id: string;
|
||||
name: string | null;
|
||||
slug: string | null;
|
||||
type: AttributeTypeEnum | null;
|
||||
visibleInStorefront: boolean;
|
||||
filterableInDashboard: boolean;
|
||||
filterableInStorefront: boolean;
|
||||
|
@ -46,8 +47,8 @@ export interface AttributeCreate_attributeCreate_attribute {
|
|||
}
|
||||
|
||||
export interface AttributeCreate_attributeCreate_errors {
|
||||
__typename: "ProductError";
|
||||
code: ProductErrorCode;
|
||||
__typename: "AttributeError";
|
||||
code: AttributeErrorCode;
|
||||
field: string | null;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,15 +2,15 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { ProductErrorCode } from "./../../types/globalTypes";
|
||||
import { AttributeErrorCode } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL mutation operation: AttributeDelete
|
||||
// ====================================================
|
||||
|
||||
export interface AttributeDelete_attributeDelete_errors {
|
||||
__typename: "ProductError";
|
||||
code: ProductErrorCode;
|
||||
__typename: "AttributeError";
|
||||
code: AttributeErrorCode;
|
||||
field: string | null;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { AttributeInputTypeEnum, AttributeValueType } from "./../../types/globalTypes";
|
||||
import { AttributeTypeEnum, AttributeInputTypeEnum, AttributeValueType } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: AttributeDetails
|
||||
|
@ -33,6 +33,7 @@ export interface AttributeDetails_attribute {
|
|||
id: string;
|
||||
name: string | null;
|
||||
slug: string | null;
|
||||
type: AttributeTypeEnum | null;
|
||||
visibleInStorefront: boolean;
|
||||
filterableInDashboard: boolean;
|
||||
filterableInStorefront: boolean;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { AttributeFilterInput, AttributeSortingInput } from "./../../types/globalTypes";
|
||||
import { AttributeFilterInput, AttributeSortingInput, AttributeTypeEnum } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: AttributeList
|
||||
|
@ -20,6 +20,7 @@ export interface AttributeList_attributes_edges_node {
|
|||
id: string;
|
||||
name: string | null;
|
||||
slug: string | null;
|
||||
type: AttributeTypeEnum | null;
|
||||
visibleInStorefront: boolean;
|
||||
filterableInDashboard: boolean;
|
||||
filterableInStorefront: boolean;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { AttributeUpdateInput, AttributeInputTypeEnum, AttributeValueType, ProductErrorCode } from "./../../types/globalTypes";
|
||||
import { AttributeUpdateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeValueType, AttributeErrorCode } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL mutation operation: AttributeUpdate
|
||||
|
@ -33,6 +33,7 @@ export interface AttributeUpdate_attributeUpdate_attribute {
|
|||
id: string;
|
||||
name: string | null;
|
||||
slug: string | null;
|
||||
type: AttributeTypeEnum | null;
|
||||
visibleInStorefront: boolean;
|
||||
filterableInDashboard: boolean;
|
||||
filterableInStorefront: boolean;
|
||||
|
@ -46,8 +47,8 @@ export interface AttributeUpdate_attributeUpdate_attribute {
|
|||
}
|
||||
|
||||
export interface AttributeUpdate_attributeUpdate_errors {
|
||||
__typename: "ProductError";
|
||||
code: ProductErrorCode;
|
||||
__typename: "AttributeError";
|
||||
code: AttributeErrorCode;
|
||||
field: string | null;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { AttributeValueCreateInput, AttributeInputTypeEnum, AttributeValueType, ProductErrorCode } from "./../../types/globalTypes";
|
||||
import { AttributeValueCreateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeValueType, AttributeErrorCode } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL mutation operation: AttributeValueCreate
|
||||
|
@ -33,6 +33,7 @@ export interface AttributeValueCreate_attributeValueCreate_attribute {
|
|||
id: string;
|
||||
name: string | null;
|
||||
slug: string | null;
|
||||
type: AttributeTypeEnum | null;
|
||||
visibleInStorefront: boolean;
|
||||
filterableInDashboard: boolean;
|
||||
filterableInStorefront: boolean;
|
||||
|
@ -46,8 +47,8 @@ export interface AttributeValueCreate_attributeValueCreate_attribute {
|
|||
}
|
||||
|
||||
export interface AttributeValueCreate_attributeValueCreate_errors {
|
||||
__typename: "ProductError";
|
||||
code: ProductErrorCode;
|
||||
__typename: "AttributeError";
|
||||
code: AttributeErrorCode;
|
||||
field: string | null;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { AttributeInputTypeEnum, AttributeValueType, ProductErrorCode } from "./../../types/globalTypes";
|
||||
import { AttributeTypeEnum, AttributeInputTypeEnum, AttributeValueType, AttributeErrorCode } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL mutation operation: AttributeValueDelete
|
||||
|
@ -33,6 +33,7 @@ export interface AttributeValueDelete_attributeValueDelete_attribute {
|
|||
id: string;
|
||||
name: string | null;
|
||||
slug: string | null;
|
||||
type: AttributeTypeEnum | null;
|
||||
visibleInStorefront: boolean;
|
||||
filterableInDashboard: boolean;
|
||||
filterableInStorefront: boolean;
|
||||
|
@ -46,8 +47,8 @@ export interface AttributeValueDelete_attributeValueDelete_attribute {
|
|||
}
|
||||
|
||||
export interface AttributeValueDelete_attributeValueDelete_errors {
|
||||
__typename: "ProductError";
|
||||
code: ProductErrorCode;
|
||||
__typename: "AttributeError";
|
||||
code: AttributeErrorCode;
|
||||
field: string | null;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { ReorderInput, ProductErrorCode } from "./../../types/globalTypes";
|
||||
import { ReorderInput, AttributeErrorCode } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL mutation operation: AttributeValueReorder
|
||||
|
@ -20,8 +20,8 @@ export interface AttributeValueReorder_attributeReorderValues_attribute {
|
|||
}
|
||||
|
||||
export interface AttributeValueReorder_attributeReorderValues_errors {
|
||||
__typename: "ProductError";
|
||||
code: ProductErrorCode;
|
||||
__typename: "AttributeError";
|
||||
code: AttributeErrorCode;
|
||||
field: string | null;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { AttributeValueCreateInput, AttributeInputTypeEnum, AttributeValueType, ProductErrorCode } from "./../../types/globalTypes";
|
||||
import { AttributeValueCreateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeValueType, AttributeErrorCode } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL mutation operation: AttributeValueUpdate
|
||||
|
@ -33,6 +33,7 @@ export interface AttributeValueUpdate_attributeValueUpdate_attribute {
|
|||
id: string;
|
||||
name: string | null;
|
||||
slug: string | null;
|
||||
type: AttributeTypeEnum | null;
|
||||
visibleInStorefront: boolean;
|
||||
filterableInDashboard: boolean;
|
||||
filterableInStorefront: boolean;
|
||||
|
@ -46,8 +47,8 @@ export interface AttributeValueUpdate_attributeValueUpdate_attribute {
|
|||
}
|
||||
|
||||
export interface AttributeValueUpdate_attributeValueUpdate_errors {
|
||||
__typename: "ProductError";
|
||||
code: ProductErrorCode;
|
||||
__typename: "AttributeError";
|
||||
code: AttributeErrorCode;
|
||||
field: string | null;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { ProductErrorFragment } from "@saleor/fragments/types/ProductErrorFragment";
|
||||
import { AttributeErrorFragment } from "@saleor/fragments/types/AttributeErrorFragment";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import useNotifier from "@saleor/hooks/useNotifier";
|
||||
import { getStringOrPlaceholder } from "@saleor/misc";
|
||||
import { ReorderEvent } from "@saleor/types";
|
||||
import { ProductErrorCode } from "@saleor/types/globalTypes";
|
||||
import { AttributeErrorCode } from "@saleor/types/globalTypes";
|
||||
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
|
||||
import createMetadataCreateHandler from "@saleor/utils/handlers/metadataCreateHandler";
|
||||
import {
|
||||
|
@ -41,9 +41,9 @@ interface AttributeDetailsProps {
|
|||
params: AttributeAddUrlQueryParams;
|
||||
}
|
||||
|
||||
const attributeValueAlreadyExistsError: ProductErrorFragment = {
|
||||
__typename: "ProductError",
|
||||
code: ProductErrorCode.ALREADY_EXISTS,
|
||||
const attributeValueAlreadyExistsError: AttributeErrorFragment = {
|
||||
__typename: "AttributeError",
|
||||
code: AttributeErrorCode.ALREADY_EXISTS,
|
||||
field: "name"
|
||||
};
|
||||
|
||||
|
@ -62,9 +62,9 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ params }) => {
|
|||
const [values, setValues] = React.useState<
|
||||
AttributeValueEditDialogFormData[]
|
||||
>([]);
|
||||
const [valueErrors, setValueErrors] = React.useState<ProductErrorFragment[]>(
|
||||
[]
|
||||
);
|
||||
const [valueErrors, setValueErrors] = React.useState<
|
||||
AttributeErrorFragment[]
|
||||
>([]);
|
||||
|
||||
const [attributeCreate, attributeCreateOpts] = useAttributeCreateMutation({
|
||||
onCompleted: data => {
|
||||
|
|
|
@ -3,7 +3,7 @@ import useNotifier from "@saleor/hooks/useNotifier";
|
|||
import { commonMessages } from "@saleor/intl";
|
||||
import { maybe } from "@saleor/misc";
|
||||
import { ReorderEvent } from "@saleor/types";
|
||||
import { getProductErrorMessage } from "@saleor/utils/errors";
|
||||
import getAttributeErrorMessage from "@saleor/utils/errors/attribute";
|
||||
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
|
||||
import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler";
|
||||
import { move } from "@saleor/utils/lists";
|
||||
|
@ -140,7 +140,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ id, params }) => {
|
|||
if (data.attributeReorderValues.errors.length !== 0) {
|
||||
notify({
|
||||
status: "error",
|
||||
text: getProductErrorMessage(
|
||||
text: getAttributeErrorMessage(
|
||||
data.attributeReorderValues.errors[0],
|
||||
intl
|
||||
)
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { attributes } from "@saleor/attributes/fixtures";
|
||||
import { fetchMoreProps } from "@saleor/fixtures";
|
||||
import AssignAttributeDialog, {
|
||||
AssignAttributeDialogProps
|
||||
} from "@saleor/productTypes/components/AssignAttributeDialog";
|
||||
} from "@saleor/components/AssignAttributeDialog";
|
||||
import { fetchMoreProps } from "@saleor/fixtures";
|
||||
import { formError } from "@saleor/storybook/misc";
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import React from "react";
|
||||
|
||||
import Decorator from "../../Decorator";
|
||||
import Decorator from "../../storybook/Decorator";
|
||||
|
||||
const props: AssignAttributeDialogProps = {
|
||||
...fetchMoreProps,
|
|
@ -16,6 +16,7 @@ import ConfirmButton, {
|
|||
ConfirmButtonTransitionState
|
||||
} from "@saleor/components/ConfirmButton";
|
||||
import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
||||
import { AvailableAttributeFragment } from "@saleor/fragments/types/AvailableAttributeFragment";
|
||||
import useElementScroll, {
|
||||
isScrolledToBottom
|
||||
} from "@saleor/hooks/useElementScroll";
|
||||
|
@ -30,8 +31,6 @@ import React from "react";
|
|||
import InfiniteScroll from "react-infinite-scroller";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import { SearchAvailableAttributes_productType_availableAttributes_edges_node } from "../../hooks/useAvailableAttributeSearch/types/SearchAvailableAttributes";
|
||||
|
||||
const useStyles = makeStyles(
|
||||
theme => ({
|
||||
actions: {
|
||||
|
@ -63,7 +62,7 @@ export interface AssignAttributeDialogProps extends FetchMoreProps {
|
|||
confirmButtonState: ConfirmButtonTransitionState;
|
||||
errors: string[];
|
||||
open: boolean;
|
||||
attributes: SearchAvailableAttributes_productType_availableAttributes_edges_node[];
|
||||
attributes: AvailableAttributeFragment[];
|
||||
selected: string[];
|
||||
onClose: () => void;
|
||||
onFetch: (query: string) => void;
|
|
@ -0,0 +1,21 @@
|
|||
import AttributeUnassignDialog, {
|
||||
AttributeUnassignDialogProps
|
||||
} from "@saleor/components/AttributeUnassignDialog";
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import React from "react";
|
||||
|
||||
import Decorator from "../../storybook/Decorator";
|
||||
|
||||
const props: AttributeUnassignDialogProps = {
|
||||
attributeName: "Size",
|
||||
confirmButtonState: "default",
|
||||
itemTypeName: "Shoes",
|
||||
onClose: () => undefined,
|
||||
onConfirm: () => undefined,
|
||||
open: true,
|
||||
title: "Unassign Attribute from Shoes"
|
||||
};
|
||||
|
||||
storiesOf("Generics / Unassign attribute", module)
|
||||
.addDecorator(Decorator)
|
||||
.add("default", () => <AttributeUnassignDialog {...props} />);
|
|
@ -0,0 +1,45 @@
|
|||
import DialogContentText from "@material-ui/core/DialogContentText";
|
||||
import ActionDialog from "@saleor/components/ActionDialog";
|
||||
import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
|
||||
import React from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
export interface AttributeUnassignDialogProps {
|
||||
title: string;
|
||||
attributeName: string;
|
||||
confirmButtonState: ConfirmButtonTransitionState;
|
||||
open: boolean;
|
||||
itemTypeName: string;
|
||||
onClose: () => void;
|
||||
onConfirm: () => void;
|
||||
}
|
||||
|
||||
const AttributeUnassignDialog: React.FC<AttributeUnassignDialogProps> = ({
|
||||
title,
|
||||
attributeName,
|
||||
confirmButtonState,
|
||||
open,
|
||||
itemTypeName,
|
||||
onClose,
|
||||
onConfirm
|
||||
}) => (
|
||||
<ActionDialog
|
||||
confirmButtonState={confirmButtonState}
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
onConfirm={onConfirm}
|
||||
title={title}
|
||||
>
|
||||
<DialogContentText>
|
||||
<FormattedMessage
|
||||
defaultMessage="Are you sure you want to unassign {attributeName} from {itemTypeName}?"
|
||||
values={{
|
||||
attributeName: <strong>{attributeName}</strong>,
|
||||
itemTypeName: <strong>{itemTypeName}</strong>
|
||||
}}
|
||||
/>
|
||||
</DialogContentText>
|
||||
</ActionDialog>
|
||||
);
|
||||
AttributeUnassignDialog.displayName = "AttributeUnassignDialog";
|
||||
export default AttributeUnassignDialog;
|
2
src/components/AttributeUnassignDialog/index.ts
Normal file
2
src/components/AttributeUnassignDialog/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./AttributeUnassignDialog";
|
||||
export * from "./AttributeUnassignDialog";
|
|
@ -0,0 +1,21 @@
|
|||
import BulkAttributeUnassignDialog, {
|
||||
BulkAttributeUnassignDialogProps
|
||||
} from "@saleor/components/BulkAttributeUnassignDialog";
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import React from "react";
|
||||
|
||||
import Decorator from "../../storybook/Decorator";
|
||||
|
||||
const props: BulkAttributeUnassignDialogProps = {
|
||||
attributeQuantity: 4,
|
||||
confirmButtonState: "default",
|
||||
itemTypeName: "Shoes",
|
||||
onClose: () => undefined,
|
||||
onConfirm: () => undefined,
|
||||
open: true,
|
||||
title: "Unassign Attribute from Shoes"
|
||||
};
|
||||
|
||||
storiesOf("Generics / Unassign multiple attributes", module)
|
||||
.addDecorator(Decorator)
|
||||
.add("default", () => <BulkAttributeUnassignDialog {...props} />);
|
|
@ -0,0 +1,47 @@
|
|||
import DialogContentText from "@material-ui/core/DialogContentText";
|
||||
import ActionDialog from "@saleor/components/ActionDialog";
|
||||
import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
|
||||
import React from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
export interface BulkAttributeUnassignDialogProps {
|
||||
title: string;
|
||||
attributeQuantity: number;
|
||||
confirmButtonState: ConfirmButtonTransitionState;
|
||||
open: boolean;
|
||||
itemTypeName: string;
|
||||
onClose: () => void;
|
||||
onConfirm: () => void;
|
||||
}
|
||||
|
||||
const BulkAttributeUnassignDialog: React.FC<BulkAttributeUnassignDialogProps> = ({
|
||||
title,
|
||||
attributeQuantity,
|
||||
confirmButtonState,
|
||||
open,
|
||||
itemTypeName,
|
||||
onClose,
|
||||
onConfirm
|
||||
}) => (
|
||||
<ActionDialog
|
||||
confirmButtonState={confirmButtonState}
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
onConfirm={onConfirm}
|
||||
title={title}
|
||||
>
|
||||
<DialogContentText>
|
||||
<FormattedMessage
|
||||
defaultMessage="{counter,plural,one{Are you sure you want to unassign this attribute from {itemTypeName}?} other{Are you sure you want to unassign {attributeQuantity} attributes from {itemTypeName}?}}"
|
||||
description="unassign multiple attributes from item"
|
||||
values={{
|
||||
attributeQuantity: <strong>{attributeQuantity}</strong>,
|
||||
counter: attributeQuantity,
|
||||
itemTypeName: <strong>{itemTypeName}</strong>
|
||||
}}
|
||||
/>
|
||||
</DialogContentText>
|
||||
</ActionDialog>
|
||||
);
|
||||
BulkAttributeUnassignDialog.displayName = "BulkAttributeUnassignDialog";
|
||||
export default BulkAttributeUnassignDialog;
|
2
src/components/BulkAttributeUnassignDialog/index.ts
Normal file
2
src/components/BulkAttributeUnassignDialog/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./BulkAttributeUnassignDialog";
|
||||
export * from "./BulkAttributeUnassignDialog";
|
|
@ -50,7 +50,7 @@ interface RadioGroupFieldProps {
|
|||
disabled?: boolean;
|
||||
error?: boolean;
|
||||
hint?: string;
|
||||
label?: string;
|
||||
label?: React.ReactNode;
|
||||
name?: string;
|
||||
value: string | number;
|
||||
onChange: (event: React.ChangeEvent<any>) => void;
|
||||
|
|
|
@ -7,6 +7,7 @@ import Attributes from "@saleor/icons/Attributes";
|
|||
import Channels from "@saleor/icons/Channels";
|
||||
import Navigation from "@saleor/icons/Navigation";
|
||||
import Pages from "@saleor/icons/Pages";
|
||||
import PageTypes from "@saleor/icons/PageTypes";
|
||||
import PermissionGroups from "@saleor/icons/PermissionGroups";
|
||||
import Plugins from "@saleor/icons/Plugins";
|
||||
import ProductTypes from "@saleor/icons/ProductTypes";
|
||||
|
@ -19,6 +20,7 @@ import { sectionNames } from "@saleor/intl";
|
|||
import { maybe } from "@saleor/misc";
|
||||
import { menuListUrl } from "@saleor/navigation/urls";
|
||||
import { pageListUrl } from "@saleor/pages/urls";
|
||||
import { pageTypeListUrl } from "@saleor/pageTypes/urls";
|
||||
import { permissionGroupListUrl } from "@saleor/permissionGroups/urls";
|
||||
import { pluginListUrl } from "@saleor/plugins/urls";
|
||||
import { productTypeListUrl } from "@saleor/productTypes/urls";
|
||||
|
@ -151,6 +153,33 @@ export function createConfigurationMenu(intl: IntlShape): MenuSection[] {
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
defaultMessage: "Content Management"
|
||||
}),
|
||||
menuItems: [
|
||||
{
|
||||
description: intl.formatMessage({
|
||||
defaultMessage: "Define types of content pages used in your store",
|
||||
id: "configurationMenuPageTypes"
|
||||
}),
|
||||
icon: <PageTypes fontSize="inherit" viewBox="0 0 44 44" />,
|
||||
permission: PermissionEnum.MANAGE_PAGES,
|
||||
title: intl.formatMessage(sectionNames.pageTypes),
|
||||
url: pageTypeListUrl()
|
||||
},
|
||||
{
|
||||
description: intl.formatMessage({
|
||||
defaultMessage: "Manage and add additional pages",
|
||||
id: "configurationMenuPages"
|
||||
}),
|
||||
icon: <Pages fontSize="inherit" viewBox="0 0 44 44" />,
|
||||
permission: PermissionEnum.MANAGE_PAGES,
|
||||
title: intl.formatMessage(sectionNames.pages),
|
||||
url: pageListUrl()
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
defaultMessage: "Miscellaneous"
|
||||
|
@ -176,16 +205,6 @@ export function createConfigurationMenu(intl: IntlShape): MenuSection[] {
|
|||
title: intl.formatMessage(sectionNames.siteSettings),
|
||||
url: siteSettingsUrl()
|
||||
},
|
||||
{
|
||||
description: intl.formatMessage({
|
||||
defaultMessage: "Manage and add additional pages",
|
||||
id: "configurationMenuPages"
|
||||
}),
|
||||
icon: <Pages fontSize="inherit" viewBox="0 0 44 44" />,
|
||||
permission: PermissionEnum.MANAGE_PAGES,
|
||||
title: intl.formatMessage(sectionNames.pages),
|
||||
url: pageListUrl()
|
||||
},
|
||||
{
|
||||
description: intl.formatMessage({
|
||||
defaultMessage: "View and update your plugins and their settings.",
|
||||
|
|
|
@ -7,6 +7,7 @@ export const attributeFragment = gql`
|
|||
id
|
||||
name
|
||||
slug
|
||||
type
|
||||
visibleInStorefront
|
||||
filterableInDashboard
|
||||
filterableInStorefront
|
||||
|
@ -31,3 +32,11 @@ export const attributeDetailsFragment = gql`
|
|||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const availableAttributeFragment = gql`
|
||||
fragment AvailableAttributeFragment on Attribute {
|
||||
id
|
||||
name
|
||||
slug
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
import gql from "graphql-tag";
|
||||
|
||||
export const attributeErrorFragment = gql`
|
||||
fragment AttributeErrorFragment on AttributeError {
|
||||
code
|
||||
field
|
||||
}
|
||||
`;
|
||||
|
||||
export const productErrorFragment = gql`
|
||||
fragment ProductErrorFragment on ProductError {
|
||||
code
|
||||
|
@ -69,6 +76,14 @@ export const pageErrorFragment = gql`
|
|||
}
|
||||
`;
|
||||
|
||||
export const pageErrorWithAttributesFragment = gql`
|
||||
${pageErrorFragment}
|
||||
fragment PageErrorWithAttributesFragment on PageError {
|
||||
...PageErrorFragment
|
||||
attributes
|
||||
}
|
||||
`;
|
||||
|
||||
export const permissionGroupErrorFragment = gql`
|
||||
fragment PermissionGroupErrorFragment on PermissionGroupError {
|
||||
code
|
||||
|
|
24
src/fragments/pageTypes.ts
Normal file
24
src/fragments/pageTypes.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import gql from "graphql-tag";
|
||||
|
||||
import { attributeFragment } from "./attributes";
|
||||
import { metadataFragment } from "./metadata";
|
||||
|
||||
export const pageTypeFragment = gql`
|
||||
fragment PageTypeFragment on PageType {
|
||||
id
|
||||
name
|
||||
}
|
||||
`;
|
||||
|
||||
export const pageTypeDetailsFragment = gql`
|
||||
${attributeFragment}
|
||||
${pageTypeFragment}
|
||||
${metadataFragment}
|
||||
fragment PageTypeDetailsFragment on PageType {
|
||||
...PageTypeFragment
|
||||
...MetadataFragment
|
||||
attributes {
|
||||
...AttributeFragment
|
||||
}
|
||||
}
|
||||
`;
|
|
@ -11,11 +11,52 @@ export const pageFragment = gql`
|
|||
}
|
||||
`;
|
||||
|
||||
export const pageAttributesFragment = gql`
|
||||
fragment PageAttributesFragment on Page {
|
||||
attributes {
|
||||
attribute {
|
||||
id
|
||||
slug
|
||||
name
|
||||
inputType
|
||||
valueRequired
|
||||
values {
|
||||
id
|
||||
name
|
||||
slug
|
||||
}
|
||||
}
|
||||
values {
|
||||
id
|
||||
name
|
||||
slug
|
||||
}
|
||||
}
|
||||
pageType {
|
||||
id
|
||||
name
|
||||
attributes {
|
||||
id
|
||||
name
|
||||
inputType
|
||||
valueRequired
|
||||
values {
|
||||
id
|
||||
name
|
||||
slug
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const pageDetailsFragment = gql`
|
||||
${pageFragment}
|
||||
${pageAttributesFragment}
|
||||
${metadataFragment}
|
||||
fragment PageDetailsFragment on Page {
|
||||
...PageFragment
|
||||
...PageAttributesFragment
|
||||
...MetadataFragment
|
||||
contentJson
|
||||
seoTitle
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { AttributeInputTypeEnum, AttributeValueType } from "./../../types/globalTypes";
|
||||
import { AttributeTypeEnum, AttributeInputTypeEnum, AttributeValueType } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL fragment: AttributeDetailsFragment
|
||||
|
@ -33,6 +33,7 @@ export interface AttributeDetailsFragment {
|
|||
id: string;
|
||||
name: string | null;
|
||||
slug: string | null;
|
||||
type: AttributeTypeEnum | null;
|
||||
visibleInStorefront: boolean;
|
||||
filterableInDashboard: boolean;
|
||||
filterableInStorefront: boolean;
|
||||
|
|
15
src/fragments/types/AttributeErrorFragment.ts
Normal file
15
src/fragments/types/AttributeErrorFragment.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { AttributeErrorCode } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL fragment: AttributeErrorFragment
|
||||
// ====================================================
|
||||
|
||||
export interface AttributeErrorFragment {
|
||||
__typename: "AttributeError";
|
||||
code: AttributeErrorCode;
|
||||
field: string | null;
|
||||
}
|
|
@ -2,6 +2,8 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { AttributeTypeEnum } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL fragment: AttributeFragment
|
||||
// ====================================================
|
||||
|
@ -11,6 +13,7 @@ export interface AttributeFragment {
|
|||
id: string;
|
||||
name: string | null;
|
||||
slug: string | null;
|
||||
type: AttributeTypeEnum | null;
|
||||
visibleInStorefront: boolean;
|
||||
filterableInDashboard: boolean;
|
||||
filterableInStorefront: boolean;
|
||||
|
|
14
src/fragments/types/AvailableAttributeFragment.ts
Normal file
14
src/fragments/types/AvailableAttributeFragment.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
// ====================================================
|
||||
// GraphQL fragment: AvailableAttributeFragment
|
||||
// ====================================================
|
||||
|
||||
export interface AvailableAttributeFragment {
|
||||
__typename: "Attribute";
|
||||
id: string;
|
||||
name: string | null;
|
||||
slug: string | null;
|
||||
}
|
|
@ -19,7 +19,7 @@ export interface MetadataFragment_privateMetadata {
|
|||
}
|
||||
|
||||
export interface MetadataFragment {
|
||||
__typename: "App" | "ShippingZone" | "ShippingMethod" | "Product" | "ProductType" | "Attribute" | "Category" | "ProductVariant" | "DigitalContent" | "Collection" | "Page" | "User" | "Checkout" | "Order" | "Fulfillment" | "Invoice";
|
||||
__typename: "ServiceAccount" | "App" | "ShippingZone" | "ShippingMethod" | "Product" | "ProductType" | "Attribute" | "Category" | "ProductVariant" | "DigitalContent" | "Collection" | "Page" | "PageType" | "User" | "Checkout" | "Order" | "Fulfillment" | "Invoice";
|
||||
metadata: (MetadataFragment_metadata | null)[];
|
||||
privateMetadata: (MetadataFragment_privateMetadata | null)[];
|
||||
}
|
||||
|
|
68
src/fragments/types/PageAttributesFragment.ts
Normal file
68
src/fragments/types/PageAttributesFragment.ts
Normal file
|
@ -0,0 +1,68 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { AttributeInputTypeEnum } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL fragment: PageAttributesFragment
|
||||
// ====================================================
|
||||
|
||||
export interface PageAttributesFragment_attributes_attribute_values {
|
||||
__typename: "AttributeValue";
|
||||
id: string;
|
||||
name: string | null;
|
||||
slug: string | null;
|
||||
}
|
||||
|
||||
export interface PageAttributesFragment_attributes_attribute {
|
||||
__typename: "Attribute";
|
||||
id: string;
|
||||
slug: string | null;
|
||||
name: string | null;
|
||||
inputType: AttributeInputTypeEnum | null;
|
||||
valueRequired: boolean;
|
||||
values: (PageAttributesFragment_attributes_attribute_values | null)[] | null;
|
||||
}
|
||||
|
||||
export interface PageAttributesFragment_attributes_values {
|
||||
__typename: "AttributeValue";
|
||||
id: string;
|
||||
name: string | null;
|
||||
slug: string | null;
|
||||
}
|
||||
|
||||
export interface PageAttributesFragment_attributes {
|
||||
__typename: "SelectedAttribute";
|
||||
attribute: PageAttributesFragment_attributes_attribute;
|
||||
values: (PageAttributesFragment_attributes_values | null)[];
|
||||
}
|
||||
|
||||
export interface PageAttributesFragment_pageType_attributes_values {
|
||||
__typename: "AttributeValue";
|
||||
id: string;
|
||||
name: string | null;
|
||||
slug: string | null;
|
||||
}
|
||||
|
||||
export interface PageAttributesFragment_pageType_attributes {
|
||||
__typename: "Attribute";
|
||||
id: string;
|
||||
name: string | null;
|
||||
inputType: AttributeInputTypeEnum | null;
|
||||
valueRequired: boolean;
|
||||
values: (PageAttributesFragment_pageType_attributes_values | null)[] | null;
|
||||
}
|
||||
|
||||
export interface PageAttributesFragment_pageType {
|
||||
__typename: "PageType";
|
||||
id: string;
|
||||
name: string;
|
||||
attributes: (PageAttributesFragment_pageType_attributes | null)[] | null;
|
||||
}
|
||||
|
||||
export interface PageAttributesFragment {
|
||||
__typename: "Page";
|
||||
attributes: PageAttributesFragment_attributes[];
|
||||
pageType: PageAttributesFragment_pageType;
|
||||
}
|
|
@ -2,10 +2,65 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { AttributeInputTypeEnum } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL fragment: PageDetailsFragment
|
||||
// ====================================================
|
||||
|
||||
export interface PageDetailsFragment_attributes_attribute_values {
|
||||
__typename: "AttributeValue";
|
||||
id: string;
|
||||
name: string | null;
|
||||
slug: string | null;
|
||||
}
|
||||
|
||||
export interface PageDetailsFragment_attributes_attribute {
|
||||
__typename: "Attribute";
|
||||
id: string;
|
||||
slug: string | null;
|
||||
name: string | null;
|
||||
inputType: AttributeInputTypeEnum | null;
|
||||
valueRequired: boolean;
|
||||
values: (PageDetailsFragment_attributes_attribute_values | null)[] | null;
|
||||
}
|
||||
|
||||
export interface PageDetailsFragment_attributes_values {
|
||||
__typename: "AttributeValue";
|
||||
id: string;
|
||||
name: string | null;
|
||||
slug: string | null;
|
||||
}
|
||||
|
||||
export interface PageDetailsFragment_attributes {
|
||||
__typename: "SelectedAttribute";
|
||||
attribute: PageDetailsFragment_attributes_attribute;
|
||||
values: (PageDetailsFragment_attributes_values | null)[];
|
||||
}
|
||||
|
||||
export interface PageDetailsFragment_pageType_attributes_values {
|
||||
__typename: "AttributeValue";
|
||||
id: string;
|
||||
name: string | null;
|
||||
slug: string | null;
|
||||
}
|
||||
|
||||
export interface PageDetailsFragment_pageType_attributes {
|
||||
__typename: "Attribute";
|
||||
id: string;
|
||||
name: string | null;
|
||||
inputType: AttributeInputTypeEnum | null;
|
||||
valueRequired: boolean;
|
||||
values: (PageDetailsFragment_pageType_attributes_values | null)[] | null;
|
||||
}
|
||||
|
||||
export interface PageDetailsFragment_pageType {
|
||||
__typename: "PageType";
|
||||
id: string;
|
||||
name: string;
|
||||
attributes: (PageDetailsFragment_pageType_attributes | null)[] | null;
|
||||
}
|
||||
|
||||
export interface PageDetailsFragment_metadata {
|
||||
__typename: "MetadataItem";
|
||||
key: string;
|
||||
|
@ -24,6 +79,8 @@ export interface PageDetailsFragment {
|
|||
title: string;
|
||||
slug: string;
|
||||
isPublished: boolean;
|
||||
attributes: PageDetailsFragment_attributes[];
|
||||
pageType: PageDetailsFragment_pageType;
|
||||
metadata: (PageDetailsFragment_metadata | null)[];
|
||||
privateMetadata: (PageDetailsFragment_privateMetadata | null)[];
|
||||
contentJson: any;
|
||||
|
|
16
src/fragments/types/PageErrorWithAttributesFragment.ts
Normal file
16
src/fragments/types/PageErrorWithAttributesFragment.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { PageErrorCode } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL fragment: PageErrorWithAttributesFragment
|
||||
// ====================================================
|
||||
|
||||
export interface PageErrorWithAttributesFragment {
|
||||
__typename: "PageError";
|
||||
code: PageErrorCode;
|
||||
field: string | null;
|
||||
attributes: string[] | null;
|
||||
}
|
41
src/fragments/types/PageTypeDetailsFragment.ts
Normal file
41
src/fragments/types/PageTypeDetailsFragment.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { AttributeTypeEnum } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL fragment: PageTypeDetailsFragment
|
||||
// ====================================================
|
||||
|
||||
export interface PageTypeDetailsFragment_metadata {
|
||||
__typename: "MetadataItem";
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface PageTypeDetailsFragment_privateMetadata {
|
||||
__typename: "MetadataItem";
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface PageTypeDetailsFragment_attributes {
|
||||
__typename: "Attribute";
|
||||
id: string;
|
||||
name: string | null;
|
||||
slug: string | null;
|
||||
type: AttributeTypeEnum | null;
|
||||
visibleInStorefront: boolean;
|
||||
filterableInDashboard: boolean;
|
||||
filterableInStorefront: boolean;
|
||||
}
|
||||
|
||||
export interface PageTypeDetailsFragment {
|
||||
__typename: "PageType";
|
||||
id: string;
|
||||
name: string;
|
||||
metadata: (PageTypeDetailsFragment_metadata | null)[];
|
||||
privateMetadata: (PageTypeDetailsFragment_privateMetadata | null)[];
|
||||
attributes: (PageTypeDetailsFragment_attributes | null)[] | null;
|
||||
}
|
13
src/fragments/types/PageTypeFragment.ts
Normal file
13
src/fragments/types/PageTypeFragment.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
// ====================================================
|
||||
// GraphQL fragment: PageTypeFragment
|
||||
// ====================================================
|
||||
|
||||
export interface PageTypeFragment {
|
||||
__typename: "PageType";
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { WeightUnitsEnum } from "./../../types/globalTypes";
|
||||
import { AttributeTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL fragment: ProductTypeDetailsFragment
|
||||
|
@ -31,6 +31,7 @@ export interface ProductTypeDetailsFragment_productAttributes {
|
|||
id: string;
|
||||
name: string | null;
|
||||
slug: string | null;
|
||||
type: AttributeTypeEnum | null;
|
||||
visibleInStorefront: boolean;
|
||||
filterableInDashboard: boolean;
|
||||
filterableInStorefront: boolean;
|
||||
|
@ -41,6 +42,7 @@ export interface ProductTypeDetailsFragment_variantAttributes {
|
|||
id: string;
|
||||
name: string | null;
|
||||
slug: string | null;
|
||||
type: AttributeTypeEnum | null;
|
||||
visibleInStorefront: boolean;
|
||||
filterableInDashboard: boolean;
|
||||
filterableInStorefront: boolean;
|
||||
|
|
16
src/icons/PageTypes.tsx
Normal file
16
src/icons/PageTypes.tsx
Normal file
|
@ -0,0 +1,16 @@
|
|||
import createSvgIcon from "@material-ui/icons/utils/createSvgIcon";
|
||||
import React from "react";
|
||||
|
||||
const PageTypes = createSvgIcon(
|
||||
<>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M19 0H0V40H26.4056H27.6667V8L19 0ZM25.1445 9.00369V37.5769H2.52221V2.42311H17.9553L18 2.46406V9H25.1404L25.1445 9.00369ZM5 12H23V17H5V12ZM12 20H5V30H12V20ZM15 20H23V35H15V20ZM12 33H5V35H12V33Z"
|
||||
fill="#06847B"
|
||||
/>
|
||||
</>,
|
||||
"PageTypes"
|
||||
);
|
||||
|
||||
export default PageTypes;
|
|
@ -46,6 +46,7 @@ import { navigationSection } from "./navigation/urls";
|
|||
import { NotFound } from "./NotFound";
|
||||
import OrdersSection from "./orders";
|
||||
import PageSection from "./pages";
|
||||
import PageTypesSection from "./pageTypes";
|
||||
import PermissionGroupSection from "./permissionGroups";
|
||||
import PluginsSection from "./plugins";
|
||||
import ProductSection from "./products";
|
||||
|
@ -177,6 +178,11 @@ const Routes: React.FC = () => {
|
|||
path="/pages"
|
||||
component={PageSection}
|
||||
/>
|
||||
<SectionRoute
|
||||
permissions={[PermissionEnum.MANAGE_PAGES]}
|
||||
path="/page-types"
|
||||
component={PageTypesSection}
|
||||
/>
|
||||
<SectionRoute
|
||||
permissions={[PermissionEnum.MANAGE_PLUGINS]}
|
||||
path="/plugins"
|
||||
|
|
|
@ -215,6 +215,10 @@ export const sectionNames = defineMessages({
|
|||
defaultMessage: "Orders",
|
||||
description: "orders section name"
|
||||
},
|
||||
pageTypes: {
|
||||
defaultMessage: "Page Types",
|
||||
description: "page types section name"
|
||||
},
|
||||
pages: {
|
||||
defaultMessage: "Pages",
|
||||
description: "pages section name"
|
||||
|
|
|
@ -0,0 +1,191 @@
|
|||
import Button from "@material-ui/core/Button";
|
||||
import Card from "@material-ui/core/Card";
|
||||
import IconButton from "@material-ui/core/IconButton";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import TableCell from "@material-ui/core/TableCell";
|
||||
import TableRow from "@material-ui/core/TableRow";
|
||||
import DeleteIcon from "@material-ui/icons/Delete";
|
||||
import CardTitle from "@saleor/components/CardTitle";
|
||||
import Checkbox from "@saleor/components/Checkbox";
|
||||
import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
||||
import Skeleton from "@saleor/components/Skeleton";
|
||||
import {
|
||||
SortableTableBody,
|
||||
SortableTableRow
|
||||
} from "@saleor/components/SortableTable";
|
||||
import TableHead from "@saleor/components/TableHead";
|
||||
import { renderCollection, stopPropagation } from "@saleor/misc";
|
||||
import { PageTypeDetails_pageType_attributes } from "@saleor/pageTypes/types/PageTypeDetails";
|
||||
import { ListActions, ReorderAction } from "@saleor/types";
|
||||
import { AttributeTypeEnum } from "@saleor/types/globalTypes";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
const useStyles = makeStyles(
|
||||
{
|
||||
colAction: {
|
||||
"&:last-child": {
|
||||
paddingRight: 0
|
||||
},
|
||||
width: 80
|
||||
},
|
||||
colGrab: {
|
||||
width: 60
|
||||
},
|
||||
colName: {},
|
||||
colSlug: {
|
||||
width: 300
|
||||
},
|
||||
link: {
|
||||
cursor: "pointer"
|
||||
},
|
||||
textLeft: {
|
||||
textAlign: "left"
|
||||
}
|
||||
},
|
||||
{ name: "PageTypeAttributes" }
|
||||
);
|
||||
|
||||
interface PageTypeAttributesProps extends ListActions {
|
||||
attributes: PageTypeDetails_pageType_attributes[];
|
||||
disabled: boolean;
|
||||
type: string;
|
||||
onAttributeAssign: (type: AttributeTypeEnum) => void;
|
||||
onAttributeClick: (id: string) => void;
|
||||
onAttributeReorder: ReorderAction;
|
||||
onAttributeUnassign: (id: string) => void;
|
||||
}
|
||||
|
||||
const numberOfColumns = 5;
|
||||
|
||||
const PageTypeAttributes: React.FC<PageTypeAttributesProps> = props => {
|
||||
const {
|
||||
attributes,
|
||||
disabled,
|
||||
isChecked,
|
||||
selected,
|
||||
toggle,
|
||||
toggleAll,
|
||||
toolbar,
|
||||
type,
|
||||
onAttributeAssign,
|
||||
onAttributeClick,
|
||||
onAttributeReorder,
|
||||
onAttributeUnassign
|
||||
} = props;
|
||||
const classes = useStyles(props);
|
||||
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<Card data-test="page-attributes">
|
||||
<CardTitle
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Content Attributes",
|
||||
description: "section header"
|
||||
})}
|
||||
toolbar={
|
||||
<Button
|
||||
color="primary"
|
||||
variant="text"
|
||||
onClick={() => onAttributeAssign(AttributeTypeEnum[type])}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Assign attribute"
|
||||
description="button"
|
||||
/>
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<ResponsiveTable>
|
||||
<colgroup>
|
||||
<col className={classes.colGrab} />
|
||||
<col />
|
||||
<col className={classes.colName} />
|
||||
<col className={classes.colSlug} />
|
||||
<col className={classes.colAction} />
|
||||
</colgroup>
|
||||
{attributes?.length > 0 && (
|
||||
<TableHead
|
||||
colSpan={numberOfColumns}
|
||||
disabled={disabled}
|
||||
dragRows
|
||||
selected={selected}
|
||||
items={attributes}
|
||||
toggleAll={toggleAll}
|
||||
toolbar={toolbar}
|
||||
>
|
||||
<TableCell className={classes.colName}>
|
||||
<FormattedMessage defaultMessage="Attribute name" />
|
||||
</TableCell>
|
||||
<TableCell className={classes.colName}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Slug"
|
||||
description="attribute internal name"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell />
|
||||
</TableHead>
|
||||
)}
|
||||
<SortableTableBody onSortEnd={onAttributeReorder}>
|
||||
{renderCollection(
|
||||
attributes,
|
||||
(attribute, attributeIndex) => {
|
||||
const isSelected = attribute ? isChecked(attribute.id) : false;
|
||||
|
||||
return (
|
||||
<SortableTableRow
|
||||
selected={isSelected}
|
||||
className={!!attribute ? classes.link : undefined}
|
||||
hover={!!attribute}
|
||||
onClick={
|
||||
!!attribute
|
||||
? () => onAttributeClick(attribute.id)
|
||||
: undefined
|
||||
}
|
||||
key={attribute?.id}
|
||||
index={attributeIndex || 0}
|
||||
data-test="id"
|
||||
data-test-id={attribute?.id}
|
||||
>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
checked={isSelected}
|
||||
disabled={disabled}
|
||||
disableClickPropagation
|
||||
onChange={() => toggle(attribute.id)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colName} data-test="name">
|
||||
{attribute?.name || <Skeleton />}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colSlug} data-test="slug">
|
||||
{attribute?.slug || <Skeleton />}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colAction}>
|
||||
<IconButton
|
||||
onClick={stopPropagation(() =>
|
||||
onAttributeUnassign(attribute.id)
|
||||
)}
|
||||
>
|
||||
<DeleteIcon color="primary" />
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</SortableTableRow>
|
||||
);
|
||||
},
|
||||
() => (
|
||||
<TableRow>
|
||||
<TableCell colSpan={numberOfColumns}>
|
||||
<FormattedMessage defaultMessage="No attributes found" />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
)}
|
||||
</SortableTableBody>
|
||||
</ResponsiveTable>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
PageTypeAttributes.displayName = "PageTypeAttributes";
|
||||
export default PageTypeAttributes;
|
2
src/pageTypes/components/PageTypeAttributes/index.ts
Normal file
2
src/pageTypes/components/PageTypeAttributes/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./PageTypeAttributes";
|
||||
export * from "./PageTypeAttributes";
|
|
@ -0,0 +1,63 @@
|
|||
import DialogContentText from "@material-ui/core/DialogContentText";
|
||||
import ActionDialog from "@saleor/components/ActionDialog";
|
||||
import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
export interface PageTypeBulkDeleteDialogProps {
|
||||
confirmButtonState: ConfirmButtonTransitionState;
|
||||
open: boolean;
|
||||
quantity: number;
|
||||
hasPages: boolean;
|
||||
onClose: () => void;
|
||||
onConfirm: () => void;
|
||||
}
|
||||
|
||||
const PageTypeBulkDeleteDialog: React.FC<PageTypeBulkDeleteDialogProps> = ({
|
||||
confirmButtonState,
|
||||
open,
|
||||
quantity,
|
||||
hasPages,
|
||||
onClose,
|
||||
onConfirm
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<ActionDialog
|
||||
confirmButtonState={confirmButtonState}
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
onConfirm={onConfirm}
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Delete Page Types",
|
||||
description: "dialog header"
|
||||
})}
|
||||
variant="delete"
|
||||
>
|
||||
<DialogContentText>
|
||||
{hasPages ? (
|
||||
<FormattedMessage
|
||||
defaultMessage="{counter,plural,one{Page Type you want to delete is used by some pages. Deleting this page type will also delete those pages. Are you sure you want to delete this page type? After doing so you won’t be able to revert changes.} other{Page Types you want to delete are used by some pages. Deleting these page types will also delete those pages. Are you sure you want to delete {displayQuantity} page types? After doing so you won’t be able to revert changes.}}"
|
||||
description="delete page types with its pages"
|
||||
values={{
|
||||
counter: quantity,
|
||||
displayQuantity: <strong>{quantity}</strong>
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
defaultMessage="{counter,plural,one{Are you sure you want to delete this page type? After doing so you won’t be able to revert changes.} other{Are you sure you want to delete {displayQuantity} page types? After doing so you won’t be able to revert changes.}}"
|
||||
description="delete page types"
|
||||
values={{
|
||||
counter: quantity,
|
||||
displayQuantity: <strong>{quantity}</strong>
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</DialogContentText>
|
||||
</ActionDialog>
|
||||
);
|
||||
};
|
||||
PageTypeBulkDeleteDialog.displayName = "PageTypeBulkDeleteDialog";
|
||||
export default PageTypeBulkDeleteDialog;
|
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./PageTypeBulkDeleteDialog";
|
||||
export * from "./PageTypeBulkDeleteDialog";
|
|
@ -0,0 +1,34 @@
|
|||
import { Omit } from "@material-ui/core";
|
||||
import { PageErrorCode } from "@saleor/types/globalTypes";
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import React from "react";
|
||||
|
||||
import Decorator from "../../../storybook/Decorator";
|
||||
import PageTypeCreatePage, { PageTypeCreatePageProps } from ".";
|
||||
|
||||
const props: Omit<PageTypeCreatePageProps, "classes"> = {
|
||||
disabled: false,
|
||||
errors: [],
|
||||
onBack: () => undefined,
|
||||
onSubmit: () => undefined,
|
||||
saveButtonBarState: "default"
|
||||
};
|
||||
|
||||
storiesOf("Views / Page types / Create page type", module)
|
||||
.addDecorator(Decorator)
|
||||
.add("default", () => <PageTypeCreatePage {...props} />)
|
||||
.add("loading", () => <PageTypeCreatePage {...props} disabled={true} />)
|
||||
.add("form errors", () => (
|
||||
<PageTypeCreatePage
|
||||
{...props}
|
||||
errors={[
|
||||
{
|
||||
code: PageErrorCode.REQUIRED,
|
||||
field: "name"
|
||||
}
|
||||
].map(err => ({
|
||||
__typename: "PageError",
|
||||
...err
|
||||
}))}
|
||||
/>
|
||||
));
|
|
@ -0,0 +1,114 @@
|
|||
import { makeStyles } from "@material-ui/core";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import AppHeader from "@saleor/components/AppHeader";
|
||||
import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
|
||||
import Container from "@saleor/components/Container";
|
||||
import Form from "@saleor/components/Form";
|
||||
import Grid from "@saleor/components/Grid";
|
||||
import Hr from "@saleor/components/Hr";
|
||||
import Metadata, { MetadataFormData } from "@saleor/components/Metadata";
|
||||
import PageHeader from "@saleor/components/PageHeader";
|
||||
import SaveButtonBar from "@saleor/components/SaveButtonBar";
|
||||
import { PageErrorFragment } from "@saleor/fragments/types/PageErrorFragment";
|
||||
import { commonMessages, sectionNames } from "@saleor/intl";
|
||||
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import PageTypeDetails from "../PageTypeDetails/PageTypeDetails";
|
||||
|
||||
export interface PageTypeForm extends MetadataFormData {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface PageTypeCreatePageProps {
|
||||
errors: PageErrorFragment[];
|
||||
disabled: boolean;
|
||||
saveButtonBarState: ConfirmButtonTransitionState;
|
||||
onBack: () => void;
|
||||
onSubmit: (data: PageTypeForm) => void;
|
||||
}
|
||||
|
||||
const formInitialData: PageTypeForm = {
|
||||
metadata: [],
|
||||
name: "",
|
||||
privateMetadata: []
|
||||
};
|
||||
|
||||
const useStyles = makeStyles(
|
||||
theme => ({
|
||||
hr: {
|
||||
gridColumnEnd: "span 2",
|
||||
margin: theme.spacing(1, 0)
|
||||
}
|
||||
}),
|
||||
{
|
||||
name: "PageTypeCreatePage"
|
||||
}
|
||||
);
|
||||
|
||||
const PageTypeCreatePage: React.FC<PageTypeCreatePageProps> = props => {
|
||||
const { disabled, errors, saveButtonBarState, onBack, onSubmit } = props;
|
||||
const classes = useStyles(props);
|
||||
const intl = useIntl();
|
||||
const {
|
||||
makeChangeHandler: makeMetadataChangeHandler
|
||||
} = useMetadataChangeTrigger();
|
||||
|
||||
return (
|
||||
<Form initial={formInitialData} onSubmit={onSubmit} confirmLeave>
|
||||
{({ change, data, hasChanged, submit }) => {
|
||||
const changeMetadata = makeMetadataChangeHandler(change);
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<AppHeader onBack={onBack}>
|
||||
{intl.formatMessage(sectionNames.pageTypes)}
|
||||
</AppHeader>
|
||||
<PageHeader
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Create Page Type",
|
||||
description: "header"
|
||||
})}
|
||||
/>
|
||||
<Grid variant="inverted">
|
||||
<div>
|
||||
<Typography>
|
||||
{intl.formatMessage(commonMessages.generalInformations)}
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
<FormattedMessage defaultMessage="These are general information about this Content Type." />
|
||||
</Typography>
|
||||
</div>
|
||||
<PageTypeDetails
|
||||
data={data}
|
||||
disabled={disabled}
|
||||
errors={errors}
|
||||
onChange={change}
|
||||
/>
|
||||
<Hr className={classes.hr} />
|
||||
<div>
|
||||
<Typography>
|
||||
<FormattedMessage
|
||||
defaultMessage="Metadata"
|
||||
description="section header"
|
||||
/>
|
||||
</Typography>
|
||||
</div>
|
||||
<Metadata data={data} onChange={changeMetadata} />
|
||||
<div></div>
|
||||
</Grid>
|
||||
<SaveButtonBar
|
||||
onCancel={onBack}
|
||||
onSave={submit}
|
||||
disabled={disabled || !hasChanged}
|
||||
state={saveButtonBarState}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
}}
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
PageTypeCreatePage.displayName = "PageTypeCreatePage";
|
||||
export default PageTypeCreatePage;
|
2
src/pageTypes/components/PageTypeCreatePage/index.ts
Normal file
2
src/pageTypes/components/PageTypeCreatePage/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./PageTypeCreatePage";
|
||||
export * from "./PageTypeCreatePage";
|
|
@ -0,0 +1,61 @@
|
|||
import DialogContentText from "@material-ui/core/DialogContentText";
|
||||
import ActionDialog from "@saleor/components/ActionDialog";
|
||||
import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
export interface PageTypeDeleteDialogProps {
|
||||
confirmButtonState: ConfirmButtonTransitionState;
|
||||
open: boolean;
|
||||
name: string;
|
||||
hasPages: boolean;
|
||||
onClose: () => void;
|
||||
onConfirm: () => void;
|
||||
}
|
||||
|
||||
const PageTypeDeleteDialog: React.FC<PageTypeDeleteDialogProps> = ({
|
||||
confirmButtonState,
|
||||
open,
|
||||
name,
|
||||
hasPages,
|
||||
onClose,
|
||||
onConfirm
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<ActionDialog
|
||||
confirmButtonState={confirmButtonState}
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
onConfirm={onConfirm}
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Delete Page Type",
|
||||
description: "dialog header"
|
||||
})}
|
||||
variant="delete"
|
||||
>
|
||||
<DialogContentText>
|
||||
{hasPages ? (
|
||||
<FormattedMessage
|
||||
defaultMessage="Page Type you want to delete is used by some pages. Deleting this page type will also delete those pages. Are you sure you want to delete {name}? After doing so you won’t be able to revert changes."
|
||||
description="delete page type with its pages"
|
||||
values={{
|
||||
name: <strong>{name}</strong>
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
defaultMessage="Are you sure you want to delete {name}? After doing so you won’t be able to revert changes."
|
||||
description="delete page type"
|
||||
values={{
|
||||
name: <strong>{name}</strong>
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</DialogContentText>
|
||||
</ActionDialog>
|
||||
);
|
||||
};
|
||||
PageTypeDeleteDialog.displayName = "PageTypeDeleteDialog";
|
||||
export default PageTypeDeleteDialog;
|
2
src/pageTypes/components/PageTypeDeleteDialog/index.ts
Normal file
2
src/pageTypes/components/PageTypeDeleteDialog/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./PageTypeDeleteDialog";
|
||||
export * from "./PageTypeDeleteDialog";
|
53
src/pageTypes/components/PageTypeDetails/PageTypeDetails.tsx
Normal file
53
src/pageTypes/components/PageTypeDetails/PageTypeDetails.tsx
Normal file
|
@ -0,0 +1,53 @@
|
|||
import Card from "@material-ui/core/Card";
|
||||
import CardContent from "@material-ui/core/CardContent";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import CardTitle from "@saleor/components/CardTitle";
|
||||
import { PageErrorFragment } from "@saleor/fragments/types/PageErrorFragment";
|
||||
import { commonMessages } from "@saleor/intl";
|
||||
import { getFormErrors } from "@saleor/utils/errors";
|
||||
import getPageErrorMessage from "@saleor/utils/errors/page";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
interface PageTypeDetailsProps {
|
||||
data?: {
|
||||
name: string;
|
||||
};
|
||||
disabled: boolean;
|
||||
errors: PageErrorFragment[];
|
||||
onChange: (event: React.ChangeEvent<any>) => void;
|
||||
}
|
||||
|
||||
const PageTypeDetails: React.FC<PageTypeDetailsProps> = props => {
|
||||
const { data, disabled, errors, onChange } = props;
|
||||
const intl = useIntl();
|
||||
|
||||
const formErrors = getFormErrors(["name"], errors);
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardTitle
|
||||
title={intl.formatMessage(commonMessages.generalInformations)}
|
||||
/>
|
||||
<CardContent>
|
||||
<TextField
|
||||
disabled={disabled}
|
||||
fullWidth
|
||||
error={!!formErrors.name}
|
||||
helperText={getPageErrorMessage(formErrors.name, intl)}
|
||||
label={intl.formatMessage({
|
||||
defaultMessage: "Content Type Name"
|
||||
})}
|
||||
name="name"
|
||||
onChange={onChange}
|
||||
value={data.name}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
PageTypeDetails.defaultProps = {
|
||||
errors: []
|
||||
};
|
||||
PageTypeDetails.displayName = "PageTypeDetails";
|
||||
export default PageTypeDetails;
|
2
src/pageTypes/components/PageTypeDetails/index.ts
Normal file
2
src/pageTypes/components/PageTypeDetails/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./PageTypeDetails";
|
||||
export * from "./PageTypeDetails";
|
|
@ -0,0 +1,60 @@
|
|||
import { Omit } from "@material-ui/core";
|
||||
import { listActionsProps } from "@saleor/fixtures";
|
||||
import { PageErrorCode } from "@saleor/types/globalTypes";
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import React from "react";
|
||||
|
||||
import Decorator from "../../../storybook/Decorator";
|
||||
import { pageType } from "../../fixtures";
|
||||
import PageTypeDetailsPage, { PageTypeDetailsPageProps } from ".";
|
||||
|
||||
const props: Omit<PageTypeDetailsPageProps, "classes"> = {
|
||||
attributeList: listActionsProps,
|
||||
disabled: false,
|
||||
errors: [],
|
||||
onAttributeAdd: () => undefined,
|
||||
onAttributeClick: () => undefined,
|
||||
onAttributeReorder: () => undefined,
|
||||
onAttributeUnassign: () => undefined,
|
||||
onBack: () => undefined,
|
||||
onDelete: () => undefined,
|
||||
onSubmit: () => undefined,
|
||||
pageTitle: pageType.name,
|
||||
pageType,
|
||||
saveButtonBarState: "default"
|
||||
};
|
||||
|
||||
storiesOf("Views / Page types / Page type details", module)
|
||||
.addDecorator(Decorator)
|
||||
.add("default", () => <PageTypeDetailsPage {...props} />)
|
||||
.add("loading", () => (
|
||||
<PageTypeDetailsPage
|
||||
{...props}
|
||||
disabled={true}
|
||||
pageTitle={undefined}
|
||||
pageType={undefined}
|
||||
/>
|
||||
))
|
||||
.add("no attributes", () => (
|
||||
<PageTypeDetailsPage
|
||||
{...props}
|
||||
pageType={{
|
||||
...pageType,
|
||||
attributes: []
|
||||
}}
|
||||
/>
|
||||
))
|
||||
.add("form errors", () => (
|
||||
<PageTypeDetailsPage
|
||||
{...props}
|
||||
errors={[
|
||||
{
|
||||
code: PageErrorCode.REQUIRED,
|
||||
field: "name"
|
||||
}
|
||||
].map(err => ({
|
||||
__typename: "PageError",
|
||||
...err
|
||||
}))}
|
||||
/>
|
||||
));
|
|
@ -0,0 +1,183 @@
|
|||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import AppHeader from "@saleor/components/AppHeader";
|
||||
import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
|
||||
import Container from "@saleor/components/Container";
|
||||
import Form from "@saleor/components/Form";
|
||||
import Grid from "@saleor/components/Grid";
|
||||
import Hr from "@saleor/components/Hr";
|
||||
import Metadata from "@saleor/components/Metadata/Metadata";
|
||||
import { MetadataFormData } from "@saleor/components/Metadata/types";
|
||||
import PageHeader from "@saleor/components/PageHeader";
|
||||
import SaveButtonBar from "@saleor/components/SaveButtonBar";
|
||||
import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField";
|
||||
import { PageErrorFragment } from "@saleor/fragments/types/PageErrorFragment";
|
||||
import { commonMessages, sectionNames } from "@saleor/intl";
|
||||
import { PageTypeDetails_pageType } from "@saleor/pageTypes/types/PageTypeDetails";
|
||||
import { ListActions, ReorderEvent } from "@saleor/types";
|
||||
import { AttributeTypeEnum } from "@saleor/types/globalTypes";
|
||||
import { mapMetadataItemToInput } from "@saleor/utils/maps";
|
||||
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import PageTypeAttributes from "../PageTypeAttributes/PageTypeAttributes";
|
||||
import PageTypeDetails from "../PageTypeDetails/PageTypeDetails";
|
||||
|
||||
export interface PageTypeForm extends MetadataFormData {
|
||||
name: string;
|
||||
attributes: SingleAutocompleteChoiceType[];
|
||||
}
|
||||
|
||||
export interface PageTypeDetailsPageProps {
|
||||
errors: PageErrorFragment[];
|
||||
pageType: PageTypeDetails_pageType;
|
||||
disabled: boolean;
|
||||
pageTitle: string;
|
||||
attributeList: ListActions;
|
||||
saveButtonBarState: ConfirmButtonTransitionState;
|
||||
onAttributeAdd: (type: AttributeTypeEnum) => void;
|
||||
onAttributeClick: (id: string) => void;
|
||||
onAttributeReorder: (event: ReorderEvent, type: AttributeTypeEnum) => void;
|
||||
onAttributeUnassign: (id: string) => void;
|
||||
onBack: () => void;
|
||||
onDelete: () => void;
|
||||
onSubmit: (data: PageTypeForm) => void;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(
|
||||
theme => ({
|
||||
hr: {
|
||||
gridColumnEnd: "span 2",
|
||||
margin: theme.spacing(1, 0)
|
||||
}
|
||||
}),
|
||||
{
|
||||
name: "PageTypeDetailsPage"
|
||||
}
|
||||
);
|
||||
|
||||
const PageTypeDetailsPage: React.FC<PageTypeDetailsPageProps> = props => {
|
||||
const {
|
||||
disabled,
|
||||
errors,
|
||||
pageTitle,
|
||||
pageType,
|
||||
attributeList,
|
||||
saveButtonBarState,
|
||||
onAttributeAdd,
|
||||
onAttributeUnassign,
|
||||
onAttributeReorder,
|
||||
onAttributeClick,
|
||||
onBack,
|
||||
onDelete,
|
||||
onSubmit
|
||||
} = props;
|
||||
const classes = useStyles(props);
|
||||
const intl = useIntl();
|
||||
const {
|
||||
isMetadataModified,
|
||||
isPrivateMetadataModified,
|
||||
makeChangeHandler: makeMetadataChangeHandler
|
||||
} = useMetadataChangeTrigger();
|
||||
|
||||
const formInitialData: PageTypeForm = {
|
||||
attributes:
|
||||
pageType?.attributes?.map(attribute => ({
|
||||
label: attribute.name,
|
||||
value: attribute.id
|
||||
})) || [],
|
||||
metadata: pageType?.metadata?.map(mapMetadataItemToInput),
|
||||
name: pageType?.name || "",
|
||||
privateMetadata: pageType?.privateMetadata?.map(mapMetadataItemToInput)
|
||||
};
|
||||
|
||||
const handleSubmit = (data: PageTypeForm) => {
|
||||
const metadata = isMetadataModified ? data.metadata : undefined;
|
||||
const privateMetadata = isPrivateMetadataModified
|
||||
? data.privateMetadata
|
||||
: undefined;
|
||||
|
||||
onSubmit({
|
||||
...data,
|
||||
metadata,
|
||||
privateMetadata
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Form initial={formInitialData} onSubmit={handleSubmit} confirmLeave>
|
||||
{({ change, data, hasChanged, submit }) => {
|
||||
const changeMetadata = makeMetadataChangeHandler(change);
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<AppHeader onBack={onBack}>
|
||||
{intl.formatMessage(sectionNames.pageTypes)}
|
||||
</AppHeader>
|
||||
<PageHeader title={pageTitle} />
|
||||
<Grid variant="inverted">
|
||||
<div>
|
||||
<Typography>
|
||||
{intl.formatMessage(commonMessages.generalInformations)}
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
<FormattedMessage defaultMessage="These are general information about this Content Type." />
|
||||
</Typography>
|
||||
</div>
|
||||
<PageTypeDetails
|
||||
data={data}
|
||||
disabled={disabled}
|
||||
errors={errors}
|
||||
onChange={change}
|
||||
/>
|
||||
<Hr className={classes.hr} />
|
||||
<div>
|
||||
<Typography>
|
||||
<FormattedMessage
|
||||
defaultMessage="Content Attributes"
|
||||
description="section header"
|
||||
/>
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
<FormattedMessage defaultMessage="This list shows all attributes that will be assigned to pages that have this page type assigned." />
|
||||
</Typography>
|
||||
</div>
|
||||
<PageTypeAttributes
|
||||
attributes={pageType?.attributes}
|
||||
disabled={disabled}
|
||||
type={AttributeTypeEnum.PAGE_TYPE}
|
||||
onAttributeAssign={onAttributeAdd}
|
||||
onAttributeClick={onAttributeClick}
|
||||
onAttributeReorder={(event: ReorderEvent) =>
|
||||
onAttributeReorder(event, AttributeTypeEnum.PAGE_TYPE)
|
||||
}
|
||||
onAttributeUnassign={onAttributeUnassign}
|
||||
{...attributeList}
|
||||
/>
|
||||
<Hr className={classes.hr} />
|
||||
<div>
|
||||
<Typography>
|
||||
<FormattedMessage
|
||||
defaultMessage="Metadata"
|
||||
description="section header"
|
||||
/>
|
||||
</Typography>
|
||||
</div>
|
||||
<Metadata data={data} onChange={changeMetadata} />
|
||||
</Grid>
|
||||
<SaveButtonBar
|
||||
onCancel={onBack}
|
||||
onDelete={onDelete}
|
||||
onSave={submit}
|
||||
disabled={disabled || !hasChanged}
|
||||
state={saveButtonBarState}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
}}
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
PageTypeDetailsPage.displayName = "PageTypeDetailsPage";
|
||||
export default PageTypeDetailsPage;
|
2
src/pageTypes/components/PageTypeDetailsPage/index.ts
Normal file
2
src/pageTypes/components/PageTypeDetailsPage/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./PageTypeDetailsPage";
|
||||
export * from "./PageTypeDetailsPage";
|
145
src/pageTypes/components/PageTypeList/PageTypeList.tsx
Normal file
145
src/pageTypes/components/PageTypeList/PageTypeList.tsx
Normal file
|
@ -0,0 +1,145 @@
|
|||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import TableBody from "@material-ui/core/TableBody";
|
||||
import TableCell from "@material-ui/core/TableCell";
|
||||
import TableFooter from "@material-ui/core/TableFooter";
|
||||
import TableRow from "@material-ui/core/TableRow";
|
||||
import Checkbox from "@saleor/components/Checkbox";
|
||||
import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
||||
import Skeleton from "@saleor/components/Skeleton";
|
||||
import TableCellHeader from "@saleor/components/TableCellHeader";
|
||||
import TableHead from "@saleor/components/TableHead";
|
||||
import TablePagination from "@saleor/components/TablePagination";
|
||||
import { PageTypeList_pageTypes_edges_node } from "@saleor/pageTypes/types/PageTypeList";
|
||||
import { PageTypeListUrlSortField } from "@saleor/pageTypes/urls";
|
||||
import { getArrowDirection } from "@saleor/utils/sort";
|
||||
import React from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import { renderCollection } from "../../../misc";
|
||||
import { ListActions, ListProps, SortPage } from "../../../types";
|
||||
|
||||
const useStyles = makeStyles(
|
||||
{
|
||||
colName: {
|
||||
paddingLeft: 0
|
||||
},
|
||||
link: {
|
||||
cursor: "pointer"
|
||||
}
|
||||
},
|
||||
{ name: "PageTypeList" }
|
||||
);
|
||||
|
||||
interface PageTypeListProps
|
||||
extends ListProps,
|
||||
ListActions,
|
||||
SortPage<PageTypeListUrlSortField> {
|
||||
pageTypes: PageTypeList_pageTypes_edges_node[];
|
||||
}
|
||||
|
||||
const numberOfColumns = 2;
|
||||
|
||||
const PageTypeList: React.FC<PageTypeListProps> = props => {
|
||||
const {
|
||||
disabled,
|
||||
pageTypes,
|
||||
pageInfo,
|
||||
onNextPage,
|
||||
onPreviousPage,
|
||||
onRowClick,
|
||||
onSort,
|
||||
isChecked,
|
||||
selected,
|
||||
sort,
|
||||
toggle,
|
||||
toggleAll,
|
||||
toolbar
|
||||
} = props;
|
||||
const classes = useStyles(props);
|
||||
|
||||
return (
|
||||
<ResponsiveTable>
|
||||
<TableHead
|
||||
colSpan={numberOfColumns}
|
||||
selected={selected}
|
||||
disabled={disabled}
|
||||
items={pageTypes}
|
||||
toggleAll={toggleAll}
|
||||
toolbar={toolbar}
|
||||
>
|
||||
<TableCellHeader
|
||||
direction={
|
||||
sort.sort === PageTypeListUrlSortField.name
|
||||
? getArrowDirection(sort.asc)
|
||||
: undefined
|
||||
}
|
||||
arrowPosition="right"
|
||||
onClick={() => onSort(PageTypeListUrlSortField.name)}
|
||||
className={classes.colName}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Content Type Name"
|
||||
description="page type name"
|
||||
/>
|
||||
</TableCellHeader>
|
||||
</TableHead>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TablePagination
|
||||
colSpan={numberOfColumns}
|
||||
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
|
||||
onNextPage={onNextPage}
|
||||
hasPreviousPage={
|
||||
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
|
||||
}
|
||||
onPreviousPage={onPreviousPage}
|
||||
/>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
<TableBody>
|
||||
{renderCollection(
|
||||
pageTypes,
|
||||
pageType => {
|
||||
const isSelected = pageType ? isChecked(pageType.id) : false;
|
||||
return (
|
||||
<TableRow
|
||||
className={!!pageType ? classes.link : undefined}
|
||||
hover={!!pageType}
|
||||
key={pageType ? pageType.id : "skeleton"}
|
||||
onClick={pageType ? onRowClick(pageType.id) : undefined}
|
||||
selected={isSelected}
|
||||
data-test="id"
|
||||
data-test-id={pageType?.id}
|
||||
>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
checked={isSelected}
|
||||
disabled={disabled}
|
||||
disableClickPropagation
|
||||
onChange={() => toggle(pageType.id)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colName}>
|
||||
{pageType ? (
|
||||
<span data-test="name">{pageType.name}</span>
|
||||
) : (
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
},
|
||||
() => (
|
||||
<TableRow>
|
||||
<TableCell colSpan={numberOfColumns}>
|
||||
<FormattedMessage defaultMessage="No page types found" />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
)}
|
||||
</TableBody>
|
||||
</ResponsiveTable>
|
||||
);
|
||||
};
|
||||
PageTypeList.displayName = "PageTypeList";
|
||||
export default PageTypeList;
|
2
src/pageTypes/components/PageTypeList/index.ts
Normal file
2
src/pageTypes/components/PageTypeList/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./PageTypeList";
|
||||
export * from "./PageTypeList";
|
|
@ -0,0 +1,36 @@
|
|||
import { PageTypeListUrlSortField } from "@saleor/pageTypes/urls";
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import React from "react";
|
||||
|
||||
import {
|
||||
listActionsProps,
|
||||
pageListProps,
|
||||
searchPageProps,
|
||||
sortPageProps,
|
||||
tabPageProps
|
||||
} from "../../../fixtures";
|
||||
import Decorator from "../../../storybook/Decorator";
|
||||
import { pageTypes } from "../../fixtures";
|
||||
import PageTypeListPage, { PageTypeListPageProps } from ".";
|
||||
|
||||
const props: PageTypeListPageProps = {
|
||||
...listActionsProps,
|
||||
...pageListProps.default,
|
||||
...searchPageProps,
|
||||
...sortPageProps,
|
||||
sort: {
|
||||
...sortPageProps.sort,
|
||||
sort: PageTypeListUrlSortField.name
|
||||
},
|
||||
...tabPageProps,
|
||||
onBack: () => undefined,
|
||||
pageTypes
|
||||
};
|
||||
|
||||
storiesOf("Views / Page types / Page types list", module)
|
||||
.addDecorator(Decorator)
|
||||
.add("default", () => <PageTypeListPage {...props} />)
|
||||
.add("loading", () => (
|
||||
<PageTypeListPage {...props} disabled={true} pageTypes={undefined} />
|
||||
))
|
||||
.add("no data", () => <PageTypeListPage {...props} pageTypes={[]} />);
|
|
@ -0,0 +1,83 @@
|
|||
import Button from "@material-ui/core/Button";
|
||||
import Card from "@material-ui/core/Card";
|
||||
import AppHeader from "@saleor/components/AppHeader";
|
||||
import Container from "@saleor/components/Container";
|
||||
import PageHeader from "@saleor/components/PageHeader";
|
||||
import SearchBar from "@saleor/components/SearchBar";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { PageTypeList_pageTypes_edges_node } from "@saleor/pageTypes/types/PageTypeList";
|
||||
import { PageTypeListUrlSortField } from "@saleor/pageTypes/urls";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import {
|
||||
ListActions,
|
||||
PageListProps,
|
||||
SearchPageProps,
|
||||
SortPage,
|
||||
TabPageProps
|
||||
} from "../../../types";
|
||||
import PageTypeList from "../PageTypeList";
|
||||
|
||||
export interface PageTypeListPageProps
|
||||
extends PageListProps,
|
||||
ListActions,
|
||||
SearchPageProps,
|
||||
SortPage<PageTypeListUrlSortField>,
|
||||
TabPageProps {
|
||||
pageTypes: PageTypeList_pageTypes_edges_node[];
|
||||
onBack: () => void;
|
||||
}
|
||||
|
||||
const PageTypeListPage: React.FC<PageTypeListPageProps> = ({
|
||||
currentTab,
|
||||
initialSearch,
|
||||
onAdd,
|
||||
onAll,
|
||||
onBack,
|
||||
onSearchChange,
|
||||
onTabChange,
|
||||
onTabDelete,
|
||||
onTabSave,
|
||||
tabs,
|
||||
...listProps
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
return (
|
||||
<Container>
|
||||
<AppHeader onBack={onBack}>
|
||||
{intl.formatMessage(sectionNames.configuration)}
|
||||
</AppHeader>
|
||||
<PageHeader title={intl.formatMessage(sectionNames.pageTypes)}>
|
||||
<Button color="primary" variant="contained" onClick={onAdd}>
|
||||
<FormattedMessage
|
||||
defaultMessage="create page type"
|
||||
description="button"
|
||||
/>
|
||||
</Button>
|
||||
</PageHeader>
|
||||
<Card>
|
||||
<SearchBar
|
||||
allTabLabel={intl.formatMessage({
|
||||
defaultMessage: "All Page Types",
|
||||
description: "tab name"
|
||||
})}
|
||||
currentTab={currentTab}
|
||||
initialSearch={initialSearch}
|
||||
searchPlaceholder={intl.formatMessage({
|
||||
defaultMessage: "Search Page Type"
|
||||
})}
|
||||
tabs={tabs}
|
||||
onAll={onAll}
|
||||
onSearchChange={onSearchChange}
|
||||
onTabChange={onTabChange}
|
||||
onTabDelete={onTabDelete}
|
||||
onTabSave={onTabSave}
|
||||
/>
|
||||
<PageTypeList {...listProps} />
|
||||
</Card>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
PageTypeListPage.displayName = "PageTypeListPage";
|
||||
export default PageTypeListPage;
|
2
src/pageTypes/components/PageTypeListPage/index.ts
Normal file
2
src/pageTypes/components/PageTypeListPage/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./PageTypeListPage";
|
||||
export * from "./PageTypeListPage";
|
73
src/pageTypes/fixtures.ts
Normal file
73
src/pageTypes/fixtures.ts
Normal file
|
@ -0,0 +1,73 @@
|
|||
/* eslint-disable sort-keys */
|
||||
import { AttributeTypeEnum } from "@saleor/types/globalTypes";
|
||||
|
||||
import { PageTypeDetails_pageType } from "./types/PageTypeDetails";
|
||||
import { PageTypeList_pageTypes_edges_node } from "./types/PageTypeList";
|
||||
|
||||
export const pageTypes: PageTypeList_pageTypes_edges_node[] = [
|
||||
{
|
||||
id: "UGFnZVR5cGU6MQ==",
|
||||
name: "Blog",
|
||||
hasPages: true,
|
||||
__typename: "PageType"
|
||||
},
|
||||
{
|
||||
id: "UGFnZVR5cGU6Mw==",
|
||||
name: "Landing Page",
|
||||
hasPages: true,
|
||||
__typename: "PageType"
|
||||
},
|
||||
{
|
||||
id: "UGFnZVR5cGU6Mg==",
|
||||
name: "Marketing Page",
|
||||
hasPages: false,
|
||||
__typename: "PageType"
|
||||
}
|
||||
];
|
||||
|
||||
export const pageType: PageTypeDetails_pageType = {
|
||||
id: "UGFnZVR5cGU6MQ==",
|
||||
__typename: "PageType",
|
||||
metadata: [
|
||||
{
|
||||
__typename: "MetadataItem",
|
||||
key: "integration.id",
|
||||
value: "100023123"
|
||||
}
|
||||
],
|
||||
name: "Blog",
|
||||
hasPages: true,
|
||||
attributes: [
|
||||
{
|
||||
__typename: "Attribute" as "Attribute",
|
||||
id: "UHJvZHVjdEF0dHJpYnV0ZTo5",
|
||||
name: "Author",
|
||||
slug: "author",
|
||||
visibleInStorefront: true,
|
||||
filterableInDashboard: true,
|
||||
filterableInStorefront: true,
|
||||
type: AttributeTypeEnum.PAGE_TYPE
|
||||
},
|
||||
{
|
||||
__typename: "Attribute" as "Attribute",
|
||||
id: "UHJvZHVjdEF0dHJpYnV0ZToxMQ==",
|
||||
name: "Language",
|
||||
slug: "language",
|
||||
visibleInStorefront: true,
|
||||
filterableInDashboard: true,
|
||||
filterableInStorefront: true,
|
||||
type: AttributeTypeEnum.PAGE_TYPE
|
||||
},
|
||||
{
|
||||
__typename: "Attribute" as "Attribute",
|
||||
id: "UHJvZHVjdEF0dHJpYnV0ZTo5",
|
||||
name: "Author",
|
||||
slug: "author",
|
||||
visibleInStorefront: true,
|
||||
filterableInDashboard: true,
|
||||
filterableInStorefront: true,
|
||||
type: AttributeTypeEnum.PAGE_TYPE
|
||||
}
|
||||
],
|
||||
privateMetadata: []
|
||||
};
|
|
@ -0,0 +1,72 @@
|
|||
import { availableAttributeFragment } from "@saleor/fragments/attributes";
|
||||
import { pageInfoFragment } from "@saleor/fragments/pageInfo";
|
||||
import makeSearch from "@saleor/hooks/makeSearch";
|
||||
import gql from "graphql-tag";
|
||||
|
||||
import {
|
||||
SearchAvailablePageAttributes,
|
||||
SearchAvailablePageAttributesVariables
|
||||
} from "./types/SearchAvailablePageAttributes";
|
||||
|
||||
export const searchPageAttributes = gql`
|
||||
${pageInfoFragment}
|
||||
${availableAttributeFragment}
|
||||
query SearchAvailablePageAttributes(
|
||||
$id: ID!
|
||||
$after: String
|
||||
$first: Int!
|
||||
$query: String!
|
||||
) {
|
||||
pageType(id: $id) {
|
||||
id
|
||||
availableAttributes(
|
||||
after: $after
|
||||
first: $first
|
||||
filter: { search: $query }
|
||||
) {
|
||||
edges {
|
||||
node {
|
||||
...AvailableAttributeFragment
|
||||
}
|
||||
}
|
||||
pageInfo {
|
||||
...PageInfoFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default makeSearch<
|
||||
SearchAvailablePageAttributes,
|
||||
SearchAvailablePageAttributesVariables
|
||||
>(searchPageAttributes, result =>
|
||||
result.loadMore(
|
||||
(prev, next) => {
|
||||
if (
|
||||
prev.pageType.availableAttributes.pageInfo.endCursor ===
|
||||
next.pageType.availableAttributes.pageInfo.endCursor
|
||||
) {
|
||||
return prev;
|
||||
}
|
||||
|
||||
return {
|
||||
...prev,
|
||||
pageType: {
|
||||
...prev.pageType,
|
||||
availableAttributes: {
|
||||
...prev.pageType.availableAttributes,
|
||||
edges: [
|
||||
...prev.pageType.availableAttributes.edges,
|
||||
...next.pageType.availableAttributes.edges
|
||||
],
|
||||
pageInfo: next.pageType.availableAttributes.pageInfo
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
{
|
||||
after: result.data.pageType.availableAttributes.pageInfo.endCursor
|
||||
}
|
||||
)
|
||||
);
|
|
@ -0,0 +1,50 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: SearchAvailablePageAttributes
|
||||
// ====================================================
|
||||
|
||||
export interface SearchAvailablePageAttributes_pageType_availableAttributes_edges_node {
|
||||
__typename: "Attribute";
|
||||
id: string;
|
||||
name: string | null;
|
||||
slug: string | null;
|
||||
}
|
||||
|
||||
export interface SearchAvailablePageAttributes_pageType_availableAttributes_edges {
|
||||
__typename: "AttributeCountableEdge";
|
||||
node: SearchAvailablePageAttributes_pageType_availableAttributes_edges_node;
|
||||
}
|
||||
|
||||
export interface SearchAvailablePageAttributes_pageType_availableAttributes_pageInfo {
|
||||
__typename: "PageInfo";
|
||||
endCursor: string | null;
|
||||
hasNextPage: boolean;
|
||||
hasPreviousPage: boolean;
|
||||
startCursor: string | null;
|
||||
}
|
||||
|
||||
export interface SearchAvailablePageAttributes_pageType_availableAttributes {
|
||||
__typename: "AttributeCountableConnection";
|
||||
edges: SearchAvailablePageAttributes_pageType_availableAttributes_edges[];
|
||||
pageInfo: SearchAvailablePageAttributes_pageType_availableAttributes_pageInfo;
|
||||
}
|
||||
|
||||
export interface SearchAvailablePageAttributes_pageType {
|
||||
__typename: "PageType";
|
||||
id: string;
|
||||
availableAttributes: SearchAvailablePageAttributes_pageType_availableAttributes | null;
|
||||
}
|
||||
|
||||
export interface SearchAvailablePageAttributes {
|
||||
pageType: SearchAvailablePageAttributes_pageType | null;
|
||||
}
|
||||
|
||||
export interface SearchAvailablePageAttributesVariables {
|
||||
id: string;
|
||||
after?: string | null;
|
||||
first: number;
|
||||
query: string;
|
||||
}
|
62
src/pageTypes/index.tsx
Normal file
62
src/pageTypes/index.tsx
Normal file
|
@ -0,0 +1,62 @@
|
|||
import { sectionNames } from "@saleor/intl";
|
||||
import { asSortParams } from "@saleor/utils/sort";
|
||||
import { parse as parseQs } from "qs";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
import { Route, RouteComponentProps, Switch } from "react-router-dom";
|
||||
|
||||
import { WindowTitle } from "../components/WindowTitle";
|
||||
import {
|
||||
pageTypeAddPath,
|
||||
pageTypeListPath,
|
||||
PageTypeListUrlQueryParams,
|
||||
PageTypeListUrlSortField,
|
||||
pageTypePath,
|
||||
PageTypeUrlQueryParams
|
||||
} from "./urls";
|
||||
import PageTypeCreate from "./views/PageTypeCreate";
|
||||
import PageTypeDetailsComponent from "./views/PageTypeDetails";
|
||||
import PageTypeListComponent from "./views/PageTypeList";
|
||||
|
||||
const PageTypeList: React.FC<RouteComponentProps<{}>> = ({ location }) => {
|
||||
const qs = parseQs(location.search.substr(1));
|
||||
const params: PageTypeListUrlQueryParams = asSortParams(
|
||||
qs,
|
||||
PageTypeListUrlSortField
|
||||
);
|
||||
return <PageTypeListComponent params={params} />;
|
||||
};
|
||||
|
||||
interface PageTypeDetailsRouteParams {
|
||||
id: string;
|
||||
}
|
||||
const PageTypeDetails: React.FC<RouteComponentProps<
|
||||
PageTypeDetailsRouteParams
|
||||
>> = ({ match }) => {
|
||||
const qs = parseQs(location.search.substr(1));
|
||||
const params: PageTypeUrlQueryParams = qs;
|
||||
|
||||
return (
|
||||
<PageTypeDetailsComponent
|
||||
id={decodeURIComponent(match.params.id)}
|
||||
params={params}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const PageTypeRouter: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<>
|
||||
<WindowTitle title={intl.formatMessage(sectionNames.pageTypes)} />
|
||||
<Switch>
|
||||
<Route exact path={pageTypeListPath} component={PageTypeList} />
|
||||
<Route exact path={pageTypeAddPath} component={PageTypeCreate} />
|
||||
<Route path={pageTypePath(":id")} component={PageTypeDetails} />
|
||||
</Switch>
|
||||
</>
|
||||
);
|
||||
};
|
||||
PageTypeRouter.displayName = "PageTypeRouter";
|
||||
export default PageTypeRouter;
|
161
src/pageTypes/mutations.ts
Normal file
161
src/pageTypes/mutations.ts
Normal file
|
@ -0,0 +1,161 @@
|
|||
import { pageErrorFragment } from "@saleor/fragments/errors";
|
||||
import { pageTypeDetailsFragment } from "@saleor/fragments/pageTypes";
|
||||
import makeMutation from "@saleor/hooks/makeMutation";
|
||||
import gql from "graphql-tag";
|
||||
|
||||
import {
|
||||
AssignPageAttribute,
|
||||
AssignPageAttributeVariables
|
||||
} from "./types/AssignPageAttribute";
|
||||
import {
|
||||
PageTypeAttributeReorder,
|
||||
PageTypeAttributeReorderVariables
|
||||
} from "./types/PageTypeAttributeReorder";
|
||||
import {
|
||||
PageTypeBulkDelete,
|
||||
PageTypeBulkDeleteVariables
|
||||
} from "./types/PageTypeBulkDelete";
|
||||
import {
|
||||
PageTypeCreate,
|
||||
PageTypeCreateVariables
|
||||
} from "./types/PageTypeCreate";
|
||||
import {
|
||||
PageTypeDelete,
|
||||
PageTypeDeleteVariables
|
||||
} from "./types/PageTypeDelete";
|
||||
import {
|
||||
PageTypeUpdate,
|
||||
PageTypeUpdateVariables
|
||||
} from "./types/PageTypeUpdate";
|
||||
import {
|
||||
UnassignPageAttribute,
|
||||
UnassignPageAttributeVariables
|
||||
} from "./types/UnassignPageAttribute";
|
||||
|
||||
export const pageTypeUpdateMutation = gql`
|
||||
${pageTypeDetailsFragment}
|
||||
${pageErrorFragment}
|
||||
mutation PageTypeUpdate($id: ID!, $input: PageTypeUpdateInput!) {
|
||||
pageTypeUpdate(id: $id, input: $input) {
|
||||
errors: pageErrors {
|
||||
...PageErrorFragment
|
||||
}
|
||||
pageType {
|
||||
...PageTypeDetailsFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const usePageTypeUpdateMutation = makeMutation<
|
||||
PageTypeUpdate,
|
||||
PageTypeUpdateVariables
|
||||
>(pageTypeUpdateMutation);
|
||||
|
||||
export const pageTypeCreateMutation = gql`
|
||||
${pageTypeDetailsFragment}
|
||||
${pageErrorFragment}
|
||||
mutation PageTypeCreate($input: PageTypeCreateInput!) {
|
||||
pageTypeCreate(input: $input) {
|
||||
errors: pageErrors {
|
||||
...PageErrorFragment
|
||||
}
|
||||
pageType {
|
||||
...PageTypeDetailsFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const usePageTypeCreateMutation = makeMutation<
|
||||
PageTypeCreate,
|
||||
PageTypeCreateVariables
|
||||
>(pageTypeCreateMutation);
|
||||
|
||||
export const assignPageAttributeMutation = gql`
|
||||
${pageTypeDetailsFragment}
|
||||
${pageErrorFragment}
|
||||
mutation AssignPageAttribute($id: ID!, $ids: [ID!]!) {
|
||||
pageAttributeAssign(pageTypeId: $id, attributeIds: $ids) {
|
||||
errors: pageErrors {
|
||||
...PageErrorFragment
|
||||
}
|
||||
pageType {
|
||||
...PageTypeDetailsFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const useAssignPageAttributeMutation = makeMutation<
|
||||
AssignPageAttribute,
|
||||
AssignPageAttributeVariables
|
||||
>(assignPageAttributeMutation);
|
||||
|
||||
export const unassignPageAttributeMutation = gql`
|
||||
${pageTypeDetailsFragment}
|
||||
${pageErrorFragment}
|
||||
mutation UnassignPageAttribute($id: ID!, $ids: [ID!]!) {
|
||||
pageAttributeUnassign(pageTypeId: $id, attributeIds: $ids) {
|
||||
errors: pageErrors {
|
||||
...PageErrorFragment
|
||||
}
|
||||
pageType {
|
||||
...PageTypeDetailsFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const useUnassignPageAttributeMutation = makeMutation<
|
||||
UnassignPageAttribute,
|
||||
UnassignPageAttributeVariables
|
||||
>(unassignPageAttributeMutation);
|
||||
|
||||
export const pageTypeDeleteMutation = gql`
|
||||
mutation PageTypeDelete($id: ID!) {
|
||||
pageTypeDelete(id: $id) {
|
||||
errors: pageErrors {
|
||||
field
|
||||
message
|
||||
}
|
||||
pageType {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const usePageTypeDeleteMutation = makeMutation<
|
||||
PageTypeDelete,
|
||||
PageTypeDeleteVariables
|
||||
>(pageTypeDeleteMutation);
|
||||
|
||||
export const pageTypeBulkDeleteMutation = gql`
|
||||
mutation PageTypeBulkDelete($ids: [ID!]!) {
|
||||
pageTypeBulkDelete(ids: $ids) {
|
||||
errors: pageErrors {
|
||||
field
|
||||
message
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const usePageTypeBulkDeleteMutation = makeMutation<
|
||||
PageTypeBulkDelete,
|
||||
PageTypeBulkDeleteVariables
|
||||
>(pageTypeBulkDeleteMutation);
|
||||
|
||||
export const pageTypeAttributeReorder = gql`
|
||||
${pageTypeDetailsFragment}
|
||||
${pageErrorFragment}
|
||||
mutation PageTypeAttributeReorder($move: ReorderInput!, $pageTypeId: ID!) {
|
||||
pageTypeReorderAttributes(moves: [$move], pageTypeId: $pageTypeId) {
|
||||
errors: pageErrors {
|
||||
...PageErrorFragment
|
||||
}
|
||||
pageType {
|
||||
...PageTypeDetailsFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const usePageTypeAttributeReorderMutation = makeMutation<
|
||||
PageTypeAttributeReorder,
|
||||
PageTypeAttributeReorderVariables
|
||||
>(pageTypeAttributeReorder);
|
63
src/pageTypes/queries.ts
Normal file
63
src/pageTypes/queries.ts
Normal file
|
@ -0,0 +1,63 @@
|
|||
import { pageInfoFragment } from "@saleor/fragments/pageInfo";
|
||||
import {
|
||||
pageTypeDetailsFragment,
|
||||
pageTypeFragment
|
||||
} from "@saleor/fragments/pageTypes";
|
||||
import makeQuery from "@saleor/hooks/makeQuery";
|
||||
import gql from "graphql-tag";
|
||||
|
||||
import {
|
||||
PageTypeDetails,
|
||||
PageTypeDetailsVariables
|
||||
} from "./types/PageTypeDetails";
|
||||
import { PageTypeList, PageTypeListVariables } from "./types/PageTypeList";
|
||||
|
||||
export const pageTypeListQuery = gql`
|
||||
${pageInfoFragment}
|
||||
${pageTypeFragment}
|
||||
query PageTypeList(
|
||||
$after: String
|
||||
$before: String
|
||||
$first: Int
|
||||
$last: Int
|
||||
$filter: PageTypeFilterInput
|
||||
$sort: PageTypeSortingInput
|
||||
) {
|
||||
pageTypes(
|
||||
after: $after
|
||||
before: $before
|
||||
first: $first
|
||||
last: $last
|
||||
filter: $filter
|
||||
sortBy: $sort
|
||||
) {
|
||||
edges {
|
||||
node {
|
||||
...PageTypeFragment
|
||||
hasPages
|
||||
}
|
||||
}
|
||||
pageInfo {
|
||||
...PageInfoFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const usePageTypeListQuery = makeQuery<
|
||||
PageTypeList,
|
||||
PageTypeListVariables
|
||||
>(pageTypeListQuery);
|
||||
|
||||
export const pageTypeDetailsQuery = gql`
|
||||
${pageTypeDetailsFragment}
|
||||
query PageTypeDetails($id: ID!) {
|
||||
pageType(id: $id) {
|
||||
...PageTypeDetailsFragment
|
||||
hasPages
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const usePageTypeDetailsQuery = makeQuery<
|
||||
PageTypeDetails,
|
||||
PageTypeDetailsVariables
|
||||
>(pageTypeDetailsQuery);
|
62
src/pageTypes/types/AssignPageAttribute.ts
Normal file
62
src/pageTypes/types/AssignPageAttribute.ts
Normal file
|
@ -0,0 +1,62 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { PageErrorCode, AttributeTypeEnum } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL mutation operation: AssignPageAttribute
|
||||
// ====================================================
|
||||
|
||||
export interface AssignPageAttribute_pageAttributeAssign_errors {
|
||||
__typename: "PageError";
|
||||
code: PageErrorCode;
|
||||
field: string | null;
|
||||
}
|
||||
|
||||
export interface AssignPageAttribute_pageAttributeAssign_pageType_metadata {
|
||||
__typename: "MetadataItem";
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface AssignPageAttribute_pageAttributeAssign_pageType_privateMetadata {
|
||||
__typename: "MetadataItem";
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface AssignPageAttribute_pageAttributeAssign_pageType_attributes {
|
||||
__typename: "Attribute";
|
||||
id: string;
|
||||
name: string | null;
|
||||
slug: string | null;
|
||||
type: AttributeTypeEnum | null;
|
||||
visibleInStorefront: boolean;
|
||||
filterableInDashboard: boolean;
|
||||
filterableInStorefront: boolean;
|
||||
}
|
||||
|
||||
export interface AssignPageAttribute_pageAttributeAssign_pageType {
|
||||
__typename: "PageType";
|
||||
id: string;
|
||||
name: string;
|
||||
metadata: (AssignPageAttribute_pageAttributeAssign_pageType_metadata | null)[];
|
||||
privateMetadata: (AssignPageAttribute_pageAttributeAssign_pageType_privateMetadata | null)[];
|
||||
attributes: (AssignPageAttribute_pageAttributeAssign_pageType_attributes | null)[] | null;
|
||||
}
|
||||
|
||||
export interface AssignPageAttribute_pageAttributeAssign {
|
||||
__typename: "PageAttributeAssign";
|
||||
errors: AssignPageAttribute_pageAttributeAssign_errors[];
|
||||
pageType: AssignPageAttribute_pageAttributeAssign_pageType | null;
|
||||
}
|
||||
|
||||
export interface AssignPageAttribute {
|
||||
pageAttributeAssign: AssignPageAttribute_pageAttributeAssign | null;
|
||||
}
|
||||
|
||||
export interface AssignPageAttributeVariables {
|
||||
id: string;
|
||||
ids: string[];
|
||||
}
|
62
src/pageTypes/types/PageTypeAttributeReorder.ts
Normal file
62
src/pageTypes/types/PageTypeAttributeReorder.ts
Normal file
|
@ -0,0 +1,62 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { ReorderInput, PageErrorCode, AttributeTypeEnum } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL mutation operation: PageTypeAttributeReorder
|
||||
// ====================================================
|
||||
|
||||
export interface PageTypeAttributeReorder_pageTypeReorderAttributes_errors {
|
||||
__typename: "PageError";
|
||||
code: PageErrorCode;
|
||||
field: string | null;
|
||||
}
|
||||
|
||||
export interface PageTypeAttributeReorder_pageTypeReorderAttributes_pageType_metadata {
|
||||
__typename: "MetadataItem";
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface PageTypeAttributeReorder_pageTypeReorderAttributes_pageType_privateMetadata {
|
||||
__typename: "MetadataItem";
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface PageTypeAttributeReorder_pageTypeReorderAttributes_pageType_attributes {
|
||||
__typename: "Attribute";
|
||||
id: string;
|
||||
name: string | null;
|
||||
slug: string | null;
|
||||
type: AttributeTypeEnum | null;
|
||||
visibleInStorefront: boolean;
|
||||
filterableInDashboard: boolean;
|
||||
filterableInStorefront: boolean;
|
||||
}
|
||||
|
||||
export interface PageTypeAttributeReorder_pageTypeReorderAttributes_pageType {
|
||||
__typename: "PageType";
|
||||
id: string;
|
||||
name: string;
|
||||
metadata: (PageTypeAttributeReorder_pageTypeReorderAttributes_pageType_metadata | null)[];
|
||||
privateMetadata: (PageTypeAttributeReorder_pageTypeReorderAttributes_pageType_privateMetadata | null)[];
|
||||
attributes: (PageTypeAttributeReorder_pageTypeReorderAttributes_pageType_attributes | null)[] | null;
|
||||
}
|
||||
|
||||
export interface PageTypeAttributeReorder_pageTypeReorderAttributes {
|
||||
__typename: "PageTypeReorderAttributes";
|
||||
errors: PageTypeAttributeReorder_pageTypeReorderAttributes_errors[];
|
||||
pageType: PageTypeAttributeReorder_pageTypeReorderAttributes_pageType | null;
|
||||
}
|
||||
|
||||
export interface PageTypeAttributeReorder {
|
||||
pageTypeReorderAttributes: PageTypeAttributeReorder_pageTypeReorderAttributes | null;
|
||||
}
|
||||
|
||||
export interface PageTypeAttributeReorderVariables {
|
||||
move: ReorderInput;
|
||||
pageTypeId: string;
|
||||
}
|
26
src/pageTypes/types/PageTypeBulkDelete.ts
Normal file
26
src/pageTypes/types/PageTypeBulkDelete.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
// ====================================================
|
||||
// GraphQL mutation operation: PageTypeBulkDelete
|
||||
// ====================================================
|
||||
|
||||
export interface PageTypeBulkDelete_pageTypeBulkDelete_errors {
|
||||
__typename: "PageError";
|
||||
field: string | null;
|
||||
message: string | null;
|
||||
}
|
||||
|
||||
export interface PageTypeBulkDelete_pageTypeBulkDelete {
|
||||
__typename: "PageTypeBulkDelete";
|
||||
errors: PageTypeBulkDelete_pageTypeBulkDelete_errors[];
|
||||
}
|
||||
|
||||
export interface PageTypeBulkDelete {
|
||||
pageTypeBulkDelete: PageTypeBulkDelete_pageTypeBulkDelete | null;
|
||||
}
|
||||
|
||||
export interface PageTypeBulkDeleteVariables {
|
||||
ids: string[];
|
||||
}
|
61
src/pageTypes/types/PageTypeCreate.ts
Normal file
61
src/pageTypes/types/PageTypeCreate.ts
Normal file
|
@ -0,0 +1,61 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { PageTypeCreateInput, PageErrorCode, AttributeTypeEnum } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL mutation operation: PageTypeCreate
|
||||
// ====================================================
|
||||
|
||||
export interface PageTypeCreate_pageTypeCreate_errors {
|
||||
__typename: "PageError";
|
||||
code: PageErrorCode;
|
||||
field: string | null;
|
||||
}
|
||||
|
||||
export interface PageTypeCreate_pageTypeCreate_pageType_metadata {
|
||||
__typename: "MetadataItem";
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface PageTypeCreate_pageTypeCreate_pageType_privateMetadata {
|
||||
__typename: "MetadataItem";
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface PageTypeCreate_pageTypeCreate_pageType_attributes {
|
||||
__typename: "Attribute";
|
||||
id: string;
|
||||
name: string | null;
|
||||
slug: string | null;
|
||||
type: AttributeTypeEnum | null;
|
||||
visibleInStorefront: boolean;
|
||||
filterableInDashboard: boolean;
|
||||
filterableInStorefront: boolean;
|
||||
}
|
||||
|
||||
export interface PageTypeCreate_pageTypeCreate_pageType {
|
||||
__typename: "PageType";
|
||||
id: string;
|
||||
name: string;
|
||||
metadata: (PageTypeCreate_pageTypeCreate_pageType_metadata | null)[];
|
||||
privateMetadata: (PageTypeCreate_pageTypeCreate_pageType_privateMetadata | null)[];
|
||||
attributes: (PageTypeCreate_pageTypeCreate_pageType_attributes | null)[] | null;
|
||||
}
|
||||
|
||||
export interface PageTypeCreate_pageTypeCreate {
|
||||
__typename: "PageTypeCreate";
|
||||
errors: PageTypeCreate_pageTypeCreate_errors[];
|
||||
pageType: PageTypeCreate_pageTypeCreate_pageType | null;
|
||||
}
|
||||
|
||||
export interface PageTypeCreate {
|
||||
pageTypeCreate: PageTypeCreate_pageTypeCreate | null;
|
||||
}
|
||||
|
||||
export interface PageTypeCreateVariables {
|
||||
input: PageTypeCreateInput;
|
||||
}
|
32
src/pageTypes/types/PageTypeDelete.ts
Normal file
32
src/pageTypes/types/PageTypeDelete.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
// ====================================================
|
||||
// GraphQL mutation operation: PageTypeDelete
|
||||
// ====================================================
|
||||
|
||||
export interface PageTypeDelete_pageTypeDelete_errors {
|
||||
__typename: "PageError";
|
||||
field: string | null;
|
||||
message: string | null;
|
||||
}
|
||||
|
||||
export interface PageTypeDelete_pageTypeDelete_pageType {
|
||||
__typename: "PageType";
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface PageTypeDelete_pageTypeDelete {
|
||||
__typename: "PageTypeDelete";
|
||||
errors: PageTypeDelete_pageTypeDelete_errors[];
|
||||
pageType: PageTypeDelete_pageTypeDelete_pageType | null;
|
||||
}
|
||||
|
||||
export interface PageTypeDelete {
|
||||
pageTypeDelete: PageTypeDelete_pageTypeDelete | null;
|
||||
}
|
||||
|
||||
export interface PageTypeDeleteVariables {
|
||||
id: string;
|
||||
}
|
50
src/pageTypes/types/PageTypeDetails.ts
Normal file
50
src/pageTypes/types/PageTypeDetails.ts
Normal file
|
@ -0,0 +1,50 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { AttributeTypeEnum } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: PageTypeDetails
|
||||
// ====================================================
|
||||
|
||||
export interface PageTypeDetails_pageType_metadata {
|
||||
__typename: "MetadataItem";
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface PageTypeDetails_pageType_privateMetadata {
|
||||
__typename: "MetadataItem";
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface PageTypeDetails_pageType_attributes {
|
||||
__typename: "Attribute";
|
||||
id: string;
|
||||
name: string | null;
|
||||
slug: string | null;
|
||||
type: AttributeTypeEnum | null;
|
||||
visibleInStorefront: boolean;
|
||||
filterableInDashboard: boolean;
|
||||
filterableInStorefront: boolean;
|
||||
}
|
||||
|
||||
export interface PageTypeDetails_pageType {
|
||||
__typename: "PageType";
|
||||
id: string;
|
||||
name: string;
|
||||
metadata: (PageTypeDetails_pageType_metadata | null)[];
|
||||
privateMetadata: (PageTypeDetails_pageType_privateMetadata | null)[];
|
||||
attributes: (PageTypeDetails_pageType_attributes | null)[] | null;
|
||||
hasPages: boolean | null;
|
||||
}
|
||||
|
||||
export interface PageTypeDetails {
|
||||
pageType: PageTypeDetails_pageType | null;
|
||||
}
|
||||
|
||||
export interface PageTypeDetailsVariables {
|
||||
id: string;
|
||||
}
|
48
src/pageTypes/types/PageTypeList.ts
Normal file
48
src/pageTypes/types/PageTypeList.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { PageTypeFilterInput, PageTypeSortingInput } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: PageTypeList
|
||||
// ====================================================
|
||||
|
||||
export interface PageTypeList_pageTypes_edges_node {
|
||||
__typename: "PageType";
|
||||
id: string;
|
||||
name: string;
|
||||
hasPages: boolean | null;
|
||||
}
|
||||
|
||||
export interface PageTypeList_pageTypes_edges {
|
||||
__typename: "PageTypeCountableEdge";
|
||||
node: PageTypeList_pageTypes_edges_node;
|
||||
}
|
||||
|
||||
export interface PageTypeList_pageTypes_pageInfo {
|
||||
__typename: "PageInfo";
|
||||
endCursor: string | null;
|
||||
hasNextPage: boolean;
|
||||
hasPreviousPage: boolean;
|
||||
startCursor: string | null;
|
||||
}
|
||||
|
||||
export interface PageTypeList_pageTypes {
|
||||
__typename: "PageTypeCountableConnection";
|
||||
edges: PageTypeList_pageTypes_edges[];
|
||||
pageInfo: PageTypeList_pageTypes_pageInfo;
|
||||
}
|
||||
|
||||
export interface PageTypeList {
|
||||
pageTypes: PageTypeList_pageTypes | null;
|
||||
}
|
||||
|
||||
export interface PageTypeListVariables {
|
||||
after?: string | null;
|
||||
before?: string | null;
|
||||
first?: number | null;
|
||||
last?: number | null;
|
||||
filter?: PageTypeFilterInput | null;
|
||||
sort?: PageTypeSortingInput | null;
|
||||
}
|
62
src/pageTypes/types/PageTypeUpdate.ts
Normal file
62
src/pageTypes/types/PageTypeUpdate.ts
Normal file
|
@ -0,0 +1,62 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { PageTypeUpdateInput, PageErrorCode, AttributeTypeEnum } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL mutation operation: PageTypeUpdate
|
||||
// ====================================================
|
||||
|
||||
export interface PageTypeUpdate_pageTypeUpdate_errors {
|
||||
__typename: "PageError";
|
||||
code: PageErrorCode;
|
||||
field: string | null;
|
||||
}
|
||||
|
||||
export interface PageTypeUpdate_pageTypeUpdate_pageType_metadata {
|
||||
__typename: "MetadataItem";
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface PageTypeUpdate_pageTypeUpdate_pageType_privateMetadata {
|
||||
__typename: "MetadataItem";
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface PageTypeUpdate_pageTypeUpdate_pageType_attributes {
|
||||
__typename: "Attribute";
|
||||
id: string;
|
||||
name: string | null;
|
||||
slug: string | null;
|
||||
type: AttributeTypeEnum | null;
|
||||
visibleInStorefront: boolean;
|
||||
filterableInDashboard: boolean;
|
||||
filterableInStorefront: boolean;
|
||||
}
|
||||
|
||||
export interface PageTypeUpdate_pageTypeUpdate_pageType {
|
||||
__typename: "PageType";
|
||||
id: string;
|
||||
name: string;
|
||||
metadata: (PageTypeUpdate_pageTypeUpdate_pageType_metadata | null)[];
|
||||
privateMetadata: (PageTypeUpdate_pageTypeUpdate_pageType_privateMetadata | null)[];
|
||||
attributes: (PageTypeUpdate_pageTypeUpdate_pageType_attributes | null)[] | null;
|
||||
}
|
||||
|
||||
export interface PageTypeUpdate_pageTypeUpdate {
|
||||
__typename: "PageTypeUpdate";
|
||||
errors: PageTypeUpdate_pageTypeUpdate_errors[];
|
||||
pageType: PageTypeUpdate_pageTypeUpdate_pageType | null;
|
||||
}
|
||||
|
||||
export interface PageTypeUpdate {
|
||||
pageTypeUpdate: PageTypeUpdate_pageTypeUpdate | null;
|
||||
}
|
||||
|
||||
export interface PageTypeUpdateVariables {
|
||||
id: string;
|
||||
input: PageTypeUpdateInput;
|
||||
}
|
62
src/pageTypes/types/UnassignPageAttribute.ts
Normal file
62
src/pageTypes/types/UnassignPageAttribute.ts
Normal file
|
@ -0,0 +1,62 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { PageErrorCode, AttributeTypeEnum } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL mutation operation: UnassignPageAttribute
|
||||
// ====================================================
|
||||
|
||||
export interface UnassignPageAttribute_pageAttributeUnassign_errors {
|
||||
__typename: "PageError";
|
||||
code: PageErrorCode;
|
||||
field: string | null;
|
||||
}
|
||||
|
||||
export interface UnassignPageAttribute_pageAttributeUnassign_pageType_metadata {
|
||||
__typename: "MetadataItem";
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface UnassignPageAttribute_pageAttributeUnassign_pageType_privateMetadata {
|
||||
__typename: "MetadataItem";
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface UnassignPageAttribute_pageAttributeUnassign_pageType_attributes {
|
||||
__typename: "Attribute";
|
||||
id: string;
|
||||
name: string | null;
|
||||
slug: string | null;
|
||||
type: AttributeTypeEnum | null;
|
||||
visibleInStorefront: boolean;
|
||||
filterableInDashboard: boolean;
|
||||
filterableInStorefront: boolean;
|
||||
}
|
||||
|
||||
export interface UnassignPageAttribute_pageAttributeUnassign_pageType {
|
||||
__typename: "PageType";
|
||||
id: string;
|
||||
name: string;
|
||||
metadata: (UnassignPageAttribute_pageAttributeUnassign_pageType_metadata | null)[];
|
||||
privateMetadata: (UnassignPageAttribute_pageAttributeUnassign_pageType_privateMetadata | null)[];
|
||||
attributes: (UnassignPageAttribute_pageAttributeUnassign_pageType_attributes | null)[] | null;
|
||||
}
|
||||
|
||||
export interface UnassignPageAttribute_pageAttributeUnassign {
|
||||
__typename: "PageAttributeUnassign";
|
||||
errors: UnassignPageAttribute_pageAttributeUnassign_errors[];
|
||||
pageType: UnassignPageAttribute_pageAttributeUnassign_pageType | null;
|
||||
}
|
||||
|
||||
export interface UnassignPageAttribute {
|
||||
pageAttributeUnassign: UnassignPageAttribute_pageAttributeUnassign | null;
|
||||
}
|
||||
|
||||
export interface UnassignPageAttributeVariables {
|
||||
id: string;
|
||||
ids: string[];
|
||||
}
|
52
src/pageTypes/urls.ts
Normal file
52
src/pageTypes/urls.ts
Normal file
|
@ -0,0 +1,52 @@
|
|||
import { stringify as stringifyQs } from "qs";
|
||||
import urlJoin from "url-join";
|
||||
|
||||
import {
|
||||
ActiveTab,
|
||||
BulkAction,
|
||||
Dialog,
|
||||
Filters,
|
||||
Pagination,
|
||||
SingleAction,
|
||||
Sort,
|
||||
TabActionDialog
|
||||
} from "../types";
|
||||
|
||||
const pageTypeSection = "/page-types/";
|
||||
|
||||
export const pageTypeListPath = pageTypeSection;
|
||||
export enum PageTypeListUrlFiltersEnum {
|
||||
type = "type",
|
||||
query = "query"
|
||||
}
|
||||
export type PageTypeListUrlFilters = Filters<PageTypeListUrlFiltersEnum>;
|
||||
export type PageTypeListUrlDialog = "remove" | TabActionDialog;
|
||||
export enum PageTypeListUrlSortField {
|
||||
name = "name"
|
||||
}
|
||||
export type PageTypeListUrlSort = Sort<PageTypeListUrlSortField>;
|
||||
export type PageTypeListUrlQueryParams = ActiveTab &
|
||||
BulkAction &
|
||||
Dialog<PageTypeListUrlDialog> &
|
||||
Pagination &
|
||||
PageTypeListUrlFilters &
|
||||
PageTypeListUrlSort;
|
||||
export const pageTypeListUrl = (params?: PageTypeListUrlQueryParams) =>
|
||||
pageTypeListPath + "?" + stringifyQs(params);
|
||||
|
||||
export const pageTypeAddPath = urlJoin(pageTypeSection, "add");
|
||||
export const pageTypeAddUrl = pageTypeAddPath;
|
||||
|
||||
export const pageTypePath = (id: string) => urlJoin(pageTypeSection, id);
|
||||
export type PageTypeUrlDialog =
|
||||
| "assign-attribute"
|
||||
| "unassign-attribute"
|
||||
| "unassign-attributes"
|
||||
| "remove";
|
||||
export type PageTypeUrlQueryParams = BulkAction &
|
||||
Dialog<PageTypeUrlDialog> &
|
||||
SingleAction & {
|
||||
type?: string;
|
||||
};
|
||||
export const pageTypeUrl = (id: string, params?: PageTypeUrlQueryParams) =>
|
||||
pageTypePath(encodeURIComponent(id)) + "?" + stringifyQs(params);
|
76
src/pageTypes/views/PageTypeCreate.tsx
Normal file
76
src/pageTypes/views/PageTypeCreate.tsx
Normal file
|
@ -0,0 +1,76 @@
|
|||
import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import useNotifier from "@saleor/hooks/useNotifier";
|
||||
import createMetadataCreateHandler from "@saleor/utils/handlers/metadataCreateHandler";
|
||||
import {
|
||||
useMetadataUpdate,
|
||||
usePrivateMetadataUpdate
|
||||
} from "@saleor/utils/metadata/updateMetadata";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import PageTypeCreatePage, {
|
||||
PageTypeForm
|
||||
} from "../components/PageTypeCreatePage";
|
||||
import { usePageTypeCreateMutation } from "../mutations";
|
||||
import { PageTypeCreate as PageTypeCreateMutation } from "../types/PageTypeCreate";
|
||||
import { pageTypeListUrl, pageTypeUrl } from "../urls";
|
||||
|
||||
export const PageTypeCreate: React.FC = () => {
|
||||
const navigate = useNavigator();
|
||||
const notify = useNotifier();
|
||||
const intl = useIntl();
|
||||
const [updateMetadata] = useMetadataUpdate({});
|
||||
const [updatePrivateMetadata] = usePrivateMetadataUpdate({});
|
||||
|
||||
const [createPageType, createPageTypeOpts] = usePageTypeCreateMutation({
|
||||
onCompleted: (updateData: PageTypeCreateMutation) => {
|
||||
if (updateData.pageTypeCreate.errors.length === 0) {
|
||||
notify({
|
||||
status: "success",
|
||||
text: intl.formatMessage({
|
||||
defaultMessage: "Successfully created page type"
|
||||
})
|
||||
});
|
||||
navigate(pageTypeUrl(updateData.pageTypeCreate.pageType.id));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const handleCreate = async (formData: PageTypeForm) => {
|
||||
const result = await createPageType({
|
||||
variables: {
|
||||
input: {
|
||||
name: formData.name
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return result.data?.pageTypeCreate.pageType?.id || null;
|
||||
};
|
||||
const handleSubmit = createMetadataCreateHandler(
|
||||
handleCreate,
|
||||
updateMetadata,
|
||||
updatePrivateMetadata
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<WindowTitle
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Create Page Type",
|
||||
description: "window title",
|
||||
id: "pageTypeCreateHeader"
|
||||
})}
|
||||
/>
|
||||
<PageTypeCreatePage
|
||||
disabled={createPageTypeOpts.loading}
|
||||
errors={createPageTypeOpts.data?.pageTypeCreate.errors || []}
|
||||
saveButtonBarState={createPageTypeOpts.status}
|
||||
onBack={() => navigate(pageTypeListUrl())}
|
||||
onSubmit={handleSubmit}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default PageTypeCreate;
|
324
src/pageTypes/views/PageTypeDetails.tsx
Normal file
324
src/pageTypes/views/PageTypeDetails.tsx
Normal file
|
@ -0,0 +1,324 @@
|
|||
import Button from "@material-ui/core/Button";
|
||||
import { attributeUrl } from "@saleor/attributes/urls";
|
||||
import AssignAttributeDialog from "@saleor/components/AssignAttributeDialog";
|
||||
import AttributeUnassignDialog from "@saleor/components/AttributeUnassignDialog";
|
||||
import BulkAttributeUnassignDialog from "@saleor/components/BulkAttributeUnassignDialog";
|
||||
import NotFoundPage from "@saleor/components/NotFoundPage";
|
||||
import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||
import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
|
||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import useNotifier from "@saleor/hooks/useNotifier";
|
||||
import { commonMessages } from "@saleor/intl";
|
||||
import { getStringOrPlaceholder } from "@saleor/misc";
|
||||
import PageTypeDeleteDialog from "@saleor/pageTypes/components/PageTypeDeleteDialog";
|
||||
import {
|
||||
useAssignPageAttributeMutation,
|
||||
usePageTypeAttributeReorderMutation,
|
||||
usePageTypeDeleteMutation,
|
||||
usePageTypeUpdateMutation,
|
||||
useUnassignPageAttributeMutation
|
||||
} from "@saleor/pageTypes/mutations";
|
||||
import { ReorderEvent } from "@saleor/types";
|
||||
import getPageErrorMessage from "@saleor/utils/errors/page";
|
||||
import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler";
|
||||
import {
|
||||
useMetadataUpdate,
|
||||
usePrivateMetadataUpdate
|
||||
} from "@saleor/utils/metadata/updateMetadata";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import PageTypeDetailsPage, {
|
||||
PageTypeForm
|
||||
} from "../components/PageTypeDetailsPage";
|
||||
import useAvailablePageAttributeSearch from "../hooks/useAvailablePageAttributeSearch";
|
||||
import { usePageTypeDetailsQuery } from "../queries";
|
||||
import { pageTypeListUrl, pageTypeUrl, PageTypeUrlQueryParams } from "../urls";
|
||||
|
||||
interface PageTypeDetailsProps {
|
||||
id: string;
|
||||
params: PageTypeUrlQueryParams;
|
||||
}
|
||||
|
||||
export const PageTypeDetails: React.FC<PageTypeDetailsProps> = ({
|
||||
id,
|
||||
params
|
||||
}) => {
|
||||
const navigate = useNavigator();
|
||||
const notify = useNotifier();
|
||||
const attributeListActions = useBulkActions();
|
||||
const intl = useIntl();
|
||||
|
||||
const [updatePageType, updatePageTypeOpts] = usePageTypeUpdateMutation({
|
||||
onCompleted: updateData => {
|
||||
if (
|
||||
!updateData.pageTypeUpdate.errors ||
|
||||
updateData.pageTypeUpdate.errors.length === 0
|
||||
) {
|
||||
notify({
|
||||
status: "success",
|
||||
text: intl.formatMessage(commonMessages.savedChanges)
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
const [deletePageType, deletePageTypeOpts] = usePageTypeDeleteMutation({
|
||||
onCompleted: deleteData => {
|
||||
if (deleteData.pageTypeDelete.errors.length === 0) {
|
||||
notify({
|
||||
status: "success",
|
||||
text: intl.formatMessage({
|
||||
defaultMessage: "Page type deleted"
|
||||
})
|
||||
});
|
||||
navigate(pageTypeListUrl(), true);
|
||||
}
|
||||
}
|
||||
});
|
||||
const [assignAttribute, assignAttributeOpts] = useAssignPageAttributeMutation(
|
||||
{
|
||||
onCompleted: data => {
|
||||
if (data.pageAttributeAssign.errors.length === 0) {
|
||||
notify({
|
||||
status: "success",
|
||||
text: intl.formatMessage(commonMessages.savedChanges)
|
||||
});
|
||||
closeModal();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
const [
|
||||
unassignAttribute,
|
||||
unassignAttributeOpts
|
||||
] = useUnassignPageAttributeMutation({
|
||||
onCompleted: data => {
|
||||
if (data.pageAttributeUnassign.errors.length === 0) {
|
||||
notify({
|
||||
status: "success",
|
||||
text: intl.formatMessage(commonMessages.savedChanges)
|
||||
});
|
||||
closeModal();
|
||||
attributeListActions.reset();
|
||||
}
|
||||
}
|
||||
});
|
||||
const [reorderAttribute] = usePageTypeAttributeReorderMutation({});
|
||||
|
||||
const [updateMetadata] = useMetadataUpdate({});
|
||||
const [updatePrivateMetadata] = usePrivateMetadataUpdate({});
|
||||
|
||||
const handleBack = () => navigate(pageTypeListUrl());
|
||||
|
||||
const handlePageTypeUpdate = async (formData: PageTypeForm) => {
|
||||
const result = await updatePageType({
|
||||
variables: {
|
||||
id,
|
||||
input: {
|
||||
name: formData.name
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return result.data.pageTypeUpdate.errors;
|
||||
};
|
||||
const handlePageTypeDelete = () => deletePageType({ variables: { id } });
|
||||
const handleAssignAttribute = () =>
|
||||
assignAttribute({
|
||||
variables: {
|
||||
id,
|
||||
ids: params.ids
|
||||
}
|
||||
});
|
||||
const handleAttributeUnassign = () =>
|
||||
unassignAttribute({
|
||||
variables: {
|
||||
id,
|
||||
ids: [params.id]
|
||||
}
|
||||
});
|
||||
const handleBulkAttributeUnassign = () =>
|
||||
unassignAttribute({
|
||||
variables: {
|
||||
id,
|
||||
ids: params.ids
|
||||
}
|
||||
});
|
||||
const handleAttributeReorder = (event: ReorderEvent) =>
|
||||
reorderAttribute({
|
||||
variables: {
|
||||
move: {
|
||||
id: data.pageType.attributes[event.oldIndex].id,
|
||||
sortOrder: event.newIndex - event.oldIndex
|
||||
},
|
||||
pageTypeId: id
|
||||
}
|
||||
});
|
||||
|
||||
const { data, loading: dataLoading } = usePageTypeDetailsQuery({
|
||||
variables: { id }
|
||||
});
|
||||
|
||||
const { loadMore, search, result } = useAvailablePageAttributeSearch({
|
||||
variables: {
|
||||
...DEFAULT_INITIAL_SEARCH_DATA,
|
||||
id
|
||||
}
|
||||
});
|
||||
|
||||
const pageType = data?.pageType;
|
||||
|
||||
if (pageType === null) {
|
||||
return <NotFoundPage onBack={handleBack} />;
|
||||
}
|
||||
|
||||
const closeModal = () => navigate(pageTypeUrl(id), true);
|
||||
|
||||
const handleSubmit = createMetadataUpdateHandler(
|
||||
data?.pageType,
|
||||
handlePageTypeUpdate,
|
||||
variables => updateMetadata({ variables }),
|
||||
variables => updatePrivateMetadata({ variables })
|
||||
);
|
||||
|
||||
const loading = updatePageTypeOpts.loading || dataLoading;
|
||||
|
||||
return (
|
||||
<>
|
||||
<WindowTitle title={data?.pageType.name} />
|
||||
<PageTypeDetailsPage
|
||||
disabled={loading}
|
||||
errors={updatePageTypeOpts.data?.pageTypeUpdate.errors}
|
||||
pageTitle={data?.pageType.name}
|
||||
pageType={data?.pageType}
|
||||
saveButtonBarState={updatePageTypeOpts.status}
|
||||
onAttributeAdd={type =>
|
||||
navigate(
|
||||
pageTypeUrl(id, {
|
||||
action: "assign-attribute",
|
||||
type
|
||||
})
|
||||
)
|
||||
}
|
||||
onAttributeClick={attributeId => navigate(attributeUrl(attributeId))}
|
||||
onAttributeReorder={handleAttributeReorder}
|
||||
onAttributeUnassign={attributeId =>
|
||||
navigate(
|
||||
pageTypeUrl(id, {
|
||||
action: "unassign-attribute",
|
||||
id: attributeId
|
||||
})
|
||||
)
|
||||
}
|
||||
onBack={handleBack}
|
||||
onDelete={() =>
|
||||
navigate(
|
||||
pageTypeUrl(id, {
|
||||
action: "remove"
|
||||
})
|
||||
)
|
||||
}
|
||||
onSubmit={handleSubmit}
|
||||
attributeList={{
|
||||
isChecked: attributeListActions.isSelected,
|
||||
selected: attributeListActions.listElements.length,
|
||||
toggle: attributeListActions.toggle,
|
||||
toggleAll: attributeListActions.toggleAll,
|
||||
toolbar: (
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={() =>
|
||||
navigate(
|
||||
pageTypeUrl(id, {
|
||||
action: "unassign-attributes",
|
||||
ids: attributeListActions.listElements
|
||||
})
|
||||
)
|
||||
}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Unassign"
|
||||
description="unassign attribute from page type, button"
|
||||
/>
|
||||
</Button>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<PageTypeDeleteDialog
|
||||
confirmButtonState={deletePageTypeOpts.status}
|
||||
name={getStringOrPlaceholder(data?.pageType.name)}
|
||||
hasPages={data?.pageType.hasPages}
|
||||
open={params.action === "remove"}
|
||||
onClose={() => navigate(pageTypeUrl(id))}
|
||||
onConfirm={handlePageTypeDelete}
|
||||
/>
|
||||
{!dataLoading && (
|
||||
<AssignAttributeDialog
|
||||
attributes={result.data?.pageType.availableAttributes.edges.map(
|
||||
edge => edge.node
|
||||
)}
|
||||
confirmButtonState={assignAttributeOpts.status}
|
||||
errors={
|
||||
assignAttributeOpts.data?.pageAttributeAssign.errors
|
||||
? assignAttributeOpts.data.pageAttributeAssign.errors.map(err =>
|
||||
getPageErrorMessage(err, intl)
|
||||
)
|
||||
: []
|
||||
}
|
||||
loading={result.loading}
|
||||
onClose={closeModal}
|
||||
onSubmit={handleAssignAttribute}
|
||||
onFetch={search}
|
||||
onFetchMore={loadMore}
|
||||
onOpen={result.refetch}
|
||||
hasMore={
|
||||
!!result.data?.pageType.availableAttributes.pageInfo.hasNextPage
|
||||
}
|
||||
open={params.action === "assign-attribute"}
|
||||
selected={params.ids || []}
|
||||
onToggle={attributeId => {
|
||||
const ids = params.ids || [];
|
||||
navigate(
|
||||
pageTypeUrl(id, {
|
||||
...params,
|
||||
ids: ids.includes(attributeId)
|
||||
? params.ids.filter(selectedId => selectedId !== attributeId)
|
||||
: [...ids, attributeId]
|
||||
})
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<BulkAttributeUnassignDialog
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Unassign Attribute from Page Type",
|
||||
description: "dialog header"
|
||||
})}
|
||||
attributeQuantity={params.ids?.length}
|
||||
confirmButtonState={unassignAttributeOpts.status}
|
||||
onClose={closeModal}
|
||||
onConfirm={handleBulkAttributeUnassign}
|
||||
open={params.action === "unassign-attributes"}
|
||||
itemTypeName={getStringOrPlaceholder(data?.pageType.name)}
|
||||
/>
|
||||
<AttributeUnassignDialog
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Unassign Attribute From Page Type",
|
||||
description: "dialog header"
|
||||
})}
|
||||
attributeName={getStringOrPlaceholder(
|
||||
data?.pageType.attributes.find(
|
||||
attribute => attribute.id === params.id
|
||||
)?.name
|
||||
)}
|
||||
confirmButtonState={unassignAttributeOpts.status}
|
||||
onClose={closeModal}
|
||||
onConfirm={handleAttributeUnassign}
|
||||
open={params.action === "unassign-attribute"}
|
||||
itemTypeName={getStringOrPlaceholder(data?.pageType.name)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default PageTypeDetails;
|
226
src/pageTypes/views/PageTypeList/PageTypeList.tsx
Normal file
226
src/pageTypes/views/PageTypeList/PageTypeList.tsx
Normal file
|
@ -0,0 +1,226 @@
|
|||
import IconButton from "@material-ui/core/IconButton";
|
||||
import DeleteIcon from "@material-ui/icons/Delete";
|
||||
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
|
||||
import SaveFilterTabDialog, {
|
||||
SaveFilterTabDialogFormData
|
||||
} from "@saleor/components/SaveFilterTabDialog";
|
||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||
import useListSettings from "@saleor/hooks/useListSettings";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import useNotifier from "@saleor/hooks/useNotifier";
|
||||
import usePaginator, {
|
||||
createPaginationState
|
||||
} from "@saleor/hooks/usePaginator";
|
||||
import { commonMessages } from "@saleor/intl";
|
||||
import { getStringOrPlaceholder } from "@saleor/misc";
|
||||
import PageTypeBulkDeleteDialog from "@saleor/pageTypes/components/PageTypeBulkDeleteDialog";
|
||||
import { usePageTypeBulkDeleteMutation } from "@saleor/pageTypes/mutations";
|
||||
import { ListViews } from "@saleor/types";
|
||||
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
|
||||
import createSortHandler from "@saleor/utils/handlers/sortHandler";
|
||||
import { getSortParams } from "@saleor/utils/sort";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import { configurationMenuUrl } from "../../../configuration";
|
||||
import PageTypeListPage from "../../components/PageTypeListPage";
|
||||
import { usePageTypeListQuery } from "../../queries";
|
||||
import {
|
||||
pageTypeAddUrl,
|
||||
pageTypeListUrl,
|
||||
PageTypeListUrlDialog,
|
||||
PageTypeListUrlFilters,
|
||||
PageTypeListUrlQueryParams,
|
||||
pageTypeUrl
|
||||
} from "../../urls";
|
||||
import {
|
||||
areFiltersApplied,
|
||||
deleteFilterTab,
|
||||
getActiveFilters,
|
||||
getFilterTabs,
|
||||
getFilterVariables,
|
||||
saveFilterTab
|
||||
} from "./filters";
|
||||
import { getSortQueryVariables } from "./sort";
|
||||
|
||||
interface PageTypeListProps {
|
||||
params: PageTypeListUrlQueryParams;
|
||||
}
|
||||
|
||||
export const PageTypeList: React.FC<PageTypeListProps> = ({ params }) => {
|
||||
const navigate = useNavigator();
|
||||
const paginate = usePaginator();
|
||||
const notify = useNotifier();
|
||||
const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions(
|
||||
params.ids
|
||||
);
|
||||
const intl = useIntl();
|
||||
const { settings } = useListSettings(ListViews.PAGES_LIST);
|
||||
|
||||
const paginationState = createPaginationState(settings.rowNumber, params);
|
||||
const queryVariables = React.useMemo(
|
||||
() => ({
|
||||
...paginationState,
|
||||
filter: getFilterVariables(params),
|
||||
sort: getSortQueryVariables(params)
|
||||
}),
|
||||
[params]
|
||||
);
|
||||
const { data, loading, refetch } = usePageTypeListQuery({
|
||||
displayLoader: true,
|
||||
variables: queryVariables
|
||||
});
|
||||
|
||||
const tabs = getFilterTabs();
|
||||
|
||||
const currentTab =
|
||||
params.activeTab === undefined
|
||||
? areFiltersApplied(params)
|
||||
? tabs.length + 1
|
||||
: 0
|
||||
: parseInt(params.activeTab, 0);
|
||||
|
||||
const changeFilterField = (filter: PageTypeListUrlFilters) => {
|
||||
reset();
|
||||
navigate(
|
||||
pageTypeListUrl({
|
||||
...getActiveFilters(params),
|
||||
...filter,
|
||||
activeTab: undefined
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const [openModal, closeModal] = createDialogActionHandlers<
|
||||
PageTypeListUrlDialog,
|
||||
PageTypeListUrlQueryParams
|
||||
>(navigate, pageTypeListUrl, params);
|
||||
|
||||
const handleTabChange = (tab: number) => {
|
||||
reset();
|
||||
navigate(
|
||||
pageTypeListUrl({
|
||||
activeTab: tab.toString(),
|
||||
...getFilterTabs()[tab - 1].data
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleTabDelete = () => {
|
||||
deleteFilterTab(currentTab);
|
||||
reset();
|
||||
navigate(pageTypeListUrl());
|
||||
};
|
||||
|
||||
const handleTabSave = (data: SaveFilterTabDialogFormData) => {
|
||||
saveFilterTab(data.name, getActiveFilters(params));
|
||||
handleTabChange(tabs.length + 1);
|
||||
};
|
||||
|
||||
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
||||
data?.pageTypes?.pageInfo,
|
||||
paginationState,
|
||||
params
|
||||
);
|
||||
|
||||
const handleSort = createSortHandler(navigate, pageTypeListUrl, params);
|
||||
|
||||
const [
|
||||
pageTypeBulkDelete,
|
||||
pageTypeBulkDeleteOpts
|
||||
] = usePageTypeBulkDeleteMutation({
|
||||
onCompleted: data => {
|
||||
if (data.pageTypeBulkDelete.errors.length === 0) {
|
||||
notify({
|
||||
status: "success",
|
||||
text: intl.formatMessage(commonMessages.savedChanges)
|
||||
});
|
||||
reset();
|
||||
refetch();
|
||||
navigate(
|
||||
pageTypeListUrl({
|
||||
...params,
|
||||
action: undefined,
|
||||
ids: undefined
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const hanldePageTypeBulkDelete = () =>
|
||||
pageTypeBulkDelete({
|
||||
variables: {
|
||||
ids: params.ids
|
||||
}
|
||||
});
|
||||
|
||||
const selectedPageTypesHasPages = data?.pageTypes.edges.some(
|
||||
pageType =>
|
||||
pageType.node.hasPages && params.ids?.some(id => id === pageType.node.id)
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageTypeListPage
|
||||
currentTab={currentTab}
|
||||
initialSearch={params.query || ""}
|
||||
onSearchChange={query => changeFilterField({ query })}
|
||||
onAll={() => navigate(pageTypeListUrl())}
|
||||
onTabChange={handleTabChange}
|
||||
onTabDelete={() => openModal("delete-search")}
|
||||
onTabSave={() => openModal("save-search")}
|
||||
tabs={tabs.map(tab => tab.name)}
|
||||
disabled={loading}
|
||||
pageTypes={data?.pageTypes?.edges?.map(edge => edge.node)}
|
||||
pageInfo={pageInfo}
|
||||
onAdd={() => navigate(pageTypeAddUrl)}
|
||||
onBack={() => navigate(configurationMenuUrl)}
|
||||
onNextPage={loadNextPage}
|
||||
onPreviousPage={loadPreviousPage}
|
||||
onRowClick={id => () => navigate(pageTypeUrl(id))}
|
||||
onSort={handleSort}
|
||||
isChecked={isSelected}
|
||||
selected={listElements.length}
|
||||
sort={getSortParams(params)}
|
||||
toggle={toggle}
|
||||
toggleAll={toggleAll}
|
||||
toolbar={
|
||||
<IconButton
|
||||
color="primary"
|
||||
onClick={() =>
|
||||
openModal("remove", {
|
||||
ids: listElements
|
||||
})
|
||||
}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
}
|
||||
/>
|
||||
<PageTypeBulkDeleteDialog
|
||||
confirmButtonState={pageTypeBulkDeleteOpts.status}
|
||||
quantity={params.ids?.length}
|
||||
hasPages={selectedPageTypesHasPages}
|
||||
open={params.action === "remove"}
|
||||
onClose={closeModal}
|
||||
onConfirm={hanldePageTypeBulkDelete}
|
||||
/>
|
||||
<SaveFilterTabDialog
|
||||
open={params.action === "save-search"}
|
||||
confirmButtonState="default"
|
||||
onClose={closeModal}
|
||||
onSubmit={handleTabSave}
|
||||
/>
|
||||
<DeleteFilterTabDialog
|
||||
open={params.action === "delete-search"}
|
||||
confirmButtonState="default"
|
||||
onClose={closeModal}
|
||||
onSubmit={handleTabDelete}
|
||||
tabName={getStringOrPlaceholder(tabs[currentTab - 1]?.name)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
PageTypeList.displayName = "PageTypeList";
|
||||
export default PageTypeList;
|
32
src/pageTypes/views/PageTypeList/filters.ts
Normal file
32
src/pageTypes/views/PageTypeList/filters.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import { PageTypeFilterInput } from "@saleor/types/globalTypes";
|
||||
|
||||
import {
|
||||
createFilterTabUtils,
|
||||
createFilterUtils
|
||||
} from "../../../utils/filters";
|
||||
import {
|
||||
PageTypeListUrlFilters,
|
||||
PageTypeListUrlFiltersEnum,
|
||||
PageTypeListUrlQueryParams
|
||||
} from "../../urls";
|
||||
|
||||
export const PAGE_TYPE_FILTERS_KEY = "pageTypeFilters";
|
||||
|
||||
export function getFilterVariables(
|
||||
params: PageTypeListUrlFilters
|
||||
): PageTypeFilterInput {
|
||||
return {
|
||||
search: params.query
|
||||
};
|
||||
}
|
||||
|
||||
export const {
|
||||
deleteFilterTab,
|
||||
getFilterTabs,
|
||||
saveFilterTab
|
||||
} = createFilterTabUtils<PageTypeListUrlFilters>(PAGE_TYPE_FILTERS_KEY);
|
||||
|
||||
export const { areFiltersApplied, getActiveFilters } = createFilterUtils<
|
||||
PageTypeListUrlQueryParams,
|
||||
PageTypeListUrlFilters
|
||||
>(PageTypeListUrlFiltersEnum);
|
2
src/pageTypes/views/PageTypeList/index.ts
Normal file
2
src/pageTypes/views/PageTypeList/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./PageTypeList";
|
||||
export * from "./PageTypeList";
|
18
src/pageTypes/views/PageTypeList/sort.ts
Normal file
18
src/pageTypes/views/PageTypeList/sort.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { PageTypeListUrlSortField } from "@saleor/pageTypes/urls";
|
||||
import { PageTypeSortField } from "@saleor/types/globalTypes";
|
||||
import { createGetSortQueryVariables } from "@saleor/utils/sort";
|
||||
|
||||
export function getSortQueryField(
|
||||
sort: PageTypeListUrlSortField
|
||||
): PageTypeSortField {
|
||||
switch (sort) {
|
||||
case PageTypeListUrlSortField.name:
|
||||
return PageTypeSortField.NAME;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export const getSortQueryVariables = createGetSortQueryVariables(
|
||||
getSortQueryField
|
||||
);
|
0
src/pageTypes/views/PageTypeUpdate/PageTypeUpdate.tsx
Normal file
0
src/pageTypes/views/PageTypeUpdate/PageTypeUpdate.tsx
Normal file
247
src/pages/components/PageAttributes/PageAttributes.tsx
Normal file
247
src/pages/components/PageAttributes/PageAttributes.tsx
Normal file
|
@ -0,0 +1,247 @@
|
|||
import Card from "@material-ui/core/Card";
|
||||
import CardContent from "@material-ui/core/CardContent";
|
||||
import IconButton from "@material-ui/core/IconButton";
|
||||
import makeStyles from "@material-ui/core/styles/makeStyles";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import ArrowDropDownIcon from "@material-ui/icons/ArrowDropDown";
|
||||
import CardTitle from "@saleor/components/CardTitle";
|
||||
import Grid from "@saleor/components/Grid";
|
||||
import Hr from "@saleor/components/Hr";
|
||||
import MultiAutocompleteSelectField, {
|
||||
MultiAutocompleteChoiceType
|
||||
} from "@saleor/components/MultiAutocompleteSelectField";
|
||||
import SingleAutocompleteSelectField, {
|
||||
SingleAutocompleteChoiceType
|
||||
} from "@saleor/components/SingleAutocompleteSelectField";
|
||||
import { PageDetailsFragment_pageType_attributes_values } from "@saleor/fragments/types/PageDetailsFragment";
|
||||
import { PageErrorWithAttributesFragment } from "@saleor/fragments/types/PageErrorWithAttributesFragment";
|
||||
import { FormsetAtomicData, FormsetChange } from "@saleor/hooks/useFormset";
|
||||
import { AttributeInputTypeEnum } from "@saleor/types/globalTypes";
|
||||
import getPageErrorMessage from "@saleor/utils/errors/page";
|
||||
import classNames from "classnames";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
export interface PageAttributeInputData {
|
||||
inputType: AttributeInputTypeEnum;
|
||||
isRequired: boolean;
|
||||
values: PageDetailsFragment_pageType_attributes_values[];
|
||||
}
|
||||
export type PageAttributeInput = FormsetAtomicData<
|
||||
PageAttributeInputData,
|
||||
string[]
|
||||
>;
|
||||
export interface PageAttributesProps {
|
||||
attributes: PageAttributeInput[];
|
||||
disabled: boolean;
|
||||
errors: PageErrorWithAttributesFragment[];
|
||||
onChange: FormsetChange;
|
||||
onMultiChange: FormsetChange;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(
|
||||
theme => ({
|
||||
attributeSection: {
|
||||
"&:last-of-type": {
|
||||
paddingBottom: 0
|
||||
},
|
||||
padding: theme.spacing(2, 0)
|
||||
},
|
||||
attributeSectionLabel: {
|
||||
alignItems: "center",
|
||||
display: "flex"
|
||||
},
|
||||
card: {
|
||||
overflow: "visible"
|
||||
},
|
||||
cardContent: {
|
||||
"&:last-child": {
|
||||
paddingBottom: theme.spacing(1)
|
||||
},
|
||||
paddingTop: theme.spacing(1)
|
||||
},
|
||||
expansionBar: {
|
||||
display: "flex"
|
||||
},
|
||||
expansionBarButton: {
|
||||
marginBottom: theme.spacing(1)
|
||||
},
|
||||
expansionBarButtonIcon: {
|
||||
transition: theme.transitions.duration.short + "ms"
|
||||
},
|
||||
expansionBarLabel: {
|
||||
color: theme.palette.text.disabled,
|
||||
fontSize: 14
|
||||
},
|
||||
expansionBarLabelContainer: {
|
||||
alignItems: "center",
|
||||
display: "flex",
|
||||
flex: 1
|
||||
},
|
||||
rotate: {
|
||||
transform: "rotate(180deg)"
|
||||
}
|
||||
}),
|
||||
{ name: "PageAttributes" }
|
||||
);
|
||||
|
||||
function getMultiChoices(
|
||||
values: PageDetailsFragment_pageType_attributes_values[]
|
||||
): MultiAutocompleteChoiceType[] {
|
||||
return values.map(value => ({
|
||||
label: value.name,
|
||||
value: value.slug
|
||||
}));
|
||||
}
|
||||
|
||||
function getMultiDisplayValue(
|
||||
attribute: PageAttributeInput
|
||||
): MultiAutocompleteChoiceType[] {
|
||||
return attribute.value.map(attributeValue => {
|
||||
const definedAttributeValue = attribute.data.values.find(
|
||||
definedValue => definedValue.slug === attributeValue
|
||||
);
|
||||
if (!!definedAttributeValue) {
|
||||
return {
|
||||
label: definedAttributeValue.name,
|
||||
value: definedAttributeValue.slug
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
label: attributeValue,
|
||||
value: attributeValue
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function getSingleChoices(
|
||||
values: PageDetailsFragment_pageType_attributes_values[]
|
||||
): SingleAutocompleteChoiceType[] {
|
||||
return values.map(value => ({
|
||||
label: value.name,
|
||||
value: value.slug
|
||||
}));
|
||||
}
|
||||
|
||||
const PageAttributes: React.FC<PageAttributesProps> = ({
|
||||
attributes,
|
||||
disabled,
|
||||
errors,
|
||||
onChange,
|
||||
onMultiChange
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const classes = useStyles({});
|
||||
const [expanded, setExpansionStatus] = React.useState(true);
|
||||
const toggleExpansion = () => setExpansionStatus(!expanded);
|
||||
|
||||
return (
|
||||
<Card className={classes.card}>
|
||||
<CardTitle
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Attributes",
|
||||
description: "page attributes, section header"
|
||||
})}
|
||||
/>
|
||||
<CardContent className={classes.cardContent}>
|
||||
<div className={classes.expansionBar}>
|
||||
<div className={classes.expansionBarLabelContainer}>
|
||||
<Typography className={classes.expansionBarLabel} variant="caption">
|
||||
<FormattedMessage
|
||||
defaultMessage="{number} Attributes"
|
||||
description="number of page attributes"
|
||||
values={{
|
||||
number: attributes.length
|
||||
}}
|
||||
/>
|
||||
</Typography>
|
||||
</div>
|
||||
<IconButton
|
||||
className={classes.expansionBarButton}
|
||||
onClick={toggleExpansion}
|
||||
data-test="page-attributes-expand"
|
||||
>
|
||||
<ArrowDropDownIcon
|
||||
className={classNames(classes.expansionBarButtonIcon, {
|
||||
[classes.rotate]: expanded
|
||||
})}
|
||||
/>
|
||||
</IconButton>
|
||||
</div>
|
||||
{expanded && attributes.length > 0 && (
|
||||
<>
|
||||
<Hr />
|
||||
{attributes.map((attribute, attributeIndex) => {
|
||||
const error = errors.find(err =>
|
||||
err.attributes?.includes(attribute.id)
|
||||
);
|
||||
|
||||
return (
|
||||
<React.Fragment key={attribute.id}>
|
||||
{attributeIndex > 0 && <Hr />}
|
||||
<Grid className={classes.attributeSection} variant="uniform">
|
||||
<div
|
||||
className={classes.attributeSectionLabel}
|
||||
data-test="page-attribute-label"
|
||||
>
|
||||
<Typography>{attribute.label}</Typography>
|
||||
</div>
|
||||
<div data-test="page-attribute-value">
|
||||
{attribute.data.inputType ===
|
||||
AttributeInputTypeEnum.DROPDOWN ? (
|
||||
<SingleAutocompleteSelectField
|
||||
choices={getSingleChoices(attribute.data.values)}
|
||||
disabled={disabled}
|
||||
displayValue={
|
||||
attribute.data.values.find(
|
||||
value => value.slug === attribute.value[0]
|
||||
)?.name ||
|
||||
attribute.value[0] ||
|
||||
""
|
||||
}
|
||||
emptyOption={!attribute.data.isRequired}
|
||||
error={!!error}
|
||||
helperText={getPageErrorMessage(error, intl)}
|
||||
name={`attribute:${attribute.label}`}
|
||||
label={intl.formatMessage({
|
||||
defaultMessage: "Value",
|
||||
description: "attribute value"
|
||||
})}
|
||||
value={attribute.value[0]}
|
||||
onChange={event =>
|
||||
onChange(attribute.id, event.target.value)
|
||||
}
|
||||
allowCustomValues={!attribute.data.isRequired}
|
||||
/>
|
||||
) : (
|
||||
<MultiAutocompleteSelectField
|
||||
choices={getMultiChoices(attribute.data.values)}
|
||||
displayValues={getMultiDisplayValue(attribute)}
|
||||
error={!!error}
|
||||
helperText={getPageErrorMessage(error, intl)}
|
||||
label={intl.formatMessage({
|
||||
defaultMessage: "Values",
|
||||
description: "attribute values"
|
||||
})}
|
||||
name={`attribute:${attribute.label}`}
|
||||
value={attribute.value}
|
||||
onChange={event =>
|
||||
onMultiChange(attribute.id, event.target.value)
|
||||
}
|
||||
allowCustomValues={!attribute.data.isRequired}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
PageAttributes.displayName = "PageAttributes";
|
||||
export default PageAttributes;
|
2
src/pages/components/PageAttributes/index.ts
Normal file
2
src/pages/components/PageAttributes/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./PageAttributes";
|
||||
export * from "./PageAttributes";
|
|
@ -8,36 +8,46 @@ import PageHeader from "@saleor/components/PageHeader";
|
|||
import SaveButtonBar from "@saleor/components/SaveButtonBar";
|
||||
import SeoForm from "@saleor/components/SeoForm";
|
||||
import VisibilityCard from "@saleor/components/VisibilityCard";
|
||||
import { PageErrorFragment } from "@saleor/fragments/types/PageErrorFragment";
|
||||
import { PageErrorWithAttributesFragment } from "@saleor/fragments/types/PageErrorWithAttributesFragment";
|
||||
import useDateLocalize from "@saleor/hooks/useDateLocalize";
|
||||
import { SubmitPromise } from "@saleor/hooks/useForm";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { SearchPageTypes_search_edges_node } from "@saleor/searches/types/SearchPageTypes";
|
||||
import { FetchMoreProps } from "@saleor/types";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import { PageDetails_page } from "../../types/PageDetails";
|
||||
import PageAttributes from "../PageAttributes";
|
||||
import PageInfo from "../PageInfo";
|
||||
import PageOrganizeContent from "../PageOrganizeContent";
|
||||
import PageForm, { PageData } from "./form";
|
||||
|
||||
export interface PageDetailsPageProps {
|
||||
disabled: boolean;
|
||||
errors: PageErrorFragment[];
|
||||
errors: PageErrorWithAttributesFragment[];
|
||||
page: PageDetails_page;
|
||||
pageTypes?: SearchPageTypes_search_edges_node[];
|
||||
allowEmptySlug?: boolean;
|
||||
saveButtonBarState: ConfirmButtonTransitionState;
|
||||
onBack: () => void;
|
||||
onRemove: () => void;
|
||||
onSubmit: (data: PageData) => SubmitPromise;
|
||||
fetchPageTypes?: (data: string) => void;
|
||||
fetchMorePageTypes?: FetchMoreProps;
|
||||
}
|
||||
|
||||
const PageDetailsPage: React.FC<PageDetailsPageProps> = ({
|
||||
disabled,
|
||||
errors,
|
||||
page,
|
||||
pageTypes,
|
||||
saveButtonBarState,
|
||||
onBack,
|
||||
onRemove,
|
||||
onSubmit
|
||||
onSubmit,
|
||||
fetchPageTypes,
|
||||
fetchMorePageTypes
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const localizeDate = useDateLocalize();
|
||||
|
@ -45,8 +55,8 @@ const PageDetailsPage: React.FC<PageDetailsPageProps> = ({
|
|||
const pageExists = page !== null;
|
||||
|
||||
return (
|
||||
<PageForm page={page} onSubmit={onSubmit}>
|
||||
{({ change, data, handlers, hasChanged, submit }) => (
|
||||
<PageForm page={page} pageTypes={pageTypes} onSubmit={onSubmit}>
|
||||
{({ change, data, pageType, handlers, hasChanged, submit }) => (
|
||||
<Container>
|
||||
<AppHeader onBack={onBack}>
|
||||
{intl.formatMessage(sectionNames.pages)}
|
||||
|
@ -88,6 +98,16 @@ const PageDetailsPage: React.FC<PageDetailsPageProps> = ({
|
|||
})}
|
||||
/>
|
||||
<CardSpacer />
|
||||
{data.attributes.length > 0 && (
|
||||
<PageAttributes
|
||||
attributes={data.attributes}
|
||||
disabled={disabled}
|
||||
errors={errors}
|
||||
onChange={handlers.changeAttribute}
|
||||
onMultiChange={handlers.changeAttributeMulti}
|
||||
/>
|
||||
)}
|
||||
<CardSpacer />
|
||||
<Metadata data={data} onChange={handlers.changeMetadata} />
|
||||
</div>
|
||||
<div>
|
||||
|
@ -117,6 +137,19 @@ const PageDetailsPage: React.FC<PageDetailsPageProps> = ({
|
|||
}}
|
||||
onChange={change}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<PageOrganizeContent
|
||||
data={data}
|
||||
errors={errors}
|
||||
disabled={disabled}
|
||||
pageTypes={pageTypes}
|
||||
pageType={pageType}
|
||||
pageTypeInputDisplayValue={pageType?.name || ""}
|
||||
onPageTypeChange={handlers.selectPageType}
|
||||
fetchPageTypes={fetchPageTypes}
|
||||
fetchMorePageTypes={fetchMorePageTypes}
|
||||
canChangeType={!page?.pageType}
|
||||
/>
|
||||
</div>
|
||||
</Grid>
|
||||
<SaveButtonBar
|
||||
|
|
|
@ -1,8 +1,20 @@
|
|||
import { OutputData } from "@editorjs/editorjs";
|
||||
import { MetadataFormData } from "@saleor/components/Metadata";
|
||||
import { RichTextEditorChange } from "@saleor/components/RichTextEditor";
|
||||
import { PageTypeFragment } from "@saleor/fragments/types/PageTypeFragment";
|
||||
import useForm, { FormChange, SubmitPromise } from "@saleor/hooks/useForm";
|
||||
import { PageDetails_page } from "@saleor/pages/types/PageDetails";
|
||||
import useFormset, { FormsetChange } from "@saleor/hooks/useFormset";
|
||||
import useStateFromProps from "@saleor/hooks/useStateFromProps";
|
||||
import {
|
||||
PageDetails_page,
|
||||
PageDetails_page_pageType
|
||||
} from "@saleor/pages/types/PageDetails";
|
||||
import { getAttributeInputFromPage } from "@saleor/pages/utils/data";
|
||||
import { createPageTypeSelectHandler } from "@saleor/pages/utils/handlers";
|
||||
import {
|
||||
createAttributeChangeHandler,
|
||||
createAttributeMultiChangeHandler
|
||||
} from "@saleor/products/utils/handlers";
|
||||
import getPublicationData from "@saleor/utils/data/getPublicationData";
|
||||
import handleFormSubmit from "@saleor/utils/handlers/handleFormSubmit";
|
||||
import { mapMetadataItemToInput } from "@saleor/utils/maps";
|
||||
|
@ -11,6 +23,8 @@ import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTr
|
|||
import useRichText from "@saleor/utils/richText/useRichText";
|
||||
import React from "react";
|
||||
|
||||
import { PageAttributeInput, PageAttributeInputData } from "../PageAttributes";
|
||||
|
||||
export interface PageFormData extends MetadataFormData {
|
||||
isPublished: boolean;
|
||||
publicationDate: string;
|
||||
|
@ -18,18 +32,24 @@ export interface PageFormData extends MetadataFormData {
|
|||
seoTitle: string;
|
||||
slug: string;
|
||||
title: string;
|
||||
pageType: string;
|
||||
}
|
||||
export interface PageData extends PageFormData {
|
||||
attributes: PageAttributeInput[];
|
||||
content: OutputData;
|
||||
}
|
||||
|
||||
interface PageUpdateHandlers {
|
||||
changeMetadata: FormChange;
|
||||
changeContent: RichTextEditorChange;
|
||||
selectPageType: FormChange;
|
||||
changeAttribute: FormsetChange<string>;
|
||||
changeAttributeMulti: FormsetChange<string>;
|
||||
}
|
||||
export interface UsePageUpdateFormResult {
|
||||
change: FormChange;
|
||||
data: PageData;
|
||||
pageType: PageTypeFragment;
|
||||
handlers: PageUpdateHandlers;
|
||||
hasChanged: boolean;
|
||||
submit: () => void;
|
||||
|
@ -38,21 +58,35 @@ export interface UsePageUpdateFormResult {
|
|||
export interface PageFormProps {
|
||||
children: (props: UsePageUpdateFormResult) => React.ReactNode;
|
||||
page: PageDetails_page;
|
||||
pageTypes?: PageDetails_page_pageType[];
|
||||
onSubmit: (data: PageData) => SubmitPromise;
|
||||
}
|
||||
|
||||
function usePageForm(
|
||||
page: PageDetails_page,
|
||||
onSubmit: (data: PageData) => SubmitPromise
|
||||
onSubmit: (data: PageData) => SubmitPromise,
|
||||
pageTypes?: PageDetails_page_pageType[]
|
||||
): UsePageUpdateFormResult {
|
||||
const [changed, setChanged] = React.useState(false);
|
||||
const triggerChange = () => setChanged(true);
|
||||
|
||||
const pageExists = page !== null;
|
||||
|
||||
const attributesFromPage = React.useMemo(
|
||||
() => getAttributeInputFromPage(page),
|
||||
[page]
|
||||
);
|
||||
|
||||
const {
|
||||
change: changeAttributeData,
|
||||
data: attributes,
|
||||
set: setAttributeData
|
||||
} = useFormset<PageAttributeInputData>(attributesFromPage || []);
|
||||
|
||||
const form = useForm<PageFormData>({
|
||||
isPublished: page?.isPublished,
|
||||
metadata: pageExists ? page?.metadata?.map(mapMetadataItemToInput) : [],
|
||||
pageType: page?.pageType.id || "",
|
||||
privateMetadata: pageExists
|
||||
? page?.privateMetadata?.map(mapMetadataItemToInput)
|
||||
: [],
|
||||
|
@ -67,6 +101,10 @@ function usePageForm(
|
|||
triggerChange
|
||||
});
|
||||
|
||||
const [pageType, setPageType] = useStateFromProps<PageTypeFragment>(
|
||||
page?.pageType || null
|
||||
);
|
||||
|
||||
const {
|
||||
isMetadataModified,
|
||||
isPrivateMetadataModified,
|
||||
|
@ -78,10 +116,26 @@ function usePageForm(
|
|||
triggerChange();
|
||||
};
|
||||
const changeMetadata = makeMetadataChangeHandler(handleChange);
|
||||
const selectPageType = createPageTypeSelectHandler(
|
||||
handleChange,
|
||||
setAttributeData,
|
||||
setPageType,
|
||||
pageTypes
|
||||
);
|
||||
const changeAttribute = createAttributeChangeHandler(
|
||||
changeAttributeData,
|
||||
triggerChange
|
||||
);
|
||||
const changeAttributeMulti = createAttributeMultiChangeHandler(
|
||||
changeAttributeData,
|
||||
attributes,
|
||||
triggerChange
|
||||
);
|
||||
|
||||
// Need to make it function to always have content.current up to date
|
||||
const getData = (): PageData => ({
|
||||
...form.data,
|
||||
attributes,
|
||||
content: content.current
|
||||
});
|
||||
|
||||
|
@ -100,16 +154,25 @@ function usePageForm(
|
|||
change: handleChange,
|
||||
data: getData(),
|
||||
handlers: {
|
||||
changeAttribute,
|
||||
changeAttributeMulti,
|
||||
changeContent,
|
||||
changeMetadata
|
||||
changeMetadata,
|
||||
selectPageType
|
||||
},
|
||||
hasChanged: changed,
|
||||
pageType,
|
||||
submit
|
||||
};
|
||||
}
|
||||
|
||||
const PageForm: React.FC<PageFormProps> = ({ children, page, onSubmit }) => {
|
||||
const props = usePageForm(page, onSubmit);
|
||||
const PageForm: React.FC<PageFormProps> = ({
|
||||
children,
|
||||
page,
|
||||
pageTypes,
|
||||
onSubmit
|
||||
}) => {
|
||||
const props = usePageForm(page, onSubmit, pageTypes);
|
||||
|
||||
return <form onSubmit={props.submit}>{children(props)}</form>;
|
||||
};
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue