From 0578de63c6a560ea490a20d5b5cd77fc660532d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Szyma=C5=84ski?= Date: Fri, 15 Jan 2021 11:10:52 +0100 Subject: [PATCH 01/35] Make sure invoices card is visible only on orders other than unconfirmed (#944) * Make sure invoices card is visible only on orders other than unconfirmed * Replace div with React.Fragment * Use short form of fragment and remove key --- .../OrderDetailsPage/OrderDetailsPage.tsx | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/orders/components/OrderDetailsPage/OrderDetailsPage.tsx b/src/orders/components/OrderDetailsPage/OrderDetailsPage.tsx index f2b777951..4fe700e72 100644 --- a/src/orders/components/OrderDetailsPage/OrderDetailsPage.tsx +++ b/src/orders/components/OrderDetailsPage/OrderDetailsPage.tsx @@ -254,13 +254,17 @@ const OrderDetailsPage: React.FC = props => { selectedChannelName={order?.channel?.name} /> - - + {order?.status !== OrderStatus.UNCONFIRMED && ( + <> + + + + )} order.customerNote)} /> From 18f09812a87c0919f6a5fa53d70848bcc8fe38e5 Mon Sep 17 00:00:00 2001 From: Jakub Majorek Date: Mon, 18 Jan 2021 12:19:04 +0100 Subject: [PATCH 02/35] Saleor 2067 - Guard against non-staff users logging in (#947) * Guard against non-staff users logging in * Scope channel permissions * Update changelog * Update tests --- CHANGELOG.md | 1 + locale/defaultMessages.json | 6 + .../recording.har | 44 +++--- .../recording.har | 49 +++---- .../recording.har | 36 ++--- .../recording.har | 132 ++++++++++++++++++ .../recording.har | 36 ++--- src/auth/AuthProvider.test.ts | 33 ++++- src/auth/AuthProvider.tsx | 42 +++--- src/auth/types/SetPassword.ts | 1 + src/auth/types/TokenAuth.ts | 1 + src/auth/types/VerifyToken.ts | 1 + .../AppLayout/AppChannelContext.tsx | 4 +- .../ChannelsAvailability.stories.tsx | 1 + src/fragments/auth.ts | 1 + src/fragments/types/User.ts | 1 + .../HomeNotificationTable.tsx | 25 ++-- src/intl.ts | 6 + 18 files changed, 285 insertions(+), 135 deletions(-) create mode 100644 recordings/User_3768991250/will-not-be-logged-if-is-non-staff_821531868/recording.har diff --git a/CHANGELOG.md b/CHANGELOG.md index f629addf2..47b9503fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ All notable, unreleased changes to this project will be documented in this file. - Update quantity column in Inventory part of Product Variant view - #904 by @dominik-zeglen - Add file attributes - #884 by @orzechdev - Add shipping delivery days - #914 by @orzechdev +- Guard against non-staff users logging in - #947 by @jwm0 # 2.11.1 diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index ef246230d..74e67b150 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -3032,6 +3032,9 @@ "src_dot_hooks_dot_3382262667": { "string": "Variant {name} has been set as default." }, + "src_dot_insufficientPermissions": { + "string": "Insufficient permissions" + }, "src_dot_lastName": { "string": "Last Name" }, @@ -6251,6 +6254,9 @@ "src_dot_translations_dot_components_dot_TranslationsVouchersPage_dot_2599922713": { "string": "Voucher Name" }, + "src_dot_unauthorizedDashboardAccess": { + "string": "Only staff users can access the dashboard" + }, "src_dot_unconfirmed": { "context": "order status", "string": "Unconfirmed" diff --git a/recordings/User_3768991250/will-be-logged-if-has-valid-token_3465908808/recording.har b/recordings/User_3768991250/will-be-logged-if-has-valid-token_3465908808/recording.har index 8ad594963..e1740a219 100644 --- a/recordings/User_3768991250/will-be-logged-if-has-valid-token_3465908808/recording.har +++ b/recordings/User_3768991250/will-be-logged-if-has-valid-token_3465908808/recording.har @@ -8,11 +8,11 @@ }, "entries": [ { - "_id": "f515e15cbc83df73e5bd41437971c2e6", + "_id": "a3088678db2635ada66ab049f76c9722", "_order": 0, "cache": {}, "request": { - "bodySize": 691, + "bodySize": 702, "cookies": [], "headers": [ { @@ -28,7 +28,7 @@ { "_fromType": "array", "name": "content-length", - "value": "691" + "value": "702" }, { "_fromType": "array", @@ -56,61 +56,53 @@ "postData": { "mimeType": "application/json", "params": [], - "text": "[{\"operationName\":\"VerifyToken\",\"variables\":{\"token\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTYwMjgyMTgsImV4cCI6MTU5NjAyODUxOCwidG9rZW4iOiJDM1NrMmtMUlZ1UEEiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6ImFjY2VzcyIsInVzZXJfaWQiOiJWWE5sY2pveU1RPT0iLCJpc19zdGFmZiI6dHJ1ZX0.eo8_Ew98HICB4cFQN2U7mCJ8ydGVOvQLGRT4CnkufMc\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation VerifyToken($token: String!) {\\n tokenVerify(token: $token) {\\n payload\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" + "text": "[{\"operationName\":\"VerifyToken\",\"variables\":{\"token\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTA2MzI1NDQsImV4cCI6MTYxMDYzMjg0NCwidG9rZW4iOiJrc0VWTXZnZzZCZmkiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6ImFjY2VzcyIsInVzZXJfaWQiOiJWWE5sY2pveU5BPT0iLCJpc19zdGFmZiI6dHJ1ZX0.QDp4vlm1tKhk8iFnY2MnREvO-IubI5j8g_Wylb1XJqc\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n isStaff\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation VerifyToken($token: String!) {\\n tokenVerify(token: $token) {\\n payload\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" }, "queryString": [], "url": "http://localhost:8000/graphql/" }, "response": { - "bodySize": 1619, + "bodySize": 1765, "content": { "mimeType": "application/json", - "size": 1619, - "text": "[{\"data\": {\"tokenVerify\": {\"payload\": {\"iat\": 1596028218, \"exp\": 1596028518, \"token\": \"C3Sk2kLRVuPA\", \"email\": \"admin@example.com\", \"type\": \"access\", \"user_id\": \"VXNlcjoyMQ==\", \"is_staff\": true}, \"user\": {\"id\": \"VXNlcjoyMQ==\", \"email\": \"admin@example.com\", \"firstName\": \"\", \"lastName\": \"\", \"userPermissions\": [{\"code\": \"MANAGE_APPS\", \"name\": \"Manage apps\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_CHECKOUTS\", \"name\": \"Manage checkouts\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_DISCOUNTS\", \"name\": \"Manage sales and vouchers.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_GIFT_CARD\", \"name\": \"Manage gift cards.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_MENUS\", \"name\": \"Manage navigation.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_ORDERS\", \"name\": \"Manage orders.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PAGES\", \"name\": \"Manage pages.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PLUGINS\", \"name\": \"Manage plugins\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PRODUCTS\", \"name\": \"Manage products.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SETTINGS\", \"name\": \"Manage settings.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SHIPPING\", \"name\": \"Manage shipping.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_STAFF\", \"name\": \"Manage staff.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_TRANSLATIONS\", \"name\": \"Manage translations.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_USERS\", \"name\": \"Manage customers.\", \"__typename\": \"UserPermission\"}], \"avatar\": null, \"__typename\": \"User\"}, \"__typename\": \"VerifyToken\"}}}]" + "size": 1765, + "text": "[{\"data\": {\"tokenVerify\": {\"payload\": {\"iat\": 1610632544, \"exp\": 1610632844, \"token\": \"ksEVMvgg6Bfi\", \"email\": \"admin@example.com\", \"type\": \"access\", \"user_id\": \"VXNlcjoyNA==\", \"is_staff\": true}, \"user\": {\"id\": \"VXNlcjoyNA==\", \"email\": \"admin@example.com\", \"firstName\": \"\", \"lastName\": \"\", \"isStaff\": true, \"userPermissions\": [{\"code\": \"MANAGE_APPS\", \"name\": \"Manage apps\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_CHECKOUTS\", \"name\": \"Manage checkouts\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_DISCOUNTS\", \"name\": \"Manage sales and vouchers.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_GIFT_CARD\", \"name\": \"Manage gift cards.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_MENUS\", \"name\": \"Manage navigation.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_ORDERS\", \"name\": \"Manage orders.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PAGES\", \"name\": \"Manage pages.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PLUGINS\", \"name\": \"Manage plugins\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PRODUCT_TYPES_AND_ATTRIBUTES\", \"name\": \"Manage product types and attributes.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PRODUCTS\", \"name\": \"Manage products.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SETTINGS\", \"name\": \"Manage settings.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SHIPPING\", \"name\": \"Manage shipping.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_STAFF\", \"name\": \"Manage staff.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_TRANSLATIONS\", \"name\": \"Manage translations.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_USERS\", \"name\": \"Manage customers.\", \"__typename\": \"UserPermission\"}], \"avatar\": null, \"__typename\": \"User\"}, \"__typename\": \"VerifyToken\"}}}]" }, "cookies": [], "headers": [ { "name": "date", - "value": "Wed, 29 Jul 2020 13:10:18 GMT" + "value": "Thu, 14 Jan 2021 14:10:40 GMT" }, { "name": "server", - "value": "WSGIServer/0.2 CPython/3.8.1" + "value": "WSGIServer/0.2 CPython/3.8.7" }, { "name": "content-type", "value": "application/json" }, - { - "name": "access-control-allow-origin", - "value": "http://localhost:9000" - }, - { - "name": "access-control-allow-methods", - "value": "POST, OPTIONS" - }, - { - "name": "access-control-allow-headers", - "value": "Origin, Content-Type, Accept, Authorization" - }, { "name": "content-length", - "value": "1619" + "value": "1765" }, { "name": "x-content-type-options", "value": "nosniff" + }, + { + "name": "referrer-policy", + "value": "same-origin" } ], - "headersSize": 336, + "headersSize": 194, "httpVersion": "HTTP/1.1", "redirectURL": "", "status": 200, "statusText": "OK" }, - "startedDateTime": "2020-07-29T13:10:18.327Z", - "time": 23, + "startedDateTime": "2021-01-14T14:10:40.434Z", + "time": 155, "timings": { "blocked": -1, "connect": -1, @@ -118,7 +110,7 @@ "receive": 0, "send": 0, "ssl": -1, - "wait": 23 + "wait": 155 } } ], diff --git a/recordings/User_3768991250/will-be-logged-in-if-has-valid-credentials_3587751314/recording.har b/recordings/User_3768991250/will-be-logged-in-if-has-valid-credentials_3587751314/recording.har index be977fd9c..0ac2a2d6a 100644 --- a/recordings/User_3768991250/will-be-logged-in-if-has-valid-credentials_3587751314/recording.har +++ b/recordings/User_3768991250/will-be-logged-in-if-has-valid-credentials_3587751314/recording.har @@ -8,11 +8,11 @@ }, "entries": [ { - "_id": "7c460842cac4a92c188d5451dfc533a2", + "_id": "a2b8a02f624e52cd2b73a831f65d9a52", "_order": 0, "cache": {}, "request": { - "bodySize": 587, + "bodySize": 598, "cookies": [], "headers": [ { @@ -28,7 +28,7 @@ { "_fromType": "array", "name": "content-length", - "value": "587" + "value": "598" }, { "_fromType": "array", @@ -56,74 +56,65 @@ "postData": { "mimeType": "application/json", "params": [], - "text": "[{\"operationName\":\"TokenAuth\",\"variables\":{\"email\":\"admin@example.com\",\"password\":\"admin\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation TokenAuth($email: String!, $password: String!) {\\n tokenCreate(email: $email, password: $password) {\\n errors: accountErrors {\\n field\\n message\\n __typename\\n }\\n csrfToken\\n token\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" + "text": "[{\"operationName\":\"TokenAuth\",\"variables\":{\"email\":\"admin@example.com\",\"password\":\"admin\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n isStaff\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation TokenAuth($email: String!, $password: String!) {\\n tokenCreate(email: $email, password: $password) {\\n errors: accountErrors {\\n field\\n message\\n __typename\\n }\\n csrfToken\\n token\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" }, "queryString": [], "url": "http://localhost:8000/graphql/" }, "response": { - "bodySize": 1830, + "bodySize": 1976, "content": { "mimeType": "application/json", - "size": 1830, - "text": "[{\"data\": {\"tokenCreate\": {\"errors\": [], \"csrfToken\": \"rLPNMGNYKXH8VY4UNEWl4nEOFMseocljioigPl36IM2CqbdmOTEpNwvdHBAJ1ZWQ\", \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTYwMjgyMTgsImV4cCI6MTU5NjAyODUxOCwidG9rZW4iOiJDM1NrMmtMUlZ1UEEiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6ImFjY2VzcyIsInVzZXJfaWQiOiJWWE5sY2pveU1RPT0iLCJpc19zdGFmZiI6dHJ1ZX0.eo8_Ew98HICB4cFQN2U7mCJ8ydGVOvQLGRT4CnkufMc\", \"user\": {\"id\": \"VXNlcjoyMQ==\", \"email\": \"admin@example.com\", \"firstName\": \"\", \"lastName\": \"\", \"userPermissions\": [{\"code\": \"MANAGE_APPS\", \"name\": \"Manage apps\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_CHECKOUTS\", \"name\": \"Manage checkouts\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_DISCOUNTS\", \"name\": \"Manage sales and vouchers.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_GIFT_CARD\", \"name\": \"Manage gift cards.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_MENUS\", \"name\": \"Manage navigation.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_ORDERS\", \"name\": \"Manage orders.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PAGES\", \"name\": \"Manage pages.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PLUGINS\", \"name\": \"Manage plugins\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PRODUCTS\", \"name\": \"Manage products.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SETTINGS\", \"name\": \"Manage settings.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SHIPPING\", \"name\": \"Manage shipping.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_STAFF\", \"name\": \"Manage staff.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_TRANSLATIONS\", \"name\": \"Manage translations.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_USERS\", \"name\": \"Manage customers.\", \"__typename\": \"UserPermission\"}], \"avatar\": null, \"__typename\": \"User\"}, \"__typename\": \"CreateToken\"}}}]" + "size": 1976, + "text": "[{\"data\": {\"tokenCreate\": {\"errors\": [], \"csrfToken\": \"UIzzJSFalS8pplfM1j5QNIUNiXb0VFH3kbe6kfTddYvLjJ9DhMasCtHJKXoGDfbw\", \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTA2MzI1NDQsImV4cCI6MTYxMDYzMjg0NCwidG9rZW4iOiJrc0VWTXZnZzZCZmkiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6ImFjY2VzcyIsInVzZXJfaWQiOiJWWE5sY2pveU5BPT0iLCJpc19zdGFmZiI6dHJ1ZX0.QDp4vlm1tKhk8iFnY2MnREvO-IubI5j8g_Wylb1XJqc\", \"user\": {\"id\": \"VXNlcjoyNA==\", \"email\": \"admin@example.com\", \"firstName\": \"\", \"lastName\": \"\", \"isStaff\": true, \"userPermissions\": [{\"code\": \"MANAGE_APPS\", \"name\": \"Manage apps\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_CHECKOUTS\", \"name\": \"Manage checkouts\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_DISCOUNTS\", \"name\": \"Manage sales and vouchers.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_GIFT_CARD\", \"name\": \"Manage gift cards.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_MENUS\", \"name\": \"Manage navigation.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_ORDERS\", \"name\": \"Manage orders.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PAGES\", \"name\": \"Manage pages.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PLUGINS\", \"name\": \"Manage plugins\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PRODUCT_TYPES_AND_ATTRIBUTES\", \"name\": \"Manage product types and attributes.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PRODUCTS\", \"name\": \"Manage products.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SETTINGS\", \"name\": \"Manage settings.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SHIPPING\", \"name\": \"Manage shipping.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_STAFF\", \"name\": \"Manage staff.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_TRANSLATIONS\", \"name\": \"Manage translations.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_USERS\", \"name\": \"Manage customers.\", \"__typename\": \"UserPermission\"}], \"avatar\": null, \"__typename\": \"User\"}, \"__typename\": \"CreateToken\"}}}]" }, "cookies": [ { "httpOnly": true, "name": "refreshToken", "path": "/", - "secure": true, - "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTYwMjgyMTgsImV4cCI6MTU5ODYyMDIxOCwidG9rZW4iOiJDM1NrMmtMUlZ1UEEiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6InJlZnJlc2giLCJ1c2VyX2lkIjoiVlhObGNqb3lNUT09IiwiaXNfc3RhZmYiOnRydWUsImNzcmZUb2tlbiI6InJMUE5NR05ZS1hIOFZZNFVORVdsNG5FT0ZNc2VvY2xqaW9pZ1BsMzZJTTJDcWJkbU9URXBOd3ZkSEJBSjFaV1EifQ.boD8G4pkSnZF-PLl5oOg85Uj-mqTiAzOkua9aAG3Bz4" + "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTA2MzI1NDQsImV4cCI6MTYxMzIyNDU0NCwidG9rZW4iOiJrc0VWTXZnZzZCZmkiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6InJlZnJlc2giLCJ1c2VyX2lkIjoiVlhObGNqb3lOQT09IiwiaXNfc3RhZmYiOnRydWUsImNzcmZUb2tlbiI6IlVJenpKU0ZhbFM4cHBsZk0xajVRTklVTmlYYjBWRkgza2JlNmtmVGRkWXZMako5RGhNYXNDdEhKS1hvR0RmYncifQ.Br0GWGPPcnysyUxukjBBfXNbwCAm2qlR5OYClwFF3ZQ" } ], "headers": [ { "name": "date", - "value": "Wed, 29 Jul 2020 13:10:18 GMT" + "value": "Thu, 14 Jan 2021 13:55:44 GMT" }, { "name": "server", - "value": "WSGIServer/0.2 CPython/3.8.1" + "value": "WSGIServer/0.2 CPython/3.8.7" }, { "name": "content-type", "value": "application/json" }, - { - "name": "access-control-allow-origin", - "value": "http://localhost:9000" - }, - { - "name": "access-control-allow-methods", - "value": "POST, OPTIONS" - }, - { - "name": "access-control-allow-headers", - "value": "Origin, Content-Type, Accept, Authorization" - }, { "name": "content-length", - "value": "1830" + "value": "1976" }, { "name": "x-content-type-options", "value": "nosniff" }, + { + "name": "referrer-policy", + "value": "same-origin" + }, { "_fromType": "array", "name": "set-cookie", - "value": "refreshToken=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTYwMjgyMTgsImV4cCI6MTU5ODYyMDIxOCwidG9rZW4iOiJDM1NrMmtMUlZ1UEEiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6InJlZnJlc2giLCJ1c2VyX2lkIjoiVlhObGNqb3lNUT09IiwiaXNfc3RhZmYiOnRydWUsImNzcmZUb2tlbiI6InJMUE5NR05ZS1hIOFZZNFVORVdsNG5FT0ZNc2VvY2xqaW9pZ1BsMzZJTTJDcWJkbU9URXBOd3ZkSEJBSjFaV1EifQ.boD8G4pkSnZF-PLl5oOg85Uj-mqTiAzOkua9aAG3Bz4; HttpOnly; Path=/; Secure" + "value": "refreshToken=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTA2MzI1NDQsImV4cCI6MTYxMzIyNDU0NCwidG9rZW4iOiJrc0VWTXZnZzZCZmkiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6InJlZnJlc2giLCJ1c2VyX2lkIjoiVlhObGNqb3lOQT09IiwiaXNfc3RhZmYiOnRydWUsImNzcmZUb2tlbiI6IlVJenpKU0ZhbFM4cHBsZk0xajVRTklVTmlYYjBWRkgza2JlNmtmVGRkWXZMako5RGhNYXNDdEhKS1hvR0RmYncifQ.Br0GWGPPcnysyUxukjBBfXNbwCAm2qlR5OYClwFF3ZQ; HttpOnly; Path=/" } ], - "headersSize": 768, + "headersSize": 618, "httpVersion": "HTTP/1.1", "redirectURL": "", "status": 200, "statusText": "OK" }, - "startedDateTime": "2020-07-29T13:10:18.064Z", - "time": 118, + "startedDateTime": "2021-01-14T13:55:44.094Z", + "time": 392, "timings": { "blocked": -1, "connect": -1, @@ -131,7 +122,7 @@ "receive": 0, "send": 0, "ssl": -1, - "wait": 118 + "wait": 392 } } ], diff --git a/recordings/User_3768991250/will-not-be-logged-if-has-invalid-token_1301762210/recording.har b/recordings/User_3768991250/will-not-be-logged-if-has-invalid-token_1301762210/recording.har index 8e4ea3f81..a86975194 100644 --- a/recordings/User_3768991250/will-not-be-logged-if-has-invalid-token_1301762210/recording.har +++ b/recordings/User_3768991250/will-not-be-logged-if-has-invalid-token_1301762210/recording.har @@ -8,11 +8,11 @@ }, "entries": [ { - "_id": "4836098613648775386c1e10728424dd", + "_id": "b1557b45bbbf7aed1a4a53f5141ca324", "_order": 0, "cache": {}, "request": { - "bodySize": 428, + "bodySize": 439, "cookies": [], "headers": [ { @@ -28,7 +28,7 @@ { "_fromType": "array", "name": "content-length", - "value": "428" + "value": "439" }, { "_fromType": "array", @@ -56,7 +56,7 @@ "postData": { "mimeType": "application/json", "params": [], - "text": "[{\"operationName\":\"VerifyToken\",\"variables\":{\"token\":\"NotAToken\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation VerifyToken($token: String!) {\\n tokenVerify(token: $token) {\\n payload\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" + "text": "[{\"operationName\":\"VerifyToken\",\"variables\":{\"token\":\"NotAToken\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n isStaff\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation VerifyToken($token: String!) {\\n tokenVerify(token: $token) {\\n payload\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" }, "queryString": [], "url": "http://localhost:8000/graphql/" @@ -72,28 +72,16 @@ "headers": [ { "name": "date", - "value": "Wed, 29 Jul 2020 13:10:18 GMT" + "value": "Thu, 14 Jan 2021 14:10:40 GMT" }, { "name": "server", - "value": "WSGIServer/0.2 CPython/3.8.1" + "value": "WSGIServer/0.2 CPython/3.8.7" }, { "name": "content-type", "value": "application/json" }, - { - "name": "access-control-allow-origin", - "value": "http://localhost:9000" - }, - { - "name": "access-control-allow-methods", - "value": "POST, OPTIONS" - }, - { - "name": "access-control-allow-headers", - "value": "Origin, Content-Type, Accept, Authorization" - }, { "name": "content-length", "value": "89" @@ -101,16 +89,20 @@ { "name": "x-content-type-options", "value": "nosniff" + }, + { + "name": "referrer-policy", + "value": "same-origin" } ], - "headersSize": 334, + "headersSize": 192, "httpVersion": "HTTP/1.1", "redirectURL": "", "status": 200, "statusText": "OK" }, - "startedDateTime": "2020-07-29T13:10:18.368Z", - "time": 6, + "startedDateTime": "2021-01-14T14:10:40.611Z", + "time": 25, "timings": { "blocked": -1, "connect": -1, @@ -118,7 +110,7 @@ "receive": 0, "send": 0, "ssl": -1, - "wait": 6 + "wait": 25 } } ], diff --git a/recordings/User_3768991250/will-not-be-logged-if-is-non-staff_821531868/recording.har b/recordings/User_3768991250/will-not-be-logged-if-is-non-staff_821531868/recording.har new file mode 100644 index 000000000..6cc007de4 --- /dev/null +++ b/recordings/User_3768991250/will-not-be-logged-if-is-non-staff_821531868/recording.har @@ -0,0 +1,132 @@ +{ + "log": { + "_recordingName": "User/will not be logged if is non-staff", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "0b09ec35ecae5b17a2ccda062b1d6ef5", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 602, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "content-type", + "value": "application/json" + }, + { + "_fromType": "array", + "name": "content-length", + "value": "602" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "localhost:8000" + } + ], + "headersSize": 254, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/json", + "params": [], + "text": "[{\"operationName\":\"TokenAuth\",\"variables\":{\"email\":\"client@example.com\",\"password\":\"password\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n isStaff\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation TokenAuth($email: String!, $password: String!) {\\n tokenCreate(email: $email, password: $password) {\\n errors: accountErrors {\\n field\\n message\\n __typename\\n }\\n csrfToken\\n token\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" + }, + "queryString": [], + "url": "http://localhost:8000/graphql/" + }, + "response": { + "bodySize": 616, + "content": { + "mimeType": "application/json", + "size": 616, + "text": "[{\"data\": {\"tokenCreate\": {\"errors\": [], \"csrfToken\": \"Gac5v8mZt6dW0HBXp5RNt8GAWciTbVzsycpqtUKV797npCXajke5h9VoF4l9MreP\", \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTA2OTg2MzMsImV4cCI6MTYxMDY5ODkzMywidG9rZW4iOiJ6MDc2QndLZkNEMmYiLCJlbWFpbCI6ImNsaWVudEBleGFtcGxlLmNvbSIsInR5cGUiOiJhY2Nlc3MiLCJ1c2VyX2lkIjoiVlhObGNqb3pNQT09IiwiaXNfc3RhZmYiOmZhbHNlfQ.RVYwqQSPEZoi2E_ImC30Ml37RJ2Fu6AnSmfDkAYMcqY\", \"user\": {\"id\": \"VXNlcjozMA==\", \"email\": \"client@example.com\", \"firstName\": \"\", \"lastName\": \"\", \"isStaff\": false, \"userPermissions\": [], \"avatar\": null, \"__typename\": \"User\"}, \"__typename\": \"CreateToken\"}}}]" + }, + "cookies": [ + { + "httpOnly": true, + "name": "refreshToken", + "path": "/", + "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTA2OTg2MzMsImV4cCI6MTYxMzI5MDYzMywidG9rZW4iOiJ6MDc2QndLZkNEMmYiLCJlbWFpbCI6ImNsaWVudEBleGFtcGxlLmNvbSIsInR5cGUiOiJyZWZyZXNoIiwidXNlcl9pZCI6IlZYTmxjam96TUE9PSIsImlzX3N0YWZmIjpmYWxzZSwiY3NyZlRva2VuIjoiR2FjNXY4bVp0NmRXMEhCWHA1Uk50OEdBV2NpVGJWenN5Y3BxdFVLVjc5N25wQ1hhamtlNWg5Vm9GNGw5TXJlUCJ9.jUF_9vvtwT8EUbQ4GM7u0YVivk7TiSoSecHDZ0jJ2MI" + } + ], + "headers": [ + { + "name": "date", + "value": "Fri, 15 Jan 2021 08:17:13 GMT" + }, + { + "name": "server", + "value": "WSGIServer/0.2 CPython/3.8.7" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "content-length", + "value": "616" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "referrer-policy", + "value": "same-origin" + }, + { + "_fromType": "array", + "name": "set-cookie", + "value": "refreshToken=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTA2OTg2MzMsImV4cCI6MTYxMzI5MDYzMywidG9rZW4iOiJ6MDc2QndLZkNEMmYiLCJlbWFpbCI6ImNsaWVudEBleGFtcGxlLmNvbSIsInR5cGUiOiJyZWZyZXNoIiwidXNlcl9pZCI6IlZYTmxjam96TUE9PSIsImlzX3N0YWZmIjpmYWxzZSwiY3NyZlRva2VuIjoiR2FjNXY4bVp0NmRXMEhCWHA1Uk50OEdBV2NpVGJWenN5Y3BxdFVLVjc5N25wQ1hhamtlNWg5Vm9GNGw5TXJlUCJ9.jUF_9vvtwT8EUbQ4GM7u0YVivk7TiSoSecHDZ0jJ2MI; HttpOnly; Path=/" + } + ], + "headersSize": 619, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2021-01-15T08:17:12.850Z", + "time": 623, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 623 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har b/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har index 547653fee..174fede4a 100644 --- a/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har +++ b/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har @@ -8,11 +8,11 @@ }, "entries": [ { - "_id": "86487093ff8b070d496fcdc566e01adf", + "_id": "d94d7821dc951e48c410d691d7eccdef", "_order": 0, "cache": {}, "request": { - "bodySize": 603, + "bodySize": 614, "cookies": [], "headers": [ { @@ -28,7 +28,7 @@ { "_fromType": "array", "name": "content-length", - "value": "603" + "value": "614" }, { "_fromType": "array", @@ -56,7 +56,7 @@ "postData": { "mimeType": "application/json", "params": [], - "text": "[{\"operationName\":\"TokenAuth\",\"variables\":{\"email\":\"admin@example.com\",\"password\":\"NotAValidPassword123!\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation TokenAuth($email: String!, $password: String!) {\\n tokenCreate(email: $email, password: $password) {\\n errors: accountErrors {\\n field\\n message\\n __typename\\n }\\n csrfToken\\n token\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" + "text": "[{\"operationName\":\"TokenAuth\",\"variables\":{\"email\":\"admin@example.com\",\"password\":\"NotAValidPassword123!\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n isStaff\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation TokenAuth($email: String!, $password: String!) {\\n tokenCreate(email: $email, password: $password) {\\n errors: accountErrors {\\n field\\n message\\n __typename\\n }\\n csrfToken\\n token\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" }, "queryString": [], "url": "http://localhost:8000/graphql/" @@ -72,28 +72,16 @@ "headers": [ { "name": "date", - "value": "Wed, 29 Jul 2020 13:10:18 GMT" + "value": "Thu, 14 Jan 2021 13:55:45 GMT" }, { "name": "server", - "value": "WSGIServer/0.2 CPython/3.8.1" + "value": "WSGIServer/0.2 CPython/3.8.7" }, { "name": "content-type", "value": "application/json" }, - { - "name": "access-control-allow-origin", - "value": "http://localhost:9000" - }, - { - "name": "access-control-allow-methods", - "value": "POST, OPTIONS" - }, - { - "name": "access-control-allow-headers", - "value": "Origin, Content-Type, Accept, Authorization" - }, { "name": "content-length", "value": "214" @@ -101,16 +89,20 @@ { "name": "x-content-type-options", "value": "nosniff" + }, + { + "name": "referrer-policy", + "value": "same-origin" } ], - "headersSize": 335, + "headersSize": 193, "httpVersion": "HTTP/1.1", "redirectURL": "", "status": 200, "statusText": "OK" }, - "startedDateTime": "2020-07-29T13:10:18.208Z", - "time": 99, + "startedDateTime": "2021-01-14T13:55:44.521Z", + "time": 1183, "timings": { "blocked": -1, "connect": -1, @@ -118,7 +110,7 @@ "receive": 0, "send": 0, "ssl": -1, - "wait": 99 + "wait": 1183 } } ], diff --git a/src/auth/AuthProvider.test.ts b/src/auth/AuthProvider.test.ts index c9d82f557..058ad8b08 100644 --- a/src/auth/AuthProvider.test.ts +++ b/src/auth/AuthProvider.test.ts @@ -20,12 +20,17 @@ function renderAuthProvider(apolloClient: ApolloClient) { return result; } -const credentials = { +const adminCredentials = { email: "admin@example.com", password: "admin", token: null }; +const nonStaffUserCredentials = { + email: "client@example.com", + password: "password" +}; + beforeEach(() => { localStorage.clear(); sessionStorage.clear(); @@ -36,10 +41,10 @@ describe("User", () => { const hook = renderAuthProvider(apolloClient); await act(() => - hook.current.login(credentials.email, credentials.password) + hook.current.login(adminCredentials.email, adminCredentials.password) ); - expect(hook.current.userContext.email).toBe(credentials.email); - credentials.token = getTokens().auth; + expect(hook.current.userContext.email).toBe(adminCredentials.email); + adminCredentials.token = getTokens().auth; done(); }); @@ -48,19 +53,33 @@ describe("User", () => { const hook = renderAuthProvider(apolloClient); await act(() => - hook.current.login(credentials.email, "NotAValidPassword123!") + hook.current.login(adminCredentials.email, "NotAValidPassword123!") ); expect(hook.current.userContext).toBe(null); done(); }); + it("will not be logged in if is non-staff", async done => { + const hook = renderAuthProvider(apolloClient); + + await act(() => + hook.current.login( + nonStaffUserCredentials.email, + nonStaffUserCredentials.password + ) + ); + expect(hook.current.userContext).toBe(undefined); + + done(); + }); + it("will be logged if has valid token", async done => { - setAuthToken(credentials.token, false); + setAuthToken(adminCredentials.token, false); const hook = renderAuthProvider(apolloClient); await act(() => hook.current.autologinPromise.current); - expect(hook.current.userContext.email).toBe(credentials.email); + expect(hook.current.userContext.email).toBe(adminCredentials.email); done(); }); diff --git a/src/auth/AuthProvider.tsx b/src/auth/AuthProvider.tsx index ad83dcea8..37b75a5b9 100644 --- a/src/auth/AuthProvider.tsx +++ b/src/auth/AuthProvider.tsx @@ -2,6 +2,7 @@ import { IMessageContext } from "@saleor/components/messages"; import { DEMO_MODE } from "@saleor/config"; import { User } from "@saleor/fragments/types/User"; import useNotifier from "@saleor/hooks/useNotifier"; +import { commonMessages } from "@saleor/intl"; import { getMutationStatus } from "@saleor/misc"; import { isSupported as isCredentialsManagementAPISupported, @@ -41,6 +42,26 @@ export function useAuthProvider( const autologinPromise = useRef>(); const refreshPromise = useRef>(); + useEffect(() => { + const token = getTokens().auth; + if (!!token && !userContext) { + autologinPromise.current = tokenVerify({ variables: { token } }); + } else { + autologinPromise.current = loginWithCredentialsManagementAPI(login); + } + }, []); + + useEffect(() => { + if (userContext && !userContext.isStaff) { + logout(); + notify({ + status: "error", + text: intl.formatMessage(commonMessages.unauthorizedDashboardAccess), + title: intl.formatMessage(commonMessages.insufficientPermissions) + }); + } + }, [userContext]); + const logout = () => { setUserContext(undefined); if (isCredentialsManagementAPISupported) { @@ -54,22 +75,18 @@ export function useAuthProvider( TokenAuthVariables >(tokenAuthMutation, { client: apolloClient, - onCompleted: result => { - if (result.tokenCreate.errors.length > 0) { + onCompleted: ({ tokenCreate }) => { + if (tokenCreate.errors.length > 0) { logout(); } - const user = result.tokenCreate.user; + const user = tokenCreate.user; // FIXME: Now we set state also when auth fails and returned user is // `null`, because the LoginView uses this `null` to display error. setUserContext(user); if (user) { - setTokens( - result.tokenCreate.token, - result.tokenCreate.csrfToken, - persistToken - ); + setTokens(tokenCreate.token, tokenCreate.csrfToken, persistToken); } }, onError: logout @@ -115,15 +132,6 @@ export function useAuthProvider( } }; - useEffect(() => { - const token = getTokens().auth; - if (!!token && !userContext) { - autologinPromise.current = tokenVerify({ variables: { token } }); - } else { - autologinPromise.current = loginWithCredentialsManagementAPI(login); - } - }, []); - const login = async (email: string, password: string) => { const result = await tokenAuth({ variables: { email, password } }); diff --git a/src/auth/types/SetPassword.ts b/src/auth/types/SetPassword.ts index 0ede71f38..2a1d539c8 100644 --- a/src/auth/types/SetPassword.ts +++ b/src/auth/types/SetPassword.ts @@ -31,6 +31,7 @@ export interface SetPassword_setPassword_user { email: string; firstName: string; lastName: string; + isStaff: boolean; userPermissions: (SetPassword_setPassword_user_userPermissions | null)[] | null; avatar: SetPassword_setPassword_user_avatar | null; } diff --git a/src/auth/types/TokenAuth.ts b/src/auth/types/TokenAuth.ts index 8da32e338..07272b217 100644 --- a/src/auth/types/TokenAuth.ts +++ b/src/auth/types/TokenAuth.ts @@ -31,6 +31,7 @@ export interface TokenAuth_tokenCreate_user { email: string; firstName: string; lastName: string; + isStaff: boolean; userPermissions: (TokenAuth_tokenCreate_user_userPermissions | null)[] | null; avatar: TokenAuth_tokenCreate_user_avatar | null; } diff --git a/src/auth/types/VerifyToken.ts b/src/auth/types/VerifyToken.ts index 10ece6970..06934e963 100644 --- a/src/auth/types/VerifyToken.ts +++ b/src/auth/types/VerifyToken.ts @@ -25,6 +25,7 @@ export interface VerifyToken_tokenVerify_user { email: string; firstName: string; lastName: string; + isStaff: boolean; userPermissions: (VerifyToken_tokenVerify_user_userPermissions | null)[] | null; avatar: VerifyToken_tokenVerify_user_avatar | null; } diff --git a/src/components/AppLayout/AppChannelContext.tsx b/src/components/AppLayout/AppChannelContext.tsx index 930cacfd0..3bfb62fa1 100644 --- a/src/components/AppLayout/AppChannelContext.tsx +++ b/src/components/AppLayout/AppChannelContext.tsx @@ -33,8 +33,8 @@ export const AppChannelProvider: React.FC = ({ children }) => { const [isPickerActive, setPickerActive] = React.useState(false); React.useEffect(() => { - if (!selectedChannel) { - setSelectedChannel(channelData?.channels[0].id); + if (!selectedChannel && channelData?.channels) { + setSelectedChannel(channelData.channels[0].id); } }, [channelData]); diff --git a/src/components/ChannelsAvailability/ChannelsAvailability.stories.tsx b/src/components/ChannelsAvailability/ChannelsAvailability.stories.tsx index 3395d2e91..262d18eaa 100644 --- a/src/components/ChannelsAvailability/ChannelsAvailability.stories.tsx +++ b/src/components/ChannelsAvailability/ChannelsAvailability.stories.tsx @@ -17,6 +17,7 @@ const user: User = { email: "email@example.com", firstName: "User", id: "123", + isStaff: true, lastName: "User", userPermissions: [ { diff --git a/src/fragments/auth.ts b/src/fragments/auth.ts index 2a897a523..c09d611eb 100644 --- a/src/fragments/auth.ts +++ b/src/fragments/auth.ts @@ -6,6 +6,7 @@ export const fragmentUser = gql` email firstName lastName + isStaff userPermissions { code name diff --git a/src/fragments/types/User.ts b/src/fragments/types/User.ts index d504cc3e2..67c1e5660 100644 --- a/src/fragments/types/User.ts +++ b/src/fragments/types/User.ts @@ -25,6 +25,7 @@ export interface User { email: string; firstName: string; lastName: string; + isStaff: boolean; userPermissions: (User_userPermissions | null)[] | null; avatar: User_avatar | null; } diff --git a/src/home/components/HomeNotificationTable/HomeNotificationTable.tsx b/src/home/components/HomeNotificationTable/HomeNotificationTable.tsx index a217306c4..918fd4646 100644 --- a/src/home/components/HomeNotificationTable/HomeNotificationTable.tsx +++ b/src/home/components/HomeNotificationTable/HomeNotificationTable.tsx @@ -95,16 +95,21 @@ const HomeNotificationTable: React.FC = props => { {noChannel && ( - - - - {intl.formatMessage(messages.createNewChannel)} - - - - - - + + + + + {intl.formatMessage(messages.createNewChannel)} + + + + + + + )} Date: Fri, 18 Dec 2020 17:39:33 +0100 Subject: [PATCH 03/35] Update changelog with reference attributes --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47b9503fd..ff811c864 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ All notable, unreleased changes to this project will be documented in this file. - Add file attributes - #884 by @orzechdev - Add shipping delivery days - #914 by @orzechdev - Guard against non-staff users logging in - #947 by @jwm0 +- Add reference attributes - #917 by @orzechdev # 2.11.1 From ee05b090b881660a3b1fcd2985fd02dc2a0e509a Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Thu, 7 Jan 2021 12:01:24 +0100 Subject: [PATCH 04/35] 1862 - Add references field to attributes section (#923) * Add references field to attributes section * Update messagees and test shapshots * Remove unused style in sortable chips component --- locale/defaultMessages.json | 4 + schema.graphql | 40 + .../Attributes/Attributes.stories.tsx | 5 +- src/components/Attributes/Attributes.tsx | 168 +-- .../Attributes/BasicAttributeRow.tsx | 44 + .../Attributes/ExtendedAttributeRow.tsx | 65 ++ src/components/Attributes/fixtures.ts | 46 +- .../Chip/Chip.stories.tsx} | 5 +- .../SortableChip/SortableChip.stories.tsx | 29 + src/components/SortableChip/SortableChip.tsx | 68 ++ .../SortableChip/SortableHandle.tsx | 29 + src/components/SortableChip/index.ts | 2 + .../SortableChipsField.stories.tsx | 27 + .../SortableChipsField/SortableChipsField.tsx | 66 ++ .../SortableChipsField/SortableContainer.tsx | 5 + src/components/SortableChipsField/index.ts | 2 + .../__snapshots__/Stories.test.ts.snap | 1031 +++++++++++++++-- src/storybook/config.js | 1 - src/types/globalTypes.ts | 7 + 19 files changed, 1468 insertions(+), 176 deletions(-) create mode 100644 src/components/Attributes/BasicAttributeRow.tsx create mode 100644 src/components/Attributes/ExtendedAttributeRow.tsx rename src/{storybook/stories/components/Chip.tsx => components/Chip/Chip.stories.tsx} (77%) create mode 100644 src/components/SortableChip/SortableChip.stories.tsx create mode 100644 src/components/SortableChip/SortableChip.tsx create mode 100644 src/components/SortableChip/SortableHandle.tsx create mode 100644 src/components/SortableChip/index.ts create mode 100644 src/components/SortableChipsField/SortableChipsField.stories.tsx create mode 100644 src/components/SortableChipsField/SortableChipsField.tsx create mode 100644 src/components/SortableChipsField/SortableContainer.tsx create mode 100644 src/components/SortableChipsField/index.ts diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index 74e67b150..57c44198f 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -1543,6 +1543,10 @@ "src_dot_components_dot_AttributeUnassignDialog_dot_2037985699": { "string": "Are you sure you want to unassign {attributeName} from {itemTypeName}?" }, + "src_dot_components_dot_Attributes_dot_3824528779": { + "context": "button label", + "string": "Assign references" + }, "src_dot_components_dot_Attributes_dot_attributesNumber": { "context": "number of attributes", "string": "{number} Attributes" diff --git a/schema.graphql b/schema.graphql index 2d320ab14..e72a6545a 100644 --- a/schema.graphql +++ b/schema.graphql @@ -393,6 +393,7 @@ type Attribute implements Node & ObjectWithMetadata { privateMetadata: [MetadataItem]! metadata: [MetadataItem]! inputType: AttributeInputTypeEnum + entityType: AttributeEntityTypeEnum name: String slug: String type: AttributeTypeEnum @@ -431,6 +432,7 @@ type AttributeCreate { input AttributeCreateInput { inputType: AttributeInputTypeEnum + entityType: AttributeEntityTypeEnum name: String! slug: String type: AttributeTypeEnum! @@ -450,6 +452,10 @@ type AttributeDelete { attribute: Attribute } +enum AttributeEntityTypeEnum { + PAGE +} + type AttributeError { field: String message: String @@ -490,6 +496,7 @@ enum AttributeInputTypeEnum { DROPDOWN MULTISELECT FILE + REFERENCE } type AttributeReorderValues { @@ -566,6 +573,7 @@ type AttributeValue implements Node { type: AttributeValueType @deprecated(reason: "Use the `inputType` field to determine the type of attribute's value. This field will be removed after 2020-07-31.") translation(languageCode: LanguageCodeEnum!): AttributeValueTranslation inputType: AttributeInputTypeEnum + reference: ID file: File } @@ -598,6 +606,7 @@ input AttributeValueInput { values: [String] file: String contentType: String + references: [ID!] } type AttributeValueTranslatableContent implements Node { @@ -643,6 +652,7 @@ type BulkProductError { message: String code: ProductErrorCode! attributes: [ID!] + values: [ID!] index: Int warehouses: [ID!] channels: [ID!] @@ -653,6 +663,7 @@ type BulkStockError { message: String code: ProductErrorCode! attributes: [ID!] + values: [ID!] index: Int } @@ -1082,6 +1093,7 @@ type CollectionChannelListingError { message: String code: ProductErrorCode! attributes: [ID!] + values: [ID!] channels: [ID!] } @@ -2596,6 +2608,7 @@ type Mutation { productTypeBulkDelete(ids: [ID]!): ProductTypeBulkDelete productTypeUpdate(id: ID!, input: ProductTypeInput!): ProductTypeUpdate productTypeReorderAttributes(moves: [ReorderInput]!, productTypeId: ID!, type: ProductAttributeType!): ProductTypeReorderAttributes + productReorderAttributeValues(attributeId: ID!, moves: [ReorderInput]!, productId: ID!): ProductReorderAttributeValues digitalContentCreate(input: DigitalContentUploadInput!, variantId: ID!): DigitalContentCreate digitalContentDelete(variantId: ID!): DigitalContentDelete digitalContentUpdate(input: DigitalContentInput!, variantId: ID!): DigitalContentUpdate @@ -2611,6 +2624,7 @@ type Mutation { productVariantSetDefault(productId: ID!, variantId: ID!): ProductVariantSetDefault productVariantTranslate(id: ID!, input: NameTranslationInput!, languageCode: LanguageCodeEnum!): ProductVariantTranslate productVariantChannelListingUpdate(id: ID!, input: [ProductVariantChannelListingAddInput!]!): ProductVariantChannelListingUpdate + productVariantReorderAttributeValues(attributeId: ID!, moves: [ReorderInput]!, variantId: ID!): ProductVariantReorderAttributeValues variantImageAssign(imageId: ID!, variantId: ID!): VariantImageAssign variantImageUnassign(imageId: ID!, variantId: ID!): VariantImageUnassign paymentCapture(amount: PositiveDecimal, paymentId: ID!): PaymentCapture @@ -2630,6 +2644,7 @@ type Mutation { pageAttributeAssign(attributeIds: [ID!]!, pageTypeId: ID!): PageAttributeAssign pageAttributeUnassign(attributeIds: [ID!]!, pageTypeId: ID!): PageAttributeUnassign pageTypeReorderAttributes(moves: [ReorderInput!]!, pageTypeId: ID!): PageTypeReorderAttributes + pageReorderAttributeValues(attributeId: ID!, moves: [ReorderInput]!, pageId: ID!): PageReorderAttributeValues draftOrderComplete(id: ID!): DraftOrderComplete draftOrderCreate(input: DraftOrderCreateInput!): DraftOrderCreate draftOrderDelete(id: ID!): DraftOrderDelete @@ -3290,6 +3305,7 @@ type PageError { message: String code: PageErrorCode! attributes: [ID!] + values: [ID!] } enum PageErrorCode { @@ -3324,6 +3340,12 @@ input PageInput { seo: SeoInput } +type PageReorderAttributeValues { + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") + page: Page + pageErrors: [PageError!]! +} + enum PageSortField { TITLE SLUG @@ -3821,6 +3843,7 @@ type ProductChannelListingError { message: String code: ProductErrorCode! attributes: [ID!] + values: [ID!] channels: [ID!] } @@ -3879,6 +3902,7 @@ type ProductError { message: String code: ProductErrorCode! attributes: [ID!] + values: [ID!] } enum ProductErrorCode { @@ -4026,6 +4050,12 @@ type ProductPricingInfo { priceRangeLocalCurrency: TaxedMoneyRange } +type ProductReorderAttributeValues { + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") + product: Product + productErrors: [ProductError!]! +} + input ProductStockFilterInput { warehouseIds: [ID!] quantity: IntRangeInput @@ -4172,8 +4202,12 @@ type ProductVariant implements Node & ObjectWithMetadata { weight: Weight privateMetadata: [MetadataItem]! metadata: [MetadataItem]! + quantity: Int! @deprecated(reason: "Use the stock field instead. This field will be removed after 2020-07-31.") + quantityAllocated: Int @deprecated(reason: "Use the stock field instead. This field will be removed after 2020-07-31.") + stockQuantity: Int! @deprecated(reason: "Use the quantityAvailable field instead. This field will be removed after 2020-07-31.") channelListings: [ProductVariantChannelListing!] pricing: VariantPricingInfo + isAvailable: Boolean @deprecated(reason: "Use the stock field instead. This field will be removed after 2020-07-31.") attributes(variantSelection: VariantAttributeScope): [SelectedAttribute!]! costPrice: Money margin: Int @@ -4278,6 +4312,12 @@ type ProductVariantReorder { productErrors: [ProductError!]! } +type ProductVariantReorderAttributeValues { + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") + productVariant: ProductVariant + productErrors: [ProductError!]! +} + type ProductVariantSetDefault { errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") product: Product diff --git a/src/components/Attributes/Attributes.stories.tsx b/src/components/Attributes/Attributes.stories.tsx index b2f222856..ba294cbd9 100644 --- a/src/components/Attributes/Attributes.stories.tsx +++ b/src/components/Attributes/Attributes.stories.tsx @@ -12,7 +12,10 @@ const props: AttributesProps = { loading: false, onChange: () => undefined, onFileChange: () => undefined, - onMultiChange: () => undefined + onMultiChange: () => undefined, + onReferencesChange: () => undefined, + onReferencesChangeClick: () => undefined, + onReferencesReorder: () => undefined }; storiesOf("Attributes / Attributes", module) diff --git a/src/components/Attributes/Attributes.tsx b/src/components/Attributes/Attributes.tsx index 9364ec721..87a9077bf 100644 --- a/src/components/Attributes/Attributes.tsx +++ b/src/components/Attributes/Attributes.tsx @@ -5,7 +5,6 @@ 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 @@ -17,6 +16,7 @@ import { AttributeValueFragment } from "@saleor/fragments/types/AttributeValueFr import { PageErrorWithAttributesFragment } from "@saleor/fragments/types/PageErrorWithAttributesFragment"; import { ProductErrorWithAttributesFragment } from "@saleor/fragments/types/ProductErrorWithAttributesFragment"; import { FormsetAtomicData, FormsetChange } from "@saleor/hooks/useFormset"; +import { ReorderAction } from "@saleor/types"; import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; import { getProductErrorMessage } from "@saleor/utils/errors"; import getPageErrorMessage from "@saleor/utils/errors/page"; @@ -30,6 +30,9 @@ import { } from "react-intl"; import FileUploadField, { FileChoiceType } from "../FileUploadField"; +import SortableChipsField from "../SortableChipsField"; +import BasicAttributeRow from "./BasicAttributeRow"; +import ExtendedAttributeRow from "./ExtendedAttributeRow"; import { VariantAttributeScope } from "./types"; export interface AttributeInputData { @@ -51,7 +54,10 @@ export interface AttributesProps { title?: React.ReactNode; onChange: FormsetChange; onMultiChange: FormsetChange; - onFileChange?: FormsetChange; // TODO: temporairy optional, should be changed to required, after all pages implement it + onFileChange: FormsetChange; + onReferencesChange?: FormsetChange; // TODO: temporairy optional, should be changed to required, after all pages implement it + onReferencesChangeClick?: () => void; // TODO: temporairy optional, should be changed to required, after all pages implement it + onReferencesReorder?: ReorderAction; // TODO: temporairy optional, should be changed to required, after all pages implement it } const useStyles = makeStyles( @@ -210,7 +216,10 @@ const Attributes: React.FC = ({ title, onChange, onMultiChange, - onFileChange + onFileChange, + onReferencesChange, + onReferencesChangeClick, + onReferencesReorder }) => { const intl = useIntl(); const classes = useStyles({}); @@ -255,76 +264,91 @@ const Attributes: React.FC = ({ return ( {attributeIndex > 0 &&
} - -
- {attribute.label} -
-
- {attribute.data.inputType === - AttributeInputTypeEnum.FILE ? ( - - onFileChange(attribute.id, file) - } - onFileDelete={() => - onFileChange(attribute.id, undefined) - } - error={!!error} - helperText={getErrorMessage(error, intl)} - inputProps={{ - name: `attribute:${attribute.label}` - }} - /> - ) : attribute.data.inputType === - AttributeInputTypeEnum.DROPDOWN ? ( - value.slug === attribute.value[0] - )?.name || - attribute.value[0] || - "" - } - emptyOption={!attribute.data.isRequired} - error={!!error} - helperText={getErrorMessage(error, intl)} - name={`attribute:${attribute.label}`} - label={intl.formatMessage(messages.valueLabel)} - value={attribute.value[0]} - onChange={event => - onChange(attribute.id, event.target.value) - } - allowCustomValues={!attribute.data.isRequired} - /> - ) : ( - - onMultiChange(attribute.id, event.target.value) - } - allowCustomValues={!attribute.data.isRequired} - /> - )} -
-
+ + onReferencesChange( + attribute.id, + attribute.value?.filter(id => id !== value) + ) + } + onValueReorder={onReferencesReorder} + loading={loading} + /> + + ) : attribute.data.inputType === + AttributeInputTypeEnum.FILE ? ( + + onFileChange(attribute.id, file)} + onFileDelete={() => + onFileChange(attribute.id, undefined) + } + error={!!error} + helperText={getErrorMessage(error, intl)} + inputProps={{ + name: `attribute:${attribute.label}` + }} + /> + + ) : attribute.data.inputType === + AttributeInputTypeEnum.DROPDOWN ? ( + + value.slug === attribute.value[0] + )?.name || + attribute.value[0] || + "" + } + emptyOption={!attribute.data.isRequired} + error={!!error} + helperText={getErrorMessage(error, intl)} + name={`attribute:${attribute.label}`} + label={intl.formatMessage(messages.valueLabel)} + value={attribute.value[0]} + onChange={event => + onChange(attribute.id, event.target.value) + } + allowCustomValues={!attribute.data.isRequired} + /> + + ) : ( + + + onMultiChange(attribute.id, event.target.value) + } + allowCustomValues={!attribute.data.isRequired} + /> + + )}
); })} diff --git a/src/components/Attributes/BasicAttributeRow.tsx b/src/components/Attributes/BasicAttributeRow.tsx new file mode 100644 index 000000000..ee0bb61dc --- /dev/null +++ b/src/components/Attributes/BasicAttributeRow.tsx @@ -0,0 +1,44 @@ +import { makeStyles } from "@material-ui/core/styles"; +import Typography from "@material-ui/core/Typography"; +import Grid from "@saleor/components/Grid"; +import React from "react"; + +const useStyles = makeStyles( + theme => ({ + attributeSection: { + "&:last-of-type": { + paddingBottom: 0 + }, + padding: theme.spacing(2, 0) + }, + attributeSectionLabel: { + alignItems: "center", + display: "flex" + } + }), + { name: "BasicAttributeRow" } +); + +interface BasicAttributeRowProps { + label: string; +} + +const BasicAttributeRow: React.FC = props => { + const { label, children } = props; + const classes = useStyles(props); + + return ( + +
+ {label} +
+
{children}
+
+ ); +}; + +BasicAttributeRow.displayName = "BasicAttributeRow"; +export default BasicAttributeRow; diff --git a/src/components/Attributes/ExtendedAttributeRow.tsx b/src/components/Attributes/ExtendedAttributeRow.tsx new file mode 100644 index 000000000..1bc9ac70b --- /dev/null +++ b/src/components/Attributes/ExtendedAttributeRow.tsx @@ -0,0 +1,65 @@ +import Button from "@material-ui/core/Button"; +import { makeStyles } from "@material-ui/core/styles"; +import Typography from "@material-ui/core/Typography"; +import Grid from "@saleor/components/Grid"; +import React from "react"; + +const useStyles = makeStyles( + theme => ({ + attributeSection: { + "&:last-of-type": { + paddingBottom: 0 + }, + padding: theme.spacing(2, 0) + }, + attributeSectionButton: { + float: "right" + }, + attributeSectionLabel: { + alignItems: "center", + display: "flex" + } + }), + { name: "ExtendedAttributeRow" } +); + +interface ExtendedAttributeRowProps { + label: string; + selectLabel: string; + disabled: boolean; + onSelect: () => void; +} + +const ExtendedAttributeRow: React.FC = props => { + const { label, selectLabel, disabled, onSelect, children } = props; + const classes = useStyles(props); + + return ( + <> + +
+ {label} +
+
+ +
+
+
{children}
+ + ); +}; + +ExtendedAttributeRow.displayName = "ExtendedAttributeRow"; +export default ExtendedAttributeRow; diff --git a/src/components/Attributes/fixtures.ts b/src/components/Attributes/fixtures.ts index 6a62b561c..e6aff799d 100644 --- a/src/components/Attributes/fixtures.ts +++ b/src/components/Attributes/fixtures.ts @@ -79,15 +79,49 @@ const FILE_ATTRIBUTE: AttributeInput = { } ] }, - id: "ifudbgidfsb", + id: "fguygygugyu", label: "File Attribute", value: [] }; +const REFERENCE_ATTRIBUTE: AttributeInput = { + data: { + inputType: AttributeInputTypeEnum.REFERENCE, + isRequired: true, + values: [ + { + __typename: "AttributeValue", + file: null, + id: "vbnhgcvjhbvhj", + name: "References First Value", + slug: "references-first-value" + }, + { + __typename: "AttributeValue", + file: null, + id: "gucngdfdfvdvd", + name: "References Second Value", + slug: "references-second-value" + }, + { + __typename: "AttributeValue", + file: null, + id: "dfdfdsfdsfdse", + name: "References Third Value", + slug: "references-third-value" + } + ] + }, + id: "kclsmcdsmcs", + label: "References Attribute", + value: [] +}; + export const ATTRIBUTES: AttributeInput[] = [ DROPDOWN_ATTRIBUTE, MULTISELECT_ATTRIBUTE, - FILE_ATTRIBUTE + FILE_ATTRIBUTE, + REFERENCE_ATTRIBUTE ]; export const ATTRIBUTES_SELECTED: AttributeInput[] = [ @@ -105,5 +139,13 @@ export const ATTRIBUTES_SELECTED: AttributeInput[] = [ { ...FILE_ATTRIBUTE, value: [FILE_ATTRIBUTE.data.values[0].slug] + }, + { + ...REFERENCE_ATTRIBUTE, + value: [ + REFERENCE_ATTRIBUTE.data.values[0].slug, + REFERENCE_ATTRIBUTE.data.values[1].slug, + REFERENCE_ATTRIBUTE.data.values[2].slug + ] } ]; diff --git a/src/storybook/stories/components/Chip.tsx b/src/components/Chip/Chip.stories.tsx similarity index 77% rename from src/storybook/stories/components/Chip.tsx rename to src/components/Chip/Chip.stories.tsx index 99ad5c9bf..0f582b110 100644 --- a/src/storybook/stories/components/Chip.tsx +++ b/src/components/Chip/Chip.stories.tsx @@ -1,10 +1,9 @@ import Chip, { ChipProps } from "@saleor/components/Chip"; +import CardDecorator from "@saleor/storybook/CardDecorator"; +import Decorator from "@saleor/storybook/Decorator"; import { storiesOf } from "@storybook/react"; import React from "react"; -import CardDecorator from "../../CardDecorator"; -import Decorator from "../../Decorator"; - const props: ChipProps = { label: "Lorem Ipsum" }; diff --git a/src/components/SortableChip/SortableChip.stories.tsx b/src/components/SortableChip/SortableChip.stories.tsx new file mode 100644 index 000000000..30b82ea4c --- /dev/null +++ b/src/components/SortableChip/SortableChip.stories.tsx @@ -0,0 +1,29 @@ +import SortableChip, { + SortableChipProps +} from "@saleor/components/SortableChip"; +import CardDecorator from "@saleor/storybook/CardDecorator"; +import Decorator from "@saleor/storybook/Decorator"; +import { storiesOf } from "@storybook/react"; +import React from "react"; +import { SortableContainer } from "react-sortable-hoc"; + +const Container = SortableContainer(props => props.children); + +const props: SortableChipProps = { + index: 0, + label: "Lorem Ipsum" +}; + +storiesOf("Generics / Sortable chip", module) + .addDecorator(CardDecorator) + .addDecorator(Decorator) + .add("default", () => ( + + + + )) + .add("with x", () => ( + + undefined} /> + + )); diff --git a/src/components/SortableChip/SortableChip.tsx b/src/components/SortableChip/SortableChip.tsx new file mode 100644 index 000000000..c78900d3c --- /dev/null +++ b/src/components/SortableChip/SortableChip.tsx @@ -0,0 +1,68 @@ +import { makeStyles } from "@material-ui/core/styles"; +import Typography from "@material-ui/core/Typography"; +import CloseIcon from "@material-ui/icons/Close"; +import classNames from "classnames"; +import React from "react"; +import { SortableElement, SortableElementProps } from "react-sortable-hoc"; + +import SortableHandle from "./SortableHandle"; + +export interface SortableChipProps extends SortableElementProps { + className?: string; + label: React.ReactNode; + onClose?: () => void; +} + +const useStyles = makeStyles( + theme => ({ + closeIcon: { + cursor: "pointer", + fontSize: 16, + marginLeft: theme.spacing(), + verticalAlign: "middle" + }, + content: { + alignItems: "center", + display: "flex" + }, + root: { + border: `1px solid ${theme.palette.divider}`, + borderRadius: 18, + display: "inline-block", + marginRight: theme.spacing(2), + padding: "6px 12px" + }, + sortableHandle: { + marginRight: theme.spacing(1) + } + }), + { name: "SortableChip" } +); + +const SortableChip = SortableElement(props => { + const { className, label, onClose } = props; + + const classes = useStyles(props); + + return ( +
+
+ + {label} + {onClose && ( + + )} +
+
+ ); +}); + +SortableChip.displayName = "SortableChip"; +export default SortableChip; diff --git a/src/components/SortableChip/SortableHandle.tsx b/src/components/SortableChip/SortableHandle.tsx new file mode 100644 index 000000000..7bfad44d4 --- /dev/null +++ b/src/components/SortableChip/SortableHandle.tsx @@ -0,0 +1,29 @@ +import { makeStyles } from "@material-ui/core/styles"; +import Draggable from "@saleor/icons/Draggable"; +import classNames from "classnames"; +import React from "react"; +import { SortableHandle as SortableHandleHoc } from "react-sortable-hoc"; + +const useStyles = makeStyles( + { + drag: { + cursor: "grab" + } + }, + { name: "SortableHandle" } +); + +interface SortableHandle { + className?: string; +} + +const SortableHandle = SortableHandleHoc(props => { + const { className, ...restProps } = props; + const classes = useStyles(props); + + return ( + + ); +}); + +export default SortableHandle; diff --git a/src/components/SortableChip/index.ts b/src/components/SortableChip/index.ts new file mode 100644 index 000000000..fabe8557d --- /dev/null +++ b/src/components/SortableChip/index.ts @@ -0,0 +1,2 @@ +export { default } from "./SortableChip"; +export * from "./SortableChip"; diff --git a/src/components/SortableChipsField/SortableChipsField.stories.tsx b/src/components/SortableChipsField/SortableChipsField.stories.tsx new file mode 100644 index 000000000..5f3f383da --- /dev/null +++ b/src/components/SortableChipsField/SortableChipsField.stories.tsx @@ -0,0 +1,27 @@ +import CardDecorator from "@saleor/storybook/CardDecorator"; +import Decorator from "@saleor/storybook/Decorator"; +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import SortableChipsField, { + SortableChipsFieldProps +} from "./SortableChipsField"; + +const props: SortableChipsFieldProps = { + onValueDelete: () => undefined, + onValueReorder: () => undefined, + values: [ + { label: "Item 1", value: "item-1" }, + { label: "Item 2", value: "item-2" }, + { label: "Item 3", value: "item-3" }, + { label: "Item 4", value: "item-4" }, + { label: "Item 5", value: "item-5" }, + { label: "Item 6", value: "item-6" } + ] +}; + +storiesOf("Generics / Sortable chips field", module) + .addDecorator(CardDecorator) + .addDecorator(Decorator) + .add("default", () => ) + .add("loading", () => ); diff --git a/src/components/SortableChipsField/SortableChipsField.tsx b/src/components/SortableChipsField/SortableChipsField.tsx new file mode 100644 index 000000000..ef44b8b4c --- /dev/null +++ b/src/components/SortableChipsField/SortableChipsField.tsx @@ -0,0 +1,66 @@ +import { makeStyles } from "@material-ui/core/styles"; +import { ReorderAction } from "@saleor/types"; +import React from "react"; +import { SortableContainerProps } from "react-sortable-hoc"; + +import Skeleton from "../Skeleton"; +import DraggableChip from "../SortableChip"; +import SortableContainer from "./SortableContainer"; + +const useStyles = makeStyles( + theme => ({ + chip: { + background: "#fff", + color: theme.palette.primary.dark, + marginBottom: theme.spacing(1) + } + }), + { + name: "SortableChipsField" + } +); + +interface SortableChipsFieldValueType { + label: string; + value: any; +} + +export interface SortableChipsFieldProps extends SortableContainerProps { + loading?: boolean; + values: SortableChipsFieldValueType[]; + onValueDelete: (id: string) => void; + onValueReorder: ReorderAction; +} + +const SortableChipsField: React.FC = props => { + const { loading, values, onValueDelete, onValueReorder } = props; + const classes = useStyles(props); + + return ( + +
+ {loading ? ( + + ) : ( + values.map((value, valueIndex) => ( + onValueDelete(value.value)} + /> + )) + )} +
+
+ ); +}; + +SortableChipsField.displayName = "SortableChipsField"; +export default SortableChipsField; diff --git a/src/components/SortableChipsField/SortableContainer.tsx b/src/components/SortableChipsField/SortableContainer.tsx new file mode 100644 index 000000000..01f88e250 --- /dev/null +++ b/src/components/SortableChipsField/SortableContainer.tsx @@ -0,0 +1,5 @@ +import { SortableContainer as SortableContainerHoc } from "react-sortable-hoc"; + +const SortableContainer = SortableContainerHoc(({ children }) => children); + +export default SortableContainer; diff --git a/src/components/SortableChipsField/index.ts b/src/components/SortableChipsField/index.ts new file mode 100644 index 000000000..80fc61c0a --- /dev/null +++ b/src/components/SortableChipsField/index.ts @@ -0,0 +1,2 @@ +export { default } from "./SortableChipsField"; +export * from "./SortableChipsField"; diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index 09ab12e52..eb5bd3e4c 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -61,7 +61,7 @@ exports[`Storyshots Attributes / Attributes default 1`] = `
- 3 Attributes + 4 Attributes
+ + +
+
+
@@ -338,7 +376,7 @@ exports[`Storyshots Attributes / Attributes disabled 1`] = `
- 3 Attributes + 4 Attributes
+ + +
+
+
@@ -618,7 +695,7 @@ exports[`Storyshots Attributes / Attributes selected 1`] = `
- 3 Attributes + 4 Attributes
+ + +
+
+
+
+ +
+ References First Value +
+ +
+
+
+
+ +
+ References Second Value +
+ +
+
+
+
+ +
+ References Third Value +
+ +
+
+
+
@@ -10397,6 +10693,547 @@ exports[`Storyshots Generics / Skeleton default 1`] = ` `; +exports[`Storyshots Generics / Sortable chip default 1`] = ` +
+
+
+
+
+ +
+ Lorem Ipsum +
+
+
+
+
+
+`; + +exports[`Storyshots Generics / Sortable chip with x 1`] = ` +
+
+
+
+
+ +
+ Lorem Ipsum +
+ +
+
+
+
+
+`; + +exports[`Storyshots Generics / Sortable chips field default 1`] = ` +
+
+
+
+
+
+ +
+ Item 1 +
+ +
+
+
+
+ +
+ Item 2 +
+ +
+
+
+
+ +
+ Item 3 +
+ +
+
+
+
+ +
+ Item 4 +
+ +
+
+
+
+ +
+ Item 5 +
+ +
+
+
+
+ +
+ Item 6 +
+ +
+
+
+
+
+
+`; + +exports[`Storyshots Generics / Sortable chips field loading 1`] = ` +
+
+
+
+ + ‌ + +
+
+
+
+`; + exports[`Storyshots Generics / Square Button default 1`] = `
Date: Tue, 12 Jan 2021 12:11:15 +0100 Subject: [PATCH 05/35] 1425 - Support reference type attribute on attribute details page (#918) * Support reference type attribute on attribute details page * Trigger CI * Prevent changing attribute entity type during attribute update * Refactor attribute details components --- locale/defaultMessages.json | 121 ++-- .../AttributeDetails/AttributeDetails.tsx | 173 ++++-- .../AttributePage/AttributePage.tsx | 9 +- .../AttributeProperties.tsx | 165 +++--- src/attributes/fixtures.ts | 1 + src/attributes/types/AttributeCreate.ts | 3 +- src/attributes/types/AttributeDetails.ts | 3 +- src/attributes/types/AttributeUpdate.ts | 3 +- src/attributes/types/AttributeValueCreate.ts | 3 +- src/attributes/types/AttributeValueDelete.ts | 3 +- src/attributes/types/AttributeValueUpdate.ts | 3 +- src/attributes/utils/data.ts | 45 ++ .../views/AttributeCreate/AttributeCreate.tsx | 38 +- .../AttributeDetails/AttributeDetails.tsx | 1 + src/fragments/attributes.ts | 2 + .../types/AttributeDetailsFragment.ts | 3 +- .../__snapshots__/Stories.test.ts.snap | 554 +++++++++--------- 17 files changed, 650 insertions(+), 480 deletions(-) diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index 57c44198f..ff4677d0d 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -733,37 +733,49 @@ "context": "dialog content", "string": "Are you sure you want to delete {attributeName}?" }, - "src_dot_attributes_dot_components_dot_AttributeDetails_dot_1005562666": { - "context": "attribute's editor component", - "string": "Catalog Input type for Store Owner" + "src_dot_attributes_dot_components_dot_AttributeDetails_dot_attributeLabel": { + "context": "attribute's label", + "string": "Default Label" }, - "src_dot_attributes_dot_components_dot_AttributeDetails_dot_1336738461": { - "context": "product attribute type", - "string": "Dropdown" - }, - "src_dot_attributes_dot_components_dot_AttributeDetails_dot_1376373679": { - "context": "file attribute type", - "string": "File" - }, - "src_dot_attributes_dot_components_dot_AttributeDetails_dot_2592224946": { - "context": "check to require attribute to have value", - "string": "Value Required" - }, - "src_dot_attributes_dot_components_dot_AttributeDetails_dot_3334509011": { - "context": "product attribute type", - "string": "Multiple Select" - }, - "src_dot_attributes_dot_components_dot_AttributeDetails_dot_3605174225": { + "src_dot_attributes_dot_components_dot_AttributeDetails_dot_attributeSlug": { "context": "attribute's slug short code label", "string": "Attribute Code" }, - "src_dot_attributes_dot_components_dot_AttributeDetails_dot_4107478955": { + "src_dot_attributes_dot_components_dot_AttributeDetails_dot_attributeSlugHelperText": { "context": "attribute slug input field helper text", "string": "This is used internally. Make sure you don’t use spaces" }, - "src_dot_attributes_dot_components_dot_AttributeDetails_dot_691600601": { - "context": "attribute's label", - "string": "Default Label" + "src_dot_attributes_dot_components_dot_AttributeDetails_dot_dropdown": { + "context": "product attribute type", + "string": "Dropdown" + }, + "src_dot_attributes_dot_components_dot_AttributeDetails_dot_entityType": { + "context": "attribute's editor component entity", + "string": "Entity" + }, + "src_dot_attributes_dot_components_dot_AttributeDetails_dot_file": { + "context": "file attribute type", + "string": "File" + }, + "src_dot_attributes_dot_components_dot_AttributeDetails_dot_inputType": { + "context": "attribute's editor component", + "string": "Catalog Input type for Store Owner" + }, + "src_dot_attributes_dot_components_dot_AttributeDetails_dot_multiselect": { + "context": "product attribute type", + "string": "Multiple Select" + }, + "src_dot_attributes_dot_components_dot_AttributeDetails_dot_page": { + "context": "page attribute entity type", + "string": "Pages" + }, + "src_dot_attributes_dot_components_dot_AttributeDetails_dot_references": { + "context": "references attribute type", + "string": "References" + }, + "src_dot_attributes_dot_components_dot_AttributeDetails_dot_valueRequired": { + "context": "check to require attribute to have value", + "string": "Value Required" }, "src_dot_attributes_dot_components_dot_AttributeListPage_dot_2417065806": { "context": "tab name", @@ -844,43 +856,46 @@ "context": "page title", "string": "Create New Attribute" }, - "src_dot_attributes_dot_components_dot_AttributeProperties_dot_1318123158": { - "context": "attribute is filterable in storefront", - "string": "Use in Faceted Navigation" - }, - "src_dot_attributes_dot_components_dot_AttributeProperties_dot_1877630205": { - "context": "attribute properties regarding storefront", - "string": "Storefront Properties" - }, - "src_dot_attributes_dot_components_dot_AttributeProperties_dot_26409543": { - "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_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." - }, - "src_dot_attributes_dot_components_dot_AttributeProperties_dot_673770329": { + "src_dot_attributes_dot_components_dot_AttributeProperties_dot_availableInGrid": { "context": "add attribute as column in product list table", "string": "Add to Column Options" }, - "src_dot_attributes_dot_components_dot_AttributeProperties_dot_714335445": { + "src_dot_attributes_dot_components_dot_AttributeProperties_dot_availableInGridCaption": { + "context": "caption", + "string": "If enabled this attribute can be used as a column in product table." + }, + "src_dot_attributes_dot_components_dot_AttributeProperties_dot_dashboardPropertiesTitle": { + "context": "attribute properties regarding dashboard", + "string": "Dashboard Properties" + }, + "src_dot_attributes_dot_components_dot_AttributeProperties_dot_filterableInDashboard": { "context": "use attribute in filtering", "string": "Use in Filtering" }, - "src_dot_attributes_dot_components_dot_AttributeProperties_dot_787251583": { + "src_dot_attributes_dot_components_dot_AttributeProperties_dot_filterableInDashboardCaption": { + "context": "caption", "string": "If enabled, you’ll be able to use this attribute to filter products in product list." }, + "src_dot_attributes_dot_components_dot_AttributeProperties_dot_filterableInStorefront": { + "context": "attribute is filterable in storefront", + "string": "Use in Faceted Navigation" + }, + "src_dot_attributes_dot_components_dot_AttributeProperties_dot_storefrontPropertiesTitle": { + "context": "attribute properties regarding storefront", + "string": "Storefront Properties" + }, + "src_dot_attributes_dot_components_dot_AttributeProperties_dot_storefrontSearchPosition": { + "context": "attribute position in storefront filters", + "string": "Position in faceted navigation" + }, + "src_dot_attributes_dot_components_dot_AttributeProperties_dot_visibleInStorefront": { + "context": "attribute visibility in storefront", + "string": "Public" + }, + "src_dot_attributes_dot_components_dot_AttributeProperties_dot_visibleInStorefrontCaption": { + "context": "caption", + "string": "If enabled, attribute will be accessible to customers." + }, "src_dot_attributes_dot_components_dot_AttributeValueDeleteDialog_dot_1326420604": { "context": "delete attribute value", "string": "Are you sure you want to delete \"{name}\" value?" diff --git a/src/attributes/components/AttributeDetails/AttributeDetails.tsx b/src/attributes/components/AttributeDetails/AttributeDetails.tsx index 8ad3451d0..1138547ff 100644 --- a/src/attributes/components/AttributeDetails/AttributeDetails.tsx +++ b/src/attributes/components/AttributeDetails/AttributeDetails.tsx @@ -1,5 +1,6 @@ import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; +import { makeStyles } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; import CardTitle from "@saleor/components/CardTitle"; import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; @@ -7,16 +8,86 @@ import FormSpacer from "@saleor/components/FormSpacer"; import SingleSelectField from "@saleor/components/SingleSelectField"; import { AttributeErrorFragment } from "@saleor/fragments/types/AttributeErrorFragment"; import { commonMessages } from "@saleor/intl"; -import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; +import { + AttributeEntityTypeEnum, + AttributeInputTypeEnum +} from "@saleor/types/globalTypes"; import { getFormErrors } from "@saleor/utils/errors"; import getAttributeErrorMessage from "@saleor/utils/errors/attribute"; import React from "react"; -import { useIntl } from "react-intl"; +import { defineMessages, useIntl } from "react-intl"; import slugify from "slugify"; import { getAttributeSlugErrorMessage } from "../../errors"; import { AttributePageFormData } from "../AttributePage"; +const messages = defineMessages({ + attributeLabel: { + defaultMessage: "Default Label", + description: "attribute's label" + }, + attributeSlug: { + defaultMessage: "Attribute Code", + description: "attribute's slug short code label" + }, + attributeSlugHelperText: { + defaultMessage: "This is used internally. Make sure you don’t use spaces", + description: "attribute slug input field helper text" + }, + entityType: { + defaultMessage: "Entity", + description: "attribute's editor component entity" + }, + inputType: { + defaultMessage: "Catalog Input type for Store Owner", + description: "attribute's editor component" + }, + valueRequired: { + defaultMessage: "Value Required", + description: "check to require attribute to have value" + } +}); + +const inputTypeMessages = defineMessages({ + dropdown: { + defaultMessage: "Dropdown", + description: "product attribute type" + }, + file: { + defaultMessage: "File", + description: "file attribute type" + }, + multiselect: { + defaultMessage: "Multiple Select", + description: "product attribute type" + }, + references: { + defaultMessage: "References", + description: "references attribute type" + } +}); + +const entityTypeMessages = defineMessages({ + page: { + defaultMessage: "Pages", + description: "page attribute entity type" + } +}); + +const useStyles = makeStyles( + theme => ({ + inputTypeSection: { + columnGap: theme.spacing(2) + "px", + display: "flex", + [theme.breakpoints.down("md")]: { + flexFlow: "wrap", + rowGap: theme.spacing(3) + "px" + } + } + }), + { name: "AttributeDetails" } +); + export interface AttributeDetailsProps { canChangeType: boolean; data: AttributePageFormData; @@ -25,39 +96,39 @@ export interface AttributeDetailsProps { onChange: (event: React.ChangeEvent) => void; } -const AttributeDetails: React.FC = ({ - canChangeType, - data, - disabled, - errors, - onChange -}) => { +const AttributeDetails: React.FC = props => { + const { canChangeType, data, disabled, errors, onChange } = props; + const classes = useStyles(props); const intl = useIntl(); const inputTypeChoices = [ { - label: intl.formatMessage({ - defaultMessage: "Dropdown", - description: "product attribute type" - }), + label: intl.formatMessage(inputTypeMessages.dropdown), value: AttributeInputTypeEnum.DROPDOWN }, { - label: intl.formatMessage({ - defaultMessage: "Multiple Select", - description: "product attribute type" - }), + label: intl.formatMessage(inputTypeMessages.multiselect), value: AttributeInputTypeEnum.MULTISELECT }, { - label: intl.formatMessage({ - defaultMessage: "File", - description: "file attribute type" - }), + label: intl.formatMessage(inputTypeMessages.file), value: AttributeInputTypeEnum.FILE + }, + { + label: intl.formatMessage(inputTypeMessages.references), + value: AttributeInputTypeEnum.REFERENCE + } + ]; + const entityTypeChoices = [ + { + label: intl.formatMessage(entityTypeMessages.page), + value: AttributeEntityTypeEnum.PAGE } ]; - const formErrors = getFormErrors(["name", "slug", "inputType"], errors); + const formErrors = getFormErrors( + ["name", "slug", "inputType", "entityType"], + errors + ); return ( @@ -68,10 +139,7 @@ const AttributeDetails: React.FC = ({ = ({ - +
+ + {data.inputType === AttributeInputTypeEnum.REFERENCE && ( + + )} +
= ({ attribute === null ? { availableInGrid: true, + entityType: null, filterableInDashboard: true, filterableInStorefront: true, inputType: AttributeInputTypeEnum.DROPDOWN, @@ -98,6 +102,7 @@ const AttributePage: React.FC = ({ } : { availableInGrid: maybe(() => attribute.availableInGrid, true), + entityType: attribute?.entityType ?? null, filterableInDashboard: maybe( () => attribute.filterableInDashboard, true @@ -172,7 +177,9 @@ const AttributePage: React.FC = ({ errors={errors} onChange={change} /> - {data.inputType !== AttributeInputTypeEnum.FILE && ( + {ATTRIBUTE_TYPES_WITH_DEDICATED_VALUES.includes( + data.inputType + ) && ( <> = ({ const formErrors = getFormErrors(["storefrontSearchPosition"], errors); + const dashboardProperties = ATTRIBUTE_TYPES_WITH_DEDICATED_VALUES.includes( + data.inputType + ); + + const storefrontFacetedNavigationProperties = + ATTRIBUTE_TYPES_WITH_DEDICATED_VALUES.includes(data.inputType) && + data.type === AttributeTypeEnum.PRODUCT_TYPE; + return ( @@ -74,61 +125,48 @@ const AttributeProperties: React.FC = ({ /> */} - +
- {data.inputType !== AttributeInputTypeEnum.FILE && - data.type === AttributeTypeEnum.PRODUCT_TYPE && ( - <> - - {data.filterableInStorefront && ( - <> - - - - )} - - )} + {storefrontFacetedNavigationProperties && ( + <> + + {data.filterableInStorefront && ( + <> + + + + )} + + )} - + - + } @@ -136,14 +174,11 @@ const AttributeProperties: React.FC = ({ onChange={onChange} disabled={disabled} /> - {data.inputType !== AttributeInputTypeEnum.FILE && ( + {dashboardProperties && ( <> - +
@@ -151,12 +186,11 @@ const AttributeProperties: React.FC = ({ name={"filterableInDashboard" as keyof FormData} label={ <> - + - + } @@ -169,12 +203,9 @@ const AttributeProperties: React.FC = ({ name={"availableInGrid" as keyof FormData} label={ <> - + - + } diff --git a/src/attributes/fixtures.ts b/src/attributes/fixtures.ts index 160d45919..ff10cea3b 100644 --- a/src/attributes/fixtures.ts +++ b/src/attributes/fixtures.ts @@ -10,6 +10,7 @@ import { AttributeList_attributes_edges_node } from "./types/AttributeList"; export const attribute: AttributeDetailsFragment = { __typename: "Attribute" as "Attribute", availableInGrid: true, + entityType: null, filterableInDashboard: false, filterableInStorefront: true, id: "UHJvZHVjdEF0dHJpYnV0ZTo5", diff --git a/src/attributes/types/AttributeCreate.ts b/src/attributes/types/AttributeCreate.ts index 0e63aa68b..2e97f1264 100644 --- a/src/attributes/types/AttributeCreate.ts +++ b/src/attributes/types/AttributeCreate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeCreateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeErrorCode } from "./../../types/globalTypes"; +import { AttributeCreateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeEntityTypeEnum, AttributeErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: AttributeCreate @@ -47,6 +47,7 @@ export interface AttributeCreate_attributeCreate_attribute { privateMetadata: (AttributeCreate_attributeCreate_attribute_privateMetadata | null)[]; availableInGrid: boolean; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; storefrontSearchPosition: number; valueRequired: boolean; values: (AttributeCreate_attributeCreate_attribute_values | null)[] | null; diff --git a/src/attributes/types/AttributeDetails.ts b/src/attributes/types/AttributeDetails.ts index 54d682d16..e5605d8fb 100644 --- a/src/attributes/types/AttributeDetails.ts +++ b/src/attributes/types/AttributeDetails.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeTypeEnum, AttributeInputTypeEnum } from "./../../types/globalTypes"; +import { AttributeTypeEnum, AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL query operation: AttributeDetails @@ -47,6 +47,7 @@ export interface AttributeDetails_attribute { privateMetadata: (AttributeDetails_attribute_privateMetadata | null)[]; availableInGrid: boolean; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; storefrontSearchPosition: number; valueRequired: boolean; values: (AttributeDetails_attribute_values | null)[] | null; diff --git a/src/attributes/types/AttributeUpdate.ts b/src/attributes/types/AttributeUpdate.ts index 162d99f69..e4d0f1124 100644 --- a/src/attributes/types/AttributeUpdate.ts +++ b/src/attributes/types/AttributeUpdate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeUpdateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeErrorCode } from "./../../types/globalTypes"; +import { AttributeUpdateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeEntityTypeEnum, AttributeErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: AttributeUpdate @@ -47,6 +47,7 @@ export interface AttributeUpdate_attributeUpdate_attribute { privateMetadata: (AttributeUpdate_attributeUpdate_attribute_privateMetadata | null)[]; availableInGrid: boolean; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; storefrontSearchPosition: number; valueRequired: boolean; values: (AttributeUpdate_attributeUpdate_attribute_values | null)[] | null; diff --git a/src/attributes/types/AttributeValueCreate.ts b/src/attributes/types/AttributeValueCreate.ts index 4c2bb316b..25b3a12d8 100644 --- a/src/attributes/types/AttributeValueCreate.ts +++ b/src/attributes/types/AttributeValueCreate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeValueCreateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeErrorCode } from "./../../types/globalTypes"; +import { AttributeValueCreateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeEntityTypeEnum, AttributeErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: AttributeValueCreate @@ -47,6 +47,7 @@ export interface AttributeValueCreate_attributeValueCreate_attribute { privateMetadata: (AttributeValueCreate_attributeValueCreate_attribute_privateMetadata | null)[]; availableInGrid: boolean; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; storefrontSearchPosition: number; valueRequired: boolean; values: (AttributeValueCreate_attributeValueCreate_attribute_values | null)[] | null; diff --git a/src/attributes/types/AttributeValueDelete.ts b/src/attributes/types/AttributeValueDelete.ts index db63e0752..a649a5fb2 100644 --- a/src/attributes/types/AttributeValueDelete.ts +++ b/src/attributes/types/AttributeValueDelete.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeTypeEnum, AttributeInputTypeEnum, AttributeErrorCode } from "./../../types/globalTypes"; +import { AttributeTypeEnum, AttributeInputTypeEnum, AttributeEntityTypeEnum, AttributeErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: AttributeValueDelete @@ -47,6 +47,7 @@ export interface AttributeValueDelete_attributeValueDelete_attribute { privateMetadata: (AttributeValueDelete_attributeValueDelete_attribute_privateMetadata | null)[]; availableInGrid: boolean; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; storefrontSearchPosition: number; valueRequired: boolean; values: (AttributeValueDelete_attributeValueDelete_attribute_values | null)[] | null; diff --git a/src/attributes/types/AttributeValueUpdate.ts b/src/attributes/types/AttributeValueUpdate.ts index da64a2a40..80ec5ec4d 100644 --- a/src/attributes/types/AttributeValueUpdate.ts +++ b/src/attributes/types/AttributeValueUpdate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeValueCreateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeErrorCode } from "./../../types/globalTypes"; +import { AttributeValueCreateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeEntityTypeEnum, AttributeErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: AttributeValueUpdate @@ -47,6 +47,7 @@ export interface AttributeValueUpdate_attributeValueUpdate_attribute { privateMetadata: (AttributeValueUpdate_attributeValueUpdate_attribute_privateMetadata | null)[]; availableInGrid: boolean; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; storefrontSearchPosition: number; valueRequired: boolean; values: (AttributeValueUpdate_attributeValueUpdate_attribute_values | null)[] | null; diff --git a/src/attributes/utils/data.ts b/src/attributes/utils/data.ts index 4cccbcd2a..ee2a64f86 100644 --- a/src/attributes/utils/data.ts +++ b/src/attributes/utils/data.ts @@ -10,8 +10,53 @@ import { } from "@saleor/types/globalTypes"; import { MutationFetchResult } from "react-apollo"; +import { AttributePageFormData } from "../components/AttributePage"; +import { AttributeValueEditDialogFormData } from "../components/AttributeValueEditDialog"; import { AttributeValueDelete } from "../types/AttributeValueDelete"; +export const ATTRIBUTE_TYPES_WITH_DEDICATED_VALUES = [ + AttributeInputTypeEnum.DROPDOWN, + AttributeInputTypeEnum.MULTISELECT +]; + +function getSimpleAttributeData( + data: AttributePageFormData, + values: AttributeValueEditDialogFormData[] +) { + return { + ...data, + metadata: undefined, + privateMetadata: undefined, + storefrontSearchPosition: parseInt(data.storefrontSearchPosition, 10), + values: values.map(value => ({ + name: value.name + })) + }; +} + +function getFileOrReferenceAttributeData( + data: AttributePageFormData, + values: AttributeValueEditDialogFormData[] +) { + return { + ...getSimpleAttributeData(data, values), + availableInGrid: undefined, + filterableInDashboard: undefined, + filterableInStorefront: undefined + }; +} + +export function getAttributeData( + data: AttributePageFormData, + values: AttributeValueEditDialogFormData[] +) { + if (ATTRIBUTE_TYPES_WITH_DEDICATED_VALUES.includes(data.inputType)) { + return getSimpleAttributeData(data, values); + } else { + return getFileOrReferenceAttributeData(data, values); + } +} + export const isFileValueUnused = ( attributesWithNewFileValue: FormsetData, existingAttribute: diff --git a/src/attributes/views/AttributeCreate/AttributeCreate.tsx b/src/attributes/views/AttributeCreate/AttributeCreate.tsx index 7fe4e48f0..3dc6bd039 100644 --- a/src/attributes/views/AttributeCreate/AttributeCreate.tsx +++ b/src/attributes/views/AttributeCreate/AttributeCreate.tsx @@ -1,12 +1,10 @@ +import { getAttributeData } from "@saleor/attributes/utils/data"; 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 { - AttributeErrorCode, - AttributeInputTypeEnum -} 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 { @@ -57,33 +55,6 @@ function areValuesEqual( return a.name === b.name; } -function getSimpleAttributeData( - data: AttributePageFormData, - values: AttributeValueEditDialogFormData[] -) { - return { - ...data, - metadata: undefined, - privateMetadata: undefined, - storefrontSearchPosition: parseInt(data.storefrontSearchPosition, 10), - values: values.map(value => ({ - name: value.name - })) - }; -} - -function getFileAttributeData( - data: AttributePageFormData, - values: AttributeValueEditDialogFormData[] -) { - return { - ...getSimpleAttributeData(data, values), - availableInGrid: undefined, - filterableInDashboard: undefined, - filterableInStorefront: undefined - }; -} - const AttributeDetails: React.FC = ({ params }) => { const navigate = useNavigator(); const notify = useNotifier(); @@ -145,10 +116,7 @@ const AttributeDetails: React.FC = ({ params }) => { setValues(move(values[oldIndex], values, areValuesEqual, newIndex)); const handleCreate = async (data: AttributePageFormData) => { - const input = - data.inputType === AttributeInputTypeEnum.FILE - ? getFileAttributeData(data, values) - : getSimpleAttributeData(data, values); + const input = getAttributeData(data, values); const result = await attributeCreate({ variables: { diff --git a/src/attributes/views/AttributeDetails/AttributeDetails.tsx b/src/attributes/views/AttributeDetails/AttributeDetails.tsx index acfc3deb3..02dc810d9 100644 --- a/src/attributes/views/AttributeDetails/AttributeDetails.tsx +++ b/src/attributes/views/AttributeDetails/AttributeDetails.tsx @@ -178,6 +178,7 @@ const AttributeDetails: React.FC = ({ id, params }) => { const handleUpdate = async (data: AttributePageFormData) => { const input = { ...data, + entityType: undefined, inputType: undefined, metadata: undefined, privateMetadata: undefined, diff --git a/src/fragments/attributes.ts b/src/fragments/attributes.ts index 76e6c2504..1ba2d436f 100644 --- a/src/fragments/attributes.ts +++ b/src/fragments/attributes.ts @@ -12,6 +12,7 @@ export const attributeValueFragment = gql` file { ...FileFragment } + reference } `; @@ -36,6 +37,7 @@ export const attributeDetailsFragment = gql` ...MetadataFragment availableInGrid inputType + entityType storefrontSearchPosition valueRequired values { diff --git a/src/fragments/types/AttributeDetailsFragment.ts b/src/fragments/types/AttributeDetailsFragment.ts index de9a0d902..3cf67ce3b 100644 --- a/src/fragments/types/AttributeDetailsFragment.ts +++ b/src/fragments/types/AttributeDetailsFragment.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeTypeEnum, AttributeInputTypeEnum } from "./../../types/globalTypes"; +import { AttributeTypeEnum, AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL fragment: AttributeDetailsFragment @@ -47,6 +47,7 @@ export interface AttributeDetailsFragment { privateMetadata: (AttributeDetailsFragment_privateMetadata | null)[]; availableInGrid: boolean; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; storefrontSearchPosition: number; valueRequired: boolean; values: (AttributeDetailsFragment_values | null)[] | null; diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index eb5bd3e4c..04b6cbf60 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -28404,57 +28404,61 @@ exports[`Storyshots Views / Attributes / Attribute details create 1`] = ` class="FormSpacer-spacer-id" />
-
+
- Dropdown -
- - - + Dropdown +
+ + + +
-
+
- Dropdown -
- - - + Dropdown +
+ + + +
-
+
- Dropdown -
- - - + Dropdown +
+ + + +
-
+
- Dropdown -
- - - + Dropdown +
+ + + +
-
+
- Multiple Select -
- - - + Multiple Select +
+ + + +
-
+
- Dropdown -
- - - + Dropdown +
+ + + +
Date: Tue, 12 Jan 2021 12:13:02 +0100 Subject: [PATCH 06/35] 1992 - Add attribute references select dialog (#931) * Add attribute references select dialog * Add reference attribute support to products and variants * Fix attribute select dialog selection * Refactor reference attribute handlers * Refactor reference attribute handlers * Refactor searching for reference pages --- locale/defaultMessages.json | 12 +++ src/attributes/utils/data.ts | 67 +++++++++++++++ .../AssignAttributeValueDialog.tsx | 56 ++++++++++++ .../AssignAttributeValueDialog/index.ts | 2 + .../Attributes/Attributes.stories.tsx | 4 +- src/components/Attributes/Attributes.tsx | 51 +++++++++-- src/components/Attributes/fixtures.ts | 23 ++++- .../SortableChipsField/SortableChipsField.tsx | 4 +- .../PageDetailsPage/PageDetailsPage.tsx | 67 ++++++++++++++- src/pages/components/PageDetailsPage/form.tsx | 26 ++++-- src/pages/index.tsx | 14 ++- src/pages/urls.ts | 9 +- src/pages/utils/data.ts | 31 +++---- src/pages/views/PageCreate.tsx | 56 ++++++++++-- src/pages/views/PageDetails.tsx | 35 ++++++++ src/pages/views/PageList/PageList.tsx | 2 +- .../ProductCreatePage/ProductCreatePage.tsx | 66 +++++++++++++- .../components/ProductCreatePage/form.tsx | 16 +++- .../ProductUpdatePage.test.tsx | 3 + .../ProductUpdatePage/ProductUpdatePage.tsx | 66 +++++++++++++- .../components/ProductUpdatePage/form.tsx | 16 +++- .../ProductVariantCreatePage.tsx | 65 +++++++++++++- .../ProductVariantCreatePage/form.tsx | 22 +++-- .../ProductVariantPage/ProductVariantPage.tsx | 67 ++++++++++++++- .../components/ProductVariantPage/form.tsx | 18 +++- src/products/index.tsx | 17 ++-- src/products/urls.ts | 34 ++++++-- src/products/utils/data.ts | 50 ++++------- src/products/utils/handlers.ts | 10 +++ .../views/ProductCreate/ProductCreate.tsx | 85 +++++++++++++------ .../views/ProductUpdate/ProductUpdate.tsx | 55 +++++++++--- src/products/views/ProductVariant.tsx | 37 ++++++++ src/products/views/ProductVariantCreate.tsx | 47 +++++++++- .../stories/pages/PageDetailsPage.tsx | 3 + .../stories/products/ProductCreatePage.tsx | 9 ++ .../stories/products/ProductUpdatePage.tsx | 3 + .../products/ProductVariantCreatePage.tsx | 15 ++++ .../stories/products/ProductVariantPage.tsx | 12 +++ 38 files changed, 1008 insertions(+), 167 deletions(-) create mode 100644 src/components/AssignAttributeValueDialog/AssignAttributeValueDialog.tsx create mode 100644 src/components/AssignAttributeValueDialog/index.ts diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index ff4677d0d..b55495938 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -1517,6 +1517,18 @@ "src_dot_components_dot_AssignAttributeDialog_dot_902296540": { "string": "Search Attributes" }, + "src_dot_components_dot_AssignAttributeValueDialog_dot_header": { + "context": "dialog header", + "string": "Assign Attribute Value" + }, + "src_dot_components_dot_AssignAttributeValueDialog_dot_searchLabel": { + "context": "label", + "string": "Search Attribute Value" + }, + "src_dot_components_dot_AssignAttributeValueDialog_dot_searchPlaceholder": { + "context": "placeholder", + "string": "Search by value name, etc..." + }, "src_dot_components_dot_AssignCategoryDialog_dot_3125506097": { "context": "dialog header", "string": "Assign Category" diff --git a/src/attributes/utils/data.ts b/src/attributes/utils/data.ts index ee2a64f86..273c4fabe 100644 --- a/src/attributes/utils/data.ts +++ b/src/attributes/utils/data.ts @@ -1,9 +1,14 @@ +import { + AttributeInput, + AttributeInputData +} from "@saleor/components/Attributes"; import { FileUpload } from "@saleor/files/types/FileUpload"; import { AttributeErrorFragment } from "@saleor/fragments/types/AttributeErrorFragment"; import { SelectedVariantAttributeFragment } from "@saleor/fragments/types/SelectedVariantAttributeFragment"; import { UploadErrorFragment } from "@saleor/fragments/types/UploadErrorFragment"; import { FormsetData } from "@saleor/hooks/useFormset"; import { PageDetails_page_attributes } from "@saleor/pages/types/PageDetails"; +import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; import { AttributeInputTypeEnum, AttributeValueInput @@ -99,6 +104,18 @@ export const mergeAttributeValueDeleteErrors = ( return errors; }, []); +export const mergeAttributeValues = ( + attributeId: string, + attributeValues: string[], + attributes: FormsetData +) => { + const attribute = attributes.find(attribute => attribute.id === attributeId); + + return attribute.value + ? [...attribute.value, ...attributeValues] + : attributeValues; +}; + export const getFileValuesToUploadFromAttributes = ( attributesWithNewFileValue: FormsetData ) => attributesWithNewFileValue.filter(fileAttribute => !!fileAttribute.value); @@ -149,3 +166,53 @@ export const getAttributesAfterFileAttributesUpdate = ( return uploadedFileAttributes.concat(removedFileAttributes); }; + +export const getFileAttributeDisplayData = ( + attribute: AttributeInput, + attributesWithNewFileValue: FormsetData +) => { + const attributeWithNewFileValue = attributesWithNewFileValue.find( + attributeWithNewFile => attribute.id === attributeWithNewFile.id + ); + + if (attributeWithNewFileValue) { + return { + ...attribute, + value: attributeWithNewFileValue?.value?.name + ? [attributeWithNewFileValue.value.name] + : [] + }; + } + return attribute; +}; + +export const getReferenceAttributeDisplayData = ( + attribute: AttributeInput, + referencePages: SearchPages_search_edges_node[] +) => ({ + ...attribute, + data: { + ...attribute.data, + references: + referencePages && + attribute.value?.map(value => + referencePages.find(reference => reference.id === value) + ) + } +}); + +export const getAttributesDisplayData = ( + attributes: AttributeInput[], + attributesWithNewFileValue: FormsetData, + referencePages: SearchPages_search_edges_node[] +) => + attributes.map(attribute => { + if (attribute.data.inputType === AttributeInputTypeEnum.REFERENCE) { + return getReferenceAttributeDisplayData(attribute, referencePages); + } + if (attribute.data.inputType === AttributeInputTypeEnum.FILE) { + return getFileAttributeDisplayData(attribute, attributesWithNewFileValue); + } + + return attribute; + }); diff --git a/src/components/AssignAttributeValueDialog/AssignAttributeValueDialog.tsx b/src/components/AssignAttributeValueDialog/AssignAttributeValueDialog.tsx new file mode 100644 index 000000000..ff2711eeb --- /dev/null +++ b/src/components/AssignAttributeValueDialog/AssignAttributeValueDialog.tsx @@ -0,0 +1,56 @@ +import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; +import React from "react"; +import { defineMessages, useIntl } from "react-intl"; + +import AssignContainerDialog, { + AssignContainerDialogProps +} from "../AssignContainerDialog"; + +const messages = defineMessages({ + header: { + defaultMessage: "Assign Attribute Value", + description: "dialog header" + }, + searchLabel: { + defaultMessage: "Search Attribute Value", + description: "label" + }, + searchPlaceholder: { + defaultMessage: "Search by value name, etc...", + description: "placeholder" + } +}); + +interface AssignAttributeValueDialogProps + extends Omit< + AssignContainerDialogProps, + "containers" | "title" | "search" | "confirmButtonState" + > { + attributeValues: SearchPages_search_edges_node[]; +} + +const AssignAttributeValueDialog: React.FC = ({ + attributeValues, + ...rest +}) => { + const intl = useIntl(); + + return ( + ({ + id: value.id, + name: value.title + }))} + search={{ + label: intl.formatMessage(messages.searchLabel), + placeholder: intl.formatMessage(messages.searchPlaceholder) + }} + title={intl.formatMessage(messages.header)} + confirmButtonState="default" + {...rest} + /> + ); +}; + +AssignAttributeValueDialog.displayName = "AssignAttributeValueDialog"; +export default AssignAttributeValueDialog; diff --git a/src/components/AssignAttributeValueDialog/index.ts b/src/components/AssignAttributeValueDialog/index.ts new file mode 100644 index 000000000..8dce80ff9 --- /dev/null +++ b/src/components/AssignAttributeValueDialog/index.ts @@ -0,0 +1,2 @@ +export { default } from "./AssignAttributeValueDialog"; +export * from "./AssignAttributeValueDialog"; diff --git a/src/components/Attributes/Attributes.stories.tsx b/src/components/Attributes/Attributes.stories.tsx index ba294cbd9..679184415 100644 --- a/src/components/Attributes/Attributes.stories.tsx +++ b/src/components/Attributes/Attributes.stories.tsx @@ -13,8 +13,8 @@ const props: AttributesProps = { onChange: () => undefined, onFileChange: () => undefined, onMultiChange: () => undefined, - onReferencesChange: () => undefined, - onReferencesChangeClick: () => undefined, + onReferencesAddClick: () => undefined, + onReferencesRemove: () => undefined, onReferencesReorder: () => undefined }; diff --git a/src/components/Attributes/Attributes.tsx b/src/components/Attributes/Attributes.tsx index 87a9077bf..4a366ceb3 100644 --- a/src/components/Attributes/Attributes.tsx +++ b/src/components/Attributes/Attributes.tsx @@ -16,6 +16,7 @@ import { AttributeValueFragment } from "@saleor/fragments/types/AttributeValueFr import { PageErrorWithAttributesFragment } from "@saleor/fragments/types/PageErrorWithAttributesFragment"; import { ProductErrorWithAttributesFragment } from "@saleor/fragments/types/ProductErrorWithAttributesFragment"; import { FormsetAtomicData, FormsetChange } from "@saleor/hooks/useFormset"; +import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; import { ReorderAction } from "@saleor/types"; import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; import { getProductErrorMessage } from "@saleor/utils/errors"; @@ -30,7 +31,9 @@ import { } from "react-intl"; import FileUploadField, { FileChoiceType } from "../FileUploadField"; -import SortableChipsField from "../SortableChipsField"; +import SortableChipsField, { + SortableChipsFieldValueType +} from "../SortableChipsField"; import BasicAttributeRow from "./BasicAttributeRow"; import ExtendedAttributeRow from "./ExtendedAttributeRow"; import { VariantAttributeScope } from "./types"; @@ -41,6 +44,7 @@ export interface AttributeInputData { isRequired: boolean; values: AttributeValueFragment[]; selectedValues?: AttributeValueFragment[]; + references?: SearchPages_search_edges_node[]; } export type AttributeInput = FormsetAtomicData; export type AttributeFileInput = FormsetAtomicData; @@ -55,8 +59,8 @@ export interface AttributesProps { onChange: FormsetChange; onMultiChange: FormsetChange; onFileChange: FormsetChange; - onReferencesChange?: FormsetChange; // TODO: temporairy optional, should be changed to required, after all pages implement it - onReferencesChangeClick?: () => void; // TODO: temporairy optional, should be changed to required, after all pages implement it + onReferencesRemove?: FormsetChange; // TODO: temporairy optional, should be changed to required, after all pages implement it + onReferencesAddClick?: (attribute: AttributeInput) => void; // TODO: temporairy optional, should be changed to required, after all pages implement it onReferencesReorder?: ReorderAction; // TODO: temporairy optional, should be changed to required, after all pages implement it } @@ -129,6 +133,10 @@ function getMultiChoices( function getMultiDisplayValue( attribute: AttributeInput ): MultiAutocompleteChoiceType[] { + if (!attribute.value) { + return []; + } + return attribute.value.map(attributeValue => { const definedAttributeValue = attribute.data.values.find( definedValue => definedValue.slug === attributeValue @@ -147,6 +155,31 @@ function getMultiDisplayValue( }); } +function getReferenceDisplayValue( + attribute: AttributeInput +): SortableChipsFieldValueType[] { + if (!attribute.value) { + return []; + } + + return attribute.value.map(attributeValue => { + const definedAttributeValue = attribute.data.references?.find( + reference => reference.id === attributeValue + ); + if (!!definedAttributeValue) { + return { + label: definedAttributeValue.title, + value: definedAttributeValue.id + }; + } + + return { + label: attributeValue, + value: attributeValue + }; + }); +} + function getSingleChoices( values: AttributeValueFragment[] ): SingleAutocompleteChoiceType[] { @@ -157,7 +190,7 @@ function getSingleChoices( } function getFileChoice(attribute: AttributeInput): FileChoiceType { - const attributeValue = attribute.value[0]; + const attributeValue = attribute.value?.length > 0 && attribute.value[0]; const definedAttributeValue = attribute.data.values.find( definedValue => definedValue.slug === attributeValue @@ -217,8 +250,8 @@ const Attributes: React.FC = ({ onChange, onMultiChange, onFileChange, - onReferencesChange, - onReferencesChangeClick, + onReferencesRemove, + onReferencesAddClick, onReferencesReorder }) => { const intl = useIntl(); @@ -272,13 +305,13 @@ const Attributes: React.FC = ({ defaultMessage: "Assign references", description: "button label" })} - onSelect={onReferencesChangeClick} + onSelect={() => onReferencesAddClick(attribute)} disabled={disabled} > - onReferencesChange( + onReferencesRemove( attribute.id, attribute.value?.filter(id => id !== value) ) diff --git a/src/components/Attributes/fixtures.ts b/src/components/Attributes/fixtures.ts index e6aff799d..ef11df402 100644 --- a/src/components/Attributes/fixtures.ts +++ b/src/components/Attributes/fixtures.ts @@ -88,6 +88,23 @@ const REFERENCE_ATTRIBUTE: AttributeInput = { data: { inputType: AttributeInputTypeEnum.REFERENCE, isRequired: true, + references: [ + { + __typename: "Page", + id: "vbnhgcvjhbvhj", + title: "References First Value" + }, + { + __typename: "Page", + id: "gucngdfdfvdvd", + title: "References Second Value" + }, + { + __typename: "Page", + id: "dfdfdsfdsfdse", + title: "References Third Value" + } + ], values: [ { __typename: "AttributeValue", @@ -143,9 +160,9 @@ export const ATTRIBUTES_SELECTED: AttributeInput[] = [ { ...REFERENCE_ATTRIBUTE, value: [ - REFERENCE_ATTRIBUTE.data.values[0].slug, - REFERENCE_ATTRIBUTE.data.values[1].slug, - REFERENCE_ATTRIBUTE.data.values[2].slug + REFERENCE_ATTRIBUTE.data.values[0].id, + REFERENCE_ATTRIBUTE.data.values[1].id, + REFERENCE_ATTRIBUTE.data.values[2].id ] } ]; diff --git a/src/components/SortableChipsField/SortableChipsField.tsx b/src/components/SortableChipsField/SortableChipsField.tsx index ef44b8b4c..a2282d441 100644 --- a/src/components/SortableChipsField/SortableChipsField.tsx +++ b/src/components/SortableChipsField/SortableChipsField.tsx @@ -20,9 +20,9 @@ const useStyles = makeStyles( } ); -interface SortableChipsFieldValueType { +export interface SortableChipsFieldValueType { label: string; - value: any; + value: string; } export interface SortableChipsFieldProps extends SortableContainerProps { diff --git a/src/pages/components/PageDetailsPage/PageDetailsPage.tsx b/src/pages/components/PageDetailsPage/PageDetailsPage.tsx index 809ba0037..6b6dadc5a 100644 --- a/src/pages/components/PageDetailsPage/PageDetailsPage.tsx +++ b/src/pages/components/PageDetailsPage/PageDetailsPage.tsx @@ -1,5 +1,7 @@ +import { mergeAttributeValues } from "@saleor/attributes/utils/data"; import AppHeader from "@saleor/components/AppHeader"; -import Attributes from "@saleor/components/Attributes"; +import AssignAttributeValueDialog from "@saleor/components/AssignAttributeValueDialog"; +import Attributes, { AttributeInput } from "@saleor/components/Attributes"; import CardSpacer from "@saleor/components/CardSpacer"; import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import Container from "@saleor/components/Container"; @@ -13,6 +15,8 @@ import { PageErrorWithAttributesFragment } from "@saleor/fragments/types/PageErr import useDateLocalize from "@saleor/hooks/useDateLocalize"; import { SubmitPromise } from "@saleor/hooks/useForm"; import { sectionNames } from "@saleor/intl"; +import { getAttributeValuesFromReferences } from "@saleor/pages/utils/data"; +import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; import { SearchPageTypes_search_edges_node } from "@saleor/searches/types/SearchPageTypes"; import { FetchMoreProps } from "@saleor/types"; import React from "react"; @@ -21,13 +25,14 @@ import { useIntl } from "react-intl"; import { PageDetails_page } from "../../types/PageDetails"; import PageInfo from "../PageInfo"; import PageOrganizeContent from "../PageOrganizeContent"; -import PageForm, { PageData } from "./form"; +import PageForm, { PageData, PageUpdateHandlers } from "./form"; export interface PageDetailsPageProps { loading: boolean; errors: PageErrorWithAttributesFragment[]; page: PageDetails_page; pageTypes?: SearchPageTypes_search_edges_node[]; + referencePages: SearchPages_search_edges_node[]; allowEmptySlug?: boolean; saveButtonBarState: ConfirmButtonTransitionState; onBack: () => void; @@ -35,6 +40,11 @@ export interface PageDetailsPageProps { onSubmit: (data: PageData) => SubmitPromise; fetchPageTypes?: (data: string) => void; fetchMorePageTypes?: FetchMoreProps; + assignReferencesAttributeId?: string; + onAssignReferencesClick: (attribute: AttributeInput) => void; + fetchReferencePages?: (data: string) => void; + fetchMoreReferencePages?: FetchMoreProps; + onCloseDialog: () => void; } const PageDetailsPage: React.FC = ({ @@ -42,20 +52,49 @@ const PageDetailsPage: React.FC = ({ errors, page, pageTypes, + referencePages, saveButtonBarState, onBack, onRemove, onSubmit, fetchPageTypes, - fetchMorePageTypes + fetchMorePageTypes, + assignReferencesAttributeId, + onAssignReferencesClick, + fetchReferencePages, + fetchMoreReferencePages, + onCloseDialog }) => { const intl = useIntl(); const localizeDate = useDateLocalize(); const pageExists = page !== null; + const canOpenAssignReferencesAttributeDialog = !!assignReferencesAttributeId; + + const handleAssignReferenceAttribute = ( + attributeValues: string[], + data: PageData, + handlers: PageUpdateHandlers + ) => { + handlers.selectAttributeReference( + assignReferencesAttributeId, + mergeAttributeValues( + assignReferencesAttributeId, + attributeValues, + data.attributes + ) + ); + onCloseDialog(); + }; + return ( - + {({ change, data, pageType, handlers, hasChanged, submit }) => ( @@ -107,6 +146,8 @@ const PageDetailsPage: React.FC = ({ onChange={handlers.selectAttribute} onMultiChange={handlers.selectAttributeMulti} onFileChange={handlers.selectAttributeFile} + onReferencesRemove={handlers.selectAttributeReference} + onReferencesAddClick={onAssignReferencesClick} /> )} @@ -161,6 +202,24 @@ const PageDetailsPage: React.FC = ({ onDelete={page === null ? undefined : onRemove} onSave={submit} /> + {canOpenAssignReferencesAttributeDialog && ( + + handleAssignReferenceAttribute(attributeValues, data, handlers) + } + /> + )} )} diff --git a/src/pages/components/PageDetailsPage/form.tsx b/src/pages/components/PageDetailsPage/form.tsx index 36e83b9de..91024107d 100644 --- a/src/pages/components/PageDetailsPage/form.tsx +++ b/src/pages/components/PageDetailsPage/form.tsx @@ -1,4 +1,5 @@ import { OutputData } from "@editorjs/editorjs"; +import { getAttributesDisplayData } from "@saleor/attributes/utils/data"; import { AttributeInput } from "@saleor/components/Attributes"; import { MetadataFormData } from "@saleor/components/Metadata"; import { RichTextEditorChange } from "@saleor/components/RichTextEditor"; @@ -13,16 +14,15 @@ import { PageDetails_page, PageDetails_page_pageType } from "@saleor/pages/types/PageDetails"; -import { - getAttributeInputFromPage, - getAttributesDisplayData -} from "@saleor/pages/utils/data"; +import { getAttributeInputFromPage } from "@saleor/pages/utils/data"; import { createPageTypeSelectHandler } from "@saleor/pages/utils/handlers"; import { createAttributeChangeHandler, createAttributeFileChangeHandler, - createAttributeMultiChangeHandler + createAttributeMultiChangeHandler, + createAttributeReferenceChangeHandler } from "@saleor/products/utils/handlers"; +import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; import getPublicationData from "@saleor/utils/data/getPublicationData"; import handleFormSubmit from "@saleor/utils/handlers/handleFormSubmit"; import { mapMetadataItemToInput } from "@saleor/utils/maps"; @@ -51,12 +51,13 @@ export interface PageSubmitData extends PageFormData { content: OutputData; } -interface PageUpdateHandlers { +export interface PageUpdateHandlers { changeMetadata: FormChange; changeContent: RichTextEditorChange; selectPageType: FormChange; selectAttribute: FormsetChange; selectAttributeMulti: FormsetChange; + selectAttributeReference: FormsetChange; selectAttributeFile: FormsetChange; } export interface UsePageUpdateFormResult { @@ -72,12 +73,14 @@ export interface PageFormProps { children: (props: UsePageUpdateFormResult) => React.ReactNode; page: PageDetails_page; pageTypes?: PageDetails_page_pageType[]; + referencePages: SearchPages_search_edges_node[]; onSubmit: (data: PageData) => SubmitPromise; } function usePageForm( page: PageDetails_page, onSubmit: (data: PageData) => SubmitPromise, + referencePages: SearchPages_search_edges_node[], pageTypes?: PageDetails_page_pageType[] ): UsePageUpdateFormResult { const [changed, setChanged] = React.useState(false); @@ -136,6 +139,10 @@ function usePageForm( attributes.data, triggerChange ); + const handleAttributeReferenceChange = createAttributeReferenceChangeHandler( + attributes.change, + triggerChange + ); const handleAttributeFileChange = createAttributeFileChangeHandler( attributes.change, attributesWithNewFileValue.data, @@ -149,7 +156,8 @@ function usePageForm( ...form.data, attributes: getAttributesDisplayData( attributes.data, - attributesWithNewFileValue.data + attributesWithNewFileValue.data, + referencePages ), content: content.current }); @@ -185,6 +193,7 @@ function usePageForm( selectAttribute: handleAttributeChange, selectAttributeFile: handleAttributeFileChange, selectAttributeMulti: handleAttributeMultiChange, + selectAttributeReference: handleAttributeReferenceChange, selectPageType }, hasChanged: changed, @@ -196,10 +205,11 @@ function usePageForm( const PageForm: React.FC = ({ children, page, + referencePages, pageTypes, onSubmit }) => { - const props = usePageForm(page, onSubmit, pageTypes); + const props = usePageForm(page, onSubmit, referencePages, pageTypes); return
{children(props)}
; }; diff --git a/src/pages/index.tsx b/src/pages/index.tsx index cf98c14ae..11bbcdb84 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -14,7 +14,7 @@ import { pagePath, PageUrlQueryParams } from "./urls"; -import PageCreate from "./views/PageCreate"; +import PageCreateComponent from "./views/PageCreate"; import PageDetailsComponent from "./views/PageDetails"; import PageListComponent from "./views/PageList"; @@ -28,6 +28,18 @@ const PageList: React.FC> = ({ location }) => { return ; }; +const PageCreate: React.FC> = ({ match }) => { + const qs = parseQs(location.search.substr(1)); + const params: PageUrlQueryParams = qs; + + return ( + + ); +}; + const PageDetails: React.FC> = ({ match }) => { const qs = parseQs(location.search.substr(1)); const params: PageUrlQueryParams = qs; diff --git a/src/pages/urls.ts b/src/pages/urls.ts index ff6e228f9..748153f58 100644 --- a/src/pages/urls.ts +++ b/src/pages/urls.ts @@ -1,7 +1,7 @@ import { stringify as stringifyQs } from "qs"; import urlJoin from "url-join"; -import { BulkAction, Dialog, Pagination, Sort } from "../types"; +import { BulkAction, Dialog, Pagination, SingleAction, Sort } from "../types"; export const pagesSection = "/pages/"; @@ -21,10 +21,11 @@ export const pageListUrl = (params?: PageListUrlQueryParams) => pageListPath + "?" + stringifyQs(params); export const pagePath = (id: string) => urlJoin(pagesSection, id); -export type PageUrlDialog = "remove"; -export type PageUrlQueryParams = Dialog; +export type PageUrlDialog = "remove" | "assign-attribute-value"; +export type PageUrlQueryParams = Dialog & SingleAction; export const pageUrl = (id: string, params?: PageUrlQueryParams) => pagePath(encodeURIComponent(id)) + "?" + stringifyQs(params); export const pageCreatePath = urlJoin(pagesSection, "add"); -export const pageCreateUrl = pageCreatePath; +export const pageCreateUrl = (params?: PageUrlQueryParams) => + pageCreatePath + "?" + stringifyQs(params); diff --git a/src/pages/utils/data.ts b/src/pages/utils/data.ts index e6dffacf4..df6ecb031 100644 --- a/src/pages/utils/data.ts +++ b/src/pages/utils/data.ts @@ -1,5 +1,5 @@ import { AttributeInput } from "@saleor/components/Attributes"; -import { FormsetData } from "@saleor/hooks/useFormset"; +import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; import { PageDetails_page, @@ -37,22 +37,17 @@ export function getAttributeInputFromPageType( })); } -export const getAttributesDisplayData = ( +export const getAttributeValuesFromReferences = ( + attributeId: string, attributes: AttributeInput[], - attributesWithNewFileValue: FormsetData -) => - attributes.map(attribute => { - const attributeWithNewFileValue = attributesWithNewFileValue.find( - attributeWithNewFile => attribute.id === attributeWithNewFile.id - ); + referencePages: SearchPages_search_edges_node[] +) => { + const attribute = attributes?.find(attribute => attribute.id === attributeId); - if (attributeWithNewFileValue) { - return { - ...attribute, - value: attributeWithNewFileValue?.value?.name - ? [attributeWithNewFileValue.value.name] - : [] - }; - } - return attribute; - }); + return ( + referencePages?.filter( + value => + !attribute?.value?.some(selectedValue => selectedValue === value.id) + ) || [] + ); +}; diff --git a/src/pages/views/PageCreate.tsx b/src/pages/views/PageCreate.tsx index 39b50aa83..a77a11edc 100644 --- a/src/pages/views/PageCreate.tsx +++ b/src/pages/views/PageCreate.tsx @@ -3,11 +3,13 @@ import { handleUploadMultipleFiles, prepareAttributesInput } from "@saleor/attributes/utils/handlers"; +import { AttributeInput } from "@saleor/components/Attributes"; import { WindowTitle } from "@saleor/components/WindowTitle"; import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config"; import { useFileUploadMutation } from "@saleor/files/mutations"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; +import usePageSearch from "@saleor/searches/usePageSearch"; import usePageTypeSearch from "@saleor/searches/usePageTypeSearch"; import createMetadataCreateHandler from "@saleor/utils/handlers/metadataCreateHandler"; import { @@ -21,13 +23,19 @@ import PageDetailsPage from "../components/PageDetailsPage"; import { PageSubmitData } from "../components/PageDetailsPage/form"; import { TypedPageCreate } from "../mutations"; import { PageCreate as PageCreateData } from "../types/PageCreate"; -import { pageListUrl, pageUrl } from "../urls"; +import { + pageCreateUrl, + pageListUrl, + pageUrl, + PageUrlQueryParams +} from "../urls"; export interface PageCreateProps { id: string; + params: PageUrlQueryParams; } -export const PageCreate: React.FC = () => { +export const PageCreate: React.FC = ({ params }) => { const navigate = useNavigator(); const notify = useNotifier(); const intl = useIntl(); @@ -42,6 +50,14 @@ export const PageCreate: React.FC = () => { variables: DEFAULT_INITIAL_SEARCH_DATA }); + const { + loadMore: loadMorePages, + search: searchPages, + result: searchPagesOpts + } = usePageSearch({ + variables: DEFAULT_INITIAL_SEARCH_DATA + }); + const [uploadFile, uploadFileOpts] = useFileUploadMutation({}); const handlePageCreate = (data: PageCreateData) => { @@ -56,6 +72,26 @@ export const PageCreate: React.FC = () => { } }; + const handleAssignAttributeReferenceClick = (attribute: AttributeInput) => + navigate( + pageCreateUrl({ + action: "assign-attribute-value", + id: attribute.id + }) + ); + + const fetchMorePageTypes = { + hasMore: searchPageTypesOpts.data?.search.pageInfo.hasNextPage, + loading: searchPageTypesOpts.loading, + onFetchMore: loadMorePageTypes + }; + + const fetchMoreReferencePages = { + hasMore: searchPagesOpts.data?.search.pageInfo.hasNextPage, + loading: searchPagesOpts.loading, + onFetchMore: loadMorePages + }; + return ( {(pageCreate, pageCreateOpts) => { @@ -119,11 +155,17 @@ export const PageCreate: React.FC = () => { onRemove={() => undefined} onSubmit={handleSubmit} fetchPageTypes={searchPageTypes} - fetchMorePageTypes={{ - hasMore: searchPageTypesOpts.data?.search.pageInfo.hasNextPage, - loading: searchPageTypesOpts.loading, - onFetchMore: loadMorePageTypes - }} + fetchMorePageTypes={fetchMorePageTypes} + assignReferencesAttributeId={ + params.action === "assign-attribute-value" && params.id + } + onAssignReferencesClick={handleAssignAttributeReferenceClick} + referencePages={searchPagesOpts.data?.search.edges.map( + edge => edge.node + )} + fetchReferencePages={searchPages} + fetchMoreReferencePages={fetchMoreReferencePages} + onCloseDialog={() => navigate(pageCreateUrl())} /> ); diff --git a/src/pages/views/PageDetails.tsx b/src/pages/views/PageDetails.tsx index 65841c5c8..2b3174c64 100644 --- a/src/pages/views/PageDetails.tsx +++ b/src/pages/views/PageDetails.tsx @@ -11,7 +11,9 @@ import { prepareAttributesInput } from "@saleor/attributes/utils/handlers"; import ActionDialog from "@saleor/components/ActionDialog"; +import { AttributeInput } from "@saleor/components/Attributes"; import { WindowTitle } from "@saleor/components/WindowTitle"; +import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config"; import { useFileUploadMutation } from "@saleor/files/mutations"; import { AttributeErrorFragment } from "@saleor/fragments/types/AttributeErrorFragment"; import { PageErrorFragment } from "@saleor/fragments/types/PageErrorFragment"; @@ -19,6 +21,7 @@ import { UploadErrorFragment } from "@saleor/fragments/types/UploadErrorFragment import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import { commonMessages } from "@saleor/intl"; +import usePageSearch from "@saleor/searches/usePageSearch"; import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler"; import { useMetadataUpdate, @@ -94,6 +97,14 @@ export const PageDetails: React.FC = ({ id, params }) => { } }); + const handleAssignAttributeReferenceClick = (attribute: AttributeInput) => + navigate( + pageUrl(id, { + action: "assign-attribute-value", + id: attribute.id + }) + ); + const handleUpdate = async (data: PageSubmitData) => { let errors: Array< AttributeErrorFragment | UploadErrorFragment | PageErrorFragment @@ -139,6 +150,20 @@ export const PageDetails: React.FC = ({ id, params }) => { variables => updatePrivateMetadata({ variables }) ); + const { + loadMore: loadMorePages, + search: searchPages, + result: searchPagesOpts + } = usePageSearch({ + variables: DEFAULT_INITIAL_SEARCH_DATA + }); + + const fetchMoreReferencePages = { + hasMore: searchPagesOpts.data?.search.pageInfo.hasNextPage, + loading: searchPagesOpts.loading, + onFetchMore: loadMorePages + }; + return ( <> pageDetails.data.page.title)} /> @@ -161,6 +186,16 @@ export const PageDetails: React.FC = ({ id, params }) => { ) } onSubmit={handleSubmit} + assignReferencesAttributeId={ + params.action === "assign-attribute-value" && params.id + } + onAssignReferencesClick={handleAssignAttributeReferenceClick} + referencePages={searchPagesOpts.data?.search.edges.map( + edge => edge.node + )} + fetchReferencePages={searchPages} + fetchMoreReferencePages={fetchMoreReferencePages} + onCloseDialog={() => navigate(pageUrl(id))} /> = ({ params }) => { settings={settings} pages={maybe(() => data.pages.edges.map(edge => edge.node))} pageInfo={pageInfo} - onAdd={() => navigate(pageCreateUrl)} + onAdd={() => navigate(pageCreateUrl())} onBack={() => navigate(configurationMenuUrl)} onNextPage={loadNextPage} onPreviousPage={loadPreviousPage} diff --git a/src/products/components/ProductCreatePage/ProductCreatePage.tsx b/src/products/components/ProductCreatePage/ProductCreatePage.tsx index a5f85f6d2..595f0a714 100644 --- a/src/products/components/ProductCreatePage/ProductCreatePage.tsx +++ b/src/products/components/ProductCreatePage/ProductCreatePage.tsx @@ -1,6 +1,8 @@ +import { mergeAttributeValues } from "@saleor/attributes/utils/data"; import { ChannelData } from "@saleor/channels/utils"; import AppHeader from "@saleor/components/AppHeader"; -import Attributes from "@saleor/components/Attributes"; +import AssignAttributeValueDialog from "@saleor/components/AssignAttributeValueDialog"; +import Attributes, { AttributeInput } from "@saleor/components/Attributes"; import AvailabilityCard from "@saleor/components/AvailabilityCard"; import CardSpacer from "@saleor/components/CardSpacer"; import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; @@ -16,10 +18,12 @@ import { ProductErrorWithAttributesFragment } from "@saleor/fragments/types/Prod import { TaxTypeFragment } from "@saleor/fragments/types/TaxTypeFragment"; import useStateFromProps from "@saleor/hooks/useStateFromProps"; import { sectionNames } from "@saleor/intl"; +import { getAttributeValuesFromReferences } from "@saleor/pages/utils/data"; import ProductVariantPrice from "@saleor/products/components/ProductVariantPrice"; import { getChoices } from "@saleor/products/utils/data"; import { SearchCategories_search_edges_node } from "@saleor/searches/types/SearchCategories"; import { SearchCollections_search_edges_node } from "@saleor/searches/types/SearchCollections"; +import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; import { SearchProductTypes_search_edges_node } from "@saleor/searches/types/SearchProductTypes"; import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses"; import React from "react"; @@ -33,7 +37,8 @@ import ProductStocks from "../ProductStocks"; import ProductTaxes from "../ProductTaxes"; import ProductCreateForm, { ProductCreateData, - ProductCreateFormData + ProductCreateFormData, + ProductCreateHandlers } from "./form"; interface ProductCreatePageProps { @@ -49,6 +54,7 @@ interface ProductCreatePageProps { fetchMoreProductTypes: FetchMoreProps; initial?: Partial; productTypes?: SearchProductTypes_search_edges_node[]; + referencePages: SearchPages_search_edges_node[]; header: string; saveButtonBarState: ConfirmButtonTransitionState; weightUnit: string; @@ -60,6 +66,11 @@ interface ProductCreatePageProps { onWarehouseConfigure: () => void; openChannelsModal: () => void; onChannelsChange: (data: ChannelData[]) => void; + assignReferencesAttributeId?: string; + onAssignReferencesClick: (attribute: AttributeInput) => void; + fetchReferencePages?: (data: string) => void; + fetchMoreReferencePages?: FetchMoreProps; + onCloseDialog: () => void; onBack?(); onSubmit?(data: ProductCreateData); } @@ -80,6 +91,7 @@ export const ProductCreatePage: React.FC = ({ header, initial, productTypes: productTypeChoiceList, + referencePages, saveButtonBarState, warehouses, taxTypes, @@ -89,7 +101,12 @@ export const ProductCreatePage: React.FC = ({ onSubmit, onChannelsChange, onWarehouseConfigure, - openChannelsModal + openChannelsModal, + assignReferencesAttributeId, + onAssignReferencesClick, + fetchReferencePages, + fetchMoreReferencePages, + onCloseDialog }: ProductCreatePageProps) => { const intl = useIntl(); @@ -115,6 +132,24 @@ export const ProductCreatePage: React.FC = ({ value: taxType.taxCode })) || []; + const canOpenAssignReferencesAttributeDialog = !!assignReferencesAttributeId; + + const handleAssignReferenceAttribute = ( + attributeValues: string[], + data: ProductCreateData, + handlers: ProductCreateHandlers + ) => { + handlers.selectAttributeReference( + assignReferencesAttributeId, + mergeAttributeValues( + assignReferencesAttributeId, + attributeValues, + data.attributes + ) + ); + onCloseDialog(); + }; + return ( = ({ categories={categories} collections={collections} productTypes={productTypeChoiceList} + referencePages={referencePages} selectedCollections={selectedCollections} setSelectedCategory={setSelectedCategory} setSelectedCollections={setSelectedCollections} @@ -168,6 +204,8 @@ export const ProductCreatePage: React.FC = ({ onChange={handlers.selectAttribute} onMultiChange={handlers.selectAttributeMultiple} onFileChange={handlers.selectAttributeFile} + onReferencesRemove={handlers.selectAttributeReference} + onReferencesAddClick={onAssignReferencesClick} /> )} @@ -283,6 +321,28 @@ export const ProductCreatePage: React.FC = ({ state={saveButtonBarState} disabled={loading || !onSubmit || formDisabled || !hasChanged} /> + {canOpenAssignReferencesAttributeDialog && ( + + handleAssignReferenceAttribute( + attributeValues, + data, + handlers + ) + } + /> + )} ); }} diff --git a/src/products/components/ProductCreatePage/form.tsx b/src/products/components/ProductCreatePage/form.tsx index 4377c96ab..682a7775f 100644 --- a/src/products/components/ProductCreatePage/form.tsx +++ b/src/products/components/ProductCreatePage/form.tsx @@ -1,4 +1,5 @@ import { OutputData } from "@editorjs/editorjs"; +import { getAttributesDisplayData } from "@saleor/attributes/utils/data"; import { ChannelData, ChannelPriceArgs } from "@saleor/channels/utils"; import { AttributeInput, @@ -16,13 +17,13 @@ import useFormset, { import useStateFromProps from "@saleor/hooks/useStateFromProps"; import { getAttributeInputFromProductType, - getAttributesDisplayData, ProductType } from "@saleor/products/utils/data"; import { createAttributeChangeHandler, createAttributeFileChangeHandler, createAttributeMultiChangeHandler, + createAttributeReferenceChangeHandler, createChannelsChangeHandler, createChannelsPriceChangeHandler, createProductTypeSelectHandler @@ -31,6 +32,7 @@ import { validateCostPrice, validatePrice } from "@saleor/products/utils/validation"; +import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; import { SearchProductTypes_search_edges_node } from "@saleor/searches/types/SearchProductTypes"; import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses"; import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler"; @@ -67,7 +69,7 @@ export interface ProductCreateData extends ProductCreateFormData { stocks: ProductStockInput[]; } -interface ProductCreateHandlers +export interface ProductCreateHandlers extends Record< | "changeMetadata" | "selectCategory" @@ -88,6 +90,7 @@ interface ProductCreateHandlers data: Omit ) => void >, + Record<"selectAttributeReference", FormsetChange>, Record<"selectAttributeFile", FormsetChange>, Record<"addStock" | "deleteStock", (id: string) => void> { changeDescription: RichTextEditorChange; @@ -117,6 +120,7 @@ export interface UseProductCreateFormOpts warehouses: SearchWarehouses_search_edges_node[]; currentChannels: ChannelData[]; productTypeChoiceList: SearchProductTypes_search_edges_node[]; + referencePages: SearchPages_search_edges_node[]; } export interface ProductCreateFormProps extends UseProductCreateFormOpts { @@ -209,6 +213,10 @@ function useProductCreateForm( attributes.data, triggerChange ); + const handleAttributeReferenceChange = createAttributeReferenceChangeHandler( + attributes.change, + triggerChange + ); const handleAttributeFileChange = createAttributeFileChangeHandler( attributes.change, attributesWithNewFileValue.data, @@ -262,7 +270,8 @@ function useProductCreateForm( ...form.data, attributes: getAttributesDisplayData( attributes.data, - attributesWithNewFileValue.data + attributesWithNewFileValue.data, + opts.referencePages ), attributesWithNewFileValue: attributesWithNewFileValue.data, description: description.current, @@ -299,6 +308,7 @@ function useProductCreateForm( selectAttribute: handleAttributeChange, selectAttributeFile: handleAttributeFileChange, selectAttributeMultiple: handleAttributeMultiChange, + selectAttributeReference: handleAttributeReferenceChange, selectCategory: handleCategorySelect, selectCollection: handleCollectionSelect, selectProductType: handleProductTypeSelect, diff --git a/src/products/components/ProductUpdatePage/ProductUpdatePage.test.tsx b/src/products/components/ProductUpdatePage/ProductUpdatePage.test.tsx index bc872118d..09913fbfb 100644 --- a/src/products/components/ProductUpdatePage/ProductUpdatePage.test.tsx +++ b/src/products/components/ProductUpdatePage/ProductUpdatePage.test.tsx @@ -42,8 +42,10 @@ const props: ProductUpdatePageProps = { hasChannelChanged: false, header: product.name, images: product.images, + onAssignReferencesClick: () => undefined, onBack: () => undefined, onChannelsChange: () => undefined, + onCloseDialog: () => undefined, onDelete: () => undefined, onImageDelete: () => undefined, onImageUpload: () => undefined, @@ -57,6 +59,7 @@ const props: ProductUpdatePageProps = { openChannelsModal: () => undefined, placeholderImage, product, + referencePages: [], saveButtonBarState: "default", selectedChannelId: "123", taxTypes, diff --git a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx index 225aa4aca..1afe7b8e8 100644 --- a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx +++ b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx @@ -1,6 +1,8 @@ import { OutputData } from "@editorjs/editorjs"; +import { mergeAttributeValues } from "@saleor/attributes/utils/data"; import { ChannelData } from "@saleor/channels/utils"; import AppHeader from "@saleor/components/AppHeader"; +import AssignAttributeValueDialog from "@saleor/components/AssignAttributeValueDialog"; import Attributes, { AttributeInput } from "@saleor/components/Attributes"; import AvailabilityCard from "@saleor/components/AvailabilityCard"; import CardSpacer from "@saleor/components/CardSpacer"; @@ -21,9 +23,11 @@ import { FormsetData } from "@saleor/hooks/useFormset"; import useStateFromProps from "@saleor/hooks/useStateFromProps"; import { sectionNames } from "@saleor/intl"; import { maybe } from "@saleor/misc"; +import { getAttributeValuesFromReferences } from "@saleor/pages/utils/data"; import ProductVariantPrice from "@saleor/products/components/ProductVariantPrice"; import { SearchCategories_search_edges_node } from "@saleor/searches/types/SearchCategories"; import { SearchCollections_search_edges_node } from "@saleor/searches/types/SearchCollections"; +import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; import { ChannelProps, FetchMoreProps, @@ -46,7 +50,10 @@ import ProductShipping from "../ProductShipping/ProductShipping"; import ProductStocks, { ProductStockInput } from "../ProductStocks"; import ProductTaxes from "../ProductTaxes"; import ProductVariants from "../ProductVariants"; -import ProductUpdateForm from "./form"; +import ProductUpdateForm, { + ProductUpdateData, + ProductUpdateHandlers +} from "./form"; export interface ProductUpdatePageProps extends ListActions, ChannelProps { defaultWeightUnit: string; @@ -69,8 +76,14 @@ export interface ProductUpdatePageProps extends ListActions, ChannelProps { saveButtonBarState: ConfirmButtonTransitionState; warehouses: WarehouseFragment[]; taxTypes: TaxTypeFragment[]; + referencePages: SearchPages_search_edges_node[]; + assignReferencesAttributeId?: string; + fetchMoreReferencePages?: FetchMoreProps; fetchCategories: (query: string) => void; fetchCollections: (query: string) => void; + fetchReferencePages?: (data: string) => void; + onAssignReferencesClick: (attribute: AttributeInput) => void; + onCloseDialog: () => void; onVariantsAdd: () => void; onVariantShow: (id: string) => () => void; onVariantReorder: ReorderAction; @@ -123,6 +136,7 @@ export const ProductUpdatePage: React.FC = ({ variants, warehouses, taxTypes, + referencePages, onBack, onDelete, onImageDelete, @@ -144,7 +158,12 @@ export const ProductUpdatePage: React.FC = ({ selectedChannelId, toggle, toggleAll, - toolbar + toolbar, + assignReferencesAttributeId, + onAssignReferencesClick, + fetchReferencePages, + fetchMoreReferencePages, + onCloseDialog }) => { const intl = useIntl(); @@ -169,6 +188,24 @@ export const ProductUpdatePage: React.FC = ({ value: taxType.taxCode })) || []; + const canOpenAssignReferencesAttributeDialog = !!assignReferencesAttributeId; + + const handleAssignReferenceAttribute = ( + attributeValues: string[], + data: ProductUpdateData, + handlers: ProductUpdateHandlers + ) => { + handlers.selectAttributeReference( + assignReferencesAttributeId, + mergeAttributeValues( + assignReferencesAttributeId, + attributeValues, + data.attributes + ) + ); + onCloseDialog(); + }; + return ( = ({ warehouses={warehouses} currentChannels={currentChannels} hasVariants={hasVariants} + referencePages={referencePages} > {({ change, @@ -227,6 +265,8 @@ export const ProductUpdatePage: React.FC = ({ onChange={handlers.selectAttribute} onMultiChange={handlers.selectAttributeMultiple} onFileChange={handlers.selectAttributeFile} + onReferencesRemove={handlers.selectAttributeReference} + onReferencesAddClick={onAssignReferencesClick} /> )} @@ -362,6 +402,28 @@ export const ProductUpdatePage: React.FC = ({ disabled || formDisabled || (!hasChanged && !hasChannelChanged) } /> + {canOpenAssignReferencesAttributeDialog && ( + + handleAssignReferenceAttribute( + attributeValues, + data, + handlers + ) + } + /> + )} )} diff --git a/src/products/components/ProductUpdatePage/form.tsx b/src/products/components/ProductUpdatePage/form.tsx index bdb7b8018..b271bf8a8 100644 --- a/src/products/components/ProductUpdatePage/form.tsx +++ b/src/products/components/ProductUpdatePage/form.tsx @@ -1,4 +1,5 @@ import { OutputData } from "@editorjs/editorjs"; +import { getAttributesDisplayData } from "@saleor/attributes/utils/data"; import { ChannelData, ChannelPriceArgs } from "@saleor/channels/utils"; import { AttributeInput } from "@saleor/components/Attributes"; import { MetadataFormData } from "@saleor/components/Metadata"; @@ -14,7 +15,6 @@ import useFormset, { import { ProductDetails_product } from "@saleor/products/types/ProductDetails"; import { getAttributeInputFromProduct, - getAttributesDisplayData, getProductUpdatePageFormData, getStockInputFromProduct } from "@saleor/products/utils/data"; @@ -22,6 +22,7 @@ import { createAttributeChangeHandler, createAttributeFileChangeHandler, createAttributeMultiChangeHandler, + createAttributeReferenceChangeHandler, createChannelsChangeHandler, createChannelsPriceChangeHandler } from "@saleor/products/utils/handlers"; @@ -29,6 +30,7 @@ import { validateCostPrice, validatePrice } from "@saleor/products/utils/validation"; +import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses"; import handleFormSubmit from "@saleor/utils/handlers/handleFormSubmit"; import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler"; @@ -85,7 +87,7 @@ export interface ProductUpdateSubmitData extends ProductUpdateFormData { removeStocks: string[]; } -interface ProductUpdateHandlers +export interface ProductUpdateHandlers extends Record< | "changeMetadata" | "selectCategory" @@ -105,6 +107,7 @@ interface ProductUpdateHandlers data: Omit ) => void >, + Record<"selectAttributeReference", FormsetChange>, Record<"selectAttributeFile", FormsetChange>, Record<"addStock" | "deleteStock", (id: string) => void> { changeDescription: RichTextEditorChange; @@ -134,6 +137,7 @@ export interface UseProductUpdateFormOpts warehouses: SearchWarehouses_search_edges_node[]; currentChannels: ChannelData[]; hasVariants: boolean; + referencePages: SearchPages_search_edges_node[]; } export interface ProductUpdateFormProps extends UseProductUpdateFormOpts { @@ -219,6 +223,10 @@ function useProductUpdateForm( attributes.data, triggerChange ); + const handleAttributeReferenceChange = createAttributeReferenceChangeHandler( + attributes.change, + triggerChange + ); const handleAttributeFileChange = createAttributeFileChangeHandler( attributes.change, attributesWithNewFileValue.data, @@ -264,7 +272,8 @@ function useProductUpdateForm( ...form.data, attributes: getAttributesDisplayData( attributes.data, - attributesWithNewFileValue.data + attributesWithNewFileValue.data, + opts.referencePages ), description: description.current, stocks: stocks.data @@ -316,6 +325,7 @@ function useProductUpdateForm( selectAttribute: handleAttributeChange, selectAttributeFile: handleAttributeFileChange, selectAttributeMultiple: handleAttributeMultiChange, + selectAttributeReference: handleAttributeReferenceChange, selectCategory: handleCategorySelect, selectCollection: handleCollectionSelect, selectTaxRate: handleTaxTypeSelect diff --git a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx index 757640156..4354d9ddd 100644 --- a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx +++ b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx @@ -1,6 +1,9 @@ +import { mergeAttributeValues } from "@saleor/attributes/utils/data"; import { ChannelPriceData } from "@saleor/channels/utils"; import AppHeader from "@saleor/components/AppHeader"; +import AssignAttributeValueDialog from "@saleor/components/AssignAttributeValueDialog"; import Attributes, { + AttributeInput, VariantAttributeScope } from "@saleor/components/Attributes"; import CardSpacer from "@saleor/components/CardSpacer"; @@ -12,8 +15,10 @@ import PageHeader from "@saleor/components/PageHeader"; import SaveButtonBar from "@saleor/components/SaveButtonBar"; import { ProductChannelListingErrorFragment } from "@saleor/fragments/types/ProductChannelListingErrorFragment"; import { ProductErrorWithAttributesFragment } from "@saleor/fragments/types/ProductErrorWithAttributesFragment"; +import { getAttributeValuesFromReferences } from "@saleor/pages/utils/data"; +import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses"; -import { ReorderAction } from "@saleor/types"; +import { FetchMoreProps, ReorderAction } from "@saleor/types"; import React from "react"; import { defineMessages, useIntl } from "react-intl"; @@ -22,7 +27,10 @@ import ProductShipping from "../ProductShipping/ProductShipping"; import ProductStocks from "../ProductStocks"; import ProductVariantNavigation from "../ProductVariantNavigation"; import ProductVariantPrice from "../ProductVariantPrice"; -import ProductVariantCreateForm, { ProductVariantCreateData } from "./form"; +import ProductVariantCreateForm, { + ProductVariantCreateData, + ProductVariantCreateHandlers +} from "./form"; const messages = defineMessages({ attributesHeader: { @@ -53,11 +61,17 @@ interface ProductVariantCreatePageProps { saveButtonBarState: ConfirmButtonTransitionState; warehouses: SearchWarehouses_search_edges_node[]; weightUnit: string; + referencePages: SearchPages_search_edges_node[]; onBack: () => void; onSubmit: (data: ProductVariantCreateData) => void; onVariantClick: (variantId: string) => void; onVariantReorder: ReorderAction; onWarehouseConfigure: () => void; + assignReferencesAttributeId?: string; + onAssignReferencesClick: (attribute: AttributeInput) => void; + fetchReferencePages?: (data: string) => void; + fetchMoreReferencePages?: FetchMoreProps; + onCloseDialog: () => void; } const ProductVariantCreatePage: React.FC = ({ @@ -70,20 +84,45 @@ const ProductVariantCreatePage: React.FC = ({ saveButtonBarState, warehouses, weightUnit, + referencePages, onBack, onSubmit, onVariantClick, onVariantReorder, - onWarehouseConfigure + onWarehouseConfigure, + assignReferencesAttributeId, + onAssignReferencesClick, + fetchReferencePages, + fetchMoreReferencePages, + onCloseDialog }) => { const intl = useIntl(); + const canOpenAssignReferencesAttributeDialog = !!assignReferencesAttributeId; + + const handleAssignReferenceAttribute = ( + attributeValues: string[], + data: ProductVariantCreateData, + handlers: ProductVariantCreateHandlers + ) => { + handlers.selectAttributeReference( + assignReferencesAttributeId, + mergeAttributeValues( + assignReferencesAttributeId, + attributeValues, + data.attributes + ) + ); + onCloseDialog(); + }; + return ( {({ change, @@ -138,6 +177,8 @@ const ProductVariantCreatePage: React.FC = ({ onChange={handlers.selectAttribute} onMultiChange={handlers.selectAttributeMultiple} onFileChange={handlers.selectAttributeFile} + onReferencesRemove={handlers.selectAttributeReference} + onReferencesAddClick={onAssignReferencesClick} /> = ({ onCancel={onBack} onSave={submit} /> + {canOpenAssignReferencesAttributeDialog && ( + + handleAssignReferenceAttribute(attributeValues, data, handlers) + } + /> + )} )} diff --git a/src/products/components/ProductVariantCreatePage/form.tsx b/src/products/components/ProductVariantCreatePage/form.tsx index 7373be6ad..c804d055b 100644 --- a/src/products/components/ProductVariantCreatePage/form.tsx +++ b/src/products/components/ProductVariantCreatePage/form.tsx @@ -1,3 +1,4 @@ +import { getAttributesDisplayData } from "@saleor/attributes/utils/data"; import { ChannelPriceData, IChannelPriceArgs } from "@saleor/channels/utils"; import { AttributeInput } from "@saleor/components/Attributes"; import { MetadataFormData } from "@saleor/components/Metadata"; @@ -7,12 +8,10 @@ import useFormset, { FormsetData } from "@saleor/hooks/useFormset"; import { ProductVariantCreateData_product } from "@saleor/products/types/ProductVariantCreateData"; -import { - getAttributesDisplayData, - getVariantAttributeInputFromProduct -} from "@saleor/products/utils/data"; +import { getVariantAttributeInputFromProduct } from "@saleor/products/utils/data"; import { createAttributeFileChangeHandler, + createAttributeReferenceChangeHandler, getChannelsInput } from "@saleor/products/utils/handlers"; import { @@ -23,6 +22,7 @@ import { validateCostPrice, validatePrice } from "@saleor/products/utils/validation"; +import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses"; import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger"; import React from "react"; @@ -44,9 +44,10 @@ export interface ProductVariantCreateData extends ProductVariantCreateFormData { export interface UseProductVariantCreateFormOpts { warehouses: SearchWarehouses_search_edges_node[]; currentChannels: ChannelPriceData[]; + referencePages: SearchPages_search_edges_node[]; } -interface ProductVariantCreateHandlers +export interface ProductVariantCreateHandlers extends Record< | "changeStock" | "selectAttribute" @@ -54,6 +55,7 @@ interface ProductVariantCreateHandlers | "changeChannels", FormsetChange >, + Record<"selectAttributeReference", FormsetChange>, Record<"selectAttributeFile", FormsetChange>, Record<"addStock" | "deleteStock", (id: string) => void> { changeMetadata: FormChange; @@ -118,6 +120,10 @@ function useProductVariantCreateForm( attributes.data, triggerChange ); + const handleAttributeReferenceChange = createAttributeReferenceChangeHandler( + attributes.change, + triggerChange + ); const handleAttributeFileChange = createAttributeFileChangeHandler( attributes.change, attributesWithNewFileValue.data, @@ -157,7 +163,8 @@ function useProductVariantCreateForm( ...form.data, attributes: getAttributesDisplayData( attributes.data, - attributesWithNewFileValue.data + attributesWithNewFileValue.data, + opts.referencePages ), attributesWithNewFileValue: attributesWithNewFileValue.data, channelListings: channels.data, @@ -178,7 +185,8 @@ function useProductVariantCreateForm( deleteStock: handleStockDelete, selectAttribute: handleAttributeChange, selectAttributeFile: handleAttributeFileChange, - selectAttributeMultiple: handleAttributeMultiChange + selectAttributeMultiple: handleAttributeMultiChange, + selectAttributeReference: handleAttributeReferenceChange }, hasChanged: changed, submit diff --git a/src/products/components/ProductVariantPage/ProductVariantPage.tsx b/src/products/components/ProductVariantPage/ProductVariantPage.tsx index 113e4bc32..960f64cf8 100644 --- a/src/products/components/ProductVariantPage/ProductVariantPage.tsx +++ b/src/products/components/ProductVariantPage/ProductVariantPage.tsx @@ -1,5 +1,7 @@ +import { mergeAttributeValues } from "@saleor/attributes/utils/data"; import { ChannelPriceData } from "@saleor/channels/utils"; import AppHeader from "@saleor/components/AppHeader"; +import AssignAttributeValueDialog from "@saleor/components/AssignAttributeValueDialog"; import Attributes, { AttributeInput, VariantAttributeScope @@ -16,8 +18,10 @@ import { ProductChannelListingErrorFragment } from "@saleor/fragments/types/Prod import { ProductErrorWithAttributesFragment } from "@saleor/fragments/types/ProductErrorWithAttributesFragment"; import { ProductVariant } from "@saleor/fragments/types/ProductVariant"; import { WarehouseFragment } from "@saleor/fragments/types/WarehouseFragment"; +import { getAttributeValuesFromReferences } from "@saleor/pages/utils/data"; import { VariantUpdate_productVariantUpdate_errors } from "@saleor/products/types/VariantUpdate"; -import { ReorderAction } from "@saleor/types"; +import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; +import { FetchMoreProps, ReorderAction } from "@saleor/types"; import React from "react"; import { defineMessages, useIntl } from "react-intl"; @@ -30,6 +34,8 @@ import ProductVariantNavigation from "../ProductVariantNavigation"; import ProductVariantPrice from "../ProductVariantPrice"; import ProductVariantSetDefault from "../ProductVariantSetDefault"; import ProductVariantUpdateForm, { + ProductVariantUpdateData, + ProductVariantUpdateHandlers, ProductVariantUpdateSubmitData } from "./form"; @@ -61,6 +67,7 @@ export interface ProductVariantPageSubmitData } interface ProductVariantPageProps { + assignReferencesAttributeId?: string; defaultVariantId?: string; defaultWeightUnit: string; errors: @@ -74,6 +81,11 @@ interface ProductVariantPageProps { saveButtonBarState: ConfirmButtonTransitionState; variant?: ProductVariant; warehouses: WarehouseFragment[]; + referencePages: SearchPages_search_edges_node[]; + fetchMoreReferencePages?: FetchMoreProps; + fetchReferencePages?: (data: string) => void; + onAssignReferencesClick: (attribute: AttributeInput) => void; + onCloseDialog: () => void; onVariantReorder: ReorderAction; onAdd(); onBack(); @@ -97,6 +109,7 @@ const ProductVariantPage: React.FC = ({ saveButtonBarState, variant, warehouses, + referencePages, onAdd, onBack, onDelete, @@ -105,7 +118,12 @@ const ProductVariantPage: React.FC = ({ onVariantClick, onVariantReorder, onSetDefaultVariant, - onWarehouseConfigure + onWarehouseConfigure, + assignReferencesAttributeId, + onAssignReferencesClick, + fetchReferencePages, + fetchMoreReferencePages, + onCloseDialog }) => { const intl = useIntl(); @@ -120,6 +138,24 @@ const ProductVariantPage: React.FC = ({ ?.filter(image => variantImages.indexOf(image.id) !== -1) .sort((prev, next) => (prev.sortOrder > next.sortOrder ? 1 : -1)); + const canOpenAssignReferencesAttributeDialog = !!assignReferencesAttributeId; + + const handleAssignReferenceAttribute = ( + attributeValues: string[], + data: ProductVariantUpdateData, + handlers: ProductVariantUpdateHandlers + ) => { + handlers.selectAttributeReference( + assignReferencesAttributeId, + mergeAttributeValues( + assignReferencesAttributeId, + attributeValues, + data.attributes + ) + ); + onCloseDialog(); + }; + return ( <> @@ -136,6 +172,7 @@ const ProductVariantPage: React.FC = ({ onSubmit={onSubmit} warehouses={warehouses} currentChannels={channels} + referencePages={referencePages} > {({ change, @@ -178,6 +215,8 @@ const ProductVariantPage: React.FC = ({ onChange={handlers.selectAttribute} onMultiChange={handlers.selectAttributeMultiple} onFileChange={handlers.selectAttributeFile} + onReferencesRemove={handlers.selectAttributeReference} + onReferencesAddClick={onAssignReferencesClick} /> = ({ onChange={handlers.selectAttribute} onMultiChange={handlers.selectAttributeMultiple} onFileChange={handlers.selectAttributeFile} + onReferencesRemove={handlers.selectAttributeReference} + onReferencesAddClick={onAssignReferencesClick} /> = ({ onDelete={onDelete} onSave={submit} /> + {canOpenAssignReferencesAttributeDialog && ( + + handleAssignReferenceAttribute( + attributeValues, + data, + handlers + ) + } + /> + )} )} diff --git a/src/products/components/ProductVariantPage/form.tsx b/src/products/components/ProductVariantPage/form.tsx index b6a9bbeac..35e7c0845 100644 --- a/src/products/components/ProductVariantPage/form.tsx +++ b/src/products/components/ProductVariantPage/form.tsx @@ -1,3 +1,4 @@ +import { getAttributesDisplayData } from "@saleor/attributes/utils/data"; import { ChannelPriceData, IChannelPriceArgs } from "@saleor/channels/utils"; import { AttributeInput } from "@saleor/components/Attributes"; import { MetadataFormData } from "@saleor/components/Metadata"; @@ -9,11 +10,11 @@ import useFormset, { } from "@saleor/hooks/useFormset"; import { getAttributeInputFromVariant, - getAttributesDisplayData, getStockInputFromVariant } from "@saleor/products/utils/data"; import { createAttributeFileChangeHandler, + createAttributeReferenceChangeHandler, getChannelsInput } from "@saleor/products/utils/handlers"; import { @@ -24,6 +25,7 @@ import { validateCostPrice, validatePrice } from "@saleor/products/utils/validation"; +import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses"; import { mapMetadataItemToInput } from "@saleor/utils/maps"; import getMetadata from "@saleor/utils/metadata/getMetadata"; @@ -57,9 +59,10 @@ export interface ProductVariantUpdateSubmitData export interface UseProductVariantUpdateFormOpts { warehouses: SearchWarehouses_search_edges_node[]; currentChannels: ChannelPriceData[]; + referencePages: SearchPages_search_edges_node[]; } -interface ProductVariantUpdateHandlers +export interface ProductVariantUpdateHandlers extends Record< | "changeStock" | "selectAttribute" @@ -67,6 +70,7 @@ interface ProductVariantUpdateHandlers | "changeChannels", FormsetChange >, + Record<"selectAttributeReference", FormsetChange>, Record<"selectAttributeFile", FormsetChange>, Record<"addStock" | "deleteStock", (id: string) => void> { changeMetadata: FormChange; @@ -133,6 +137,10 @@ function useProductVariantUpdateForm( attributes.data, triggerChange ); + const handleAttributeReferenceChange = createAttributeReferenceChangeHandler( + attributes.change, + triggerChange + ); const handleAttributeFileChange = createAttributeFileChangeHandler( attributes.change, attributesWithNewFileValue.data, @@ -186,7 +194,8 @@ function useProductVariantUpdateForm( ...form.data, attributes: getAttributesDisplayData( attributes.data, - attributesWithNewFileValue.data + attributesWithNewFileValue.data, + opts.referencePages ), channelListings: channels.data, stocks: stocks.data @@ -226,7 +235,8 @@ function useProductVariantUpdateForm( deleteStock: handleStockDelete, selectAttribute: handleAttributeChange, selectAttributeFile: handleAttributeFileChange, - selectAttributeMultiple: handleAttributeMultiChange + selectAttributeMultiple: handleAttributeMultiChange, + selectAttributeReference: handleAttributeReferenceChange }, hasChanged: changed, submit diff --git a/src/products/index.tsx b/src/products/index.tsx index 8663604c3..58134544a 100644 --- a/src/products/index.tsx +++ b/src/products/index.tsx @@ -18,6 +18,7 @@ import { productPath, ProductUrlQueryParams, productVariantAddPath, + ProductVariantAddUrlQueryParams, productVariantCreatorPath, productVariantEditPath, ProductVariantEditUrlQueryParams @@ -99,11 +100,17 @@ const ProductImage: React.FC> = ({ const ProductVariantCreate: React.FC> = ({ match -}) => ( - -); +}) => { + const qs = parseQs(location.search.substr(1)); + const params: ProductVariantAddUrlQueryParams = qs; + + return ( + + ); +}; const ProductVariantCreator: React.FC productListPath + "?" + stringifyQs(params); export const productPath = (id: string) => urlJoin(productSection + id); -export type ProductUrlDialog = "remove" | "remove-variants" | ChannelsAction; -export type ProductUrlQueryParams = BulkAction & Dialog; -export type ProductCreateUrlQueryParams = Dialog; +export type ProductUrlDialog = + | "remove" + | "remove-variants" + | "assign-attribute-value" + | ChannelsAction; +export type ProductUrlQueryParams = BulkAction & + Dialog & + SingleAction; +export type ProductCreateUrlDialog = "assign-attribute-value" | ChannelsAction; +export type ProductCreateUrlQueryParams = Dialog & + SingleAction; export const productUrl = (id: string, params?: ProductUrlQueryParams) => productPath(encodeURIComponent(id)) + "?" + stringifyQs(params); export const productVariantEditPath = (productId: string, variantId: string) => urlJoin(productSection, productId, "variant", variantId); -export type ProductVariantEditUrlDialog = "remove"; +export type ProductVariantEditUrlDialog = "remove" | "assign-attribute-value"; export type ProductVariantEditUrlQueryParams = Dialog< ProductVariantEditUrlDialog ->; +> & + SingleAction; export const productVariantEditUrl = ( productId: string, variantId: string, @@ -92,10 +102,20 @@ export const productVariantCreatorPath = (productId: string) => export const productVariantCreatorUrl = (productId: string) => productVariantCreatorPath(encodeURIComponent(productId)); +export type ProductVariantAddUrlDialog = "assign-attribute-value"; +export type ProductVariantAddUrlQueryParams = Dialog< + ProductVariantAddUrlDialog +> & + SingleAction; export const productVariantAddPath = (productId: string) => urlJoin(productSection, productId, "variant/add"); -export const productVariantAddUrl = (productId: string): string => - productVariantAddPath(encodeURIComponent(productId)); +export const productVariantAddUrl = ( + productId: string, + params?: ProductVariantAddUrlQueryParams +): string => + productVariantAddPath(encodeURIComponent(productId)) + + "?" + + stringifyQs(params); export const productImagePath = (productId: string, imageId: string) => urlJoin(productSection, productId, "image", imageId); diff --git a/src/products/utils/data.ts b/src/products/utils/data.ts index 98717ec8c..08f28e058 100644 --- a/src/products/utils/data.ts +++ b/src/products/utils/data.ts @@ -9,7 +9,7 @@ import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompl import { ProductVariant } from "@saleor/fragments/types/ProductVariant"; import { SelectedVariantAttributeFragment } from "@saleor/fragments/types/SelectedVariantAttributeFragment"; import { VariantAttributeFragment } from "@saleor/fragments/types/VariantAttributeFragment"; -import { FormsetAtomicData, FormsetData } from "@saleor/hooks/useFormset"; +import { FormsetAtomicData } from "@saleor/hooks/useFormset"; import { maybe } from "@saleor/misc"; import { ProductDetails_product, @@ -116,18 +116,22 @@ export function getAttributeInputFromSelectedAttributes( variantAttributes: SelectedVariantAttributeFragment[], variantAttributeScope: VariantAttributeScope ): AttributeInput[] { - return variantAttributes?.map(attribute => ({ - data: { - inputType: attribute.attribute.inputType, - isRequired: attribute.attribute.valueRequired, - selectedValues: attribute.values, - values: attribute.attribute.values, - variantAttributeScope - }, - id: attribute.attribute.id, - label: attribute.attribute.name, - value: [(attribute.values.length && attribute.values[0]?.slug) || null] - })); + return variantAttributes?.map(attribute => { + const value = attribute.values.length > 0 && attribute.values[0]?.slug; + + return { + data: { + inputType: attribute.attribute.inputType, + isRequired: attribute.attribute.valueRequired, + selectedValues: attribute.values, + values: attribute.attribute.values, + variantAttributeScope + }, + id: attribute.attribute.id, + label: attribute.attribute.name, + value: value ? [value] : undefined + }; + }); } export function getAttributeInputFromVariant( @@ -217,26 +221,6 @@ export function getChoices(nodes: Node[]): SingleAutocompleteChoiceType[] { ); } -export const getAttributesDisplayData = ( - attributes: AttributeInput[], - attributesWithNewFileValue: FormsetData -) => - attributes.map(attribute => { - const attributeWithNewFileValue = attributesWithNewFileValue.find( - attributeWithNewFile => attribute.id === attributeWithNewFile.id - ); - - if (attributeWithNewFileValue) { - return { - ...attribute, - value: attributeWithNewFileValue?.value?.name - ? [attributeWithNewFileValue.value.name] - : [] - }; - } - return attribute; - }); - export interface ProductUpdatePageFormData extends MetadataFormData { category: string | null; changeTaxCode: boolean; diff --git a/src/products/utils/handlers.ts b/src/products/utils/handlers.ts index a07f440d5..40e79b80c 100644 --- a/src/products/utils/handlers.ts +++ b/src/products/utils/handlers.ts @@ -124,6 +124,16 @@ export function createAttributeMultiChangeHandler( }; } +export function createAttributeReferenceChangeHandler( + changeAttributeData: FormsetChange, + triggerChange: () => void +): FormsetChange { + return (attributeId: string, values: string[]) => { + changeAttributeData(attributeId, values); + triggerChange(); + }; +} + export function createAttributeFileChangeHandler( changeAttributeData: FormsetChange, attributesWithNewFileValue: FormsetData>, diff --git a/src/products/views/ProductCreate/ProductCreate.tsx b/src/products/views/ProductCreate/ProductCreate.tsx index a8e49afd4..03150579e 100644 --- a/src/products/views/ProductCreate/ProductCreate.tsx +++ b/src/products/views/ProductCreate/ProductCreate.tsx @@ -1,6 +1,6 @@ import { useChannelsList } from "@saleor/channels/queries"; -import { ChannelsAction } from "@saleor/channels/urls"; import { ChannelData, createSortedChannelsData } from "@saleor/channels/utils"; +import { AttributeInput } from "@saleor/components/Attributes"; import ChannelsAvailabilityDialog from "@saleor/components/ChannelsAvailabilityDialog"; import { WindowTitle } from "@saleor/components/WindowTitle"; import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config"; @@ -19,12 +19,14 @@ import { import { useProductCreateMutation } from "@saleor/products/mutations"; import { productAddUrl, + ProductCreateUrlDialog, ProductCreateUrlQueryParams, productListUrl, productUrl } from "@saleor/products/urls"; import useCategorySearch from "@saleor/searches/useCategorySearch"; import useCollectionSearch from "@saleor/searches/useCollectionSearch"; +import usePageSearch from "@saleor/searches/usePageSearch"; import useProductTypeSearch from "@saleor/searches/useProductTypeSearch"; import { useTaxTypeList } from "@saleor/taxes/queries"; import { getProductErrorMessage } from "@saleor/utils/errors"; @@ -55,7 +57,7 @@ export const ProductCreateView: React.FC = ({ params }) => { ); const [openModal, closeModal] = createDialogActionHandlers< - ChannelsAction, + ProductCreateUrlDialog, ProductCreateUrlQueryParams >(navigate, params => productAddUrl(params), params); @@ -80,6 +82,13 @@ export const ProductCreateView: React.FC = ({ params }) => { } = useProductTypeSearch({ variables: DEFAULT_INITIAL_SEARCH_DATA }); + const { + loadMore: loadMorePages, + search: searchPages, + result: searchPagesOpts + } = usePageSearch({ + variables: DEFAULT_INITIAL_SEARCH_DATA + }); const warehouses = useWarehouseList({ displayLoader: true, variables: { @@ -176,6 +185,14 @@ export const ProductCreateView: React.FC = ({ params }) => { } }; + const handleAssignAttributeReferenceClick = (attribute: AttributeInput) => + navigate( + productAddUrl({ + action: "assign-attribute-value", + id: attribute.id + }) + ); + React.useEffect(() => { const productId = productCreateOpts.data?.productCreate?.product?.id; @@ -184,6 +201,34 @@ export const ProductCreateView: React.FC = ({ params }) => { } }, [productCreateComplete]); + const fetchMoreProductTypes = { + hasMore: searchProductTypesOpts.data?.search.pageInfo.hasNextPage, + loading: searchProductTypesOpts.loading, + onFetchMore: loadMoreProductTypes + }; + const fetchMoreCollections = { + hasMore: searchCollectionOpts.data?.search.pageInfo.hasNextPage, + loading: searchCollectionOpts.loading, + onFetchMore: loadMoreCollections + }; + const fetchMoreCategories = { + hasMore: searchCategoryOpts.data?.search.pageInfo.hasNextPage, + loading: searchCategoryOpts.loading, + onFetchMore: loadMoreCategories + }; + const fetchMoreReferencePages = { + hasMore: searchPagesOpts.data?.search.pageInfo.hasNextPage, + loading: searchPagesOpts.loading, + onFetchMore: loadMorePages + }; + + const loading = + uploadFileOpts.loading || + productCreateOpts.loading || + productVariantCreateOpts.loading || + updateChannelsOpts.loading || + updateVariantChannelsOpts.loading; + return ( <> = ({ params }) => { collections={(searchCollectionOpts?.data?.search?.edges || []).map( edge => edge.node )} - loading={ - uploadFileOpts.loading || - productCreateOpts.loading || - productVariantCreateOpts.loading || - updateChannelsOpts.loading || - updateVariantChannelsOpts.loading - } + loading={loading} channelsErrors={ updateVariantChannelsOpts.data?.productVariantChannelListingUpdate ?.errors @@ -245,21 +284,9 @@ export const ProductCreateView: React.FC = ({ params }) => { onSubmit={handleSubmit} onWarehouseConfigure={() => navigate(warehouseAddPath)} saveButtonBarState={productCreateOpts.status} - fetchMoreCategories={{ - hasMore: searchCategoryOpts.data?.search.pageInfo.hasNextPage, - loading: searchCategoryOpts.loading, - onFetchMore: loadMoreCategories - }} - fetchMoreCollections={{ - hasMore: searchCollectionOpts.data?.search.pageInfo.hasNextPage, - loading: searchCollectionOpts.loading, - onFetchMore: loadMoreCollections - }} - fetchMoreProductTypes={{ - hasMore: searchProductTypesOpts.data?.search.pageInfo.hasNextPage, - loading: searchProductTypesOpts.loading, - onFetchMore: loadMoreProductTypes - }} + fetchMoreCategories={fetchMoreCategories} + fetchMoreCollections={fetchMoreCollections} + fetchMoreProductTypes={fetchMoreProductTypes} warehouses={ warehouses.data?.warehouses.edges.map(edge => edge.node) || [] } @@ -267,6 +294,16 @@ export const ProductCreateView: React.FC = ({ params }) => { weightUnit={shop?.defaultWeightUnit} openChannelsModal={handleChannelsModalOpen} onChannelsChange={setCurrentChannels} + assignReferencesAttributeId={ + params.action === "assign-attribute-value" && params.id + } + onAssignReferencesClick={handleAssignAttributeReferenceClick} + referencePages={searchPagesOpts.data?.search.edges.map( + edge => edge.node + )} + fetchReferencePages={searchPages} + fetchMoreReferencePages={fetchMoreReferencePages} + onCloseDialog={() => navigate(productAddUrl())} /> ); diff --git a/src/products/views/ProductUpdate/ProductUpdate.tsx b/src/products/views/ProductUpdate/ProductUpdate.tsx index de1553425..9bca2a110 100644 --- a/src/products/views/ProductUpdate/ProductUpdate.tsx +++ b/src/products/views/ProductUpdate/ProductUpdate.tsx @@ -11,6 +11,7 @@ import { } from "@saleor/channels/utils"; import ActionDialog from "@saleor/components/ActionDialog"; import useAppChannel from "@saleor/components/AppLayout/AppChannelContext"; +import { AttributeInput } from "@saleor/components/Attributes"; import ChannelsAvailabilityDialog from "@saleor/components/ChannelsAvailabilityDialog"; import NotFoundPage from "@saleor/components/NotFoundPage"; import { WindowTitle } from "@saleor/components/WindowTitle"; @@ -38,6 +39,7 @@ import { } from "@saleor/products/mutations"; import useCategorySearch from "@saleor/searches/useCategorySearch"; import useCollectionSearch from "@saleor/searches/useCollectionSearch"; +import usePageSearch from "@saleor/searches/usePageSearch"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler"; import { @@ -97,6 +99,13 @@ export const ProductUpdate: React.FC = ({ id, params }) => { } = useCollectionSearch({ variables: DEFAULT_INITIAL_SEARCH_DATA }); + const { + loadMore: loadMorePages, + search: searchPages, + result: searchPagesOpts + } = usePageSearch({ + variables: DEFAULT_INITIAL_SEARCH_DATA + }); const warehouses = useWarehouseList({ displayLoader: true, variables: { @@ -296,6 +305,14 @@ export const ProductUpdate: React.FC = ({ id, params }) => { reorderProductVariants({ variables }) ); + const handleAssignAttributeReferenceClick = (attribute: AttributeInput) => + navigate( + productUrl(id, { + action: "assign-attribute-value", + id: attribute.id + }) + ); + const disableFormSave = uploadFileOpts.loading || createProductImageOpts.loading || @@ -338,6 +355,22 @@ export const ProductUpdate: React.FC = ({ id, params }) => { ?.errors || []) ]; + const fetchMoreCollections = { + hasMore: searchCollectionsOpts.data?.search.pageInfo.hasNextPage, + loading: searchCollectionsOpts.loading, + onFetchMore: loadMoreCollections + }; + const fetchMoreCategories = { + hasMore: searchCategoriesOpts.data?.search.pageInfo.hasNextPage, + loading: searchCategoriesOpts.loading, + onFetchMore: loadMoreCategories + }; + const fetchMoreReferencePages = { + hasMore: searchPagesOpts.data?.search.pageInfo.hasNextPage, + loading: searchPagesOpts.loading, + onFetchMore: loadMorePages + }; + return ( <> @@ -413,19 +446,21 @@ export const ProductUpdate: React.FC = ({ id, params }) => { selected={listElements.length} toggle={toggle} toggleAll={toggleAll} - fetchMoreCategories={{ - hasMore: searchCategoriesOpts?.data?.search?.pageInfo?.hasNextPage, - loading: searchCategoriesOpts.loading, - onFetchMore: loadMoreCategories - }} - fetchMoreCollections={{ - hasMore: searchCollectionsOpts?.data?.search?.pageInfo?.hasNextPage, - loading: searchCollectionsOpts.loading, - onFetchMore: loadMoreCollections - }} + fetchMoreCategories={fetchMoreCategories} + fetchMoreCollections={fetchMoreCollections} selectedChannelId={channel?.id} openChannelsModal={handleChannelsModalOpen} onChannelsChange={setCurrentChannels} + assignReferencesAttributeId={ + params.action === "assign-attribute-value" && params.id + } + onAssignReferencesClick={handleAssignAttributeReferenceClick} + referencePages={searchPagesOpts.data?.search.edges.map( + edge => edge.node + )} + fetchReferencePages={searchPages} + fetchMoreReferencePages={fetchMoreReferencePages} + onCloseDialog={() => navigate(productUrl(id))} /> = ({ variables => updatePrivateMetadata({ variables }) ); + const handleAssignAttributeReferenceClick = (attribute: AttributeInput) => + navigate( + productVariantEditUrl(productId, variantId, { + action: "assign-attribute-value", + id: attribute.id + }) + ); + + const { + loadMore: loadMorePages, + search: searchPages, + result: searchPagesOpts + } = usePageSearch({ + variables: DEFAULT_INITIAL_SEARCH_DATA + }); + + const fetchMoreReferencePages = { + hasMore: searchPagesOpts.data?.search.pageInfo.hasNextPage, + loading: searchPagesOpts.loading, + onFetchMore: loadMorePages + }; + return ( <> @@ -303,6 +328,18 @@ export const ProductVariant: React.FC = ({ navigate(productVariantEditUrl(productId, variantId)); }} onVariantReorder={handleVariantReorder} + assignReferencesAttributeId={ + params.action === "assign-attribute-value" && params.id + } + onAssignReferencesClick={handleAssignAttributeReferenceClick} + referencePages={searchPagesOpts.data?.search.edges.map( + edge => edge.node + )} + fetchReferencePages={searchPages} + fetchMoreReferencePages={fetchMoreReferencePages} + onCloseDialog={() => + navigate(productVariantEditUrl(productId, variantId)) + } /> = ({ - productId + productId, + params }) => { const navigate = useNavigator(); const notify = useNotifier(); @@ -172,6 +183,28 @@ export const ProductVariant: React.FC = ({ const handleVariantClick = (id: string) => navigate(productVariantEditUrl(productId, id)); + const handleAssignAttributeReferenceClick = (attribute: AttributeInput) => + navigate( + productVariantAddUrl(productId, { + action: "assign-attribute-value", + id: attribute.id + }) + ); + + const { + loadMore: loadMorePages, + search: searchPages, + result: searchPagesOpts + } = usePageSearch({ + variables: DEFAULT_INITIAL_SEARCH_DATA + }); + + const fetchMoreReferencePages = { + hasMore: searchPagesOpts.data?.search.pageInfo.hasNextPage, + loading: searchPagesOpts.loading, + onFetchMore: loadMorePages + }; + const disableForm = productLoading || uploadFileOpts.loading || @@ -208,6 +241,16 @@ export const ProductVariant: React.FC = ({ warehouses.data?.warehouses.edges.map(edge => edge.node) || [] } weightUnit={shop?.defaultWeightUnit} + assignReferencesAttributeId={ + params.action === "assign-attribute-value" && params.id + } + onAssignReferencesClick={handleAssignAttributeReferenceClick} + referencePages={searchPagesOpts.data?.search.edges.map( + edge => edge.node + )} + fetchReferencePages={searchPages} + fetchMoreReferencePages={fetchMoreReferencePages} + onCloseDialog={() => navigate(productVariantAddUrl(productId))} /> ); diff --git a/src/storybook/stories/pages/PageDetailsPage.tsx b/src/storybook/stories/pages/PageDetailsPage.tsx index 35f876fdc..8e70f05ab 100644 --- a/src/storybook/stories/pages/PageDetailsPage.tsx +++ b/src/storybook/stories/pages/PageDetailsPage.tsx @@ -12,10 +12,13 @@ import Decorator from "../../Decorator"; const props: PageDetailsPageProps = { errors: [], loading: false, + onAssignReferencesClick: () => undefined, onBack: () => undefined, + onCloseDialog: () => undefined, onRemove: () => undefined, onSubmit: () => undefined, page, + referencePages: [], saveButtonBarState: "default" }; diff --git a/src/storybook/stories/products/ProductCreatePage.tsx b/src/storybook/stories/products/ProductCreatePage.tsx index 3bf2c845e..ba6b9cc4b 100644 --- a/src/storybook/stories/products/ProductCreatePage.tsx +++ b/src/storybook/stories/products/ProductCreatePage.tsx @@ -45,6 +45,9 @@ storiesOf("Views / Products / Create product", module) onWarehouseConfigure={() => undefined} taxTypes={taxTypes} weightUnit="kg" + referencePages={[]} + onAssignReferencesClick={() => undefined} + onCloseDialog={() => undefined} /> )) .add("When loading", () => ( @@ -73,6 +76,9 @@ storiesOf("Views / Products / Create product", module) onWarehouseConfigure={() => undefined} taxTypes={taxTypes} weightUnit="kg" + referencePages={[]} + onAssignReferencesClick={() => undefined} + onCloseDialog={() => undefined} /> )) .add("form errors", () => ( @@ -118,5 +124,8 @@ storiesOf("Views / Products / Create product", module) onWarehouseConfigure={() => undefined} taxTypes={taxTypes} weightUnit="kg" + referencePages={[]} + onAssignReferencesClick={() => undefined} + onCloseDialog={() => undefined} /> )); diff --git a/src/storybook/stories/products/ProductUpdatePage.tsx b/src/storybook/stories/products/ProductUpdatePage.tsx index 2259779ae..da6134cb2 100644 --- a/src/storybook/stories/products/ProductUpdatePage.tsx +++ b/src/storybook/stories/products/ProductUpdatePage.tsx @@ -42,8 +42,10 @@ const props: ProductUpdatePageProps = { hasChannelChanged: false, header: product.name, images: product.images, + onAssignReferencesClick: () => undefined, onBack: () => undefined, onChannelsChange: () => undefined, + onCloseDialog: () => undefined, onDelete: () => undefined, onImageDelete: () => undefined, onImageUpload: () => undefined, @@ -57,6 +59,7 @@ const props: ProductUpdatePageProps = { openChannelsModal: () => undefined, placeholderImage, product, + referencePages: [], saveButtonBarState: "default", selectedChannelId: "123", taxTypes, diff --git a/src/storybook/stories/products/ProductVariantCreatePage.tsx b/src/storybook/stories/products/ProductVariantCreatePage.tsx index 25998faba..e75232693 100644 --- a/src/storybook/stories/products/ProductVariantCreatePage.tsx +++ b/src/storybook/stories/products/ProductVariantCreatePage.tsx @@ -35,6 +35,9 @@ storiesOf("Views / Products / Create product variant", module) saveButtonBarState="default" warehouses={warehouseList} onWarehouseConfigure={() => undefined} + referencePages={[]} + onAssignReferencesClick={() => undefined} + onCloseDialog={() => undefined} /> )) .add("with errors", () => ( @@ -72,6 +75,9 @@ storiesOf("Views / Products / Create product variant", module) saveButtonBarState="default" warehouses={warehouseList} onWarehouseConfigure={() => undefined} + referencePages={[]} + onAssignReferencesClick={() => undefined} + onCloseDialog={() => undefined} /> )) .add("when loading data", () => ( @@ -90,6 +96,9 @@ storiesOf("Views / Products / Create product variant", module) saveButtonBarState="default" warehouses={warehouseList} onWarehouseConfigure={() => undefined} + referencePages={[]} + onAssignReferencesClick={() => undefined} + onCloseDialog={() => undefined} /> )) .add("add first variant", () => ( @@ -111,6 +120,9 @@ storiesOf("Views / Products / Create product variant", module) saveButtonBarState="default" warehouses={warehouseList} onWarehouseConfigure={() => undefined} + referencePages={[]} + onAssignReferencesClick={() => undefined} + onCloseDialog={() => undefined} /> )) .add("no warehouses", () => ( @@ -129,5 +141,8 @@ storiesOf("Views / Products / Create product variant", module) saveButtonBarState="default" warehouses={[]} onWarehouseConfigure={() => undefined} + referencePages={[]} + onAssignReferencesClick={() => undefined} + onCloseDialog={() => undefined} /> )); diff --git a/src/storybook/stories/products/ProductVariantPage.tsx b/src/storybook/stories/products/ProductVariantPage.tsx index 86722a224..0b0f50086 100644 --- a/src/storybook/stories/products/ProductVariantPage.tsx +++ b/src/storybook/stories/products/ProductVariantPage.tsx @@ -33,6 +33,9 @@ storiesOf("Views / Products / Product variant details", module) saveButtonBarState="default" warehouses={warehouseList} onWarehouseConfigure={() => undefined} + referencePages={[]} + onAssignReferencesClick={() => undefined} + onCloseDialog={() => undefined} /> )) .add("when loading data", () => ( @@ -55,6 +58,9 @@ storiesOf("Views / Products / Product variant details", module) saveButtonBarState="default" warehouses={warehouseList} onWarehouseConfigure={() => undefined} + referencePages={[]} + onAssignReferencesClick={() => undefined} + onCloseDialog={() => undefined} /> )) .add("no warehouses", () => ( @@ -76,6 +82,9 @@ storiesOf("Views / Products / Product variant details", module) saveButtonBarState="default" warehouses={[]} onWarehouseConfigure={() => undefined} + referencePages={[]} + onAssignReferencesClick={() => undefined} + onCloseDialog={() => undefined} /> )) .add("attribute errors", () => ( @@ -125,5 +134,8 @@ storiesOf("Views / Products / Product variant details", module) ]} warehouses={warehouseList} onWarehouseConfigure={() => undefined} + referencePages={[]} + onAssignReferencesClick={() => undefined} + onCloseDialog={() => undefined} /> )); From 4b8c2ea5e7ca1cdd01b7079f203e7775dadb11d7 Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Wed, 13 Jan 2021 13:11:01 +0100 Subject: [PATCH 07/35] 1427, 1866, 1868 - Implement reference attribute submission (#938) * Implement reference attribute submitting * Fix updating reference attributs * Fix displaying references attribute values names --- src/attributes/fixtures.ts | 37 +++++++++++++ src/attributes/types/AttributeCreate.ts | 1 + src/attributes/types/AttributeDetails.ts | 1 + src/attributes/types/AttributeUpdate.ts | 1 + src/attributes/types/AttributeValueCreate.ts | 1 + src/attributes/types/AttributeValueDelete.ts | 1 + src/attributes/types/AttributeValueUpdate.ts | 1 + src/attributes/utils/data.ts | 22 ++++++-- src/attributes/utils/handlers.ts | 6 +++ .../views/AttributeCreate/AttributeCreate.tsx | 1 + src/components/Attributes/Attributes.tsx | 20 +++++-- src/components/Attributes/fixtures.ts | 9 ++++ .../types/AttributeDetailsFragment.ts | 1 + src/fragments/types/AttributeValueFragment.ts | 1 + src/fragments/types/PageAttributesFragment.ts | 3 ++ src/fragments/types/PageDetailsFragment.ts | 3 ++ src/fragments/types/Product.ts | 3 ++ src/fragments/types/ProductVariant.ts | 4 ++ .../types/ProductVariantAttributesFragment.ts | 3 ++ .../types/SelectedVariantAttributeFragment.ts | 2 + .../types/VariantAttributeFragment.ts | 1 + src/pages/fixtures.ts | 16 ++++++ src/pages/types/PageCreate.ts | 3 ++ src/pages/types/PageDetails.ts | 3 ++ src/pages/types/PageUpdate.ts | 3 ++ src/pages/utils/data.ts | 3 +- src/pages/utils/handlers.test.ts | 5 ++ src/productTypes/fixtures.ts | 35 +++++++++++++ src/products/fixtures.ts | 41 +++++++++++++++ .../types/CreateMultipleVariantsData.ts | 3 ++ .../types/ProductChannelListingUpdate.ts | 3 ++ src/products/types/ProductCreate.ts | 3 ++ src/products/types/ProductDetails.ts | 3 ++ src/products/types/ProductImageCreate.ts | 3 ++ src/products/types/ProductImageUpdate.ts | 3 ++ src/products/types/ProductList.ts | 1 + src/products/types/ProductUpdate.ts | 3 ++ .../ProductVariantChannelListingUpdate.ts | 4 ++ .../types/ProductVariantCreateData.ts | 2 + src/products/types/ProductVariantDetails.ts | 4 ++ src/products/types/ProductVariantReorder.ts | 3 ++ .../types/ProductVariantSetDefault.ts | 3 ++ src/products/types/SimpleProductUpdate.ts | 19 +++++++ src/products/types/VariantCreate.ts | 4 ++ src/products/types/VariantImageAssign.ts | 4 ++ src/products/types/VariantImageUnassign.ts | 4 ++ src/products/types/VariantUpdate.ts | 8 +++ src/products/utils/data.ts | 52 +++++-------------- src/products/utils/handlers.test.ts | 5 ++ src/searches/types/SearchPageTypes.ts | 1 + src/searches/types/SearchProductTypes.ts | 1 + .../__snapshots__/Stories.test.ts.snap | 24 ++++----- 52 files changed, 332 insertions(+), 59 deletions(-) diff --git a/src/attributes/fixtures.ts b/src/attributes/fixtures.ts index ff10cea3b..e0e167360 100644 --- a/src/attributes/fixtures.ts +++ b/src/attributes/fixtures.ts @@ -34,6 +34,7 @@ export const attribute: AttributeDetailsFragment = { file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI0", name: "John Doe", + reference: null, slug: "john-doe" }, { @@ -41,6 +42,7 @@ export const attribute: AttributeDetailsFragment = { file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI1", name: "Milionare Pirate", + reference: null, slug: "milionare-pirate" } ], @@ -64,6 +66,7 @@ export const attributes: Array value.reference); + } + return attribute.values.map(value => value.slug); +} + export const isFileValueUnused = ( attributesWithNewFileValue: FormsetData, existingAttribute: @@ -194,10 +207,11 @@ export const getReferenceAttributeDisplayData = ( data: { ...attribute.data, references: - referencePages && - attribute.value?.map(value => - referencePages.find(reference => reference.id === value) - ) + referencePages?.length > 0 && attribute.value?.length > 0 + ? attribute.value.map(value => + referencePages.find(reference => reference.id === value) + ) + : [] } }); diff --git a/src/attributes/utils/handlers.ts b/src/attributes/utils/handlers.ts index 496fb4758..e1d8ca82d 100644 --- a/src/attributes/utils/handlers.ts +++ b/src/attributes/utils/handlers.ts @@ -44,6 +44,12 @@ export const prepareAttributesInput = ({ id: attribute.id }; } + if (attribute.data.inputType === AttributeInputTypeEnum.REFERENCE) { + return { + id: attribute.id, + references: attribute.value + }; + } return { id: attribute.id, values: attribute.value[0] === "" ? [] : attribute.value diff --git a/src/attributes/views/AttributeCreate/AttributeCreate.tsx b/src/attributes/views/AttributeCreate/AttributeCreate.tsx index 3dc6bd039..42589f312 100644 --- a/src/attributes/views/AttributeCreate/AttributeCreate.tsx +++ b/src/attributes/views/AttributeCreate/AttributeCreate.tsx @@ -158,6 +158,7 @@ const AttributeDetails: React.FC = ({ params }) => { __typename: "AttributeValue" as "AttributeValue", file: null, id: valueIndex.toString(), + reference: null, slug: slugify(value.name).toLowerCase(), sortOrder: valueIndex, value: null, diff --git a/src/components/Attributes/Attributes.tsx b/src/components/Attributes/Attributes.tsx index 4a366ceb3..c06644f45 100644 --- a/src/components/Attributes/Attributes.tsx +++ b/src/components/Attributes/Attributes.tsx @@ -163,13 +163,25 @@ function getReferenceDisplayValue( } return attribute.value.map(attributeValue => { - const definedAttributeValue = attribute.data.references?.find( - reference => reference.id === attributeValue + const definedAttributeValue = attribute.data.values.find( + definedValue => definedValue.reference === attributeValue ); + // If value has been previously assigned, use it's data if (!!definedAttributeValue) { return { - label: definedAttributeValue.title, - value: definedAttributeValue.id + label: definedAttributeValue.name, + value: definedAttributeValue.reference + }; + } + + const definedAttributeReference = attribute.data.references?.find( + reference => reference.id === attributeValue + ); + // If value has not been yet assigned, use data of reference + if (!!definedAttributeReference) { + return { + label: definedAttributeReference.title, + value: definedAttributeReference.id }; } diff --git a/src/components/Attributes/fixtures.ts b/src/components/Attributes/fixtures.ts index ef11df402..205bdd74c 100644 --- a/src/components/Attributes/fixtures.ts +++ b/src/components/Attributes/fixtures.ts @@ -12,6 +12,7 @@ const DROPDOWN_ATTRIBUTE: AttributeInput = { file: null, id: "fdinugiffgffd", name: "Dropdown First Value", + reference: null, slug: "dropdown-first-value" }, { @@ -19,6 +20,7 @@ const DROPDOWN_ATTRIBUTE: AttributeInput = { file: null, id: "fdhfdhdihidff", name: "Dropdown Second Value", + reference: null, slug: "dropdown-second-value" } ] @@ -38,6 +40,7 @@ const MULTISELECT_ATTRIBUTE: AttributeInput = { file: null, id: "terteretregtt", name: "Multiselect First Value", + reference: null, slug: "multiselect-first-value" }, { @@ -45,6 +48,7 @@ const MULTISELECT_ATTRIBUTE: AttributeInput = { file: null, id: "tyueyryetopwr", name: "Multiselect Second Value", + reference: null, slug: "multiselect-second-value" }, { @@ -52,6 +56,7 @@ const MULTISELECT_ATTRIBUTE: AttributeInput = { file: null, id: "truiwrtweirqd", name: "Multiselect Third Value", + reference: null, slug: "multiselect-third-value" } ] @@ -75,6 +80,7 @@ const FILE_ATTRIBUTE: AttributeInput = { }, id: "gdghdgdhkkdae", name: "File First Value", + reference: null, slug: "file-first-value" } ] @@ -111,6 +117,7 @@ const REFERENCE_ATTRIBUTE: AttributeInput = { file: null, id: "vbnhgcvjhbvhj", name: "References First Value", + reference: null, slug: "references-first-value" }, { @@ -118,6 +125,7 @@ const REFERENCE_ATTRIBUTE: AttributeInput = { file: null, id: "gucngdfdfvdvd", name: "References Second Value", + reference: null, slug: "references-second-value" }, { @@ -125,6 +133,7 @@ const REFERENCE_ATTRIBUTE: AttributeInput = { file: null, id: "dfdfdsfdsfdse", name: "References Third Value", + reference: null, slug: "references-third-value" } ] diff --git a/src/fragments/types/AttributeDetailsFragment.ts b/src/fragments/types/AttributeDetailsFragment.ts index 3cf67ce3b..c978c558e 100644 --- a/src/fragments/types/AttributeDetailsFragment.ts +++ b/src/fragments/types/AttributeDetailsFragment.ts @@ -32,6 +32,7 @@ export interface AttributeDetailsFragment_values { name: string | null; slug: string | null; file: AttributeDetailsFragment_values_file | null; + reference: string | null; } export interface AttributeDetailsFragment { diff --git a/src/fragments/types/AttributeValueFragment.ts b/src/fragments/types/AttributeValueFragment.ts index e66f4af4d..450f07854 100644 --- a/src/fragments/types/AttributeValueFragment.ts +++ b/src/fragments/types/AttributeValueFragment.ts @@ -18,4 +18,5 @@ export interface AttributeValueFragment { name: string | null; slug: string | null; file: AttributeValueFragment_file | null; + reference: string | null; } diff --git a/src/fragments/types/PageAttributesFragment.ts b/src/fragments/types/PageAttributesFragment.ts index aac6ad487..693850779 100644 --- a/src/fragments/types/PageAttributesFragment.ts +++ b/src/fragments/types/PageAttributesFragment.ts @@ -20,6 +20,7 @@ export interface PageAttributesFragment_attributes_attribute_values { name: string | null; slug: string | null; file: PageAttributesFragment_attributes_attribute_values_file | null; + reference: string | null; } export interface PageAttributesFragment_attributes_attribute { @@ -44,6 +45,7 @@ export interface PageAttributesFragment_attributes_values { name: string | null; slug: string | null; file: PageAttributesFragment_attributes_values_file | null; + reference: string | null; } export interface PageAttributesFragment_attributes { @@ -64,6 +66,7 @@ export interface PageAttributesFragment_pageType_attributes_values { name: string | null; slug: string | null; file: PageAttributesFragment_pageType_attributes_values_file | null; + reference: string | null; } export interface PageAttributesFragment_pageType_attributes { diff --git a/src/fragments/types/PageDetailsFragment.ts b/src/fragments/types/PageDetailsFragment.ts index c150461ce..d05db0d3a 100644 --- a/src/fragments/types/PageDetailsFragment.ts +++ b/src/fragments/types/PageDetailsFragment.ts @@ -20,6 +20,7 @@ export interface PageDetailsFragment_attributes_attribute_values { name: string | null; slug: string | null; file: PageDetailsFragment_attributes_attribute_values_file | null; + reference: string | null; } export interface PageDetailsFragment_attributes_attribute { @@ -44,6 +45,7 @@ export interface PageDetailsFragment_attributes_values { name: string | null; slug: string | null; file: PageDetailsFragment_attributes_values_file | null; + reference: string | null; } export interface PageDetailsFragment_attributes { @@ -64,6 +66,7 @@ export interface PageDetailsFragment_pageType_attributes_values { name: string | null; slug: string | null; file: PageDetailsFragment_pageType_attributes_values_file | null; + reference: string | null; } export interface PageDetailsFragment_pageType_attributes { diff --git a/src/fragments/types/Product.ts b/src/fragments/types/Product.ts index 890c4c77a..c5b5195d7 100644 --- a/src/fragments/types/Product.ts +++ b/src/fragments/types/Product.ts @@ -20,6 +20,7 @@ export interface Product_attributes_attribute_values { name: string | null; slug: string | null; file: Product_attributes_attribute_values_file | null; + reference: string | null; } export interface Product_attributes_attribute { @@ -44,6 +45,7 @@ export interface Product_attributes_values { name: string | null; slug: string | null; file: Product_attributes_values_file | null; + reference: string | null; } export interface Product_attributes { @@ -64,6 +66,7 @@ export interface Product_productType_variantAttributes_values { name: string | null; slug: string | null; file: Product_productType_variantAttributes_values_file | null; + reference: string | null; } export interface Product_productType_variantAttributes { diff --git a/src/fragments/types/ProductVariant.ts b/src/fragments/types/ProductVariant.ts index 0139aa986..78eded821 100644 --- a/src/fragments/types/ProductVariant.ts +++ b/src/fragments/types/ProductVariant.ts @@ -32,6 +32,7 @@ export interface ProductVariant_selectionAttributes_attribute_values { name: string | null; slug: string | null; file: ProductVariant_selectionAttributes_attribute_values_file | null; + reference: string | null; } export interface ProductVariant_selectionAttributes_attribute { @@ -56,6 +57,7 @@ export interface ProductVariant_selectionAttributes_values { name: string | null; slug: string | null; file: ProductVariant_selectionAttributes_values_file | null; + reference: string | null; } export interface ProductVariant_selectionAttributes { @@ -76,6 +78,7 @@ export interface ProductVariant_nonSelectionAttributes_attribute_values { name: string | null; slug: string | null; file: ProductVariant_nonSelectionAttributes_attribute_values_file | null; + reference: string | null; } export interface ProductVariant_nonSelectionAttributes_attribute { @@ -100,6 +103,7 @@ export interface ProductVariant_nonSelectionAttributes_values { name: string | null; slug: string | null; file: ProductVariant_nonSelectionAttributes_values_file | null; + reference: string | null; } export interface ProductVariant_nonSelectionAttributes { diff --git a/src/fragments/types/ProductVariantAttributesFragment.ts b/src/fragments/types/ProductVariantAttributesFragment.ts index 0345c4cf8..71f35d484 100644 --- a/src/fragments/types/ProductVariantAttributesFragment.ts +++ b/src/fragments/types/ProductVariantAttributesFragment.ts @@ -20,6 +20,7 @@ export interface ProductVariantAttributesFragment_attributes_attribute_values { name: string | null; slug: string | null; file: ProductVariantAttributesFragment_attributes_attribute_values_file | null; + reference: string | null; } export interface ProductVariantAttributesFragment_attributes_attribute { @@ -44,6 +45,7 @@ export interface ProductVariantAttributesFragment_attributes_values { name: string | null; slug: string | null; file: ProductVariantAttributesFragment_attributes_values_file | null; + reference: string | null; } export interface ProductVariantAttributesFragment_attributes { @@ -64,6 +66,7 @@ export interface ProductVariantAttributesFragment_productType_variantAttributes_ name: string | null; slug: string | null; file: ProductVariantAttributesFragment_productType_variantAttributes_values_file | null; + reference: string | null; } export interface ProductVariantAttributesFragment_productType_variantAttributes { diff --git a/src/fragments/types/SelectedVariantAttributeFragment.ts b/src/fragments/types/SelectedVariantAttributeFragment.ts index 5b4ea1696..9b5a69592 100644 --- a/src/fragments/types/SelectedVariantAttributeFragment.ts +++ b/src/fragments/types/SelectedVariantAttributeFragment.ts @@ -20,6 +20,7 @@ export interface SelectedVariantAttributeFragment_attribute_values { name: string | null; slug: string | null; file: SelectedVariantAttributeFragment_attribute_values_file | null; + reference: string | null; } export interface SelectedVariantAttributeFragment_attribute { @@ -44,6 +45,7 @@ export interface SelectedVariantAttributeFragment_values { name: string | null; slug: string | null; file: SelectedVariantAttributeFragment_values_file | null; + reference: string | null; } export interface SelectedVariantAttributeFragment { diff --git a/src/fragments/types/VariantAttributeFragment.ts b/src/fragments/types/VariantAttributeFragment.ts index e455ede8e..bfaeecc1b 100644 --- a/src/fragments/types/VariantAttributeFragment.ts +++ b/src/fragments/types/VariantAttributeFragment.ts @@ -20,6 +20,7 @@ export interface VariantAttributeFragment_values { name: string | null; slug: string | null; file: VariantAttributeFragment_values_file | null; + reference: string | null; } export interface VariantAttributeFragment { diff --git a/src/pages/fixtures.ts b/src/pages/fixtures.ts index afb7bccab..7e1938c15 100644 --- a/src/pages/fixtures.ts +++ b/src/pages/fixtures.ts @@ -52,6 +52,7 @@ export const page: PageDetails_page = { id: "QXR0cmlidXRlVmFsdWU6ODc=", name: "Suzanne Ellison", slug: "suzanne-ellison", + reference: null, __typename: "AttributeValue", file: null }, @@ -59,6 +60,7 @@ export const page: PageDetails_page = { id: "QXR0cmlidXRlVmFsdWU6ODg=", name: "Dennis Perkins", slug: "dennis-perkins", + reference: null, __typename: "AttributeValue", file: null }, @@ -66,6 +68,7 @@ export const page: PageDetails_page = { id: "QXR0cmlidXRlVmFsdWU6ODk=", name: "Dylan Lamb", slug: "dylan-lamb", + reference: null, __typename: "AttributeValue", file: null } @@ -77,6 +80,7 @@ export const page: PageDetails_page = { id: "QXR0cmlidXRlVmFsdWU6ODk=", name: "Dylan Lamb", slug: "dylan-lamb", + reference: null, __typename: "AttributeValue", file: null } @@ -95,6 +99,7 @@ export const page: PageDetails_page = { id: "QXR0cmlidXRlVmFsdWU6OTA=", name: "Security", slug: "security", + reference: null, __typename: "AttributeValue", file: null }, @@ -102,6 +107,7 @@ export const page: PageDetails_page = { id: "QXR0cmlidXRlVmFsdWU6OTE=", name: "Support", slug: "support", + reference: null, __typename: "AttributeValue", file: null }, @@ -109,6 +115,7 @@ export const page: PageDetails_page = { id: "QXR0cmlidXRlVmFsdWU6OTI=", name: "Medical", slug: "medical", + reference: null, __typename: "AttributeValue", file: null }, @@ -116,6 +123,7 @@ export const page: PageDetails_page = { id: "QXR0cmlidXRlVmFsdWU6OTM=", name: "General", slug: "general", + reference: null, __typename: "AttributeValue", file: null } @@ -127,6 +135,7 @@ export const page: PageDetails_page = { id: "QXR0cmlidXRlVmFsdWU6OTA=", name: "Security", slug: "security", + reference: null, __typename: "AttributeValue", file: null } @@ -159,6 +168,7 @@ export const page: PageDetails_page = { id: "QXR0cmlidXRlVmFsdWU6ODc=", name: "Suzanne Ellison", slug: "suzanne-ellison", + reference: null, __typename: "AttributeValue", file: null }, @@ -166,6 +176,7 @@ export const page: PageDetails_page = { id: "QXR0cmlidXRlVmFsdWU6ODg=", name: "Dennis Perkins", slug: "dennis-perkins", + reference: null, __typename: "AttributeValue", file: null }, @@ -173,6 +184,7 @@ export const page: PageDetails_page = { id: "QXR0cmlidXRlVmFsdWU6ODk=", name: "Dylan Lamb", slug: "dylan-lamb", + reference: null, __typename: "AttributeValue", file: null } @@ -189,6 +201,7 @@ export const page: PageDetails_page = { id: "QXR0cmlidXRlVmFsdWU6OTA=", name: "Security", slug: "security", + reference: null, __typename: "AttributeValue", file: null }, @@ -196,6 +209,7 @@ export const page: PageDetails_page = { id: "QXR0cmlidXRlVmFsdWU6OTE=", name: "Support", slug: "support", + reference: null, __typename: "AttributeValue", file: null }, @@ -203,6 +217,7 @@ export const page: PageDetails_page = { id: "QXR0cmlidXRlVmFsdWU6OTI=", name: "Medical", slug: "medical", + reference: null, __typename: "AttributeValue", file: null }, @@ -210,6 +225,7 @@ export const page: PageDetails_page = { id: "QXR0cmlidXRlVmFsdWU6OTM=", name: "General", slug: "general", + reference: null, __typename: "AttributeValue", file: null } diff --git a/src/pages/types/PageCreate.ts b/src/pages/types/PageCreate.ts index 02ed59e41..bf67a2907 100644 --- a/src/pages/types/PageCreate.ts +++ b/src/pages/types/PageCreate.ts @@ -28,6 +28,7 @@ export interface PageCreate_pageCreate_page_attributes_attribute_values { name: string | null; slug: string | null; file: PageCreate_pageCreate_page_attributes_attribute_values_file | null; + reference: string | null; } export interface PageCreate_pageCreate_page_attributes_attribute { @@ -52,6 +53,7 @@ export interface PageCreate_pageCreate_page_attributes_values { name: string | null; slug: string | null; file: PageCreate_pageCreate_page_attributes_values_file | null; + reference: string | null; } export interface PageCreate_pageCreate_page_attributes { @@ -72,6 +74,7 @@ export interface PageCreate_pageCreate_page_pageType_attributes_values { name: string | null; slug: string | null; file: PageCreate_pageCreate_page_pageType_attributes_values_file | null; + reference: string | null; } export interface PageCreate_pageCreate_page_pageType_attributes { diff --git a/src/pages/types/PageDetails.ts b/src/pages/types/PageDetails.ts index 813cd49a4..a891bd408 100644 --- a/src/pages/types/PageDetails.ts +++ b/src/pages/types/PageDetails.ts @@ -20,6 +20,7 @@ export interface PageDetails_page_attributes_attribute_values { name: string | null; slug: string | null; file: PageDetails_page_attributes_attribute_values_file | null; + reference: string | null; } export interface PageDetails_page_attributes_attribute { @@ -44,6 +45,7 @@ export interface PageDetails_page_attributes_values { name: string | null; slug: string | null; file: PageDetails_page_attributes_values_file | null; + reference: string | null; } export interface PageDetails_page_attributes { @@ -64,6 +66,7 @@ export interface PageDetails_page_pageType_attributes_values { name: string | null; slug: string | null; file: PageDetails_page_pageType_attributes_values_file | null; + reference: string | null; } export interface PageDetails_page_pageType_attributes { diff --git a/src/pages/types/PageUpdate.ts b/src/pages/types/PageUpdate.ts index f3c1d5c2a..4d4cc1b26 100644 --- a/src/pages/types/PageUpdate.ts +++ b/src/pages/types/PageUpdate.ts @@ -27,6 +27,7 @@ export interface PageUpdate_pageUpdate_page_attributes_attribute_values { name: string | null; slug: string | null; file: PageUpdate_pageUpdate_page_attributes_attribute_values_file | null; + reference: string | null; } export interface PageUpdate_pageUpdate_page_attributes_attribute { @@ -51,6 +52,7 @@ export interface PageUpdate_pageUpdate_page_attributes_values { name: string | null; slug: string | null; file: PageUpdate_pageUpdate_page_attributes_values_file | null; + reference: string | null; } export interface PageUpdate_pageUpdate_page_attributes { @@ -71,6 +73,7 @@ export interface PageUpdate_pageUpdate_page_pageType_attributes_values { name: string | null; slug: string | null; file: PageUpdate_pageUpdate_page_pageType_attributes_values_file | null; + reference: string | null; } export interface PageUpdate_pageUpdate_page_pageType_attributes { diff --git a/src/pages/utils/data.ts b/src/pages/utils/data.ts index df6ecb031..db9116f38 100644 --- a/src/pages/utils/data.ts +++ b/src/pages/utils/data.ts @@ -1,3 +1,4 @@ +import { getSelectedAttributeValues } from "@saleor/attributes/utils/data"; import { AttributeInput } from "@saleor/components/Attributes"; import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; @@ -18,7 +19,7 @@ export function getAttributeInputFromPage( }, id: attribute.attribute.id, label: attribute.attribute.name, - value: attribute.values.map(value => value.slug) + value: getSelectedAttributeValues(attribute) })); } diff --git a/src/pages/utils/handlers.test.ts b/src/pages/utils/handlers.test.ts index 8b79dea92..d750a9f83 100644 --- a/src/pages/utils/handlers.test.ts +++ b/src/pages/utils/handlers.test.ts @@ -15,6 +15,7 @@ const attributes: FormsetData = [ file: null, id: "attrv-1", name: "Attribute 1 Value 1", + reference: null, slug: "attr-1-v-1" } ] @@ -33,6 +34,7 @@ const attributes: FormsetData = [ file: null, id: "attrv-2", name: "Attribute 2 Value 1", + reference: null, slug: "attr-2-v-1" }, { @@ -40,6 +42,7 @@ const attributes: FormsetData = [ file: null, id: "attrv-3", name: "Attribute 2 Value 2", + reference: null, slug: "attr-2-v-2" }, { @@ -47,6 +50,7 @@ const attributes: FormsetData = [ file: null, id: "attrv-4", name: "Attribute 2 Value 3", + reference: null, slug: "attr-2-v-3" } ] @@ -69,6 +73,7 @@ const attributes: FormsetData = [ }, id: "gdghdgdhkkdae", name: "File First Value", + reference: null, slug: "file-first-value" } ] diff --git a/src/productTypes/fixtures.ts b/src/productTypes/fixtures.ts index d6235632e..a115d1a56 100644 --- a/src/productTypes/fixtures.ts +++ b/src/productTypes/fixtures.ts @@ -26,6 +26,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI0", name: "John Doe", + reference: null, slug: "john-doe", sortOrder: 0, type: "STRING", @@ -36,6 +37,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI1", name: "Milionare Pirate", + reference: null, slug: "milionare-pirate", sortOrder: 1, type: "STRING", @@ -58,6 +60,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjE1", name: "100g", + reference: null, slug: "100g", sortOrder: 0, type: "STRING", @@ -68,6 +71,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjE2", name: "250g", + reference: null, slug: "250g", sortOrder: 1, type: "STRING", @@ -78,6 +82,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjE3", name: "500g", + reference: null, slug: "500g", sortOrder: 2, type: "STRING", @@ -88,6 +93,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjE4", name: "1kg", + reference: null, slug: "1kg", sortOrder: 3, type: "STRING", @@ -110,6 +116,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjY=", name: "Saleor", + reference: null, slug: "saleor", sortOrder: 0, type: "STRING", @@ -132,6 +139,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjIx", name: "100g", + reference: null, slug: "100g", sortOrder: 0, type: "STRING", @@ -142,6 +150,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjIy", name: "250g", + reference: null, slug: "250g", sortOrder: 1, type: "STRING", @@ -152,6 +161,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjIz", name: "500g", + reference: null, slug: "500g", sortOrder: 2, type: "STRING", @@ -174,6 +184,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjEz", name: "Arabica", + reference: null, slug: "arabica", sortOrder: 0, type: "STRING", @@ -184,6 +195,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjE0", name: "Robusta", + reference: null, slug: "robusta", sortOrder: 1, type: "STRING", @@ -206,6 +218,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjM=", name: "Round", + reference: null, slug: "round", sortOrder: 0, type: "STRING", @@ -216,6 +229,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjQ=", name: "V-Neck", + reference: null, slug: "v-neck", sortOrder: 1, type: "STRING", @@ -226,6 +240,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjU=", name: "Polo", + reference: null, slug: "polo", sortOrder: 2, type: "STRING", @@ -248,6 +263,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjE=", name: "Blue", + reference: null, slug: "blue", sortOrder: 0, type: "STRING", @@ -258,6 +274,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI=", name: "White", + reference: null, slug: "white", sortOrder: 1, type: "STRING", @@ -280,6 +297,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjMw", name: "Soft", + reference: null, slug: "soft", sortOrder: 0, type: "STRING", @@ -290,6 +308,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjMx", name: "Hard", + reference: null, slug: "hard", sortOrder: 1, type: "STRING", @@ -300,6 +319,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjMy", name: "Middle soft", + reference: null, slug: "middle-soft", sortOrder: 2, type: "STRING", @@ -310,6 +330,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjMz", name: "Middle hard", + reference: null, slug: "middle-hard", sortOrder: 3, type: "STRING", @@ -320,6 +341,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjM0", name: "Middle", + reference: null, slug: "middle", sortOrder: 4, type: "STRING", @@ -330,6 +352,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjM1", name: "Very hard", + reference: null, slug: "very-hard", sortOrder: 5, type: "STRING", @@ -352,6 +375,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjE5", name: "Sour", + reference: null, slug: "sour", sortOrder: 0, type: "STRING", @@ -362,6 +386,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjIw", name: "Sweet", + reference: null, slug: "sweet", sortOrder: 1, type: "STRING", @@ -384,6 +409,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI4", name: "English", + reference: null, slug: "english", sortOrder: 0, type: "STRING", @@ -394,6 +420,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI5", name: "Pirate", + reference: null, slug: "pirate", sortOrder: 1, type: "STRING", @@ -416,6 +443,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI2", name: "Mirumee Press", + reference: null, slug: "mirumee-press", sortOrder: 0, type: "STRING", @@ -426,6 +454,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI3", name: "Saleor Publishing", + reference: null, slug: "saleor-publishing", sortOrder: 1, type: "STRING", @@ -448,6 +477,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjc=", name: "XS", + reference: null, slug: "xs", sortOrder: 0, type: "STRING", @@ -458,6 +488,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjg=", name: "S", + reference: null, slug: "s", sortOrder: 1, type: "STRING", @@ -468,6 +499,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjk=", name: "M", + reference: null, slug: "m", sortOrder: 2, type: "STRING", @@ -478,6 +510,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjEw", name: "L", + reference: null, slug: "l", sortOrder: 3, type: "STRING", @@ -488,6 +521,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjEx", name: "XL", + reference: null, slug: "xl", sortOrder: 4, type: "STRING", @@ -498,6 +532,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjEy", name: "XXL", + reference: null, slug: "xxl", sortOrder: 5, type: "STRING", diff --git a/src/products/fixtures.ts b/src/products/fixtures.ts index 7b7499327..e12aa85f9 100644 --- a/src/products/fixtures.ts +++ b/src/products/fixtures.ts @@ -33,6 +33,7 @@ export const product: ( file: null, id: "ptav47282", name: "portals", + reference: null, slug: "portals" }, { @@ -40,6 +41,7 @@ export const product: ( file: null, id: "ptav17253", name: "Baht", + reference: null, slug: "Baht" } ] @@ -50,6 +52,7 @@ export const product: ( file: null, id: "ptav47282", name: "portals", + reference: null, slug: "portals" } ] @@ -69,6 +72,7 @@ export const product: ( file: null, id: "ptav31282", name: "payment", + reference: null, slug: "payment" }, { @@ -76,6 +80,7 @@ export const product: ( file: null, id: "ptav14907", name: "Auto Loan Account", + reference: null, slug: "Auto-Loan-Account" }, { @@ -83,6 +88,7 @@ export const product: ( file: null, id: "ptav27366", name: "Garden", + reference: null, slug: "Garden" }, { @@ -90,6 +96,7 @@ export const product: ( file: null, id: "ptav11873", name: "override", + reference: null, slug: "override" } ] @@ -100,6 +107,7 @@ export const product: ( file: null, id: "ptav14907", name: "Auto Loan Account", + reference: null, slug: "Auto-Loan-Account" } ] @@ -269,6 +277,7 @@ export const product: ( }, id: "gdghdgdhkkdae", name: "File First Value", + reference: null, slug: "file-first-value" } ] @@ -288,6 +297,7 @@ export const product: ( file: null, id: "ptvav47282", name: "Black", + reference: null, slug: "black" }, { @@ -295,6 +305,7 @@ export const product: ( file: null, id: "ptvav17253", name: "White", + reference: null, slug: "white" } ] @@ -323,6 +334,7 @@ export const product: ( }, id: "gdghdgdhkkdae", name: "File First Value", + reference: null, slug: "file-first-value" } ] @@ -340,6 +352,7 @@ export const product: ( file: null, id: "ptvav47282", name: "Black", + reference: null, slug: "black" }, { @@ -347,6 +360,7 @@ export const product: ( file: null, id: "ptvav17253", name: "White", + reference: null, slug: "white" } ] @@ -794,6 +808,7 @@ export const products = ( file: null, id: "QXR0cmlidXRlVmFsdWU6MQ==", name: "Pineapple", + reference: null, slug: "pineapple" } ] @@ -901,6 +916,7 @@ export const products = ( file: null, id: "QXR0cmlidXRlVmFsdWU6Mg==", name: "Coconut", + reference: null, slug: "coconut" } ] @@ -1008,6 +1024,7 @@ export const products = ( file: null, id: "QXR0cmlidXRlVmFsdWU6Mw==", name: "Apple", + reference: null, slug: "apple" } ] @@ -1116,6 +1133,7 @@ export const products = ( file: null, id: "QXR0cmlidXRlVmFsdWU6NDk=", name: "Orange", + reference: null, slug: "orange" } ] @@ -1223,6 +1241,7 @@ export const products = ( file: null, id: "QXR0cmlidXRlVmFsdWU6NTA=", name: "Banana", + reference: null, slug: "banana" } ] @@ -1330,6 +1349,7 @@ export const products = ( file: null, id: "QXR0cmlidXRlVmFsdWU6NTE=", name: "Bean", + reference: null, slug: "bean" } ] @@ -1437,6 +1457,7 @@ export const products = ( file: null, id: "QXR0cmlidXRlVmFsdWU6NTI=", name: "Carrot", + reference: null, slug: "carrot" } ] @@ -1544,6 +1565,7 @@ export const products = ( file: null, id: "QXR0cmlidXRlVmFsdWU6NTM=", name: "Sprouty", + reference: null, slug: "sprouty" } ] @@ -1651,6 +1673,7 @@ export const products = ( file: null, id: "QXR0cmlidXRlVmFsdWU6ODI=", name: "Cotton", + reference: null, slug: "cotton" } ] @@ -1758,6 +1781,7 @@ export const products = ( file: null, id: "QXR0cmlidXRlVmFsdWU6ODI=", name: "Cotton", + reference: null, slug: "cotton" } ] @@ -1865,6 +1889,7 @@ export const products = ( file: null, id: "QXR0cmlidXRlVmFsdWU6ODI=", name: "Cotton", + reference: null, slug: "cotton" } ] @@ -1972,6 +1997,7 @@ export const products = ( file: null, id: "QXR0cmlidXRlVmFsdWU6ODI=", name: "Cotton", + reference: null, slug: "cotton" } ] @@ -2079,6 +2105,7 @@ export const products = ( file: null, id: "QXR0cmlidXRlVmFsdWU6ODI=", name: "Cotton", + reference: null, slug: "cotton" } ] @@ -2186,6 +2213,7 @@ export const products = ( file: null, id: "QXR0cmlidXRlVmFsdWU6ODI=", name: "Cotton", + reference: null, slug: "cotton" } ] @@ -2293,6 +2321,7 @@ export const products = ( file: null, id: "QXR0cmlidXRlVmFsdWU6ODI=", name: "Cotton", + reference: null, slug: "cotton" } ] @@ -2400,6 +2429,7 @@ export const products = ( file: null, id: "QXR0cmlidXRlVmFsdWU6ODI=", name: "Cotton", + reference: null, slug: "cotton" } ] @@ -2507,6 +2537,7 @@ export const products = ( file: null, id: "QXR0cmlidXRlVmFsdWU6NzI=", name: "Cotton", + reference: null, slug: "cotton" } ] @@ -2694,6 +2725,7 @@ export const variant = (placeholderImage: string): ProductVariant => ({ }, id: "gdghdgdhkkdae", name: "File First Value", + reference: null, slug: "file-first-value" } ] @@ -2708,6 +2740,7 @@ export const variant = (placeholderImage: string): ProductVariant => ({ }, id: "gdghdgdhkkdae", name: "File First Value", + reference: null, slug: "file-first-value" } ] @@ -2923,6 +2956,7 @@ export const variant = (placeholderImage: string): ProductVariant => ({ file: null, id: "ptav47282", name: "portals", + reference: null, slug: "portals" }, { @@ -2930,6 +2964,7 @@ export const variant = (placeholderImage: string): ProductVariant => ({ file: null, id: "ptav17253", name: "Baht", + reference: null, slug: "Baht" } ] @@ -2940,6 +2975,7 @@ export const variant = (placeholderImage: string): ProductVariant => ({ file: null, id: "ptav47282", name: "portals", + reference: null, slug: "portals" } ] @@ -2959,6 +2995,7 @@ export const variant = (placeholderImage: string): ProductVariant => ({ file: null, id: "ptav31282", name: "payment", + reference: null, slug: "payment" }, { @@ -2966,6 +3003,7 @@ export const variant = (placeholderImage: string): ProductVariant => ({ file: null, id: "ptav14907", name: "Auto Loan Account", + reference: null, slug: "Auto-Loan-Account" }, { @@ -2973,6 +3011,7 @@ export const variant = (placeholderImage: string): ProductVariant => ({ file: null, id: "ptav27366", name: "Garden", + reference: null, slug: "Garden" }, { @@ -2980,6 +3019,7 @@ export const variant = (placeholderImage: string): ProductVariant => ({ file: null, id: "ptav11873", name: "override", + reference: null, slug: "override" } ] @@ -2990,6 +3030,7 @@ export const variant = (placeholderImage: string): ProductVariant => ({ file: null, id: "ptav14907", name: "Auto Loan Account", + reference: null, slug: "Auto-Loan-Account" } ] diff --git a/src/products/types/CreateMultipleVariantsData.ts b/src/products/types/CreateMultipleVariantsData.ts index 279c7b2e1..5d7621d9d 100644 --- a/src/products/types/CreateMultipleVariantsData.ts +++ b/src/products/types/CreateMultipleVariantsData.ts @@ -20,6 +20,7 @@ export interface CreateMultipleVariantsData_product_attributes_attribute_values name: string | null; slug: string | null; file: CreateMultipleVariantsData_product_attributes_attribute_values_file | null; + reference: string | null; } export interface CreateMultipleVariantsData_product_attributes_attribute { @@ -44,6 +45,7 @@ export interface CreateMultipleVariantsData_product_attributes_values { name: string | null; slug: string | null; file: CreateMultipleVariantsData_product_attributes_values_file | null; + reference: string | null; } export interface CreateMultipleVariantsData_product_attributes { @@ -64,6 +66,7 @@ export interface CreateMultipleVariantsData_product_productType_variantAttribute name: string | null; slug: string | null; file: CreateMultipleVariantsData_product_productType_variantAttributes_values_file | null; + reference: string | null; } export interface CreateMultipleVariantsData_product_productType_variantAttributes { diff --git a/src/products/types/ProductChannelListingUpdate.ts b/src/products/types/ProductChannelListingUpdate.ts index ce34926e9..c7e8495e4 100644 --- a/src/products/types/ProductChannelListingUpdate.ts +++ b/src/products/types/ProductChannelListingUpdate.ts @@ -20,6 +20,7 @@ export interface ProductChannelListingUpdate_productChannelListingUpdate_product name: string | null; slug: string | null; file: ProductChannelListingUpdate_productChannelListingUpdate_product_attributes_attribute_values_file | null; + reference: string | null; } export interface ProductChannelListingUpdate_productChannelListingUpdate_product_attributes_attribute { @@ -44,6 +45,7 @@ export interface ProductChannelListingUpdate_productChannelListingUpdate_product name: string | null; slug: string | null; file: ProductChannelListingUpdate_productChannelListingUpdate_product_attributes_values_file | null; + reference: string | null; } export interface ProductChannelListingUpdate_productChannelListingUpdate_product_attributes { @@ -64,6 +66,7 @@ export interface ProductChannelListingUpdate_productChannelListingUpdate_product name: string | null; slug: string | null; file: ProductChannelListingUpdate_productChannelListingUpdate_product_productType_variantAttributes_values_file | null; + reference: string | null; } export interface ProductChannelListingUpdate_productChannelListingUpdate_product_productType_variantAttributes { diff --git a/src/products/types/ProductCreate.ts b/src/products/types/ProductCreate.ts index a3413fd25..c44492a14 100644 --- a/src/products/types/ProductCreate.ts +++ b/src/products/types/ProductCreate.ts @@ -27,6 +27,7 @@ export interface ProductCreate_productCreate_product_attributes_attribute_values name: string | null; slug: string | null; file: ProductCreate_productCreate_product_attributes_attribute_values_file | null; + reference: string | null; } export interface ProductCreate_productCreate_product_attributes_attribute { @@ -51,6 +52,7 @@ export interface ProductCreate_productCreate_product_attributes_values { name: string | null; slug: string | null; file: ProductCreate_productCreate_product_attributes_values_file | null; + reference: string | null; } export interface ProductCreate_productCreate_product_attributes { @@ -71,6 +73,7 @@ export interface ProductCreate_productCreate_product_productType_variantAttribut name: string | null; slug: string | null; file: ProductCreate_productCreate_product_productType_variantAttributes_values_file | null; + reference: string | null; } export interface ProductCreate_productCreate_product_productType_variantAttributes { diff --git a/src/products/types/ProductDetails.ts b/src/products/types/ProductDetails.ts index c3fbd17fb..1dd8b0bcc 100644 --- a/src/products/types/ProductDetails.ts +++ b/src/products/types/ProductDetails.ts @@ -20,6 +20,7 @@ export interface ProductDetails_product_attributes_attribute_values { name: string | null; slug: string | null; file: ProductDetails_product_attributes_attribute_values_file | null; + reference: string | null; } export interface ProductDetails_product_attributes_attribute { @@ -44,6 +45,7 @@ export interface ProductDetails_product_attributes_values { name: string | null; slug: string | null; file: ProductDetails_product_attributes_values_file | null; + reference: string | null; } export interface ProductDetails_product_attributes { @@ -64,6 +66,7 @@ export interface ProductDetails_product_productType_variantAttributes_values { name: string | null; slug: string | null; file: ProductDetails_product_productType_variantAttributes_values_file | null; + reference: string | null; } export interface ProductDetails_product_productType_variantAttributes { diff --git a/src/products/types/ProductImageCreate.ts b/src/products/types/ProductImageCreate.ts index 0c6a58dc4..3ef7a7253 100644 --- a/src/products/types/ProductImageCreate.ts +++ b/src/products/types/ProductImageCreate.ts @@ -26,6 +26,7 @@ export interface ProductImageCreate_productImageCreate_product_attributes_attrib name: string | null; slug: string | null; file: ProductImageCreate_productImageCreate_product_attributes_attribute_values_file | null; + reference: string | null; } export interface ProductImageCreate_productImageCreate_product_attributes_attribute { @@ -50,6 +51,7 @@ export interface ProductImageCreate_productImageCreate_product_attributes_values name: string | null; slug: string | null; file: ProductImageCreate_productImageCreate_product_attributes_values_file | null; + reference: string | null; } export interface ProductImageCreate_productImageCreate_product_attributes { @@ -70,6 +72,7 @@ export interface ProductImageCreate_productImageCreate_product_productType_varia name: string | null; slug: string | null; file: ProductImageCreate_productImageCreate_product_productType_variantAttributes_values_file | null; + reference: string | null; } export interface ProductImageCreate_productImageCreate_product_productType_variantAttributes { diff --git a/src/products/types/ProductImageUpdate.ts b/src/products/types/ProductImageUpdate.ts index 0a0451595..c6fa243f0 100644 --- a/src/products/types/ProductImageUpdate.ts +++ b/src/products/types/ProductImageUpdate.ts @@ -26,6 +26,7 @@ export interface ProductImageUpdate_productImageUpdate_product_attributes_attrib name: string | null; slug: string | null; file: ProductImageUpdate_productImageUpdate_product_attributes_attribute_values_file | null; + reference: string | null; } export interface ProductImageUpdate_productImageUpdate_product_attributes_attribute { @@ -50,6 +51,7 @@ export interface ProductImageUpdate_productImageUpdate_product_attributes_values name: string | null; slug: string | null; file: ProductImageUpdate_productImageUpdate_product_attributes_values_file | null; + reference: string | null; } export interface ProductImageUpdate_productImageUpdate_product_attributes { @@ -70,6 +72,7 @@ export interface ProductImageUpdate_productImageUpdate_product_productType_varia name: string | null; slug: string | null; file: ProductImageUpdate_productImageUpdate_product_productType_variantAttributes_values_file | null; + reference: string | null; } export interface ProductImageUpdate_productImageUpdate_product_productType_variantAttributes { diff --git a/src/products/types/ProductList.ts b/src/products/types/ProductList.ts index 30acab46a..a7015fa3a 100644 --- a/src/products/types/ProductList.ts +++ b/src/products/types/ProductList.ts @@ -88,6 +88,7 @@ export interface ProductList_products_edges_node_attributes_values { name: string | null; slug: string | null; file: ProductList_products_edges_node_attributes_values_file | null; + reference: string | null; } export interface ProductList_products_edges_node_attributes { diff --git a/src/products/types/ProductUpdate.ts b/src/products/types/ProductUpdate.ts index 45a2ee585..75d41461e 100644 --- a/src/products/types/ProductUpdate.ts +++ b/src/products/types/ProductUpdate.ts @@ -27,6 +27,7 @@ export interface ProductUpdate_productUpdate_product_attributes_attribute_values name: string | null; slug: string | null; file: ProductUpdate_productUpdate_product_attributes_attribute_values_file | null; + reference: string | null; } export interface ProductUpdate_productUpdate_product_attributes_attribute { @@ -51,6 +52,7 @@ export interface ProductUpdate_productUpdate_product_attributes_values { name: string | null; slug: string | null; file: ProductUpdate_productUpdate_product_attributes_values_file | null; + reference: string | null; } export interface ProductUpdate_productUpdate_product_attributes { @@ -71,6 +73,7 @@ export interface ProductUpdate_productUpdate_product_productType_variantAttribut name: string | null; slug: string | null; file: ProductUpdate_productUpdate_product_productType_variantAttributes_values_file | null; + reference: string | null; } export interface ProductUpdate_productUpdate_product_productType_variantAttributes { diff --git a/src/products/types/ProductVariantChannelListingUpdate.ts b/src/products/types/ProductVariantChannelListingUpdate.ts index 1981f7721..b8c6d66bc 100644 --- a/src/products/types/ProductVariantChannelListingUpdate.ts +++ b/src/products/types/ProductVariantChannelListingUpdate.ts @@ -32,6 +32,7 @@ export interface ProductVariantChannelListingUpdate_productVariantChannelListing name: string | null; slug: string | null; file: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_selectionAttributes_attribute_values_file | null; + reference: string | null; } export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_selectionAttributes_attribute { @@ -56,6 +57,7 @@ export interface ProductVariantChannelListingUpdate_productVariantChannelListing name: string | null; slug: string | null; file: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_selectionAttributes_values_file | null; + reference: string | null; } export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_selectionAttributes { @@ -76,6 +78,7 @@ export interface ProductVariantChannelListingUpdate_productVariantChannelListing name: string | null; slug: string | null; file: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_nonSelectionAttributes_attribute_values_file | null; + reference: string | null; } export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_nonSelectionAttributes_attribute { @@ -100,6 +103,7 @@ export interface ProductVariantChannelListingUpdate_productVariantChannelListing name: string | null; slug: string | null; file: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_nonSelectionAttributes_values_file | null; + reference: string | null; } export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_nonSelectionAttributes { diff --git a/src/products/types/ProductVariantCreateData.ts b/src/products/types/ProductVariantCreateData.ts index 5967f7729..7cb5f0e4c 100644 --- a/src/products/types/ProductVariantCreateData.ts +++ b/src/products/types/ProductVariantCreateData.ts @@ -39,6 +39,7 @@ export interface ProductVariantCreateData_product_productType_selectionVariantAt name: string | null; slug: string | null; file: ProductVariantCreateData_product_productType_selectionVariantAttributes_values_file | null; + reference: string | null; } export interface ProductVariantCreateData_product_productType_selectionVariantAttributes { @@ -63,6 +64,7 @@ export interface ProductVariantCreateData_product_productType_nonSelectionVarian name: string | null; slug: string | null; file: ProductVariantCreateData_product_productType_nonSelectionVariantAttributes_values_file | null; + reference: string | null; } export interface ProductVariantCreateData_product_productType_nonSelectionVariantAttributes { diff --git a/src/products/types/ProductVariantDetails.ts b/src/products/types/ProductVariantDetails.ts index a54c3baea..a7a4ebc9d 100644 --- a/src/products/types/ProductVariantDetails.ts +++ b/src/products/types/ProductVariantDetails.ts @@ -32,6 +32,7 @@ export interface ProductVariantDetails_productVariant_selectionAttributes_attrib name: string | null; slug: string | null; file: ProductVariantDetails_productVariant_selectionAttributes_attribute_values_file | null; + reference: string | null; } export interface ProductVariantDetails_productVariant_selectionAttributes_attribute { @@ -56,6 +57,7 @@ export interface ProductVariantDetails_productVariant_selectionAttributes_values name: string | null; slug: string | null; file: ProductVariantDetails_productVariant_selectionAttributes_values_file | null; + reference: string | null; } export interface ProductVariantDetails_productVariant_selectionAttributes { @@ -76,6 +78,7 @@ export interface ProductVariantDetails_productVariant_nonSelectionAttributes_att name: string | null; slug: string | null; file: ProductVariantDetails_productVariant_nonSelectionAttributes_attribute_values_file | null; + reference: string | null; } export interface ProductVariantDetails_productVariant_nonSelectionAttributes_attribute { @@ -100,6 +103,7 @@ export interface ProductVariantDetails_productVariant_nonSelectionAttributes_val name: string | null; slug: string | null; file: ProductVariantDetails_productVariant_nonSelectionAttributes_values_file | null; + reference: string | null; } export interface ProductVariantDetails_productVariant_nonSelectionAttributes { diff --git a/src/products/types/ProductVariantReorder.ts b/src/products/types/ProductVariantReorder.ts index 53894d18a..652d1a2c8 100644 --- a/src/products/types/ProductVariantReorder.ts +++ b/src/products/types/ProductVariantReorder.ts @@ -26,6 +26,7 @@ export interface ProductVariantReorder_productVariantReorder_product_attributes_ name: string | null; slug: string | null; file: ProductVariantReorder_productVariantReorder_product_attributes_attribute_values_file | null; + reference: string | null; } export interface ProductVariantReorder_productVariantReorder_product_attributes_attribute { @@ -50,6 +51,7 @@ export interface ProductVariantReorder_productVariantReorder_product_attributes_ name: string | null; slug: string | null; file: ProductVariantReorder_productVariantReorder_product_attributes_values_file | null; + reference: string | null; } export interface ProductVariantReorder_productVariantReorder_product_attributes { @@ -70,6 +72,7 @@ export interface ProductVariantReorder_productVariantReorder_product_productType name: string | null; slug: string | null; file: ProductVariantReorder_productVariantReorder_product_productType_variantAttributes_values_file | null; + reference: string | null; } export interface ProductVariantReorder_productVariantReorder_product_productType_variantAttributes { diff --git a/src/products/types/ProductVariantSetDefault.ts b/src/products/types/ProductVariantSetDefault.ts index 732258c5c..8a9c2ecb3 100644 --- a/src/products/types/ProductVariantSetDefault.ts +++ b/src/products/types/ProductVariantSetDefault.ts @@ -26,6 +26,7 @@ export interface ProductVariantSetDefault_productVariantSetDefault_product_attri name: string | null; slug: string | null; file: ProductVariantSetDefault_productVariantSetDefault_product_attributes_attribute_values_file | null; + reference: string | null; } export interface ProductVariantSetDefault_productVariantSetDefault_product_attributes_attribute { @@ -50,6 +51,7 @@ export interface ProductVariantSetDefault_productVariantSetDefault_product_attri name: string | null; slug: string | null; file: ProductVariantSetDefault_productVariantSetDefault_product_attributes_values_file | null; + reference: string | null; } export interface ProductVariantSetDefault_productVariantSetDefault_product_attributes { @@ -70,6 +72,7 @@ export interface ProductVariantSetDefault_productVariantSetDefault_product_produ name: string | null; slug: string | null; file: ProductVariantSetDefault_productVariantSetDefault_product_productType_variantAttributes_values_file | null; + reference: string | null; } export interface ProductVariantSetDefault_productVariantSetDefault_product_productType_variantAttributes { diff --git a/src/products/types/SimpleProductUpdate.ts b/src/products/types/SimpleProductUpdate.ts index 714bc782a..9be3b83bf 100644 --- a/src/products/types/SimpleProductUpdate.ts +++ b/src/products/types/SimpleProductUpdate.ts @@ -27,6 +27,7 @@ export interface SimpleProductUpdate_productUpdate_product_attributes_attribute_ name: string | null; slug: string | null; file: SimpleProductUpdate_productUpdate_product_attributes_attribute_values_file | null; + reference: string | null; } export interface SimpleProductUpdate_productUpdate_product_attributes_attribute { @@ -51,6 +52,7 @@ export interface SimpleProductUpdate_productUpdate_product_attributes_values { name: string | null; slug: string | null; file: SimpleProductUpdate_productUpdate_product_attributes_values_file | null; + reference: string | null; } export interface SimpleProductUpdate_productUpdate_product_attributes { @@ -71,6 +73,7 @@ export interface SimpleProductUpdate_productUpdate_product_productType_variantAt name: string | null; slug: string | null; file: SimpleProductUpdate_productUpdate_product_productType_variantAttributes_values_file | null; + reference: string | null; } export interface SimpleProductUpdate_productUpdate_product_productType_variantAttributes { @@ -308,6 +311,7 @@ export interface SimpleProductUpdate_productVariantUpdate_productVariant_selecti name: string | null; slug: string | null; file: SimpleProductUpdate_productVariantUpdate_productVariant_selectionAttributes_attribute_values_file | null; + reference: string | null; } export interface SimpleProductUpdate_productVariantUpdate_productVariant_selectionAttributes_attribute { @@ -332,6 +336,7 @@ export interface SimpleProductUpdate_productVariantUpdate_productVariant_selecti name: string | null; slug: string | null; file: SimpleProductUpdate_productVariantUpdate_productVariant_selectionAttributes_values_file | null; + reference: string | null; } export interface SimpleProductUpdate_productVariantUpdate_productVariant_selectionAttributes { @@ -352,6 +357,7 @@ export interface SimpleProductUpdate_productVariantUpdate_productVariant_nonSele name: string | null; slug: string | null; file: SimpleProductUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_attribute_values_file | null; + reference: string | null; } export interface SimpleProductUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_attribute { @@ -376,6 +382,7 @@ export interface SimpleProductUpdate_productVariantUpdate_productVariant_nonSele name: string | null; slug: string | null; file: SimpleProductUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_values_file | null; + reference: string | null; } export interface SimpleProductUpdate_productVariantUpdate_productVariant_nonSelectionAttributes { @@ -579,6 +586,7 @@ export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_s name: string | null; slug: string | null; file: SimpleProductUpdate_productVariantStocksCreate_productVariant_selectionAttributes_attribute_values_file | null; + reference: string | null; } export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_selectionAttributes_attribute { @@ -603,6 +611,7 @@ export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_s name: string | null; slug: string | null; file: SimpleProductUpdate_productVariantStocksCreate_productVariant_selectionAttributes_values_file | null; + reference: string | null; } export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_selectionAttributes { @@ -623,6 +632,7 @@ export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_n name: string | null; slug: string | null; file: SimpleProductUpdate_productVariantStocksCreate_productVariant_nonSelectionAttributes_attribute_values_file | null; + reference: string | null; } export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_nonSelectionAttributes_attribute { @@ -647,6 +657,7 @@ export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_n name: string | null; slug: string | null; file: SimpleProductUpdate_productVariantStocksCreate_productVariant_nonSelectionAttributes_values_file | null; + reference: string | null; } export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_nonSelectionAttributes { @@ -849,6 +860,7 @@ export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_s name: string | null; slug: string | null; file: SimpleProductUpdate_productVariantStocksDelete_productVariant_selectionAttributes_attribute_values_file | null; + reference: string | null; } export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_selectionAttributes_attribute { @@ -873,6 +885,7 @@ export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_s name: string | null; slug: string | null; file: SimpleProductUpdate_productVariantStocksDelete_productVariant_selectionAttributes_values_file | null; + reference: string | null; } export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_selectionAttributes { @@ -893,6 +906,7 @@ export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_n name: string | null; slug: string | null; file: SimpleProductUpdate_productVariantStocksDelete_productVariant_nonSelectionAttributes_attribute_values_file | null; + reference: string | null; } export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_nonSelectionAttributes_attribute { @@ -917,6 +931,7 @@ export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_n name: string | null; slug: string | null; file: SimpleProductUpdate_productVariantStocksDelete_productVariant_nonSelectionAttributes_values_file | null; + reference: string | null; } export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_nonSelectionAttributes { @@ -1120,6 +1135,7 @@ export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_s name: string | null; slug: string | null; file: SimpleProductUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_attribute_values_file | null; + reference: string | null; } export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_attribute { @@ -1144,6 +1160,7 @@ export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_s name: string | null; slug: string | null; file: SimpleProductUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_values_file | null; + reference: string | null; } export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_selectionAttributes { @@ -1164,6 +1181,7 @@ export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_n name: string | null; slug: string | null; file: SimpleProductUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_attribute_values_file | null; + reference: string | null; } export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_attribute { @@ -1188,6 +1206,7 @@ export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_n name: string | null; slug: string | null; file: SimpleProductUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_values_file | null; + reference: string | null; } export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes { diff --git a/src/products/types/VariantCreate.ts b/src/products/types/VariantCreate.ts index 7bda40386..f31d5b352 100644 --- a/src/products/types/VariantCreate.ts +++ b/src/products/types/VariantCreate.ts @@ -39,6 +39,7 @@ export interface VariantCreate_productVariantCreate_productVariant_selectionAttr name: string | null; slug: string | null; file: VariantCreate_productVariantCreate_productVariant_selectionAttributes_attribute_values_file | null; + reference: string | null; } export interface VariantCreate_productVariantCreate_productVariant_selectionAttributes_attribute { @@ -63,6 +64,7 @@ export interface VariantCreate_productVariantCreate_productVariant_selectionAttr name: string | null; slug: string | null; file: VariantCreate_productVariantCreate_productVariant_selectionAttributes_values_file | null; + reference: string | null; } export interface VariantCreate_productVariantCreate_productVariant_selectionAttributes { @@ -83,6 +85,7 @@ export interface VariantCreate_productVariantCreate_productVariant_nonSelectionA name: string | null; slug: string | null; file: VariantCreate_productVariantCreate_productVariant_nonSelectionAttributes_attribute_values_file | null; + reference: string | null; } export interface VariantCreate_productVariantCreate_productVariant_nonSelectionAttributes_attribute { @@ -107,6 +110,7 @@ export interface VariantCreate_productVariantCreate_productVariant_nonSelectionA name: string | null; slug: string | null; file: VariantCreate_productVariantCreate_productVariant_nonSelectionAttributes_values_file | null; + reference: string | null; } export interface VariantCreate_productVariantCreate_productVariant_nonSelectionAttributes { diff --git a/src/products/types/VariantImageAssign.ts b/src/products/types/VariantImageAssign.ts index 1ae3fce63..c6058315f 100644 --- a/src/products/types/VariantImageAssign.ts +++ b/src/products/types/VariantImageAssign.ts @@ -38,6 +38,7 @@ export interface VariantImageAssign_variantImageAssign_productVariant_selectionA name: string | null; slug: string | null; file: VariantImageAssign_variantImageAssign_productVariant_selectionAttributes_attribute_values_file | null; + reference: string | null; } export interface VariantImageAssign_variantImageAssign_productVariant_selectionAttributes_attribute { @@ -62,6 +63,7 @@ export interface VariantImageAssign_variantImageAssign_productVariant_selectionA name: string | null; slug: string | null; file: VariantImageAssign_variantImageAssign_productVariant_selectionAttributes_values_file | null; + reference: string | null; } export interface VariantImageAssign_variantImageAssign_productVariant_selectionAttributes { @@ -82,6 +84,7 @@ export interface VariantImageAssign_variantImageAssign_productVariant_nonSelecti name: string | null; slug: string | null; file: VariantImageAssign_variantImageAssign_productVariant_nonSelectionAttributes_attribute_values_file | null; + reference: string | null; } export interface VariantImageAssign_variantImageAssign_productVariant_nonSelectionAttributes_attribute { @@ -106,6 +109,7 @@ export interface VariantImageAssign_variantImageAssign_productVariant_nonSelecti name: string | null; slug: string | null; file: VariantImageAssign_variantImageAssign_productVariant_nonSelectionAttributes_values_file | null; + reference: string | null; } export interface VariantImageAssign_variantImageAssign_productVariant_nonSelectionAttributes { diff --git a/src/products/types/VariantImageUnassign.ts b/src/products/types/VariantImageUnassign.ts index 12b186416..be0df6e40 100644 --- a/src/products/types/VariantImageUnassign.ts +++ b/src/products/types/VariantImageUnassign.ts @@ -38,6 +38,7 @@ export interface VariantImageUnassign_variantImageUnassign_productVariant_select name: string | null; slug: string | null; file: VariantImageUnassign_variantImageUnassign_productVariant_selectionAttributes_attribute_values_file | null; + reference: string | null; } export interface VariantImageUnassign_variantImageUnassign_productVariant_selectionAttributes_attribute { @@ -62,6 +63,7 @@ export interface VariantImageUnassign_variantImageUnassign_productVariant_select name: string | null; slug: string | null; file: VariantImageUnassign_variantImageUnassign_productVariant_selectionAttributes_values_file | null; + reference: string | null; } export interface VariantImageUnassign_variantImageUnassign_productVariant_selectionAttributes { @@ -82,6 +84,7 @@ export interface VariantImageUnassign_variantImageUnassign_productVariant_nonSel name: string | null; slug: string | null; file: VariantImageUnassign_variantImageUnassign_productVariant_nonSelectionAttributes_attribute_values_file | null; + reference: string | null; } export interface VariantImageUnassign_variantImageUnassign_productVariant_nonSelectionAttributes_attribute { @@ -106,6 +109,7 @@ export interface VariantImageUnassign_variantImageUnassign_productVariant_nonSel name: string | null; slug: string | null; file: VariantImageUnassign_variantImageUnassign_productVariant_nonSelectionAttributes_values_file | null; + reference: string | null; } export interface VariantImageUnassign_variantImageUnassign_productVariant_nonSelectionAttributes { diff --git a/src/products/types/VariantUpdate.ts b/src/products/types/VariantUpdate.ts index 2696d52e3..519334bc8 100644 --- a/src/products/types/VariantUpdate.ts +++ b/src/products/types/VariantUpdate.ts @@ -39,6 +39,7 @@ export interface VariantUpdate_productVariantUpdate_productVariant_selectionAttr name: string | null; slug: string | null; file: VariantUpdate_productVariantUpdate_productVariant_selectionAttributes_attribute_values_file | null; + reference: string | null; } export interface VariantUpdate_productVariantUpdate_productVariant_selectionAttributes_attribute { @@ -63,6 +64,7 @@ export interface VariantUpdate_productVariantUpdate_productVariant_selectionAttr name: string | null; slug: string | null; file: VariantUpdate_productVariantUpdate_productVariant_selectionAttributes_values_file | null; + reference: string | null; } export interface VariantUpdate_productVariantUpdate_productVariant_selectionAttributes { @@ -83,6 +85,7 @@ export interface VariantUpdate_productVariantUpdate_productVariant_nonSelectionA name: string | null; slug: string | null; file: VariantUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_attribute_values_file | null; + reference: string | null; } export interface VariantUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_attribute { @@ -107,6 +110,7 @@ export interface VariantUpdate_productVariantUpdate_productVariant_nonSelectionA name: string | null; slug: string | null; file: VariantUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_values_file | null; + reference: string | null; } export interface VariantUpdate_productVariantUpdate_productVariant_nonSelectionAttributes { @@ -310,6 +314,7 @@ export interface VariantUpdate_productVariantStocksUpdate_productVariant_selecti name: string | null; slug: string | null; file: VariantUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_attribute_values_file | null; + reference: string | null; } export interface VariantUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_attribute { @@ -334,6 +339,7 @@ export interface VariantUpdate_productVariantStocksUpdate_productVariant_selecti name: string | null; slug: string | null; file: VariantUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_values_file | null; + reference: string | null; } export interface VariantUpdate_productVariantStocksUpdate_productVariant_selectionAttributes { @@ -354,6 +360,7 @@ export interface VariantUpdate_productVariantStocksUpdate_productVariant_nonSele name: string | null; slug: string | null; file: VariantUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_attribute_values_file | null; + reference: string | null; } export interface VariantUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_attribute { @@ -378,6 +385,7 @@ export interface VariantUpdate_productVariantStocksUpdate_productVariant_nonSele name: string | null; slug: string | null; file: VariantUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_values_file | null; + reference: string | null; } export interface VariantUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes { diff --git a/src/products/utils/data.ts b/src/products/utils/data.ts index 08f28e058..8ee4a6d6c 100644 --- a/src/products/utils/data.ts +++ b/src/products/utils/data.ts @@ -1,10 +1,10 @@ +import { getSelectedAttributeValues } from "@saleor/attributes/utils/data"; import { ChannelData } from "@saleor/channels/utils"; import { AttributeInput, VariantAttributeScope } from "@saleor/components/Attributes"; import { MetadataFormData } from "@saleor/components/Metadata/types"; -import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField"; import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; import { ProductVariant } from "@saleor/fragments/types/ProductVariant"; import { SelectedVariantAttributeFragment } from "@saleor/fragments/types/SelectedVariantAttributeFragment"; @@ -54,27 +54,7 @@ export function getAttributeInputFromProduct( }, id: attribute.attribute.id, label: attribute.attribute.name, - value: attribute.values.map(value => value.slug) - })), - [] - ); -} - -export interface ProductAttributeValueChoices { - id: string; - values: MultiAutocompleteChoiceType[]; -} -export function getSelectedAttributesFromProduct( - product: ProductDetails_product -): ProductAttributeValueChoices[] { - return maybe( - () => - product.attributes.map(attribute => ({ - id: attribute.attribute.id, - values: attribute.values.map(value => ({ - label: value.name, - value: value.slug - })) + value: getSelectedAttributeValues(attribute) })), [] ); @@ -116,22 +96,18 @@ export function getAttributeInputFromSelectedAttributes( variantAttributes: SelectedVariantAttributeFragment[], variantAttributeScope: VariantAttributeScope ): AttributeInput[] { - return variantAttributes?.map(attribute => { - const value = attribute.values.length > 0 && attribute.values[0]?.slug; - - return { - data: { - inputType: attribute.attribute.inputType, - isRequired: attribute.attribute.valueRequired, - selectedValues: attribute.values, - values: attribute.attribute.values, - variantAttributeScope - }, - id: attribute.attribute.id, - label: attribute.attribute.name, - value: value ? [value] : undefined - }; - }); + return variantAttributes?.map(attribute => ({ + data: { + inputType: attribute.attribute.inputType, + isRequired: attribute.attribute.valueRequired, + selectedValues: attribute.values, + values: attribute.attribute.values, + variantAttributeScope + }, + id: attribute.attribute.id, + label: attribute.attribute.name, + value: getSelectedAttributeValues(attribute) + })); } export function getAttributeInputFromVariant( diff --git a/src/products/utils/handlers.test.ts b/src/products/utils/handlers.test.ts index c4309f937..c01a29da0 100644 --- a/src/products/utils/handlers.test.ts +++ b/src/products/utils/handlers.test.ts @@ -15,6 +15,7 @@ const attributes: FormsetData = [ file: null, id: "attrv-1", name: "Attribute 1 Value 1", + reference: null, slug: "attr-1-v-1" } ] @@ -33,6 +34,7 @@ const attributes: FormsetData = [ file: null, id: "attrv-2", name: "Attribute 2 Value 1", + reference: null, slug: "attr-2-v-1" }, { @@ -40,6 +42,7 @@ const attributes: FormsetData = [ file: null, id: "attrv-3", name: "Attribute 2 Value 2", + reference: null, slug: "attr-2-v-2" }, { @@ -47,6 +50,7 @@ const attributes: FormsetData = [ file: null, id: "attrv-4", name: "Attribute 2 Value 3", + reference: null, slug: "attr-2-v-3" } ] @@ -69,6 +73,7 @@ const attributes: FormsetData = [ }, id: "attrv-5", name: "File First Value", + reference: null, slug: "file-first-value" } ] diff --git a/src/searches/types/SearchPageTypes.ts b/src/searches/types/SearchPageTypes.ts index a2720f143..9fc09393d 100644 --- a/src/searches/types/SearchPageTypes.ts +++ b/src/searches/types/SearchPageTypes.ts @@ -20,6 +20,7 @@ export interface SearchPageTypes_search_edges_node_attributes_values { name: string | null; slug: string | null; file: SearchPageTypes_search_edges_node_attributes_values_file | null; + reference: string | null; } export interface SearchPageTypes_search_edges_node_attributes { diff --git a/src/searches/types/SearchProductTypes.ts b/src/searches/types/SearchProductTypes.ts index 2296ac29e..91ef9d89a 100644 --- a/src/searches/types/SearchProductTypes.ts +++ b/src/searches/types/SearchProductTypes.ts @@ -20,6 +20,7 @@ export interface SearchProductTypes_search_edges_node_productAttributes_values { name: string | null; slug: string | null; file: SearchProductTypes_search_edges_node_productAttributes_values_file | null; + reference: string | null; } export interface SearchProductTypes_search_edges_node_productAttributes { diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index 04b6cbf60..f1661c266 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -34716,7 +34716,7 @@ exports[`Storyshots Views / Attributes / Attribute list default 1`] = ` class="MuiTableRow-root-id AttributeList-link-id MuiTableRow-hover-id" data-test="id" data-test-id="UHJvZHVjdEF0dHJpYnV0ZTo5" - data-test-values="[{\\"__typename\\":\\"AttributeValue\\",\\"file\\":null,\\"id\\":\\"UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI0\\",\\"name\\":\\"John Doe\\",\\"slug\\":\\"john-doe\\",\\"sortOrder\\":0,\\"value\\":\\"\\"},{\\"__typename\\":\\"AttributeValue\\",\\"file\\":null,\\"id\\":\\"UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI1\\",\\"name\\":\\"Milionare Pirate\\",\\"slug\\":\\"milionare-pirate\\",\\"sortOrder\\":1,\\"value\\":\\"\\"}]" + data-test-values="[{\\"__typename\\":\\"AttributeValue\\",\\"file\\":null,\\"id\\":\\"UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI0\\",\\"name\\":\\"John Doe\\",\\"reference\\":null,\\"slug\\":\\"john-doe\\",\\"sortOrder\\":0,\\"value\\":\\"\\"},{\\"__typename\\":\\"AttributeValue\\",\\"file\\":null,\\"id\\":\\"UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI1\\",\\"name\\":\\"Milionare Pirate\\",\\"reference\\":null,\\"slug\\":\\"milionare-pirate\\",\\"sortOrder\\":1,\\"value\\":\\"\\"}]" > Date: Mon, 18 Jan 2021 16:37:38 +0100 Subject: [PATCH 08/35] 1863 - Support reference attribute reordering (#946) * Support reference attribute reordering * Update attribute handlers tests --- .../utils/handlers.test.ts | 3 +- src/attributes/utils/handlers.ts | 106 ++++- src/components/Attributes/Attributes.tsx | 20 +- .../SortableChipsField.stories.tsx | 9 +- .../SortableChipsField/SortableChipsField.tsx | 20 +- .../PageDetailsPage/PageDetailsPage.tsx | 1 + src/pages/components/PageDetailsPage/form.tsx | 21 +- src/pages/utils/handlers.ts | 34 +- .../ProductCreatePage/ProductCreatePage.tsx | 1 + .../components/ProductCreatePage/form.tsx | 19 +- .../ProductUpdatePage/ProductUpdatePage.tsx | 1 + .../components/ProductUpdatePage/form.tsx | 19 +- .../ProductVariantCreatePage.tsx | 4 + .../ProductVariantCreatePage/form.tsx | 25 +- .../ProductVariantPage/ProductVariantPage.tsx | 2 + .../components/ProductVariantPage/form.tsx | 25 +- src/products/utils/handlers.test.ts | 142 ------- src/products/utils/handlers.ts | 77 +--- .../__snapshots__/Stories.test.ts.snap | 383 ++++++++++++++++++ 19 files changed, 615 insertions(+), 297 deletions(-) rename src/{pages => attributes}/utils/handlers.test.ts (97%) delete mode 100644 src/products/utils/handlers.test.ts diff --git a/src/pages/utils/handlers.test.ts b/src/attributes/utils/handlers.test.ts similarity index 97% rename from src/pages/utils/handlers.test.ts rename to src/attributes/utils/handlers.test.ts index d750a9f83..b85684fa8 100644 --- a/src/pages/utils/handlers.test.ts +++ b/src/attributes/utils/handlers.test.ts @@ -1,9 +1,8 @@ +import { createAttributeMultiChangeHandler } from "@saleor/attributes/utils/handlers"; import { AttributeInputData } from "@saleor/components/Attributes"; import { FormsetData } from "@saleor/hooks/useFormset"; import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; -import { createAttributeMultiChangeHandler } from "./handlers"; - const attributes: FormsetData = [ { data: { diff --git a/src/attributes/utils/handlers.ts b/src/attributes/utils/handlers.ts index e1d8ca82d..afddebd11 100644 --- a/src/attributes/utils/handlers.ts +++ b/src/attributes/utils/handlers.ts @@ -1,14 +1,23 @@ -import { AttributeInput } from "@saleor/components/Attributes"; +import { + AttributeInput, + AttributeInputData +} from "@saleor/components/Attributes"; import { FileUpload, FileUploadVariables } from "@saleor/files/types/FileUpload"; -import { FormsetData } from "@saleor/hooks/useFormset"; +import { + FormsetAtomicData, + FormsetChange, + FormsetData +} from "@saleor/hooks/useFormset"; import { PageDetails_page_attributes } from "@saleor/pages/types/PageDetails"; +import { ReorderEvent } from "@saleor/types"; import { AttributeInputTypeEnum, AttributeValueInput } from "@saleor/types/globalTypes"; +import { move, toggle } from "@saleor/utils/lists"; import { MutationFetchResult } from "react-apollo"; import { @@ -17,6 +26,99 @@ import { } from "../types/AttributeValueDelete"; import { getFileValuesToUploadFromAttributes, isFileValueUnused } from "./data"; +export function createAttributeChangeHandler( + changeAttributeData: FormsetChange, + triggerChange: () => void +): FormsetChange { + return (attributeId: string, value: string) => { + triggerChange(); + changeAttributeData(attributeId, value === "" ? [] : [value]); + }; +} + +export function createAttributeMultiChangeHandler( + changeAttributeData: FormsetChange, + attributes: FormsetData, + triggerChange: () => void +): FormsetChange { + return (attributeId: string, value: string) => { + const attribute = attributes.find( + attribute => attribute.id === attributeId + ); + + const newAttributeValues = toggle( + value, + attribute.value, + (a, b) => a === b + ); + + triggerChange(); + changeAttributeData(attributeId, newAttributeValues); + }; +} + +export function createAttributeReferenceChangeHandler( + changeAttributeData: FormsetChange, + triggerChange: () => void +): FormsetChange { + return (attributeId: string, values: string[]) => { + changeAttributeData(attributeId, values); + triggerChange(); + }; +} + +export function createAttributeFileChangeHandler( + changeAttributeData: FormsetChange, + attributesWithNewFileValue: FormsetData>, + addAttributeNewFileValue: (data: FormsetAtomicData) => void, + changeAttributeNewFileValue: FormsetChange, + triggerChange: () => void +): FormsetChange { + return (attributeId: string, value: File) => { + triggerChange(); + + const newFileValueAssigned = attributesWithNewFileValue.find( + attribute => attribute.id === attributeId + ); + + if (newFileValueAssigned) { + changeAttributeNewFileValue(attributeId, value); + } else { + addAttributeNewFileValue({ + data: null, + id: attributeId, + label: null, + value + }); + } + + changeAttributeData(attributeId, value ? [value.name] : []); + }; +} + +export function createAttributeValueReorderHandler( + changeAttributeData: FormsetChange, + attributes: FormsetData, + triggerChange: () => void +): FormsetChange { + return (attributeId: string, reorder: ReorderEvent) => { + triggerChange(); + + const attribute = attributes.find( + attribute => attribute.id === attributeId + ); + + const reorderedValues = move( + attribute.value[reorder.oldIndex], + attribute.value, + (a, b) => a === b, + reorder.newIndex + ); + + changeAttributeData(attributeId, reorderedValues); + }; +} + interface AttributesArgs { attributes: AttributeInput[]; updatedFileAttributes: AttributeValueInput[]; diff --git a/src/components/Attributes/Attributes.tsx b/src/components/Attributes/Attributes.tsx index c06644f45..95de1fb5b 100644 --- a/src/components/Attributes/Attributes.tsx +++ b/src/components/Attributes/Attributes.tsx @@ -17,7 +17,7 @@ import { PageErrorWithAttributesFragment } from "@saleor/fragments/types/PageErr import { ProductErrorWithAttributesFragment } from "@saleor/fragments/types/ProductErrorWithAttributesFragment"; import { FormsetAtomicData, FormsetChange } from "@saleor/hooks/useFormset"; import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; -import { ReorderAction } from "@saleor/types"; +import { ReorderEvent } from "@saleor/types"; import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; import { getProductErrorMessage } from "@saleor/utils/errors"; import getPageErrorMessage from "@saleor/utils/errors/page"; @@ -56,12 +56,12 @@ export interface AttributesProps { ProductErrorWithAttributesFragment | PageErrorWithAttributesFragment >; title?: React.ReactNode; - onChange: FormsetChange; - onMultiChange: FormsetChange; - onFileChange: FormsetChange; - onReferencesRemove?: FormsetChange; // TODO: temporairy optional, should be changed to required, after all pages implement it - onReferencesAddClick?: (attribute: AttributeInput) => void; // TODO: temporairy optional, should be changed to required, after all pages implement it - onReferencesReorder?: ReorderAction; // TODO: temporairy optional, should be changed to required, after all pages implement it + onChange: FormsetChange; + onMultiChange: FormsetChange; + onFileChange: FormsetChange; + onReferencesRemove: FormsetChange; + onReferencesAddClick: (attribute: AttributeInput) => void; + onReferencesReorder: FormsetChange; } const useStyles = makeStyles( @@ -328,8 +328,12 @@ const Attributes: React.FC = ({ attribute.value?.filter(id => id !== value) ) } - onValueReorder={onReferencesReorder} + onValueReorder={event => + onReferencesReorder(attribute.id, event) + } loading={loading} + error={!!error} + helperText={getErrorMessage(error, intl)} /> ) : attribute.data.inputType === diff --git a/src/components/SortableChipsField/SortableChipsField.stories.tsx b/src/components/SortableChipsField/SortableChipsField.stories.tsx index 5f3f383da..b8e41e69b 100644 --- a/src/components/SortableChipsField/SortableChipsField.stories.tsx +++ b/src/components/SortableChipsField/SortableChipsField.stories.tsx @@ -24,4 +24,11 @@ storiesOf("Generics / Sortable chips field", module) .addDecorator(CardDecorator) .addDecorator(Decorator) .add("default", () => ) - .add("loading", () => ); + .add("loading", () => ) + .add("with error", () => ( + + )); diff --git a/src/components/SortableChipsField/SortableChipsField.tsx b/src/components/SortableChipsField/SortableChipsField.tsx index a2282d441..e580b082d 100644 --- a/src/components/SortableChipsField/SortableChipsField.tsx +++ b/src/components/SortableChipsField/SortableChipsField.tsx @@ -1,4 +1,5 @@ import { makeStyles } from "@material-ui/core/styles"; +import Typography from "@material-ui/core/Typography"; import { ReorderAction } from "@saleor/types"; import React from "react"; import { SortableContainerProps } from "react-sortable-hoc"; @@ -13,6 +14,9 @@ const useStyles = makeStyles( background: "#fff", color: theme.palette.primary.dark, marginBottom: theme.spacing(1) + }, + errorText: { + color: theme.palette.error.light } }), { @@ -28,12 +32,21 @@ export interface SortableChipsFieldValueType { export interface SortableChipsFieldProps extends SortableContainerProps { loading?: boolean; values: SortableChipsFieldValueType[]; + error?: boolean; + helperText?: string; onValueDelete: (id: string) => void; onValueReorder: ReorderAction; } const SortableChipsField: React.FC = props => { - const { loading, values, onValueDelete, onValueReorder } = props; + const { + loading, + values, + error, + helperText, + onValueDelete, + onValueReorder + } = props; const classes = useStyles(props); return ( @@ -57,6 +70,11 @@ const SortableChipsField: React.FC = props => { /> )) )} + {error && ( + + {helperText} + + )}
); diff --git a/src/pages/components/PageDetailsPage/PageDetailsPage.tsx b/src/pages/components/PageDetailsPage/PageDetailsPage.tsx index 6b6dadc5a..edfeb5841 100644 --- a/src/pages/components/PageDetailsPage/PageDetailsPage.tsx +++ b/src/pages/components/PageDetailsPage/PageDetailsPage.tsx @@ -148,6 +148,7 @@ const PageDetailsPage: React.FC = ({ onFileChange={handlers.selectAttributeFile} onReferencesRemove={handlers.selectAttributeReference} onReferencesAddClick={onAssignReferencesClick} + onReferencesReorder={handlers.reorderAttributeValue} /> )} diff --git a/src/pages/components/PageDetailsPage/form.tsx b/src/pages/components/PageDetailsPage/form.tsx index 91024107d..cec3a130e 100644 --- a/src/pages/components/PageDetailsPage/form.tsx +++ b/src/pages/components/PageDetailsPage/form.tsx @@ -1,5 +1,12 @@ import { OutputData } from "@editorjs/editorjs"; import { getAttributesDisplayData } from "@saleor/attributes/utils/data"; +import { + createAttributeChangeHandler, + createAttributeFileChangeHandler, + createAttributeMultiChangeHandler, + createAttributeReferenceChangeHandler, + createAttributeValueReorderHandler +} from "@saleor/attributes/utils/handlers"; import { AttributeInput } from "@saleor/components/Attributes"; import { MetadataFormData } from "@saleor/components/Metadata"; import { RichTextEditorChange } from "@saleor/components/RichTextEditor"; @@ -16,13 +23,8 @@ import { } from "@saleor/pages/types/PageDetails"; import { getAttributeInputFromPage } from "@saleor/pages/utils/data"; import { createPageTypeSelectHandler } from "@saleor/pages/utils/handlers"; -import { - createAttributeChangeHandler, - createAttributeFileChangeHandler, - createAttributeMultiChangeHandler, - createAttributeReferenceChangeHandler -} from "@saleor/products/utils/handlers"; import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; +import { ReorderEvent } from "@saleor/types"; import getPublicationData from "@saleor/utils/data/getPublicationData"; import handleFormSubmit from "@saleor/utils/handlers/handleFormSubmit"; import { mapMetadataItemToInput } from "@saleor/utils/maps"; @@ -59,6 +61,7 @@ export interface PageUpdateHandlers { selectAttributeMulti: FormsetChange; selectAttributeReference: FormsetChange; selectAttributeFile: FormsetChange; + reorderAttributeValue: FormsetChange; } export interface UsePageUpdateFormResult { change: FormChange; @@ -150,6 +153,11 @@ function usePageForm( attributesWithNewFileValue.change, triggerChange ); + const handleAttributeValueReorder = createAttributeValueReorderHandler( + attributes.change, + attributes.data, + triggerChange + ); // Need to make it function to always have content.current up to date const getData = (): PageData => ({ @@ -190,6 +198,7 @@ function usePageForm( handlers: { changeContent, changeMetadata, + reorderAttributeValue: handleAttributeValueReorder, selectAttribute: handleAttributeChange, selectAttributeFile: handleAttributeFileChange, selectAttributeMulti: handleAttributeMultiChange, diff --git a/src/pages/utils/handlers.ts b/src/pages/utils/handlers.ts index a97db47ed..a4685a40a 100644 --- a/src/pages/utils/handlers.ts +++ b/src/pages/utils/handlers.ts @@ -1,7 +1,6 @@ import { AttributeInputData } from "@saleor/components/Attributes"; import { FormChange } from "@saleor/hooks/useForm"; -import { FormsetChange, FormsetData } from "@saleor/hooks/useFormset"; -import { toggle } from "@saleor/utils/lists"; +import { FormsetData } from "@saleor/hooks/useFormset"; import { PageDetails_page_pageType } from "../types/PageDetails"; import { getAttributeInputFromPageType } from "./data"; @@ -23,34 +22,3 @@ export function createPageTypeSelectHandler( setAttributes(getAttributeInputFromPageType(selectedPageType)); }; } - -export function createAttributeChangeHandler( - changeAttributeData: FormsetChange, - triggerChange: () => void -): FormsetChange { - return (attributeId: string, value: string) => { - triggerChange(); - changeAttributeData(attributeId, value === "" ? [] : [value]); - }; -} - -export function createAttributeMultiChangeHandler( - changeAttributeData: FormsetChange, - attributes: FormsetData, - triggerChange: () => void -): FormsetChange { - return (attributeId: string, value: string) => { - const attribute = attributes.find( - attribute => attribute.id === attributeId - ); - - const newAttributeValues = toggle( - value, - attribute.value, - (a, b) => a === b - ); - - triggerChange(); - changeAttributeData(attributeId, newAttributeValues); - }; -} diff --git a/src/products/components/ProductCreatePage/ProductCreatePage.tsx b/src/products/components/ProductCreatePage/ProductCreatePage.tsx index 595f0a714..5a54d0892 100644 --- a/src/products/components/ProductCreatePage/ProductCreatePage.tsx +++ b/src/products/components/ProductCreatePage/ProductCreatePage.tsx @@ -206,6 +206,7 @@ export const ProductCreatePage: React.FC = ({ onFileChange={handlers.selectAttributeFile} onReferencesRemove={handlers.selectAttributeReference} onReferencesAddClick={onAssignReferencesClick} + onReferencesReorder={handlers.reorderAttributeValue} /> )} diff --git a/src/products/components/ProductCreatePage/form.tsx b/src/products/components/ProductCreatePage/form.tsx index 682a7775f..4e534659d 100644 --- a/src/products/components/ProductCreatePage/form.tsx +++ b/src/products/components/ProductCreatePage/form.tsx @@ -1,5 +1,12 @@ import { OutputData } from "@editorjs/editorjs"; import { getAttributesDisplayData } from "@saleor/attributes/utils/data"; +import { + createAttributeChangeHandler, + createAttributeFileChangeHandler, + createAttributeMultiChangeHandler, + createAttributeReferenceChangeHandler, + createAttributeValueReorderHandler +} from "@saleor/attributes/utils/handlers"; import { ChannelData, ChannelPriceArgs } from "@saleor/channels/utils"; import { AttributeInput, @@ -20,10 +27,6 @@ import { ProductType } from "@saleor/products/utils/data"; import { - createAttributeChangeHandler, - createAttributeFileChangeHandler, - createAttributeMultiChangeHandler, - createAttributeReferenceChangeHandler, createChannelsChangeHandler, createChannelsPriceChangeHandler, createProductTypeSelectHandler @@ -35,6 +38,7 @@ import { import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; import { SearchProductTypes_search_edges_node } from "@saleor/searches/types/SearchProductTypes"; import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses"; +import { ReorderEvent } from "@saleor/types"; import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler"; import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger"; @@ -92,6 +96,7 @@ export interface ProductCreateHandlers >, Record<"selectAttributeReference", FormsetChange>, Record<"selectAttributeFile", FormsetChange>, + Record<"reorderAttributeValue", FormsetChange>, Record<"addStock" | "deleteStock", (id: string) => void> { changeDescription: RichTextEditorChange; } @@ -224,6 +229,11 @@ function useProductCreateForm( attributesWithNewFileValue.change, triggerChange ); + const handleAttributeValueReorder = createAttributeValueReorderHandler( + attributes.change, + attributes.data, + triggerChange + ); const handleProductTypeSelect = createProductTypeSelectHandler( attributes.set, setProductType, @@ -305,6 +315,7 @@ function useProductCreateForm( changeMetadata, changeStock: handleStockChange, deleteStock: handleStockDelete, + reorderAttributeValue: handleAttributeValueReorder, selectAttribute: handleAttributeChange, selectAttributeFile: handleAttributeFileChange, selectAttributeMultiple: handleAttributeMultiChange, diff --git a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx index 1afe7b8e8..87c00d0fd 100644 --- a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx +++ b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx @@ -267,6 +267,7 @@ export const ProductUpdatePage: React.FC = ({ onFileChange={handlers.selectAttributeFile} onReferencesRemove={handlers.selectAttributeReference} onReferencesAddClick={onAssignReferencesClick} + onReferencesReorder={handlers.reorderAttributeValue} /> )} diff --git a/src/products/components/ProductUpdatePage/form.tsx b/src/products/components/ProductUpdatePage/form.tsx index b271bf8a8..c49f2c7f1 100644 --- a/src/products/components/ProductUpdatePage/form.tsx +++ b/src/products/components/ProductUpdatePage/form.tsx @@ -1,5 +1,12 @@ import { OutputData } from "@editorjs/editorjs"; import { getAttributesDisplayData } from "@saleor/attributes/utils/data"; +import { + createAttributeChangeHandler, + createAttributeFileChangeHandler, + createAttributeMultiChangeHandler, + createAttributeReferenceChangeHandler, + createAttributeValueReorderHandler +} from "@saleor/attributes/utils/handlers"; import { ChannelData, ChannelPriceArgs } from "@saleor/channels/utils"; import { AttributeInput } from "@saleor/components/Attributes"; import { MetadataFormData } from "@saleor/components/Metadata"; @@ -19,10 +26,6 @@ import { getStockInputFromProduct } from "@saleor/products/utils/data"; import { - createAttributeChangeHandler, - createAttributeFileChangeHandler, - createAttributeMultiChangeHandler, - createAttributeReferenceChangeHandler, createChannelsChangeHandler, createChannelsPriceChangeHandler } from "@saleor/products/utils/handlers"; @@ -32,6 +35,7 @@ import { } from "@saleor/products/utils/validation"; import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses"; +import { ReorderEvent } from "@saleor/types"; import handleFormSubmit from "@saleor/utils/handlers/handleFormSubmit"; import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler"; import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; @@ -109,6 +113,7 @@ export interface ProductUpdateHandlers >, Record<"selectAttributeReference", FormsetChange>, Record<"selectAttributeFile", FormsetChange>, + Record<"reorderAttributeValue", FormsetChange>, Record<"addStock" | "deleteStock", (id: string) => void> { changeDescription: RichTextEditorChange; } @@ -234,6 +239,11 @@ function useProductUpdateForm( attributesWithNewFileValue.change, triggerChange ); + const handleAttributeValueReorder = createAttributeValueReorderHandler( + attributes.change, + attributes.data, + triggerChange + ); const handleStockChange: FormsetChange = (id, value) => { triggerChange(); stocks.change(id, value); @@ -322,6 +332,7 @@ function useProductUpdateForm( changeMetadata, changeStock: handleStockChange, deleteStock: handleStockDelete, + reorderAttributeValue: handleAttributeValueReorder, selectAttribute: handleAttributeChange, selectAttributeFile: handleAttributeFileChange, selectAttributeMultiple: handleAttributeMultiChange, diff --git a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx index 4354d9ddd..b0caf9591 100644 --- a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx +++ b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx @@ -162,6 +162,9 @@ const ProductVariantCreatePage: React.FC = ({ onChange={handlers.selectAttribute} onMultiChange={handlers.selectAttributeMultiple} onFileChange={handlers.selectAttributeFile} + onReferencesRemove={handlers.selectAttributeReference} + onReferencesAddClick={onAssignReferencesClick} + onReferencesReorder={handlers.reorderAttributeValue} /> = ({ onFileChange={handlers.selectAttributeFile} onReferencesRemove={handlers.selectAttributeReference} onReferencesAddClick={onAssignReferencesClick} + onReferencesReorder={handlers.reorderAttributeValue} /> , Record<"selectAttributeReference", FormsetChange>, Record<"selectAttributeFile", FormsetChange>, + Record<"reorderAttributeValue", FormsetChange>, Record<"addStock" | "deleteStock", (id: string) => void> { changeMetadata: FormChange; } @@ -131,6 +132,11 @@ function useProductVariantCreateForm( attributesWithNewFileValue.change, triggerChange ); + const handleAttributeValueReorder = createAttributeValueReorderHandler( + attributes.change, + attributes.data, + triggerChange + ); const handleStockAdd = (id: string) => { triggerChange(); stocks.add({ @@ -183,6 +189,7 @@ function useProductVariantCreateForm( changeMetadata, changeStock: handleStockChange, deleteStock: handleStockDelete, + reorderAttributeValue: handleAttributeValueReorder, selectAttribute: handleAttributeChange, selectAttributeFile: handleAttributeFileChange, selectAttributeMultiple: handleAttributeMultiChange, diff --git a/src/products/components/ProductVariantPage/ProductVariantPage.tsx b/src/products/components/ProductVariantPage/ProductVariantPage.tsx index 960f64cf8..6e88471a5 100644 --- a/src/products/components/ProductVariantPage/ProductVariantPage.tsx +++ b/src/products/components/ProductVariantPage/ProductVariantPage.tsx @@ -217,6 +217,7 @@ const ProductVariantPage: React.FC = ({ onFileChange={handlers.selectAttributeFile} onReferencesRemove={handlers.selectAttributeReference} onReferencesAddClick={onAssignReferencesClick} + onReferencesReorder={handlers.reorderAttributeValue} /> = ({ onFileChange={handlers.selectAttributeFile} onReferencesRemove={handlers.selectAttributeReference} onReferencesAddClick={onAssignReferencesClick} + onReferencesReorder={handlers.reorderAttributeValue} /> , Record<"selectAttributeReference", FormsetChange>, Record<"selectAttributeFile", FormsetChange>, + Record<"reorderAttributeValue", FormsetChange>, Record<"addStock" | "deleteStock", (id: string) => void> { changeMetadata: FormChange; } @@ -148,6 +149,11 @@ function useProductVariantUpdateForm( attributesWithNewFileValue.change, triggerChange ); + const handleAttributeValueReorder = createAttributeValueReorderHandler( + attributes.change, + attributes.data, + triggerChange + ); const handleStockAdd = (id: string) => { triggerChange(); stocks.add({ @@ -233,6 +239,7 @@ function useProductVariantUpdateForm( changeMetadata, changeStock: handleStockChange, deleteStock: handleStockDelete, + reorderAttributeValue: handleAttributeValueReorder, selectAttribute: handleAttributeChange, selectAttributeFile: handleAttributeFileChange, selectAttributeMultiple: handleAttributeMultiChange, diff --git a/src/products/utils/handlers.test.ts b/src/products/utils/handlers.test.ts deleted file mode 100644 index c01a29da0..000000000 --- a/src/products/utils/handlers.test.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { AttributeInputData } from "@saleor/components/Attributes"; -import { FormsetData } from "@saleor/hooks/useFormset"; -import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; - -import { createAttributeMultiChangeHandler } from "./handlers"; - -const attributes: FormsetData = [ - { - data: { - inputType: AttributeInputTypeEnum.DROPDOWN, - isRequired: false, - values: [ - { - __typename: "AttributeValue", - file: null, - id: "attrv-1", - name: "Attribute 1 Value 1", - reference: null, - slug: "attr-1-v-1" - } - ] - }, - id: "attr-1", - label: "Attribute 1", - value: [] - }, - { - data: { - inputType: AttributeInputTypeEnum.MULTISELECT, - isRequired: false, - values: [ - { - __typename: "AttributeValue", - file: null, - id: "attrv-2", - name: "Attribute 2 Value 1", - reference: null, - slug: "attr-2-v-1" - }, - { - __typename: "AttributeValue", - file: null, - id: "attrv-3", - name: "Attribute 2 Value 2", - reference: null, - slug: "attr-2-v-2" - }, - { - __typename: "AttributeValue", - file: null, - id: "attrv-4", - name: "Attribute 2 Value 3", - reference: null, - slug: "attr-2-v-3" - } - ] - }, - id: "attr-2", - label: "Attribute 2", - value: ["attr-2-v-3"] - }, - { - data: { - inputType: AttributeInputTypeEnum.FILE, - isRequired: false, - values: [ - { - __typename: "AttributeValue", - file: { - __typename: "File", - contentType: "image/png", - url: "some-non-existing-url" - }, - id: "attrv-5", - name: "File First Value", - reference: null, - slug: "file-first-value" - } - ] - }, - id: "attr-3", - label: "File Attribute", - value: [] - } -]; - -describe("Multiple select", () => { - it("is able to select value", () => { - const change = jest.fn(); - const trigger = jest.fn(); - const handler = createAttributeMultiChangeHandler( - change, - attributes, - trigger - ); - - handler("attr-2", "attr-2-v-1"); - - expect(change).toHaveBeenCalledTimes(1); - expect(change.mock.calls[0][0]).toBe("attr-2"); - expect(change.mock.calls[0][1]).toHaveLength(2); - expect(change.mock.calls[0][1][0]).toBe("attr-2-v-3"); - expect(change.mock.calls[0][1][1]).toBe("attr-2-v-1"); - expect(trigger).toHaveBeenCalledTimes(1); - }); - - it("is able to deselect value", () => { - const change = jest.fn(); - const trigger = jest.fn(); - const handler = createAttributeMultiChangeHandler( - change, - attributes, - trigger - ); - - handler("attr-2", "attr-2-v-3"); - - expect(change).toHaveBeenCalledTimes(1); - expect(change.mock.calls[0][0]).toBe("attr-2"); - expect(change.mock.calls[0][1]).toHaveLength(0); - expect(trigger).toHaveBeenCalledTimes(1); - }); - - it("is able to add custom value", () => { - const change = jest.fn(); - const trigger = jest.fn(); - const handler = createAttributeMultiChangeHandler( - change, - attributes, - trigger - ); - - handler("attr-2", "A Value"); - - expect(change).toHaveBeenCalledTimes(1); - expect(change.mock.calls[0][0]).toBe("attr-2"); - expect(change.mock.calls[0][1]).toHaveLength(2); - expect(change.mock.calls[0][1][0]).toBe("attr-2-v-3"); - expect(change.mock.calls[0][1][1]).toBe("A Value"); - expect(trigger).toHaveBeenCalledTimes(1); - }); -}); diff --git a/src/products/utils/handlers.ts b/src/products/utils/handlers.ts index 40e79b80c..00608064c 100644 --- a/src/products/utils/handlers.ts +++ b/src/products/utils/handlers.ts @@ -5,25 +5,10 @@ import { } from "@saleor/channels/utils"; import { AttributeInputData } from "@saleor/components/Attributes"; import { FormChange } from "@saleor/hooks/useForm"; -import { - FormsetAtomicData, - FormsetChange, - FormsetData -} from "@saleor/hooks/useFormset"; -import { toggle } from "@saleor/utils/lists"; +import { FormsetData } from "@saleor/hooks/useFormset"; import { getAttributeInputFromProductType, ProductType } from "./data"; -export function createAttributeChangeHandler( - changeAttributeData: FormsetChange, - triggerChange: () => void -): FormsetChange { - return (attributeId: string, value: string) => { - triggerChange(); - changeAttributeData(attributeId, value === "" ? [] : [value]); - }; -} - export function createChannelsPriceChangeHandler( channelListings: ChannelPriceData[], updateChannels: (data: ChannelPriceData[]) => void, @@ -103,66 +88,6 @@ export function createVariantChannelsChangeHandler( }; } -export function createAttributeMultiChangeHandler( - changeAttributeData: FormsetChange, - attributes: FormsetData, - triggerChange: () => void -): FormsetChange { - return (attributeId: string, value: string) => { - const attribute = attributes.find( - attribute => attribute.id === attributeId - ); - - const newAttributeValues = toggle( - value, - attribute.value, - (a, b) => a === b - ); - - triggerChange(); - changeAttributeData(attributeId, newAttributeValues); - }; -} - -export function createAttributeReferenceChangeHandler( - changeAttributeData: FormsetChange, - triggerChange: () => void -): FormsetChange { - return (attributeId: string, values: string[]) => { - changeAttributeData(attributeId, values); - triggerChange(); - }; -} - -export function createAttributeFileChangeHandler( - changeAttributeData: FormsetChange, - attributesWithNewFileValue: FormsetData>, - addAttributeNewFileValue: (data: FormsetAtomicData) => void, - changeAttributeNewFileValue: FormsetChange, - triggerChange: () => void -): FormsetChange { - return (attributeId: string, value: File) => { - triggerChange(); - - const newFileValueAssigned = attributesWithNewFileValue.find( - attribute => attribute.id === attributeId - ); - - if (newFileValueAssigned) { - changeAttributeNewFileValue(attributeId, value); - } else { - addAttributeNewFileValue({ - data: null, - id: attributeId, - label: null, - value - }); - } - - changeAttributeData(attributeId, value ? [value.name] : []); - }; -} - export function createProductTypeSelectHandler( setAttributes: (data: FormsetData) => void, setProductType: (productType: ProductType) => void, diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index f1661c266..fbd9a13b8 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -11234,6 +11234,389 @@ exports[`Storyshots Generics / Sortable chips field loading 1`] = `
`; +exports[`Storyshots Generics / Sortable chips field with error 1`] = ` +
+
+
+
+
+
+ +
+ Item 1 +
+ +
+
+
+
+ +
+ Item 2 +
+ +
+
+
+
+ +
+ Item 3 +
+ +
+
+
+
+ +
+ Item 4 +
+ +
+
+
+
+ +
+ Item 5 +
+ +
+
+
+
+ +
+ Item 6 +
+ +
+
+
+ Something went wrong +
+
+
+
+
+`; + exports[`Storyshots Generics / Square Button default 1`] = `
Date: Wed, 20 Jan 2021 17:16:43 +0100 Subject: [PATCH 09/35] Feature/order reissue (#910) * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * Add change to changelog * Remove console.log * Update tests * Extract messages * Add utils functions for selecting only ulfulfilled order lines * Add optional value selection for line item * Update tests * Add optional rendering of unfulfilled items card and refactor a bit * Update displaying of items card title when refunded card * UUpdate utils, form data etc. not to include refunded items when calculating replaced items amount * Uppdate return items card not to display replace buttons for refunded items * Refactor and small fixes after review * Update extracted messages * Fix card title when no fullfilemtn id * wip * Initially stitch returns page. Update types, add mutation * remove unnecessary component display names * Add loading status from form submission & refactor * Add errors from response * Add errors from response and refactor * Remove comments * Add optional error adding when no data from return create request * Update messages * wip * Update snapshots * Remove unnecessary console.log * Add better typing for getParsedLineData function * Update & refactor card title to match cards both in return and order details * Add handling of new statuses to order details cards. Also refactor, and devide order fulfillment card into couple of smaller components * Update messages * Update schema to match api * Update types * Update status label component to match colors with new designs and order details cards * RUpdate and refactor order fulfillment card components to be reusable. Also add replaced status handling * Updayte card title component to handle all cases and statuses * Update oorder unfulfilled items card and order details page, reduce some of the boilerplate * Fix card title types and adjust returns card to match * Update messages * Update snapshots * RUpdate order fulfillment card with subtitles and buttons for returned status * Add onRefund to order fulfillment card * Fix typo and wrong message in card title * Add missing condition in return form submission utils to decice if to refund products * Update fulfillment subtitles row and tests * Update messages * Change naming and locations of OrderFulfillment and items card components * Update messages * U[pdate names of components again to even better ones * Update messages * changelog * Update schema and types so that order history event also includes user first and last name * Add extended timeline event and event header components. Move some of the logic to utils and add way to display links in the event header. * FFix types * Update messages * Change naming of isOfType -> isTimelineEventOfType and refactor extended timeline event messages selection to be less complicated * Add ids and update messages * Add ids and update messages some more * Update storybook decorator to work with react router context in components and tests * Refactor after review * Update messages * Add rredirecting to draft order * Add handling draft creation from replacement * Add related order to order event fragment and update lots and lots of types * Update extended timeline event to match related order type on order history event * Update fixtures * Refactor ExtendedTimelineEvent Co-authored-by: Jakub Majorek * Fix typing * Update messages * Fix missing history event for replacement draft created for replaced products * Update messages * Handle new statuses for returned and partially returned orders * Update messages * update snapshots * BBump empty line to rebuild ci * Change status to proper color * Change replaceable items in return for replace to be auto off instead of on * Add utils functions and make order details menu not show option to return items when there are returnable items in the order * Fix replace checkbox showing when previously hidden and clicked set maximal quantities * Fix return form invalid money values * Add default values to avoid returning of NaN in utils for return amount and refactor * Add ggeneral error alerts * Add eproduct error box component and style. style a lot. * Fixes * Fix lint * Add cannot refund error title + description * Extract messages * Refactor after review * Add better, nicer and fancier imports to product error cell * Use error color from palette in product error cell * Fix max refund when 0 for return * Add ddisable ability to refund products button so it's disabled when 0 products selected * Add class for order return form data parsing and add condition to not do refund when total captured on order is 0 * Update snapshots * Add condition for order lines quantity in order products table row * Fix return amount submit button * Add change to changelog Co-authored-by: Jakub Majorek --- CHANGELOG.md | 1 + locale/defaultMessages.json | 437 +- schema.graphql | 45 +- src/components/StatusLabel/StatusLabel.tsx | 12 +- src/components/Theme/themes.ts | 2 +- src/components/Timeline/TimelineEvent.tsx | 97 +- .../Timeline/TimelineEventHeader.tsx | 93 + src/fragments/orders.ts | 6 + src/fragments/types/OrderDetailsFragment.ts | 9 + src/fragments/types/OrderEventFragment.ts | 9 + src/icons/ErrorExclamationCircle.tsx | 25 + src/misc.ts | 18 + .../OrderDetailsPage/OrderDetailsPage.tsx | 113 +- .../OrderDetailsPage/utils.test.tsx | 44 + .../components/OrderDetailsPage/utils.ts | 26 + .../ActionButtons.tsx | 64 + .../ExtraInfoLines.tsx | 103 + .../OrderFulfilledProductsCard.tsx | 114 + .../OrderFulfilledProductsCard/index.ts | 2 + .../OrderFulfillment/OrderFulfillment.tsx | 333 -- .../components/OrderFulfillment/index.ts | 2 - .../OrderHistory/ExtendedTimelineEvent.tsx | 211 + .../components/OrderHistory/OrderHistory.tsx | 184 +- src/orders/components/OrderHistory/utils.tsx | 41 + .../OrderProductsCardHeader.tsx | 95 + .../OrderProductsTableRow.tsx | 129 + .../components/OrderRefundAmount/index.ts | 2 - .../OrderRefundAmountValues.tsx | 153 - .../OrderRefundAmountValues/index.ts | 2 - .../OrderRefundPage/OrderRefundPage.tsx | 185 +- .../OrderRefundReturnAmount.tsx} | 229 +- .../OrderRefundReturnAmountValues.tsx | 133 + .../RefundAmountInput.tsx | 108 + .../OrderRefundReturnAmount/index.ts | 2 + .../OrderRefundReturnAmount/utils.ts | 203 + .../OrderReturnPage/OrderReturnPage.tsx | 129 + .../OrderReturnRefundItemsCard/CardTitle.tsx | 135 + .../MaximalButton.tsx | 39 + .../ProductErrorCell.tsx | 100 + .../ReturnItemsCard.tsx | 262 + .../OrderReturnRefundItemsCard/index.tsx | 2 + .../components/OrderReturnPage/form.tsx | 233 + .../components/OrderReturnPage/index.ts | 2 + .../components/OrderReturnPage/utils.tsx | 93 + .../OrderUnfulfilledItems.tsx | 192 - .../components/OrderUnfulfilledItems/index.ts | 2 - .../OrderUnfulfilledProductsCard.tsx | 64 + .../OrderUnfulfilledProductsCard/index.ts | 2 + src/orders/fixtures.ts | 16 +- src/orders/index.tsx | 7 + src/orders/mutations.ts | 31 + src/orders/queries.ts | 4 + src/orders/types/FulfillOrder.ts | 9 + src/orders/types/FulfillmentReturnProducts.ts | 41 + src/orders/types/OrderAddNote.ts | 9 + src/orders/types/OrderCancel.ts | 9 + src/orders/types/OrderCapture.ts | 9 + src/orders/types/OrderConfirm.ts | 9 + src/orders/types/OrderDetails.ts | 9 + src/orders/types/OrderDraftCancel.ts | 9 + src/orders/types/OrderDraftFinalize.ts | 9 + src/orders/types/OrderDraftUpdate.ts | 9 + src/orders/types/OrderFulfillmentCancel.ts | 9 + .../types/OrderFulfillmentRefundProducts.ts | 9 + .../types/OrderFulfillmentUpdateTracking.ts | 9 + src/orders/types/OrderLineDelete.ts | 9 + src/orders/types/OrderLineUpdate.ts | 9 + src/orders/types/OrderLinesAdd.ts | 9 + src/orders/types/OrderMarkAsPaid.ts | 9 + src/orders/types/OrderRefund.ts | 9 + src/orders/types/OrderUpdate.ts | 9 + src/orders/types/OrderVoid.ts | 9 + src/orders/urls.ts | 10 + src/orders/utils/data.test.ts | 812 ++- src/orders/utils/data.ts | 139 +- src/orders/views/OrderDetails/index.tsx | 2 + src/orders/views/OrderReturn/OrderReturn.tsx | 113 + src/orders/views/OrderReturn/index.tsx | 2 + src/orders/views/OrderReturn/utils.tsx | 102 + src/storybook/Decorator.tsx | 22 +- .../__snapshots__/Stories.test.ts.snap | 5042 +++++++---------- .../stories/orders/OrderDetailsPage.tsx | 1 + src/types/globalTypes.ts | 32 +- 83 files changed, 6819 insertions(+), 4215 deletions(-) create mode 100644 src/components/Timeline/TimelineEventHeader.tsx create mode 100644 src/icons/ErrorExclamationCircle.tsx create mode 100644 src/orders/components/OrderDetailsPage/utils.test.tsx create mode 100644 src/orders/components/OrderDetailsPage/utils.ts create mode 100644 src/orders/components/OrderFulfilledProductsCard/ActionButtons.tsx create mode 100644 src/orders/components/OrderFulfilledProductsCard/ExtraInfoLines.tsx create mode 100644 src/orders/components/OrderFulfilledProductsCard/OrderFulfilledProductsCard.tsx create mode 100644 src/orders/components/OrderFulfilledProductsCard/index.ts delete mode 100644 src/orders/components/OrderFulfillment/OrderFulfillment.tsx delete mode 100644 src/orders/components/OrderFulfillment/index.ts create mode 100644 src/orders/components/OrderHistory/ExtendedTimelineEvent.tsx create mode 100644 src/orders/components/OrderHistory/utils.tsx create mode 100644 src/orders/components/OrderProductsCardElements/OrderProductsCardHeader.tsx create mode 100644 src/orders/components/OrderProductsCardElements/OrderProductsTableRow.tsx delete mode 100644 src/orders/components/OrderRefundAmount/index.ts delete mode 100644 src/orders/components/OrderRefundAmountValues/OrderRefundAmountValues.tsx delete mode 100644 src/orders/components/OrderRefundAmountValues/index.ts rename src/orders/components/{OrderRefundAmount/OrderRefundAmount.tsx => OrderRefundReturnAmount/OrderRefundReturnAmount.tsx} (60%) create mode 100644 src/orders/components/OrderRefundReturnAmount/OrderRefundReturnAmountValues.tsx create mode 100644 src/orders/components/OrderRefundReturnAmount/RefundAmountInput.tsx create mode 100644 src/orders/components/OrderRefundReturnAmount/index.ts create mode 100644 src/orders/components/OrderRefundReturnAmount/utils.ts create mode 100644 src/orders/components/OrderReturnPage/OrderReturnPage.tsx create mode 100644 src/orders/components/OrderReturnPage/OrderReturnRefundItemsCard/CardTitle.tsx create mode 100644 src/orders/components/OrderReturnPage/OrderReturnRefundItemsCard/MaximalButton.tsx create mode 100644 src/orders/components/OrderReturnPage/OrderReturnRefundItemsCard/ProductErrorCell.tsx create mode 100644 src/orders/components/OrderReturnPage/OrderReturnRefundItemsCard/ReturnItemsCard.tsx create mode 100644 src/orders/components/OrderReturnPage/OrderReturnRefundItemsCard/index.tsx create mode 100644 src/orders/components/OrderReturnPage/form.tsx create mode 100644 src/orders/components/OrderReturnPage/index.ts create mode 100644 src/orders/components/OrderReturnPage/utils.tsx delete mode 100644 src/orders/components/OrderUnfulfilledItems/OrderUnfulfilledItems.tsx delete mode 100644 src/orders/components/OrderUnfulfilledItems/index.ts create mode 100644 src/orders/components/OrderUnfulfilledProductsCard/OrderUnfulfilledProductsCard.tsx create mode 100644 src/orders/components/OrderUnfulfilledProductsCard/index.ts create mode 100644 src/orders/types/FulfillmentReturnProducts.ts create mode 100644 src/orders/views/OrderReturn/OrderReturn.tsx create mode 100644 src/orders/views/OrderReturn/index.tsx create mode 100644 src/orders/views/OrderReturn/utils.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index ff811c864..fe0127347 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ All notable, unreleased changes to this project will be documented in this file. - Add shipping methods to translation section - #864 by @marekchoinski - New Miscellaneous and Product refunds - #870 by @orzechdev - Add zip code exclusion - #877 by @dominik-zeglen +- Add order reissue - Update quantity column in Inventory part of Product Variant view - #904 by @dominik-zeglen - Add file attributes - #884 by @orzechdev - Add shipping delivery days - #914 by @orzechdev diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index b55495938..a328c8454 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -3,6 +3,14 @@ "context": "dialog header", "string": "Cancel Order" }, + "amount title": { + "context": "amount title", + "string": "Refunded amount" + }, + "by preposition": { + "context": "by preposition", + "string": "by" + }, "configurationMenuAttributes": { "string": "Determine attributes used to create product types" }, @@ -42,6 +50,38 @@ "configurationPluginsPages": { "string": "View and update your plugins and their settings." }, + "event products list title refunded": { + "context": "refunded products list title", + "string": "Products refunded" + }, + "event products list title replaced": { + "context": "replaced products list title", + "string": "Products replaced" + }, + "event products list title returned": { + "context": "returned products list title", + "string": "Products returned" + }, + "event products title draft reissued": { + "context": "draft created from replace products list title", + "string": "Products replaced" + }, + "event title draft reissued": { + "context": "draft created from replace event title", + "string": "Draft was reissued from order " + }, + "event title refunded": { + "context": "refunded event title", + "string": "Products were refunded by " + }, + "event title replaced": { + "context": "replaced event title", + "string": "Products were replaced by " + }, + "event title returned": { + "context": "returned event title", + "string": "Products were returned by" + }, "homeActivityCardHeader": { "context": "header", "string": "Activity" @@ -336,6 +376,10 @@ "context": "unassign product from sale, button", "string": "Unassign" }, + "shipment refund title": { + "context": "shipment refund title", + "string": "Shipment was refunded" + }, "shippingZoneDetailsDialogsDeleteShippingMethod": { "context": "delete shipping method", "string": "Are you sure you want to delete {name}?" @@ -3164,13 +3208,17 @@ "src_dot_orders_dot_components_dot_OrderCustomer_dot_4282475982": { "string": "Billing Address" }, - "src_dot_orders_dot_components_dot_OrderDetailsPage_dot_1854613983": { - "context": "button", + "src_dot_orders_dot_components_dot_OrderDetailsPage_dot_cancelOrder": { + "context": "cancel button", "string": "Cancel order" }, - "src_dot_orders_dot_components_dot_OrderDetailsPage_dot_3086420445": { + "src_dot_orders_dot_components_dot_OrderDetailsPage_dot_confirmOrder": { "context": "save button", - "string": "confirm order" + "string": "Confirm order" + }, + "src_dot_orders_dot_components_dot_OrderDetailsPage_dot_returnOrder": { + "context": "return button", + "string": "Return / Replace order" }, "src_dot_orders_dot_components_dot_OrderDraftCancelDialog_dot_1961675716": { "context": "dialog header", @@ -3306,6 +3354,37 @@ "context": "product's sku", "string": "SKU" }, + "src_dot_orders_dot_components_dot_OrderFulfilledProductsCard_dot_1119771899": { + "context": "add tracking button", + "string": "Add tracking" + }, + "src_dot_orders_dot_components_dot_OrderFulfilledProductsCard_dot_2211099657": { + "context": "edit tracking button", + "string": "Edit tracking" + }, + "src_dot_orders_dot_components_dot_OrderFulfilledProductsCard_dot_2845258362": { + "context": "refund button", + "string": "Refund" + }, + "src_dot_orders_dot_components_dot_OrderFulfilledProductsCard_dot_3254150098": { + "string": "Tracking Number: {trackingNumber}" + }, + "src_dot_orders_dot_components_dot_OrderFulfilledProductsCard_dot_732594284": { + "context": "button", + "string": "Cancel Fulfillment" + }, + "src_dot_orders_dot_components_dot_OrderFulfilledProductsCard_dot_fulfilled": { + "context": "fulfillment group", + "string": "Fulfilled from: " + }, + "src_dot_orders_dot_components_dot_OrderFulfilledProductsCard_dot_restocked": { + "context": "restocked group", + "string": "Restocked from: " + }, + "src_dot_orders_dot_components_dot_OrderFulfilledProductsCard_dot_tracking": { + "context": "tracking number", + "string": "Tracking Number: {trackingNumber}" + }, "src_dot_orders_dot_components_dot_OrderFulfillmentCancelDialog_dot_1097287358": { "string": "Are you sure you want to cancel fulfillment? Canceling a fulfillment will restock products at a selected warehouse." }, @@ -3343,71 +3422,10 @@ "context": "dialog header", "string": "Add Tracking Code" }, - "src_dot_orders_dot_components_dot_OrderFulfillment_dot_1119771899": { - "context": "fulfillment group tracking number", - "string": "Add tracking" - }, - "src_dot_orders_dot_components_dot_OrderFulfillment_dot_1134347598": { - "context": "product price", - "string": "Price" - }, - "src_dot_orders_dot_components_dot_OrderFulfillment_dot_1895667608": { - "context": "product name", - "string": "Product" - }, - "src_dot_orders_dot_components_dot_OrderFulfillment_dot_2211099657": { - "context": "fulfillment group tracking number", - "string": "Edit tracking" - }, - "src_dot_orders_dot_components_dot_OrderFulfillment_dot_2567258278": { - "context": "refunded fulfillment, section header", - "string": "Refunded ({quantity})" - }, - "src_dot_orders_dot_components_dot_OrderFulfillment_dot_2796503714": { - "context": "ordered product quantity", - "string": "Quantity" - }, - "src_dot_orders_dot_components_dot_OrderFulfillment_dot_3254150098": { - "string": "Tracking Number: {trackingNumber}" - }, - "src_dot_orders_dot_components_dot_OrderFulfillment_dot_3494686506": { - "context": "section header", - "string": "Fulfilled ({quantity})" - }, - "src_dot_orders_dot_components_dot_OrderFulfillment_dot_4039425374": { - "context": "cancelled fulfillment, section header", - "string": "Cancelled ({quantity})" - }, - "src_dot_orders_dot_components_dot_OrderFulfillment_dot_693960049": { - "context": "ordered product sku", - "string": "SKU" - }, - "src_dot_orders_dot_components_dot_OrderFulfillment_dot_732594284": { - "context": "button", - "string": "Cancel Fulfillment" - }, - "src_dot_orders_dot_components_dot_OrderFulfillment_dot_77179533": { - "context": "fulfillment group", - "string": "Fulfilled from: {warehouseName}" - }, - "src_dot_orders_dot_components_dot_OrderFulfillment_dot_878013594": { - "context": "order line total price", - "string": "Total" - }, - "src_dot_orders_dot_components_dot_OrderHistory_dot_1154330234": { - "context": "transaction reference", - "string": "Transaction Reference {transactionReference}" - }, "src_dot_orders_dot_components_dot_OrderHistory_dot_1230178536": { "context": "order history message", "string": "Order address was updated" }, - "src_dot_orders_dot_components_dot_OrderHistory_dot_123236698": { - "string": "Shipment was refunded" - }, - "src_dot_orders_dot_components_dot_OrderHistory_dot_1322321687": { - "string": "Refunded amount" - }, "src_dot_orders_dot_components_dot_OrderHistory_dot_1463685940": { "context": "order history message", "string": "Order was marked as paid" @@ -3523,9 +3541,6 @@ "context": "order history message", "string": "Payment failed" }, - "src_dot_orders_dot_components_dot_OrderHistory_dot_492197448": { - "string": "Products refunded" - }, "src_dot_orders_dot_components_dot_OrderHistory_dot_493321552": { "context": "order history message", "string": "Order cancel information was sent to customer" @@ -3546,6 +3561,14 @@ "context": "order history message", "string": "Order was cancelled" }, + "src_dot_orders_dot_components_dot_OrderHistory_dot_description": { + "context": "replacement created order history message description", + "string": "was created for replaced products" + }, + "src_dot_orders_dot_components_dot_OrderHistory_dot_draftNumber": { + "context": "replacement created order history message draft number", + "string": "Draft #{orderNumber} " + }, "src_dot_orders_dot_components_dot_OrderInvoiceEmailSendDialog_dot_1821123638": { "string": "Are you sure you want to send this invoice: {invoiceNumber} to the customer?" }, @@ -3729,71 +3752,25 @@ "src_dot_orders_dot_components_dot_OrderProductAddDialog_dot_353369701": { "string": "No products matching given query" }, - "src_dot_orders_dot_components_dot_OrderRefundAmountValues_dot_1580639738": { - "context": "order refund amount", - "string": "Proposed refund amount" + "src_dot_orders_dot_components_dot_OrderProductsCardElements_dot_1134347598": { + "context": "product price", + "string": "Price" }, - "src_dot_orders_dot_components_dot_OrderRefundAmountValues_dot_1705174606": { - "context": "order refund amount", - "string": "Max Refund" + "src_dot_orders_dot_components_dot_OrderProductsCardElements_dot_1895667608": { + "context": "product name", + "string": "Product" }, - "src_dot_orders_dot_components_dot_OrderRefundAmountValues_dot_1734445951": { - "context": "order refund amount", - "string": "Refund total amount" + "src_dot_orders_dot_components_dot_OrderProductsCardElements_dot_2796503714": { + "context": "ordered product quantity", + "string": "Quantity" }, - "src_dot_orders_dot_components_dot_OrderRefundAmountValues_dot_2045860028": { - "context": "order refund amount", - "string": "Authorized Amount" + "src_dot_orders_dot_components_dot_OrderProductsCardElements_dot_693960049": { + "context": "ordered product sku", + "string": "SKU" }, - "src_dot_orders_dot_components_dot_OrderRefundAmountValues_dot_2854815744": { - "context": "order refund amount", - "string": "Previously refunded" - }, - "src_dot_orders_dot_components_dot_OrderRefundAmountValues_dot_2907874606": { - "context": "order refund amount", - "string": "Selected products value" - }, - "src_dot_orders_dot_components_dot_OrderRefundAmountValues_dot_79173946": { - "context": "order refund amount", - "string": "Shipment cost" - }, - "src_dot_orders_dot_components_dot_OrderRefundAmount_dot_120912052": { - "context": "order refund amount", - "string": "Refunded items can’t be fulfilled" - }, - "src_dot_orders_dot_components_dot_OrderRefundAmount_dot_159210811": { - "string": "Amount must be bigger than 0" - }, - "src_dot_orders_dot_components_dot_OrderRefundAmount_dot_2256869831": { - "context": "section header", - "string": "Refunded Amount" - }, - "src_dot_orders_dot_components_dot_OrderRefundAmount_dot_2845258362": { - "context": "order refund amount, input button", - "string": "Refund" - }, - "src_dot_orders_dot_components_dot_OrderRefundAmount_dot_4033685232": { - "string": "Amount cannot be bigger than max refund" - }, - "src_dot_orders_dot_components_dot_OrderRefundAmount_dot_40513382": { - "context": "order refund amount, input button", - "string": "Refund {currency} {amount}" - }, - "src_dot_orders_dot_components_dot_OrderRefundAmount_dot_4224226791": { - "context": "label", - "string": "Automatic Amount" - }, - "src_dot_orders_dot_components_dot_OrderRefundAmount_dot_508357513": { - "context": "label", - "string": "Manual Amount" - }, - "src_dot_orders_dot_components_dot_OrderRefundAmount_dot_553737700": { - "context": "checkbox", - "string": "Refund shipment costs" - }, - "src_dot_orders_dot_components_dot_OrderRefundAmount_dot_75546233": { - "context": "order refund amount, input label", - "string": "Amount" + "src_dot_orders_dot_components_dot_OrderProductsCardElements_dot_878013594": { + "context": "order line total price", + "string": "Total" }, "src_dot_orders_dot_components_dot_OrderRefundFulfilledProducts_dot_1134347598": { "context": "tabel column header", @@ -3838,6 +3815,86 @@ "context": "page header with order number", "string": "Order #{orderNumber}" }, + "src_dot_orders_dot_components_dot_OrderRefundReturnAmount_dot_2256869831": { + "context": "section header", + "string": "Refunded Amount" + }, + "src_dot_orders_dot_components_dot_OrderRefundReturnAmount_dot_40513382": { + "context": "order refund amount, input button", + "string": "Refund {currency} {amount}" + }, + "src_dot_orders_dot_components_dot_OrderRefundReturnAmount_dot_4224226791": { + "context": "label", + "string": "Automatic Amount" + }, + "src_dot_orders_dot_components_dot_OrderRefundReturnAmount_dot_508357513": { + "context": "label", + "string": "Manual Amount" + }, + "src_dot_orders_dot_components_dot_OrderRefundReturnAmount_dot_553737700": { + "context": "checkbox", + "string": "Refund shipment costs" + }, + "src_dot_orders_dot_components_dot_OrderRefundReturnAmount_dot_amountTooBig": { + "context": "Amount error message", + "string": "Amount cannot be bigger than max refund" + }, + "src_dot_orders_dot_components_dot_OrderRefundReturnAmount_dot_amountTooSmall": { + "context": "Amount error message", + "string": "Amount must be bigger than 0" + }, + "src_dot_orders_dot_components_dot_OrderRefundReturnAmount_dot_authorizedAmount": { + "context": "order refund amount", + "string": "Authorized Amount" + }, + "src_dot_orders_dot_components_dot_OrderRefundReturnAmount_dot_label": { + "context": "order refund amount, input label", + "string": "Amount" + }, + "src_dot_orders_dot_components_dot_OrderRefundReturnAmount_dot_maxRefund": { + "context": "order refund amount", + "string": "Max Refund" + }, + "src_dot_orders_dot_components_dot_OrderRefundReturnAmount_dot_previouslyRefunded": { + "context": "order refund amount", + "string": "Previously refunded" + }, + "src_dot_orders_dot_components_dot_OrderRefundReturnAmount_dot_proposedRefundAmount": { + "context": "order refund amount", + "string": "Proposed refund amount" + }, + "src_dot_orders_dot_components_dot_OrderRefundReturnAmount_dot_refundButton": { + "context": "order refund amount button", + "string": "Refund" + }, + "src_dot_orders_dot_components_dot_OrderRefundReturnAmount_dot_refundCannotBeFulfilled": { + "context": "order refund subtitle", + "string": "Refunded items can't be fulfilled" + }, + "src_dot_orders_dot_components_dot_OrderRefundReturnAmount_dot_refundTotalAmount": { + "context": "order refund amount", + "string": "Refund total amount" + }, + "src_dot_orders_dot_components_dot_OrderRefundReturnAmount_dot_replacedProductsValue": { + "context": "order refund amount", + "string": "Replaced Products Value" + }, + "src_dot_orders_dot_components_dot_OrderRefundReturnAmount_dot_returnButton": { + "context": "order return amount button", + "string": "Return & Replace products" + }, + "src_dot_orders_dot_components_dot_OrderRefundReturnAmount_dot_returnCannotBeFulfilled": { + "context": "order return subtitle", + "string": "Returned items can't be fulfilled" + }, + "src_dot_orders_dot_components_dot_OrderRefundReturnAmount_dot_selectedProductsValue": { + "context": "order refund amount", + "string": "Selected Products Value" + }, + "src_dot_orders_dot_components_dot_OrderRefundReturnAmount_dot_shipmentCost": { + "context": "order refund amount", + "string": "Shipment Cost" + }, "src_dot_orders_dot_components_dot_OrderRefundUnfulfilledProducts_dot_1134347598": { "context": "tabel column header", "string": "Price" @@ -3885,6 +3942,82 @@ "context": "refund type", "string": "Refund Products" }, + "src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_1134347598": { + "context": "table column header", + "string": "Price" + }, + "src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_1784788864": { + "context": "table column header", + "string": "Return" + }, + "src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_1895667608": { + "context": "table column header", + "string": "Product" + }, + "src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_2049070632": { + "context": "table column header", + "string": "Replace" + }, + "src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_3988345170": { + "context": "button", + "string": "Set maximal quantities" + }, + "src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_cancelled": { + "context": "cancelled fulfillment, section header", + "string": "Cancelled ({quantity})" + }, + "src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_description": { + "context": "product no longer exists error description", + "string": "This product is no longer in database so it can’t be replaced, nor returned" + }, + "src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_fulfilled": { + "context": "section header", + "string": "Fulfilled ({quantity})" + }, + "src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_improperValue": { + "context": "error message", + "string": "Improper value" + }, + "src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_refunded": { + "context": "refunded fulfillment, section header", + "string": "Refunded ({quantity})" + }, + "src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_refundedAndReturned": { + "context": "cancelled fulfillment, section header", + "string": "Refunded and Returned ({quantity})" + }, + "src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_replaced": { + "context": "refunded fulfillment, section header", + "string": "Replaced ({quantity})" + }, + "src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_returned": { + "context": "refunded fulfillment, section header", + "string": "Returned ({quantity})" + }, + "src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_title": { + "context": "product no longer exists error title", + "string": "Product no longer exists" + }, + "src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_titleFulfilled": { + "context": "section header", + "string": "Fulfillment - #{fulfilmentId}" + }, + "src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_titleUnfulfilled": { + "context": "section header", + "string": "Unfulfilled Items" + }, + "src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_unfulfilled": { + "context": "section header", + "string": "Unfulfilled" + }, + "src_dot_orders_dot_components_dot_OrderReturnPage_dot_appTitle": { + "context": "page header with order number", + "string": "Order #{orderNumber}" + }, + "src_dot_orders_dot_components_dot_OrderReturnPage_dot_pageTitle": { + "context": "page header", + "string": "Order no. {orderNumber} - Replace/Return" + }, "src_dot_orders_dot_components_dot_OrderSettingsPage_dot_1149215359": { "context": "header", "string": "Order settings" @@ -3908,34 +4041,10 @@ "context": "dialog header", "string": "Edit Shipping Method" }, - "src_dot_orders_dot_components_dot_OrderUnfulfilledItems_dot_1134347598": { - "context": "product unit price", - "string": "Price" - }, - "src_dot_orders_dot_components_dot_OrderUnfulfilledItems_dot_1895667608": { - "context": "product name", - "string": "Product" - }, - "src_dot_orders_dot_components_dot_OrderUnfulfilledItems_dot_2095687440": { + "src_dot_orders_dot_components_dot_OrderUnfulfilledProductsCard_dot_2095687440": { "context": "button", "string": "Fulfill" }, - "src_dot_orders_dot_components_dot_OrderUnfulfilledItems_dot_2796503714": { - "context": "ordered products", - "string": "Quantity" - }, - "src_dot_orders_dot_components_dot_OrderUnfulfilledItems_dot_2886647373": { - "context": "section header", - "string": "Unfulfilled ({quantity})" - }, - "src_dot_orders_dot_components_dot_OrderUnfulfilledItems_dot_693960049": { - "context": "ordered product sku", - "string": "SKU" - }, - "src_dot_orders_dot_components_dot_OrderUnfulfilledItems_dot_878013594": { - "context": "order line total price", - "string": "Total" - }, "src_dot_orders_dot_views_dot_OrderDetails_dot_1039259580": { "string": "We’re generating the invoice you requested. Please wait a couple of moments" }, @@ -4025,6 +4134,18 @@ "context": "order refunded success message", "string": "Refunded Items" }, + "src_dot_orders_dot_views_dot_OrderReturn_dot_cannotRefundDescription": { + "context": "order return error description when cannot refund", + "string": "We’ve encountered a problem while refunding the products. Product’s were not refunded. Please try again." + }, + "src_dot_orders_dot_views_dot_OrderReturn_dot_cannotRefundTitle": { + "context": "order return error title when cannot refund", + "string": "Couldn't refund products" + }, + "src_dot_orders_dot_views_dot_OrderReturn_dot_successAlert": { + "context": "order returned success message", + "string": "Successfully returned products!" + }, "src_dot_pageTypes": { "context": "page types section name", "string": "Page Types" @@ -4271,6 +4392,10 @@ "context": "payment status", "string": "Partially refunded" }, + "src_dot_partiallyReturned": { + "context": "order status", + "string": "Partially returned" + }, "src_dot_permissionGroups": { "context": "permission groups section name", "string": "Permission Groups" @@ -5306,6 +5431,10 @@ "src_dot_requiredField": { "string": "This field is required" }, + "src_dot_returned": { + "context": "order status", + "string": "Returned" + }, "src_dot_sales": { "context": "sales section name", "string": "Sales" diff --git a/schema.graphql b/schema.graphql index e72a6545a..e3f8eb49f 100644 --- a/schema.graphql +++ b/schema.graphql @@ -2006,9 +2006,21 @@ type FulfillmentRefundProducts { orderErrors: [OrderError!]! } +type FulfillmentReturnProducts { + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") + returnFulfillment: Fulfillment + replaceFulfillment: Fulfillment + order: Order + replaceOrder: Order + orderErrors: [OrderError!]! +} + enum FulfillmentStatus { FULFILLED REFUNDED + RETURNED + REPLACED + REFUNDED_AND_RETURNED CANCELED } @@ -2662,6 +2674,7 @@ type Mutation { orderFulfillmentCancel(id: ID!, input: FulfillmentCancelInput!): FulfillmentCancel orderFulfillmentUpdateTracking(id: ID!, input: FulfillmentUpdateTrackingInput!): FulfillmentUpdateTracking orderFulfillmentRefundProducts(input: OrderRefundProductsInput!, order: ID!): FulfillmentRefundProducts + orderFulfillmentReturnProducts(input: OrderReturnProductsInput!, order: ID!): FulfillmentReturnProducts orderMarkAsPaid(id: ID!, transactionReference: String): OrderMarkAsPaid orderRefund(amount: PositiveDecimal!, id: ID!): OrderRefund orderUpdate(id: ID!, input: OrderUpdateInput!): OrderUpdate @@ -2959,8 +2972,7 @@ enum OrderErrorCode { UNIQUE VOID_INACTIVE_PAYMENT ZERO_QUANTITY - INVALID_REFUND_QUANTITY - CANNOT_REFUND_FULFILLMENT_LINE + INVALID_QUANTITY INSUFFICIENT_STOCK DUPLICATED_INPUT_ITEM NOT_AVAILABLE_IN_CHANNEL @@ -2988,6 +3000,7 @@ type OrderEvent implements Node { warehouse: Warehouse transactionReference: String shippingCostsIncluded: Boolean + relatedOrder: Order } type OrderEventCountableConnection { @@ -3021,6 +3034,7 @@ enum OrderEventsEmailsEnum { enum OrderEventsEnum { DRAFT_CREATED + DRAFT_CREATED_FROM_REPLACE DRAFT_ADDED_PRODUCTS DRAFT_REMOVED_PRODUCTS PLACED @@ -3029,6 +3043,7 @@ enum OrderEventsEnum { CANCELED ORDER_MARKED_AS_PAID ORDER_FULLY_PAID + ORDER_REPLACEMENT_CREATED UPDATED_ADDRESS EMAIL_SENT CONFIRMED @@ -3046,6 +3061,8 @@ enum OrderEventsEnum { FULFILLMENT_RESTOCKED_ITEMS FULFILLMENT_FULFILLED_ITEMS FULFILLMENT_REFUNDED + FULFILLMENT_RETURNED + FULFILLMENT_REPLACED TRACKING_UPDATED NOTE_ADDED OTHER @@ -3139,6 +3156,26 @@ input OrderRefundProductsInput { includeShippingCosts: Boolean = false } +input OrderReturnFulfillmentLineInput { + fulfillmentLineId: ID! + quantity: Int! + replace: Boolean = false +} + +input OrderReturnLineInput { + orderLineId: ID! + quantity: Int! + replace: Boolean = false +} + +input OrderReturnProductsInput { + orderLines: [OrderReturnLineInput!] + fulfillmentLines: [OrderReturnFulfillmentLineInput!] + amountToRefund: PositiveDecimal + includeShippingCosts: Boolean = false + refund: Boolean = false +} + type OrderSettings { automaticallyConfirmAllNewOrders: Boolean! } @@ -3181,6 +3218,8 @@ enum OrderStatus { UNCONFIRMED UNFULFILLED PARTIALLY_FULFILLED + PARTIALLY_RETURNED + RETURNED FULFILLED CANCELED } @@ -5819,4 +5858,4 @@ union _Entity = Address | User | Group | App | ProductVariant | Product | Produc type _Service { sdl: String -} +} \ No newline at end of file diff --git a/src/components/StatusLabel/StatusLabel.tsx b/src/components/StatusLabel/StatusLabel.tsx index 1838ac014..22f301ebb 100644 --- a/src/components/StatusLabel/StatusLabel.tsx +++ b/src/components/StatusLabel/StatusLabel.tsx @@ -19,11 +19,14 @@ const useStyles = makeStyles( }; return { + alertDot: { + "&:before": { backgroundColor: yellow[500], ...dot } + }, errorDot: { "&:before": { backgroundColor: theme.palette.error.main, ...dot } }, neutralDot: { - "&:before": { backgroundColor: yellow[500], ...dot } + "&:before": { backgroundColor: grey[300], ...dot } }, root: { display: "inline-block", @@ -35,9 +38,6 @@ const useStyles = makeStyles( }, successDot: { "&:before": { backgroundColor: theme.palette.primary.main, ...dot } - }, - unspecifiedDot: { - "&:before": { backgroundColor: grey[500], ...dot } } }; }, @@ -47,7 +47,7 @@ const useStyles = makeStyles( interface StatusLabelProps { className?: string; label: string | React.ReactNode; - status: "success" | "neutral" | "unspecified" | "error" | string; + status: "success" | "alert" | "neutral" | "error" | string; typographyProps?: TypographyProps; } @@ -62,8 +62,8 @@ const StatusLabel: React.FC = props => { [classes.root]: true, [className]: true, [classes.successDot]: status === "success", + [classes.alertDot]: status === "alert", [classes.neutralDot]: status === "neutral", - [classes.unspecifiedDot]: status === "unspecified", [classes.errorDot]: status === "error" })} > diff --git a/src/components/Theme/themes.ts b/src/components/Theme/themes.ts index 8a17c1bbf..2bd24b3ba 100644 --- a/src/components/Theme/themes.ts +++ b/src/components/Theme/themes.ts @@ -77,7 +77,7 @@ export const light: IThemeColors = { default: "#616161" }, divider: "#EAEAEA", - error: "#C22D74", + error: "#FE6D76", font: { button: "#FFFFFF", default: "#3D3D3D", diff --git a/src/components/Timeline/TimelineEvent.tsx b/src/components/Timeline/TimelineEvent.tsx index 49c4972cc..d379d6eb9 100644 --- a/src/components/Timeline/TimelineEvent.tsx +++ b/src/components/Timeline/TimelineEvent.tsx @@ -4,30 +4,12 @@ import ExpansionPanelSummary from "@material-ui/core/ExpansionPanelSummary"; import { makeStyles } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; import ExpandMoreIcon from "@material-ui/icons/ExpandMore"; -import classNames from "classnames"; import React from "react"; -import { DateTime } from "../Date"; +import TimelineEventHeader, { TitleElement } from "./TimelineEventHeader"; const useStyles = makeStyles( theme => ({ - container: { - alignItems: "center", - display: "flex", - flexDirection: "column", - justifyContent: "space-between", - marginBottom: theme.spacing(), - marginLeft: theme.spacing(3), - width: "100%" - }, - date: { - color: theme.typography.caption.color - }, - dateExpander: { - color: theme.typography.caption.color, - position: "absolute", - right: 0 - }, dot: { backgroundColor: theme.palette.primary.main, borderRadius: "100%", @@ -37,27 +19,27 @@ const useStyles = makeStyles( top: 6, width: 8 }, - expanded: {}, - noExpander: { - alignItems: "center", - display: "flex", - justifyContent: "space-between", - width: "100%" - }, panel: { - "&$expanded": { - margin: 0 + "& .MuiExpansionPanelDetails-root": { + padding: 0, + paddingTop: theme.spacing(2) + }, + "&.Mui-expanded": { + borderColor: "red", + margin: 0, + minHeight: 0 }, "&:before": { display: "none" }, background: "none", + display: "", margin: 0, + minHeight: 0, width: "100%" }, panelExpander: { - "&$expanded": { - margin: 0, + "&.MuiExpansionPanelSummary-root.Mui-expanded": { minHeight: 0 }, "&> .MuiExpansionPanelSummary-content": { @@ -65,9 +47,12 @@ const useStyles = makeStyles( }, "&> .MuiExpansionPanelSummary-expandIcon": { padding: 0, + position: "absolute", right: theme.spacing(18) }, - margin: 0 + margin: 0, + minHeight: 0, + padding: 0 }, root: { "&:last-child:after": { @@ -82,27 +67,24 @@ const useStyles = makeStyles( alignItems: "center", display: "flex", marginBottom: theme.spacing(3), + marginTop: 0, position: "relative", width: "100%" - }, - secondaryTitle: { - color: "#9e9e9e", - fontSize: 14, - marginTop: theme.spacing(2) } }), { name: "TimelineEvent" } ); -interface TimelineEventProps { +export interface TimelineEventProps { children?: React.ReactNode; date: string; secondaryTitle?: string; - title: string; + title?: string; + titleElements?: TitleElement[]; } export const TimelineEvent: React.FC = props => { - const { children, date, secondaryTitle, title } = props; + const { children, date, secondaryTitle, title, titleElements } = props; const classes = useStyles(props); @@ -110,39 +92,28 @@ export const TimelineEvent: React.FC = props => {
{children ? ( - + } > - {title} - - - + {children} ) : ( -
-
- {title} - - - -
- {secondaryTitle && ( -
- - {secondaryTitle} - -
- )} -
+ )}
); diff --git a/src/components/Timeline/TimelineEventHeader.tsx b/src/components/Timeline/TimelineEventHeader.tsx new file mode 100644 index 000000000..8742a23a8 --- /dev/null +++ b/src/components/Timeline/TimelineEventHeader.tsx @@ -0,0 +1,93 @@ +import { makeStyles } from "@material-ui/core/styles"; +import Typography from "@material-ui/core/Typography"; +import useNavigator from "@saleor/hooks/useNavigator"; +import React from "react"; + +import { DateTime } from "../Date"; +import Link from "../Link"; + +const useStyles = makeStyles( + theme => ({ + container: { + alignItems: "center", + display: "flex", + flexDirection: "row", + justifyContent: "space-between", + width: "100%" + }, + date: { + color: theme.typography.caption.color, + paddingLeft: 24 + }, + elementsContainer: { + alignItems: "center", + display: "flex", + flexDirection: "row", + flexWrap: "wrap" + }, + secondaryTitle: { + color: "#9e9e9e", + fontSize: 14, + marginTop: theme.spacing(2) + }, + titleElement: { + marginRight: "0.5rem" + } + }), + { name: "TimelineEventHeader" } +); + +export interface TitleElement { + text: string; + link?: string; +} + +export interface TimelineEventHeaderProps { + title?: string; + date: string; + titleElements?: TitleElement[]; + secondaryTitle?: string; +} + +export const TimelineEventHeader: React.FC = props => { + const { title, date, titleElements, secondaryTitle } = props; + const navigate = useNavigator(); + + const classes = useStyles(props); + + return ( +
+ {title && {title}} + {titleElements && ( +
+ {titleElements.map(({ text, link }) => { + if (link) { + return ( + navigate(link)} + > + {text} + + ); + } + + return ( + {text} + ); + })} +
+ )} + + + + {secondaryTitle && ( + + {secondaryTitle} + + )} +
+ ); +}; + +export default TimelineEventHeader; diff --git a/src/fragments/orders.ts b/src/fragments/orders.ts index ab11173a5..3ae5427eb 100644 --- a/src/fragments/orders.ts +++ b/src/fragments/orders.ts @@ -12,6 +12,10 @@ export const fragmentOrderEvent = gql` email emailType invoiceNumber + relatedOrder { + id + number + } message quantity transactionReference @@ -19,6 +23,8 @@ export const fragmentOrderEvent = gql` user { id email + firstName + lastName } lines { quantity diff --git a/src/fragments/types/OrderDetailsFragment.ts b/src/fragments/types/OrderDetailsFragment.ts index cf1a2b0d5..104e2109f 100644 --- a/src/fragments/types/OrderDetailsFragment.ts +++ b/src/fragments/types/OrderDetailsFragment.ts @@ -42,10 +42,18 @@ export interface OrderDetailsFragment_billingAddress { streetAddress2: string; } +export interface OrderDetailsFragment_events_relatedOrder { + __typename: "Order"; + id: string; + number: string | null; +} + export interface OrderDetailsFragment_events_user { __typename: "User"; id: string; email: string; + firstName: string; + lastName: string; } export interface OrderDetailsFragment_events_lines_orderLine { @@ -70,6 +78,7 @@ export interface OrderDetailsFragment_events { email: string | null; emailType: OrderEventsEmailsEnum | null; invoiceNumber: string | null; + relatedOrder: OrderDetailsFragment_events_relatedOrder | null; message: string | null; quantity: number | null; transactionReference: string | null; diff --git a/src/fragments/types/OrderEventFragment.ts b/src/fragments/types/OrderEventFragment.ts index 613a9cce0..c2ce63beb 100644 --- a/src/fragments/types/OrderEventFragment.ts +++ b/src/fragments/types/OrderEventFragment.ts @@ -8,10 +8,18 @@ import { OrderEventsEmailsEnum, OrderEventsEnum } from "./../../types/globalType // GraphQL fragment: OrderEventFragment // ==================================================== +export interface OrderEventFragment_relatedOrder { + __typename: "Order"; + id: string; + number: string | null; +} + export interface OrderEventFragment_user { __typename: "User"; id: string; email: string; + firstName: string; + lastName: string; } export interface OrderEventFragment_lines_orderLine { @@ -36,6 +44,7 @@ export interface OrderEventFragment { email: string | null; emailType: OrderEventsEmailsEnum | null; invoiceNumber: string | null; + relatedOrder: OrderEventFragment_relatedOrder | null; message: string | null; quantity: number | null; transactionReference: string | null; diff --git a/src/icons/ErrorExclamationCircle.tsx b/src/icons/ErrorExclamationCircle.tsx new file mode 100644 index 000000000..bfda916c5 --- /dev/null +++ b/src/icons/ErrorExclamationCircle.tsx @@ -0,0 +1,25 @@ +import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; +import React from "react"; + +const ErrorExclamationCircle = createSvgIcon( + <> + + + + + , + "ErrorExclamationCircle" +); + +export default ErrorExclamationCircle; diff --git a/src/misc.ts b/src/misc.ts index 5a7e58d15..2c6946e4c 100644 --- a/src/misc.ts +++ b/src/misc.ts @@ -136,6 +136,10 @@ export const orderStatusMessages = defineMessages({ defaultMessage: "Partially fulfilled", description: "order status" }, + partiallyReturned: { + defaultMessage: "Partially returned", + description: "order status" + }, readyToCapture: { defaultMessage: "Ready to capture", description: "order status" @@ -144,6 +148,10 @@ export const orderStatusMessages = defineMessages({ defaultMessage: "Ready to fulfill", description: "order status" }, + returned: { + defaultMessage: "Returned", + description: "order status" + }, unconfirmed: { defaultMessage: "Unconfirmed", description: "order status" @@ -189,6 +197,16 @@ export const transformOrderStatus = ( localized: intl.formatMessage(orderStatusMessages.unconfirmed), status: StatusType.NEUTRAL }; + case OrderStatus.PARTIALLY_RETURNED: + return { + localized: intl.formatMessage(orderStatusMessages.partiallyReturned), + status: StatusType.NEUTRAL + }; + case OrderStatus.RETURNED: + return { + localized: intl.formatMessage(orderStatusMessages.returned), + status: StatusType.NEUTRAL + }; } return { localized: status, diff --git a/src/orders/components/OrderDetailsPage/OrderDetailsPage.tsx b/src/orders/components/OrderDetailsPage/OrderDetailsPage.tsx index 4fe700e72..ddaaedb23 100644 --- a/src/orders/components/OrderDetailsPage/OrderDetailsPage.tsx +++ b/src/orders/components/OrderDetailsPage/OrderDetailsPage.tsx @@ -19,19 +19,20 @@ import { UserPermissionProps } from "@saleor/types"; import { mapMetadataItemToInput } from "@saleor/utils/maps"; import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger"; import React from "react"; -import { useIntl } from "react-intl"; +import { defineMessages, useIntl } from "react-intl"; -import { maybe, renderCollection } from "../../../misc"; +import { maybe } from "../../../misc"; import { OrderStatus } from "../../../types/globalTypes"; import { OrderDetails_order } from "../../types/OrderDetails"; import OrderCustomer from "../OrderCustomer"; import OrderCustomerNote from "../OrderCustomerNote"; -import OrderFulfillment from "../OrderFulfillment"; +import OrderFulfilledProductsCard from "../OrderFulfilledProductsCard"; import OrderHistory, { FormData as HistoryFormData } from "../OrderHistory"; import OrderInvoiceList from "../OrderInvoiceList"; import OrderPayment from "../OrderPayment/OrderPayment"; -import OrderUnfulfilledItems from "../OrderUnfulfilledItems/OrderUnfulfilledItems"; +import OrderUnfulfilledProductsCard from "../OrderUnfulfilledProductsCard"; import Title from "./Title"; +import { filteredConditionalItems, hasAnyItemsReplaceable } from "./utils"; const useStyles = makeStyles( theme => ({ @@ -75,12 +76,28 @@ export interface OrderDetailsPageProps extends UserPermissionProps { onOrderCancel(); onNoteAdd(data: HistoryFormData); onProfileView(); + onOrderReturn(); onInvoiceClick(invoiceId: string); onInvoiceGenerate(); onInvoiceSend(invoiceId: string); onSubmit(data: MetadataFormData): SubmitPromise; } +const messages = defineMessages({ + cancelOrder: { + defaultMessage: "Cancel order", + description: "cancel button" + }, + confirmOrder: { + defaultMessage: "Confirm order", + description: "save button" + }, + returnOrder: { + defaultMessage: "Return / Replace order", + description: "return button" + } +}); + const OrderDetailsPage: React.FC = props => { const { disabled, @@ -103,6 +120,7 @@ const OrderDetailsPage: React.FC = props => { onInvoiceClick, onInvoiceGenerate, onInvoiceSend, + onOrderReturn, onSubmit } = props; const classes = useStyles(props); @@ -140,10 +158,7 @@ const OrderDetailsPage: React.FC = props => { const saveLabel = order?.status === OrderStatus.UNCONFIRMED - ? intl.formatMessage({ - defaultMessage: "confirm order", - description: "save button" - }) + ? intl.formatMessage(messages.confirmOrder) : undefined; const allowSave = (hasChanged: boolean) => { @@ -154,6 +169,23 @@ const OrderDetailsPage: React.FC = props => { return disabled; }; + const selectCardMenuItems = filteredConditionalItems([ + { + item: { + label: intl.formatMessage(messages.cancelOrder), + onSelect: onOrderCancel + }, + shouldExist: canCancel + }, + { + item: { + label: intl.formatMessage(messages.returnOrder), + onSelect: onOrderReturn + }, + shouldExist: hasAnyItemsReplaceable(order) + } + ]); + return (
{({ change, data, hasChanged, submit }) => { @@ -169,19 +201,7 @@ const OrderDetailsPage: React.FC = props => { inline title={} > - {canCancel && ( - <CardMenu - menuItems={[ - { - label: intl.formatMessage({ - defaultMessage: "Cancel order", - description: "button" - }), - onSelect: onOrderCancel - } - ]} - /> - )} + <CardMenu menuItems={selectCardMenuItems} /> </PageHeader> <div className={classes.date}> {order && order.created ? ( @@ -194,36 +214,26 @@ const OrderDetailsPage: React.FC<OrderDetailsPageProps> = props => { </div> <Grid> <div> - {unfulfilled.length > 0 && ( - <OrderUnfulfilledItems - canFulfill={canFulfill} - lines={unfulfilled} - onFulfill={onOrderFulfill} - /> - )} - {renderCollection( - maybe(() => order.fulfillments), - (fulfillment, fulfillmentIndex) => ( - <React.Fragment - key={maybe(() => fulfillment.id, "loading")} - > - {!( - unfulfilled.length === 0 && fulfillmentIndex === 0 - ) && <CardSpacer />} - <OrderFulfillment - fulfillment={fulfillment} - orderNumber={maybe(() => order.number)} - onOrderFulfillmentCancel={() => - onFulfillmentCancel(fulfillment.id) - } - onTrackingCodeAdd={() => - onFulfillmentTrackingNumberUpdate(fulfillment.id) - } - /> - </React.Fragment> - ) - )} - <CardSpacer /> + <OrderUnfulfilledProductsCard + canFulfill={canFulfill} + lines={unfulfilled} + onFulfill={onOrderFulfill} + /> + {order?.fulfillments?.map(fulfillment => ( + <React.Fragment key={fulfillment.id}> + <OrderFulfilledProductsCard + fulfillment={fulfillment} + orderNumber={order.number} + onOrderFulfillmentCancel={() => + onFulfillmentCancel(fulfillment.id) + } + onTrackingCodeAdd={() => + onFulfillmentTrackingNumberUpdate(fulfillment.id) + } + onRefund={onPaymentRefund} + /> + </React.Fragment> + ))} <OrderPayment order={order} onCapture={onPaymentCapture} @@ -281,5 +291,6 @@ const OrderDetailsPage: React.FC<OrderDetailsPageProps> = props => { </Form> ); }; + OrderDetailsPage.displayName = "OrderDetailsPage"; export default OrderDetailsPage; diff --git a/src/orders/components/OrderDetailsPage/utils.test.tsx b/src/orders/components/OrderDetailsPage/utils.test.tsx new file mode 100644 index 000000000..2637ebe52 --- /dev/null +++ b/src/orders/components/OrderDetailsPage/utils.test.tsx @@ -0,0 +1,44 @@ +import { filteredConditionalItems } from "./utils"; + +describe("filteredConditionalItems", () => { + it("should return empty [] when no items has shouldExist set to true", () => { + const items = [ + { + item: { id: "#1" }, + shouldExist: false + }, + { + item: { id: "#2" }, + shouldExist: false + }, + { + item: { id: "#3" }, + shouldExist: false + } + ]; + + expect(filteredConditionalItems(items)).toEqual([]); + }); + + it("should return only items that has shouldExist set to true", () => { + const items = [ + { + item: { id: "#1" }, + shouldExist: false + }, + { + item: { id: "#2" }, + shouldExist: true + }, + { + item: { id: "#3" }, + shouldExist: true + } + ]; + + expect(filteredConditionalItems(items)).toEqual([ + { id: "#2" }, + { id: "#3" } + ]); + }); +}); diff --git a/src/orders/components/OrderDetailsPage/utils.ts b/src/orders/components/OrderDetailsPage/utils.ts new file mode 100644 index 000000000..62dda66f8 --- /dev/null +++ b/src/orders/components/OrderDetailsPage/utils.ts @@ -0,0 +1,26 @@ +import { OrderDetails_order } from "@saleor/orders/types/OrderDetails"; + +import { + getFulfilledFulfillemnts, + getUnfulfilledLines +} from "../OrderReturnPage/utils"; + +export const hasAnyItemsReplaceable = (order?: OrderDetails_order) => { + if (!order) { + return false; + } + + const hasAnyUnfulfilledItems = getUnfulfilledLines(order).length > 0; + + const hasAnyFulfilmentsToReturn = getFulfilledFulfillemnts(order).length > 0; + + return hasAnyUnfulfilledItems || hasAnyFulfilmentsToReturn; +}; + +export interface ConditionalItem { + shouldExist: boolean; + item: any; +} + +export const filteredConditionalItems = (items: ConditionalItem[]) => + items.filter(({ shouldExist }) => shouldExist).map(({ item }) => item); diff --git a/src/orders/components/OrderFulfilledProductsCard/ActionButtons.tsx b/src/orders/components/OrderFulfilledProductsCard/ActionButtons.tsx new file mode 100644 index 000000000..65a90e143 --- /dev/null +++ b/src/orders/components/OrderFulfilledProductsCard/ActionButtons.tsx @@ -0,0 +1,64 @@ +import { Button, CardActions } from "@material-ui/core"; +import { FulfillmentStatus } from "@saleor/types/globalTypes"; +import React from "react"; +import { FormattedMessage } from "react-intl"; + +interface AcionButtonsProps { + status: FulfillmentStatus; + trackingNumber?: string; + onTrackingCodeAdd(); + onRefund(); +} + +const statusesToShow = [ + FulfillmentStatus.FULFILLED, + FulfillmentStatus.RETURNED +]; + +const ActionButtons: React.FC<AcionButtonsProps> = ({ + status, + onTrackingCodeAdd, + trackingNumber, + onRefund +}) => { + const hasTrackingNumber = !!trackingNumber; + + if (!statusesToShow.includes(status)) { + return null; + } + + if (status === FulfillmentStatus.RETURNED) { + return ( + <CardActions> + <Button color="primary" onClick={onRefund}> + <FormattedMessage + defaultMessage="Refund" + description="refund button" + /> + </Button> + </CardActions> + ); + } + + return hasTrackingNumber ? ( + <CardActions> + <Button color="primary" onClick={onTrackingCodeAdd}> + <FormattedMessage + defaultMessage="Edit tracking" + description="edit tracking button" + /> + </Button> + </CardActions> + ) : ( + <CardActions> + <Button color="primary" onClick={onTrackingCodeAdd}> + <FormattedMessage + defaultMessage="Add tracking" + description="add tracking button" + /> + </Button> + </CardActions> + ); +}; + +export default ActionButtons; diff --git a/src/orders/components/OrderFulfilledProductsCard/ExtraInfoLines.tsx b/src/orders/components/OrderFulfilledProductsCard/ExtraInfoLines.tsx new file mode 100644 index 000000000..69813e9cc --- /dev/null +++ b/src/orders/components/OrderFulfilledProductsCard/ExtraInfoLines.tsx @@ -0,0 +1,103 @@ +import { makeStyles, TableCell, TableRow, Typography } from "@material-ui/core"; +import { getStringOrPlaceholder } from "@saleor/misc"; +import { FulfillmentStatus } from "@saleor/types/globalTypes"; +import classNames from "classnames"; +import React from "react"; +import { defineMessages, useIntl } from "react-intl"; +import { FormattedMessage } from "react-intl"; + +import { OrderDetails_order_fulfillments } from "../../types/OrderDetails"; + +const useStyles = makeStyles( + theme => ({ + infoLabel: { + display: "inline-block" + }, + infoLabelWithMargin: { + marginBottom: theme.spacing() + }, + infoRow: { + padding: theme.spacing(2, 3) + } + }), + { name: "ExtraInfoLines" } +); + +const messages = defineMessages({ + fulfilled: { + defaultMessage: "Fulfilled from: ", + description: "fulfillment group" + }, + restocked: { + defaultMessage: "Restocked from: ", + description: "restocked group" + }, + tracking: { + defaultMessage: "Tracking Number: {trackingNumber}", + description: "tracking number" + } +}); + +const NUMBER_OF_COLUMNS = 5; + +interface ExtraInfoLinesProps { + fulfillment?: OrderDetails_order_fulfillments; +} + +const ExtraInfoLines: React.FC<ExtraInfoLinesProps> = ({ fulfillment }) => { + const intl = useIntl(); + const classes = useStyles({}); + + if (!fulfillment || !fulfillment?.warehouse || !fulfillment?.trackingNumber) { + return null; + } + + const { warehouse, trackingNumber, status } = fulfillment; + + return ( + <TableRow> + <TableCell className={classes.infoRow} colSpan={NUMBER_OF_COLUMNS}> + <Typography color="textSecondary" variant="body2"> + {warehouse && ( + <> + {intl.formatMessage( + status === FulfillmentStatus.RETURNED + ? messages.restocked + : messages.fulfilled + )} + <Typography + className={classNames(classes.infoLabel, { + [classes.infoLabelWithMargin]: !!trackingNumber + })} + color="textPrimary" + variant="body2" + > + {getStringOrPlaceholder(warehouse?.name)} + </Typography> + </> + )} + </Typography> + <Typography color="textSecondary" variant="body2"> + {trackingNumber && ( + <FormattedMessage + defaultMessage="Tracking Number: {trackingNumber}" + values={{ + trackingNumber: ( + <Typography + className={classes.infoLabel} + color="textPrimary" + variant="body2" + > + {trackingNumber} + </Typography> + ) + }} + /> + )} + </Typography> + </TableCell> + </TableRow> + ); +}; + +export default ExtraInfoLines; diff --git a/src/orders/components/OrderFulfilledProductsCard/OrderFulfilledProductsCard.tsx b/src/orders/components/OrderFulfilledProductsCard/OrderFulfilledProductsCard.tsx new file mode 100644 index 000000000..97bc623b7 --- /dev/null +++ b/src/orders/components/OrderFulfilledProductsCard/OrderFulfilledProductsCard.tsx @@ -0,0 +1,114 @@ +import Card from "@material-ui/core/Card"; +import { makeStyles } from "@material-ui/core/styles"; +import TableBody from "@material-ui/core/TableBody"; +import CardMenu from "@saleor/components/CardMenu"; +import CardSpacer from "@saleor/components/CardSpacer"; +import ResponsiveTable from "@saleor/components/ResponsiveTable"; +import { mergeRepeatedOrderLines } from "@saleor/orders/utils/data"; +import React from "react"; +import { useIntl } from "react-intl"; + +import { maybe, renderCollection } from "../../../misc"; +import { FulfillmentStatus } from "../../../types/globalTypes"; +import { OrderDetails_order_fulfillments } from "../../types/OrderDetails"; +import TableHeader from "../OrderProductsCardElements/OrderProductsCardHeader"; +import TableLine from "../OrderProductsCardElements/OrderProductsTableRow"; +import CardTitle from "../OrderReturnPage/OrderReturnRefundItemsCard/CardTitle"; +import ActionButtons from "./ActionButtons"; +import ExtraInfoLines from "./ExtraInfoLines"; + +const useStyles = makeStyles( + () => ({ + table: { + tableLayout: "fixed" + } + }), + { name: "OrderFulfillment" } +); + +interface OrderFulfilledProductsCardProps { + fulfillment: OrderDetails_order_fulfillments; + orderNumber?: string; + onOrderFulfillmentCancel: () => void; + onTrackingCodeAdd: () => void; + onRefund: () => void; +} + +const OrderFulfilledProductsCard: React.FC<OrderFulfilledProductsCardProps> = props => { + const { + fulfillment, + orderNumber, + onOrderFulfillmentCancel, + onTrackingCodeAdd, + onRefund + } = props; + const classes = useStyles(props); + + const intl = useIntl(); + + if (!fulfillment) { + return null; + } + + const getLines = () => { + const statusesToMergeLines = [ + FulfillmentStatus.REFUNDED, + FulfillmentStatus.REFUNDED_AND_RETURNED, + FulfillmentStatus.RETURNED, + FulfillmentStatus.REPLACED + ]; + + if (statusesToMergeLines.includes(fulfillment?.status)) { + return mergeRepeatedOrderLines(fulfillment.lines); + } + + return fulfillment?.lines || []; + }; + + return ( + <> + <Card> + <CardTitle + withStatus + lines={fulfillment?.lines} + fulfillmentOrder={fulfillment?.fulfillmentOrder} + status={fulfillment?.status} + orderNumber={orderNumber} + toolbar={ + maybe(() => fulfillment.status) === FulfillmentStatus.FULFILLED && ( + <CardMenu + menuItems={[ + { + label: intl.formatMessage({ + defaultMessage: "Cancel Fulfillment", + description: "button" + }), + onSelect: onOrderFulfillmentCancel + } + ]} + /> + ) + } + /> + <ResponsiveTable className={classes.table}> + <TableHeader /> + <TableBody> + {renderCollection(getLines(), line => ( + <TableLine line={line} /> + ))} + </TableBody> + <ExtraInfoLines fulfillment={fulfillment} /> + </ResponsiveTable> + <ActionButtons + status={fulfillment?.status} + trackingNumber={fulfillment?.trackingNumber} + onTrackingCodeAdd={onTrackingCodeAdd} + onRefund={onRefund} + /> + </Card> + <CardSpacer /> + </> + ); +}; + +export default OrderFulfilledProductsCard; diff --git a/src/orders/components/OrderFulfilledProductsCard/index.ts b/src/orders/components/OrderFulfilledProductsCard/index.ts new file mode 100644 index 000000000..fea64ef8f --- /dev/null +++ b/src/orders/components/OrderFulfilledProductsCard/index.ts @@ -0,0 +1,2 @@ +export { default } from "./OrderFulfilledProductsCard"; +export * from "./OrderFulfilledProductsCard"; diff --git a/src/orders/components/OrderFulfillment/OrderFulfillment.tsx b/src/orders/components/OrderFulfillment/OrderFulfillment.tsx deleted file mode 100644 index b05fa0997..000000000 --- a/src/orders/components/OrderFulfillment/OrderFulfillment.tsx +++ /dev/null @@ -1,333 +0,0 @@ -import Button from "@material-ui/core/Button"; -import Card from "@material-ui/core/Card"; -import CardActions from "@material-ui/core/CardActions"; -import { makeStyles } from "@material-ui/core/styles"; -import TableBody from "@material-ui/core/TableBody"; -import TableCell from "@material-ui/core/TableCell"; -import TableHead from "@material-ui/core/TableHead"; -import TableRow from "@material-ui/core/TableRow"; -import Typography from "@material-ui/core/Typography"; -import CardMenu from "@saleor/components/CardMenu"; -import CardTitle from "@saleor/components/CardTitle"; -import Money from "@saleor/components/Money"; -import ResponsiveTable from "@saleor/components/ResponsiveTable"; -import Skeleton from "@saleor/components/Skeleton"; -import StatusLabel from "@saleor/components/StatusLabel"; -import TableCellAvatar, { - AVATAR_MARGIN -} from "@saleor/components/TableCellAvatar"; -import { mergeRepeatedOrderLines } from "@saleor/orders/utils/data"; -import classNames from "classnames"; -import React from "react"; -import { FormattedMessage, useIntl } from "react-intl"; - -import { getStringOrPlaceholder, maybe, renderCollection } from "../../../misc"; -import { FulfillmentStatus } from "../../../types/globalTypes"; -import { OrderDetails_order_fulfillments } from "../../types/OrderDetails"; - -const useStyles = makeStyles( - theme => ({ - clickableRow: { - cursor: "pointer" - }, - colName: { - width: "auto" - }, - colNameLabel: { - marginLeft: AVATAR_MARGIN - }, - colPrice: { - textAlign: "right", - width: 120 - }, - colQuantity: { - textAlign: "center", - width: 120 - }, - colSku: { - textAlign: "right", - textOverflow: "ellipsis", - width: 120 - }, - colTotal: { - textAlign: "right", - width: 120 - }, - infoLabel: { - display: "inline-block" - }, - infoLabelWithMargin: { - marginBottom: theme.spacing() - }, - infoRow: { - padding: theme.spacing(2, 3) - }, - orderNumber: { - display: "inline", - marginLeft: theme.spacing(1) - }, - statusBar: { - paddingTop: 0 - }, - table: { - tableLayout: "fixed" - } - }), - { name: "OrderFulfillment" } -); - -interface OrderFulfillmentProps { - fulfillment: OrderDetails_order_fulfillments; - orderNumber: string; - onOrderFulfillmentCancel: () => void; - onTrackingCodeAdd: () => void; -} - -const numberOfColumns = 5; - -const OrderFulfillment: React.FC<OrderFulfillmentProps> = props => { - const { - fulfillment, - orderNumber, - onOrderFulfillmentCancel, - onTrackingCodeAdd - } = props; - const classes = useStyles(props); - - const intl = useIntl(); - - const lines = - fulfillment?.status === FulfillmentStatus.REFUNDED - ? mergeRepeatedOrderLines(fulfillment?.lines) - : fulfillment?.lines; - const status = maybe(() => fulfillment.status); - const quantity = lines - ? lines.map(line => line.quantity).reduce((prev, curr) => prev + curr, 0) - : "..."; - - return ( - <Card> - <CardTitle - title={ - !!lines ? ( - <StatusLabel - label={ - <> - {status === FulfillmentStatus.FULFILLED - ? intl.formatMessage( - { - defaultMessage: "Fulfilled ({quantity})", - description: "section header" - }, - { - quantity - } - ) - : status === FulfillmentStatus.REFUNDED - ? intl.formatMessage( - { - defaultMessage: "Refunded ({quantity})", - description: "refunded fulfillment, section header" - }, - { - quantity - } - ) - : intl.formatMessage( - { - defaultMessage: "Cancelled ({quantity})", - description: "cancelled fulfillment, section header" - }, - { - quantity - } - )} - <Typography className={classes.orderNumber} variant="body1"> - {maybe( - () => `#${orderNumber}-${fulfillment.fulfillmentOrder}` - )} - </Typography> - </> - } - status={ - status === FulfillmentStatus.FULFILLED - ? "success" - : status === FulfillmentStatus.REFUNDED - ? "unspecified" - : "error" - } - /> - ) : ( - <Skeleton /> - ) - } - toolbar={ - maybe(() => fulfillment.status) === FulfillmentStatus.FULFILLED && ( - <CardMenu - menuItems={[ - { - label: intl.formatMessage({ - defaultMessage: "Cancel Fulfillment", - description: "button" - }), - onSelect: onOrderFulfillmentCancel - } - ]} - /> - ) - } - /> - <ResponsiveTable className={classes.table}> - <TableHead> - <TableRow> - <TableCell className={classes.colName}> - <span className={classes.colNameLabel}> - <FormattedMessage - defaultMessage="Product" - description="product name" - /> - </span> - </TableCell> - <TableCell className={classes.colSku}> - <FormattedMessage - defaultMessage="SKU" - description="ordered product sku" - /> - </TableCell> - <TableCell className={classes.colQuantity}> - <FormattedMessage - defaultMessage="Quantity" - description="ordered product quantity" - /> - </TableCell> - <TableCell className={classes.colPrice}> - <FormattedMessage - defaultMessage="Price" - description="product price" - /> - </TableCell> - <TableCell className={classes.colTotal}> - <FormattedMessage - defaultMessage="Total" - description="order line total price" - /> - </TableCell> - </TableRow> - </TableHead> - <TableBody> - {renderCollection(lines, line => ( - <TableRow - className={!!line ? classes.clickableRow : undefined} - hover={!!line} - key={maybe(() => line.id)} - > - <TableCellAvatar - className={classes.colName} - thumbnail={maybe(() => line.orderLine.thumbnail.url)} - > - {maybe(() => line.orderLine.productName) || <Skeleton />} - </TableCellAvatar> - <TableCell className={classes.colSku}> - {line?.orderLine.productSku || <Skeleton />} - </TableCell> - <TableCell className={classes.colQuantity}> - {line?.quantity || <Skeleton />} - </TableCell> - <TableCell className={classes.colPrice}> - {maybe(() => line.orderLine.unitPrice.gross) ? ( - <Money money={line.orderLine.unitPrice.gross} /> - ) : ( - <Skeleton /> - )} - </TableCell> - <TableCell className={classes.colTotal}> - {maybe( - () => line.quantity * line.orderLine.unitPrice.gross.amount - ) ? ( - <Money - money={{ - amount: - line.quantity * line.orderLine.unitPrice.gross.amount, - currency: line.orderLine.unitPrice.gross.currency - }} - /> - ) : ( - <Skeleton /> - )} - </TableCell> - </TableRow> - ))} - {(fulfillment?.warehouse || fulfillment?.trackingNumber) && ( - <TableRow> - <TableCell className={classes.infoRow} colSpan={numberOfColumns}> - <Typography color="textSecondary" variant="body2"> - {fulfillment?.warehouse && ( - <FormattedMessage - defaultMessage="Fulfilled from: {warehouseName}" - description="fulfillment group" - values={{ - warehouseName: ( - <Typography - className={classNames(classes.infoLabel, { - [classes.infoLabelWithMargin]: !!fulfillment?.trackingNumber - })} - color="textPrimary" - variant="body2" - > - {getStringOrPlaceholder( - fulfillment?.warehouse?.name - )} - </Typography> - ) - }} - /> - )} - </Typography> - <Typography color="textSecondary" variant="body2"> - {fulfillment?.trackingNumber && ( - <FormattedMessage - defaultMessage="Tracking Number: {trackingNumber}" - values={{ - trackingNumber: ( - <Typography - className={classes.infoLabel} - color="textPrimary" - variant="body2" - > - {fulfillment.trackingNumber} - </Typography> - ) - }} - /> - )} - </Typography> - </TableCell> - </TableRow> - )} - </TableBody> - </ResponsiveTable> - {status === FulfillmentStatus.FULFILLED && !fulfillment.trackingNumber && ( - <CardActions> - <Button color="primary" onClick={onTrackingCodeAdd}> - <FormattedMessage - defaultMessage="Add tracking" - description="fulfillment group tracking number" - /> - </Button> - </CardActions> - )} - {status === FulfillmentStatus.FULFILLED && fulfillment.trackingNumber && ( - <CardActions> - <Button color="primary" onClick={onTrackingCodeAdd}> - <FormattedMessage - defaultMessage="Edit tracking" - description="fulfillment group tracking number" - /> - </Button> - </CardActions> - )} - </Card> - ); -}; -OrderFulfillment.displayName = "OrderFulfillment"; -export default OrderFulfillment; diff --git a/src/orders/components/OrderFulfillment/index.ts b/src/orders/components/OrderFulfillment/index.ts deleted file mode 100644 index ca836178f..000000000 --- a/src/orders/components/OrderFulfillment/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from "./OrderFulfillment"; -export * from "./OrderFulfillment"; diff --git a/src/orders/components/OrderHistory/ExtendedTimelineEvent.tsx b/src/orders/components/OrderHistory/ExtendedTimelineEvent.tsx new file mode 100644 index 000000000..36bbc5519 --- /dev/null +++ b/src/orders/components/OrderHistory/ExtendedTimelineEvent.tsx @@ -0,0 +1,211 @@ +import { makeStyles, Typography } from "@material-ui/core"; +import Money from "@saleor/components/Money"; +import { TimelineEvent } from "@saleor/components/Timeline"; +import { orderUrl } from "@saleor/orders/urls"; +import { staffMemberDetailsUrl } from "@saleor/staff/urls"; +import { OrderEventsEnum } from "@saleor/types/globalTypes"; +import classNames from "classnames"; +import camelCase from "lodash/camelCase"; +import React from "react"; +import { defineMessages, useIntl } from "react-intl"; + +import { OrderDetails_order_events } from "../../types/OrderDetails"; + +const useStyles = makeStyles( + theme => ({ + eventSubtitle: { + marginBottom: theme.spacing(0.5), + marginTop: theme.spacing(1) + }, + header: { + fontWeight: 500, + marginBottom: theme.spacing(1) + }, + linesTableCell: { + paddingRight: theme.spacing(3) + }, + root: { marginTop: theme.spacing(4) }, + topSpacer: { + marginTop: theme.spacing(3) + }, + user: { + marginBottom: theme.spacing(1) + } + }), + { name: "OrderHistory" } +); + +export const productTitles = defineMessages({ + draftCreatedFromReplace: { + defaultMessage: "Products replaced", + description: "draft created from replace products list title", + id: "event products title draft reissued" + }, + fulfillmentRefunded: { + defaultMessage: "Products refunded", + description: "refunded products list title", + id: "event products list title refunded" + }, + fulfillmentReplaced: { + defaultMessage: "Products replaced", + description: "replaced products list title", + id: "event products list title replaced" + }, + fulfillmentReturned: { + defaultMessage: "Products returned", + description: "returned products list title", + id: "event products list title returned" + } +}); + +export const titles = defineMessages({ + draftCreatedFromReplace: { + defaultMessage: "Draft was reissued from order ", + description: "draft created from replace event title", + id: "event title draft reissued" + }, + fulfillmentRefunded: { + defaultMessage: "Products were refunded by ", + description: "refunded event title", + id: "event title refunded" + }, + fulfillmentReplaced: { + defaultMessage: "Products were replaced by ", + description: "replaced event title", + id: "event title replaced" + }, + fulfillmentReturned: { + defaultMessage: "Products were returned by", + description: "returned event title", + id: "event title returned" + } +}); + +export const messages = defineMessages({ + by: { + defaultMessage: "by", + description: "by preposition", + id: "by preposition" + }, + refundedAmount: { + defaultMessage: "Refunded amount", + description: "amount title", + id: "amount title" + }, + refundedShipment: { + defaultMessage: "Shipment was refunded", + description: "shipment refund title", + id: "shipment refund title" + } +}); + +interface ExtendedTimelineEventProps { + event: OrderDetails_order_events; + orderCurrency: string; +} + +const ExtendedTimelineEvent: React.FC<ExtendedTimelineEventProps> = ({ + event, + orderCurrency +}) => { + const { + id, + date, + type, + user, + lines, + amount, + shippingCostsIncluded, + relatedOrder + } = event; + const classes = useStyles({}); + const intl = useIntl(); + + const eventTypeInCamelCase = camelCase(type); + + const employeeName = `${user.firstName} ${user.lastName}`; + + const titleElements = { + by: { text: intl.formatMessage(messages.by) }, + employeeName: { link: staffMemberDetailsUrl(user.id), text: employeeName }, + orderNumber: { + link: orderUrl(relatedOrder?.id), + text: `#${relatedOrder?.number}` + }, + title: { text: intl.formatMessage(titles[eventTypeInCamelCase]) } + }; + + const selectTitleElements = () => { + const { title, by, employeeName, orderNumber } = titleElements; + + switch (type) { + case OrderEventsEnum.DRAFT_CREATED_FROM_REPLACE: { + return [title, orderNumber, by, employeeName]; + } + default: { + return [title, employeeName]; + } + } + }; + + return ( + <TimelineEvent date={date} titleElements={selectTitleElements()} key={id}> + {lines && ( + <> + <Typography + variant="caption" + color="textSecondary" + className={classes.eventSubtitle} + > + {intl.formatMessage(productTitles[eventTypeInCamelCase])} + </Typography> + <table> + <tbody> + {lines.map(({ orderLine, quantity }) => ( + <tr key={orderLine.id}> + <td className={classes.linesTableCell}> + {orderLine.productName} + </td> + <td className={classes.linesTableCell}> + <Typography variant="caption" color="textSecondary"> + {orderLine.variantName} + </Typography> + </td> + <td className={classes.linesTableCell}> + <Typography variant="caption" color="textSecondary"> + {`qty: ${quantity}`} + </Typography> + </td> + </tr> + ))} + </tbody> + </table> + {(amount || amount === 0) && ( + <> + <Typography + variant="caption" + color="textSecondary" + className={classNames(classes.eventSubtitle, classes.topSpacer)} + > + {intl.formatMessage(messages.refundedAmount)} + </Typography> + <Money + money={{ + amount, + currency: orderCurrency + }} + /> + </> + )} + {shippingCostsIncluded && ( + <Typography> + {intl.formatMessage(messages.refundedShipment)} + </Typography> + )} + </> + )} + </TimelineEvent> + ); +}; + +export default ExtendedTimelineEvent; diff --git a/src/orders/components/OrderHistory/OrderHistory.tsx b/src/orders/components/OrderHistory/OrderHistory.tsx index 5d115517c..a1907f037 100644 --- a/src/orders/components/OrderHistory/OrderHistory.tsx +++ b/src/orders/components/OrderHistory/OrderHistory.tsx @@ -2,28 +2,32 @@ import { makeStyles } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; import Form from "@saleor/components/Form"; import Hr from "@saleor/components/Hr"; -import Money from "@saleor/components/Money"; import Skeleton from "@saleor/components/Skeleton"; import { Timeline, TimelineAddNote, TimelineEvent, + TimelineEventProps, TimelineNote } from "@saleor/components/Timeline"; -import React from "react"; -import { FormattedMessage, IntlShape, useIntl } from "react-intl"; - +import { TitleElement } from "@saleor/components/Timeline/TimelineEventHeader"; +import { OrderDetails_order_events } from "@saleor/orders/types/OrderDetails"; +import { orderUrl } from "@saleor/orders/urls"; import { OrderEventsEmailsEnum, OrderEventsEnum -} from "../../../types/globalTypes"; -import { OrderDetails_order_events } from "../../types/OrderDetails"; +} from "@saleor/types/globalTypes"; +import React from "react"; +import { defineMessages } from "react-intl"; +import { FormattedMessage, IntlShape, useIntl } from "react-intl"; -export interface FormData { - message: string; -} +import ExtendedTimelineEvent from "./ExtendedTimelineEvent"; +import { getEventSecondaryTitle, isTimelineEventOfType } from "./utils"; -const getEventMessage = (event: OrderDetails_order_events, intl: IntlShape) => { +export const getEventMessage = ( + event: OrderDetails_order_events, + intl: IntlShape +) => { switch (event.type) { case OrderEventsEnum.CANCELED: return intl.formatMessage({ @@ -247,6 +251,21 @@ const getEventMessage = (event: OrderDetails_order_events, intl: IntlShape) => { } }; +export const replacementCreatedMessages = defineMessages({ + description: { + defaultMessage: "was created for replaced products", + description: "replacement created order history message description" + }, + draftNumber: { + defaultMessage: "Draft #{orderNumber} ", + description: "replacement created order history message draft number" + } +}); + +export interface FormData { + message: string; +} + const useStyles = makeStyles( theme => ({ eventSubtitle: { @@ -279,6 +298,45 @@ const OrderHistory: React.FC<OrderHistoryProps> = props => { const intl = useIntl(); + const getTimelineEventTitleProps = ( + event: OrderDetails_order_events + ): Partial<TimelineEventProps> => { + const { type, message } = event; + + const title = isTimelineEventOfType("rawMessage", type) + ? message + : getEventMessage(event, intl); + + if (isTimelineEventOfType("secondaryTitle", type)) { + return { + secondaryTitle: intl.formatMessage(...getEventSecondaryTitle(event)), + title + }; + } + + return { title }; + }; + + const getTitleElements = ( + event: OrderDetails_order_events + ): TitleElement[] => { + const { type, relatedOrder } = event; + + switch (type) { + case OrderEventsEnum.ORDER_REPLACEMENT_CREATED: { + return [ + { + link: orderUrl(relatedOrder?.id), + text: intl.formatMessage(replacementCreatedMessages.draftNumber, { + orderNumber: relatedOrder?.number + }) + }, + { text: intl.formatMessage(replacementCreatedMessages.description) } + ]; + } + } + }; + return ( <div className={classes.root}> <Typography className={classes.header} color="textSecondary"> @@ -301,106 +359,42 @@ const OrderHistory: React.FC<OrderHistoryProps> = props => { .slice() .reverse() .map(event => { - if (event.type === OrderEventsEnum.NOTE_ADDED) { + const { id, user, date, message, type } = event; + + if (isTimelineEventOfType("note", type)) { return ( <TimelineNote - date={event.date} - user={event.user} - message={event.message} - key={event.id} + date={date} + user={user} + message={message} + key={id} /> ); } - if (event.type === OrderEventsEnum.ORDER_MARKED_AS_PAID) { + if (isTimelineEventOfType("extendable", type)) { return ( - <TimelineEvent - date={event.date} - title={getEventMessage(event, intl)} - secondaryTitle={intl.formatMessage( - { - defaultMessage: - "Transaction Reference {transactionReference}", - description: "transaction reference" - }, - { transactionReference: event.transactionReference } - )} - key={event.id} + <ExtendedTimelineEvent + event={event} + orderCurrency={orderCurrency} /> ); } - if (event.type === OrderEventsEnum.FULFILLMENT_REFUNDED) { + + if (isTimelineEventOfType("linked", type)) { return ( <TimelineEvent - date={event.date} - title={getEventMessage(event, intl)} - key={event.id} - > - {event.lines && ( - <> - <Typography - variant="caption" - color="textSecondary" - className={classes.eventSubtitle} - > - <FormattedMessage defaultMessage="Products refunded" /> - </Typography> - <table> - <tbody> - {event.lines.map(line => ( - <tr key={line.orderLine.id}> - <td className={classes.linesTableCell}> - {line.orderLine.productName} - </td> - <td className={classes.linesTableCell}> - <Typography - variant="caption" - color="textSecondary" - > - {line.orderLine.variantName} - </Typography> - </td> - <td className={classes.linesTableCell}> - <Typography - variant="caption" - color="textSecondary" - > - {`qty: ${line.quantity}`} - </Typography> - </td> - </tr> - ))} - </tbody> - </table> - <Typography - variant="caption" - color="textSecondary" - className={classes.eventSubtitle} - > - <FormattedMessage defaultMessage="Refunded amount" /> - </Typography> - {(event.amount || event.amount === 0) && ( - <Money - money={{ - amount: event.amount, - currency: orderCurrency - }} - /> - )} - {event.shippingCostsIncluded && ( - <Typography> - <FormattedMessage defaultMessage="Shipment was refunded" /> - </Typography> - )} - </> - )} - </TimelineEvent> + titleElements={getTitleElements(event)} + key={id} + date={date} + /> ); } + return ( <TimelineEvent - date={event.date} - title={getEventMessage(event, intl)} - key={event.id} + {...getTimelineEventTitleProps(event)} + key={id} + date={date} /> ); })} diff --git a/src/orders/components/OrderHistory/utils.tsx b/src/orders/components/OrderHistory/utils.tsx new file mode 100644 index 000000000..3d1c3030f --- /dev/null +++ b/src/orders/components/OrderHistory/utils.tsx @@ -0,0 +1,41 @@ +import { OrderDetails_order_events } from "@saleor/orders/types/OrderDetails"; +import { OrderEventsEnum } from "@saleor/types/globalTypes"; +import { MessageDescriptor } from "react-intl"; + +export const getEventSecondaryTitle = ( + event: OrderDetails_order_events +): [MessageDescriptor, any?] => { + switch (event.type) { + case OrderEventsEnum.ORDER_MARKED_AS_PAID: { + return [ + { + defaultMessage: "Transaction Reference {transactionReference}", + description: "transaction reference", + id: "transaction-reference-order-history" + }, + { transactionReference: event.transactionReference } + ]; + } + } +}; + +const timelineEventTypes = { + extendable: [ + OrderEventsEnum.FULFILLMENT_REFUNDED, + OrderEventsEnum.FULFILLMENT_REPLACED, + OrderEventsEnum.FULFILLMENT_RETURNED, + OrderEventsEnum.DRAFT_CREATED_FROM_REPLACE + ], + linked: [OrderEventsEnum.ORDER_REPLACEMENT_CREATED], + note: [OrderEventsEnum.NOTE_ADDED], + rawMessage: [ + OrderEventsEnum.OTHER, + OrderEventsEnum.EXTERNAL_SERVICE_NOTIFICATION + ], + secondaryTitle: [OrderEventsEnum.ORDER_MARKED_AS_PAID] +}; + +export const isTimelineEventOfType = ( + type: "extendable" | "secondaryTitle" | "rawMessage" | "note" | "linked", + eventType: OrderEventsEnum +) => !!timelineEventTypes[type]?.includes(eventType); diff --git a/src/orders/components/OrderProductsCardElements/OrderProductsCardHeader.tsx b/src/orders/components/OrderProductsCardElements/OrderProductsCardHeader.tsx new file mode 100644 index 000000000..2dc8e7f39 --- /dev/null +++ b/src/orders/components/OrderProductsCardElements/OrderProductsCardHeader.tsx @@ -0,0 +1,95 @@ +import { makeStyles, TableCell, TableHead, TableRow } from "@material-ui/core"; +import React from "react"; +import { FormattedMessage } from "react-intl"; + +const useStyles = makeStyles( + theme => ({ + clickableRow: { + cursor: "pointer" + }, + colName: { + textAlign: "left", + width: "auto" + }, + colPrice: { + textAlign: "right", + width: 120 + }, + colQuantity: { + textAlign: "center", + width: 120 + }, + colSku: { + textAlign: "right", + textOverflow: "ellipsis", + width: 120 + }, + colTotal: { + textAlign: "right", + width: 120 + }, + infoLabel: { + display: "inline-block" + }, + infoLabelWithMargin: { + marginBottom: theme.spacing() + }, + infoRow: { + padding: theme.spacing(2, 3) + }, + orderNumber: { + display: "inline", + marginLeft: theme.spacing(1) + }, + statusBar: { + paddingTop: 0 + }, + table: { + tableLayout: "fixed" + } + }), + { name: "TableHeader" } +); + +const TableHeader = () => { + const classes = useStyles({}); + + return ( + <TableHead> + <TableRow> + <TableCell className={classes.colName}> + <FormattedMessage + defaultMessage="Product" + description="product name" + /> + </TableCell> + <TableCell className={classes.colSku}> + <FormattedMessage + defaultMessage="SKU" + description="ordered product sku" + /> + </TableCell> + <TableCell className={classes.colQuantity}> + <FormattedMessage + defaultMessage="Quantity" + description="ordered product quantity" + /> + </TableCell> + <TableCell className={classes.colPrice}> + <FormattedMessage + defaultMessage="Price" + description="product price" + /> + </TableCell> + <TableCell className={classes.colTotal}> + <FormattedMessage + defaultMessage="Total" + description="order line total price" + /> + </TableCell> + </TableRow> + </TableHead> + ); +}; + +export default TableHeader; diff --git a/src/orders/components/OrderProductsCardElements/OrderProductsTableRow.tsx b/src/orders/components/OrderProductsCardElements/OrderProductsTableRow.tsx new file mode 100644 index 000000000..3dbd733c2 --- /dev/null +++ b/src/orders/components/OrderProductsCardElements/OrderProductsTableRow.tsx @@ -0,0 +1,129 @@ +import { makeStyles, TableCell, TableRow } from "@material-ui/core"; +import Money from "@saleor/components/Money"; +import Skeleton from "@saleor/components/Skeleton"; +import TableCellAvatar, { + AVATAR_MARGIN +} from "@saleor/components/TableCellAvatar"; +import { maybe } from "@saleor/misc"; +import { + OrderDetails_order_fulfillments_lines, + OrderDetails_order_lines +} from "@saleor/orders/types/OrderDetails"; +import React from "react"; + +const useStyles = makeStyles( + theme => ({ + clickableRow: { + cursor: "pointer" + }, + colName: { + width: "auto" + }, + colNameLabel: { + marginLeft: AVATAR_MARGIN + }, + colPrice: { + textAlign: "right", + width: 120 + }, + colQuantity: { + textAlign: "center", + width: 120 + }, + colSku: { + textAlign: "right", + textOverflow: "ellipsis", + width: 120 + }, + colTotal: { + textAlign: "right", + width: 120 + }, + infoLabel: { + display: "inline-block" + }, + infoLabelWithMargin: { + marginBottom: theme.spacing() + }, + infoRow: { + padding: theme.spacing(2, 3) + }, + orderNumber: { + display: "inline", + marginLeft: theme.spacing(1) + }, + statusBar: { + paddingTop: 0 + }, + table: { + tableLayout: "fixed" + } + }), + { name: "TableLine" } +); + +interface TableLineProps { + line: OrderDetails_order_fulfillments_lines | OrderDetails_order_lines; + isOrderLine?: boolean; +} + +const TableLine: React.FC<TableLineProps> = ({ + line: lineData, + isOrderLine = false +}) => { + const classes = useStyles({}); + const { quantity, quantityFulfilled } = lineData as OrderDetails_order_lines; + + if (!lineData) { + return <Skeleton />; + } + + const line = isOrderLine + ? ({ + ...lineData, + orderLine: lineData + } as OrderDetails_order_fulfillments_lines) + : (lineData as OrderDetails_order_fulfillments_lines); + + const quantityToDisplay = isOrderLine + ? quantity - quantityFulfilled + : quantity; + + return ( + <TableRow className={classes.clickableRow} hover key={line.id}> + <TableCellAvatar + className={classes.colName} + thumbnail={maybe(() => line.orderLine.thumbnail.url)} + > + {maybe(() => line.orderLine.productName) || <Skeleton />} + </TableCellAvatar> + <TableCell className={classes.colSku}> + {line?.orderLine.productSku || <Skeleton />} + </TableCell> + <TableCell className={classes.colQuantity}> + {quantityToDisplay || <Skeleton />} + </TableCell> + <TableCell className={classes.colPrice}> + {maybe(() => line.orderLine.unitPrice.gross) ? ( + <Money money={line.orderLine.unitPrice.gross} /> + ) : ( + <Skeleton /> + )} + </TableCell> + <TableCell className={classes.colTotal}> + {maybe(() => line.quantity * line.orderLine.unitPrice.gross.amount) ? ( + <Money + money={{ + amount: line.quantity * line.orderLine.unitPrice.gross.amount, + currency: line.orderLine.unitPrice.gross.currency + }} + /> + ) : ( + <Skeleton /> + )} + </TableCell> + </TableRow> + ); +}; + +export default TableLine; diff --git a/src/orders/components/OrderRefundAmount/index.ts b/src/orders/components/OrderRefundAmount/index.ts deleted file mode 100644 index f6f346ecf..000000000 --- a/src/orders/components/OrderRefundAmount/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./OrderRefundAmount"; -export { default } from "./OrderRefundAmount"; diff --git a/src/orders/components/OrderRefundAmountValues/OrderRefundAmountValues.tsx b/src/orders/components/OrderRefundAmountValues/OrderRefundAmountValues.tsx deleted file mode 100644 index 4fa9dc175..000000000 --- a/src/orders/components/OrderRefundAmountValues/OrderRefundAmountValues.tsx +++ /dev/null @@ -1,153 +0,0 @@ -import { makeStyles } from "@material-ui/core"; -import Money, { IMoney } from "@saleor/components/Money"; -import Skeleton from "@saleor/components/Skeleton"; -import React from "react"; -import { FormattedMessage } from "react-intl"; - -const useStyles = makeStyles( - theme => ({ - highlightedRow: { - fontWeight: 600 - }, - root: { - ...theme.typography.body1, - lineHeight: 1.9, - width: "100%" - }, - textRight: { - minWidth: 30, - textAlign: "right" - } - }), - { name: "OrderRefundAmountValues" } -); - -export interface OrderRefundAmountValuesProps { - authorizedAmount: IMoney; - shipmentCost?: IMoney; - selectedProductsValue?: IMoney; - previouslyRefunded: IMoney; - maxRefund: IMoney; - proposedRefundAmount?: IMoney; - refundTotalAmount?: IMoney; -} - -const OrderRefundAmountValues: React.FC<OrderRefundAmountValuesProps> = ({ - authorizedAmount, - shipmentCost, - selectedProductsValue, - previouslyRefunded, - maxRefund, - proposedRefundAmount, - refundTotalAmount -}) => { - const classes = useStyles({}); - - return ( - <table className={classes.root}> - <tbody> - <tr> - <td> - <FormattedMessage - defaultMessage="Authorized Amount" - description="order refund amount" - /> - </td> - <td className={classes.textRight}> - {authorizedAmount?.amount !== undefined ? ( - <Money money={authorizedAmount} /> - ) : ( - <Skeleton /> - )} - </td> - </tr> - {shipmentCost?.amount !== undefined && ( - <tr> - <td> - <FormattedMessage - defaultMessage="Shipment cost" - description="order refund amount" - /> - </td> - <td className={classes.textRight}> - <Money money={shipmentCost} /> - </td> - </tr> - )} - {selectedProductsValue?.amount !== undefined && ( - <tr> - <td> - <FormattedMessage - defaultMessage="Selected products value" - description="order refund amount" - /> - </td> - <td className={classes.textRight}> - <Money money={selectedProductsValue} /> - </td> - </tr> - )} - <tr> - <td> - <FormattedMessage - defaultMessage="Previously refunded" - description="order refund amount" - /> - </td> - <td className={classes.textRight}> - {previouslyRefunded?.amount !== undefined ? ( - <> - <Money money={previouslyRefunded} /> - </> - ) : ( - <Skeleton /> - )} - </td> - </tr> - <tr className={classes.highlightedRow}> - <td> - <FormattedMessage - defaultMessage="Max Refund" - description="order refund amount" - /> - </td> - <td className={classes.textRight}> - {maxRefund?.amount !== undefined ? ( - <Money money={maxRefund} /> - ) : ( - <Skeleton /> - )} - </td> - </tr> - {proposedRefundAmount?.amount !== undefined && ( - <tr className={classes.highlightedRow}> - <td> - <FormattedMessage - defaultMessage="Proposed refund amount" - description="order refund amount" - /> - </td> - <td className={classes.textRight}> - <Money money={proposedRefundAmount} /> - </td> - </tr> - )} - {refundTotalAmount?.amount !== undefined && ( - <tr className={classes.highlightedRow}> - <td> - <FormattedMessage - defaultMessage="Refund total amount" - description="order refund amount" - /> - </td> - <td className={classes.textRight}> - <Money money={refundTotalAmount} /> - </td> - </tr> - )} - </tbody> - </table> - ); -}; -OrderRefundAmountValues.displayName = "OrderRefundAmountValues"; -export default OrderRefundAmountValues; diff --git a/src/orders/components/OrderRefundAmountValues/index.ts b/src/orders/components/OrderRefundAmountValues/index.ts deleted file mode 100644 index b0f5c8559..000000000 --- a/src/orders/components/OrderRefundAmountValues/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./OrderRefundAmountValues"; -export { default } from "./OrderRefundAmountValues"; diff --git a/src/orders/components/OrderRefundPage/OrderRefundPage.tsx b/src/orders/components/OrderRefundPage/OrderRefundPage.tsx index d7580f363..676cd399b 100644 --- a/src/orders/components/OrderRefundPage/OrderRefundPage.tsx +++ b/src/orders/components/OrderRefundPage/OrderRefundPage.tsx @@ -12,8 +12,12 @@ import React from "react"; import { useIntl } from "react-intl"; import OrderRefund from "../OrderRefund"; -import OrderRefundAmount from "../OrderRefundAmount"; import OrderRefundFulfilledProducts from "../OrderRefundFulfilledProducts"; +import OrderRefundAmount from "../OrderRefundReturnAmount"; +import { + getMiscellaneousAmountValues, + getRefundProductsAmountValues +} from "../OrderRefundReturnAmount/utils"; import OrderRefundUnfulfilledProducts from "../OrderRefundUnfulfilledProducts"; import OrderRefundForm, { OrderRefundSubmitData, @@ -55,91 +59,104 @@ const OrderRefundPage: React.FC<OrderRefundPageProps> = props => { defaultType={defaultType} onSubmit={onSubmit} > - {({ data, handlers, change, submit }) => ( - <Container> - <AppHeader onBack={onBack}> - {order?.number - ? intl.formatMessage( - { - defaultMessage: "Order #{orderNumber}", - description: "page header with order number" - }, - { - orderNumber: order.number - } - ) - : intl.formatMessage({ - defaultMessage: "Order", + {({ data, handlers, change, submit }) => { + const isProductRefund = data.type === OrderRefundType.PRODUCTS; + + return ( + <Container> + <AppHeader onBack={onBack}> + {order?.number + ? intl.formatMessage( + { + defaultMessage: "Order #{orderNumber}", + description: "page header with order number" + }, + { + orderNumber: order.number + } + ) + : intl.formatMessage({ + defaultMessage: "Order", + description: "page header" + })} + </AppHeader> + <PageHeader + title={intl.formatMessage( + { + defaultMessage: "Order no. {orderNumber} - Refund", description: "page header" - })} - </AppHeader> - <PageHeader - title={intl.formatMessage( - { - defaultMessage: "Order no. {orderNumber} - Refund", - description: "page header" - }, - { - orderNumber: order?.number - } - )} - /> - <Grid> - <div> - <OrderRefund data={data} disabled={disabled} onChange={change} /> - {data.type === OrderRefundType.PRODUCTS && ( - <> - {unfulfilledLines?.length > 0 && ( - <> - <CardSpacer /> - <OrderRefundUnfulfilledProducts - unfulfilledLines={unfulfilledLines} - data={data} - disabled={disabled} - onRefundedProductQuantityChange={ - handlers.changeRefundedProductQuantity - } - onSetMaximalQuantities={ - handlers.setMaximalRefundedProductQuantities - } - /> - </> - )} - {renderCollection(fulfilledFulfillemnts, fulfillment => ( - <React.Fragment key={fulfillment?.id}> - <CardSpacer /> - <OrderRefundFulfilledProducts - fulfillment={fulfillment} - data={data} - disabled={disabled} - orderNumber={order?.number} - onRefundedProductQuantityChange={ - handlers.changeRefundedFulfilledProductQuantity - } - onSetMaximalQuantities={() => - handlers.setMaximalRefundedFulfilledProductQuantities( - fulfillment?.id - ) - } - /> - </React.Fragment> - ))} - </> + }, + { + orderNumber: order?.number + } )} - </div> - <div> - <OrderRefundAmount - data={data} - order={order} - disabled={disabled} - errors={errors} - onChange={change} - onRefund={submit} - /> - </div> - </Grid> - </Container> - )} + /> + <Grid> + <div> + <OrderRefund + data={data} + disabled={disabled} + onChange={change} + /> + {isProductRefund && ( + <> + {unfulfilledLines?.length > 0 && ( + <> + <CardSpacer /> + <OrderRefundUnfulfilledProducts + unfulfilledLines={unfulfilledLines} + data={data} + disabled={disabled} + onRefundedProductQuantityChange={ + handlers.changeRefundedProductQuantity + } + onSetMaximalQuantities={ + handlers.setMaximalRefundedProductQuantities + } + /> + </> + )} + {renderCollection(fulfilledFulfillemnts, fulfillment => ( + <React.Fragment key={fulfillment?.id}> + <CardSpacer /> + <OrderRefundFulfilledProducts + fulfillment={fulfillment} + data={data} + disabled={disabled} + orderNumber={order?.number} + onRefundedProductQuantityChange={ + handlers.changeRefundedFulfilledProductQuantity + } + onSetMaximalQuantities={() => + handlers.setMaximalRefundedFulfilledProductQuantities( + fulfillment?.id + ) + } + /> + </React.Fragment> + ))} + </> + )} + </div> + <div> + <OrderRefundAmount + amountData={ + isProductRefund + ? getRefundProductsAmountValues(order, data) + : getMiscellaneousAmountValues(order) + } + data={data} + order={order} + disabled={disabled} + errors={errors} + onChange={change} + onRefund={submit} + /> + </div> + </Grid> + </Container> + ); + }} </OrderRefundForm> ); }; diff --git a/src/orders/components/OrderRefundAmount/OrderRefundAmount.tsx b/src/orders/components/OrderRefundReturnAmount/OrderRefundReturnAmount.tsx similarity index 60% rename from src/orders/components/OrderRefundAmount/OrderRefundAmount.tsx rename to src/orders/components/OrderRefundReturnAmount/OrderRefundReturnAmount.tsx index 778e651ec..e67629555 100644 --- a/src/orders/components/OrderRefundAmount/OrderRefundAmount.tsx +++ b/src/orders/components/OrderRefundReturnAmount/OrderRefundReturnAmount.tsx @@ -9,91 +9,22 @@ import CardSpacer from "@saleor/components/CardSpacer"; import CardTitle from "@saleor/components/CardTitle"; import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; import Hr from "@saleor/components/Hr"; -import { IMoney } from "@saleor/components/Money"; -import PriceField from "@saleor/components/PriceField"; import { OrderErrorFragment } from "@saleor/fragments/types/OrderErrorFragment"; +import { OrderDetails_order } from "@saleor/orders/types/OrderDetails"; import { OrderRefundData_order } from "@saleor/orders/types/OrderRefundData"; -import { - getAllFulfillmentLinesPriceSum, - getPreviouslyRefundedPrice, - getRefundedLinesPriceSum -} from "@saleor/orders/utils/data"; -import { getFormErrors } from "@saleor/utils/errors"; -import getOrderErrorMessage from "@saleor/utils/errors/order"; import React from "react"; -import { FormattedMessage, useIntl } from "react-intl"; +import { defineMessages, FormattedMessage, useIntl } from "react-intl"; -import OrderRefundAmountValues, { - OrderRefundAmountValuesProps -} from "../OrderRefundAmountValues"; import { OrderRefundAmountCalculationMode, OrderRefundFormData, OrderRefundType } from "../OrderRefundPage/form"; - -const getMiscellaneousAmountValues = ( - order: OrderRefundData_order -): OrderRefundAmountValuesProps => { - const authorizedAmount = order?.total?.gross; - const previouslyRefunded = getPreviouslyRefundedPrice(order); - const maxRefund = order?.totalCaptured; - - return { - authorizedAmount, - maxRefund, - previouslyRefunded - }; -}; - -const getProductsAmountValues = ( - order: OrderRefundData_order, - data: OrderRefundFormData -): OrderRefundAmountValuesProps => { - const authorizedAmount = order?.total?.gross; - const shipmentCost = - authorizedAmount?.currency && - (order?.shippingPrice?.gross || { - amount: 0, - currency: authorizedAmount?.currency - }); - const previouslyRefunded = getPreviouslyRefundedPrice(order); - const maxRefund = order?.totalCaptured; - const refundedLinesSum = getRefundedLinesPriceSum( - order?.lines, - data.refundedProductQuantities - ); - const allFulfillmentLinesSum = getAllFulfillmentLinesPriceSum( - order?.fulfillments, - data.refundedFulfilledProductQuantities - ); - const allLinesSum = refundedLinesSum + allFulfillmentLinesSum; - const calculatedTotalAmount = data.refundShipmentCosts - ? allLinesSum + shipmentCost?.amount - : allLinesSum; - const selectedProductsValue = authorizedAmount?.currency && { - amount: allLinesSum, - currency: authorizedAmount.currency - }; - const proposedRefundAmount = authorizedAmount?.currency && { - amount: calculatedTotalAmount, - currency: authorizedAmount.currency - }; - const refundTotalAmount = authorizedAmount?.currency && { - amount: calculatedTotalAmount, - currency: authorizedAmount.currency - }; - - return { - authorizedAmount, - maxRefund, - previouslyRefunded, - proposedRefundAmount, - refundTotalAmount, - selectedProductsValue, - shipmentCost - }; -}; +import { OrderReturnFormData } from "../OrderReturnPage/form"; +import OrderRefundAmountValues, { + OrderRefundAmountValuesProps +} from "./OrderRefundReturnAmountValues"; +import RefundAmountInput from "./RefundAmountInput"; const useStyles = makeStyles( theme => ({ @@ -124,80 +55,54 @@ const useStyles = makeStyles( { name: "OrderRefundAmount" } ); -interface RefundAmountInputProps { - data: OrderRefundFormData; - maxRefund: IMoney; - currencySymbol: string; - amountTooSmall: boolean; - amountTooBig: boolean; - disabled: boolean; - errors: OrderErrorFragment[]; - onChange: (event: React.ChangeEvent<any>) => void; -} - -const RefundAmountInput: React.FC<RefundAmountInputProps> = props => { - const { - data, - maxRefund, - amountTooSmall, - amountTooBig, - currencySymbol, - disabled, - errors, - onChange - } = props; - const intl = useIntl(); - const classes = useStyles(props); - - const formErrors = getFormErrors(["amount"], errors); - - return ( - <PriceField - disabled={disabled} - onChange={onChange} - currencySymbol={currencySymbol} - name={"amount" as keyof FormData} - value={data.amount} - label={intl.formatMessage({ - defaultMessage: "Amount", - description: "order refund amount, input label" - })} - className={classes.priceField} - InputProps={{ inputProps: { max: maxRefund?.amount } }} - inputProps={{ - "data-test": "amountInput", - max: maxRefund?.amount - }} - error={!!formErrors.amount || amountTooSmall || amountTooBig} - hint={ - getOrderErrorMessage(formErrors.amount, intl) || - (amountTooSmall && - intl.formatMessage({ - defaultMessage: "Amount must be bigger than 0" - })) || - (amountTooBig && - intl.formatMessage({ - defaultMessage: "Amount cannot be bigger than max refund" - })) - } - /> - ); -}; +const messages = defineMessages({ + refundButton: { + defaultMessage: "Refund", + description: "order refund amount button" + }, + refundCannotBeFulfilled: { + defaultMessage: "Refunded items can't be fulfilled", + description: "order refund subtitle" + }, + returnButton: { + defaultMessage: "Return & Replace products", + description: "order return amount button" + }, + returnCannotBeFulfilled: { + defaultMessage: "Returned items can't be fulfilled", + description: "order return subtitle" + } +}); interface OrderRefundAmountProps { - data: OrderRefundFormData; - order: OrderRefundData_order; + data: OrderRefundFormData | OrderReturnFormData; + order: OrderRefundData_order | OrderDetails_order; disabled: boolean; + disableSubmitButton?: boolean; + isReturn?: boolean; errors: OrderErrorFragment[]; + amountData: OrderRefundAmountValuesProps; onChange: (event: React.ChangeEvent<any>) => void; onRefund: () => void; } const OrderRefundAmount: React.FC<OrderRefundAmountProps> = props => { - const { data, order, disabled, errors, onChange, onRefund } = props; + const { + data, + order, + disabled, + errors, + onChange, + onRefund, + isReturn = false, + amountData, + disableSubmitButton + } = props; const classes = useStyles(props); const intl = useIntl(); + const { type = OrderRefundType.PRODUCTS } = data as OrderRefundFormData; + const amountCurrency = order?.total?.gross?.currency; const { @@ -207,14 +112,12 @@ const OrderRefundAmount: React.FC<OrderRefundAmountProps> = props => { proposedRefundAmount, refundTotalAmount, selectedProductsValue, - shipmentCost - } = - data.type === OrderRefundType.PRODUCTS - ? getProductsAmountValues(order, data) - : getMiscellaneousAmountValues(order); + shipmentCost, + replacedProductsValue + } = amountData; const selectedRefundAmount = - data.type === OrderRefundType.PRODUCTS && + type === OrderRefundType.PRODUCTS && data.amountCalculationMode === OrderRefundAmountCalculationMode.AUTOMATIC ? refundTotalAmount?.amount : data.amount; @@ -222,8 +125,9 @@ const OrderRefundAmount: React.FC<OrderRefundAmountProps> = props => { const isAmountTooSmall = selectedRefundAmount && selectedRefundAmount <= 0; const isAmountTooBig = selectedRefundAmount > maxRefund?.amount; - const disableRefundButton = - !selectedRefundAmount || isAmountTooSmall || isAmountTooBig; + const disableRefundButton = isReturn + ? disableSubmitButton || isAmountTooSmall || isAmountTooBig + : !selectedRefundAmount || isAmountTooBig || isAmountTooSmall; return ( <Card> @@ -234,7 +138,7 @@ const OrderRefundAmount: React.FC<OrderRefundAmountProps> = props => { })} /> <CardContent> - {data.type === OrderRefundType.PRODUCTS && ( + {type === OrderRefundType.PRODUCTS && ( <RadioGroup value={data.amountCalculationMode} onChange={onChange} @@ -261,6 +165,7 @@ const OrderRefundAmount: React.FC<OrderRefundAmountProps> = props => { name="refundShipmentCosts" onChange={onChange} /> + <CardSpacer /> <OrderRefundAmountValues authorizedAmount={authorizedAmount} previouslyRefunded={previouslyRefunded} @@ -268,8 +173,8 @@ const OrderRefundAmount: React.FC<OrderRefundAmountProps> = props => { selectedProductsValue={selectedProductsValue} refundTotalAmount={refundTotalAmount} shipmentCost={data.refundShipmentCosts && shipmentCost} + replacedProductsValue={replacedProductsValue} /> - <CardSpacer /> </> )} <Hr className={classes.hr} /> @@ -302,9 +207,10 @@ const OrderRefundAmount: React.FC<OrderRefundAmountProps> = props => { selectedProductsValue={selectedProductsValue} proposedRefundAmount={proposedRefundAmount} shipmentCost={data.refundShipmentCosts && shipmentCost} + replacedProductsValue={replacedProductsValue} /> <RefundAmountInput - data={data} + data={data as OrderRefundFormData} maxRefund={maxRefund} amountTooSmall={isAmountTooSmall} amountTooBig={isAmountTooBig} @@ -317,7 +223,7 @@ const OrderRefundAmount: React.FC<OrderRefundAmountProps> = props => { )} </RadioGroup> )} - {data.type === OrderRefundType.MISCELLANEOUS && ( + {type === OrderRefundType.MISCELLANEOUS && ( <> <OrderRefundAmountValues authorizedAmount={authorizedAmount} @@ -325,7 +231,7 @@ const OrderRefundAmount: React.FC<OrderRefundAmountProps> = props => { maxRefund={maxRefund} /> <RefundAmountInput - data={data} + data={data as OrderRefundFormData} maxRefund={maxRefund} amountTooSmall={isAmountTooSmall} amountTooBig={isAmountTooBig} @@ -337,17 +243,16 @@ const OrderRefundAmount: React.FC<OrderRefundAmountProps> = props => { </> )} <Button - type="submit" color="primary" variant="contained" fullWidth size="large" onClick={onRefund} className={classes.refundButton} - disabled={disabled || disableRefundButton} + disabled={disableRefundButton} data-test="submit" > - {!disableRefundButton ? ( + {!disableRefundButton && !isReturn ? ( <FormattedMessage defaultMessage="Refund {currency} {amount}" description="order refund amount, input button" @@ -357,10 +262,9 @@ const OrderRefundAmount: React.FC<OrderRefundAmountProps> = props => { }} /> ) : ( - <FormattedMessage - defaultMessage="Refund" - description="order refund amount, input button" - /> + intl.formatMessage( + isReturn ? messages.returnButton : messages.refundButton + ) )} </Button> <Typography @@ -368,10 +272,11 @@ const OrderRefundAmount: React.FC<OrderRefundAmountProps> = props => { color="textSecondary" className={classes.refundCaution} > - <FormattedMessage - defaultMessage="Refunded items can’t be fulfilled" - description="order refund amount" - /> + {intl.formatMessage( + isReturn + ? messages.returnCannotBeFulfilled + : messages.refundCannotBeFulfilled + )} </Typography> </CardContent> </Card> diff --git a/src/orders/components/OrderRefundReturnAmount/OrderRefundReturnAmountValues.tsx b/src/orders/components/OrderRefundReturnAmount/OrderRefundReturnAmountValues.tsx new file mode 100644 index 000000000..97b76a08f --- /dev/null +++ b/src/orders/components/OrderRefundReturnAmount/OrderRefundReturnAmountValues.tsx @@ -0,0 +1,133 @@ +import { makeStyles } from "@material-ui/core"; +import Money, { IMoney } from "@saleor/components/Money"; +import Skeleton from "@saleor/components/Skeleton"; +import classNames from "classnames"; +import { reduce } from "lodash"; +import React from "react"; +import { useIntl } from "react-intl"; +import { defineMessages } from "react-intl"; + +const useStyles = makeStyles( + theme => ({ + container: { + ...theme.typography.body1, + lineHeight: 1.9, + width: "100%" + }, + highlightedRow: { + fontWeight: 600 + }, + row: { + display: "flex", + flexDirection: "row", + justifyContent: "space-between", + marginBottom: theme.spacing(2), + textAlign: "right" + } + }), + { name: "OrderRefundAmountValues" } +); + +export interface OrderRefundAmountValuesProps { + authorizedAmount: IMoney; + shipmentCost?: IMoney; + selectedProductsValue?: IMoney; + previouslyRefunded: IMoney; + maxRefund: IMoney; + proposedRefundAmount?: IMoney; + replacedProductsValue?: IMoney; + refundTotalAmount?: IMoney; +} + +const messages = defineMessages({ + authorizedAmount: { + defaultMessage: "Authorized Amount", + description: "order refund amount" + }, + maxRefund: { + defaultMessage: "Max Refund", + description: "order refund amount" + }, + previouslyRefunded: { + defaultMessage: "Previously refunded", + description: "order refund amount" + }, + proposedRefundAmount: { + defaultMessage: "Proposed refund amount", + description: "order refund amount" + }, + refundTotalAmount: { + defaultMessage: "Refund total amount", + description: "order refund amount" + }, + replacedProductsValue: { + defaultMessage: "Replaced Products Value", + description: "order refund amount" + }, + selectedProductsValue: { + defaultMessage: "Selected Products Value", + description: "order refund amount" + }, + shipmentCost: { + defaultMessage: "Shipment Cost", + description: "order refund amount" + } +}); + +const OrderRefundAmountValues: React.FC<OrderRefundAmountValuesProps> = props => { + const intl = useIntl(); + const classes = useStyles({}); + + const orderedKeys: Array<keyof OrderRefundAmountValuesProps> = [ + "authorizedAmount", + "shipmentCost", + "selectedProductsValue", + "previouslyRefunded", + "replacedProductsValue", + "maxRefund", + "refundTotalAmount" + ]; + + const highlightedItems: Array<keyof OrderRefundAmountValuesProps> = [ + "maxRefund", + "refundTotalAmount" + ]; + + const items = reduce( + orderedKeys, + (result, key) => { + const value = props[key]; + + if (!value) { + return result; + } + + return [ + ...result, + { data: value, highlighted: highlightedItems.includes(key), key } + ]; + }, + [] + ); + + return ( + <div className={classes.container}> + {items.map(({ key, data, highlighted }) => ( + <div + className={classNames(classes.row, { + [classes.highlightedRow]: highlighted + })} + key={key} + > + {intl.formatMessage(messages[key])} + <div> + {data?.amount !== undefined ? <Money money={data} /> : <Skeleton />} + </div> + </div> + ))} + </div> + ); +}; + +OrderRefundAmountValues.displayName = "OrderRefundAmountValues"; +export default OrderRefundAmountValues; diff --git a/src/orders/components/OrderRefundReturnAmount/RefundAmountInput.tsx b/src/orders/components/OrderRefundReturnAmount/RefundAmountInput.tsx new file mode 100644 index 000000000..9dfe9e450 --- /dev/null +++ b/src/orders/components/OrderRefundReturnAmount/RefundAmountInput.tsx @@ -0,0 +1,108 @@ +import { makeStyles } from "@material-ui/core"; +import { IMoney } from "@saleor/components/Money"; +import PriceField from "@saleor/components/PriceField"; +import { OrderErrorFragment } from "@saleor/fragments/types/OrderErrorFragment"; +import { getFormErrors } from "@saleor/utils/errors"; +import getOrderErrorMessage from "@saleor/utils/errors/order"; +import React from "react"; +import { defineMessages, useIntl } from "react-intl"; + +import { OrderRefundFormData } from "../OrderRefundPage/form"; + +const useStyles = makeStyles( + theme => ({ + hr: { + margin: theme.spacing(1, 0) + }, + maxRefundRow: { + fontWeight: 600 + }, + priceField: { + marginTop: theme.spacing(2) + }, + refundButton: { + marginTop: theme.spacing(2) + }, + refundCaution: { + marginTop: theme.spacing(1) + }, + root: { + ...theme.typography.body1, + lineHeight: 1.9, + width: "100%" + }, + textRight: { + textAlign: "right" + } + }), + { name: "OrderRefundAmount" } +); + +interface RefundAmountInputProps { + data: OrderRefundFormData; + maxRefund: IMoney; + currencySymbol: string; + amountTooSmall: boolean; + amountTooBig: boolean; + disabled: boolean; + errors: OrderErrorFragment[]; + onChange: (event: React.ChangeEvent<any>) => void; +} + +const messages = defineMessages({ + amountTooBig: { + defaultMessage: "Amount cannot be bigger than max refund", + description: "Amount error message" + }, + amountTooSmall: { + defaultMessage: "Amount must be bigger than 0", + description: "Amount error message" + }, + label: { + defaultMessage: "Amount", + description: "order refund amount, input label" + } +}); + +const RefundAmountInput: React.FC<RefundAmountInputProps> = props => { + const { + data, + maxRefund, + amountTooSmall, + amountTooBig, + currencySymbol, + disabled, + errors, + onChange + } = props; + const intl = useIntl(); + const classes = useStyles(props); + const formErrors = getFormErrors(["amount"], errors); + + const isError = !!formErrors.amount || amountTooSmall || amountTooBig; + + return ( + <PriceField + disabled={disabled} + onChange={onChange} + currencySymbol={currencySymbol} + name={"amount" as keyof FormData} + value={data.amount} + label={intl.formatMessage(messages.label)} + className={classes.priceField} + InputProps={{ inputProps: { max: maxRefund?.amount } }} + inputProps={{ + "data-test": "amountInput", + max: maxRefund?.amount + }} + error={isError} + hint={ + getOrderErrorMessage(formErrors.amount, intl) || + (amountTooSmall && intl.formatMessage(messages.amountTooSmall)) || + (amountTooBig && intl.formatMessage(messages.amountTooBig)) + } + /> + ); +}; + +export default RefundAmountInput; diff --git a/src/orders/components/OrderRefundReturnAmount/index.ts b/src/orders/components/OrderRefundReturnAmount/index.ts new file mode 100644 index 000000000..4f071c9a7 --- /dev/null +++ b/src/orders/components/OrderRefundReturnAmount/index.ts @@ -0,0 +1,2 @@ +export * from "./OrderRefundReturnAmount"; +export { default } from "./OrderRefundReturnAmount"; diff --git a/src/orders/components/OrderRefundReturnAmount/utils.ts b/src/orders/components/OrderRefundReturnAmount/utils.ts new file mode 100644 index 000000000..96f343487 --- /dev/null +++ b/src/orders/components/OrderRefundReturnAmount/utils.ts @@ -0,0 +1,203 @@ +import { IMoney } from "@saleor/components/Money"; +import { FormsetData } from "@saleor/hooks/useFormset"; +import { OrderDetails_order } from "@saleor/orders/types/OrderDetails"; +import { OrderRefundData_order } from "@saleor/orders/types/OrderRefundData"; +import { + getAllFulfillmentLinesPriceSum, + getPreviouslyRefundedPrice, + getRefundedLinesPriceSum, + getReplacedProductsAmount, + getReturnSelectedProductsAmount +} from "@saleor/orders/utils/data"; + +import { OrderRefundFormData } from "../OrderRefundPage/form"; +import { LineItemData, OrderReturnFormData } from "../OrderReturnPage/form"; +import { OrderRefundAmountValuesProps } from "./OrderRefundReturnAmountValues"; + +export const getMiscellaneousAmountValues = ( + order: OrderRefundData_order +): OrderRefundAmountValuesProps => { + const authorizedAmount = order?.total?.gross; + const previouslyRefunded = getPreviouslyRefundedPrice(order); + const maxRefund = order?.totalCaptured; + + return { + authorizedAmount, + maxRefund, + previouslyRefunded + }; +}; + +const getAuthorizedAmount = (order: OrderRefundData_order) => + order?.total?.gross; + +const getShipmentCost = (order: OrderRefundData_order) => + getAuthorizedAmount(order)?.currency && + (order?.shippingPrice?.gross || { + amount: 0, + currency: getAuthorizedAmount(order)?.currency + }); + +const getMaxRefund = (order: OrderRefundData_order) => order?.totalCaptured; + +export const getProductsAmountValues = ( + order: OrderRefundData_order, + fulfilledItemsQuantities: FormsetData<null | LineItemData, string | number>, + unfulfilledItemsQuantities: FormsetData<null | LineItemData, string | number>, + shipmentCosts +): OrderRefundAmountValuesProps => { + const authorizedAmount = getAuthorizedAmount(order); + const shipmentCost = getShipmentCost(order); + + const previouslyRefunded = getPreviouslyRefundedPrice(order); + const maxRefund = getMaxRefund(order); + const refundedLinesSum = getRefundedLinesPriceSum( + order?.lines, + unfulfilledItemsQuantities as FormsetData<null, string> + ); + const allFulfillmentLinesSum = getAllFulfillmentLinesPriceSum( + order?.fulfillments, + fulfilledItemsQuantities as FormsetData<null, string> + ); + const allLinesSum = refundedLinesSum + allFulfillmentLinesSum; + const calculatedTotalAmount = getCalculatedTotalAmount({ + allLinesSum, + maxRefund, + previouslyRefunded, + shipmentCost, + shipmentCosts + }); + + const selectedProductsValue = authorizedAmount?.currency && { + amount: allLinesSum, + currency: authorizedAmount.currency + }; + + const proposedRefundAmount = authorizedAmount?.currency && { + amount: calculatedTotalAmount, + currency: authorizedAmount.currency + }; + const refundTotalAmount = authorizedAmount?.currency && { + amount: calculatedTotalAmount, + currency: authorizedAmount.currency + }; + + return { + authorizedAmount, + maxRefund, + previouslyRefunded, + proposedRefundAmount, + refundTotalAmount, + selectedProductsValue, + shipmentCost + }; +}; + +const getCalculatedTotalAmount = ({ + shipmentCost, + shipmentCosts, + allLinesSum, + maxRefund +}: { + shipmentCost: IMoney; + shipmentCosts: IMoney; + allLinesSum: number; + previouslyRefunded: IMoney; + maxRefund: IMoney; +}) => { + if (maxRefund?.amount === 0) { + return 0; + } + + const shipmentCostValue = shipmentCost ? shipmentCost.amount : 0; + + const calculatedTotalAmount = shipmentCosts + ? allLinesSum + shipmentCostValue + : allLinesSum; + + return calculatedTotalAmount; +}; + +const getReturnTotalAmount = ({ + selectedProductsValue, + refundShipmentCosts, + order, + maxRefund +}: { + order: OrderDetails_order; + selectedProductsValue: IMoney; + refundShipmentCosts: boolean; + maxRefund: IMoney; +}) => { + if (maxRefund?.amount === 0) { + return 0; + } + + if (refundShipmentCosts) { + const totalValue = + selectedProductsValue?.amount + getShipmentCost(order)?.amount; + return totalValue || 0; + } + + return selectedProductsValue?.amount || 0; +}; + +export const getReturnProductsAmountValues = ( + order: OrderDetails_order, + formData: OrderReturnFormData +) => { + const authorizedAmount = getAuthorizedAmount(order); + + const { + fulfiledItemsQuantities, + unfulfiledItemsQuantities, + refundShipmentCosts + } = formData; + + const replacedProductsValue = authorizedAmount?.currency && { + amount: getReplacedProductsAmount(order, formData), + currency: authorizedAmount.currency + }; + + const selectedProductsValue = authorizedAmount?.currency && { + amount: getReturnSelectedProductsAmount(order, formData), + currency: authorizedAmount.currency + }; + + const refundTotalAmount = authorizedAmount?.currency && { + amount: getReturnTotalAmount({ + maxRefund: getMaxRefund(order), + order, + refundShipmentCosts, + selectedProductsValue + }), + currency: authorizedAmount.currency + }; + + return { + ...getProductsAmountValues( + order, + fulfiledItemsQuantities, + unfulfiledItemsQuantities, + refundShipmentCosts + ), + refundTotalAmount, + replacedProductsValue, + selectedProductsValue + }; +}; + +export const getRefundProductsAmountValues = ( + order: OrderRefundData_order, + { + refundedFulfilledProductQuantities, + refundShipmentCosts, + refundedProductQuantities + }: OrderRefundFormData +) => + getProductsAmountValues( + order, + refundedFulfilledProductQuantities, + refundedProductQuantities, + refundShipmentCosts + ); diff --git a/src/orders/components/OrderReturnPage/OrderReturnPage.tsx b/src/orders/components/OrderReturnPage/OrderReturnPage.tsx new file mode 100644 index 000000000..457ae52e6 --- /dev/null +++ b/src/orders/components/OrderReturnPage/OrderReturnPage.tsx @@ -0,0 +1,129 @@ +import AppHeader from "@saleor/components/AppHeader"; +import CardSpacer from "@saleor/components/CardSpacer"; +import Container from "@saleor/components/Container"; +import Grid from "@saleor/components/Grid"; +import PageHeader from "@saleor/components/PageHeader"; +import { OrderErrorFragment } from "@saleor/fragments/types/OrderErrorFragment"; +import { SubmitPromise } from "@saleor/hooks/useForm"; +import { renderCollection } from "@saleor/misc"; +import { OrderDetails_order } from "@saleor/orders/types/OrderDetails"; +import React from "react"; +import { defineMessages, useIntl } from "react-intl"; + +import OrderAmount from "../OrderRefundReturnAmount"; +import { getReturnProductsAmountValues } from "../OrderRefundReturnAmount/utils"; +import OrderRefundForm, { OrderRefundSubmitData } from "./form"; +import ItemsCard from "./OrderReturnRefundItemsCard/ReturnItemsCard"; +import { + getFulfilledFulfillemnts, + getParsedFulfiledLines, + getUnfulfilledLines +} from "./utils"; + +const messages = defineMessages({ + appTitle: { + defaultMessage: "Order #{orderNumber}", + description: "page header with order number" + }, + pageTitle: { + defaultMessage: "Order no. {orderNumber} - Replace/Return", + description: "page header" + } +}); + +export interface OrderReturnPageProps { + order: OrderDetails_order; + loading: boolean; + errors?: OrderErrorFragment[]; + onBack: () => void; + onSubmit: (data: OrderRefundSubmitData) => SubmitPromise; +} + +const OrderRefundPage: React.FC<OrderReturnPageProps> = props => { + const { order, loading, errors = [], onBack, onSubmit } = props; + + const intl = useIntl(); + return ( + <OrderRefundForm order={order} onSubmit={onSubmit}> + {({ data, handlers, change, submit }) => { + const { fulfiledItemsQuantities, unfulfiledItemsQuantities } = data; + + const hasAnyItemsSelected = + fulfiledItemsQuantities.some(({ value }) => !!value) || + unfulfiledItemsQuantities.some(({ value }) => !!value); + + return ( + <Container> + <AppHeader onBack={onBack}> + {intl.formatMessage(messages.appTitle, { + orderNumber: order?.number + })} + </AppHeader> + <PageHeader + title={intl.formatMessage(messages.pageTitle, { + orderNumber: order?.number + })} + /> + <Grid> + <div> + {!!data.unfulfiledItemsQuantities.length && ( + <> + <ItemsCard + errors={errors} + order={order} + lines={getUnfulfilledLines(order)} + itemsQuantities={data.unfulfiledItemsQuantities} + itemsSelections={data.itemsToBeReplaced} + onChangeQuantity={handlers.changeUnfulfiledItemsQuantity} + onSetMaxQuantity={ + handlers.handleSetMaximalUnfulfiledItemsQuantities + } + onChangeSelected={handlers.changeItemsToBeReplaced} + /> + <CardSpacer /> + </> + )} + {renderCollection( + getFulfilledFulfillemnts(order), + ({ id, lines }) => ( + <React.Fragment key={id}> + <ItemsCard + errors={errors} + order={order} + fulfilmentId={id} + lines={getParsedFulfiledLines(lines)} + itemsQuantities={data.fulfiledItemsQuantities} + itemsSelections={data.itemsToBeReplaced} + onChangeQuantity={handlers.changeFulfiledItemsQuantity} + onSetMaxQuantity={handlers.handleSetMaximalFulfiledItemsQuantities( + id + )} + onChangeSelected={handlers.changeItemsToBeReplaced} + /> + <CardSpacer /> + </React.Fragment> + ) + )} + </div> + <div> + <OrderAmount + isReturn + amountData={getReturnProductsAmountValues(order, data)} + data={data} + order={order} + disableSubmitButton={!hasAnyItemsSelected} + disabled={loading} + errors={errors} + onChange={change} + onRefund={submit} + /> + </div> + </Grid> + </Container> + ); + }} + </OrderRefundForm> + ); +}; + +export default OrderRefundPage; diff --git a/src/orders/components/OrderReturnPage/OrderReturnRefundItemsCard/CardTitle.tsx b/src/orders/components/OrderReturnPage/OrderReturnRefundItemsCard/CardTitle.tsx new file mode 100644 index 000000000..0b3942b47 --- /dev/null +++ b/src/orders/components/OrderReturnPage/OrderReturnRefundItemsCard/CardTitle.tsx @@ -0,0 +1,135 @@ +import { makeStyles, Typography } from "@material-ui/core"; +import DefaultCardTitle from "@saleor/components/CardTitle"; +import { StatusType } from "@saleor/components/StatusChip/types"; +import StatusLabel from "@saleor/components/StatusLabel"; +import { FulfillmentStatus } from "@saleor/types/globalTypes"; +import camelCase from "lodash/camelCase"; +import React from "react"; +import { defineMessages } from "react-intl"; +import { useIntl } from "react-intl"; + +const useStyles = makeStyles( + theme => ({ + orderNumber: { + display: "inline", + marginLeft: theme.spacing(1) + } + }), + { name: "CardTitle" } +); + +const messages = defineMessages({ + cancelled: { + defaultMessage: "Cancelled ({quantity})", + description: "cancelled fulfillment, section header" + }, + fulfilled: { + defaultMessage: "Fulfilled ({quantity})", + description: "section header" + }, + refunded: { + defaultMessage: "Refunded ({quantity})", + description: "refunded fulfillment, section header" + }, + refundedAndReturned: { + defaultMessage: "Refunded and Returned ({quantity})", + description: "cancelled fulfillment, section header" + }, + replaced: { + defaultMessage: "Replaced ({quantity})", + description: "refunded fulfillment, section header" + }, + returned: { + defaultMessage: "Returned ({quantity})", + description: "refunded fulfillment, section header" + }, + unfulfilled: { + defaultMessage: "Unfulfilled", + description: "section header" + } +}); + +type CardTitleStatus = FulfillmentStatus | "unfulfilled"; + +type CardTitleLines = Array<{ + quantity: number; +}>; + +interface CardTitleProps { + lines?: CardTitleLines; + fulfillmentOrder?: number; + status: CardTitleStatus; + toolbar?: React.ReactNode; + orderNumber?: string; + withStatus?: boolean; +} + +const selectStatus = (status: CardTitleStatus) => { + switch (status) { + case FulfillmentStatus.FULFILLED: + return StatusType.SUCCESS; + case FulfillmentStatus.REFUNDED: + return StatusType.NEUTRAL; + case FulfillmentStatus.RETURNED: + return StatusType.NEUTRAL; + case FulfillmentStatus.REPLACED: + return StatusType.NEUTRAL; + case FulfillmentStatus.REFUNDED_AND_RETURNED: + return StatusType.NEUTRAL; + case FulfillmentStatus.CANCELED: + return StatusType.ERROR; + default: + return StatusType.ALERT; + } +}; + +const CardTitle: React.FC<CardTitleProps> = ({ + lines = [], + fulfillmentOrder, + status, + orderNumber = "", + withStatus = false, + toolbar +}) => { + const intl = useIntl(); + const classes = useStyles({}); + + const fulfillmentName = + orderNumber && fulfillmentOrder + ? `#${orderNumber}-${fulfillmentOrder}` + : ""; + + const messageForStatus = messages[camelCase(status)] || messages.unfulfilled; + + const totalQuantity = lines.reduce( + (resultQuantity, { quantity }) => resultQuantity + quantity, + 0 + ); + + const title = ( + <> + {intl.formatMessage(messageForStatus, { + fulfillmentName, + quantity: totalQuantity + })} + <Typography className={classes.orderNumber} variant="body1"> + {fulfillmentName} + </Typography> + </> + ); + + return ( + <DefaultCardTitle + toolbar={toolbar} + title={ + withStatus ? ( + <StatusLabel label={title} status={selectStatus(status)} /> + ) : ( + title + ) + } + /> + ); +}; + +export default CardTitle; diff --git a/src/orders/components/OrderReturnPage/OrderReturnRefundItemsCard/MaximalButton.tsx b/src/orders/components/OrderReturnPage/OrderReturnRefundItemsCard/MaximalButton.tsx new file mode 100644 index 000000000..9fd5b1a57 --- /dev/null +++ b/src/orders/components/OrderReturnPage/OrderReturnRefundItemsCard/MaximalButton.tsx @@ -0,0 +1,39 @@ +import { Button, makeStyles } from "@material-ui/core"; +import React from "react"; +import { FormattedMessage } from "react-intl"; + +const useStyles = makeStyles( + theme => ({ + button: { + letterSpacing: 2, + marginBottom: theme.spacing(1), + marginTop: theme.spacing(3), + padding: 0 + } + }), + { name: "MaximalButton" } +); + +interface MaximalButtonProps { + onClick: () => void; +} + +const MaximalButton: React.FC<MaximalButtonProps> = ({ onClick }) => { + const classes = useStyles({}); + + return ( + <Button + className={classes.button} + color="primary" + onClick={onClick} + data-test="setMaximalQuantityUnfulfilledButton" + > + <FormattedMessage + defaultMessage="Set maximal quantities" + description="button" + /> + </Button> + ); +}; + +export default MaximalButton; diff --git a/src/orders/components/OrderReturnPage/OrderReturnRefundItemsCard/ProductErrorCell.tsx b/src/orders/components/OrderReturnPage/OrderReturnRefundItemsCard/ProductErrorCell.tsx new file mode 100644 index 000000000..d0a721ce8 --- /dev/null +++ b/src/orders/components/OrderReturnPage/OrderReturnRefundItemsCard/ProductErrorCell.tsx @@ -0,0 +1,100 @@ +import Popper from "@material-ui/core/Popper"; +import makeStyles from "@material-ui/core/styles/makeStyles"; +import TableCell from "@material-ui/core/TableCell"; +import Typography from "@material-ui/core/Typography"; +import ErrorExclamationCircleIcon from "@saleor/icons/ErrorExclamationCircle"; +import React, { useState } from "react"; +import { defineMessages } from "react-intl"; +import { useIntl } from "react-intl"; + +const useStyles = makeStyles( + theme => ({ + container: { + position: "relative" + }, + errorBox: { + backgroundColor: theme.palette.error.main, + borderRadius: 8, + marginRight: theme.spacing(3), + padding: theme.spacing(2, 3), + width: 280, + zIndex: 1000 + }, + errorText: { + color: "white", + fontSize: 14 + }, + errorTextHighlighted: { + color: theme.palette.error.main, + fontSize: 12, + marginRight: theme.spacing(1) + }, + titleContainer: { + alignItems: "center", + display: "flex", + flexDirection: "row", + justifyContent: "flex-end" + } + }), + { name: "ProductErrorCell" } +); + +const messages = defineMessages({ + description: { + defaultMessage: + "This product is no longer in database so it can’t be replaced, nor returned", + description: "product no longer exists error description" + }, + title: { + defaultMessage: "Product no longer exists", + description: "product no longer exists error title" + } +}); + +interface ProductErrorCellProps { + hasVariant: boolean; +} + +const ProductErrorCell: React.FC<ProductErrorCellProps> = ({ hasVariant }) => { + const classes = useStyles({}); + const intl = useIntl(); + const popperAnchorRef = React.useRef<HTMLButtonElement | null>(null); + + const [showErrorBox, setShowErrorBox] = useState<boolean>(false); + + if (hasVariant) { + return <TableCell />; + } + + return ( + <TableCell + align="right" + className={classes.container} + ref={popperAnchorRef} + > + <div + className={classes.titleContainer} + onMouseEnter={() => setShowErrorBox(true)} + onMouseLeave={() => setShowErrorBox(false)} + > + <Typography className={classes.errorTextHighlighted}> + {intl.formatMessage(messages.title)} + </Typography> + <ErrorExclamationCircleIcon /> + </div> + <Popper + placement="bottom-end" + open={showErrorBox} + anchorEl={popperAnchorRef.current} + > + <div className={classes.errorBox}> + <Typography className={classes.errorText}> + {intl.formatMessage(messages.description)} + </Typography> + </div> + </Popper> + </TableCell> + ); +}; + +export default ProductErrorCell; diff --git a/src/orders/components/OrderReturnPage/OrderReturnRefundItemsCard/ReturnItemsCard.tsx b/src/orders/components/OrderReturnPage/OrderReturnRefundItemsCard/ReturnItemsCard.tsx new file mode 100644 index 000000000..ac13c0cc8 --- /dev/null +++ b/src/orders/components/OrderReturnPage/OrderReturnRefundItemsCard/ReturnItemsCard.tsx @@ -0,0 +1,262 @@ +import { + Card, + CardContent, + Checkbox, + makeStyles, + Table, + TableBody, + TableCell, + TableHead, + TableRow, + TextField +} from "@material-ui/core"; +import Money from "@saleor/components/Money"; +import Skeleton from "@saleor/components/Skeleton"; +import TableCellAvatar from "@saleor/components/TableCellAvatar"; +import { OrderErrorFragment } from "@saleor/fragments/types/OrderErrorFragment"; +import { FormsetChange } from "@saleor/hooks/useFormset"; +import { renderCollection } from "@saleor/misc"; +import { + OrderDetails_order, + OrderDetails_order_lines +} from "@saleor/orders/types/OrderDetails"; +import React, { CSSProperties } from "react"; +import { defineMessages, FormattedMessage, useIntl } from "react-intl"; + +import { FormsetQuantityData, FormsetReplacementData } from "../form"; +import { getById } from "../utils"; +import CardTitle from "./CardTitle"; +import MaximalButton from "./MaximalButton"; +import ProductErrorCell from "./ProductErrorCell"; + +const useStyles = makeStyles( + theme => { + const inputPadding: CSSProperties = { + paddingBottom: theme.spacing(2), + paddingTop: theme.spacing(2) + }; + + return { + cartContent: { + paddingBottom: 0, + paddingTop: 0 + }, + + notice: { + marginBottom: theme.spacing(1), + marginTop: theme.spacing(2) + }, + + quantityInnerInput: { + ...inputPadding + }, + quantityInnerInputNoRemaining: { + paddingRight: 0 + }, + remainingQuantity: { + ...inputPadding, + color: theme.palette.text.secondary, + whiteSpace: "nowrap" + }, + setMaximalQuantityButton: { + marginBottom: theme.spacing(1), + marginTop: theme.spacing(2), + padding: 0 + } + }; + }, + { name: "ItemsCard" } +); + +const messages = defineMessages({ + improperValue: { + defaultMessage: "Improper value", + description: "error message" + }, + + titleFulfilled: { + defaultMessage: "Fulfillment - #{fulfilmentId}", + description: "section header" + }, + titleUnfulfilled: { + defaultMessage: "Unfulfilled Items", + description: "section header" + } +}); + +interface OrderReturnRefundLinesCardProps { + onChangeQuantity: FormsetChange<number>; + fulfilmentId?: string; + canReplace?: boolean; + errors: OrderErrorFragment[]; + lines: OrderDetails_order_lines[]; + order: OrderDetails_order; + itemsSelections: FormsetReplacementData; + itemsQuantities: FormsetQuantityData; + onChangeSelected: FormsetChange<boolean>; + onSetMaxQuantity(); +} + +const ItemsCard: React.FC<OrderReturnRefundLinesCardProps> = ({ + lines, + onSetMaxQuantity, + onChangeQuantity, + onChangeSelected, + itemsSelections, + itemsQuantities, + fulfilmentId, + order +}) => { + const classes = useStyles({}); + const intl = useIntl(); + + const handleChangeQuantity = (id: string) => ( + event: React.ChangeEvent<HTMLInputElement> + ) => onChangeQuantity(id, parseInt(event.target.value, 10)); + + const fulfillment = order?.fulfillments.find(getById(fulfilmentId)); + + return ( + <Card> + <CardTitle + orderNumber={order?.number} + lines={lines} + fulfillmentOrder={fulfillment?.fulfillmentOrder} + status={fulfillment?.status} + /> + <CardContent className={classes.cartContent}> + <MaximalButton onClick={onSetMaxQuantity} /> + </CardContent> + <Table> + <TableHead> + <TableRow> + <TableCell> + <FormattedMessage + defaultMessage="Product" + description="table column header" + /> + </TableCell> + <TableCell /> + <TableCell align="right"> + <FormattedMessage + defaultMessage="Price" + description="table column header" + /> + </TableCell> + <TableCell align="right"> + <FormattedMessage + defaultMessage="Return" + description="table column header" + /> + </TableCell> + <TableCell align="center"> + <FormattedMessage + defaultMessage="Replace" + description="table column header" + /> + </TableCell> + </TableRow> + </TableHead> + <TableBody> + {renderCollection( + lines, + line => { + const { + quantity, + quantityFulfilled, + id, + thumbnail, + unitPrice, + productName, + variant + } = line; + const isValueError = false; + const isRefunded = itemsQuantities.find(getById(id)).data + .isRefunded; + const isReplacable = !!variant && !isRefunded; + const isReturnable = !!variant; + const lineQuantity = fulfilmentId + ? quantity + : quantity - quantityFulfilled; + const isSelected = itemsSelections.find(getById(id))?.value; + const currentQuantity = itemsQuantities.find(getById(id))?.value; + const anyLineWithoutVariant = lines.some( + ({ variant }) => !variant + ); + const productNameCellWidth = anyLineWithoutVariant + ? "30%" + : "50%"; + + return ( + <TableRow key={id}> + <TableCellAvatar + thumbnail={thumbnail?.url} + style={{ width: productNameCellWidth }} + > + {productName || <Skeleton />} + </TableCellAvatar> + <ProductErrorCell hasVariant={isReturnable} /> + <TableCell align="right"> + <Money + money={{ + amount: unitPrice.gross.amount, + currency: unitPrice.gross.currency + }} + /> + </TableCell> + <TableCell align="right"> + {isReturnable && ( + <TextField + type="number" + inputProps={{ + className: classes.quantityInnerInput, + "data-test": "quantityInput", + "data-test-id": id, + max: lineQuantity.toString(), + min: 0, + style: { textAlign: "right" } + }} + fullWidth + value={currentQuantity} + onChange={handleChangeQuantity(id)} + InputProps={{ + endAdornment: lineQuantity && ( + <div className={classes.remainingQuantity}> + / {lineQuantity} + </div> + ) + }} + error={isValueError} + helperText={ + isValueError && + intl.formatMessage(messages.improperValue) + } + /> + )} + </TableCell> + <TableCell align="center"> + {isReplacable && ( + <Checkbox + checked={isSelected} + onChange={() => onChangeSelected(id, !isSelected)} + /> + )} + </TableCell> + </TableRow> + ); + }, + () => ( + <TableRow> + <TableCell colSpan={4}> + <Skeleton /> + </TableCell> + </TableRow> + ) + )} + </TableBody> + </Table> + </Card> + ); +}; + +export default ItemsCard; diff --git a/src/orders/components/OrderReturnPage/OrderReturnRefundItemsCard/index.tsx b/src/orders/components/OrderReturnPage/OrderReturnRefundItemsCard/index.tsx new file mode 100644 index 000000000..34df1a89a --- /dev/null +++ b/src/orders/components/OrderReturnPage/OrderReturnRefundItemsCard/index.tsx @@ -0,0 +1,2 @@ +export * from "./ReturnItemsCard"; +export { default } from "./ReturnItemsCard"; diff --git a/src/orders/components/OrderReturnPage/form.tsx b/src/orders/components/OrderReturnPage/form.tsx new file mode 100644 index 000000000..6f33ecac4 --- /dev/null +++ b/src/orders/components/OrderReturnPage/form.tsx @@ -0,0 +1,233 @@ +import useForm, { FormChange, SubmitPromise } from "@saleor/hooks/useForm"; +import useFormset, { + FormsetChange, + FormsetData +} from "@saleor/hooks/useFormset"; +import { OrderDetails_order } from "@saleor/orders/types/OrderDetails"; +import { FulfillmentStatus } from "@saleor/types/globalTypes"; +import handleFormSubmit from "@saleor/utils/handlers/handleFormSubmit"; +import React, { useState } from "react"; + +import { OrderRefundAmountCalculationMode } from "../OrderRefundPage/form"; +import { + getById, + getLineItem, + getOrderUnfulfilledLines, + getParsedLineData, + getParsedLineDataForFulfillmentStatus +} from "./utils"; + +export interface LineItemOptions<T> { + initialValue: T; + isFulfillment?: boolean; + isRefunded?: boolean; +} + +export interface LineItemData { + isFulfillment: boolean; + isRefunded: boolean; +} + +export type FormsetQuantityData = FormsetData<LineItemData, number>; +export type FormsetReplacementData = FormsetData<LineItemData, boolean>; + +export interface OrderReturnData { + amount: number; + refundShipmentCosts: boolean; + amountCalculationMode: OrderRefundAmountCalculationMode; +} + +export interface OrderReturnHandlers { + changeFulfiledItemsQuantity: FormsetChange<number>; + changeUnfulfiledItemsQuantity: FormsetChange<number>; + changeItemsToBeReplaced: FormsetChange<boolean>; + handleSetMaximalFulfiledItemsQuantities; + handleSetMaximalUnfulfiledItemsQuantities; +} + +export interface OrderReturnFormData extends OrderReturnData { + itemsToBeReplaced: FormsetReplacementData; + fulfiledItemsQuantities: FormsetQuantityData; + unfulfiledItemsQuantities: FormsetQuantityData; +} + +export type OrderRefundSubmitData = OrderReturnFormData; + +export interface UseOrderRefundFormResult { + change: FormChange; + hasChanged: boolean; + data: OrderReturnFormData; + handlers: OrderReturnHandlers; + submit: () => Promise<boolean>; +} + +interface OrderReturnProps { + children: (props: UseOrderRefundFormResult) => React.ReactNode; + order: OrderDetails_order; + onSubmit: (data: OrderRefundSubmitData) => SubmitPromise; +} + +const getOrderRefundPageFormData = (): OrderReturnData => ({ + amount: undefined, + amountCalculationMode: OrderRefundAmountCalculationMode.AUTOMATIC, + refundShipmentCosts: false +}); + +function useOrderReturnForm( + order: OrderDetails_order, + onSubmit: (data: OrderRefundSubmitData) => SubmitPromise +): UseOrderRefundFormResult { + const form = useForm(getOrderRefundPageFormData()); + const [hasChanged, setHasChanged] = useState(false); + + const handleChange: FormChange = (event, cb) => { + form.change(event, cb); + }; + + const unfulfiledItemsQuantites = useFormset<LineItemData, number>( + getOrderUnfulfilledLines(order).map(getParsedLineData({ initialValue: 0 })) + ); + + const getItemsFulfilled = () => { + const commonOptions = { + initialValue: 0, + isFulfillment: true + }; + + const refundedFulfilmentsItems = getParsedLineDataForFulfillmentStatus( + order, + FulfillmentStatus.REFUNDED, + { ...commonOptions, isRefunded: true } + ); + + const fulfilledFulfillmentsItems = getParsedLineDataForFulfillmentStatus( + order, + FulfillmentStatus.FULFILLED, + commonOptions + ); + + return refundedFulfilmentsItems.concat(fulfilledFulfillmentsItems); + }; + + const fulfiledItemsQuatities = useFormset<LineItemData, number>( + getItemsFulfilled() + ); + + const getItemsToBeReplaced = () => { + if (!order) { + return []; + } + + const orderLinesItems = getOrderUnfulfilledLines(order).map( + getParsedLineData({ initialValue: false }) + ); + + const refundedFulfilmentsItems = getParsedLineDataForFulfillmentStatus( + order, + FulfillmentStatus.REFUNDED, + { initialValue: false, isFulfillment: true } + ); + + const fulfilledFulfillmentsItems = getParsedLineDataForFulfillmentStatus( + order, + FulfillmentStatus.FULFILLED, + { initialValue: false, isFulfillment: true } + ); + + return [ + ...orderLinesItems, + ...refundedFulfilmentsItems, + ...fulfilledFulfillmentsItems + ]; + }; + + const itemsToBeReplaced = useFormset<LineItemData, boolean>( + getItemsToBeReplaced() + ); + + const handleSetMaximalUnfulfiledItemsQuantities = () => { + const newQuantities: FormsetQuantityData = unfulfiledItemsQuantites.data.map( + ({ id }) => { + const line = order.lines.find(getById(id)); + const initialValue = line.quantity - line.quantityFulfilled; + + return getLineItem(line, { initialValue }); + } + ); + + triggerChange(); + unfulfiledItemsQuantites.set(newQuantities); + }; + + const handleSetMaximalFulfiledItemsQuantities = ( + fulfillmentId: string + ) => () => { + const { lines } = order.fulfillments.find(getById(fulfillmentId)); + + const newQuantities: FormsetQuantityData = fulfiledItemsQuatities.data.map( + item => { + const line = lines.find(getById(item.id)); + + if (!line) { + return item; + } + + return getLineItem(line, { + initialValue: line.quantity, + isRefunded: item.data.isRefunded + }); + } + ); + + triggerChange(); + fulfiledItemsQuatities.set(newQuantities); + }; + + const data: OrderReturnFormData = { + fulfiledItemsQuantities: fulfiledItemsQuatities.data, + itemsToBeReplaced: itemsToBeReplaced.data, + unfulfiledItemsQuantities: unfulfiledItemsQuantites.data, + ...form.data + }; + + const submit = () => handleFormSubmit(data, onSubmit, setHasChanged); + + const triggerChange = () => setHasChanged(true); + + function handleHandlerChange<T>(callback: (id: string, value: T) => void) { + return (id: string, value: T) => { + triggerChange(); + callback(id, value); + }; + } + + return { + change: handleChange, + data, + handlers: { + changeFulfiledItemsQuantity: handleHandlerChange( + fulfiledItemsQuatities.change + ), + changeItemsToBeReplaced: handleHandlerChange(itemsToBeReplaced.change), + changeUnfulfiledItemsQuantity: handleHandlerChange( + unfulfiledItemsQuantites.change + ), + handleSetMaximalFulfiledItemsQuantities, + handleSetMaximalUnfulfiledItemsQuantities + }, + hasChanged, + submit + }; +} + +const OrderReturnForm: React.FC<OrderReturnProps> = ({ + children, + order, + onSubmit +}) => { + const props = useOrderReturnForm(order, onSubmit); + + return <form>{children(props)}</form>; +}; + +export default OrderReturnForm; diff --git a/src/orders/components/OrderReturnPage/index.ts b/src/orders/components/OrderReturnPage/index.ts new file mode 100644 index 000000000..e3456dee7 --- /dev/null +++ b/src/orders/components/OrderReturnPage/index.ts @@ -0,0 +1,2 @@ +export * from "./OrderReturnPage"; +export { default } from "./OrderReturnPage"; diff --git a/src/orders/components/OrderReturnPage/utils.tsx b/src/orders/components/OrderReturnPage/utils.tsx new file mode 100644 index 000000000..c926dadb6 --- /dev/null +++ b/src/orders/components/OrderReturnPage/utils.tsx @@ -0,0 +1,93 @@ +import { OrderDetailsFragment_fulfillments_lines } from "@saleor/fragments/types/OrderDetailsFragment"; +import { + OrderDetails_order, + OrderDetails_order_fulfillments +} from "@saleor/orders/types/OrderDetails"; +import { FulfillmentStatus } from "@saleor/types/globalTypes"; + +import { LineItemOptions } from "./form"; + +const fulfiledStatuses = [ + FulfillmentStatus.FULFILLED, + FulfillmentStatus.REFUNDED +]; + +export const getOrderUnfulfilledLines = (order: OrderDetails_order) => + order?.lines.filter(line => line.quantityFulfilled !== line.quantity) || []; + +export const getFulfilledFulfillment = fulfillment => + fulfiledStatuses.includes(fulfillment.status); + +export const getFulfilledFulfillemnts = (order?: OrderDetails_order) => + order?.fulfillments.filter(getFulfilledFulfillment) || []; + +export const getUnfulfilledLines = (order?: OrderDetails_order) => + order?.lines.filter(line => line.quantity !== line.quantityFulfilled) || []; + +export const getAllOrderFulfilledLines = (order?: OrderDetails_order) => + getFulfilledFulfillemnts(order).reduce( + (result, { lines }) => [...result, ...getParsedFulfiledLines(lines)], + [] + ); + +export function getLineItem<T>( + { id }: { id: string }, + { + initialValue, + isFulfillment = false, + isRefunded = false + }: LineItemOptions<T> +) { + return { + data: { isFulfillment, isRefunded }, + id, + label: null, + value: initialValue + }; +} + +export function getParsedLineData<T>({ + initialValue, + isFulfillment = false, + isRefunded = false +}: LineItemOptions<T>) { + return (item: { id: string }) => + getLineItem(item, { initialValue, isFulfillment, isRefunded }); +} + +export function getParsedLineDataForFulfillmentStatus<T>( + order: OrderDetails_order, + fulfillmentStatus: FulfillmentStatus, + lineItemOptions: LineItemOptions<T> +) { + return getParsedLinesOfFulfillments( + getFulfillmentsWithStatus(order, fulfillmentStatus) + ).map(getParsedLineData(lineItemOptions)); +} + +export const getFulfillmentsWithStatus = ( + order: OrderDetails_order, + fulfillmentStatus: FulfillmentStatus +) => + order?.fulfillments.filter(({ status }) => status === fulfillmentStatus) || + []; + +export const getParsedLinesOfFulfillments = ( + fullfillments: OrderDetails_order_fulfillments[] +) => + fullfillments.reduce( + (result, { lines }) => [...result, ...getParsedFulfiledLines(lines)], + [] + ); + +export const getParsedFulfiledLines = ( + lines: OrderDetailsFragment_fulfillments_lines[] +) => + lines.map(({ id, quantity, orderLine }) => ({ + ...orderLine, + id, + quantity + })); + +export const getById = (idToCompare: string) => (obj: { id: string }) => + obj.id === idToCompare; diff --git a/src/orders/components/OrderUnfulfilledItems/OrderUnfulfilledItems.tsx b/src/orders/components/OrderUnfulfilledItems/OrderUnfulfilledItems.tsx deleted file mode 100644 index 47634f897..000000000 --- a/src/orders/components/OrderUnfulfilledItems/OrderUnfulfilledItems.tsx +++ /dev/null @@ -1,192 +0,0 @@ -import Button from "@material-ui/core/Button"; -import Card from "@material-ui/core/Card"; -import CardActions from "@material-ui/core/CardActions"; -import { makeStyles } from "@material-ui/core/styles"; -import TableBody from "@material-ui/core/TableBody"; -import TableCell from "@material-ui/core/TableCell"; -import TableHead from "@material-ui/core/TableHead"; -import TableRow from "@material-ui/core/TableRow"; -import CardTitle from "@saleor/components/CardTitle"; -import Money from "@saleor/components/Money"; -import ResponsiveTable from "@saleor/components/ResponsiveTable"; -import Skeleton from "@saleor/components/Skeleton"; -import StatusLabel from "@saleor/components/StatusLabel"; -import TableCellAvatar, { - AVATAR_MARGIN -} from "@saleor/components/TableCellAvatar"; -import React from "react"; -import { FormattedMessage, useIntl } from "react-intl"; - -import { maybe } from "../../../misc"; -import { OrderDetails_order_lines } from "../../types/OrderDetails"; - -const useStyles = makeStyles( - { - clickableRow: { - cursor: "pointer" - }, - colName: { - paddingLeft: 0, - width: "auto" - }, - colNameLabel: { - marginLeft: AVATAR_MARGIN - }, - colPrice: { - textAlign: "right", - width: 120 - }, - colQuantity: { - textAlign: "center", - width: 120 - }, - colSku: { - textAlign: "right", - textOverflow: "ellipsis", - width: 120 - }, - colTotal: { - textAlign: "right", - width: 120 - }, - statusBar: { - paddingTop: 0 - }, - table: { - tableLayout: "fixed" - } - }, - { name: "OrderUnfulfilledItems" } -); - -interface OrderUnfulfilledItemsProps { - canFulfill: boolean; - lines: OrderDetails_order_lines[]; - onFulfill: () => void; -} - -const OrderUnfulfilledItems: React.FC<OrderUnfulfilledItemsProps> = props => { - const { canFulfill, lines, onFulfill } = props; - const classes = useStyles(props); - - const intl = useIntl(); - - return ( - <Card> - <CardTitle - title={ - <StatusLabel - label={intl.formatMessage( - { - defaultMessage: "Unfulfilled ({quantity})", - description: "section header" - }, - { - quantity: lines - .map(line => line.quantity - line.quantityFulfilled) - .reduce((prev, curr) => prev + curr, 0) - } - )} - status="error" - /> - } - /> - <ResponsiveTable className={classes.table}> - <TableHead> - <TableRow> - <TableCell className={classes.colName}> - <span className={classes.colNameLabel}> - <FormattedMessage - defaultMessage="Product" - description="product name" - /> - </span> - </TableCell> - <TableCell className={classes.colSku}> - <FormattedMessage - defaultMessage="SKU" - description="ordered product sku" - /> - </TableCell> - <TableCell className={classes.colQuantity}> - <FormattedMessage - defaultMessage="Quantity" - description="ordered products" - /> - </TableCell> - <TableCell className={classes.colPrice}> - <FormattedMessage - defaultMessage="Price" - description="product unit price" - /> - </TableCell> - <TableCell className={classes.colTotal}> - <FormattedMessage - defaultMessage="Total" - description="order line total price" - /> - </TableCell> - </TableRow> - </TableHead> - <TableBody> - {lines.map(line => ( - <TableRow - className={!!line ? classes.clickableRow : undefined} - hover={!!line} - key={maybe(() => line.id)} - > - <TableCellAvatar - className={classes.colName} - thumbnail={maybe(() => line.thumbnail.url)} - > - {maybe(() => line.productName) || <Skeleton />} - </TableCellAvatar> - <TableCell className={classes.colSku}> - {line?.productSku || <Skeleton />} - </TableCell> - <TableCell className={classes.colQuantity}> - {maybe(() => line.quantity - line.quantityFulfilled) || ( - <Skeleton /> - )} - </TableCell> - <TableCell className={classes.colPrice}> - {maybe(() => line.unitPrice.gross) ? ( - <Money money={line.unitPrice.gross} /> - ) : ( - <Skeleton /> - )} - </TableCell> - <TableCell className={classes.colTotal}> - {maybe( - () => - (line.quantity - line.quantityFulfilled) * - line.unitPrice.gross.amount - ) ? ( - <Money - money={{ - amount: - (line.quantity - line.quantityFulfilled) * - line.unitPrice.gross.amount, - currency: line.unitPrice.gross.currency - }} - /> - ) : ( - <Skeleton /> - )} - </TableCell> - </TableRow> - ))} - </TableBody> - </ResponsiveTable> - {canFulfill && ( - <CardActions> - <Button variant="text" color="primary" onClick={onFulfill}> - <FormattedMessage defaultMessage="Fulfill" description="button" /> - </Button> - </CardActions> - )} - </Card> - ); -}; -OrderUnfulfilledItems.displayName = "OrderUnfulfilledItems"; -export default OrderUnfulfilledItems; diff --git a/src/orders/components/OrderUnfulfilledItems/index.ts b/src/orders/components/OrderUnfulfilledItems/index.ts deleted file mode 100644 index 4d7c26864..000000000 --- a/src/orders/components/OrderUnfulfilledItems/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from "./OrderUnfulfilledItems"; -export * from "./OrderUnfulfilledItems"; diff --git a/src/orders/components/OrderUnfulfilledProductsCard/OrderUnfulfilledProductsCard.tsx b/src/orders/components/OrderUnfulfilledProductsCard/OrderUnfulfilledProductsCard.tsx new file mode 100644 index 000000000..0a7697bfa --- /dev/null +++ b/src/orders/components/OrderUnfulfilledProductsCard/OrderUnfulfilledProductsCard.tsx @@ -0,0 +1,64 @@ +import { makeStyles, TableBody } from "@material-ui/core"; +import Button from "@material-ui/core/Button"; +import Card from "@material-ui/core/Card"; +import CardActions from "@material-ui/core/CardActions"; +import CardSpacer from "@saleor/components/CardSpacer"; +import ResponsiveTable from "@saleor/components/ResponsiveTable"; +import { renderCollection } from "@saleor/misc"; +import React from "react"; +import { FormattedMessage } from "react-intl"; + +import { OrderDetails_order_lines } from "../../types/OrderDetails"; +import TableHeader from "../OrderProductsCardElements/OrderProductsCardHeader"; +import TableLine from "../OrderProductsCardElements/OrderProductsTableRow"; +import CardTitle from "../OrderReturnPage/OrderReturnRefundItemsCard/CardTitle"; + +const useStyles = makeStyles( + () => ({ + table: { + tableLayout: "fixed" + } + }), + { name: "OrderUnfulfilledItems" } +); + +interface OrderUnfulfilledProductsCardProps { + canFulfill: boolean; + lines: OrderDetails_order_lines[]; + onFulfill: () => void; +} + +const OrderUnfulfilledProductsCard: React.FC<OrderUnfulfilledProductsCardProps> = props => { + const { canFulfill, lines, onFulfill } = props; + const classes = useStyles({}); + + if (!lines.length) { + return null; + } + + return ( + <> + <Card> + <CardTitle withStatus status="unfulfilled" /> + <ResponsiveTable className={classes.table}> + <TableHeader /> + <TableBody> + {renderCollection(lines, line => ( + <TableLine isOrderLine line={line} /> + ))} + </TableBody> + </ResponsiveTable> + {canFulfill && ( + <CardActions> + <Button variant="text" color="primary" onClick={onFulfill}> + <FormattedMessage defaultMessage="Fulfill" description="button" /> + </Button> + </CardActions> + )} + </Card> + <CardSpacer /> + </> + ); +}; + +export default OrderUnfulfilledProductsCard; diff --git a/src/orders/components/OrderUnfulfilledProductsCard/index.ts b/src/orders/components/OrderUnfulfilledProductsCard/index.ts new file mode 100644 index 000000000..edeae2d5a --- /dev/null +++ b/src/orders/components/OrderUnfulfilledProductsCard/index.ts @@ -0,0 +1,2 @@ +export { default } from "./OrderUnfulfilledProductsCard"; +export * from "./OrderUnfulfilledProductsCard"; diff --git a/src/orders/fixtures.ts b/src/orders/fixtures.ts index 9665bb10e..d7da06e92 100644 --- a/src/orders/fixtures.ts +++ b/src/orders/fixtures.ts @@ -834,13 +834,16 @@ export const order = (placeholder: string): OrderDetails_order => ({ lines: [], message: null, quantity: 1, + relatedOrder: null, shippingCostsIncluded: false, transactionReference: "123", type: OrderEventsEnum.FULFILLMENT_FULFILLED_ITEMS, user: { __typename: "User", email: "admin@example.com", - id: "QWRkcmVzczoxNQ==" + firstName: "John", + id: "QWRkcmVzczoxNQ==", + lastName: "Doe" } }, { @@ -875,13 +878,16 @@ export const order = (placeholder: string): OrderDetails_order => ({ ], message: null, quantity: 1, + relatedOrder: null, shippingCostsIncluded: true, transactionReference: "123", type: OrderEventsEnum.FULFILLMENT_REFUNDED, user: { __typename: "User", email: "admin@example.com", - id: "QWRkcmVzczoxNQ==" + firstName: "Jane", + id: "QWRkcmVzczoxNQ==", + lastName: "Doe" } }, { @@ -895,6 +901,7 @@ export const order = (placeholder: string): OrderDetails_order => ({ lines: [], message: "This is note", quantity: null, + relatedOrder: null, shippingCostsIncluded: false, transactionReference: "124", type: OrderEventsEnum.NOTE_ADDED, @@ -911,6 +918,7 @@ export const order = (placeholder: string): OrderDetails_order => ({ lines: [], message: "This is note", quantity: null, + relatedOrder: null, shippingCostsIncluded: false, transactionReference: "125", type: OrderEventsEnum.NOTE_ADDED, @@ -927,6 +935,7 @@ export const order = (placeholder: string): OrderDetails_order => ({ lines: [], message: "Note from external service", quantity: null, + relatedOrder: null, shippingCostsIncluded: false, transactionReference: "126", type: OrderEventsEnum.EXTERNAL_SERVICE_NOTIFICATION, @@ -943,6 +952,7 @@ export const order = (placeholder: string): OrderDetails_order => ({ lines: [], message: null, quantity: null, + relatedOrder: null, shippingCostsIncluded: false, transactionReference: "127", type: OrderEventsEnum.EMAIL_SENT, @@ -959,6 +969,7 @@ export const order = (placeholder: string): OrderDetails_order => ({ lines: [], message: null, quantity: null, + relatedOrder: null, shippingCostsIncluded: false, transactionReference: "128", type: OrderEventsEnum.EMAIL_SENT, @@ -975,6 +986,7 @@ export const order = (placeholder: string): OrderDetails_order => ({ lines: [], message: null, quantity: null, + relatedOrder: null, shippingCostsIncluded: false, transactionReference: "129", type: OrderEventsEnum.PAYMENT_AUTHORIZED, diff --git a/src/orders/index.tsx b/src/orders/index.tsx index 46d353c0c..93b129441 100644 --- a/src/orders/index.tsx +++ b/src/orders/index.tsx @@ -16,6 +16,7 @@ import { OrderListUrlSortField, orderPath, orderRefundPath, + orderReturnPath, orderSettingsPath, OrderUrlQueryParams } from "./urls"; @@ -24,6 +25,7 @@ import OrderDraftListComponent from "./views/OrderDraftList"; import OrderFulfillComponent from "./views/OrderFulfill"; import OrderListComponent from "./views/OrderList"; import OrderRefundComponent from "./views/OrderRefund"; +import OrderReturnComponent from "./views/OrderReturn"; import OrderSettings from "./views/OrderSettings"; const OrderList: React.FC<RouteComponentProps<any>> = ({ location }) => { @@ -71,6 +73,10 @@ const OrderRefund: React.FC<RouteComponentProps<any>> = ({ match }) => ( <OrderRefundComponent orderId={decodeURIComponent(match.params.id)} /> ); +const OrderReturn: React.FC<RouteComponentProps<any>> = ({ match }) => ( + <OrderReturnComponent orderId={decodeURIComponent(match.params.id)} /> +); + const Component = () => { const intl = useIntl(); @@ -82,6 +88,7 @@ const Component = () => { <Route exact path={orderDraftListPath} component={OrderDraftList} /> <Route exact path={orderListPath} component={OrderList} /> <Route path={orderFulfillPath(":id")} component={OrderFulfill} /> + <Route path={orderReturnPath(":id")} component={OrderReturn} /> <Route path={orderRefundPath(":id")} component={OrderRefund} /> <Route path={orderPath(":id")} component={OrderDetails} /> </Switch> diff --git a/src/orders/mutations.ts b/src/orders/mutations.ts index c201a9389..393eb7c1e 100644 --- a/src/orders/mutations.ts +++ b/src/orders/mutations.ts @@ -14,6 +14,10 @@ import makeMutation from "@saleor/hooks/makeMutation"; import gql from "graphql-tag"; import { TypedMutation } from "../mutations"; +import { + FulfillmentReturnProducts, + FulfillmentReturnProductsVariables +} from "./types/FulfillmentReturnProducts"; import { FulfillOrder, FulfillOrderVariables } from "./types/FulfillOrder"; import { InvoiceEmailSend, @@ -172,6 +176,32 @@ const orderDraftFinalizeMutation = gql` } } `; + +const orderReturnCreateMutation = gql` + ${orderErrorFragment} + mutation FulfillmentReturnProducts( + $id: ID! + $input: OrderReturnProductsInput! + ) { + orderFulfillmentReturnProducts(input: $input, order: $id) { + errors: orderErrors { + ...OrderErrorFragment + } + order { + id + } + replaceOrder { + id + } + } + } +`; + +export const useOrderReturnCreateMutation = makeMutation< + FulfillmentReturnProducts, + FulfillmentReturnProductsVariables +>(orderReturnCreateMutation); + export const TypedOrderDraftFinalizeMutation = TypedMutation< OrderDraftFinalize, OrderDraftFinalizeVariables @@ -191,6 +221,7 @@ const orderRefundMutation = gql` } } `; + export const useOrderRefundMutation = makeMutation< OrderRefund, OrderRefundVariables diff --git a/src/orders/queries.ts b/src/orders/queries.ts index 09e2bd84e..a033e621c 100644 --- a/src/orders/queries.ts +++ b/src/orders/queries.ts @@ -157,6 +157,10 @@ export const TypedOrderDetailsQuery = TypedQuery< OrderDetailsVariables >(orderDetailsQuery); +export const useOrderQuery = makeQuery<OrderDetails, OrderDetailsVariables>( + orderDetailsQuery +); + export const searchOrderVariant = gql` query SearchOrderVariant($first: Int!, $query: String!, $after: String) { search: products(first: $first, after: $after, filter: { search: $query }) { diff --git a/src/orders/types/FulfillOrder.ts b/src/orders/types/FulfillOrder.ts index 6c72a4282..45aeb0896 100644 --- a/src/orders/types/FulfillOrder.ts +++ b/src/orders/types/FulfillOrder.ts @@ -50,10 +50,18 @@ export interface FulfillOrder_orderFulfill_order_billingAddress { streetAddress2: string; } +export interface FulfillOrder_orderFulfill_order_events_relatedOrder { + __typename: "Order"; + id: string; + number: string | null; +} + export interface FulfillOrder_orderFulfill_order_events_user { __typename: "User"; id: string; email: string; + firstName: string; + lastName: string; } export interface FulfillOrder_orderFulfill_order_events_lines_orderLine { @@ -78,6 +86,7 @@ export interface FulfillOrder_orderFulfill_order_events { email: string | null; emailType: OrderEventsEmailsEnum | null; invoiceNumber: string | null; + relatedOrder: FulfillOrder_orderFulfill_order_events_relatedOrder | null; message: string | null; quantity: number | null; transactionReference: string | null; diff --git a/src/orders/types/FulfillmentReturnProducts.ts b/src/orders/types/FulfillmentReturnProducts.ts new file mode 100644 index 000000000..09de2c887 --- /dev/null +++ b/src/orders/types/FulfillmentReturnProducts.ts @@ -0,0 +1,41 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { OrderReturnProductsInput, OrderErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: FulfillmentReturnProducts +// ==================================================== + +export interface FulfillmentReturnProducts_orderFulfillmentReturnProducts_errors { + __typename: "OrderError"; + code: OrderErrorCode; + field: string | null; +} + +export interface FulfillmentReturnProducts_orderFulfillmentReturnProducts_order { + __typename: "Order"; + id: string; +} + +export interface FulfillmentReturnProducts_orderFulfillmentReturnProducts_replaceOrder { + __typename: "Order"; + id: string; +} + +export interface FulfillmentReturnProducts_orderFulfillmentReturnProducts { + __typename: "FulfillmentReturnProducts"; + errors: FulfillmentReturnProducts_orderFulfillmentReturnProducts_errors[]; + order: FulfillmentReturnProducts_orderFulfillmentReturnProducts_order | null; + replaceOrder: FulfillmentReturnProducts_orderFulfillmentReturnProducts_replaceOrder | null; +} + +export interface FulfillmentReturnProducts { + orderFulfillmentReturnProducts: FulfillmentReturnProducts_orderFulfillmentReturnProducts | null; +} + +export interface FulfillmentReturnProductsVariables { + id: string; + input: OrderReturnProductsInput; +} diff --git a/src/orders/types/OrderAddNote.ts b/src/orders/types/OrderAddNote.ts index b8998271a..df92a2e07 100644 --- a/src/orders/types/OrderAddNote.ts +++ b/src/orders/types/OrderAddNote.ts @@ -14,10 +14,18 @@ export interface OrderAddNote_orderAddNote_errors { field: string | null; } +export interface OrderAddNote_orderAddNote_order_events_relatedOrder { + __typename: "Order"; + id: string; + number: string | null; +} + export interface OrderAddNote_orderAddNote_order_events_user { __typename: "User"; id: string; email: string; + firstName: string; + lastName: string; } export interface OrderAddNote_orderAddNote_order_events_lines_orderLine { @@ -42,6 +50,7 @@ export interface OrderAddNote_orderAddNote_order_events { email: string | null; emailType: OrderEventsEmailsEnum | null; invoiceNumber: string | null; + relatedOrder: OrderAddNote_orderAddNote_order_events_relatedOrder | null; message: string | null; quantity: number | null; transactionReference: string | null; diff --git a/src/orders/types/OrderCancel.ts b/src/orders/types/OrderCancel.ts index 48f3ab2ba..822c51b6f 100644 --- a/src/orders/types/OrderCancel.ts +++ b/src/orders/types/OrderCancel.ts @@ -48,10 +48,18 @@ export interface OrderCancel_orderCancel_order_billingAddress { streetAddress2: string; } +export interface OrderCancel_orderCancel_order_events_relatedOrder { + __typename: "Order"; + id: string; + number: string | null; +} + export interface OrderCancel_orderCancel_order_events_user { __typename: "User"; id: string; email: string; + firstName: string; + lastName: string; } export interface OrderCancel_orderCancel_order_events_lines_orderLine { @@ -76,6 +84,7 @@ export interface OrderCancel_orderCancel_order_events { email: string | null; emailType: OrderEventsEmailsEnum | null; invoiceNumber: string | null; + relatedOrder: OrderCancel_orderCancel_order_events_relatedOrder | null; message: string | null; quantity: number | null; transactionReference: string | null; diff --git a/src/orders/types/OrderCapture.ts b/src/orders/types/OrderCapture.ts index caacb902c..3420640b0 100644 --- a/src/orders/types/OrderCapture.ts +++ b/src/orders/types/OrderCapture.ts @@ -48,10 +48,18 @@ export interface OrderCapture_orderCapture_order_billingAddress { streetAddress2: string; } +export interface OrderCapture_orderCapture_order_events_relatedOrder { + __typename: "Order"; + id: string; + number: string | null; +} + export interface OrderCapture_orderCapture_order_events_user { __typename: "User"; id: string; email: string; + firstName: string; + lastName: string; } export interface OrderCapture_orderCapture_order_events_lines_orderLine { @@ -76,6 +84,7 @@ export interface OrderCapture_orderCapture_order_events { email: string | null; emailType: OrderEventsEmailsEnum | null; invoiceNumber: string | null; + relatedOrder: OrderCapture_orderCapture_order_events_relatedOrder | null; message: string | null; quantity: number | null; transactionReference: string | null; diff --git a/src/orders/types/OrderConfirm.ts b/src/orders/types/OrderConfirm.ts index cf6a78b3f..fe95a9613 100644 --- a/src/orders/types/OrderConfirm.ts +++ b/src/orders/types/OrderConfirm.ts @@ -48,10 +48,18 @@ export interface OrderConfirm_orderConfirm_order_billingAddress { streetAddress2: string; } +export interface OrderConfirm_orderConfirm_order_events_relatedOrder { + __typename: "Order"; + id: string; + number: string | null; +} + export interface OrderConfirm_orderConfirm_order_events_user { __typename: "User"; id: string; email: string; + firstName: string; + lastName: string; } export interface OrderConfirm_orderConfirm_order_events_lines_orderLine { @@ -76,6 +84,7 @@ export interface OrderConfirm_orderConfirm_order_events { email: string | null; emailType: OrderEventsEmailsEnum | null; invoiceNumber: string | null; + relatedOrder: OrderConfirm_orderConfirm_order_events_relatedOrder | null; message: string | null; quantity: number | null; transactionReference: string | null; diff --git a/src/orders/types/OrderDetails.ts b/src/orders/types/OrderDetails.ts index 35d0b6576..34b754218 100644 --- a/src/orders/types/OrderDetails.ts +++ b/src/orders/types/OrderDetails.ts @@ -42,10 +42,18 @@ export interface OrderDetails_order_billingAddress { streetAddress2: string; } +export interface OrderDetails_order_events_relatedOrder { + __typename: "Order"; + id: string; + number: string | null; +} + export interface OrderDetails_order_events_user { __typename: "User"; id: string; email: string; + firstName: string; + lastName: string; } export interface OrderDetails_order_events_lines_orderLine { @@ -70,6 +78,7 @@ export interface OrderDetails_order_events { email: string | null; emailType: OrderEventsEmailsEnum | null; invoiceNumber: string | null; + relatedOrder: OrderDetails_order_events_relatedOrder | null; message: string | null; quantity: number | null; transactionReference: string | null; diff --git a/src/orders/types/OrderDraftCancel.ts b/src/orders/types/OrderDraftCancel.ts index ca189622d..515e4061b 100644 --- a/src/orders/types/OrderDraftCancel.ts +++ b/src/orders/types/OrderDraftCancel.ts @@ -48,10 +48,18 @@ export interface OrderDraftCancel_draftOrderDelete_order_billingAddress { streetAddress2: string; } +export interface OrderDraftCancel_draftOrderDelete_order_events_relatedOrder { + __typename: "Order"; + id: string; + number: string | null; +} + export interface OrderDraftCancel_draftOrderDelete_order_events_user { __typename: "User"; id: string; email: string; + firstName: string; + lastName: string; } export interface OrderDraftCancel_draftOrderDelete_order_events_lines_orderLine { @@ -76,6 +84,7 @@ export interface OrderDraftCancel_draftOrderDelete_order_events { email: string | null; emailType: OrderEventsEmailsEnum | null; invoiceNumber: string | null; + relatedOrder: OrderDraftCancel_draftOrderDelete_order_events_relatedOrder | null; message: string | null; quantity: number | null; transactionReference: string | null; diff --git a/src/orders/types/OrderDraftFinalize.ts b/src/orders/types/OrderDraftFinalize.ts index f355c8e6c..d896114bd 100644 --- a/src/orders/types/OrderDraftFinalize.ts +++ b/src/orders/types/OrderDraftFinalize.ts @@ -48,10 +48,18 @@ export interface OrderDraftFinalize_draftOrderComplete_order_billingAddress { streetAddress2: string; } +export interface OrderDraftFinalize_draftOrderComplete_order_events_relatedOrder { + __typename: "Order"; + id: string; + number: string | null; +} + export interface OrderDraftFinalize_draftOrderComplete_order_events_user { __typename: "User"; id: string; email: string; + firstName: string; + lastName: string; } export interface OrderDraftFinalize_draftOrderComplete_order_events_lines_orderLine { @@ -76,6 +84,7 @@ export interface OrderDraftFinalize_draftOrderComplete_order_events { email: string | null; emailType: OrderEventsEmailsEnum | null; invoiceNumber: string | null; + relatedOrder: OrderDraftFinalize_draftOrderComplete_order_events_relatedOrder | null; message: string | null; quantity: number | null; transactionReference: string | null; diff --git a/src/orders/types/OrderDraftUpdate.ts b/src/orders/types/OrderDraftUpdate.ts index 5ab2b9f4c..739060f42 100644 --- a/src/orders/types/OrderDraftUpdate.ts +++ b/src/orders/types/OrderDraftUpdate.ts @@ -48,10 +48,18 @@ export interface OrderDraftUpdate_draftOrderUpdate_order_billingAddress { streetAddress2: string; } +export interface OrderDraftUpdate_draftOrderUpdate_order_events_relatedOrder { + __typename: "Order"; + id: string; + number: string | null; +} + export interface OrderDraftUpdate_draftOrderUpdate_order_events_user { __typename: "User"; id: string; email: string; + firstName: string; + lastName: string; } export interface OrderDraftUpdate_draftOrderUpdate_order_events_lines_orderLine { @@ -76,6 +84,7 @@ export interface OrderDraftUpdate_draftOrderUpdate_order_events { email: string | null; emailType: OrderEventsEmailsEnum | null; invoiceNumber: string | null; + relatedOrder: OrderDraftUpdate_draftOrderUpdate_order_events_relatedOrder | null; message: string | null; quantity: number | null; transactionReference: string | null; diff --git a/src/orders/types/OrderFulfillmentCancel.ts b/src/orders/types/OrderFulfillmentCancel.ts index 0899a3bd8..54ecd69ee 100644 --- a/src/orders/types/OrderFulfillmentCancel.ts +++ b/src/orders/types/OrderFulfillmentCancel.ts @@ -48,10 +48,18 @@ export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_billingAddr streetAddress2: string; } +export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_events_relatedOrder { + __typename: "Order"; + id: string; + number: string | null; +} + export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_events_user { __typename: "User"; id: string; email: string; + firstName: string; + lastName: string; } export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_events_lines_orderLine { @@ -76,6 +84,7 @@ export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_events { email: string | null; emailType: OrderEventsEmailsEnum | null; invoiceNumber: string | null; + relatedOrder: OrderFulfillmentCancel_orderFulfillmentCancel_order_events_relatedOrder | null; message: string | null; quantity: number | null; transactionReference: string | null; diff --git a/src/orders/types/OrderFulfillmentRefundProducts.ts b/src/orders/types/OrderFulfillmentRefundProducts.ts index 58683080f..5cc66f5a3 100644 --- a/src/orders/types/OrderFulfillmentRefundProducts.ts +++ b/src/orders/types/OrderFulfillmentRefundProducts.ts @@ -113,10 +113,18 @@ export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_o streetAddress2: string; } +export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_events_relatedOrder { + __typename: "Order"; + id: string; + number: string | null; +} + export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_events_user { __typename: "User"; id: string; email: string; + firstName: string; + lastName: string; } export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_events_lines_orderLine { @@ -141,6 +149,7 @@ export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_o email: string | null; emailType: OrderEventsEmailsEnum | null; invoiceNumber: string | null; + relatedOrder: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_events_relatedOrder | null; message: string | null; quantity: number | null; transactionReference: string | null; diff --git a/src/orders/types/OrderFulfillmentUpdateTracking.ts b/src/orders/types/OrderFulfillmentUpdateTracking.ts index 513f1fdb4..810f4c66f 100644 --- a/src/orders/types/OrderFulfillmentUpdateTracking.ts +++ b/src/orders/types/OrderFulfillmentUpdateTracking.ts @@ -48,10 +48,18 @@ export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_o streetAddress2: string; } +export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_events_relatedOrder { + __typename: "Order"; + id: string; + number: string | null; +} + export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_events_user { __typename: "User"; id: string; email: string; + firstName: string; + lastName: string; } export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_events_lines_orderLine { @@ -76,6 +84,7 @@ export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_o email: string | null; emailType: OrderEventsEmailsEnum | null; invoiceNumber: string | null; + relatedOrder: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_events_relatedOrder | null; message: string | null; quantity: number | null; transactionReference: string | null; diff --git a/src/orders/types/OrderLineDelete.ts b/src/orders/types/OrderLineDelete.ts index 5196b89b8..adf804d37 100644 --- a/src/orders/types/OrderLineDelete.ts +++ b/src/orders/types/OrderLineDelete.ts @@ -48,10 +48,18 @@ export interface OrderLineDelete_draftOrderLineDelete_order_billingAddress { streetAddress2: string; } +export interface OrderLineDelete_draftOrderLineDelete_order_events_relatedOrder { + __typename: "Order"; + id: string; + number: string | null; +} + export interface OrderLineDelete_draftOrderLineDelete_order_events_user { __typename: "User"; id: string; email: string; + firstName: string; + lastName: string; } export interface OrderLineDelete_draftOrderLineDelete_order_events_lines_orderLine { @@ -76,6 +84,7 @@ export interface OrderLineDelete_draftOrderLineDelete_order_events { email: string | null; emailType: OrderEventsEmailsEnum | null; invoiceNumber: string | null; + relatedOrder: OrderLineDelete_draftOrderLineDelete_order_events_relatedOrder | null; message: string | null; quantity: number | null; transactionReference: string | null; diff --git a/src/orders/types/OrderLineUpdate.ts b/src/orders/types/OrderLineUpdate.ts index 9e408659b..4f373e6ea 100644 --- a/src/orders/types/OrderLineUpdate.ts +++ b/src/orders/types/OrderLineUpdate.ts @@ -48,10 +48,18 @@ export interface OrderLineUpdate_draftOrderLineUpdate_order_billingAddress { streetAddress2: string; } +export interface OrderLineUpdate_draftOrderLineUpdate_order_events_relatedOrder { + __typename: "Order"; + id: string; + number: string | null; +} + export interface OrderLineUpdate_draftOrderLineUpdate_order_events_user { __typename: "User"; id: string; email: string; + firstName: string; + lastName: string; } export interface OrderLineUpdate_draftOrderLineUpdate_order_events_lines_orderLine { @@ -76,6 +84,7 @@ export interface OrderLineUpdate_draftOrderLineUpdate_order_events { email: string | null; emailType: OrderEventsEmailsEnum | null; invoiceNumber: string | null; + relatedOrder: OrderLineUpdate_draftOrderLineUpdate_order_events_relatedOrder | null; message: string | null; quantity: number | null; transactionReference: string | null; diff --git a/src/orders/types/OrderLinesAdd.ts b/src/orders/types/OrderLinesAdd.ts index a28a6b2be..4d2390378 100644 --- a/src/orders/types/OrderLinesAdd.ts +++ b/src/orders/types/OrderLinesAdd.ts @@ -48,10 +48,18 @@ export interface OrderLinesAdd_draftOrderLinesCreate_order_billingAddress { streetAddress2: string; } +export interface OrderLinesAdd_draftOrderLinesCreate_order_events_relatedOrder { + __typename: "Order"; + id: string; + number: string | null; +} + export interface OrderLinesAdd_draftOrderLinesCreate_order_events_user { __typename: "User"; id: string; email: string; + firstName: string; + lastName: string; } export interface OrderLinesAdd_draftOrderLinesCreate_order_events_lines_orderLine { @@ -76,6 +84,7 @@ export interface OrderLinesAdd_draftOrderLinesCreate_order_events { email: string | null; emailType: OrderEventsEmailsEnum | null; invoiceNumber: string | null; + relatedOrder: OrderLinesAdd_draftOrderLinesCreate_order_events_relatedOrder | null; message: string | null; quantity: number | null; transactionReference: string | null; diff --git a/src/orders/types/OrderMarkAsPaid.ts b/src/orders/types/OrderMarkAsPaid.ts index fc3719d4b..4ac19d2b8 100644 --- a/src/orders/types/OrderMarkAsPaid.ts +++ b/src/orders/types/OrderMarkAsPaid.ts @@ -48,10 +48,18 @@ export interface OrderMarkAsPaid_orderMarkAsPaid_order_billingAddress { streetAddress2: string; } +export interface OrderMarkAsPaid_orderMarkAsPaid_order_events_relatedOrder { + __typename: "Order"; + id: string; + number: string | null; +} + export interface OrderMarkAsPaid_orderMarkAsPaid_order_events_user { __typename: "User"; id: string; email: string; + firstName: string; + lastName: string; } export interface OrderMarkAsPaid_orderMarkAsPaid_order_events_lines_orderLine { @@ -76,6 +84,7 @@ export interface OrderMarkAsPaid_orderMarkAsPaid_order_events { email: string | null; emailType: OrderEventsEmailsEnum | null; invoiceNumber: string | null; + relatedOrder: OrderMarkAsPaid_orderMarkAsPaid_order_events_relatedOrder | null; message: string | null; quantity: number | null; transactionReference: string | null; diff --git a/src/orders/types/OrderRefund.ts b/src/orders/types/OrderRefund.ts index c47f96a0d..55c3b8887 100644 --- a/src/orders/types/OrderRefund.ts +++ b/src/orders/types/OrderRefund.ts @@ -48,10 +48,18 @@ export interface OrderRefund_orderRefund_order_billingAddress { streetAddress2: string; } +export interface OrderRefund_orderRefund_order_events_relatedOrder { + __typename: "Order"; + id: string; + number: string | null; +} + export interface OrderRefund_orderRefund_order_events_user { __typename: "User"; id: string; email: string; + firstName: string; + lastName: string; } export interface OrderRefund_orderRefund_order_events_lines_orderLine { @@ -76,6 +84,7 @@ export interface OrderRefund_orderRefund_order_events { email: string | null; emailType: OrderEventsEmailsEnum | null; invoiceNumber: string | null; + relatedOrder: OrderRefund_orderRefund_order_events_relatedOrder | null; message: string | null; quantity: number | null; transactionReference: string | null; diff --git a/src/orders/types/OrderUpdate.ts b/src/orders/types/OrderUpdate.ts index b9fb56b5c..3e496557c 100644 --- a/src/orders/types/OrderUpdate.ts +++ b/src/orders/types/OrderUpdate.ts @@ -48,10 +48,18 @@ export interface OrderUpdate_orderUpdate_order_billingAddress { streetAddress2: string; } +export interface OrderUpdate_orderUpdate_order_events_relatedOrder { + __typename: "Order"; + id: string; + number: string | null; +} + export interface OrderUpdate_orderUpdate_order_events_user { __typename: "User"; id: string; email: string; + firstName: string; + lastName: string; } export interface OrderUpdate_orderUpdate_order_events_lines_orderLine { @@ -76,6 +84,7 @@ export interface OrderUpdate_orderUpdate_order_events { email: string | null; emailType: OrderEventsEmailsEnum | null; invoiceNumber: string | null; + relatedOrder: OrderUpdate_orderUpdate_order_events_relatedOrder | null; message: string | null; quantity: number | null; transactionReference: string | null; diff --git a/src/orders/types/OrderVoid.ts b/src/orders/types/OrderVoid.ts index 4c052297d..a772e25e1 100644 --- a/src/orders/types/OrderVoid.ts +++ b/src/orders/types/OrderVoid.ts @@ -48,10 +48,18 @@ export interface OrderVoid_orderVoid_order_billingAddress { streetAddress2: string; } +export interface OrderVoid_orderVoid_order_events_relatedOrder { + __typename: "Order"; + id: string; + number: string | null; +} + export interface OrderVoid_orderVoid_order_events_user { __typename: "User"; id: string; email: string; + firstName: string; + lastName: string; } export interface OrderVoid_orderVoid_order_events_lines_orderLine { @@ -76,6 +84,7 @@ export interface OrderVoid_orderVoid_order_events { email: string | null; emailType: OrderEventsEmailsEnum | null; invoiceNumber: string | null; + relatedOrder: OrderVoid_orderVoid_order_events_relatedOrder | null; message: string | null; quantity: number | null; transactionReference: string | null; diff --git a/src/orders/urls.ts b/src/orders/urls.ts index 615f57043..47be7712f 100644 --- a/src/orders/urls.ts +++ b/src/orders/urls.ts @@ -99,6 +99,7 @@ export const orderDraftListUrl = ( }; export const orderPath = (id: string) => urlJoin(orderSectionUrl, id); + export type OrderUrlDialog = | "add-order-line" | "cancel" @@ -112,17 +113,26 @@ export type OrderUrlDialog = | "mark-paid" | "void" | "invoice-send"; + export type OrderUrlQueryParams = Dialog<OrderUrlDialog> & SingleAction; + export const orderUrl = (id: string, params?: OrderUrlQueryParams) => orderPath(encodeURIComponent(id)) + "?" + stringifyQs(params); export const orderFulfillPath = (id: string) => urlJoin(orderPath(id), "fulfill"); + +export const orderReturnPath = (id: string) => urlJoin(orderPath(id), "return"); + export const orderFulfillUrl = (id: string) => orderFulfillPath(encodeURIComponent(id)); export const orderSettingsPath = urlJoin(orderSectionUrl, "settings"); export const orderRefundPath = (id: string) => urlJoin(orderPath(id), "refund"); + export const orderRefundUrl = (id: string) => orderRefundPath(encodeURIComponent(id)); + +export const orderReturnUrl = (id: string) => + orderReturnPath(encodeURIComponent(id)); diff --git a/src/orders/utils/data.test.ts b/src/orders/utils/data.test.ts index 3330444bb..152e08a04 100644 --- a/src/orders/utils/data.test.ts +++ b/src/orders/utils/data.test.ts @@ -1,8 +1,17 @@ /* eslint-disable sort-keys */ import { FormsetData } from "@saleor/hooks/useFormset"; -import { FulfillmentStatus } from "@saleor/types/globalTypes"; +import { + FulfillmentStatus, + OrderStatus, + PaymentChargeStatusEnum +} from "@saleor/types/globalTypes"; -import { OrderDetails_order_fulfillments_lines } from "../types/OrderDetails"; +import { LineItemData } from "../components/OrderReturnPage/form"; +import { + OrderDetails_order, + OrderDetails_order_fulfillments_lines, + OrderDetails_order_lines +} from "../types/OrderDetails"; import { OrderRefundData_order_fulfillments, OrderRefundData_order_lines @@ -11,10 +20,53 @@ import { getAllFulfillmentLinesPriceSum, getPreviouslyRefundedPrice, getRefundedLinesPriceSum, + getReplacedProductsAmount, + getReturnSelectedProductsAmount, mergeRepeatedOrderLines, OrderWithTotalAndTotalCaptured } from "./data"; +const orderBase: OrderDetails_order = { + __typename: "Order", + actions: [], + availableShippingMethods: [], + canFinalize: true, + channel: null, + billingAddress: { + __typename: "Address", + city: "Port Danielshire", + cityArea: "", + companyName: "", + country: { + __typename: "CountryDisplay", + code: "SE", + country: "Szwecja" + }, + countryArea: "", + firstName: "Elizabeth", + id: "QWRkcmVzczoy", + lastName: "Vaughn", + phone: "", + postalCode: "52203", + streetAddress1: "419 Ruiz Orchard Apt. 199", + streetAddress2: "" + }, + created: "2018-09-11T09:37:30.124154+00:00", + id: "T3JkZXI6MTk=", + number: "19", + paymentStatus: PaymentChargeStatusEnum.FULLY_CHARGED, + status: OrderStatus.FULFILLED, + // @ts-ignore + total: { + __typename: "TaxedMoney", + gross: { + __typename: "Money", + amount: 1215.89, + currency: "USD" + } + } +}; + describe("Get previously refunded price", () => { it("is able to calculate refunded price from order", () => { const order: OrderWithTotalAndTotalCaptured = { @@ -397,6 +449,762 @@ describe("Get get all fulfillment lines price sum", () => { }); }); +describe("Get the total value of all replaced products", () => { + it("sums up correctly", () => { + const unfulfilledLines: OrderDetails_order_lines[] = [ + { + id: "1", + isShippingRequired: false, + variant: { + id: "UHJvZHVjdFZhcmlhbnQ6MzE3", + quantityAvailable: 50, + __typename: "ProductVariant" + }, + productName: "Lake Tunes", + productSku: "lake-tunes-mp3", + quantity: 2, + quantityFulfilled: 2, + unitPrice: { + gross: { + amount: 9.99, + currency: "USD", + __typename: "Money" + }, + net: { + amount: 9.99, + currency: "USD", + __typename: "Money" + }, + __typename: "TaxedMoney" + }, + thumbnail: { + url: + "http://localhost:8000/media/__sized__/products/saleor-digital-03_2-thumbnail-255x255.png", + __typename: "Image" + }, + __typename: "OrderLine" + }, + { + id: "2", + isShippingRequired: false, + variant: { + id: "UHJvZHVjdFZhcmlhbnQ6MzE3", + quantityAvailable: 50, + __typename: "ProductVariant" + }, + productName: "Lake Tunes", + productSku: "lake-tunes-mp3", + quantity: 10, + quantityFulfilled: 2, + unitPrice: { + gross: { + amount: 9.99, + currency: "USD", + __typename: "Money" + }, + net: { + amount: 9.99, + currency: "USD", + __typename: "Money" + }, + __typename: "TaxedMoney" + }, + thumbnail: { + url: + "http://localhost:8000/media/__sized__/products/saleor-digital-03_2-thumbnail-255x255.png", + __typename: "Image" + }, + __typename: "OrderLine" + }, + { + id: "3", + isShippingRequired: true, + variant: { + id: "UHJvZHVjdFZhcmlhbnQ6Mjg2", + quantityAvailable: 50, + __typename: "ProductVariant" + }, + productName: "T-shirt", + productSku: "29810068", + quantity: 6, + quantityFulfilled: 1, + unitPrice: { + gross: { + amount: 2.5, + currency: "USD", + __typename: "Money" + }, + net: { + amount: 2.5, + currency: "USD", + __typename: "Money" + }, + __typename: "TaxedMoney" + }, + thumbnail: { + url: + "http://localhost:8000/media/__sized__/products/saleordemoproduct_cl_boot06_1-thumbnail-255x255.png", + __typename: "Image" + }, + __typename: "OrderLine" + } + ]; + + const fulfilledLines: OrderDetails_order_fulfillments_lines[] = [ + { + id: "4", + quantity: 1, + orderLine: { + id: "T3JkZXJMaW5lOjQ1", + isShippingRequired: false, + variant: { + id: "UHJvZHVjdFZhcmlhbnQ6MzE3", + quantityAvailable: 50, + __typename: "ProductVariant" + }, + productName: "Lake Tunes", + productSku: "lake-tunes-mp3", + quantity: 20, + quantityFulfilled: 6, + unitPrice: { + gross: { + amount: 9.99, + currency: "USD", + __typename: "Money" + }, + net: { + amount: 9.99, + currency: "USD", + __typename: "Money" + }, + __typename: "TaxedMoney" + }, + thumbnail: { + url: + "http://localhost:8000/media/__sized__/products/saleor-digital-03_2-thumbnail-255x255.png", + __typename: "Image" + }, + __typename: "OrderLine" + }, + __typename: "FulfillmentLine" + }, + { + id: "5", + quantity: 1, + orderLine: { + id: "T3JkZXJMaW5lOjQ1", + isShippingRequired: false, + variant: { + id: "UHJvZHVjdFZhcmlhbnQ6MzE3", + quantityAvailable: 50, + __typename: "ProductVariant" + }, + productName: "Lake Tunes", + productSku: "lake-tunes-mp3", + quantity: 25, + quantityFulfilled: 8, + unitPrice: { + gross: { + amount: 9.99, + currency: "USD", + __typename: "Money" + }, + net: { + amount: 9.99, + currency: "USD", + __typename: "Money" + }, + __typename: "TaxedMoney" + }, + thumbnail: { + url: + "http://localhost:8000/media/__sized__/products/saleor-digital-03_2-thumbnail-255x255.png", + __typename: "Image" + }, + __typename: "OrderLine" + }, + __typename: "FulfillmentLine" + }, + { + id: "6", + quantity: 1, + orderLine: { + id: "T3JkZXJMaW5lOjQ3", + isShippingRequired: true, + variant: { + id: "UHJvZHVjdFZhcmlhbnQ6Mjg2", + quantityAvailable: 50, + __typename: "ProductVariant" + }, + productName: "T-shirt", + productSku: "29810068", + quantity: 10, + quantityFulfilled: 3, + unitPrice: { + gross: { + amount: 2.5, + currency: "USD", + __typename: "Money" + }, + net: { + amount: 2.5, + currency: "USD", + __typename: "Money" + }, + __typename: "TaxedMoney" + }, + thumbnail: { + url: + "http://localhost:8000/media/__sized__/products/saleordemoproduct_cl_boot06_1-thumbnail-255x255.png", + __typename: "Image" + }, + __typename: "OrderLine" + }, + __typename: "FulfillmentLine" + }, + { + id: "7", + quantity: 1, + orderLine: { + id: "T3JkZXJMaW5lOjQ1", + isShippingRequired: false, + variant: { + id: "UHJvZHVjdFZhcmlhbnQ6MzE3", + quantityAvailable: 50, + __typename: "ProductVariant" + }, + productName: "Lake Tunes", + productSku: "lake-tunes-mp3", + quantity: 20, + quantityFulfilled: 6, + unitPrice: { + gross: { + amount: 9.99, + currency: "USD", + __typename: "Money" + }, + net: { + amount: 9.99, + currency: "USD", + __typename: "Money" + }, + __typename: "TaxedMoney" + }, + thumbnail: { + url: + "http://localhost:8000/media/__sized__/products/saleor-digital-03_2-thumbnail-255x255.png", + __typename: "Image" + }, + __typename: "OrderLine" + }, + __typename: "FulfillmentLine" + }, + { + id: "8", + quantity: 1, + orderLine: { + id: "T3JkZXJMaW5lOjQ1", + isShippingRequired: false, + variant: { + id: "UHJvZHVjdFZhcmlhbnQ6MzE3", + quantityAvailable: 50, + __typename: "ProductVariant" + }, + productName: "Lake Tunes", + productSku: "lake-tunes-mp3", + quantity: 25, + quantityFulfilled: 8, + unitPrice: { + gross: { + amount: 9.99, + currency: "USD", + __typename: "Money" + }, + net: { + amount: 9.99, + currency: "USD", + __typename: "Money" + }, + __typename: "TaxedMoney" + }, + thumbnail: { + url: + "http://localhost:8000/media/__sized__/products/saleor-digital-03_2-thumbnail-255x255.png", + __typename: "Image" + }, + __typename: "OrderLine" + }, + __typename: "FulfillmentLine" + } + ]; + + const unfulfiledItemsQuantities: FormsetData<LineItemData, number> = [ + { + data: { isFulfillment: false, isRefunded: false }, + id: "1", + label: null, + value: 0 + }, + { + data: { isFulfillment: false, isRefunded: false }, + id: "2", + label: null, + value: 2 + }, + { + data: { isFulfillment: false, isRefunded: false }, + id: "3", + label: null, + value: 1 + } + ]; + + const fulfiledItemsQuantities: FormsetData<LineItemData, number> = [ + { + data: { isFulfillment: true, isRefunded: false }, + id: "4", + label: null, + value: 4 + }, + { + data: { isFulfillment: true, isRefunded: false }, + id: "5", + label: null, + value: 0 + }, + { + data: { isFulfillment: true, isRefunded: false }, + id: "6", + label: null, + value: 3 + }, + { + data: { isFulfillment: true, isRefunded: true }, + id: "7", + label: null, + value: 4 + }, + { + data: { isFulfillment: true, isRefunded: true }, + id: "8", + label: null, + value: 3 + } + ]; + + const itemsToBeReplaced: FormsetData<LineItemData, boolean> = [ + { + data: { isFulfillment: false, isRefunded: false }, + id: "1", + label: null, + value: true + }, + { + data: { isFulfillment: false, isRefunded: false }, + id: "2", + label: null, + value: false + }, + { + data: { isFulfillment: false, isRefunded: false }, + id: "3", + label: null, + value: true + }, + { + data: { isFulfillment: true, isRefunded: false }, + id: "4", + label: null, + value: false + }, + { + data: { isFulfillment: true, isRefunded: false }, + id: "5", + label: null, + value: true + }, + { + data: { isFulfillment: true, isRefunded: false }, + id: "6", + label: null, + value: true + }, + { + data: { isFulfillment: true, isRefunded: true }, + id: "7", + label: null, + value: false + }, + { + data: { isFulfillment: true, isRefunded: true }, + id: "8", + label: null, + value: true + } + ]; + + const totalValue = getReplacedProductsAmount( + { + ...orderBase, + lines: unfulfilledLines, + fulfillments: [ + { + id: "#1", + fulfillmentOrder: 1, + status: FulfillmentStatus.FULFILLED, + warehouse: null, + trackingNumber: "", + lines: fulfilledLines, + __typename: "Fulfillment" + } + ] + }, + { + itemsToBeReplaced, + unfulfiledItemsQuantities, + fulfiledItemsQuantities + } + ); + + expect(totalValue).toBe(10); + }); +}); + +describe("Get the total value of all selected products", () => { + it("sums up correctly", () => { + const unfulfilledLines: OrderDetails_order_lines[] = [ + { + id: "1", + isShippingRequired: false, + variant: { + id: "UHJvZHVjdFZhcmlhbnQ6MzE3", + quantityAvailable: 50, + __typename: "ProductVariant" + }, + productName: "Lake Tunes", + productSku: "lake-tunes-mp3", + quantity: 2, + quantityFulfilled: 2, + unitPrice: { + gross: { + amount: 9.99, + currency: "USD", + __typename: "Money" + }, + net: { + amount: 9.99, + currency: "USD", + __typename: "Money" + }, + __typename: "TaxedMoney" + }, + thumbnail: { + url: + "http://localhost:8000/media/__sized__/products/saleor-digital-03_2-thumbnail-255x255.png", + __typename: "Image" + }, + __typename: "OrderLine" + }, + { + id: "2", + isShippingRequired: false, + variant: { + id: "UHJvZHVjdFZhcmlhbnQ6MzE3", + quantityAvailable: 50, + __typename: "ProductVariant" + }, + productName: "Lake Tunes", + productSku: "lake-tunes-mp3", + quantity: 10, + quantityFulfilled: 2, + unitPrice: { + gross: { + amount: 9.99, + currency: "USD", + __typename: "Money" + }, + net: { + amount: 9.99, + currency: "USD", + __typename: "Money" + }, + __typename: "TaxedMoney" + }, + thumbnail: { + url: + "http://localhost:8000/media/__sized__/products/saleor-digital-03_2-thumbnail-255x255.png", + __typename: "Image" + }, + __typename: "OrderLine" + }, + { + id: "3", + isShippingRequired: true, + variant: { + id: "UHJvZHVjdFZhcmlhbnQ6Mjg2", + quantityAvailable: 50, + __typename: "ProductVariant" + }, + productName: "T-shirt", + productSku: "29810068", + quantity: 6, + quantityFulfilled: 1, + unitPrice: { + gross: { + amount: 2.5, + currency: "USD", + __typename: "Money" + }, + net: { + amount: 2.5, + currency: "USD", + __typename: "Money" + }, + __typename: "TaxedMoney" + }, + thumbnail: { + url: + "http://localhost:8000/media/__sized__/products/saleordemoproduct_cl_boot06_1-thumbnail-255x255.png", + __typename: "Image" + }, + __typename: "OrderLine" + } + ]; + + const fulfilledLines: OrderDetails_order_fulfillments_lines[] = [ + { + id: "4", + quantity: 1, + orderLine: { + id: "T3JkZXJMaW5lOjQ1", + isShippingRequired: false, + variant: { + id: "UHJvZHVjdFZhcmlhbnQ6MzE3", + quantityAvailable: 50, + __typename: "ProductVariant" + }, + productName: "Lake Tunes", + productSku: "lake-tunes-mp3", + quantity: 20, + quantityFulfilled: 6, + unitPrice: { + gross: { + amount: 9.99, + currency: "USD", + __typename: "Money" + }, + net: { + amount: 9.99, + currency: "USD", + __typename: "Money" + }, + __typename: "TaxedMoney" + }, + thumbnail: { + url: + "http://localhost:8000/media/__sized__/products/saleor-digital-03_2-thumbnail-255x255.png", + __typename: "Image" + }, + __typename: "OrderLine" + }, + __typename: "FulfillmentLine" + }, + { + id: "5", + quantity: 1, + orderLine: { + id: "T3JkZXJMaW5lOjQ1", + isShippingRequired: false, + variant: { + id: "UHJvZHVjdFZhcmlhbnQ6MzE3", + quantityAvailable: 50, + __typename: "ProductVariant" + }, + productName: "Lake Tunes", + productSku: "lake-tunes-mp3", + quantity: 25, + quantityFulfilled: 8, + unitPrice: { + gross: { + amount: 9.99, + currency: "USD", + __typename: "Money" + }, + net: { + amount: 9.99, + currency: "USD", + __typename: "Money" + }, + __typename: "TaxedMoney" + }, + thumbnail: { + url: + "http://localhost:8000/media/__sized__/products/saleor-digital-03_2-thumbnail-255x255.png", + __typename: "Image" + }, + __typename: "OrderLine" + }, + __typename: "FulfillmentLine" + }, + { + id: "6", + quantity: 1, + orderLine: { + id: "T3JkZXJMaW5lOjQ3", + isShippingRequired: true, + variant: { + id: "UHJvZHVjdFZhcmlhbnQ6Mjg2", + quantityAvailable: 50, + __typename: "ProductVariant" + }, + productName: "T-shirt", + productSku: "29810068", + quantity: 10, + quantityFulfilled: 3, + unitPrice: { + gross: { + amount: 2.5, + currency: "USD", + __typename: "Money" + }, + net: { + amount: 2.5, + currency: "USD", + __typename: "Money" + }, + __typename: "TaxedMoney" + }, + thumbnail: { + url: + "http://localhost:8000/media/__sized__/products/saleordemoproduct_cl_boot06_1-thumbnail-255x255.png", + __typename: "Image" + }, + __typename: "OrderLine" + }, + __typename: "FulfillmentLine" + } + ]; + + const unfulfiledItemsQuantities: FormsetData<null, number> = [ + { + data: null, + id: "1", + label: null, + value: 0 + }, + { + data: null, + id: "2", + label: null, + value: 2 + }, + { + data: null, + id: "3", + label: null, + value: 1 + } + ]; + + const fulfiledItemsQuantities: FormsetData<null, number> = [ + { + data: null, + id: "4", + label: null, + value: 4 + }, + { + data: null, + id: "5", + label: null, + value: 0 + }, + { + data: null, + id: "6", + label: null, + value: 3 + } + ]; + + const itemsToBeReplaced: FormsetData<LineItemData, boolean> = [ + { + data: { isFulfillment: false, isRefunded: false }, + id: "1", + label: null, + value: true + }, + { + data: { isFulfillment: false, isRefunded: false }, + id: "2", + label: null, + value: false + }, + { + data: { isFulfillment: false, isRefunded: false }, + id: "3", + label: null, + value: true + }, + { + data: { isFulfillment: true, isRefunded: false }, + id: "4", + label: null, + value: false + }, + { + data: { isFulfillment: true, isRefunded: false }, + id: "5", + label: null, + value: true + }, + { + data: { isFulfillment: true, isRefunded: false }, + id: "6", + label: null, + value: true + }, + { + data: { isFulfillment: true, isRefunded: true }, + id: "7", + label: null, + value: true + }, + { + data: { isFulfillment: true, isRefunded: true }, + id: "8", + label: null, + value: true + } + ]; + + const totalValue = getReturnSelectedProductsAmount( + { + ...orderBase, + lines: unfulfilledLines, + fulfillments: [ + { + id: "#1", + fulfillmentOrder: 1, + status: FulfillmentStatus.FULFILLED, + warehouse: null, + trackingNumber: "", + lines: fulfilledLines, + __typename: "Fulfillment" + } + ] + }, + { + itemsToBeReplaced, + unfulfiledItemsQuantities, + fulfiledItemsQuantities + } + ); + + expect(totalValue).toBe(59.94); + }); +}); + describe("Merge repeated order lines of fulfillment lines", () => { it("is able to merge repeated order lines and sum their quantities", () => { const lines: OrderDetails_order_fulfillments_lines[] = [ diff --git a/src/orders/utils/data.ts b/src/orders/utils/data.ts index 2816e91bc..72c1954b3 100644 --- a/src/orders/utils/data.ts +++ b/src/orders/utils/data.ts @@ -1,7 +1,19 @@ import { IMoney, subtractMoney } from "@saleor/components/Money"; import { FormsetData } from "@saleor/hooks/useFormset"; -import { OrderDetails_order_fulfillments_lines } from "../types/OrderDetails"; +import { + LineItemData, + OrderReturnFormData +} from "../components/OrderReturnPage/form"; +import { + getAllOrderFulfilledLines, + getById +} from "../components/OrderReturnPage/utils"; +import { + OrderDetails_order, + OrderDetails_order_fulfillments_lines, + OrderDetails_order_lines +} from "../types/OrderDetails"; import { OrderRefundData_order, OrderRefundData_order_fulfillments, @@ -23,9 +35,130 @@ export function getPreviouslyRefundedPrice( ); } +const getItemPriceAndQuantity = ({ + orderLines, + itemsQuantities, + id +}: { + orderLines: OrderDetails_order_lines[]; + itemsQuantities: FormsetData<LineItemData, number>; + id: string; +}) => { + const { unitPrice } = orderLines.find(getById(id)); + const selectedQuantity = itemsQuantities.find(getById(id))?.value; + + return { selectedQuantity, unitPrice }; +}; + +const selectItemPriceAndQuantity = ( + order: OrderDetails_order, + { + fulfiledItemsQuantities, + unfulfiledItemsQuantities + }: Partial<OrderReturnFormData>, + id: string, + isFulfillment: boolean +) => + isFulfillment + ? getItemPriceAndQuantity({ + id, + itemsQuantities: fulfiledItemsQuantities, + orderLines: getAllOrderFulfilledLines(order) + }) + : getItemPriceAndQuantity({ + id, + itemsQuantities: unfulfiledItemsQuantities, + orderLines: order.lines + }); + +export const getReplacedProductsAmount = ( + order: OrderDetails_order, + { + itemsToBeReplaced, + unfulfiledItemsQuantities, + fulfiledItemsQuantities + }: Partial<OrderReturnFormData> +) => { + if (!order || !itemsToBeReplaced.length) { + return 0; + } + + return itemsToBeReplaced.reduce( + ( + resultAmount: number, + { id, value: isItemToBeReplaced, data: { isFulfillment, isRefunded } } + ) => { + if (!isItemToBeReplaced || isRefunded) { + return resultAmount; + } + + const { unitPrice, selectedQuantity } = selectItemPriceAndQuantity( + order, + { fulfiledItemsQuantities, unfulfiledItemsQuantities }, + id, + isFulfillment + ); + + return resultAmount + unitPrice?.gross?.amount * selectedQuantity; + }, + 0 + ); +}; + +export const getReturnSelectedProductsAmount = ( + order: OrderDetails_order, + { itemsToBeReplaced, unfulfiledItemsQuantities, fulfiledItemsQuantities } +) => { + if (!order) { + return 0; + } + + const unfulfilledItemsValue = getPartialProductsValue({ + itemsQuantities: unfulfiledItemsQuantities, + itemsToBeReplaced, + orderLines: order.lines + }); + + const fulfiledItemsValue = getPartialProductsValue({ + itemsQuantities: fulfiledItemsQuantities, + itemsToBeReplaced, + orderLines: getAllOrderFulfilledLines(order) + }); + + return unfulfilledItemsValue + fulfiledItemsValue; +}; + +const getPartialProductsValue = ({ + orderLines, + itemsQuantities, + itemsToBeReplaced +}: { + itemsToBeReplaced: FormsetData<LineItemData, boolean>; + itemsQuantities: FormsetData<LineItemData, number>; + orderLines: OrderDetails_order_lines[]; +}) => + itemsQuantities.reduce((resultAmount, { id, value: quantity }) => { + const { + value: isItemToBeReplaced, + data: { isRefunded } + } = itemsToBeReplaced.find(getById(id)); + + if (quantity < 1 || isItemToBeReplaced || isRefunded) { + return resultAmount; + } + + const { selectedQuantity, unitPrice } = getItemPriceAndQuantity({ + id, + itemsQuantities, + orderLines + }); + + return resultAmount + unitPrice.gross.amount * selectedQuantity; + }, 0); + export function getRefundedLinesPriceSum( lines: OrderRefundData_order_lines[], - refundedProductQuantities: FormsetData<null, string> + refundedProductQuantities: FormsetData<null, string | number> ): number { return lines?.reduce((sum, line) => { const refundedLine = refundedProductQuantities.find( @@ -37,7 +170,7 @@ export function getRefundedLinesPriceSum( export function getAllFulfillmentLinesPriceSum( fulfillments: OrderRefundData_order_fulfillments[], - refundedFulfilledProductQuantities: FormsetData<null, string> + refundedFulfilledProductQuantities: FormsetData<null, string | number> ): number { return fulfillments?.reduce((sum, fulfillment) => { const fulfilmentLinesSum = fulfillment?.lines.reduce((sum, line) => { diff --git a/src/orders/views/OrderDetails/index.tsx b/src/orders/views/OrderDetails/index.tsx index 845735818..18405ac20 100644 --- a/src/orders/views/OrderDetails/index.tsx +++ b/src/orders/views/OrderDetails/index.tsx @@ -50,6 +50,7 @@ import { orderFulfillUrl, orderListUrl, orderRefundUrl, + orderReturnPath, orderUrl, OrderUrlDialog, OrderUrlQueryParams @@ -227,6 +228,7 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => { )} /> <OrderDetailsPage + onOrderReturn={() => navigate(orderReturnPath(id))} disabled={ updateMetadataOpts.loading || updatePrivateMetadataOpts.loading diff --git a/src/orders/views/OrderReturn/OrderReturn.tsx b/src/orders/views/OrderReturn/OrderReturn.tsx new file mode 100644 index 000000000..c38667bfd --- /dev/null +++ b/src/orders/views/OrderReturn/OrderReturn.tsx @@ -0,0 +1,113 @@ +import useNavigator from "@saleor/hooks/useNavigator"; +import useNotifier from "@saleor/hooks/useNotifier"; +import { commonMessages } from "@saleor/intl"; +import OrderReturnPage from "@saleor/orders/components/OrderReturnPage"; +import { OrderReturnFormData } from "@saleor/orders/components/OrderReturnPage/form"; +import { useOrderReturnCreateMutation } from "@saleor/orders/mutations"; +import { useOrderQuery } from "@saleor/orders/queries"; +import { orderUrl } from "@saleor/orders/urls"; +import { OrderErrorCode } from "@saleor/types/globalTypes"; +import React from "react"; +import { defineMessages } from "react-intl"; +import { useIntl } from "react-intl"; + +import ReturnFormDataParser from "./utils"; + +export const messages = defineMessages({ + cannotRefundDescription: { + defaultMessage: + "We’ve encountered a problem while refunding the products. Product’s were not refunded. Please try again.", + description: "order return error description when cannot refund" + }, + cannotRefundTitle: { + defaultMessage: "Couldn't refund products", + description: "order return error title when cannot refund" + }, + successAlert: { + defaultMessage: "Successfully returned products!", + description: "order returned success message" + } +}); + +interface OrderReturnProps { + orderId: string; +} + +const OrderReturn: React.FC<OrderReturnProps> = ({ orderId }) => { + const navigate = useNavigator(); + const notify = useNotifier(); + const intl = useIntl(); + + const { data, loading } = useOrderQuery({ + displayLoader: true, + variables: { + id: orderId + } + }); + + const [returnCreate, returnCreateOpts] = useOrderReturnCreateMutation({ + onCompleted: ({ + orderFulfillmentReturnProducts: { errors, replaceOrder } + }) => { + if (!errors.length) { + notify({ + status: "success", + text: intl.formatMessage(messages.successAlert) + }); + + navigateToOrder(replaceOrder?.id); + } + + if (errors[0].code === OrderErrorCode.CANNOT_REFUND) { + notify({ + autohide: 5000, + status: "error", + text: intl.formatMessage(messages.cannotRefundDescription), + title: intl.formatMessage(messages.cannotRefundTitle) + }); + + return; + } + + notify({ + autohide: 5000, + status: "error", + text: intl.formatMessage(commonMessages.somethingWentWrong) + }); + } + }); + + const handleSubmit = async (formData: OrderReturnFormData) => { + if (!data?.order) { + return; + } + + const result = await returnCreate({ + variables: { + id: data.order.id, + input: new ReturnFormDataParser(data.order, formData).getParsedData() + } + }); + + const { + data: { orderFulfillmentReturnProducts } + } = result; + + return orderFulfillmentReturnProducts.errors; + }; + + const navigateToOrder = (id?: string) => navigate(orderUrl(id || orderId)); + + return ( + <OrderReturnPage + errors={returnCreateOpts.data?.orderFulfillmentReturnProducts.errors} + order={data?.order} + loading={loading || returnCreateOpts.loading} + onSubmit={handleSubmit} + onBack={() => navigateToOrder()} + /> + ); +}; + +OrderReturn.displayName = "OrderReturn"; +export default OrderReturn; diff --git a/src/orders/views/OrderReturn/index.tsx b/src/orders/views/OrderReturn/index.tsx new file mode 100644 index 000000000..db84f1192 --- /dev/null +++ b/src/orders/views/OrderReturn/index.tsx @@ -0,0 +1,2 @@ +export * from "./OrderReturn"; +export { default } from "./OrderReturn"; diff --git a/src/orders/views/OrderReturn/utils.tsx b/src/orders/views/OrderReturn/utils.tsx new file mode 100644 index 000000000..02f02f834 --- /dev/null +++ b/src/orders/views/OrderReturn/utils.tsx @@ -0,0 +1,102 @@ +/* eslint-disable @typescript-eslint/member-ordering */ +import { OrderRefundAmountCalculationMode } from "@saleor/orders/components/OrderRefundPage/form"; +import { + FormsetQuantityData, + OrderReturnFormData +} from "@saleor/orders/components/OrderReturnPage/form"; +import { getById } from "@saleor/orders/components/OrderReturnPage/utils"; +import { OrderDetails_order } from "@saleor/orders/types/OrderDetails"; +import { + OrderReturnFulfillmentLineInput, + OrderReturnLineInput, + OrderReturnProductsInput +} from "@saleor/types/globalTypes"; + +class ReturnFormDataParser { + private order: OrderDetails_order; + private formData: OrderReturnFormData; + + constructor(order: OrderDetails_order, formData: OrderReturnFormData) { + this.order = order; + this.formData = formData; + } + + public getParsedData = (): OrderReturnProductsInput => { + const { + fulfiledItemsQuantities, + unfulfiledItemsQuantities, + refundShipmentCosts + } = this.formData; + + const fulfillmentLines = this.getParsedLineData< + OrderReturnFulfillmentLineInput + >(fulfiledItemsQuantities, "fulfillmentLineId"); + + const orderLines = this.getParsedLineData<OrderReturnLineInput>( + unfulfiledItemsQuantities, + "orderLineId" + ); + + return { + amountToRefund: this.getAmountToRefund(), + fulfillmentLines, + includeShippingCosts: refundShipmentCosts, + orderLines, + refund: this.getShouldRefund(orderLines, fulfillmentLines) + }; + }; + + private getAmountToRefund = (): number | undefined => + this.formData.amountCalculationMode === + OrderRefundAmountCalculationMode.MANUAL + ? this.formData.amount + : undefined; + + private getParsedLineData = function< + T extends OrderReturnFulfillmentLineInput | OrderReturnLineInput + >( + itemsQuantities: FormsetQuantityData, + idKey: "fulfillmentLineId" | "orderLineId" + ): T[] { + const { itemsToBeReplaced } = this.formData; + + return itemsQuantities.reduce((result, { value: quantity, id }) => { + if (!quantity) { + return result; + } + + const shouldReplace = !!itemsToBeReplaced.find(getById(id))?.value; + + return [ + ...result, + ({ [idKey]: id, quantity, replace: shouldReplace } as unknown) as T + ]; + }, []); + }; + + private getShouldRefund = ( + orderLines: OrderReturnLineInput[], + fulfillmentLines: OrderReturnFulfillmentLineInput[] + ) => { + if (!this.order.totalCaptured?.amount) { + return false; + } + + if (!!this.getAmountToRefund()) { + return true; + } + + return ( + orderLines.some(ReturnFormDataParser.isLineRefundable) || + fulfillmentLines.some(ReturnFormDataParser.isLineRefundable) + ); + }; + + private static isLineRefundable = function< + T extends OrderReturnLineInput | OrderReturnFulfillmentLineInput + >({ replace }: T) { + return !replace; + }; +} + +export default ReturnFormDataParser; diff --git a/src/storybook/Decorator.tsx b/src/storybook/Decorator.tsx index 66e548747..a92989d6c 100644 --- a/src/storybook/Decorator.tsx +++ b/src/storybook/Decorator.tsx @@ -1,11 +1,13 @@ import { Locale, RawLocaleProvider } from "@saleor/components/Locale"; import React from "react"; import { IntlProvider } from "react-intl"; +import { BrowserRouter } from "react-router-dom"; import { Provider as DateProvider } from "../components/Date/DateContext"; import MessageManagerProvider from "../components/messages"; import ThemeProvider from "../components/Theme"; import { TimezoneProvider } from "../components/Timezone"; +import { APP_MOUNT_URI } from "../config"; export const Decorator = storyFn => ( <IntlProvider defaultLocale={Locale.EN} locale={Locale.EN}> @@ -18,15 +20,17 @@ export const Decorator = storyFn => ( <DateProvider value={+new Date("2018-08-07T14:30:44+00:00")}> <TimezoneProvider value="America/New_York"> <ThemeProvider isDefaultDark={false}> - <MessageManagerProvider> - <div - style={{ - padding: 24 - }} - > - {storyFn()} - </div> - </MessageManagerProvider> + <BrowserRouter basename={APP_MOUNT_URI}> + <MessageManagerProvider> + <div + style={{ + padding: 24 + }} + > + {storyFn()} + </div> + </MessageManagerProvider> + </BrowserRouter> </ThemeProvider> </TimezoneProvider> </DateProvider> diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index fbd9a13b8..2c714118b 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -12449,27 +12449,23 @@ exports[`Storyshots Generics / Timeline default 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Expansion panel 1 + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1525712282000" + title="May 7, 2018 12:58 PM" > - Expansion panel 1 - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1525712282000" - title="May 7, 2018 12:58 PM" - > - 3 months ago - </time> - </div> + 3 months ago + </time> </div> </div> </div> @@ -12480,27 +12476,23 @@ exports[`Storyshots Generics / Timeline default 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Expansion panel 2 + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1525711682000" + title="May 7, 2018 12:48 PM" > - Expansion panel 2 - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1525711682000" - title="May 7, 2018 12:48 PM" - > - 3 months ago - </time> - </div> + 3 months ago + </time> </div> </div> </div> @@ -12511,27 +12503,23 @@ exports[`Storyshots Generics / Timeline default 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Expansion panel 3 + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1525625882000" + title="May 6, 2018 12:58 PM" > - Expansion panel 3 - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1525625882000" - title="May 6, 2018 12:58 PM" - > - 3 months ago - </time> - </div> + 3 months ago + </time> </div> </div> </div> @@ -12553,12 +12541,12 @@ exports[`Storyshots Generics / Timeline with expansion panels 1`] = ` class="TimelineEvent-dot-id" /> <div - class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id TimelineEvent-expanded-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" + class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" > <div aria-disabled="false" aria-expanded="false" - class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id TimelineEvent-expanded-id" + class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id" role="button" tabindex="0" > @@ -12566,20 +12554,24 @@ exports[`Storyshots Generics / Timeline with expansion panels 1`] = ` class="MuiExpansionPanelSummary-content-id" > <div - class="MuiTypography-root-id MuiTypography-body1-id" + class="TimelineEventHeader-container-id" > - Expansion panel 1 - </div> - <div - class="MuiTypography-root-id TimelineEvent-dateExpander-id MuiTypography-body1-id" - > - <time - class="" - datetime="1525712282000" - title="May 7, 2018 12:58 PM" + <div + class="MuiTypography-root-id MuiTypography-body1-id" > - 3 months ago - </time> + Expansion panel 1 + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1525712282000" + title="May 7, 2018 12:58 PM" + > + 3 months ago + </time> + </div> </div> </div> <div @@ -12639,12 +12631,12 @@ exports[`Storyshots Generics / Timeline with expansion panels 1`] = ` class="TimelineEvent-dot-id" /> <div - class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id TimelineEvent-expanded-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" + class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" > <div aria-disabled="false" aria-expanded="false" - class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id TimelineEvent-expanded-id" + class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id" role="button" tabindex="0" > @@ -12652,20 +12644,24 @@ exports[`Storyshots Generics / Timeline with expansion panels 1`] = ` class="MuiExpansionPanelSummary-content-id" > <div - class="MuiTypography-root-id MuiTypography-body1-id" + class="TimelineEventHeader-container-id" > - Expansion panel 2 - </div> - <div - class="MuiTypography-root-id TimelineEvent-dateExpander-id MuiTypography-body1-id" - > - <time - class="" - datetime="1525711682000" - title="May 7, 2018 12:48 PM" + <div + class="MuiTypography-root-id MuiTypography-body1-id" > - 3 months ago - </time> + Expansion panel 2 + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1525711682000" + title="May 7, 2018 12:48 PM" + > + 3 months ago + </time> + </div> </div> </div> <div @@ -12725,12 +12721,12 @@ exports[`Storyshots Generics / Timeline with expansion panels 1`] = ` class="TimelineEvent-dot-id" /> <div - class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id TimelineEvent-expanded-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" + class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" > <div aria-disabled="false" aria-expanded="false" - class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id TimelineEvent-expanded-id" + class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id" role="button" tabindex="0" > @@ -12738,20 +12734,24 @@ exports[`Storyshots Generics / Timeline with expansion panels 1`] = ` class="MuiExpansionPanelSummary-content-id" > <div - class="MuiTypography-root-id MuiTypography-body1-id" + class="TimelineEventHeader-container-id" > - Expansion panel 3 - </div> - <div - class="MuiTypography-root-id TimelineEvent-dateExpander-id MuiTypography-body1-id" - > - <time - class="" - datetime="1525625882000" - title="May 6, 2018 12:58 PM" + <div + class="MuiTypography-root-id MuiTypography-body1-id" > - 3 months ago - </time> + Expansion panel 3 + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1525625882000" + title="May 6, 2018 12:58 PM" + > + 3 months ago + </time> + </div> </div> </div> <div @@ -12822,12 +12822,12 @@ exports[`Storyshots Generics / Timeline with order notes 1`] = ` class="TimelineEvent-dot-id" /> <div - class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id TimelineEvent-expanded-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" + class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" > <div aria-disabled="false" aria-expanded="false" - class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id TimelineEvent-expanded-id" + class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id" role="button" tabindex="0" > @@ -12835,20 +12835,24 @@ exports[`Storyshots Generics / Timeline with order notes 1`] = ` class="MuiExpansionPanelSummary-content-id" > <div - class="MuiTypography-root-id MuiTypography-body1-id" + class="TimelineEventHeader-container-id" > - Expansion panel 1 - </div> - <div - class="MuiTypography-root-id TimelineEvent-dateExpander-id MuiTypography-body1-id" - > - <time - class="" - datetime="1525712282000" - title="May 7, 2018 12:58 PM" + <div + class="MuiTypography-root-id MuiTypography-body1-id" > - 3 months ago - </time> + Expansion panel 1 + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1525712282000" + title="May 7, 2018 12:58 PM" + > + 3 months ago + </time> + </div> </div> </div> <div @@ -13014,12 +13018,12 @@ exports[`Storyshots Generics / Timeline with order notes 1`] = ` class="TimelineEvent-dot-id" /> <div - class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id TimelineEvent-expanded-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" + class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" > <div aria-disabled="false" aria-expanded="false" - class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id TimelineEvent-expanded-id" + class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id" role="button" tabindex="0" > @@ -13027,20 +13031,24 @@ exports[`Storyshots Generics / Timeline with order notes 1`] = ` class="MuiExpansionPanelSummary-content-id" > <div - class="MuiTypography-root-id MuiTypography-body1-id" + class="TimelineEventHeader-container-id" > - Expansion panel 3 - </div> - <div - class="MuiTypography-root-id TimelineEvent-dateExpander-id MuiTypography-body1-id" - > - <time - class="" - datetime="1525625882000" - title="May 6, 2018 12:58 PM" + <div + class="MuiTypography-root-id MuiTypography-body1-id" > - 3 months ago - </time> + Expansion panel 3 + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1525625882000" + title="May 6, 2018 12:58 PM" + > + 3 months ago + </time> + </div> </div> </div> <div @@ -14154,27 +14162,23 @@ exports[`Storyshots Orders / OrderHistory default 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Payment was authorized + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Payment was authorized - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -14185,27 +14189,23 @@ exports[`Storyshots Orders / OrderHistory default 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Order refund information was sent to customer + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Order refund information was sent to customer - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -14216,27 +14216,23 @@ exports[`Storyshots Orders / OrderHistory default 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Order cancel information was sent to customer + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Order cancel information was sent to customer - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -14247,27 +14243,23 @@ exports[`Storyshots Orders / OrderHistory default 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Note from external service + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Note from external service - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -14348,12 +14340,12 @@ exports[`Storyshots Orders / OrderHistory default 1`] = ` class="TimelineEvent-dot-id" /> <div - class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id TimelineEvent-expanded-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" + class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" > <div aria-disabled="false" aria-expanded="false" - class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id TimelineEvent-expanded-id" + class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id" role="button" tabindex="0" > @@ -14361,20 +14353,33 @@ exports[`Storyshots Orders / OrderHistory default 1`] = ` class="MuiExpansionPanelSummary-content-id" > <div - class="MuiTypography-root-id MuiTypography-body1-id" + class="TimelineEventHeader-container-id" > - Order was refunded by admin@example.com - </div> - <div - class="MuiTypography-root-id TimelineEvent-dateExpander-id MuiTypography-body1-id" - > - <time - class="" - datetime="1537190544376" - title="Sep 17, 2018 9:22 AM" + <div + class="TimelineEventHeader-elementsContainer-id" > - in a month - </time> + <div + class="MuiTypography-root-id TimelineEventHeader-titleElement-id MuiTypography-body1-id" + > + Products were refunded by + </div> + <a + class="MuiTypography-root-id TimelineEventHeader-titleElement-id Link-root-id Link-primary-id MuiTypography-body1-id" + > + Jane Doe + </a> + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1537190544376" + title="Sep 17, 2018 9:22 AM" + > + in a month + </time> + </div> </div> </div> <div @@ -14477,11 +14482,6 @@ exports[`Storyshots Orders / OrderHistory default 1`] = ` </tr> </tbody> </table> - <div - class="MuiTypography-root-id OrderHistory-eventSubtitle-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" - > - Refunded amount - </div> <div class="MuiTypography-root-id MuiTypography-body1-id" > @@ -14502,27 +14502,23 @@ exports[`Storyshots Orders / OrderHistory default 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Fulfilled 1 items + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1537190544376" + title="Sep 17, 2018 9:22 AM" > - Fulfilled 1 items - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1537190544376" - title="Sep 17, 2018 9:22 AM" - > - in a month - </time> - </div> + in a month + </time> </div> </div> </div> @@ -101201,7 +101197,33 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = ` > <div class="PageHeader-root-id" - /> + > + <div> + <button + aria-haspopup="true" + aria-label="More" + class="MuiButtonBase-root-id MuiIconButton-root-id CardMenu-iconButton-id MuiIconButton-colorPrimary-id" + tabindex="0" + type="button" + > + <span + class="MuiIconButton-label-id" + > + <svg + aria-hidden="true" + class="MuiSvgIcon-root-id" + focusable="false" + role="presentation" + viewBox="0 0 24 24" + > + <path + d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z" + /> + </svg> + </span> + </button> + </div> + </div> </div> </div> <div @@ -101233,9 +101255,12 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = ` class="MuiTypography-root-id CardTitle-title-id MuiTypography-h5-id" > <div - class="StatusLabel-root-id undefined StatusLabel-errorDot-id" + class="StatusLabel-root-id undefined StatusLabel-alertDot-id" > - Unfulfilled (3) + Unfulfilled + <div + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" + /> </div> </span> <div @@ -101261,35 +101286,31 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderUnfulfilledItems-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -101300,10 +101321,10 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderUnfulfilledItems-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderUnfulfilledItems-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -101324,22 +101345,22 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 59-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 3 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $18.51 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $55.53 </td> @@ -101363,9 +101384,9 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = ` <div class="StatusLabel-root-id undefined StatusLabel-errorDot-id" > - Cancelled (1) + Unfulfilled <div - class="MuiTypography-root-id OrderFulfillment-orderNumber-id MuiTypography-body1-id" + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" > #9-2 </div> @@ -101394,35 +101415,31 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderFulfillment-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -101433,10 +101450,10 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderFulfillment-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -101457,48 +101474,26 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 5-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 1 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $79.71 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $79.71 </td> </tr> - <tr - class="MuiTableRow-root-id" - > - <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-infoRow-id" - colspan="5" - > - <div - class="MuiTypography-root-id MuiTypography-body2-id MuiTypography-colorTextSecondary-id" - > - Fulfilled from: - <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" - > - Be stocked - </div> - </div> - <div - class="MuiTypography-root-id MuiTypography-body2-id MuiTypography-colorTextSecondary-id" - /> - </td> - </tr> </tbody> </table> </div> @@ -101518,9 +101513,9 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = ` <div class="StatusLabel-root-id undefined StatusLabel-errorDot-id" > - Cancelled (1) + Unfulfilled <div - class="MuiTypography-root-id OrderFulfillment-orderNumber-id MuiTypography-body1-id" + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" > #9-1 </div> @@ -101549,35 +101544,31 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderFulfillment-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -101588,10 +101579,10 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderFulfillment-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -101612,31 +101603,33 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 5-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 1 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $79.71 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $79.71 </td> </tr> + </tbody> + <tbody> <tr class="MuiTableRow-root-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-infoRow-id" + class="MuiTableCell-root-id ExtraInfoLines-infoRow-id" colspan="5" > <div @@ -101644,7 +101637,7 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = ` > Fulfilled from: <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id OrderFulfillment-infoLabelWithMargin-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" + class="MuiTypography-root-id ExtraInfoLines-infoLabel-id ExtraInfoLines-infoLabelWithMargin-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" > C our wares </div> @@ -101654,7 +101647,7 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = ` > Tracking Number: <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" + class="MuiTypography-root-id ExtraInfoLines-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" > 01nn12399su12nndfsy </div> @@ -102183,27 +102176,23 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Payment was authorized + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Payment was authorized - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -102214,27 +102203,23 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Order refund information was sent to customer + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Order refund information was sent to customer - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -102245,27 +102230,23 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Order cancel information was sent to customer + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Order cancel information was sent to customer - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -102276,27 +102257,23 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Note from external service + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Note from external service - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -102377,12 +102354,12 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = ` class="TimelineEvent-dot-id" /> <div - class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id TimelineEvent-expanded-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" + class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" > <div aria-disabled="false" aria-expanded="false" - class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id TimelineEvent-expanded-id" + class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id" role="button" tabindex="0" > @@ -102390,20 +102367,33 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = ` class="MuiExpansionPanelSummary-content-id" > <div - class="MuiTypography-root-id MuiTypography-body1-id" + class="TimelineEventHeader-container-id" > - Order was refunded by admin@example.com - </div> - <div - class="MuiTypography-root-id TimelineEvent-dateExpander-id MuiTypography-body1-id" - > - <time - class="" - datetime="1537190544376" - title="Sep 17, 2018 9:22 AM" + <div + class="TimelineEventHeader-elementsContainer-id" > - in a month - </time> + <div + class="MuiTypography-root-id TimelineEventHeader-titleElement-id MuiTypography-body1-id" + > + Products were refunded by + </div> + <a + class="MuiTypography-root-id TimelineEventHeader-titleElement-id Link-root-id Link-primary-id MuiTypography-body1-id" + > + Jane Doe + </a> + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1537190544376" + title="Sep 17, 2018 9:22 AM" + > + in a month + </time> + </div> </div> </div> <div @@ -102506,11 +102496,6 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = ` </tr> </tbody> </table> - <div - class="MuiTypography-root-id OrderHistory-eventSubtitle-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" - > - Refunded amount - </div> <div class="MuiTypography-root-id MuiTypography-body1-id" > @@ -102531,27 +102516,23 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Fulfilled 1 items + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1537190544376" + title="Sep 17, 2018 9:22 AM" > - Fulfilled 1 items - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1537190544376" - title="Sep 17, 2018 9:22 AM" - > - in a month - </time> - </div> + in a month + </time> </div> </div> </div> @@ -102904,9 +102885,12 @@ exports[`Storyshots Views / Orders / Order details default 1`] = ` class="MuiTypography-root-id CardTitle-title-id MuiTypography-h5-id" > <div - class="StatusLabel-root-id undefined StatusLabel-errorDot-id" + class="StatusLabel-root-id undefined StatusLabel-alertDot-id" > - Unfulfilled (3) + Unfulfilled + <div + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" + /> </div> </span> <div @@ -102932,35 +102916,31 @@ exports[`Storyshots Views / Orders / Order details default 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderUnfulfilledItems-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -102971,10 +102951,10 @@ exports[`Storyshots Views / Orders / Order details default 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderUnfulfilledItems-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderUnfulfilledItems-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -102995,22 +102975,22 @@ exports[`Storyshots Views / Orders / Order details default 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 59-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 3 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $18.51 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $55.53 </td> @@ -103051,7 +103031,7 @@ exports[`Storyshots Views / Orders / Order details default 1`] = ` > Fulfilled (1) <div - class="MuiTypography-root-id OrderFulfillment-orderNumber-id MuiTypography-body1-id" + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" > #9-2 </div> @@ -103106,35 +103086,31 @@ exports[`Storyshots Views / Orders / Order details default 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderFulfillment-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -103145,10 +103121,10 @@ exports[`Storyshots Views / Orders / Order details default 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderFulfillment-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -103169,48 +103145,26 @@ exports[`Storyshots Views / Orders / Order details default 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 5-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 1 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $79.71 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $79.71 </td> </tr> - <tr - class="MuiTableRow-root-id" - > - <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-infoRow-id" - colspan="5" - > - <div - class="MuiTypography-root-id MuiTypography-body2-id MuiTypography-colorTextSecondary-id" - > - Fulfilled from: - <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" - > - Be stocked - </div> - </div> - <div - class="MuiTypography-root-id MuiTypography-body2-id MuiTypography-colorTextSecondary-id" - /> - </td> - </tr> </tbody> </table> </div> @@ -103247,7 +103201,7 @@ exports[`Storyshots Views / Orders / Order details default 1`] = ` > Fulfilled (1) <div - class="MuiTypography-root-id OrderFulfillment-orderNumber-id MuiTypography-body1-id" + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" > #9-1 </div> @@ -103302,35 +103256,31 @@ exports[`Storyshots Views / Orders / Order details default 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderFulfillment-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -103341,10 +103291,10 @@ exports[`Storyshots Views / Orders / Order details default 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderFulfillment-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -103365,31 +103315,33 @@ exports[`Storyshots Views / Orders / Order details default 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 5-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 1 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $79.71 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $79.71 </td> </tr> + </tbody> + <tbody> <tr class="MuiTableRow-root-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-infoRow-id" + class="MuiTableCell-root-id ExtraInfoLines-infoRow-id" colspan="5" > <div @@ -103397,7 +103349,7 @@ exports[`Storyshots Views / Orders / Order details default 1`] = ` > Fulfilled from: <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id OrderFulfillment-infoLabelWithMargin-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" + class="MuiTypography-root-id ExtraInfoLines-infoLabel-id ExtraInfoLines-infoLabelWithMargin-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" > C our wares </div> @@ -103407,7 +103359,7 @@ exports[`Storyshots Views / Orders / Order details default 1`] = ` > Tracking Number: <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" + class="MuiTypography-root-id ExtraInfoLines-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" > 01nn12399su12nndfsy </div> @@ -104002,27 +103954,23 @@ exports[`Storyshots Views / Orders / Order details default 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Payment was authorized + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Payment was authorized - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -104033,27 +103981,23 @@ exports[`Storyshots Views / Orders / Order details default 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Order refund information was sent to customer + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Order refund information was sent to customer - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -104064,27 +104008,23 @@ exports[`Storyshots Views / Orders / Order details default 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Order cancel information was sent to customer + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Order cancel information was sent to customer - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -104095,27 +104035,23 @@ exports[`Storyshots Views / Orders / Order details default 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Note from external service + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Note from external service - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -104196,12 +104132,12 @@ exports[`Storyshots Views / Orders / Order details default 1`] = ` class="TimelineEvent-dot-id" /> <div - class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id TimelineEvent-expanded-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" + class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" > <div aria-disabled="false" aria-expanded="false" - class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id TimelineEvent-expanded-id" + class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id" role="button" tabindex="0" > @@ -104209,20 +104145,33 @@ exports[`Storyshots Views / Orders / Order details default 1`] = ` class="MuiExpansionPanelSummary-content-id" > <div - class="MuiTypography-root-id MuiTypography-body1-id" + class="TimelineEventHeader-container-id" > - Order was refunded by admin@example.com - </div> - <div - class="MuiTypography-root-id TimelineEvent-dateExpander-id MuiTypography-body1-id" - > - <time - class="" - datetime="1537190544376" - title="Sep 17, 2018 9:22 AM" + <div + class="TimelineEventHeader-elementsContainer-id" > - in a month - </time> + <div + class="MuiTypography-root-id TimelineEventHeader-titleElement-id MuiTypography-body1-id" + > + Products were refunded by + </div> + <a + class="MuiTypography-root-id TimelineEventHeader-titleElement-id Link-root-id Link-primary-id MuiTypography-body1-id" + > + Jane Doe + </a> + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1537190544376" + title="Sep 17, 2018 9:22 AM" + > + in a month + </time> + </div> </div> </div> <div @@ -104325,11 +104274,6 @@ exports[`Storyshots Views / Orders / Order details default 1`] = ` </tr> </tbody> </table> - <div - class="MuiTypography-root-id OrderHistory-eventSubtitle-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" - > - Refunded amount - </div> <div class="MuiTypography-root-id MuiTypography-body1-id" > @@ -104350,27 +104294,23 @@ exports[`Storyshots Views / Orders / Order details default 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Fulfilled 1 items + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1537190544376" + title="Sep 17, 2018 9:22 AM" > - Fulfilled 1 items - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1537190544376" - title="Sep 17, 2018 9:22 AM" - > - in a month - </time> - </div> + in a month + </time> </div> </div> </div> @@ -104753,9 +104693,12 @@ exports[`Storyshots Views / Orders / Order details fulfilled 1`] = ` class="MuiTypography-root-id CardTitle-title-id MuiTypography-h5-id" > <div - class="StatusLabel-root-id undefined StatusLabel-errorDot-id" + class="StatusLabel-root-id undefined StatusLabel-alertDot-id" > - Unfulfilled (3) + Unfulfilled + <div + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" + /> </div> </span> <div @@ -104781,35 +104724,31 @@ exports[`Storyshots Views / Orders / Order details fulfilled 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderUnfulfilledItems-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -104820,10 +104759,10 @@ exports[`Storyshots Views / Orders / Order details fulfilled 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderUnfulfilledItems-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderUnfulfilledItems-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -104844,22 +104783,22 @@ exports[`Storyshots Views / Orders / Order details fulfilled 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 59-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 3 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $18.51 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $55.53 </td> @@ -104900,7 +104839,7 @@ exports[`Storyshots Views / Orders / Order details fulfilled 1`] = ` > Fulfilled (1) <div - class="MuiTypography-root-id OrderFulfillment-orderNumber-id MuiTypography-body1-id" + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" > #9-2 </div> @@ -104955,35 +104894,31 @@ exports[`Storyshots Views / Orders / Order details fulfilled 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderFulfillment-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -104994,10 +104929,10 @@ exports[`Storyshots Views / Orders / Order details fulfilled 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderFulfillment-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -105018,48 +104953,26 @@ exports[`Storyshots Views / Orders / Order details fulfilled 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 5-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 1 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $79.71 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $79.71 </td> </tr> - <tr - class="MuiTableRow-root-id" - > - <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-infoRow-id" - colspan="5" - > - <div - class="MuiTypography-root-id MuiTypography-body2-id MuiTypography-colorTextSecondary-id" - > - Fulfilled from: - <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" - > - Be stocked - </div> - </div> - <div - class="MuiTypography-root-id MuiTypography-body2-id MuiTypography-colorTextSecondary-id" - /> - </td> - </tr> </tbody> </table> </div> @@ -105096,7 +105009,7 @@ exports[`Storyshots Views / Orders / Order details fulfilled 1`] = ` > Fulfilled (1) <div - class="MuiTypography-root-id OrderFulfillment-orderNumber-id MuiTypography-body1-id" + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" > #9-1 </div> @@ -105151,35 +105064,31 @@ exports[`Storyshots Views / Orders / Order details fulfilled 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderFulfillment-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -105190,10 +105099,10 @@ exports[`Storyshots Views / Orders / Order details fulfilled 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderFulfillment-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -105214,31 +105123,33 @@ exports[`Storyshots Views / Orders / Order details fulfilled 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 5-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 1 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $79.71 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $79.71 </td> </tr> + </tbody> + <tbody> <tr class="MuiTableRow-root-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-infoRow-id" + class="MuiTableCell-root-id ExtraInfoLines-infoRow-id" colspan="5" > <div @@ -105246,7 +105157,7 @@ exports[`Storyshots Views / Orders / Order details fulfilled 1`] = ` > Fulfilled from: <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id OrderFulfillment-infoLabelWithMargin-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" + class="MuiTypography-root-id ExtraInfoLines-infoLabel-id ExtraInfoLines-infoLabelWithMargin-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" > C our wares </div> @@ -105256,7 +105167,7 @@ exports[`Storyshots Views / Orders / Order details fulfilled 1`] = ` > Tracking Number: <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" + class="MuiTypography-root-id ExtraInfoLines-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" > 01nn12399su12nndfsy </div> @@ -105851,27 +105762,23 @@ exports[`Storyshots Views / Orders / Order details fulfilled 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Payment was authorized + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Payment was authorized - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -105882,27 +105789,23 @@ exports[`Storyshots Views / Orders / Order details fulfilled 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Order refund information was sent to customer + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Order refund information was sent to customer - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -105913,27 +105816,23 @@ exports[`Storyshots Views / Orders / Order details fulfilled 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Order cancel information was sent to customer + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Order cancel information was sent to customer - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -105944,27 +105843,23 @@ exports[`Storyshots Views / Orders / Order details fulfilled 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Note from external service + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Note from external service - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -106045,12 +105940,12 @@ exports[`Storyshots Views / Orders / Order details fulfilled 1`] = ` class="TimelineEvent-dot-id" /> <div - class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id TimelineEvent-expanded-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" + class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" > <div aria-disabled="false" aria-expanded="false" - class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id TimelineEvent-expanded-id" + class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id" role="button" tabindex="0" > @@ -106058,20 +105953,33 @@ exports[`Storyshots Views / Orders / Order details fulfilled 1`] = ` class="MuiExpansionPanelSummary-content-id" > <div - class="MuiTypography-root-id MuiTypography-body1-id" + class="TimelineEventHeader-container-id" > - Order was refunded by admin@example.com - </div> - <div - class="MuiTypography-root-id TimelineEvent-dateExpander-id MuiTypography-body1-id" - > - <time - class="" - datetime="1537190544376" - title="Sep 17, 2018 9:22 AM" + <div + class="TimelineEventHeader-elementsContainer-id" > - in a month - </time> + <div + class="MuiTypography-root-id TimelineEventHeader-titleElement-id MuiTypography-body1-id" + > + Products were refunded by + </div> + <a + class="MuiTypography-root-id TimelineEventHeader-titleElement-id Link-root-id Link-primary-id MuiTypography-body1-id" + > + Jane Doe + </a> + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1537190544376" + title="Sep 17, 2018 9:22 AM" + > + in a month + </time> + </div> </div> </div> <div @@ -106174,11 +106082,6 @@ exports[`Storyshots Views / Orders / Order details fulfilled 1`] = ` </tr> </tbody> </table> - <div - class="MuiTypography-root-id OrderHistory-eventSubtitle-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" - > - Refunded amount - </div> <div class="MuiTypography-root-id MuiTypography-body1-id" > @@ -106199,27 +106102,23 @@ exports[`Storyshots Views / Orders / Order details fulfilled 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Fulfilled 1 items + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1537190544376" + title="Sep 17, 2018 9:22 AM" > - Fulfilled 1 items - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1537190544376" - title="Sep 17, 2018 9:22 AM" - > - in a month - </time> - </div> + in a month + </time> </div> </div> </div> @@ -106568,164 +106467,6 @@ exports[`Storyshots Views / Orders / Order details loading 1`] = ` class="Grid-root-id Grid-default-id" > <div> - <div - class="CardSpacer-spacer-id" - /> - <div - class="MuiPaper-root-id MuiPaper-elevation0-id MuiCard-root-id MuiPaper-rounded-id" - > - <div - class="CardTitle-root-id" - > - <span - class="MuiTypography-root-id CardTitle-title-id MuiTypography-h5-id" - > - <span - class="Skeleton-skeleton-id" - > - ‌ - </span> - </span> - <div - class="CardTitle-toolbar-id" - /> - </div> - <div - class="CardTitle-children-id" - /> - <hr - class="CardTitle-hr-id" - /> - <div - class="ResponsiveTable-root-id" - > - <table - class="MuiTable-root-id OrderFulfillment-table-id" - > - <thead - class="MuiTableHead-root-id" - > - <tr - class="MuiTableRow-root-id MuiTableRow-head-id" - > - <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colName-id" - scope="col" - > - <span - class="OrderFulfillment-colNameLabel-id" - > - Product - </span> - </th> - <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colSku-id" - scope="col" - > - SKU - </th> - <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colQuantity-id" - scope="col" - > - Quantity - </th> - <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colPrice-id" - scope="col" - > - Price - </th> - <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colTotal-id" - scope="col" - > - Total - </th> - </tr> - </thead> - <tbody - class="MuiTableBody-root-id" - > - <tr - class="MuiTableRow-root-id" - > - <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderFulfillment-colName-id" - > - <div - class="TableCellAvatar-content-id" - > - <div - class="MuiAvatar-root-id MuiAvatar-circle-id TableCellAvatar-avatar-id MuiAvatar-colorDefault-id" - > - <svg - aria-hidden="true" - class="MuiSvgIcon-root-id MuiSvgIcon-colorPrimary-id" - focusable="false" - role="presentation" - viewBox="0 0 24 24" - > - <path - d="M19 8l-4 4h3c0 3.31-2.69 6-6 6-1.01 0-1.97-.25-2.8-.7l-1.46 1.46C8.97 19.54 10.43 20 12 20c4.42 0 8-3.58 8-8h3l-4-4zM6 12c0-3.31 2.69-6 6-6 1.01 0 1.97.25 2.8.7l1.46-1.46C15.03 4.46 13.57 4 12 4c-4.42 0-8 3.58-8 8H1l4 4 4-4H6z" - /> - </svg> - </div> - <div - class="TableCellAvatar-children-id" - > - <span - class="Skeleton-skeleton-id" - > - ‌ - </span> - </div> - </div> - </td> - <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colSku-id" - > - <span - class="Skeleton-skeleton-id" - > - ‌ - </span> - </td> - <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colQuantity-id" - > - <span - class="Skeleton-skeleton-id" - > - ‌ - </span> - </td> - <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colPrice-id" - > - <span - class="Skeleton-skeleton-id" - > - ‌ - </span> - </td> - <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colTotal-id" - > - <span - class="Skeleton-skeleton-id" - > - ‌ - </span> - </td> - </tr> - </tbody> - </table> - </div> - </div> - <div - class="CardSpacer-spacer-id" - /> <div class="MuiPaper-root-id MuiPaper-elevation0-id MuiCard-root-id MuiPaper-rounded-id" > @@ -107328,9 +107069,12 @@ exports[`Storyshots Views / Orders / Order details no customer note 1`] = ` class="MuiTypography-root-id CardTitle-title-id MuiTypography-h5-id" > <div - class="StatusLabel-root-id undefined StatusLabel-errorDot-id" + class="StatusLabel-root-id undefined StatusLabel-alertDot-id" > - Unfulfilled (3) + Unfulfilled + <div + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" + /> </div> </span> <div @@ -107356,35 +107100,31 @@ exports[`Storyshots Views / Orders / Order details no customer note 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderUnfulfilledItems-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -107395,10 +107135,10 @@ exports[`Storyshots Views / Orders / Order details no customer note 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderUnfulfilledItems-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderUnfulfilledItems-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -107419,22 +107159,22 @@ exports[`Storyshots Views / Orders / Order details no customer note 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 59-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 3 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $18.51 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $55.53 </td> @@ -107475,7 +107215,7 @@ exports[`Storyshots Views / Orders / Order details no customer note 1`] = ` > Fulfilled (1) <div - class="MuiTypography-root-id OrderFulfillment-orderNumber-id MuiTypography-body1-id" + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" > #9-2 </div> @@ -107530,35 +107270,31 @@ exports[`Storyshots Views / Orders / Order details no customer note 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderFulfillment-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -107569,10 +107305,10 @@ exports[`Storyshots Views / Orders / Order details no customer note 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderFulfillment-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -107593,48 +107329,26 @@ exports[`Storyshots Views / Orders / Order details no customer note 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 5-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 1 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $79.71 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $79.71 </td> </tr> - <tr - class="MuiTableRow-root-id" - > - <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-infoRow-id" - colspan="5" - > - <div - class="MuiTypography-root-id MuiTypography-body2-id MuiTypography-colorTextSecondary-id" - > - Fulfilled from: - <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" - > - Be stocked - </div> - </div> - <div - class="MuiTypography-root-id MuiTypography-body2-id MuiTypography-colorTextSecondary-id" - /> - </td> - </tr> </tbody> </table> </div> @@ -107671,7 +107385,7 @@ exports[`Storyshots Views / Orders / Order details no customer note 1`] = ` > Fulfilled (1) <div - class="MuiTypography-root-id OrderFulfillment-orderNumber-id MuiTypography-body1-id" + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" > #9-1 </div> @@ -107726,35 +107440,31 @@ exports[`Storyshots Views / Orders / Order details no customer note 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderFulfillment-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -107765,10 +107475,10 @@ exports[`Storyshots Views / Orders / Order details no customer note 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderFulfillment-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -107789,31 +107499,33 @@ exports[`Storyshots Views / Orders / Order details no customer note 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 5-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 1 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $79.71 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $79.71 </td> </tr> + </tbody> + <tbody> <tr class="MuiTableRow-root-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-infoRow-id" + class="MuiTableCell-root-id ExtraInfoLines-infoRow-id" colspan="5" > <div @@ -107821,7 +107533,7 @@ exports[`Storyshots Views / Orders / Order details no customer note 1`] = ` > Fulfilled from: <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id OrderFulfillment-infoLabelWithMargin-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" + class="MuiTypography-root-id ExtraInfoLines-infoLabel-id ExtraInfoLines-infoLabelWithMargin-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" > C our wares </div> @@ -107831,7 +107543,7 @@ exports[`Storyshots Views / Orders / Order details no customer note 1`] = ` > Tracking Number: <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" + class="MuiTypography-root-id ExtraInfoLines-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" > 01nn12399su12nndfsy </div> @@ -108426,27 +108138,23 @@ exports[`Storyshots Views / Orders / Order details no customer note 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Payment was authorized + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Payment was authorized - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -108457,27 +108165,23 @@ exports[`Storyshots Views / Orders / Order details no customer note 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Order refund information was sent to customer + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Order refund information was sent to customer - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -108488,27 +108192,23 @@ exports[`Storyshots Views / Orders / Order details no customer note 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Order cancel information was sent to customer + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Order cancel information was sent to customer - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -108519,27 +108219,23 @@ exports[`Storyshots Views / Orders / Order details no customer note 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Note from external service + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Note from external service - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -108620,12 +108316,12 @@ exports[`Storyshots Views / Orders / Order details no customer note 1`] = ` class="TimelineEvent-dot-id" /> <div - class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id TimelineEvent-expanded-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" + class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" > <div aria-disabled="false" aria-expanded="false" - class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id TimelineEvent-expanded-id" + class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id" role="button" tabindex="0" > @@ -108633,20 +108329,33 @@ exports[`Storyshots Views / Orders / Order details no customer note 1`] = ` class="MuiExpansionPanelSummary-content-id" > <div - class="MuiTypography-root-id MuiTypography-body1-id" + class="TimelineEventHeader-container-id" > - Order was refunded by admin@example.com - </div> - <div - class="MuiTypography-root-id TimelineEvent-dateExpander-id MuiTypography-body1-id" - > - <time - class="" - datetime="1537190544376" - title="Sep 17, 2018 9:22 AM" + <div + class="TimelineEventHeader-elementsContainer-id" > - in a month - </time> + <div + class="MuiTypography-root-id TimelineEventHeader-titleElement-id MuiTypography-body1-id" + > + Products were refunded by + </div> + <a + class="MuiTypography-root-id TimelineEventHeader-titleElement-id Link-root-id Link-primary-id MuiTypography-body1-id" + > + Jane Doe + </a> + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1537190544376" + title="Sep 17, 2018 9:22 AM" + > + in a month + </time> + </div> </div> </div> <div @@ -108749,11 +108458,6 @@ exports[`Storyshots Views / Orders / Order details no customer note 1`] = ` </tr> </tbody> </table> - <div - class="MuiTypography-root-id OrderHistory-eventSubtitle-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" - > - Refunded amount - </div> <div class="MuiTypography-root-id MuiTypography-body1-id" > @@ -108774,27 +108478,23 @@ exports[`Storyshots Views / Orders / Order details no customer note 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Fulfilled 1 items + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1537190544376" + title="Sep 17, 2018 9:22 AM" > - Fulfilled 1 items - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1537190544376" - title="Sep 17, 2018 9:22 AM" - > - in a month - </time> - </div> + in a month + </time> </div> </div> </div> @@ -109177,9 +108877,12 @@ exports[`Storyshots Views / Orders / Order details no payment 1`] = ` class="MuiTypography-root-id CardTitle-title-id MuiTypography-h5-id" > <div - class="StatusLabel-root-id undefined StatusLabel-errorDot-id" + class="StatusLabel-root-id undefined StatusLabel-alertDot-id" > - Unfulfilled (3) + Unfulfilled + <div + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" + /> </div> </span> <div @@ -109205,35 +108908,31 @@ exports[`Storyshots Views / Orders / Order details no payment 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderUnfulfilledItems-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -109244,10 +108943,10 @@ exports[`Storyshots Views / Orders / Order details no payment 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderUnfulfilledItems-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderUnfulfilledItems-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -109268,22 +108967,22 @@ exports[`Storyshots Views / Orders / Order details no payment 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 59-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 3 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $18.51 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $55.53 </td> @@ -109324,7 +109023,7 @@ exports[`Storyshots Views / Orders / Order details no payment 1`] = ` > Fulfilled (1) <div - class="MuiTypography-root-id OrderFulfillment-orderNumber-id MuiTypography-body1-id" + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" > #9-2 </div> @@ -109379,35 +109078,31 @@ exports[`Storyshots Views / Orders / Order details no payment 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderFulfillment-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -109418,10 +109113,10 @@ exports[`Storyshots Views / Orders / Order details no payment 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderFulfillment-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -109442,48 +109137,26 @@ exports[`Storyshots Views / Orders / Order details no payment 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 5-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 1 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $79.71 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $79.71 </td> </tr> - <tr - class="MuiTableRow-root-id" - > - <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-infoRow-id" - colspan="5" - > - <div - class="MuiTypography-root-id MuiTypography-body2-id MuiTypography-colorTextSecondary-id" - > - Fulfilled from: - <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" - > - Be stocked - </div> - </div> - <div - class="MuiTypography-root-id MuiTypography-body2-id MuiTypography-colorTextSecondary-id" - /> - </td> - </tr> </tbody> </table> </div> @@ -109520,7 +109193,7 @@ exports[`Storyshots Views / Orders / Order details no payment 1`] = ` > Fulfilled (1) <div - class="MuiTypography-root-id OrderFulfillment-orderNumber-id MuiTypography-body1-id" + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" > #9-1 </div> @@ -109575,35 +109248,31 @@ exports[`Storyshots Views / Orders / Order details no payment 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderFulfillment-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -109614,10 +109283,10 @@ exports[`Storyshots Views / Orders / Order details no payment 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderFulfillment-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -109638,31 +109307,33 @@ exports[`Storyshots Views / Orders / Order details no payment 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 5-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 1 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $79.71 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $79.71 </td> </tr> + </tbody> + <tbody> <tr class="MuiTableRow-root-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-infoRow-id" + class="MuiTableCell-root-id ExtraInfoLines-infoRow-id" colspan="5" > <div @@ -109670,7 +109341,7 @@ exports[`Storyshots Views / Orders / Order details no payment 1`] = ` > Fulfilled from: <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id OrderFulfillment-infoLabelWithMargin-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" + class="MuiTypography-root-id ExtraInfoLines-infoLabel-id ExtraInfoLines-infoLabelWithMargin-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" > C our wares </div> @@ -109680,7 +109351,7 @@ exports[`Storyshots Views / Orders / Order details no payment 1`] = ` > Tracking Number: <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" + class="MuiTypography-root-id ExtraInfoLines-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" > 01nn12399su12nndfsy </div> @@ -110275,27 +109946,23 @@ exports[`Storyshots Views / Orders / Order details no payment 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Payment was authorized + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Payment was authorized - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -110306,27 +109973,23 @@ exports[`Storyshots Views / Orders / Order details no payment 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Order refund information was sent to customer + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Order refund information was sent to customer - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -110337,27 +110000,23 @@ exports[`Storyshots Views / Orders / Order details no payment 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Order cancel information was sent to customer + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Order cancel information was sent to customer - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -110368,27 +110027,23 @@ exports[`Storyshots Views / Orders / Order details no payment 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Note from external service + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Note from external service - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -110469,12 +110124,12 @@ exports[`Storyshots Views / Orders / Order details no payment 1`] = ` class="TimelineEvent-dot-id" /> <div - class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id TimelineEvent-expanded-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" + class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" > <div aria-disabled="false" aria-expanded="false" - class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id TimelineEvent-expanded-id" + class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id" role="button" tabindex="0" > @@ -110482,20 +110137,33 @@ exports[`Storyshots Views / Orders / Order details no payment 1`] = ` class="MuiExpansionPanelSummary-content-id" > <div - class="MuiTypography-root-id MuiTypography-body1-id" + class="TimelineEventHeader-container-id" > - Order was refunded by admin@example.com - </div> - <div - class="MuiTypography-root-id TimelineEvent-dateExpander-id MuiTypography-body1-id" - > - <time - class="" - datetime="1537190544376" - title="Sep 17, 2018 9:22 AM" + <div + class="TimelineEventHeader-elementsContainer-id" > - in a month - </time> + <div + class="MuiTypography-root-id TimelineEventHeader-titleElement-id MuiTypography-body1-id" + > + Products were refunded by + </div> + <a + class="MuiTypography-root-id TimelineEventHeader-titleElement-id Link-root-id Link-primary-id MuiTypography-body1-id" + > + Jane Doe + </a> + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1537190544376" + title="Sep 17, 2018 9:22 AM" + > + in a month + </time> + </div> </div> </div> <div @@ -110598,11 +110266,6 @@ exports[`Storyshots Views / Orders / Order details no payment 1`] = ` </tr> </tbody> </table> - <div - class="MuiTypography-root-id OrderHistory-eventSubtitle-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" - > - Refunded amount - </div> <div class="MuiTypography-root-id MuiTypography-body1-id" > @@ -110623,27 +110286,23 @@ exports[`Storyshots Views / Orders / Order details no payment 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Fulfilled 1 items + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1537190544376" + title="Sep 17, 2018 9:22 AM" > - Fulfilled 1 items - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1537190544376" - title="Sep 17, 2018 9:22 AM" - > - in a month - </time> - </div> + in a month + </time> </div> </div> </div> @@ -111026,9 +110685,12 @@ exports[`Storyshots Views / Orders / Order details no shipping address 1`] = ` class="MuiTypography-root-id CardTitle-title-id MuiTypography-h5-id" > <div - class="StatusLabel-root-id undefined StatusLabel-errorDot-id" + class="StatusLabel-root-id undefined StatusLabel-alertDot-id" > - Unfulfilled (3) + Unfulfilled + <div + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" + /> </div> </span> <div @@ -111054,35 +110716,31 @@ exports[`Storyshots Views / Orders / Order details no shipping address 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderUnfulfilledItems-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -111093,10 +110751,10 @@ exports[`Storyshots Views / Orders / Order details no shipping address 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderUnfulfilledItems-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderUnfulfilledItems-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -111117,22 +110775,22 @@ exports[`Storyshots Views / Orders / Order details no shipping address 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 59-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 3 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $18.51 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $55.53 </td> @@ -111173,7 +110831,7 @@ exports[`Storyshots Views / Orders / Order details no shipping address 1`] = ` > Fulfilled (1) <div - class="MuiTypography-root-id OrderFulfillment-orderNumber-id MuiTypography-body1-id" + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" > #9-2 </div> @@ -111228,35 +110886,31 @@ exports[`Storyshots Views / Orders / Order details no shipping address 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderFulfillment-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -111267,10 +110921,10 @@ exports[`Storyshots Views / Orders / Order details no shipping address 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderFulfillment-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -111291,48 +110945,26 @@ exports[`Storyshots Views / Orders / Order details no shipping address 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 5-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 1 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $79.71 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $79.71 </td> </tr> - <tr - class="MuiTableRow-root-id" - > - <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-infoRow-id" - colspan="5" - > - <div - class="MuiTypography-root-id MuiTypography-body2-id MuiTypography-colorTextSecondary-id" - > - Fulfilled from: - <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" - > - Be stocked - </div> - </div> - <div - class="MuiTypography-root-id MuiTypography-body2-id MuiTypography-colorTextSecondary-id" - /> - </td> - </tr> </tbody> </table> </div> @@ -111369,7 +111001,7 @@ exports[`Storyshots Views / Orders / Order details no shipping address 1`] = ` > Fulfilled (1) <div - class="MuiTypography-root-id OrderFulfillment-orderNumber-id MuiTypography-body1-id" + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" > #9-1 </div> @@ -111424,35 +111056,31 @@ exports[`Storyshots Views / Orders / Order details no shipping address 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderFulfillment-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -111463,10 +111091,10 @@ exports[`Storyshots Views / Orders / Order details no shipping address 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderFulfillment-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -111487,31 +111115,33 @@ exports[`Storyshots Views / Orders / Order details no shipping address 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 5-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 1 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $79.71 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $79.71 </td> </tr> + </tbody> + <tbody> <tr class="MuiTableRow-root-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-infoRow-id" + class="MuiTableCell-root-id ExtraInfoLines-infoRow-id" colspan="5" > <div @@ -111519,7 +111149,7 @@ exports[`Storyshots Views / Orders / Order details no shipping address 1`] = ` > Fulfilled from: <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id OrderFulfillment-infoLabelWithMargin-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" + class="MuiTypography-root-id ExtraInfoLines-infoLabel-id ExtraInfoLines-infoLabelWithMargin-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" > C our wares </div> @@ -111529,7 +111159,7 @@ exports[`Storyshots Views / Orders / Order details no shipping address 1`] = ` > Tracking Number: <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" + class="MuiTypography-root-id ExtraInfoLines-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" > 01nn12399su12nndfsy </div> @@ -112124,27 +111754,23 @@ exports[`Storyshots Views / Orders / Order details no shipping address 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Payment was authorized + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Payment was authorized - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -112155,27 +111781,23 @@ exports[`Storyshots Views / Orders / Order details no shipping address 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Order refund information was sent to customer + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Order refund information was sent to customer - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -112186,27 +111808,23 @@ exports[`Storyshots Views / Orders / Order details no shipping address 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Order cancel information was sent to customer + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Order cancel information was sent to customer - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -112217,27 +111835,23 @@ exports[`Storyshots Views / Orders / Order details no shipping address 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Note from external service + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Note from external service - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -112318,12 +111932,12 @@ exports[`Storyshots Views / Orders / Order details no shipping address 1`] = ` class="TimelineEvent-dot-id" /> <div - class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id TimelineEvent-expanded-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" + class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" > <div aria-disabled="false" aria-expanded="false" - class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id TimelineEvent-expanded-id" + class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id" role="button" tabindex="0" > @@ -112331,20 +111945,33 @@ exports[`Storyshots Views / Orders / Order details no shipping address 1`] = ` class="MuiExpansionPanelSummary-content-id" > <div - class="MuiTypography-root-id MuiTypography-body1-id" + class="TimelineEventHeader-container-id" > - Order was refunded by admin@example.com - </div> - <div - class="MuiTypography-root-id TimelineEvent-dateExpander-id MuiTypography-body1-id" - > - <time - class="" - datetime="1537190544376" - title="Sep 17, 2018 9:22 AM" + <div + class="TimelineEventHeader-elementsContainer-id" > - in a month - </time> + <div + class="MuiTypography-root-id TimelineEventHeader-titleElement-id MuiTypography-body1-id" + > + Products were refunded by + </div> + <a + class="MuiTypography-root-id TimelineEventHeader-titleElement-id Link-root-id Link-primary-id MuiTypography-body1-id" + > + Jane Doe + </a> + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1537190544376" + title="Sep 17, 2018 9:22 AM" + > + in a month + </time> + </div> </div> </div> <div @@ -112447,11 +112074,6 @@ exports[`Storyshots Views / Orders / Order details no shipping address 1`] = ` </tr> </tbody> </table> - <div - class="MuiTypography-root-id OrderHistory-eventSubtitle-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" - > - Refunded amount - </div> <div class="MuiTypography-root-id MuiTypography-body1-id" > @@ -112472,27 +112094,23 @@ exports[`Storyshots Views / Orders / Order details no shipping address 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Fulfilled 1 items + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1537190544376" + title="Sep 17, 2018 9:22 AM" > - Fulfilled 1 items - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1537190544376" - title="Sep 17, 2018 9:22 AM" - > - in a month - </time> - </div> + in a month + </time> </div> </div> </div> @@ -112875,9 +112493,12 @@ exports[`Storyshots Views / Orders / Order details partially fulfilled 1`] = ` class="MuiTypography-root-id CardTitle-title-id MuiTypography-h5-id" > <div - class="StatusLabel-root-id undefined StatusLabel-errorDot-id" + class="StatusLabel-root-id undefined StatusLabel-alertDot-id" > - Unfulfilled (3) + Unfulfilled + <div + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" + /> </div> </span> <div @@ -112903,35 +112524,31 @@ exports[`Storyshots Views / Orders / Order details partially fulfilled 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderUnfulfilledItems-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -112942,10 +112559,10 @@ exports[`Storyshots Views / Orders / Order details partially fulfilled 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderUnfulfilledItems-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderUnfulfilledItems-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -112966,22 +112583,22 @@ exports[`Storyshots Views / Orders / Order details partially fulfilled 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 59-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 3 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $18.51 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $55.53 </td> @@ -113022,7 +112639,7 @@ exports[`Storyshots Views / Orders / Order details partially fulfilled 1`] = ` > Fulfilled (1) <div - class="MuiTypography-root-id OrderFulfillment-orderNumber-id MuiTypography-body1-id" + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" > #9-2 </div> @@ -113077,35 +112694,31 @@ exports[`Storyshots Views / Orders / Order details partially fulfilled 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderFulfillment-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -113116,10 +112729,10 @@ exports[`Storyshots Views / Orders / Order details partially fulfilled 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderFulfillment-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -113140,48 +112753,26 @@ exports[`Storyshots Views / Orders / Order details partially fulfilled 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 5-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 1 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $79.71 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $79.71 </td> </tr> - <tr - class="MuiTableRow-root-id" - > - <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-infoRow-id" - colspan="5" - > - <div - class="MuiTypography-root-id MuiTypography-body2-id MuiTypography-colorTextSecondary-id" - > - Fulfilled from: - <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" - > - Be stocked - </div> - </div> - <div - class="MuiTypography-root-id MuiTypography-body2-id MuiTypography-colorTextSecondary-id" - /> - </td> - </tr> </tbody> </table> </div> @@ -113218,7 +112809,7 @@ exports[`Storyshots Views / Orders / Order details partially fulfilled 1`] = ` > Fulfilled (1) <div - class="MuiTypography-root-id OrderFulfillment-orderNumber-id MuiTypography-body1-id" + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" > #9-1 </div> @@ -113273,35 +112864,31 @@ exports[`Storyshots Views / Orders / Order details partially fulfilled 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderFulfillment-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -113312,10 +112899,10 @@ exports[`Storyshots Views / Orders / Order details partially fulfilled 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderFulfillment-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -113336,31 +112923,33 @@ exports[`Storyshots Views / Orders / Order details partially fulfilled 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 5-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 1 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $79.71 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $79.71 </td> </tr> + </tbody> + <tbody> <tr class="MuiTableRow-root-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-infoRow-id" + class="MuiTableCell-root-id ExtraInfoLines-infoRow-id" colspan="5" > <div @@ -113368,7 +112957,7 @@ exports[`Storyshots Views / Orders / Order details partially fulfilled 1`] = ` > Fulfilled from: <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id OrderFulfillment-infoLabelWithMargin-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" + class="MuiTypography-root-id ExtraInfoLines-infoLabel-id ExtraInfoLines-infoLabelWithMargin-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" > C our wares </div> @@ -113378,7 +112967,7 @@ exports[`Storyshots Views / Orders / Order details partially fulfilled 1`] = ` > Tracking Number: <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" + class="MuiTypography-root-id ExtraInfoLines-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" > 01nn12399su12nndfsy </div> @@ -113973,27 +113562,23 @@ exports[`Storyshots Views / Orders / Order details partially fulfilled 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Payment was authorized + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Payment was authorized - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -114004,27 +113589,23 @@ exports[`Storyshots Views / Orders / Order details partially fulfilled 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Order refund information was sent to customer + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Order refund information was sent to customer - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -114035,27 +113616,23 @@ exports[`Storyshots Views / Orders / Order details partially fulfilled 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Order cancel information was sent to customer + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Order cancel information was sent to customer - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -114066,27 +113643,23 @@ exports[`Storyshots Views / Orders / Order details partially fulfilled 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Note from external service + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Note from external service - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -114167,12 +113740,12 @@ exports[`Storyshots Views / Orders / Order details partially fulfilled 1`] = ` class="TimelineEvent-dot-id" /> <div - class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id TimelineEvent-expanded-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" + class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" > <div aria-disabled="false" aria-expanded="false" - class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id TimelineEvent-expanded-id" + class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id" role="button" tabindex="0" > @@ -114180,20 +113753,33 @@ exports[`Storyshots Views / Orders / Order details partially fulfilled 1`] = ` class="MuiExpansionPanelSummary-content-id" > <div - class="MuiTypography-root-id MuiTypography-body1-id" + class="TimelineEventHeader-container-id" > - Order was refunded by admin@example.com - </div> - <div - class="MuiTypography-root-id TimelineEvent-dateExpander-id MuiTypography-body1-id" - > - <time - class="" - datetime="1537190544376" - title="Sep 17, 2018 9:22 AM" + <div + class="TimelineEventHeader-elementsContainer-id" > - in a month - </time> + <div + class="MuiTypography-root-id TimelineEventHeader-titleElement-id MuiTypography-body1-id" + > + Products were refunded by + </div> + <a + class="MuiTypography-root-id TimelineEventHeader-titleElement-id Link-root-id Link-primary-id MuiTypography-body1-id" + > + Jane Doe + </a> + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1537190544376" + title="Sep 17, 2018 9:22 AM" + > + in a month + </time> + </div> </div> </div> <div @@ -114296,11 +113882,6 @@ exports[`Storyshots Views / Orders / Order details partially fulfilled 1`] = ` </tr> </tbody> </table> - <div - class="MuiTypography-root-id OrderHistory-eventSubtitle-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" - > - Refunded amount - </div> <div class="MuiTypography-root-id MuiTypography-body1-id" > @@ -114321,27 +113902,23 @@ exports[`Storyshots Views / Orders / Order details partially fulfilled 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Fulfilled 1 items + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1537190544376" + title="Sep 17, 2018 9:22 AM" > - Fulfilled 1 items - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1537190544376" - title="Sep 17, 2018 9:22 AM" - > - in a month - </time> - </div> + in a month + </time> </div> </div> </div> @@ -114724,9 +114301,12 @@ exports[`Storyshots Views / Orders / Order details payment confirmed 1`] = ` class="MuiTypography-root-id CardTitle-title-id MuiTypography-h5-id" > <div - class="StatusLabel-root-id undefined StatusLabel-errorDot-id" + class="StatusLabel-root-id undefined StatusLabel-alertDot-id" > - Unfulfilled (3) + Unfulfilled + <div + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" + /> </div> </span> <div @@ -114752,35 +114332,31 @@ exports[`Storyshots Views / Orders / Order details payment confirmed 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderUnfulfilledItems-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -114791,10 +114367,10 @@ exports[`Storyshots Views / Orders / Order details payment confirmed 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderUnfulfilledItems-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderUnfulfilledItems-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -114815,22 +114391,22 @@ exports[`Storyshots Views / Orders / Order details payment confirmed 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 59-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 3 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $18.51 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $55.53 </td> @@ -114871,7 +114447,7 @@ exports[`Storyshots Views / Orders / Order details payment confirmed 1`] = ` > Fulfilled (1) <div - class="MuiTypography-root-id OrderFulfillment-orderNumber-id MuiTypography-body1-id" + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" > #9-2 </div> @@ -114926,35 +114502,31 @@ exports[`Storyshots Views / Orders / Order details payment confirmed 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderFulfillment-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -114965,10 +114537,10 @@ exports[`Storyshots Views / Orders / Order details payment confirmed 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderFulfillment-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -114989,48 +114561,26 @@ exports[`Storyshots Views / Orders / Order details payment confirmed 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 5-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 1 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $79.71 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $79.71 </td> </tr> - <tr - class="MuiTableRow-root-id" - > - <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-infoRow-id" - colspan="5" - > - <div - class="MuiTypography-root-id MuiTypography-body2-id MuiTypography-colorTextSecondary-id" - > - Fulfilled from: - <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" - > - Be stocked - </div> - </div> - <div - class="MuiTypography-root-id MuiTypography-body2-id MuiTypography-colorTextSecondary-id" - /> - </td> - </tr> </tbody> </table> </div> @@ -115067,7 +114617,7 @@ exports[`Storyshots Views / Orders / Order details payment confirmed 1`] = ` > Fulfilled (1) <div - class="MuiTypography-root-id OrderFulfillment-orderNumber-id MuiTypography-body1-id" + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" > #9-1 </div> @@ -115122,35 +114672,31 @@ exports[`Storyshots Views / Orders / Order details payment confirmed 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderFulfillment-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -115161,10 +114707,10 @@ exports[`Storyshots Views / Orders / Order details payment confirmed 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderFulfillment-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -115185,31 +114731,33 @@ exports[`Storyshots Views / Orders / Order details payment confirmed 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 5-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 1 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $79.71 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $79.71 </td> </tr> + </tbody> + <tbody> <tr class="MuiTableRow-root-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-infoRow-id" + class="MuiTableCell-root-id ExtraInfoLines-infoRow-id" colspan="5" > <div @@ -115217,7 +114765,7 @@ exports[`Storyshots Views / Orders / Order details payment confirmed 1`] = ` > Fulfilled from: <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id OrderFulfillment-infoLabelWithMargin-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" + class="MuiTypography-root-id ExtraInfoLines-infoLabel-id ExtraInfoLines-infoLabelWithMargin-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" > C our wares </div> @@ -115227,7 +114775,7 @@ exports[`Storyshots Views / Orders / Order details payment confirmed 1`] = ` > Tracking Number: <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" + class="MuiTypography-root-id ExtraInfoLines-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" > 01nn12399su12nndfsy </div> @@ -115822,27 +115370,23 @@ exports[`Storyshots Views / Orders / Order details payment confirmed 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Payment was authorized + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Payment was authorized - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -115853,27 +115397,23 @@ exports[`Storyshots Views / Orders / Order details payment confirmed 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Order refund information was sent to customer + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Order refund information was sent to customer - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -115884,27 +115424,23 @@ exports[`Storyshots Views / Orders / Order details payment confirmed 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Order cancel information was sent to customer + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Order cancel information was sent to customer - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -115915,27 +115451,23 @@ exports[`Storyshots Views / Orders / Order details payment confirmed 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Note from external service + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Note from external service - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -116016,12 +115548,12 @@ exports[`Storyshots Views / Orders / Order details payment confirmed 1`] = ` class="TimelineEvent-dot-id" /> <div - class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id TimelineEvent-expanded-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" + class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" > <div aria-disabled="false" aria-expanded="false" - class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id TimelineEvent-expanded-id" + class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id" role="button" tabindex="0" > @@ -116029,20 +115561,33 @@ exports[`Storyshots Views / Orders / Order details payment confirmed 1`] = ` class="MuiExpansionPanelSummary-content-id" > <div - class="MuiTypography-root-id MuiTypography-body1-id" + class="TimelineEventHeader-container-id" > - Order was refunded by admin@example.com - </div> - <div - class="MuiTypography-root-id TimelineEvent-dateExpander-id MuiTypography-body1-id" - > - <time - class="" - datetime="1537190544376" - title="Sep 17, 2018 9:22 AM" + <div + class="TimelineEventHeader-elementsContainer-id" > - in a month - </time> + <div + class="MuiTypography-root-id TimelineEventHeader-titleElement-id MuiTypography-body1-id" + > + Products were refunded by + </div> + <a + class="MuiTypography-root-id TimelineEventHeader-titleElement-id Link-root-id Link-primary-id MuiTypography-body1-id" + > + Jane Doe + </a> + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1537190544376" + title="Sep 17, 2018 9:22 AM" + > + in a month + </time> + </div> </div> </div> <div @@ -116145,11 +115690,6 @@ exports[`Storyshots Views / Orders / Order details payment confirmed 1`] = ` </tr> </tbody> </table> - <div - class="MuiTypography-root-id OrderHistory-eventSubtitle-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" - > - Refunded amount - </div> <div class="MuiTypography-root-id MuiTypography-body1-id" > @@ -116170,27 +115710,23 @@ exports[`Storyshots Views / Orders / Order details payment confirmed 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Fulfilled 1 items + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1537190544376" + title="Sep 17, 2018 9:22 AM" > - Fulfilled 1 items - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1537190544376" - title="Sep 17, 2018 9:22 AM" - > - in a month - </time> - </div> + in a month + </time> </div> </div> </div> @@ -116573,9 +116109,12 @@ exports[`Storyshots Views / Orders / Order details payment error 1`] = ` class="MuiTypography-root-id CardTitle-title-id MuiTypography-h5-id" > <div - class="StatusLabel-root-id undefined StatusLabel-errorDot-id" + class="StatusLabel-root-id undefined StatusLabel-alertDot-id" > - Unfulfilled (3) + Unfulfilled + <div + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" + /> </div> </span> <div @@ -116601,35 +116140,31 @@ exports[`Storyshots Views / Orders / Order details payment error 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderUnfulfilledItems-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -116640,10 +116175,10 @@ exports[`Storyshots Views / Orders / Order details payment error 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderUnfulfilledItems-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderUnfulfilledItems-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -116664,22 +116199,22 @@ exports[`Storyshots Views / Orders / Order details payment error 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 59-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 3 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $18.51 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $55.53 </td> @@ -116720,7 +116255,7 @@ exports[`Storyshots Views / Orders / Order details payment error 1`] = ` > Fulfilled (1) <div - class="MuiTypography-root-id OrderFulfillment-orderNumber-id MuiTypography-body1-id" + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" > #9-2 </div> @@ -116775,35 +116310,31 @@ exports[`Storyshots Views / Orders / Order details payment error 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderFulfillment-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -116814,10 +116345,10 @@ exports[`Storyshots Views / Orders / Order details payment error 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderFulfillment-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -116838,48 +116369,26 @@ exports[`Storyshots Views / Orders / Order details payment error 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 5-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 1 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $79.71 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $79.71 </td> </tr> - <tr - class="MuiTableRow-root-id" - > - <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-infoRow-id" - colspan="5" - > - <div - class="MuiTypography-root-id MuiTypography-body2-id MuiTypography-colorTextSecondary-id" - > - Fulfilled from: - <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" - > - Be stocked - </div> - </div> - <div - class="MuiTypography-root-id MuiTypography-body2-id MuiTypography-colorTextSecondary-id" - /> - </td> - </tr> </tbody> </table> </div> @@ -116916,7 +116425,7 @@ exports[`Storyshots Views / Orders / Order details payment error 1`] = ` > Fulfilled (1) <div - class="MuiTypography-root-id OrderFulfillment-orderNumber-id MuiTypography-body1-id" + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" > #9-1 </div> @@ -116971,35 +116480,31 @@ exports[`Storyshots Views / Orders / Order details payment error 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderFulfillment-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -117010,10 +116515,10 @@ exports[`Storyshots Views / Orders / Order details payment error 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderFulfillment-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -117034,31 +116539,33 @@ exports[`Storyshots Views / Orders / Order details payment error 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 5-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 1 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $79.71 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $79.71 </td> </tr> + </tbody> + <tbody> <tr class="MuiTableRow-root-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-infoRow-id" + class="MuiTableCell-root-id ExtraInfoLines-infoRow-id" colspan="5" > <div @@ -117066,7 +116573,7 @@ exports[`Storyshots Views / Orders / Order details payment error 1`] = ` > Fulfilled from: <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id OrderFulfillment-infoLabelWithMargin-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" + class="MuiTypography-root-id ExtraInfoLines-infoLabel-id ExtraInfoLines-infoLabelWithMargin-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" > C our wares </div> @@ -117076,7 +116583,7 @@ exports[`Storyshots Views / Orders / Order details payment error 1`] = ` > Tracking Number: <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" + class="MuiTypography-root-id ExtraInfoLines-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" > 01nn12399su12nndfsy </div> @@ -117671,27 +117178,23 @@ exports[`Storyshots Views / Orders / Order details payment error 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Payment was authorized + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Payment was authorized - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -117702,27 +117205,23 @@ exports[`Storyshots Views / Orders / Order details payment error 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Order refund information was sent to customer + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Order refund information was sent to customer - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -117733,27 +117232,23 @@ exports[`Storyshots Views / Orders / Order details payment error 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Order cancel information was sent to customer + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Order cancel information was sent to customer - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -117764,27 +117259,23 @@ exports[`Storyshots Views / Orders / Order details payment error 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Note from external service + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Note from external service - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -117865,12 +117356,12 @@ exports[`Storyshots Views / Orders / Order details payment error 1`] = ` class="TimelineEvent-dot-id" /> <div - class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id TimelineEvent-expanded-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" + class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" > <div aria-disabled="false" aria-expanded="false" - class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id TimelineEvent-expanded-id" + class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id" role="button" tabindex="0" > @@ -117878,20 +117369,33 @@ exports[`Storyshots Views / Orders / Order details payment error 1`] = ` class="MuiExpansionPanelSummary-content-id" > <div - class="MuiTypography-root-id MuiTypography-body1-id" + class="TimelineEventHeader-container-id" > - Order was refunded by admin@example.com - </div> - <div - class="MuiTypography-root-id TimelineEvent-dateExpander-id MuiTypography-body1-id" - > - <time - class="" - datetime="1537190544376" - title="Sep 17, 2018 9:22 AM" + <div + class="TimelineEventHeader-elementsContainer-id" > - in a month - </time> + <div + class="MuiTypography-root-id TimelineEventHeader-titleElement-id MuiTypography-body1-id" + > + Products were refunded by + </div> + <a + class="MuiTypography-root-id TimelineEventHeader-titleElement-id Link-root-id Link-primary-id MuiTypography-body1-id" + > + Jane Doe + </a> + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1537190544376" + title="Sep 17, 2018 9:22 AM" + > + in a month + </time> + </div> </div> </div> <div @@ -117994,11 +117498,6 @@ exports[`Storyshots Views / Orders / Order details payment error 1`] = ` </tr> </tbody> </table> - <div - class="MuiTypography-root-id OrderHistory-eventSubtitle-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" - > - Refunded amount - </div> <div class="MuiTypography-root-id MuiTypography-body1-id" > @@ -118019,27 +117518,23 @@ exports[`Storyshots Views / Orders / Order details payment error 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Fulfilled 1 items + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1537190544376" + title="Sep 17, 2018 9:22 AM" > - Fulfilled 1 items - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1537190544376" - title="Sep 17, 2018 9:22 AM" - > - in a month - </time> - </div> + in a month + </time> </div> </div> </div> @@ -118422,9 +117917,12 @@ exports[`Storyshots Views / Orders / Order details pending payment 1`] = ` class="MuiTypography-root-id CardTitle-title-id MuiTypography-h5-id" > <div - class="StatusLabel-root-id undefined StatusLabel-errorDot-id" + class="StatusLabel-root-id undefined StatusLabel-alertDot-id" > - Unfulfilled (3) + Unfulfilled + <div + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" + /> </div> </span> <div @@ -118450,35 +117948,31 @@ exports[`Storyshots Views / Orders / Order details pending payment 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderUnfulfilledItems-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -118489,10 +117983,10 @@ exports[`Storyshots Views / Orders / Order details pending payment 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderUnfulfilledItems-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderUnfulfilledItems-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -118513,22 +118007,22 @@ exports[`Storyshots Views / Orders / Order details pending payment 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 59-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 3 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $18.51 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $55.53 </td> @@ -118569,7 +118063,7 @@ exports[`Storyshots Views / Orders / Order details pending payment 1`] = ` > Fulfilled (1) <div - class="MuiTypography-root-id OrderFulfillment-orderNumber-id MuiTypography-body1-id" + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" > #9-2 </div> @@ -118624,35 +118118,31 @@ exports[`Storyshots Views / Orders / Order details pending payment 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderFulfillment-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -118663,10 +118153,10 @@ exports[`Storyshots Views / Orders / Order details pending payment 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderFulfillment-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -118687,48 +118177,26 @@ exports[`Storyshots Views / Orders / Order details pending payment 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 5-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 1 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $79.71 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $79.71 </td> </tr> - <tr - class="MuiTableRow-root-id" - > - <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-infoRow-id" - colspan="5" - > - <div - class="MuiTypography-root-id MuiTypography-body2-id MuiTypography-colorTextSecondary-id" - > - Fulfilled from: - <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" - > - Be stocked - </div> - </div> - <div - class="MuiTypography-root-id MuiTypography-body2-id MuiTypography-colorTextSecondary-id" - /> - </td> - </tr> </tbody> </table> </div> @@ -118765,7 +118233,7 @@ exports[`Storyshots Views / Orders / Order details pending payment 1`] = ` > Fulfilled (1) <div - class="MuiTypography-root-id OrderFulfillment-orderNumber-id MuiTypography-body1-id" + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" > #9-1 </div> @@ -118820,35 +118288,31 @@ exports[`Storyshots Views / Orders / Order details pending payment 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderFulfillment-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -118859,10 +118323,10 @@ exports[`Storyshots Views / Orders / Order details pending payment 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderFulfillment-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -118883,31 +118347,33 @@ exports[`Storyshots Views / Orders / Order details pending payment 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 5-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 1 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $79.71 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $79.71 </td> </tr> + </tbody> + <tbody> <tr class="MuiTableRow-root-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-infoRow-id" + class="MuiTableCell-root-id ExtraInfoLines-infoRow-id" colspan="5" > <div @@ -118915,7 +118381,7 @@ exports[`Storyshots Views / Orders / Order details pending payment 1`] = ` > Fulfilled from: <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id OrderFulfillment-infoLabelWithMargin-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" + class="MuiTypography-root-id ExtraInfoLines-infoLabel-id ExtraInfoLines-infoLabelWithMargin-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" > C our wares </div> @@ -118925,7 +118391,7 @@ exports[`Storyshots Views / Orders / Order details pending payment 1`] = ` > Tracking Number: <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" + class="MuiTypography-root-id ExtraInfoLines-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" > 01nn12399su12nndfsy </div> @@ -119520,27 +118986,23 @@ exports[`Storyshots Views / Orders / Order details pending payment 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Payment was authorized + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Payment was authorized - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -119551,27 +119013,23 @@ exports[`Storyshots Views / Orders / Order details pending payment 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Order refund information was sent to customer + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Order refund information was sent to customer - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -119582,27 +119040,23 @@ exports[`Storyshots Views / Orders / Order details pending payment 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Order cancel information was sent to customer + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Order cancel information was sent to customer - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -119613,27 +119067,23 @@ exports[`Storyshots Views / Orders / Order details pending payment 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Note from external service + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Note from external service - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -119714,12 +119164,12 @@ exports[`Storyshots Views / Orders / Order details pending payment 1`] = ` class="TimelineEvent-dot-id" /> <div - class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id TimelineEvent-expanded-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" + class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" > <div aria-disabled="false" aria-expanded="false" - class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id TimelineEvent-expanded-id" + class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id" role="button" tabindex="0" > @@ -119727,20 +119177,33 @@ exports[`Storyshots Views / Orders / Order details pending payment 1`] = ` class="MuiExpansionPanelSummary-content-id" > <div - class="MuiTypography-root-id MuiTypography-body1-id" + class="TimelineEventHeader-container-id" > - Order was refunded by admin@example.com - </div> - <div - class="MuiTypography-root-id TimelineEvent-dateExpander-id MuiTypography-body1-id" - > - <time - class="" - datetime="1537190544376" - title="Sep 17, 2018 9:22 AM" + <div + class="TimelineEventHeader-elementsContainer-id" > - in a month - </time> + <div + class="MuiTypography-root-id TimelineEventHeader-titleElement-id MuiTypography-body1-id" + > + Products were refunded by + </div> + <a + class="MuiTypography-root-id TimelineEventHeader-titleElement-id Link-root-id Link-primary-id MuiTypography-body1-id" + > + Jane Doe + </a> + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1537190544376" + title="Sep 17, 2018 9:22 AM" + > + in a month + </time> + </div> </div> </div> <div @@ -119843,11 +119306,6 @@ exports[`Storyshots Views / Orders / Order details pending payment 1`] = ` </tr> </tbody> </table> - <div - class="MuiTypography-root-id OrderHistory-eventSubtitle-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" - > - Refunded amount - </div> <div class="MuiTypography-root-id MuiTypography-body1-id" > @@ -119868,27 +119326,23 @@ exports[`Storyshots Views / Orders / Order details pending payment 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Fulfilled 1 items + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1537190544376" + title="Sep 17, 2018 9:22 AM" > - Fulfilled 1 items - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1537190544376" - title="Sep 17, 2018 9:22 AM" - > - in a month - </time> - </div> + in a month + </time> </div> </div> </div> @@ -120271,9 +119725,12 @@ exports[`Storyshots Views / Orders / Order details refunded payment 1`] = ` class="MuiTypography-root-id CardTitle-title-id MuiTypography-h5-id" > <div - class="StatusLabel-root-id undefined StatusLabel-errorDot-id" + class="StatusLabel-root-id undefined StatusLabel-alertDot-id" > - Unfulfilled (3) + Unfulfilled + <div + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" + /> </div> </span> <div @@ -120299,35 +119756,31 @@ exports[`Storyshots Views / Orders / Order details refunded payment 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderUnfulfilledItems-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -120338,10 +119791,10 @@ exports[`Storyshots Views / Orders / Order details refunded payment 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderUnfulfilledItems-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderUnfulfilledItems-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -120362,22 +119815,22 @@ exports[`Storyshots Views / Orders / Order details refunded payment 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 59-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 3 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $18.51 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $55.53 </td> @@ -120418,7 +119871,7 @@ exports[`Storyshots Views / Orders / Order details refunded payment 1`] = ` > Fulfilled (1) <div - class="MuiTypography-root-id OrderFulfillment-orderNumber-id MuiTypography-body1-id" + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" > #9-2 </div> @@ -120473,35 +119926,31 @@ exports[`Storyshots Views / Orders / Order details refunded payment 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderFulfillment-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -120512,10 +119961,10 @@ exports[`Storyshots Views / Orders / Order details refunded payment 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderFulfillment-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -120536,48 +119985,26 @@ exports[`Storyshots Views / Orders / Order details refunded payment 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 5-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 1 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $79.71 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $79.71 </td> </tr> - <tr - class="MuiTableRow-root-id" - > - <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-infoRow-id" - colspan="5" - > - <div - class="MuiTypography-root-id MuiTypography-body2-id MuiTypography-colorTextSecondary-id" - > - Fulfilled from: - <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" - > - Be stocked - </div> - </div> - <div - class="MuiTypography-root-id MuiTypography-body2-id MuiTypography-colorTextSecondary-id" - /> - </td> - </tr> </tbody> </table> </div> @@ -120614,7 +120041,7 @@ exports[`Storyshots Views / Orders / Order details refunded payment 1`] = ` > Fulfilled (1) <div - class="MuiTypography-root-id OrderFulfillment-orderNumber-id MuiTypography-body1-id" + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" > #9-1 </div> @@ -120669,35 +120096,31 @@ exports[`Storyshots Views / Orders / Order details refunded payment 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderFulfillment-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -120708,10 +120131,10 @@ exports[`Storyshots Views / Orders / Order details refunded payment 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderFulfillment-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -120732,31 +120155,33 @@ exports[`Storyshots Views / Orders / Order details refunded payment 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 5-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 1 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $79.71 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $79.71 </td> </tr> + </tbody> + <tbody> <tr class="MuiTableRow-root-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-infoRow-id" + class="MuiTableCell-root-id ExtraInfoLines-infoRow-id" colspan="5" > <div @@ -120764,7 +120189,7 @@ exports[`Storyshots Views / Orders / Order details refunded payment 1`] = ` > Fulfilled from: <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id OrderFulfillment-infoLabelWithMargin-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" + class="MuiTypography-root-id ExtraInfoLines-infoLabel-id ExtraInfoLines-infoLabelWithMargin-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" > C our wares </div> @@ -120774,7 +120199,7 @@ exports[`Storyshots Views / Orders / Order details refunded payment 1`] = ` > Tracking Number: <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" + class="MuiTypography-root-id ExtraInfoLines-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" > 01nn12399su12nndfsy </div> @@ -121369,27 +120794,23 @@ exports[`Storyshots Views / Orders / Order details refunded payment 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Payment was authorized + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Payment was authorized - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -121400,27 +120821,23 @@ exports[`Storyshots Views / Orders / Order details refunded payment 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Order refund information was sent to customer + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Order refund information was sent to customer - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -121431,27 +120848,23 @@ exports[`Storyshots Views / Orders / Order details refunded payment 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Order cancel information was sent to customer + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Order cancel information was sent to customer - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -121462,27 +120875,23 @@ exports[`Storyshots Views / Orders / Order details refunded payment 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Note from external service + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Note from external service - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -121563,12 +120972,12 @@ exports[`Storyshots Views / Orders / Order details refunded payment 1`] = ` class="TimelineEvent-dot-id" /> <div - class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id TimelineEvent-expanded-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" + class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" > <div aria-disabled="false" aria-expanded="false" - class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id TimelineEvent-expanded-id" + class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id" role="button" tabindex="0" > @@ -121576,20 +120985,33 @@ exports[`Storyshots Views / Orders / Order details refunded payment 1`] = ` class="MuiExpansionPanelSummary-content-id" > <div - class="MuiTypography-root-id MuiTypography-body1-id" + class="TimelineEventHeader-container-id" > - Order was refunded by admin@example.com - </div> - <div - class="MuiTypography-root-id TimelineEvent-dateExpander-id MuiTypography-body1-id" - > - <time - class="" - datetime="1537190544376" - title="Sep 17, 2018 9:22 AM" + <div + class="TimelineEventHeader-elementsContainer-id" > - in a month - </time> + <div + class="MuiTypography-root-id TimelineEventHeader-titleElement-id MuiTypography-body1-id" + > + Products were refunded by + </div> + <a + class="MuiTypography-root-id TimelineEventHeader-titleElement-id Link-root-id Link-primary-id MuiTypography-body1-id" + > + Jane Doe + </a> + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1537190544376" + title="Sep 17, 2018 9:22 AM" + > + in a month + </time> + </div> </div> </div> <div @@ -121692,11 +121114,6 @@ exports[`Storyshots Views / Orders / Order details refunded payment 1`] = ` </tr> </tbody> </table> - <div - class="MuiTypography-root-id OrderHistory-eventSubtitle-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" - > - Refunded amount - </div> <div class="MuiTypography-root-id MuiTypography-body1-id" > @@ -121717,27 +121134,23 @@ exports[`Storyshots Views / Orders / Order details refunded payment 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Fulfilled 1 items + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1537190544376" + title="Sep 17, 2018 9:22 AM" > - Fulfilled 1 items - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1537190544376" - title="Sep 17, 2018 9:22 AM" - > - in a month - </time> - </div> + in a month + </time> </div> </div> </div> @@ -122120,9 +121533,12 @@ exports[`Storyshots Views / Orders / Order details rejected payment 1`] = ` class="MuiTypography-root-id CardTitle-title-id MuiTypography-h5-id" > <div - class="StatusLabel-root-id undefined StatusLabel-errorDot-id" + class="StatusLabel-root-id undefined StatusLabel-alertDot-id" > - Unfulfilled (3) + Unfulfilled + <div + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" + /> </div> </span> <div @@ -122148,35 +121564,31 @@ exports[`Storyshots Views / Orders / Order details rejected payment 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderUnfulfilledItems-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -122187,10 +121599,10 @@ exports[`Storyshots Views / Orders / Order details rejected payment 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderUnfulfilledItems-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderUnfulfilledItems-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -122211,22 +121623,22 @@ exports[`Storyshots Views / Orders / Order details rejected payment 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 59-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 3 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $18.51 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $55.53 </td> @@ -122267,7 +121679,7 @@ exports[`Storyshots Views / Orders / Order details rejected payment 1`] = ` > Fulfilled (1) <div - class="MuiTypography-root-id OrderFulfillment-orderNumber-id MuiTypography-body1-id" + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" > #9-2 </div> @@ -122322,35 +121734,31 @@ exports[`Storyshots Views / Orders / Order details rejected payment 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderFulfillment-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -122361,10 +121769,10 @@ exports[`Storyshots Views / Orders / Order details rejected payment 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderFulfillment-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -122385,48 +121793,26 @@ exports[`Storyshots Views / Orders / Order details rejected payment 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 5-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 1 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $79.71 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $79.71 </td> </tr> - <tr - class="MuiTableRow-root-id" - > - <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-infoRow-id" - colspan="5" - > - <div - class="MuiTypography-root-id MuiTypography-body2-id MuiTypography-colorTextSecondary-id" - > - Fulfilled from: - <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" - > - Be stocked - </div> - </div> - <div - class="MuiTypography-root-id MuiTypography-body2-id MuiTypography-colorTextSecondary-id" - /> - </td> - </tr> </tbody> </table> </div> @@ -122463,7 +121849,7 @@ exports[`Storyshots Views / Orders / Order details rejected payment 1`] = ` > Fulfilled (1) <div - class="MuiTypography-root-id OrderFulfillment-orderNumber-id MuiTypography-body1-id" + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" > #9-1 </div> @@ -122518,35 +121904,31 @@ exports[`Storyshots Views / Orders / Order details rejected payment 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderFulfillment-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -122557,10 +121939,10 @@ exports[`Storyshots Views / Orders / Order details rejected payment 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderFulfillment-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -122581,31 +121963,33 @@ exports[`Storyshots Views / Orders / Order details rejected payment 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 5-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 1 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $79.71 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $79.71 </td> </tr> + </tbody> + <tbody> <tr class="MuiTableRow-root-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-infoRow-id" + class="MuiTableCell-root-id ExtraInfoLines-infoRow-id" colspan="5" > <div @@ -122613,7 +121997,7 @@ exports[`Storyshots Views / Orders / Order details rejected payment 1`] = ` > Fulfilled from: <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id OrderFulfillment-infoLabelWithMargin-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" + class="MuiTypography-root-id ExtraInfoLines-infoLabel-id ExtraInfoLines-infoLabelWithMargin-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" > C our wares </div> @@ -122623,7 +122007,7 @@ exports[`Storyshots Views / Orders / Order details rejected payment 1`] = ` > Tracking Number: <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" + class="MuiTypography-root-id ExtraInfoLines-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" > 01nn12399su12nndfsy </div> @@ -123218,27 +122602,23 @@ exports[`Storyshots Views / Orders / Order details rejected payment 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Payment was authorized + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Payment was authorized - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -123249,27 +122629,23 @@ exports[`Storyshots Views / Orders / Order details rejected payment 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Order refund information was sent to customer + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Order refund information was sent to customer - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -123280,27 +122656,23 @@ exports[`Storyshots Views / Orders / Order details rejected payment 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Order cancel information was sent to customer + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Order cancel information was sent to customer - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -123311,27 +122683,23 @@ exports[`Storyshots Views / Orders / Order details rejected payment 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Note from external service + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Note from external service - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -123412,12 +122780,12 @@ exports[`Storyshots Views / Orders / Order details rejected payment 1`] = ` class="TimelineEvent-dot-id" /> <div - class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id TimelineEvent-expanded-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" + class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" > <div aria-disabled="false" aria-expanded="false" - class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id TimelineEvent-expanded-id" + class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id" role="button" tabindex="0" > @@ -123425,20 +122793,33 @@ exports[`Storyshots Views / Orders / Order details rejected payment 1`] = ` class="MuiExpansionPanelSummary-content-id" > <div - class="MuiTypography-root-id MuiTypography-body1-id" + class="TimelineEventHeader-container-id" > - Order was refunded by admin@example.com - </div> - <div - class="MuiTypography-root-id TimelineEvent-dateExpander-id MuiTypography-body1-id" - > - <time - class="" - datetime="1537190544376" - title="Sep 17, 2018 9:22 AM" + <div + class="TimelineEventHeader-elementsContainer-id" > - in a month - </time> + <div + class="MuiTypography-root-id TimelineEventHeader-titleElement-id MuiTypography-body1-id" + > + Products were refunded by + </div> + <a + class="MuiTypography-root-id TimelineEventHeader-titleElement-id Link-root-id Link-primary-id MuiTypography-body1-id" + > + Jane Doe + </a> + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1537190544376" + title="Sep 17, 2018 9:22 AM" + > + in a month + </time> + </div> </div> </div> <div @@ -123541,11 +122922,6 @@ exports[`Storyshots Views / Orders / Order details rejected payment 1`] = ` </tr> </tbody> </table> - <div - class="MuiTypography-root-id OrderHistory-eventSubtitle-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" - > - Refunded amount - </div> <div class="MuiTypography-root-id MuiTypography-body1-id" > @@ -123566,27 +122942,23 @@ exports[`Storyshots Views / Orders / Order details rejected payment 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Fulfilled 1 items + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1537190544376" + title="Sep 17, 2018 9:22 AM" > - Fulfilled 1 items - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1537190544376" - title="Sep 17, 2018 9:22 AM" - > - in a month - </time> - </div> + in a month + </time> </div> </div> </div> @@ -123969,9 +123341,12 @@ exports[`Storyshots Views / Orders / Order details unfulfilled 1`] = ` class="MuiTypography-root-id CardTitle-title-id MuiTypography-h5-id" > <div - class="StatusLabel-root-id undefined StatusLabel-errorDot-id" + class="StatusLabel-root-id undefined StatusLabel-alertDot-id" > - Unfulfilled (3) + Unfulfilled + <div + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" + /> </div> </span> <div @@ -123997,35 +123372,31 @@ exports[`Storyshots Views / Orders / Order details unfulfilled 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderUnfulfilledItems-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderUnfulfilledItems-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -124036,10 +123407,10 @@ exports[`Storyshots Views / Orders / Order details unfulfilled 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderUnfulfilledItems-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderUnfulfilledItems-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -124060,22 +123431,22 @@ exports[`Storyshots Views / Orders / Order details unfulfilled 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 59-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 3 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $18.51 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderUnfulfilledItems-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $55.53 </td> @@ -124116,7 +123487,7 @@ exports[`Storyshots Views / Orders / Order details unfulfilled 1`] = ` > Fulfilled (1) <div - class="MuiTypography-root-id OrderFulfillment-orderNumber-id MuiTypography-body1-id" + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" > #9-2 </div> @@ -124171,35 +123542,31 @@ exports[`Storyshots Views / Orders / Order details unfulfilled 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderFulfillment-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -124210,10 +123577,10 @@ exports[`Storyshots Views / Orders / Order details unfulfilled 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderFulfillment-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -124234,48 +123601,26 @@ exports[`Storyshots Views / Orders / Order details unfulfilled 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 5-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 1 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $79.71 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $79.71 </td> </tr> - <tr - class="MuiTableRow-root-id" - > - <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-infoRow-id" - colspan="5" - > - <div - class="MuiTypography-root-id MuiTypography-body2-id MuiTypography-colorTextSecondary-id" - > - Fulfilled from: - <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" - > - Be stocked - </div> - </div> - <div - class="MuiTypography-root-id MuiTypography-body2-id MuiTypography-colorTextSecondary-id" - /> - </td> - </tr> </tbody> </table> </div> @@ -124312,7 +123657,7 @@ exports[`Storyshots Views / Orders / Order details unfulfilled 1`] = ` > Fulfilled (1) <div - class="MuiTypography-root-id OrderFulfillment-orderNumber-id MuiTypography-body1-id" + class="MuiTypography-root-id CardTitle-orderNumber-id MuiTypography-body1-id" > #9-1 </div> @@ -124367,35 +123712,31 @@ exports[`Storyshots Views / Orders / Order details unfulfilled 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" > <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colName-id" scope="col" > - <span - class="OrderFulfillment-colNameLabel-id" - > - Product - </span> + Product </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colSku-id" scope="col" > SKU </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colQuantity-id" scope="col" > Quantity </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colPrice-id" scope="col" > Price </th> <th - class="MuiTableCell-root-id MuiTableCell-head-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-head-id TableHeader-colTotal-id" scope="col" > Total @@ -124406,10 +123747,10 @@ exports[`Storyshots Views / Orders / Order details unfulfilled 1`] = ` class="MuiTableBody-root-id" > <tr - class="MuiTableRow-root-id OrderFulfillment-clickableRow-id MuiTableRow-hover-id" + class="MuiTableRow-root-id TableLine-clickableRow-id MuiTableRow-hover-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id OrderFulfillment-colName-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id TableLine-colName-id" > <div class="TableCellAvatar-content-id" @@ -124430,31 +123771,33 @@ exports[`Storyshots Views / Orders / Order details unfulfilled 1`] = ` </div> </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colSku-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colSku-id" > 5-1337 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colQuantity-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colQuantity-id" > 1 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colPrice-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colPrice-id" > $79.71 </td> <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-colTotal-id" + class="MuiTableCell-root-id MuiTableCell-body-id TableLine-colTotal-id" > $79.71 </td> </tr> + </tbody> + <tbody> <tr class="MuiTableRow-root-id" > <td - class="MuiTableCell-root-id MuiTableCell-body-id OrderFulfillment-infoRow-id" + class="MuiTableCell-root-id ExtraInfoLines-infoRow-id" colspan="5" > <div @@ -124462,7 +123805,7 @@ exports[`Storyshots Views / Orders / Order details unfulfilled 1`] = ` > Fulfilled from: <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id OrderFulfillment-infoLabelWithMargin-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" + class="MuiTypography-root-id ExtraInfoLines-infoLabel-id ExtraInfoLines-infoLabelWithMargin-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" > C our wares </div> @@ -124472,7 +123815,7 @@ exports[`Storyshots Views / Orders / Order details unfulfilled 1`] = ` > Tracking Number: <div - class="MuiTypography-root-id OrderFulfillment-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" + class="MuiTypography-root-id ExtraInfoLines-infoLabel-id MuiTypography-body2-id MuiTypography-colorTextPrimary-id" > 01nn12399su12nndfsy </div> @@ -125067,27 +124410,23 @@ exports[`Storyshots Views / Orders / Order details unfulfilled 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Payment was authorized + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Payment was authorized - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -125098,27 +124437,23 @@ exports[`Storyshots Views / Orders / Order details unfulfilled 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Order refund information was sent to customer + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Order refund information was sent to customer - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -125129,27 +124464,23 @@ exports[`Storyshots Views / Orders / Order details unfulfilled 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Order cancel information was sent to customer + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Order cancel information was sent to customer - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -125160,27 +124491,23 @@ exports[`Storyshots Views / Orders / Order details unfulfilled 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Note from external service + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1568726544376" + title="Sep 17, 2019 9:22 AM" > - Note from external service - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1568726544376" - title="Sep 17, 2019 9:22 AM" - > - in a year - </time> - </div> + in a year + </time> </div> </div> </div> @@ -125261,12 +124588,12 @@ exports[`Storyshots Views / Orders / Order details unfulfilled 1`] = ` class="TimelineEvent-dot-id" /> <div - class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id TimelineEvent-expanded-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" + class="MuiPaper-root-id MuiPaper-elevation0-id MuiExpansionPanel-root-id TimelineEvent-panel-id MuiExpansionPanel-rounded-id MuiPaper-rounded-id" > <div aria-disabled="false" aria-expanded="false" - class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id TimelineEvent-expanded-id" + class="MuiButtonBase-root-id MuiExpansionPanelSummary-root-id TimelineEvent-panelExpander-id" role="button" tabindex="0" > @@ -125274,20 +124601,33 @@ exports[`Storyshots Views / Orders / Order details unfulfilled 1`] = ` class="MuiExpansionPanelSummary-content-id" > <div - class="MuiTypography-root-id MuiTypography-body1-id" + class="TimelineEventHeader-container-id" > - Order was refunded by admin@example.com - </div> - <div - class="MuiTypography-root-id TimelineEvent-dateExpander-id MuiTypography-body1-id" - > - <time - class="" - datetime="1537190544376" - title="Sep 17, 2018 9:22 AM" + <div + class="TimelineEventHeader-elementsContainer-id" > - in a month - </time> + <div + class="MuiTypography-root-id TimelineEventHeader-titleElement-id MuiTypography-body1-id" + > + Products were refunded by + </div> + <a + class="MuiTypography-root-id TimelineEventHeader-titleElement-id Link-root-id Link-primary-id MuiTypography-body1-id" + > + Jane Doe + </a> + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1537190544376" + title="Sep 17, 2018 9:22 AM" + > + in a month + </time> + </div> </div> </div> <div @@ -125390,11 +124730,6 @@ exports[`Storyshots Views / Orders / Order details unfulfilled 1`] = ` </tr> </tbody> </table> - <div - class="MuiTypography-root-id OrderHistory-eventSubtitle-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" - > - Refunded amount - </div> <div class="MuiTypography-root-id MuiTypography-body1-id" > @@ -125415,27 +124750,23 @@ exports[`Storyshots Views / Orders / Order details unfulfilled 1`] = ` class="TimelineEvent-dot-id" /> <div - class="TimelineEvent-container-id" + class="TimelineEventHeader-container-id" > <div - class="TimelineEvent-noExpander-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - <div - class="MuiTypography-root-id MuiTypography-body1-id" + Fulfilled 1 items + </div> + <div + class="MuiTypography-root-id TimelineEventHeader-date-id MuiTypography-body1-id" + > + <time + class="" + datetime="1537190544376" + title="Sep 17, 2018 9:22 AM" > - Fulfilled 1 items - </div> - <div - class="MuiTypography-root-id TimelineEvent-date-id MuiTypography-body1-id" - > - <time - class="" - datetime="1537190544376" - title="Sep 17, 2018 9:22 AM" - > - in a month - </time> - </div> + in a month + </time> </div> </div> </div> @@ -130540,59 +129871,12 @@ exports[`Storyshots Views / Orders / Refund order loading 1`] = ` Refund shipment costs </span> </label> - <table - class="OrderRefundAmountValues-root-id" - > - <tbody> - <tr> - <td> - Authorized Amount - </td> - <td - class="OrderRefundAmountValues-textRight-id" - > - <span - class="Skeleton-skeleton-id" - > - ‌ - </span> - </td> - </tr> - <tr> - <td> - Previously refunded - </td> - <td - class="OrderRefundAmountValues-textRight-id" - > - <span - class="Skeleton-skeleton-id" - > - ‌ - </span> - </td> - </tr> - <tr - class="OrderRefundAmountValues-highlightedRow-id" - > - <td> - Max Refund - </td> - <td - class="OrderRefundAmountValues-textRight-id" - > - <span - class="Skeleton-skeleton-id" - > - ‌ - </span> - </td> - </tr> - </tbody> - </table> <div class="CardSpacer-spacer-id" /> + <div + class="OrderRefundAmountValues-container-id" + /> <hr class="Hr-root-id OrderRefundAmount-hr-id" /> @@ -130654,7 +129938,7 @@ exports[`Storyshots Views / Orders / Refund order loading 1`] = ` data-test="submit" disabled="" tabindex="-1" - type="submit" + type="button" > <span class="MuiButton-label-id" @@ -130665,7 +129949,7 @@ exports[`Storyshots Views / Orders / Refund order loading 1`] = ` <div class="MuiTypography-root-id OrderRefundAmount-refundCaution-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" > - Refunded items can’t be fulfilled + Refunded items can't be fulfilled </div> </div> </div> @@ -130867,44 +130151,34 @@ exports[`Storyshots Views / Orders / Refund order miscellaneous 1`] = ` <div class="MuiCardContent-root-id" > - <table - class="OrderRefundAmountValues-root-id" + <div + class="OrderRefundAmountValues-container-id" > - <tbody> - <tr> - <td> - Authorized Amount - </td> - <td - class="OrderRefundAmountValues-textRight-id" - > - $744.38 - </td> - </tr> - <tr> - <td> - Previously refunded - </td> - <td - class="OrderRefundAmountValues-textRight-id" - > - -$100.00 - </td> - </tr> - <tr - class="OrderRefundAmountValues-highlightedRow-id" - > - <td> - Max Refund - </td> - <td - class="OrderRefundAmountValues-textRight-id" - > - $644.38 - </td> - </tr> - </tbody> - </table> + <div + class="OrderRefundAmountValues-row-id" + > + Authorized Amount + <div> + $744.38 + </div> + </div> + <div + class="OrderRefundAmountValues-row-id" + > + Previously refunded + <div> + -$100.00 + </div> + </div> + <div + class="OrderRefundAmountValues-row-id OrderRefundAmountValues-highlightedRow-id" + > + Max Refund + <div> + $644.38 + </div> + </div> + </div> <div class="MuiFormControl-root-id MuiTextField-root-id OrderRefundAmount-priceField-id MuiFormControl-fullWidth-id" > @@ -130955,7 +130229,7 @@ exports[`Storyshots Views / Orders / Refund order miscellaneous 1`] = ` data-test="submit" disabled="" tabindex="-1" - type="submit" + type="button" > <span class="MuiButton-label-id" @@ -130966,7 +130240,7 @@ exports[`Storyshots Views / Orders / Refund order miscellaneous 1`] = ` <div class="MuiTypography-root-id OrderRefundAmount-refundCaution-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" > - Refunded items can’t be fulfilled + Refunded items can't be fulfilled </div> </div> </div> @@ -131985,69 +131259,53 @@ exports[`Storyshots Views / Orders / Refund order products 1`] = ` Refund shipment costs </span> </label> - <table - class="OrderRefundAmountValues-root-id" - > - <tbody> - <tr> - <td> - Authorized Amount - </td> - <td - class="OrderRefundAmountValues-textRight-id" - > - $744.38 - </td> - </tr> - <tr> - <td> - Selected products value - </td> - <td - class="OrderRefundAmountValues-textRight-id" - > - $0.00 - </td> - </tr> - <tr> - <td> - Previously refunded - </td> - <td - class="OrderRefundAmountValues-textRight-id" - > - -$100.00 - </td> - </tr> - <tr - class="OrderRefundAmountValues-highlightedRow-id" - > - <td> - Max Refund - </td> - <td - class="OrderRefundAmountValues-textRight-id" - > - $644.38 - </td> - </tr> - <tr - class="OrderRefundAmountValues-highlightedRow-id" - > - <td> - Refund total amount - </td> - <td - class="OrderRefundAmountValues-textRight-id" - > - $0.00 - </td> - </tr> - </tbody> - </table> <div class="CardSpacer-spacer-id" /> + <div + class="OrderRefundAmountValues-container-id" + > + <div + class="OrderRefundAmountValues-row-id" + > + Authorized Amount + <div> + $744.38 + </div> + </div> + <div + class="OrderRefundAmountValues-row-id" + > + Selected Products Value + <div> + $0.00 + </div> + </div> + <div + class="OrderRefundAmountValues-row-id" + > + Previously refunded + <div> + -$100.00 + </div> + </div> + <div + class="OrderRefundAmountValues-row-id OrderRefundAmountValues-highlightedRow-id" + > + Max Refund + <div> + $644.38 + </div> + </div> + <div + class="OrderRefundAmountValues-row-id OrderRefundAmountValues-highlightedRow-id" + > + Refund total amount + <div> + $0.00 + </div> + </div> + </div> <hr class="Hr-root-id OrderRefundAmount-hr-id" /> @@ -132107,7 +131365,7 @@ exports[`Storyshots Views / Orders / Refund order products 1`] = ` data-test="submit" disabled="" tabindex="-1" - type="submit" + type="button" > <span class="MuiButton-label-id" @@ -132118,7 +131376,7 @@ exports[`Storyshots Views / Orders / Refund order products 1`] = ` <div class="MuiTypography-root-id OrderRefundAmount-refundCaution-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" > - Refunded items can’t be fulfilled + Refunded items can't be fulfilled </div> </div> </div> diff --git a/src/storybook/stories/orders/OrderDetailsPage.tsx b/src/storybook/stories/orders/OrderDetailsPage.tsx index f8613132f..5cff7a2a0 100644 --- a/src/storybook/stories/orders/OrderDetailsPage.tsx +++ b/src/storybook/stories/orders/OrderDetailsPage.tsx @@ -30,6 +30,7 @@ const props: Omit<OrderDetailsPageProps, "classes"> = { onNoteAdd: undefined, onOrderCancel: undefined, onOrderFulfill: undefined, + onOrderReturn: () => undefined, onPaymentCapture: undefined, onPaymentPaid: undefined, onPaymentRefund: undefined, diff --git a/src/types/globalTypes.ts b/src/types/globalTypes.ts index e968dd06c..ca344b83b 100644 --- a/src/types/globalTypes.ts +++ b/src/types/globalTypes.ts @@ -451,6 +451,9 @@ export enum FulfillmentStatus { CANCELED = "CANCELED", FULFILLED = "FULFILLED", REFUNDED = "REFUNDED", + REFUNDED_AND_RETURNED = "REFUNDED_AND_RETURNED", + REPLACED = "REPLACED", + RETURNED = "RETURNED", } export enum InvoiceErrorCode { @@ -564,7 +567,6 @@ export enum OrderErrorCode { CANNOT_CANCEL_ORDER = "CANNOT_CANCEL_ORDER", CANNOT_DELETE = "CANNOT_DELETE", CANNOT_REFUND = "CANNOT_REFUND", - CANNOT_REFUND_FULFILLMENT_LINE = "CANNOT_REFUND_FULFILLMENT_LINE", CAPTURE_INACTIVE_PAYMENT = "CAPTURE_INACTIVE_PAYMENT", CHANNEL_INACTIVE = "CHANNEL_INACTIVE", DUPLICATED_INPUT_ITEM = "DUPLICATED_INPUT_ITEM", @@ -572,7 +574,7 @@ export enum OrderErrorCode { GRAPHQL_ERROR = "GRAPHQL_ERROR", INSUFFICIENT_STOCK = "INSUFFICIENT_STOCK", INVALID = "INVALID", - INVALID_REFUND_QUANTITY = "INVALID_REFUND_QUANTITY", + INVALID_QUANTITY = "INVALID_QUANTITY", NOT_AVAILABLE_IN_CHANNEL = "NOT_AVAILABLE_IN_CHANNEL", NOT_EDITABLE = "NOT_EDITABLE", NOT_FOUND = "NOT_FOUND", @@ -607,13 +609,16 @@ export enum OrderEventsEnum { CONFIRMED = "CONFIRMED", DRAFT_ADDED_PRODUCTS = "DRAFT_ADDED_PRODUCTS", DRAFT_CREATED = "DRAFT_CREATED", + DRAFT_CREATED_FROM_REPLACE = "DRAFT_CREATED_FROM_REPLACE", DRAFT_REMOVED_PRODUCTS = "DRAFT_REMOVED_PRODUCTS", EMAIL_SENT = "EMAIL_SENT", EXTERNAL_SERVICE_NOTIFICATION = "EXTERNAL_SERVICE_NOTIFICATION", FULFILLMENT_CANCELED = "FULFILLMENT_CANCELED", FULFILLMENT_FULFILLED_ITEMS = "FULFILLMENT_FULFILLED_ITEMS", FULFILLMENT_REFUNDED = "FULFILLMENT_REFUNDED", + FULFILLMENT_REPLACED = "FULFILLMENT_REPLACED", FULFILLMENT_RESTOCKED_ITEMS = "FULFILLMENT_RESTOCKED_ITEMS", + FULFILLMENT_RETURNED = "FULFILLMENT_RETURNED", INVOICE_GENERATED = "INVOICE_GENERATED", INVOICE_REQUESTED = "INVOICE_REQUESTED", INVOICE_SENT = "INVOICE_SENT", @@ -621,6 +626,7 @@ export enum OrderEventsEnum { NOTE_ADDED = "NOTE_ADDED", ORDER_FULLY_PAID = "ORDER_FULLY_PAID", ORDER_MARKED_AS_PAID = "ORDER_MARKED_AS_PAID", + ORDER_REPLACEMENT_CREATED = "ORDER_REPLACEMENT_CREATED", OTHER = "OTHER", OVERSOLD_ITEMS = "OVERSOLD_ITEMS", PAYMENT_AUTHORIZED = "PAYMENT_AUTHORIZED", @@ -651,6 +657,8 @@ export enum OrderStatus { DRAFT = "DRAFT", FULFILLED = "FULFILLED", PARTIALLY_FULFILLED = "PARTIALLY_FULFILLED", + PARTIALLY_RETURNED = "PARTIALLY_RETURNED", + RETURNED = "RETURNED", UNCONFIRMED = "UNCONFIRMED", UNFULFILLED = "UNFULFILLED", } @@ -1377,6 +1385,26 @@ export interface OrderRefundProductsInput { includeShippingCosts?: boolean | null; } +export interface OrderReturnFulfillmentLineInput { + fulfillmentLineId: string; + quantity: number; + replace?: boolean | null; +} + +export interface OrderReturnLineInput { + orderLineId: string; + quantity: number; + replace?: boolean | null; +} + +export interface OrderReturnProductsInput { + orderLines?: OrderReturnLineInput[] | null; + fulfillmentLines?: OrderReturnFulfillmentLineInput[] | null; + amountToRefund?: any | null; + includeShippingCosts?: boolean | null; + refund?: boolean | null; +} + export interface OrderSettingsUpdateInput { automaticallyConfirmAllNewOrders: boolean; } From 11bddd3d1b6eb6ba33b31b704bc106341d732bb5 Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk <tarasiukdawid@gmail.com> Date: Wed, 20 Jan 2021 17:37:36 +0100 Subject: [PATCH 10/35] Product reference attributes (#948) * Update changelog with product reference attributes * 2068 - Add product reference type to attributes (#949) * 2069 - Add product reference attribute search implementation (#951) --- CHANGELOG.md | 1 + locale/defaultMessages.json | 4 + schema.graphql | 25 ++--- .../AttributeDetails/AttributeDetails.tsx | 8 ++ src/attributes/utils/data.ts | 95 ++++++++++++++++++- src/attributes/utils/handlers.ts | 53 ++++++++++- .../AssignAttributeValueDialog.tsx | 8 +- src/components/Attributes/Attributes.tsx | 17 ++-- src/components/Attributes/fixtures.ts | 21 ++-- src/fragments/pages.ts | 2 + src/fragments/products.ts | 2 + src/fragments/types/MetadataFragment.ts | 2 +- src/fragments/types/PageAttributesFragment.ts | 4 +- src/fragments/types/PageDetailsFragment.ts | 4 +- src/fragments/types/Product.ts | 3 +- src/fragments/types/ProductVariant.ts | 4 +- .../types/ProductVariantAttributesFragment.ts | 3 +- .../types/SelectedVariantAttributeFragment.ts | 3 +- .../types/VariantAttributeFragment.ts | 3 +- .../PageDetailsPage/PageDetailsPage.tsx | 34 +++++-- src/pages/components/PageDetailsPage/form.tsx | 52 +++++++--- src/pages/fixtures.ts | 4 + src/pages/types/PageCreate.ts | 4 +- src/pages/types/PageDetails.ts | 4 +- src/pages/types/PageUpdate.ts | 4 +- src/pages/utils/data.ts | 18 +--- src/pages/views/PageCreate.tsx | 24 ++++- src/pages/views/PageDetails.tsx | 22 ++++- src/productTypes/fixtures.ts | 12 +++ .../ProductCreatePage/ProductCreatePage.tsx | 34 +++++-- .../components/ProductCreatePage/form.tsx | 32 ++++++- .../ProductUpdatePage.test.tsx | 1 + .../ProductUpdatePage/ProductUpdatePage.tsx | 34 +++++-- .../components/ProductUpdatePage/form.tsx | 32 ++++++- .../ProductVariantCreatePage.tsx | 34 +++++-- .../ProductVariantCreatePage/form.tsx | 32 ++++++- .../ProductVariantPage/ProductVariantPage.tsx | 34 +++++-- .../components/ProductVariantPage/form.tsx | 32 ++++++- src/products/fixtures.ts | 7 ++ .../types/CreateMultipleVariantsData.ts | 3 +- .../types/ProductChannelListingUpdate.ts | 3 +- src/products/types/ProductCreate.ts | 3 +- src/products/types/ProductDetails.ts | 3 +- src/products/types/ProductImageCreate.ts | 3 +- src/products/types/ProductImageUpdate.ts | 3 +- src/products/types/ProductUpdate.ts | 3 +- .../ProductVariantChannelListingUpdate.ts | 4 +- .../types/ProductVariantCreateData.ts | 4 +- src/products/types/ProductVariantDetails.ts | 4 +- src/products/types/ProductVariantReorder.ts | 3 +- .../types/ProductVariantSetDefault.ts | 3 +- src/products/types/SimpleProductUpdate.ts | 11 ++- src/products/types/VariantCreate.ts | 4 +- src/products/types/VariantImageAssign.ts | 4 +- src/products/types/VariantImageUnassign.ts | 4 +- src/products/types/VariantUpdate.ts | 6 +- src/products/utils/data.ts | 4 + .../views/ProductCreate/ProductCreate.tsx | 26 ++++- .../views/ProductUpdate/ProductUpdate.tsx | 24 ++++- src/products/views/ProductVariant.tsx | 20 +++- src/products/views/ProductVariantCreate.tsx | 20 +++- src/searches/types/SearchPageTypes.ts | 3 +- src/searches/types/SearchProductTypes.ts | 3 +- src/searches/usePageTypeSearch.ts | 1 + src/searches/useProductTypeSearch.ts | 1 + .../stories/pages/PageDetailsPage.tsx | 1 + .../stories/products/ProductCreatePage.tsx | 3 + .../stories/products/ProductUpdatePage.tsx | 1 + .../products/ProductVariantCreatePage.tsx | 5 + .../stories/products/ProductVariantPage.tsx | 4 + src/types/globalTypes.ts | 2 + src/utils/maps.ts | 10 ++ src/utils/metadata/types/UpdateMetadata.ts | 2 +- .../metadata/types/UpdatePrivateMetadata.ts | 2 +- 74 files changed, 740 insertions(+), 172 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe0127347..404849087 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ All notable, unreleased changes to this project will be documented in this file. - Add shipping delivery days - #914 by @orzechdev - Guard against non-staff users logging in - #947 by @jwm0 - Add reference attributes - #917 by @orzechdev +- Add product reference attributes - #948 by @orzechdev # 2.11.1 diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index a328c8454..c876de88e 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -813,6 +813,10 @@ "context": "page attribute entity type", "string": "Pages" }, + "src_dot_attributes_dot_components_dot_AttributeDetails_dot_product": { + "context": "product attribute entity type", + "string": "Products" + }, "src_dot_attributes_dot_components_dot_AttributeDetails_dot_references": { "context": "references attribute type", "string": "References" diff --git a/schema.graphql b/schema.graphql index e3f8eb49f..1ef686df7 100644 --- a/schema.graphql +++ b/schema.graphql @@ -454,6 +454,7 @@ type AttributeDelete { enum AttributeEntityTypeEnum { PAGE + PRODUCT } type AttributeError { @@ -570,7 +571,6 @@ type AttributeValue implements Node { id: ID! name: String slug: String - type: AttributeValueType @deprecated(reason: "Use the `inputType` field to determine the type of attribute's value. This field will be removed after 2020-07-31.") translation(languageCode: LanguageCodeEnum!): AttributeValueTranslation inputType: AttributeInputTypeEnum reference: ID @@ -628,13 +628,6 @@ type AttributeValueTranslation implements Node { language: LanguageDisplay! } -enum AttributeValueType { - COLOR - GRADIENT - URL - STRING -} - type AttributeValueUpdate { errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") attribute: Attribute @@ -2319,10 +2312,12 @@ type Margin { stop: Int } -type Menu implements Node { +type Menu implements Node & ObjectWithMetadata { id: ID! name: String! slug: String! + privateMetadata: [MetadataItem]! + metadata: [MetadataItem]! items: [MenuItem] } @@ -2389,7 +2384,7 @@ input MenuInput { slug: String } -type MenuItem implements Node { +type MenuItem implements Node & ObjectWithMetadata { id: ID! name: String! menu: Menu! @@ -2398,6 +2393,8 @@ type MenuItem implements Node { collection: Collection page: Page level: Int! + privateMetadata: [MetadataItem]! + metadata: [MetadataItem]! children: [MenuItem] url: String translation(languageCode: LanguageCodeEnum!): MenuItemTranslation @@ -2841,6 +2838,7 @@ type Order implements Node & ObjectWithMetadata { shippingMethodName: String channel: Channel! shippingPrice: TaxedMoney + shippingTaxRate: Float! token: String! voucher: Voucher giftCards: [GiftCard] @@ -2937,6 +2935,7 @@ input OrderDraftFilterInput { customer: String created: DateRangeInput search: String + channels: [ID] } type OrderError { @@ -4241,12 +4240,8 @@ type ProductVariant implements Node & ObjectWithMetadata { weight: Weight privateMetadata: [MetadataItem]! metadata: [MetadataItem]! - quantity: Int! @deprecated(reason: "Use the stock field instead. This field will be removed after 2020-07-31.") - quantityAllocated: Int @deprecated(reason: "Use the stock field instead. This field will be removed after 2020-07-31.") - stockQuantity: Int! @deprecated(reason: "Use the quantityAvailable field instead. This field will be removed after 2020-07-31.") channelListings: [ProductVariantChannelListing!] pricing: VariantPricingInfo - isAvailable: Boolean @deprecated(reason: "Use the stock field instead. This field will be removed after 2020-07-31.") attributes(variantSelection: VariantAttributeScope): [SelectedAttribute!]! costPrice: Money margin: Int @@ -4484,7 +4479,7 @@ type Query { permissionGroup(id: ID!): Group me: User staffUsers(filter: StaffUserInput, sortBy: UserSortingInput, before: String, after: String, first: Int, last: Int): UserCountableConnection - user(id: ID!): User + user(id: ID, email: String): User _entities(representations: [_Any]): [_Entity] _service: _Service } diff --git a/src/attributes/components/AttributeDetails/AttributeDetails.tsx b/src/attributes/components/AttributeDetails/AttributeDetails.tsx index 1138547ff..83f204f0e 100644 --- a/src/attributes/components/AttributeDetails/AttributeDetails.tsx +++ b/src/attributes/components/AttributeDetails/AttributeDetails.tsx @@ -71,6 +71,10 @@ const entityTypeMessages = defineMessages({ page: { defaultMessage: "Pages", description: "page attribute entity type" + }, + product: { + defaultMessage: "Products", + description: "product attribute entity type" } }); @@ -122,6 +126,10 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = props => { { label: intl.formatMessage(entityTypeMessages.page), value: AttributeEntityTypeEnum.PAGE + }, + { + label: intl.formatMessage(entityTypeMessages.product), + value: AttributeEntityTypeEnum.PRODUCT } ]; diff --git a/src/attributes/utils/data.ts b/src/attributes/utils/data.ts index 718fe479c..8d53dd5ff 100644 --- a/src/attributes/utils/data.ts +++ b/src/attributes/utils/data.ts @@ -10,10 +10,13 @@ import { FormsetData } from "@saleor/hooks/useFormset"; import { PageDetails_page_attributes } from "@saleor/pages/types/PageDetails"; import { ProductDetails_product_attributes } from "@saleor/products/types/ProductDetails"; import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; +import { SearchProducts_search_edges_node } from "@saleor/searches/types/SearchProducts"; import { + AttributeEntityTypeEnum, AttributeInputTypeEnum, AttributeValueInput } from "@saleor/types/globalTypes"; +import { mapNodeToChoice, mapPagesToChoices } from "@saleor/utils/maps"; import { MutationFetchResult } from "react-apollo"; import { AttributePageFormData } from "../components/AttributePage"; @@ -25,6 +28,11 @@ export const ATTRIBUTE_TYPES_WITH_DEDICATED_VALUES = [ AttributeInputTypeEnum.MULTISELECT ]; +export interface AttributeReference { + label: string; + value: string; +} + function getSimpleAttributeData( data: AttributePageFormData, values: AttributeValueEditDialogFormData[] @@ -199,7 +207,7 @@ export const getFileAttributeDisplayData = ( return attribute; }; -export const getReferenceAttributeDisplayData = ( +export const getPageReferenceAttributeDisplayData = ( attribute: AttributeInput, referencePages: SearchPages_search_edges_node[] ) => ({ @@ -208,21 +216,67 @@ export const getReferenceAttributeDisplayData = ( ...attribute.data, references: referencePages?.length > 0 && attribute.value?.length > 0 - ? attribute.value.map(value => - referencePages.find(reference => reference.id === value) + ? mapPagesToChoices( + attribute.value.map(value => { + const reference = referencePages.find( + reference => reference.id === value + ); + return { ...reference }; + }) ) : [] } }); +export const getProductReferenceAttributeDisplayData = ( + attribute: AttributeInput, + referenceProducts: SearchProducts_search_edges_node[] +) => ({ + ...attribute, + data: { + ...attribute.data, + references: + referenceProducts?.length > 0 && attribute.value?.length > 0 + ? mapNodeToChoice( + attribute.value.map(value => { + const reference = referenceProducts.find( + reference => reference.id === value + ); + return { ...reference }; + }) + ) + : [] + } +}); + +export const getReferenceAttributeDisplayData = ( + attribute: AttributeInput, + referencePages: SearchPages_search_edges_node[], + referenceProducts: SearchProducts_search_edges_node[] +) => { + if (attribute.data.entityType === AttributeEntityTypeEnum.PAGE) { + return getPageReferenceAttributeDisplayData(attribute, referencePages); + } else if (attribute.data.entityType === AttributeEntityTypeEnum.PRODUCT) { + return getProductReferenceAttributeDisplayData( + attribute, + referenceProducts + ); + } +}; + export const getAttributesDisplayData = ( attributes: AttributeInput[], attributesWithNewFileValue: FormsetData<null, File>, - referencePages: SearchPages_search_edges_node[] + referencePages: SearchPages_search_edges_node[], + referenceProducts: SearchProducts_search_edges_node[] ) => attributes.map(attribute => { if (attribute.data.inputType === AttributeInputTypeEnum.REFERENCE) { - return getReferenceAttributeDisplayData(attribute, referencePages); + return getReferenceAttributeDisplayData( + attribute, + referencePages, + referenceProducts + ); } if (attribute.data.inputType === AttributeInputTypeEnum.FILE) { return getFileAttributeDisplayData(attribute, attributesWithNewFileValue); @@ -230,3 +284,34 @@ export const getAttributesDisplayData = ( return attribute; }); + +export const getSelectedReferencesFromAttribute = < + Node extends SearchPages_search_edges_node | SearchProducts_search_edges_node +>( + attribute?: AttributeInput, + references?: Node[] +) => + references?.filter( + value => + !attribute?.value?.some(selectedValue => selectedValue === value.id) + ) || []; + +export const getAttributeValuesFromReferences = ( + attributeId: string, + attributes?: AttributeInput[], + referencePages?: SearchPages_search_edges_node[], + referenceProducts?: SearchProducts_search_edges_node[] +) => { + const attribute = attributes?.find(attribute => attribute.id === attributeId); + + if (attribute?.data?.entityType === AttributeEntityTypeEnum.PAGE) { + return mapPagesToChoices( + getSelectedReferencesFromAttribute(attribute, referencePages) + ); + } else if (attribute?.data?.entityType === AttributeEntityTypeEnum.PRODUCT) { + return mapNodeToChoice( + getSelectedReferencesFromAttribute(attribute, referenceProducts) + ); + } + return []; +}; diff --git a/src/attributes/utils/handlers.ts b/src/attributes/utils/handlers.ts index afddebd11..28110b2db 100644 --- a/src/attributes/utils/handlers.ts +++ b/src/attributes/utils/handlers.ts @@ -12,8 +12,9 @@ import { FormsetData } from "@saleor/hooks/useFormset"; import { PageDetails_page_attributes } from "@saleor/pages/types/PageDetails"; -import { ReorderEvent } from "@saleor/types"; +import { FetchMoreProps, ReorderEvent } from "@saleor/types"; import { + AttributeEntityTypeEnum, AttributeInputTypeEnum, AttributeValueInput } from "@saleor/types/globalTypes"; @@ -67,6 +68,56 @@ export function createAttributeReferenceChangeHandler( }; } +export function createFetchReferencesHandler( + attributes: FormsetData<AttributeInputData, string[]>, + assignReferencesAttributeId: string, + fetchReferencePages?: (data: string) => void, + fetchReferenceProducts?: (data: string) => void +) { + return (value: string) => { + const attribute = attributes?.find( + attribute => attribute.id === assignReferencesAttributeId + ); + + if (!attribute) { + return; + } + + if ( + attribute.data.entityType === AttributeEntityTypeEnum.PAGE && + fetchReferencePages + ) { + fetchReferencePages(value); + } else if ( + attribute.data.entityType === AttributeEntityTypeEnum.PRODUCT && + fetchReferenceProducts + ) { + fetchReferenceProducts(value); + } + }; +} + +export function createFetchMoreReferencesHandler( + attributes: FormsetData<AttributeInputData, string[]>, + assignReferencesAttributeId: string, + fetchMoreReferencePages?: FetchMoreProps, + fetchMoreReferenceProducts?: FetchMoreProps +) { + const attribute = attributes?.find( + attribute => attribute.id === assignReferencesAttributeId + ); + + if (!attribute) { + return; + } + + if (attribute.data.entityType === AttributeEntityTypeEnum.PAGE) { + return fetchMoreReferencePages; + } else if (attribute.data.entityType === AttributeEntityTypeEnum.PRODUCT) { + return fetchMoreReferenceProducts; + } +} + export function createAttributeFileChangeHandler( changeAttributeData: FormsetChange<string[]>, attributesWithNewFileValue: FormsetData<FormsetData<null, File>>, diff --git a/src/components/AssignAttributeValueDialog/AssignAttributeValueDialog.tsx b/src/components/AssignAttributeValueDialog/AssignAttributeValueDialog.tsx index ff2711eeb..0a8d4523b 100644 --- a/src/components/AssignAttributeValueDialog/AssignAttributeValueDialog.tsx +++ b/src/components/AssignAttributeValueDialog/AssignAttributeValueDialog.tsx @@ -1,4 +1,4 @@ -import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; +import { AttributeReference } from "@saleor/attributes/utils/data"; import React from "react"; import { defineMessages, useIntl } from "react-intl"; @@ -26,7 +26,7 @@ interface AssignAttributeValueDialogProps AssignContainerDialogProps, "containers" | "title" | "search" | "confirmButtonState" > { - attributeValues: SearchPages_search_edges_node[]; + attributeValues: AttributeReference[]; } const AssignAttributeValueDialog: React.FC<AssignAttributeValueDialogProps> = ({ @@ -38,8 +38,8 @@ const AssignAttributeValueDialog: React.FC<AssignAttributeValueDialogProps> = ({ return ( <AssignContainerDialog containers={attributeValues.map(value => ({ - id: value.id, - name: value.title + id: value.value, + name: value.label }))} search={{ label: intl.formatMessage(messages.searchLabel), diff --git a/src/components/Attributes/Attributes.tsx b/src/components/Attributes/Attributes.tsx index 95de1fb5b..aada001ec 100644 --- a/src/components/Attributes/Attributes.tsx +++ b/src/components/Attributes/Attributes.tsx @@ -4,6 +4,7 @@ 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 { AttributeReference } from "@saleor/attributes/utils/data"; import CardTitle from "@saleor/components/CardTitle"; import Hr from "@saleor/components/Hr"; import MultiAutocompleteSelectField, { @@ -16,9 +17,11 @@ import { AttributeValueFragment } from "@saleor/fragments/types/AttributeValueFr import { PageErrorWithAttributesFragment } from "@saleor/fragments/types/PageErrorWithAttributesFragment"; import { ProductErrorWithAttributesFragment } from "@saleor/fragments/types/ProductErrorWithAttributesFragment"; import { FormsetAtomicData, FormsetChange } from "@saleor/hooks/useFormset"; -import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; import { ReorderEvent } from "@saleor/types"; -import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; +import { + AttributeEntityTypeEnum, + AttributeInputTypeEnum +} from "@saleor/types/globalTypes"; import { getProductErrorMessage } from "@saleor/utils/errors"; import getPageErrorMessage from "@saleor/utils/errors/page"; import classNames from "classnames"; @@ -40,11 +43,12 @@ import { VariantAttributeScope } from "./types"; export interface AttributeInputData { inputType: AttributeInputTypeEnum; + entityType?: AttributeEntityTypeEnum; variantAttributeScope?: VariantAttributeScope; isRequired: boolean; values: AttributeValueFragment[]; selectedValues?: AttributeValueFragment[]; - references?: SearchPages_search_edges_node[]; + references?: AttributeReference[]; } export type AttributeInput = FormsetAtomicData<AttributeInputData, string[]>; export type AttributeFileInput = FormsetAtomicData<AttributeInputData, File[]>; @@ -175,14 +179,11 @@ function getReferenceDisplayValue( } const definedAttributeReference = attribute.data.references?.find( - reference => reference.id === attributeValue + reference => reference.value === attributeValue ); // If value has not been yet assigned, use data of reference if (!!definedAttributeReference) { - return { - label: definedAttributeReference.title, - value: definedAttributeReference.id - }; + return definedAttributeReference; } return { diff --git a/src/components/Attributes/fixtures.ts b/src/components/Attributes/fixtures.ts index 205bdd74c..6fb778150 100644 --- a/src/components/Attributes/fixtures.ts +++ b/src/components/Attributes/fixtures.ts @@ -1,4 +1,7 @@ -import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; +import { + AttributeEntityTypeEnum, + AttributeInputTypeEnum +} from "@saleor/types/globalTypes"; import { AttributeInput } from "./Attributes"; @@ -92,23 +95,21 @@ const FILE_ATTRIBUTE: AttributeInput = { const REFERENCE_ATTRIBUTE: AttributeInput = { data: { + entityType: AttributeEntityTypeEnum.PAGE, inputType: AttributeInputTypeEnum.REFERENCE, isRequired: true, references: [ { - __typename: "Page", - id: "vbnhgcvjhbvhj", - title: "References First Value" + label: "References First Value", + value: "vbnhgcvjhbvhj" }, { - __typename: "Page", - id: "gucngdfdfvdvd", - title: "References Second Value" + label: "References Second Value", + value: "gucngdfdfvdvd" }, { - __typename: "Page", - id: "dfdfdsfdsfdse", - title: "References Third Value" + label: "References Third Value", + value: "dfdfdsfdsfdse" } ], values: [ diff --git a/src/fragments/pages.ts b/src/fragments/pages.ts index f757f0459..d91a1a43d 100644 --- a/src/fragments/pages.ts +++ b/src/fragments/pages.ts @@ -21,6 +21,7 @@ export const pageAttributesFragment = gql` slug name inputType + entityType valueRequired values { ...AttributeValueFragment @@ -37,6 +38,7 @@ export const pageAttributesFragment = gql` id name inputType + entityType valueRequired values { ...AttributeValueFragment diff --git a/src/fragments/products.ts b/src/fragments/products.ts index a1fce235f..9cec7123b 100644 --- a/src/fragments/products.ts +++ b/src/fragments/products.ts @@ -123,6 +123,7 @@ export const productVariantAttributesFragment = gql` slug name inputType + entityType valueRequired values { ...AttributeValueFragment @@ -231,6 +232,7 @@ export const variantAttributeFragment = gql` name slug inputType + entityType valueRequired values { ...AttributeValueFragment diff --git a/src/fragments/types/MetadataFragment.ts b/src/fragments/types/MetadataFragment.ts index 4c6128e0b..53ae1cb06 100644 --- a/src/fragments/types/MetadataFragment.ts +++ b/src/fragments/types/MetadataFragment.ts @@ -19,7 +19,7 @@ export interface MetadataFragment_privateMetadata { } export interface MetadataFragment { - __typename: "App" | "ShippingZone" | "ShippingMethod" | "Product" | "ProductType" | "Attribute" | "Category" | "ProductVariant" | "DigitalContent" | "Collection" | "Page" | "PageType" | "User" | "Checkout" | "Order" | "Fulfillment" | "Invoice"; + __typename: "App" | "ShippingZone" | "ShippingMethod" | "Product" | "ProductType" | "Attribute" | "Category" | "ProductVariant" | "DigitalContent" | "Collection" | "Page" | "PageType" | "MenuItem" | "Menu" | "User" | "Checkout" | "Order" | "Fulfillment" | "Invoice"; metadata: (MetadataFragment_metadata | null)[]; privateMetadata: (MetadataFragment_privateMetadata | null)[]; } diff --git a/src/fragments/types/PageAttributesFragment.ts b/src/fragments/types/PageAttributesFragment.ts index 693850779..62c2b6697 100644 --- a/src/fragments/types/PageAttributesFragment.ts +++ b/src/fragments/types/PageAttributesFragment.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL fragment: PageAttributesFragment @@ -29,6 +29,7 @@ export interface PageAttributesFragment_attributes_attribute { slug: string | null; name: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (PageAttributesFragment_attributes_attribute_values | null)[] | null; } @@ -74,6 +75,7 @@ export interface PageAttributesFragment_pageType_attributes { id: string; name: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (PageAttributesFragment_pageType_attributes_values | null)[] | null; } diff --git a/src/fragments/types/PageDetailsFragment.ts b/src/fragments/types/PageDetailsFragment.ts index d05db0d3a..2ba241f78 100644 --- a/src/fragments/types/PageDetailsFragment.ts +++ b/src/fragments/types/PageDetailsFragment.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL fragment: PageDetailsFragment @@ -29,6 +29,7 @@ export interface PageDetailsFragment_attributes_attribute { slug: string | null; name: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (PageDetailsFragment_attributes_attribute_values | null)[] | null; } @@ -74,6 +75,7 @@ export interface PageDetailsFragment_pageType_attributes { id: string; name: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (PageDetailsFragment_pageType_attributes_values | null)[] | null; } diff --git a/src/fragments/types/Product.ts b/src/fragments/types/Product.ts index c5b5195d7..f9ba2f490 100644 --- a/src/fragments/types/Product.ts +++ b/src/fragments/types/Product.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, AttributeEntityTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL fragment: Product @@ -29,6 +29,7 @@ export interface Product_attributes_attribute { slug: string | null; name: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (Product_attributes_attribute_values | null)[] | null; } diff --git a/src/fragments/types/ProductVariant.ts b/src/fragments/types/ProductVariant.ts index 78eded821..371bc43cc 100644 --- a/src/fragments/types/ProductVariant.ts +++ b/src/fragments/types/ProductVariant.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, AttributeEntityTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL fragment: ProductVariant @@ -41,6 +41,7 @@ export interface ProductVariant_selectionAttributes_attribute { name: string | null; slug: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (ProductVariant_selectionAttributes_attribute_values | null)[] | null; } @@ -87,6 +88,7 @@ export interface ProductVariant_nonSelectionAttributes_attribute { name: string | null; slug: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (ProductVariant_nonSelectionAttributes_attribute_values | null)[] | null; } diff --git a/src/fragments/types/ProductVariantAttributesFragment.ts b/src/fragments/types/ProductVariantAttributesFragment.ts index 71f35d484..f2dae3282 100644 --- a/src/fragments/types/ProductVariantAttributesFragment.ts +++ b/src/fragments/types/ProductVariantAttributesFragment.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL fragment: ProductVariantAttributesFragment @@ -29,6 +29,7 @@ export interface ProductVariantAttributesFragment_attributes_attribute { slug: string | null; name: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (ProductVariantAttributesFragment_attributes_attribute_values | null)[] | null; } diff --git a/src/fragments/types/SelectedVariantAttributeFragment.ts b/src/fragments/types/SelectedVariantAttributeFragment.ts index 9b5a69592..bc91e9574 100644 --- a/src/fragments/types/SelectedVariantAttributeFragment.ts +++ b/src/fragments/types/SelectedVariantAttributeFragment.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL fragment: SelectedVariantAttributeFragment @@ -29,6 +29,7 @@ export interface SelectedVariantAttributeFragment_attribute { name: string | null; slug: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (SelectedVariantAttributeFragment_attribute_values | null)[] | null; } diff --git a/src/fragments/types/VariantAttributeFragment.ts b/src/fragments/types/VariantAttributeFragment.ts index bfaeecc1b..9bbf7316e 100644 --- a/src/fragments/types/VariantAttributeFragment.ts +++ b/src/fragments/types/VariantAttributeFragment.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL fragment: VariantAttributeFragment @@ -29,6 +29,7 @@ export interface VariantAttributeFragment { name: string | null; slug: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (VariantAttributeFragment_values | null)[] | null; } diff --git a/src/pages/components/PageDetailsPage/PageDetailsPage.tsx b/src/pages/components/PageDetailsPage/PageDetailsPage.tsx index edfeb5841..e35e9a17f 100644 --- a/src/pages/components/PageDetailsPage/PageDetailsPage.tsx +++ b/src/pages/components/PageDetailsPage/PageDetailsPage.tsx @@ -1,4 +1,7 @@ -import { mergeAttributeValues } from "@saleor/attributes/utils/data"; +import { + getAttributeValuesFromReferences, + mergeAttributeValues +} from "@saleor/attributes/utils/data"; import AppHeader from "@saleor/components/AppHeader"; import AssignAttributeValueDialog from "@saleor/components/AssignAttributeValueDialog"; import Attributes, { AttributeInput } from "@saleor/components/Attributes"; @@ -15,9 +18,9 @@ import { PageErrorWithAttributesFragment } from "@saleor/fragments/types/PageErr import useDateLocalize from "@saleor/hooks/useDateLocalize"; import { SubmitPromise } from "@saleor/hooks/useForm"; import { sectionNames } from "@saleor/intl"; -import { getAttributeValuesFromReferences } from "@saleor/pages/utils/data"; import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; import { SearchPageTypes_search_edges_node } from "@saleor/searches/types/SearchPageTypes"; +import { SearchProducts_search_edges_node } from "@saleor/searches/types/SearchProducts"; import { FetchMoreProps } from "@saleor/types"; import React from "react"; import { useIntl } from "react-intl"; @@ -32,7 +35,8 @@ export interface PageDetailsPageProps { errors: PageErrorWithAttributesFragment[]; page: PageDetails_page; pageTypes?: SearchPageTypes_search_edges_node[]; - referencePages: SearchPages_search_edges_node[]; + referencePages?: SearchPages_search_edges_node[]; + referenceProducts?: SearchProducts_search_edges_node[]; allowEmptySlug?: boolean; saveButtonBarState: ConfirmButtonTransitionState; onBack: () => void; @@ -44,6 +48,8 @@ export interface PageDetailsPageProps { onAssignReferencesClick: (attribute: AttributeInput) => void; fetchReferencePages?: (data: string) => void; fetchMoreReferencePages?: FetchMoreProps; + fetchReferenceProducts?: (data: string) => void; + fetchMoreReferenceProducts?: FetchMoreProps; onCloseDialog: () => void; } @@ -52,7 +58,8 @@ const PageDetailsPage: React.FC<PageDetailsPageProps> = ({ errors, page, pageTypes, - referencePages, + referencePages = [], + referenceProducts = [], saveButtonBarState, onBack, onRemove, @@ -63,6 +70,8 @@ const PageDetailsPage: React.FC<PageDetailsPageProps> = ({ onAssignReferencesClick, fetchReferencePages, fetchMoreReferencePages, + fetchReferenceProducts, + fetchMoreReferenceProducts, onCloseDialog }) => { const intl = useIntl(); @@ -93,6 +102,12 @@ const PageDetailsPage: React.FC<PageDetailsPageProps> = ({ page={page} pageTypes={pageTypes} referencePages={referencePages} + referenceProducts={referenceProducts} + fetchReferencePages={fetchReferencePages} + fetchMoreReferencePages={fetchMoreReferencePages} + fetchReferenceProducts={fetchReferenceProducts} + fetchMoreReferenceProducts={fetchMoreReferenceProducts} + assignReferencesAttributeId={assignReferencesAttributeId} onSubmit={onSubmit} > {({ change, data, pageType, handlers, hasChanged, submit }) => ( @@ -208,13 +223,14 @@ const PageDetailsPage: React.FC<PageDetailsPageProps> = ({ attributeValues={getAttributeValuesFromReferences( assignReferencesAttributeId, data.attributes, - referencePages + referencePages, + referenceProducts )} - hasMore={fetchMoreReferencePages?.hasMore} + hasMore={handlers.fetchMoreReferences?.hasMore} open={canOpenAssignReferencesAttributeDialog} - onFetch={fetchReferencePages} - onFetchMore={fetchMoreReferencePages?.onFetchMore} - loading={fetchMoreReferencePages?.loading} + onFetch={handlers.fetchReferences} + onFetchMore={handlers.fetchMoreReferences?.onFetchMore} + loading={handlers.fetchMoreReferences?.loading} onClose={onCloseDialog} onSubmit={attributeValues => handleAssignReferenceAttribute(attributeValues, data, handlers) diff --git a/src/pages/components/PageDetailsPage/form.tsx b/src/pages/components/PageDetailsPage/form.tsx index cec3a130e..758c7d598 100644 --- a/src/pages/components/PageDetailsPage/form.tsx +++ b/src/pages/components/PageDetailsPage/form.tsx @@ -5,7 +5,9 @@ import { createAttributeFileChangeHandler, createAttributeMultiChangeHandler, createAttributeReferenceChangeHandler, - createAttributeValueReorderHandler + createAttributeValueReorderHandler, + createFetchMoreReferencesHandler, + createFetchReferencesHandler } from "@saleor/attributes/utils/handlers"; import { AttributeInput } from "@saleor/components/Attributes"; import { MetadataFormData } from "@saleor/components/Metadata"; @@ -24,7 +26,8 @@ import { import { getAttributeInputFromPage } from "@saleor/pages/utils/data"; import { createPageTypeSelectHandler } from "@saleor/pages/utils/handlers"; import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; -import { ReorderEvent } from "@saleor/types"; +import { SearchProducts_search_edges_node } from "@saleor/searches/types/SearchProducts"; +import { FetchMoreProps, ReorderEvent } from "@saleor/types"; import getPublicationData from "@saleor/utils/data/getPublicationData"; import handleFormSubmit from "@saleor/utils/handlers/handleFormSubmit"; import { mapMetadataItemToInput } from "@saleor/utils/maps"; @@ -62,6 +65,8 @@ export interface PageUpdateHandlers { selectAttributeReference: FormsetChange<string[]>; selectAttributeFile: FormsetChange<File>; reorderAttributeValue: FormsetChange<ReorderEvent>; + fetchReferences: (value: string) => void; + fetchMoreReferences: FetchMoreProps; } export interface UsePageUpdateFormResult { change: FormChange; @@ -72,19 +77,28 @@ export interface UsePageUpdateFormResult { submit: () => void; } -export interface PageFormProps { +export interface UsePageFormOpts { + pageTypes?: PageDetails_page_pageType[]; + referencePages: SearchPages_search_edges_node[]; + referenceProducts: SearchProducts_search_edges_node[]; + fetchReferencePages?: (data: string) => void; + fetchMoreReferencePages?: FetchMoreProps; + fetchReferenceProducts?: (data: string) => void; + fetchMoreReferenceProducts?: FetchMoreProps; + assignReferencesAttributeId?: string; +} + +export interface PageFormProps extends UsePageFormOpts { children: (props: UsePageUpdateFormResult) => React.ReactNode; page: PageDetails_page; pageTypes?: PageDetails_page_pageType[]; - referencePages: SearchPages_search_edges_node[]; onSubmit: (data: PageData) => SubmitPromise; } function usePageForm( page: PageDetails_page, onSubmit: (data: PageData) => SubmitPromise, - referencePages: SearchPages_search_edges_node[], - pageTypes?: PageDetails_page_pageType[] + opts: UsePageFormOpts ): UsePageUpdateFormResult { const [changed, setChanged] = React.useState(false); const triggerChange = () => setChanged(true); @@ -131,7 +145,7 @@ function usePageForm( handleChange, attributes.set, setPageType, - pageTypes + opts.pageTypes ); const handleAttributeChange = createAttributeChangeHandler( attributes.change, @@ -146,6 +160,18 @@ function usePageForm( attributes.change, triggerChange ); + const handleFetchReferences = createFetchReferencesHandler( + attributes.data, + opts.assignReferencesAttributeId, + opts.fetchReferencePages, + opts.fetchReferenceProducts + ); + const handleFetchMoreReferences = createFetchMoreReferencesHandler( + attributes.data, + opts.assignReferencesAttributeId, + opts.fetchMoreReferencePages, + opts.fetchMoreReferenceProducts + ); const handleAttributeFileChange = createAttributeFileChangeHandler( attributes.change, attributesWithNewFileValue.data, @@ -165,7 +191,8 @@ function usePageForm( attributes: getAttributesDisplayData( attributes.data, attributesWithNewFileValue.data, - referencePages + opts.referencePages, + opts.referenceProducts ), content: content.current }); @@ -198,6 +225,8 @@ function usePageForm( handlers: { changeContent, changeMetadata, + fetchMoreReferences: handleFetchMoreReferences, + fetchReferences: handleFetchReferences, reorderAttributeValue: handleAttributeValueReorder, selectAttribute: handleAttributeChange, selectAttributeFile: handleAttributeFileChange, @@ -214,11 +243,10 @@ function usePageForm( const PageForm: React.FC<PageFormProps> = ({ children, page, - referencePages, - pageTypes, - onSubmit + onSubmit, + ...rest }) => { - const props = usePageForm(page, onSubmit, referencePages, pageTypes); + const props = usePageForm(page, onSubmit, rest); return <form onSubmit={props.submit}>{children(props)}</form>; }; diff --git a/src/pages/fixtures.ts b/src/pages/fixtures.ts index 7e1938c15..0d1cd2e0b 100644 --- a/src/pages/fixtures.ts +++ b/src/pages/fixtures.ts @@ -45,6 +45,7 @@ export const page: PageDetails_page = { id: "QXR0cmlidXRlOjI3", slug: "author", name: "Author", + entityType: null, inputType: AttributeInputTypeEnum.DROPDOWN, valueRequired: false, values: [ @@ -92,6 +93,7 @@ export const page: PageDetails_page = { id: "QXR0cmlidXRlOjI5", slug: "tag", name: "Tag", + entityType: null, inputType: AttributeInputTypeEnum.MULTISELECT, valueRequired: false, values: [ @@ -161,6 +163,7 @@ export const page: PageDetails_page = { { id: "QXR0cmlidXRlOjI3", name: "Author", + entityType: null, inputType: AttributeInputTypeEnum.DROPDOWN, valueRequired: false, values: [ @@ -194,6 +197,7 @@ export const page: PageDetails_page = { { id: "QXR0cmlidXRlOjI5", name: "Tag", + entityType: null, inputType: AttributeInputTypeEnum.MULTISELECT, valueRequired: false, values: [ diff --git a/src/pages/types/PageCreate.ts b/src/pages/types/PageCreate.ts index bf67a2907..8499600b3 100644 --- a/src/pages/types/PageCreate.ts +++ b/src/pages/types/PageCreate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { PageCreateInput, PageErrorCode, AttributeInputTypeEnum } from "./../../types/globalTypes"; +import { PageCreateInput, PageErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: PageCreate @@ -37,6 +37,7 @@ export interface PageCreate_pageCreate_page_attributes_attribute { slug: string | null; name: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (PageCreate_pageCreate_page_attributes_attribute_values | null)[] | null; } @@ -82,6 +83,7 @@ export interface PageCreate_pageCreate_page_pageType_attributes { id: string; name: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (PageCreate_pageCreate_page_pageType_attributes_values | null)[] | null; } diff --git a/src/pages/types/PageDetails.ts b/src/pages/types/PageDetails.ts index a891bd408..3526d05ac 100644 --- a/src/pages/types/PageDetails.ts +++ b/src/pages/types/PageDetails.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL query operation: PageDetails @@ -29,6 +29,7 @@ export interface PageDetails_page_attributes_attribute { slug: string | null; name: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (PageDetails_page_attributes_attribute_values | null)[] | null; } @@ -74,6 +75,7 @@ export interface PageDetails_page_pageType_attributes { id: string; name: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (PageDetails_page_pageType_attributes_values | null)[] | null; } diff --git a/src/pages/types/PageUpdate.ts b/src/pages/types/PageUpdate.ts index 4d4cc1b26..8b78848f1 100644 --- a/src/pages/types/PageUpdate.ts +++ b/src/pages/types/PageUpdate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { PageInput, PageErrorCode, AttributeInputTypeEnum } from "./../../types/globalTypes"; +import { PageInput, PageErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: PageUpdate @@ -36,6 +36,7 @@ export interface PageUpdate_pageUpdate_page_attributes_attribute { slug: string | null; name: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (PageUpdate_pageUpdate_page_attributes_attribute_values | null)[] | null; } @@ -81,6 +82,7 @@ export interface PageUpdate_pageUpdate_page_pageType_attributes { id: string; name: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (PageUpdate_pageUpdate_page_pageType_attributes_values | null)[] | null; } diff --git a/src/pages/utils/data.ts b/src/pages/utils/data.ts index db9116f38..083383dfe 100644 --- a/src/pages/utils/data.ts +++ b/src/pages/utils/data.ts @@ -1,6 +1,5 @@ import { getSelectedAttributeValues } from "@saleor/attributes/utils/data"; import { AttributeInput } from "@saleor/components/Attributes"; -import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; import { PageDetails_page, @@ -12,6 +11,7 @@ export function getAttributeInputFromPage( ): AttributeInput[] { return page?.attributes.map(attribute => ({ data: { + entityType: attribute.attribute.entityType, inputType: attribute.attribute.inputType, isRequired: attribute.attribute.valueRequired, selectedValues: attribute.values, @@ -28,6 +28,7 @@ export function getAttributeInputFromPageType( ): AttributeInput[] { return pageType?.attributes.map(attribute => ({ data: { + entityType: attribute.entityType, inputType: attribute.inputType, isRequired: attribute.valueRequired, values: attribute.values @@ -37,18 +38,3 @@ export function getAttributeInputFromPageType( value: [] })); } - -export const getAttributeValuesFromReferences = ( - attributeId: string, - attributes: AttributeInput[], - referencePages: SearchPages_search_edges_node[] -) => { - const attribute = attributes?.find(attribute => attribute.id === attributeId); - - return ( - referencePages?.filter( - value => - !attribute?.value?.some(selectedValue => selectedValue === value.id) - ) || [] - ); -}; diff --git a/src/pages/views/PageCreate.tsx b/src/pages/views/PageCreate.tsx index a77a11edc..2ee83d15b 100644 --- a/src/pages/views/PageCreate.tsx +++ b/src/pages/views/PageCreate.tsx @@ -11,6 +11,7 @@ import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import usePageSearch from "@saleor/searches/usePageSearch"; import usePageTypeSearch from "@saleor/searches/usePageTypeSearch"; +import useProductSearch from "@saleor/searches/useProductSearch"; import createMetadataCreateHandler from "@saleor/utils/handlers/metadataCreateHandler"; import { useMetadataUpdate, @@ -58,6 +59,14 @@ export const PageCreate: React.FC<PageCreateProps> = ({ params }) => { variables: DEFAULT_INITIAL_SEARCH_DATA }); + const { + loadMore: loadMoreProducts, + search: searchProducts, + result: searchProductsOpts + } = useProductSearch({ + variables: DEFAULT_INITIAL_SEARCH_DATA + }); + const [uploadFile, uploadFileOpts] = useFileUploadMutation({}); const handlePageCreate = (data: PageCreateData) => { @@ -81,17 +90,23 @@ export const PageCreate: React.FC<PageCreateProps> = ({ params }) => { ); const fetchMorePageTypes = { - hasMore: searchPageTypesOpts.data?.search.pageInfo.hasNextPage, + hasMore: searchPageTypesOpts.data?.search?.pageInfo?.hasNextPage, loading: searchPageTypesOpts.loading, onFetchMore: loadMorePageTypes }; const fetchMoreReferencePages = { - hasMore: searchPagesOpts.data?.search.pageInfo.hasNextPage, + hasMore: searchPagesOpts.data?.search?.pageInfo?.hasNextPage, loading: searchPagesOpts.loading, onFetchMore: loadMorePages }; + const fetchMoreReferenceProducts = { + hasMore: searchProductsOpts.data?.search?.pageInfo?.hasNextPage, + loading: searchProductsOpts.loading, + onFetchMore: loadMoreProducts + }; + return ( <TypedPageCreate onCompleted={handlePageCreate}> {(pageCreate, pageCreateOpts) => { @@ -163,8 +178,13 @@ export const PageCreate: React.FC<PageCreateProps> = ({ params }) => { referencePages={searchPagesOpts.data?.search.edges.map( edge => edge.node )} + referenceProducts={searchProductsOpts.data?.search.edges.map( + edge => edge.node + )} fetchReferencePages={searchPages} fetchMoreReferencePages={fetchMoreReferencePages} + fetchReferenceProducts={searchProducts} + fetchMoreReferenceProducts={fetchMoreReferenceProducts} onCloseDialog={() => navigate(pageCreateUrl())} /> </> diff --git a/src/pages/views/PageDetails.tsx b/src/pages/views/PageDetails.tsx index 2b3174c64..687ddaf6f 100644 --- a/src/pages/views/PageDetails.tsx +++ b/src/pages/views/PageDetails.tsx @@ -22,6 +22,7 @@ import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import { commonMessages } from "@saleor/intl"; import usePageSearch from "@saleor/searches/usePageSearch"; +import useProductSearch from "@saleor/searches/useProductSearch"; import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler"; import { useMetadataUpdate, @@ -158,12 +159,26 @@ export const PageDetails: React.FC<PageDetailsProps> = ({ id, params }) => { variables: DEFAULT_INITIAL_SEARCH_DATA }); + const { + loadMore: loadMoreProducts, + search: searchProducts, + result: searchProductsOpts + } = useProductSearch({ + variables: DEFAULT_INITIAL_SEARCH_DATA + }); + const fetchMoreReferencePages = { - hasMore: searchPagesOpts.data?.search.pageInfo.hasNextPage, + hasMore: searchPagesOpts.data?.search?.pageInfo?.hasNextPage, loading: searchPagesOpts.loading, onFetchMore: loadMorePages }; + const fetchMoreReferenceProducts = { + hasMore: searchProductsOpts.data?.search?.pageInfo?.hasNextPage, + loading: searchProductsOpts.loading, + onFetchMore: loadMoreProducts + }; + return ( <> <WindowTitle title={maybe(() => pageDetails.data.page.title)} /> @@ -193,8 +208,13 @@ export const PageDetails: React.FC<PageDetailsProps> = ({ id, params }) => { referencePages={searchPagesOpts.data?.search.edges.map( edge => edge.node )} + referenceProducts={searchProductsOpts.data?.search.edges.map( + edge => edge.node + )} fetchReferencePages={searchPages} fetchMoreReferencePages={fetchMoreReferencePages} + fetchReferenceProducts={searchProducts} + fetchMoreReferenceProducts={fetchMoreReferenceProducts} onCloseDialog={() => navigate(pageUrl(id))} /> <ActionDialog diff --git a/src/productTypes/fixtures.ts b/src/productTypes/fixtures.ts index a115d1a56..b8d751655 100644 --- a/src/productTypes/fixtures.ts +++ b/src/productTypes/fixtures.ts @@ -15,6 +15,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ { node: { __typename: "Attribute" as "Attribute", + entityType: null, id: "UHJvZHVjdEF0dHJpYnV0ZTo5", inputType: AttributeInputTypeEnum.DROPDOWN, name: "Author", @@ -49,6 +50,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ { node: { __typename: "Attribute" as "Attribute", + entityType: null, id: "UHJvZHVjdEF0dHJpYnV0ZTo2", inputType: AttributeInputTypeEnum.DROPDOWN, name: "Box Size", @@ -105,6 +107,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ { node: { __typename: "Attribute" as "Attribute", + entityType: null, id: "UHJvZHVjdEF0dHJpYnV0ZToz", inputType: AttributeInputTypeEnum.DROPDOWN, name: "Brand", @@ -128,6 +131,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ { node: { __typename: "Attribute" as "Attribute", + entityType: null, id: "UHJvZHVjdEF0dHJpYnV0ZTo4", inputType: AttributeInputTypeEnum.DROPDOWN, name: "Candy Box Size", @@ -173,6 +177,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ { node: { __typename: "Attribute" as "Attribute", + entityType: null, id: "UHJvZHVjdEF0dHJpYnV0ZTo1", inputType: AttributeInputTypeEnum.DROPDOWN, name: "Coffee Genre", @@ -207,6 +212,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ { node: { __typename: "Attribute" as "Attribute", + entityType: null, id: "UHJvZHVjdEF0dHJpYnV0ZToy", inputType: AttributeInputTypeEnum.DROPDOWN, name: "Collar", @@ -252,6 +258,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ { node: { __typename: "Attribute" as "Attribute", + entityType: null, id: "UHJvZHVjdEF0dHJpYnV0ZTox", inputType: AttributeInputTypeEnum.DROPDOWN, name: "Color", @@ -286,6 +293,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ { node: { __typename: "Attribute" as "Attribute", + entityType: null, id: "UHJvZHVjdEF0dHJpYnV0ZToxMg==", inputType: AttributeInputTypeEnum.DROPDOWN, name: "Cover", @@ -364,6 +372,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ { node: { __typename: "Attribute" as "Attribute", + entityType: null, id: "UHJvZHVjdEF0dHJpYnV0ZTo3", inputType: AttributeInputTypeEnum.DROPDOWN, name: "Flavor", @@ -398,6 +407,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ { node: { __typename: "Attribute" as "Attribute", + entityType: null, id: "UHJvZHVjdEF0dHJpYnV0ZToxMQ==", inputType: AttributeInputTypeEnum.DROPDOWN, name: "Language", @@ -432,6 +442,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ { node: { __typename: "Attribute" as "Attribute", + entityType: null, id: "UHJvZHVjdEF0dHJpYnV0ZToxMA==", inputType: AttributeInputTypeEnum.DROPDOWN, name: "Publisher", @@ -466,6 +477,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ { node: { __typename: "Attribute" as "Attribute", + entityType: null, id: "UHJvZHVjdEF0dHJpYnV0ZTo0", inputType: AttributeInputTypeEnum.DROPDOWN, name: "Size", diff --git a/src/products/components/ProductCreatePage/ProductCreatePage.tsx b/src/products/components/ProductCreatePage/ProductCreatePage.tsx index 5a54d0892..32e57982d 100644 --- a/src/products/components/ProductCreatePage/ProductCreatePage.tsx +++ b/src/products/components/ProductCreatePage/ProductCreatePage.tsx @@ -1,4 +1,7 @@ -import { mergeAttributeValues } from "@saleor/attributes/utils/data"; +import { + getAttributeValuesFromReferences, + mergeAttributeValues +} from "@saleor/attributes/utils/data"; import { ChannelData } from "@saleor/channels/utils"; import AppHeader from "@saleor/components/AppHeader"; import AssignAttributeValueDialog from "@saleor/components/AssignAttributeValueDialog"; @@ -18,12 +21,12 @@ import { ProductErrorWithAttributesFragment } from "@saleor/fragments/types/Prod import { TaxTypeFragment } from "@saleor/fragments/types/TaxTypeFragment"; import useStateFromProps from "@saleor/hooks/useStateFromProps"; import { sectionNames } from "@saleor/intl"; -import { getAttributeValuesFromReferences } from "@saleor/pages/utils/data"; import ProductVariantPrice from "@saleor/products/components/ProductVariantPrice"; import { getChoices } from "@saleor/products/utils/data"; import { SearchCategories_search_edges_node } from "@saleor/searches/types/SearchCategories"; import { SearchCollections_search_edges_node } from "@saleor/searches/types/SearchCollections"; import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; +import { SearchProducts_search_edges_node } from "@saleor/searches/types/SearchProducts"; import { SearchProductTypes_search_edges_node } from "@saleor/searches/types/SearchProductTypes"; import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses"; import React from "react"; @@ -54,7 +57,8 @@ interface ProductCreatePageProps { fetchMoreProductTypes: FetchMoreProps; initial?: Partial<ProductCreateFormData>; productTypes?: SearchProductTypes_search_edges_node[]; - referencePages: SearchPages_search_edges_node[]; + referencePages?: SearchPages_search_edges_node[]; + referenceProducts?: SearchProducts_search_edges_node[]; header: string; saveButtonBarState: ConfirmButtonTransitionState; weightUnit: string; @@ -69,7 +73,9 @@ interface ProductCreatePageProps { assignReferencesAttributeId?: string; onAssignReferencesClick: (attribute: AttributeInput) => void; fetchReferencePages?: (data: string) => void; + fetchReferenceProducts?: (data: string) => void; fetchMoreReferencePages?: FetchMoreProps; + fetchMoreReferenceProducts?: FetchMoreProps; onCloseDialog: () => void; onBack?(); onSubmit?(data: ProductCreateData); @@ -91,7 +97,8 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({ header, initial, productTypes: productTypeChoiceList, - referencePages, + referencePages = [], + referenceProducts = [], saveButtonBarState, warehouses, taxTypes, @@ -106,6 +113,8 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({ onAssignReferencesClick, fetchReferencePages, fetchMoreReferencePages, + fetchReferenceProducts, + fetchMoreReferenceProducts, onCloseDialog }: ProductCreatePageProps) => { const intl = useIntl(); @@ -158,6 +167,7 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({ collections={collections} productTypes={productTypeChoiceList} referencePages={referencePages} + referenceProducts={referenceProducts} selectedCollections={selectedCollections} setSelectedCategory={setSelectedCategory} setSelectedCollections={setSelectedCollections} @@ -167,6 +177,11 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({ warehouses={warehouses} currentChannels={currentChannels} productTypeChoiceList={productTypeChoiceList} + fetchReferencePages={fetchReferencePages} + fetchMoreReferencePages={fetchMoreReferencePages} + fetchReferenceProducts={fetchReferenceProducts} + fetchMoreReferenceProducts={fetchMoreReferenceProducts} + assignReferencesAttributeId={assignReferencesAttributeId} > {({ change, @@ -327,13 +342,14 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({ attributeValues={getAttributeValuesFromReferences( assignReferencesAttributeId, data.attributes, - referencePages + referencePages, + referenceProducts )} - hasMore={fetchMoreReferencePages?.hasMore} + hasMore={handlers.fetchMoreReferences?.hasMore} open={canOpenAssignReferencesAttributeDialog} - onFetch={fetchReferencePages} - onFetchMore={fetchMoreReferencePages?.onFetchMore} - loading={fetchMoreReferencePages?.loading} + onFetch={handlers.fetchReferences} + onFetchMore={handlers.fetchMoreReferences?.onFetchMore} + loading={handlers.fetchMoreReferences?.loading} onClose={onCloseDialog} onSubmit={attributeValues => handleAssignReferenceAttribute( diff --git a/src/products/components/ProductCreatePage/form.tsx b/src/products/components/ProductCreatePage/form.tsx index 4e534659d..3cf000d12 100644 --- a/src/products/components/ProductCreatePage/form.tsx +++ b/src/products/components/ProductCreatePage/form.tsx @@ -5,7 +5,9 @@ import { createAttributeFileChangeHandler, createAttributeMultiChangeHandler, createAttributeReferenceChangeHandler, - createAttributeValueReorderHandler + createAttributeValueReorderHandler, + createFetchMoreReferencesHandler, + createFetchReferencesHandler } from "@saleor/attributes/utils/handlers"; import { ChannelData, ChannelPriceArgs } from "@saleor/channels/utils"; import { @@ -36,9 +38,10 @@ import { validatePrice } from "@saleor/products/utils/validation"; import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; +import { SearchProducts_search_edges_node } from "@saleor/searches/types/SearchProducts"; import { SearchProductTypes_search_edges_node } from "@saleor/searches/types/SearchProductTypes"; import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses"; -import { ReorderEvent } from "@saleor/types"; +import { FetchMoreProps, ReorderEvent } from "@saleor/types"; import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler"; import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger"; @@ -99,6 +102,8 @@ export interface ProductCreateHandlers Record<"reorderAttributeValue", FormsetChange<ReorderEvent>>, Record<"addStock" | "deleteStock", (id: string) => void> { changeDescription: RichTextEditorChange; + fetchReferences: (value: string) => void; + fetchMoreReferences: FetchMoreProps; } export interface UseProductCreateFormResult { change: FormChange; @@ -126,6 +131,12 @@ export interface UseProductCreateFormOpts currentChannels: ChannelData[]; productTypeChoiceList: SearchProductTypes_search_edges_node[]; referencePages: SearchPages_search_edges_node[]; + referenceProducts: SearchProducts_search_edges_node[]; + fetchReferencePages?: (data: string) => void; + fetchMoreReferencePages?: FetchMoreProps; + fetchReferenceProducts?: (data: string) => void; + fetchMoreReferenceProducts?: FetchMoreProps; + assignReferencesAttributeId?: string; } export interface ProductCreateFormProps extends UseProductCreateFormOpts { @@ -222,6 +233,18 @@ function useProductCreateForm( attributes.change, triggerChange ); + const handleFetchReferences = createFetchReferencesHandler( + attributes.data, + opts.assignReferencesAttributeId, + opts.fetchReferencePages, + opts.fetchReferenceProducts + ); + const handleFetchMoreReferences = createFetchMoreReferencesHandler( + attributes.data, + opts.assignReferencesAttributeId, + opts.fetchMoreReferencePages, + opts.fetchMoreReferenceProducts + ); const handleAttributeFileChange = createAttributeFileChangeHandler( attributes.change, attributesWithNewFileValue.data, @@ -281,7 +304,8 @@ function useProductCreateForm( attributes: getAttributesDisplayData( attributes.data, attributesWithNewFileValue.data, - opts.referencePages + opts.referencePages, + opts.referenceProducts ), attributesWithNewFileValue: attributesWithNewFileValue.data, description: description.current, @@ -315,6 +339,8 @@ function useProductCreateForm( changeMetadata, changeStock: handleStockChange, deleteStock: handleStockDelete, + fetchMoreReferences: handleFetchMoreReferences, + fetchReferences: handleFetchReferences, reorderAttributeValue: handleAttributeValueReorder, selectAttribute: handleAttributeChange, selectAttributeFile: handleAttributeFileChange, diff --git a/src/products/components/ProductUpdatePage/ProductUpdatePage.test.tsx b/src/products/components/ProductUpdatePage/ProductUpdatePage.test.tsx index 09913fbfb..b96bbaf00 100644 --- a/src/products/components/ProductUpdatePage/ProductUpdatePage.test.tsx +++ b/src/products/components/ProductUpdatePage/ProductUpdatePage.test.tsx @@ -60,6 +60,7 @@ const props: ProductUpdatePageProps = { placeholderImage, product, referencePages: [], + referenceProducts: [], saveButtonBarState: "default", selectedChannelId: "123", taxTypes, diff --git a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx index 87c00d0fd..e50d8ed1c 100644 --- a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx +++ b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx @@ -1,5 +1,8 @@ import { OutputData } from "@editorjs/editorjs"; -import { mergeAttributeValues } from "@saleor/attributes/utils/data"; +import { + getAttributeValuesFromReferences, + mergeAttributeValues +} from "@saleor/attributes/utils/data"; import { ChannelData } from "@saleor/channels/utils"; import AppHeader from "@saleor/components/AppHeader"; import AssignAttributeValueDialog from "@saleor/components/AssignAttributeValueDialog"; @@ -23,11 +26,11 @@ import { FormsetData } from "@saleor/hooks/useFormset"; import useStateFromProps from "@saleor/hooks/useStateFromProps"; import { sectionNames } from "@saleor/intl"; import { maybe } from "@saleor/misc"; -import { getAttributeValuesFromReferences } from "@saleor/pages/utils/data"; import ProductVariantPrice from "@saleor/products/components/ProductVariantPrice"; import { SearchCategories_search_edges_node } from "@saleor/searches/types/SearchCategories"; import { SearchCollections_search_edges_node } from "@saleor/searches/types/SearchCollections"; import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; +import { SearchProducts_search_edges_node } from "@saleor/searches/types/SearchProducts"; import { ChannelProps, FetchMoreProps, @@ -76,12 +79,15 @@ export interface ProductUpdatePageProps extends ListActions, ChannelProps { saveButtonBarState: ConfirmButtonTransitionState; warehouses: WarehouseFragment[]; taxTypes: TaxTypeFragment[]; - referencePages: SearchPages_search_edges_node[]; + referencePages?: SearchPages_search_edges_node[]; + referenceProducts?: SearchProducts_search_edges_node[]; assignReferencesAttributeId?: string; fetchMoreReferencePages?: FetchMoreProps; + fetchMoreReferenceProducts?: FetchMoreProps; fetchCategories: (query: string) => void; fetchCollections: (query: string) => void; fetchReferencePages?: (data: string) => void; + fetchReferenceProducts?: (data: string) => void; onAssignReferencesClick: (attribute: AttributeInput) => void; onCloseDialog: () => void; onVariantsAdd: () => void; @@ -136,7 +142,8 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({ variants, warehouses, taxTypes, - referencePages, + referencePages = [], + referenceProducts = [], onBack, onDelete, onImageDelete, @@ -163,6 +170,8 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({ onAssignReferencesClick, fetchReferencePages, fetchMoreReferencePages, + fetchReferenceProducts, + fetchMoreReferenceProducts, onCloseDialog }) => { const intl = useIntl(); @@ -222,6 +231,12 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({ currentChannels={currentChannels} hasVariants={hasVariants} referencePages={referencePages} + referenceProducts={referenceProducts} + fetchReferencePages={fetchReferencePages} + fetchMoreReferencePages={fetchMoreReferencePages} + fetchReferenceProducts={fetchReferenceProducts} + fetchMoreReferenceProducts={fetchMoreReferenceProducts} + assignReferencesAttributeId={assignReferencesAttributeId} > {({ change, @@ -408,13 +423,14 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({ attributeValues={getAttributeValuesFromReferences( assignReferencesAttributeId, data.attributes, - referencePages + referencePages, + referenceProducts )} - hasMore={fetchMoreReferencePages?.hasMore} + hasMore={handlers.fetchMoreReferences?.hasMore} open={canOpenAssignReferencesAttributeDialog} - onFetch={fetchReferencePages} - onFetchMore={fetchMoreReferencePages?.onFetchMore} - loading={fetchMoreReferencePages?.loading} + onFetch={handlers.fetchReferences} + onFetchMore={handlers.fetchMoreReferences?.onFetchMore} + loading={handlers.fetchMoreReferences?.loading} onClose={onCloseDialog} onSubmit={attributeValues => handleAssignReferenceAttribute( diff --git a/src/products/components/ProductUpdatePage/form.tsx b/src/products/components/ProductUpdatePage/form.tsx index c49f2c7f1..8873c597e 100644 --- a/src/products/components/ProductUpdatePage/form.tsx +++ b/src/products/components/ProductUpdatePage/form.tsx @@ -5,7 +5,9 @@ import { createAttributeFileChangeHandler, createAttributeMultiChangeHandler, createAttributeReferenceChangeHandler, - createAttributeValueReorderHandler + createAttributeValueReorderHandler, + createFetchMoreReferencesHandler, + createFetchReferencesHandler } from "@saleor/attributes/utils/handlers"; import { ChannelData, ChannelPriceArgs } from "@saleor/channels/utils"; import { AttributeInput } from "@saleor/components/Attributes"; @@ -34,8 +36,9 @@ import { validatePrice } from "@saleor/products/utils/validation"; import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; +import { SearchProducts_search_edges_node } from "@saleor/searches/types/SearchProducts"; import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses"; -import { ReorderEvent } from "@saleor/types"; +import { FetchMoreProps, ReorderEvent } from "@saleor/types"; import handleFormSubmit from "@saleor/utils/handlers/handleFormSubmit"; import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler"; import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; @@ -116,6 +119,8 @@ export interface ProductUpdateHandlers Record<"reorderAttributeValue", FormsetChange<ReorderEvent>>, Record<"addStock" | "deleteStock", (id: string) => void> { changeDescription: RichTextEditorChange; + fetchReferences: (value: string) => void; + fetchMoreReferences: FetchMoreProps; } export interface UseProductUpdateFormResult { change: FormChange; @@ -143,6 +148,12 @@ export interface UseProductUpdateFormOpts currentChannels: ChannelData[]; hasVariants: boolean; referencePages: SearchPages_search_edges_node[]; + referenceProducts: SearchProducts_search_edges_node[]; + fetchReferencePages?: (data: string) => void; + fetchMoreReferencePages?: FetchMoreProps; + fetchReferenceProducts?: (data: string) => void; + fetchMoreReferenceProducts?: FetchMoreProps; + assignReferencesAttributeId?: string; } export interface ProductUpdateFormProps extends UseProductUpdateFormOpts { @@ -232,6 +243,18 @@ function useProductUpdateForm( attributes.change, triggerChange ); + const handleFetchReferences = createFetchReferencesHandler( + attributes.data, + opts.assignReferencesAttributeId, + opts.fetchReferencePages, + opts.fetchReferenceProducts + ); + const handleFetchMoreReferences = createFetchMoreReferencesHandler( + attributes.data, + opts.assignReferencesAttributeId, + opts.fetchMoreReferencePages, + opts.fetchMoreReferenceProducts + ); const handleAttributeFileChange = createAttributeFileChangeHandler( attributes.change, attributesWithNewFileValue.data, @@ -283,7 +306,8 @@ function useProductUpdateForm( attributes: getAttributesDisplayData( attributes.data, attributesWithNewFileValue.data, - opts.referencePages + opts.referencePages, + opts.referenceProducts ), description: description.current, stocks: stocks.data @@ -332,6 +356,8 @@ function useProductUpdateForm( changeMetadata, changeStock: handleStockChange, deleteStock: handleStockDelete, + fetchMoreReferences: handleFetchMoreReferences, + fetchReferences: handleFetchReferences, reorderAttributeValue: handleAttributeValueReorder, selectAttribute: handleAttributeChange, selectAttributeFile: handleAttributeFileChange, diff --git a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx index b0caf9591..b8bb91f67 100644 --- a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx +++ b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx @@ -1,4 +1,7 @@ -import { mergeAttributeValues } from "@saleor/attributes/utils/data"; +import { + getAttributeValuesFromReferences, + mergeAttributeValues +} from "@saleor/attributes/utils/data"; import { ChannelPriceData } from "@saleor/channels/utils"; import AppHeader from "@saleor/components/AppHeader"; import AssignAttributeValueDialog from "@saleor/components/AssignAttributeValueDialog"; @@ -15,8 +18,8 @@ import PageHeader from "@saleor/components/PageHeader"; import SaveButtonBar from "@saleor/components/SaveButtonBar"; import { ProductChannelListingErrorFragment } from "@saleor/fragments/types/ProductChannelListingErrorFragment"; import { ProductErrorWithAttributesFragment } from "@saleor/fragments/types/ProductErrorWithAttributesFragment"; -import { getAttributeValuesFromReferences } from "@saleor/pages/utils/data"; import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; +import { SearchProducts_search_edges_node } from "@saleor/searches/types/SearchProducts"; import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses"; import { FetchMoreProps, ReorderAction } from "@saleor/types"; import React from "react"; @@ -61,7 +64,8 @@ interface ProductVariantCreatePageProps { saveButtonBarState: ConfirmButtonTransitionState; warehouses: SearchWarehouses_search_edges_node[]; weightUnit: string; - referencePages: SearchPages_search_edges_node[]; + referencePages?: SearchPages_search_edges_node[]; + referenceProducts?: SearchProducts_search_edges_node[]; onBack: () => void; onSubmit: (data: ProductVariantCreateData) => void; onVariantClick: (variantId: string) => void; @@ -70,7 +74,9 @@ interface ProductVariantCreatePageProps { assignReferencesAttributeId?: string; onAssignReferencesClick: (attribute: AttributeInput) => void; fetchReferencePages?: (data: string) => void; + fetchReferenceProducts?: (data: string) => void; fetchMoreReferencePages?: FetchMoreProps; + fetchMoreReferenceProducts?: FetchMoreProps; onCloseDialog: () => void; } @@ -84,7 +90,8 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({ saveButtonBarState, warehouses, weightUnit, - referencePages, + referencePages = [], + referenceProducts = [], onBack, onSubmit, onVariantClick, @@ -93,7 +100,9 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({ assignReferencesAttributeId, onAssignReferencesClick, fetchReferencePages, + fetchReferenceProducts, fetchMoreReferencePages, + fetchMoreReferenceProducts, onCloseDialog }) => { const intl = useIntl(); @@ -123,6 +132,12 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({ warehouses={warehouses} currentChannels={channels} referencePages={referencePages} + referenceProducts={referenceProducts} + fetchReferencePages={fetchReferencePages} + fetchMoreReferencePages={fetchMoreReferencePages} + fetchReferenceProducts={fetchReferenceProducts} + fetchMoreReferenceProducts={fetchMoreReferenceProducts} + assignReferencesAttributeId={assignReferencesAttributeId} > {({ change, @@ -237,13 +252,14 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({ attributeValues={getAttributeValuesFromReferences( assignReferencesAttributeId, data.attributes, - referencePages + referencePages, + referenceProducts )} - hasMore={fetchMoreReferencePages?.hasMore} + hasMore={handlers.fetchMoreReferences?.hasMore} open={canOpenAssignReferencesAttributeDialog} - onFetch={fetchReferencePages} - onFetchMore={fetchMoreReferencePages?.onFetchMore} - loading={fetchMoreReferencePages?.loading} + onFetch={handlers.fetchReferences} + onFetchMore={handlers.fetchMoreReferences?.onFetchMore} + loading={handlers.fetchMoreReferences?.loading} onClose={onCloseDialog} onSubmit={attributeValues => handleAssignReferenceAttribute(attributeValues, data, handlers) diff --git a/src/products/components/ProductVariantCreatePage/form.tsx b/src/products/components/ProductVariantCreatePage/form.tsx index 3febccb3c..bad8c22a9 100644 --- a/src/products/components/ProductVariantCreatePage/form.tsx +++ b/src/products/components/ProductVariantCreatePage/form.tsx @@ -4,7 +4,9 @@ import { createAttributeFileChangeHandler, createAttributeMultiChangeHandler, createAttributeReferenceChangeHandler, - createAttributeValueReorderHandler + createAttributeValueReorderHandler, + createFetchMoreReferencesHandler, + createFetchReferencesHandler } from "@saleor/attributes/utils/handlers"; import { ChannelPriceData, IChannelPriceArgs } from "@saleor/channels/utils"; import { AttributeInput } from "@saleor/components/Attributes"; @@ -22,8 +24,9 @@ import { validatePrice } from "@saleor/products/utils/validation"; import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; +import { SearchProducts_search_edges_node } from "@saleor/searches/types/SearchProducts"; import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses"; -import { ReorderEvent } from "@saleor/types"; +import { FetchMoreProps, ReorderEvent } from "@saleor/types"; import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger"; import React from "react"; @@ -45,6 +48,12 @@ export interface UseProductVariantCreateFormOpts { warehouses: SearchWarehouses_search_edges_node[]; currentChannels: ChannelPriceData[]; referencePages: SearchPages_search_edges_node[]; + referenceProducts: SearchProducts_search_edges_node[]; + fetchReferencePages?: (data: string) => void; + fetchMoreReferencePages?: FetchMoreProps; + fetchReferenceProducts?: (data: string) => void; + fetchMoreReferenceProducts?: FetchMoreProps; + assignReferencesAttributeId?: string; } export interface ProductVariantCreateHandlers @@ -60,6 +69,8 @@ export interface ProductVariantCreateHandlers Record<"reorderAttributeValue", FormsetChange<ReorderEvent>>, Record<"addStock" | "deleteStock", (id: string) => void> { changeMetadata: FormChange; + fetchReferences: (value: string) => void; + fetchMoreReferences: FetchMoreProps; } export interface UseProductVariantCreateFormResult { @@ -125,6 +136,18 @@ function useProductVariantCreateForm( attributes.change, triggerChange ); + const handleFetchReferences = createFetchReferencesHandler( + attributes.data, + opts.assignReferencesAttributeId, + opts.fetchReferencePages, + opts.fetchReferenceProducts + ); + const handleFetchMoreReferences = createFetchMoreReferencesHandler( + attributes.data, + opts.assignReferencesAttributeId, + opts.fetchMoreReferencePages, + opts.fetchMoreReferenceProducts + ); const handleAttributeFileChange = createAttributeFileChangeHandler( attributes.change, attributesWithNewFileValue.data, @@ -170,7 +193,8 @@ function useProductVariantCreateForm( attributes: getAttributesDisplayData( attributes.data, attributesWithNewFileValue.data, - opts.referencePages + opts.referencePages, + opts.referenceProducts ), attributesWithNewFileValue: attributesWithNewFileValue.data, channelListings: channels.data, @@ -189,6 +213,8 @@ function useProductVariantCreateForm( changeMetadata, changeStock: handleStockChange, deleteStock: handleStockDelete, + fetchMoreReferences: handleFetchMoreReferences, + fetchReferences: handleFetchReferences, reorderAttributeValue: handleAttributeValueReorder, selectAttribute: handleAttributeChange, selectAttributeFile: handleAttributeFileChange, diff --git a/src/products/components/ProductVariantPage/ProductVariantPage.tsx b/src/products/components/ProductVariantPage/ProductVariantPage.tsx index 6e88471a5..0c86e51f1 100644 --- a/src/products/components/ProductVariantPage/ProductVariantPage.tsx +++ b/src/products/components/ProductVariantPage/ProductVariantPage.tsx @@ -1,4 +1,7 @@ -import { mergeAttributeValues } from "@saleor/attributes/utils/data"; +import { + getAttributeValuesFromReferences, + mergeAttributeValues +} from "@saleor/attributes/utils/data"; import { ChannelPriceData } from "@saleor/channels/utils"; import AppHeader from "@saleor/components/AppHeader"; import AssignAttributeValueDialog from "@saleor/components/AssignAttributeValueDialog"; @@ -18,9 +21,9 @@ import { ProductChannelListingErrorFragment } from "@saleor/fragments/types/Prod import { ProductErrorWithAttributesFragment } from "@saleor/fragments/types/ProductErrorWithAttributesFragment"; import { ProductVariant } from "@saleor/fragments/types/ProductVariant"; import { WarehouseFragment } from "@saleor/fragments/types/WarehouseFragment"; -import { getAttributeValuesFromReferences } from "@saleor/pages/utils/data"; import { VariantUpdate_productVariantUpdate_errors } from "@saleor/products/types/VariantUpdate"; import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; +import { SearchProducts_search_edges_node } from "@saleor/searches/types/SearchProducts"; import { FetchMoreProps, ReorderAction } from "@saleor/types"; import React from "react"; import { defineMessages, useIntl } from "react-intl"; @@ -81,9 +84,12 @@ interface ProductVariantPageProps { saveButtonBarState: ConfirmButtonTransitionState; variant?: ProductVariant; warehouses: WarehouseFragment[]; - referencePages: SearchPages_search_edges_node[]; + referencePages?: SearchPages_search_edges_node[]; + referenceProducts?: SearchProducts_search_edges_node[]; fetchMoreReferencePages?: FetchMoreProps; + fetchMoreReferenceProducts?: FetchMoreProps; fetchReferencePages?: (data: string) => void; + fetchReferenceProducts?: (data: string) => void; onAssignReferencesClick: (attribute: AttributeInput) => void; onCloseDialog: () => void; onVariantReorder: ReorderAction; @@ -109,7 +115,8 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({ saveButtonBarState, variant, warehouses, - referencePages, + referencePages = [], + referenceProducts = [], onAdd, onBack, onDelete, @@ -122,7 +129,9 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({ assignReferencesAttributeId, onAssignReferencesClick, fetchReferencePages, + fetchReferenceProducts, fetchMoreReferencePages, + fetchMoreReferenceProducts, onCloseDialog }) => { const intl = useIntl(); @@ -173,6 +182,12 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({ warehouses={warehouses} currentChannels={channels} referencePages={referencePages} + referenceProducts={referenceProducts} + fetchReferencePages={fetchReferencePages} + fetchMoreReferencePages={fetchMoreReferencePages} + fetchReferenceProducts={fetchReferenceProducts} + fetchMoreReferenceProducts={fetchMoreReferenceProducts} + assignReferencesAttributeId={assignReferencesAttributeId} > {({ change, @@ -296,13 +311,14 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({ attributeValues={getAttributeValuesFromReferences( assignReferencesAttributeId, data.attributes, - referencePages + referencePages, + referenceProducts )} - hasMore={fetchMoreReferencePages?.hasMore} + hasMore={handlers.fetchMoreReferences?.hasMore} open={canOpenAssignReferencesAttributeDialog} - onFetch={fetchReferencePages} - onFetchMore={fetchMoreReferencePages?.onFetchMore} - loading={fetchMoreReferencePages?.loading} + onFetch={handlers.fetchReferences} + onFetchMore={handlers.fetchMoreReferences?.onFetchMore} + loading={handlers.fetchMoreReferences?.loading} onClose={onCloseDialog} onSubmit={attributeValues => handleAssignReferenceAttribute( diff --git a/src/products/components/ProductVariantPage/form.tsx b/src/products/components/ProductVariantPage/form.tsx index d257d4306..44b859390 100644 --- a/src/products/components/ProductVariantPage/form.tsx +++ b/src/products/components/ProductVariantPage/form.tsx @@ -4,7 +4,9 @@ import { createAttributeFileChangeHandler, createAttributeMultiChangeHandler, createAttributeReferenceChangeHandler, - createAttributeValueReorderHandler + createAttributeValueReorderHandler, + createFetchMoreReferencesHandler, + createFetchReferencesHandler } from "@saleor/attributes/utils/handlers"; import { ChannelPriceData, IChannelPriceArgs } from "@saleor/channels/utils"; import { AttributeInput } from "@saleor/components/Attributes"; @@ -25,8 +27,9 @@ import { validatePrice } from "@saleor/products/utils/validation"; import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; +import { SearchProducts_search_edges_node } from "@saleor/searches/types/SearchProducts"; import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses"; -import { ReorderEvent } from "@saleor/types"; +import { FetchMoreProps, ReorderEvent } from "@saleor/types"; import { mapMetadataItemToInput } from "@saleor/utils/maps"; import getMetadata from "@saleor/utils/metadata/getMetadata"; import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger"; @@ -60,6 +63,12 @@ export interface UseProductVariantUpdateFormOpts { warehouses: SearchWarehouses_search_edges_node[]; currentChannels: ChannelPriceData[]; referencePages: SearchPages_search_edges_node[]; + referenceProducts: SearchProducts_search_edges_node[]; + fetchReferencePages?: (data: string) => void; + fetchMoreReferencePages?: FetchMoreProps; + fetchReferenceProducts?: (data: string) => void; + fetchMoreReferenceProducts?: FetchMoreProps; + assignReferencesAttributeId?: string; } export interface ProductVariantUpdateHandlers @@ -75,6 +84,8 @@ export interface ProductVariantUpdateHandlers Record<"reorderAttributeValue", FormsetChange<ReorderEvent>>, Record<"addStock" | "deleteStock", (id: string) => void> { changeMetadata: FormChange; + fetchReferences: (value: string) => void; + fetchMoreReferences: FetchMoreProps; } export interface UseProductVariantUpdateFormResult { @@ -142,6 +153,18 @@ function useProductVariantUpdateForm( attributes.change, triggerChange ); + const handleFetchReferences = createFetchReferencesHandler( + attributes.data, + opts.assignReferencesAttributeId, + opts.fetchReferencePages, + opts.fetchReferenceProducts + ); + const handleFetchMoreReferences = createFetchMoreReferencesHandler( + attributes.data, + opts.assignReferencesAttributeId, + opts.fetchMoreReferencePages, + opts.fetchMoreReferenceProducts + ); const handleAttributeFileChange = createAttributeFileChangeHandler( attributes.change, attributesWithNewFileValue.data, @@ -201,7 +224,8 @@ function useProductVariantUpdateForm( attributes: getAttributesDisplayData( attributes.data, attributesWithNewFileValue.data, - opts.referencePages + opts.referencePages, + opts.referenceProducts ), channelListings: channels.data, stocks: stocks.data @@ -239,6 +263,8 @@ function useProductVariantUpdateForm( changeMetadata, changeStock: handleStockChange, deleteStock: handleStockDelete, + fetchMoreReferences: handleFetchMoreReferences, + fetchReferences: handleFetchReferences, reorderAttributeValue: handleAttributeValueReorder, selectAttribute: handleAttributeChange, selectAttributeFile: handleAttributeFileChange, diff --git a/src/products/fixtures.ts b/src/products/fixtures.ts index e12aa85f9..602232486 100644 --- a/src/products/fixtures.ts +++ b/src/products/fixtures.ts @@ -22,6 +22,7 @@ export const product: ( __typename: "SelectedAttribute", attribute: { __typename: "Attribute" as "Attribute", + entityType: null, id: "pta18161", inputType: AttributeInputTypeEnum.DROPDOWN, name: "Borders", @@ -61,6 +62,7 @@ export const product: ( __typename: "SelectedAttribute", attribute: { __typename: "Attribute" as "Attribute", + entityType: null, id: "pta22785", inputType: AttributeInputTypeEnum.MULTISELECT, name: "Legacy", @@ -262,6 +264,7 @@ export const product: ( nonSelectionVariantAttributes: [ { __typename: "Attribute", + entityType: null, id: "isdugfhud", inputType: AttributeInputTypeEnum.FILE, name: "Attachment", @@ -286,6 +289,7 @@ export const product: ( selectionVariantAttributes: [ { __typename: "Attribute", + entityType: null, id: "pta18161", inputType: AttributeInputTypeEnum.DROPDOWN, name: "Color", @@ -2710,6 +2714,7 @@ export const variant = (placeholderImage: string): ProductVariant => ({ __typename: "SelectedAttribute", attribute: { __typename: "Attribute", + entityType: null, id: "nfnyffcf8eyfm", inputType: AttributeInputTypeEnum.FILE, name: "Attachment", @@ -2945,6 +2950,7 @@ export const variant = (placeholderImage: string): ProductVariant => ({ __typename: "SelectedAttribute", attribute: { __typename: "Attribute" as "Attribute", + entityType: null, id: "pta18161", inputType: AttributeInputTypeEnum.DROPDOWN, name: "Borders", @@ -2984,6 +2990,7 @@ export const variant = (placeholderImage: string): ProductVariant => ({ __typename: "SelectedAttribute", attribute: { __typename: "Attribute" as "Attribute", + entityType: null, id: "pta22785", inputType: AttributeInputTypeEnum.DROPDOWN, name: "Legacy", diff --git a/src/products/types/CreateMultipleVariantsData.ts b/src/products/types/CreateMultipleVariantsData.ts index 5d7621d9d..4dc515a03 100644 --- a/src/products/types/CreateMultipleVariantsData.ts +++ b/src/products/types/CreateMultipleVariantsData.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL query operation: CreateMultipleVariantsData @@ -29,6 +29,7 @@ export interface CreateMultipleVariantsData_product_attributes_attribute { slug: string | null; name: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (CreateMultipleVariantsData_product_attributes_attribute_values | null)[] | null; } diff --git a/src/products/types/ProductChannelListingUpdate.ts b/src/products/types/ProductChannelListingUpdate.ts index c7e8495e4..d12f9953d 100644 --- a/src/products/types/ProductChannelListingUpdate.ts +++ b/src/products/types/ProductChannelListingUpdate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { ProductChannelListingUpdateInput, AttributeInputTypeEnum, WeightUnitsEnum, ProductErrorCode } from "./../../types/globalTypes"; +import { ProductChannelListingUpdateInput, AttributeInputTypeEnum, AttributeEntityTypeEnum, WeightUnitsEnum, ProductErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: ProductChannelListingUpdate @@ -29,6 +29,7 @@ export interface ProductChannelListingUpdate_productChannelListingUpdate_product slug: string | null; name: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (ProductChannelListingUpdate_productChannelListingUpdate_product_attributes_attribute_values | null)[] | null; } diff --git a/src/products/types/ProductCreate.ts b/src/products/types/ProductCreate.ts index c44492a14..174158d4b 100644 --- a/src/products/types/ProductCreate.ts +++ b/src/products/types/ProductCreate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { ProductCreateInput, ProductErrorCode, AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; +import { ProductCreateInput, ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: ProductCreate @@ -36,6 +36,7 @@ export interface ProductCreate_productCreate_product_attributes_attribute { slug: string | null; name: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (ProductCreate_productCreate_product_attributes_attribute_values | null)[] | null; } diff --git a/src/products/types/ProductDetails.ts b/src/products/types/ProductDetails.ts index 1dd8b0bcc..a8ed59463 100644 --- a/src/products/types/ProductDetails.ts +++ b/src/products/types/ProductDetails.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, AttributeEntityTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL query operation: ProductDetails @@ -29,6 +29,7 @@ export interface ProductDetails_product_attributes_attribute { slug: string | null; name: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (ProductDetails_product_attributes_attribute_values | null)[] | null; } diff --git a/src/products/types/ProductImageCreate.ts b/src/products/types/ProductImageCreate.ts index 3ef7a7253..704823003 100644 --- a/src/products/types/ProductImageCreate.ts +++ b/src/products/types/ProductImageCreate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { ProductErrorCode, AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; +import { ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: ProductImageCreate @@ -35,6 +35,7 @@ export interface ProductImageCreate_productImageCreate_product_attributes_attrib slug: string | null; name: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (ProductImageCreate_productImageCreate_product_attributes_attribute_values | null)[] | null; } diff --git a/src/products/types/ProductImageUpdate.ts b/src/products/types/ProductImageUpdate.ts index c6fa243f0..219de6624 100644 --- a/src/products/types/ProductImageUpdate.ts +++ b/src/products/types/ProductImageUpdate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { ProductErrorCode, AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; +import { ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: ProductImageUpdate @@ -35,6 +35,7 @@ export interface ProductImageUpdate_productImageUpdate_product_attributes_attrib slug: string | null; name: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (ProductImageUpdate_productImageUpdate_product_attributes_attribute_values | null)[] | null; } diff --git a/src/products/types/ProductUpdate.ts b/src/products/types/ProductUpdate.ts index 75d41461e..661a7a0cf 100644 --- a/src/products/types/ProductUpdate.ts +++ b/src/products/types/ProductUpdate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { ProductInput, ProductErrorCode, AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; +import { ProductInput, ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: ProductUpdate @@ -36,6 +36,7 @@ export interface ProductUpdate_productUpdate_product_attributes_attribute { slug: string | null; name: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (ProductUpdate_productUpdate_product_attributes_attribute_values | null)[] | null; } diff --git a/src/products/types/ProductVariantChannelListingUpdate.ts b/src/products/types/ProductVariantChannelListingUpdate.ts index b8c6d66bc..30220e6c0 100644 --- a/src/products/types/ProductVariantChannelListingUpdate.ts +++ b/src/products/types/ProductVariantChannelListingUpdate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { ProductVariantChannelListingAddInput, AttributeInputTypeEnum, WeightUnitsEnum, ProductErrorCode } from "./../../types/globalTypes"; +import { ProductVariantChannelListingAddInput, AttributeInputTypeEnum, AttributeEntityTypeEnum, WeightUnitsEnum, ProductErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: ProductVariantChannelListingUpdate @@ -41,6 +41,7 @@ export interface ProductVariantChannelListingUpdate_productVariantChannelListing name: string | null; slug: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_selectionAttributes_attribute_values | null)[] | null; } @@ -87,6 +88,7 @@ export interface ProductVariantChannelListingUpdate_productVariantChannelListing name: string | null; slug: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_nonSelectionAttributes_attribute_values | null)[] | null; } diff --git a/src/products/types/ProductVariantCreateData.ts b/src/products/types/ProductVariantCreateData.ts index 7cb5f0e4c..042a9d2af 100644 --- a/src/products/types/ProductVariantCreateData.ts +++ b/src/products/types/ProductVariantCreateData.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL query operation: ProductVariantCreateData @@ -48,6 +48,7 @@ export interface ProductVariantCreateData_product_productType_selectionVariantAt name: string | null; slug: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (ProductVariantCreateData_product_productType_selectionVariantAttributes_values | null)[] | null; } @@ -73,6 +74,7 @@ export interface ProductVariantCreateData_product_productType_nonSelectionVarian name: string | null; slug: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (ProductVariantCreateData_product_productType_nonSelectionVariantAttributes_values | null)[] | null; } diff --git a/src/products/types/ProductVariantDetails.ts b/src/products/types/ProductVariantDetails.ts index a7a4ebc9d..c4853e724 100644 --- a/src/products/types/ProductVariantDetails.ts +++ b/src/products/types/ProductVariantDetails.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, AttributeEntityTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL query operation: ProductVariantDetails @@ -41,6 +41,7 @@ export interface ProductVariantDetails_productVariant_selectionAttributes_attrib name: string | null; slug: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (ProductVariantDetails_productVariant_selectionAttributes_attribute_values | null)[] | null; } @@ -87,6 +88,7 @@ export interface ProductVariantDetails_productVariant_nonSelectionAttributes_att name: string | null; slug: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (ProductVariantDetails_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; } diff --git a/src/products/types/ProductVariantReorder.ts b/src/products/types/ProductVariantReorder.ts index 652d1a2c8..68fb0c764 100644 --- a/src/products/types/ProductVariantReorder.ts +++ b/src/products/types/ProductVariantReorder.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { ReorderInput, ProductErrorCode, AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; +import { ReorderInput, ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: ProductVariantReorder @@ -35,6 +35,7 @@ export interface ProductVariantReorder_productVariantReorder_product_attributes_ slug: string | null; name: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (ProductVariantReorder_productVariantReorder_product_attributes_attribute_values | null)[] | null; } diff --git a/src/products/types/ProductVariantSetDefault.ts b/src/products/types/ProductVariantSetDefault.ts index 8a9c2ecb3..ed095108d 100644 --- a/src/products/types/ProductVariantSetDefault.ts +++ b/src/products/types/ProductVariantSetDefault.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { ProductErrorCode, AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; +import { ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: ProductVariantSetDefault @@ -35,6 +35,7 @@ export interface ProductVariantSetDefault_productVariantSetDefault_product_attri slug: string | null; name: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (ProductVariantSetDefault_productVariantSetDefault_product_attributes_attribute_values | null)[] | null; } diff --git a/src/products/types/SimpleProductUpdate.ts b/src/products/types/SimpleProductUpdate.ts index 9be3b83bf..2703b3ed8 100644 --- a/src/products/types/SimpleProductUpdate.ts +++ b/src/products/types/SimpleProductUpdate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { ProductInput, ProductVariantInput, StockInput, ProductErrorCode, AttributeInputTypeEnum, WeightUnitsEnum, StockErrorCode } from "./../../types/globalTypes"; +import { ProductInput, ProductVariantInput, StockInput, ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, WeightUnitsEnum, StockErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: SimpleProductUpdate @@ -36,6 +36,7 @@ export interface SimpleProductUpdate_productUpdate_product_attributes_attribute slug: string | null; name: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (SimpleProductUpdate_productUpdate_product_attributes_attribute_values | null)[] | null; } @@ -320,6 +321,7 @@ export interface SimpleProductUpdate_productVariantUpdate_productVariant_selecti name: string | null; slug: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (SimpleProductUpdate_productVariantUpdate_productVariant_selectionAttributes_attribute_values | null)[] | null; } @@ -366,6 +368,7 @@ export interface SimpleProductUpdate_productVariantUpdate_productVariant_nonSele name: string | null; slug: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (SimpleProductUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; } @@ -595,6 +598,7 @@ export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_s name: string | null; slug: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (SimpleProductUpdate_productVariantStocksCreate_productVariant_selectionAttributes_attribute_values | null)[] | null; } @@ -641,6 +645,7 @@ export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_n name: string | null; slug: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (SimpleProductUpdate_productVariantStocksCreate_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; } @@ -869,6 +874,7 @@ export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_s name: string | null; slug: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (SimpleProductUpdate_productVariantStocksDelete_productVariant_selectionAttributes_attribute_values | null)[] | null; } @@ -915,6 +921,7 @@ export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_n name: string | null; slug: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (SimpleProductUpdate_productVariantStocksDelete_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; } @@ -1144,6 +1151,7 @@ export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_s name: string | null; slug: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (SimpleProductUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_attribute_values | null)[] | null; } @@ -1190,6 +1198,7 @@ export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_n name: string | null; slug: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (SimpleProductUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; } diff --git a/src/products/types/VariantCreate.ts b/src/products/types/VariantCreate.ts index f31d5b352..a805f7dd2 100644 --- a/src/products/types/VariantCreate.ts +++ b/src/products/types/VariantCreate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { ProductVariantCreateInput, ProductErrorCode, AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; +import { ProductVariantCreateInput, ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: VariantCreate @@ -48,6 +48,7 @@ export interface VariantCreate_productVariantCreate_productVariant_selectionAttr name: string | null; slug: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (VariantCreate_productVariantCreate_productVariant_selectionAttributes_attribute_values | null)[] | null; } @@ -94,6 +95,7 @@ export interface VariantCreate_productVariantCreate_productVariant_nonSelectionA name: string | null; slug: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (VariantCreate_productVariantCreate_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; } diff --git a/src/products/types/VariantImageAssign.ts b/src/products/types/VariantImageAssign.ts index c6058315f..1b5a70a03 100644 --- a/src/products/types/VariantImageAssign.ts +++ b/src/products/types/VariantImageAssign.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { ProductErrorCode, AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; +import { ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: VariantImageAssign @@ -47,6 +47,7 @@ export interface VariantImageAssign_variantImageAssign_productVariant_selectionA name: string | null; slug: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (VariantImageAssign_variantImageAssign_productVariant_selectionAttributes_attribute_values | null)[] | null; } @@ -93,6 +94,7 @@ export interface VariantImageAssign_variantImageAssign_productVariant_nonSelecti name: string | null; slug: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (VariantImageAssign_variantImageAssign_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; } diff --git a/src/products/types/VariantImageUnassign.ts b/src/products/types/VariantImageUnassign.ts index be0df6e40..a9cd3818c 100644 --- a/src/products/types/VariantImageUnassign.ts +++ b/src/products/types/VariantImageUnassign.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { ProductErrorCode, AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; +import { ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: VariantImageUnassign @@ -47,6 +47,7 @@ export interface VariantImageUnassign_variantImageUnassign_productVariant_select name: string | null; slug: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (VariantImageUnassign_variantImageUnassign_productVariant_selectionAttributes_attribute_values | null)[] | null; } @@ -93,6 +94,7 @@ export interface VariantImageUnassign_variantImageUnassign_productVariant_nonSel name: string | null; slug: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (VariantImageUnassign_variantImageUnassign_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; } diff --git a/src/products/types/VariantUpdate.ts b/src/products/types/VariantUpdate.ts index 519334bc8..0b2c430de 100644 --- a/src/products/types/VariantUpdate.ts +++ b/src/products/types/VariantUpdate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { StockInput, AttributeValueInput, ProductErrorCode, AttributeInputTypeEnum, WeightUnitsEnum, StockErrorCode } from "./../../types/globalTypes"; +import { StockInput, AttributeValueInput, ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, WeightUnitsEnum, StockErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: VariantUpdate @@ -48,6 +48,7 @@ export interface VariantUpdate_productVariantUpdate_productVariant_selectionAttr name: string | null; slug: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (VariantUpdate_productVariantUpdate_productVariant_selectionAttributes_attribute_values | null)[] | null; } @@ -94,6 +95,7 @@ export interface VariantUpdate_productVariantUpdate_productVariant_nonSelectionA name: string | null; slug: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (VariantUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; } @@ -323,6 +325,7 @@ export interface VariantUpdate_productVariantStocksUpdate_productVariant_selecti name: string | null; slug: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (VariantUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_attribute_values | null)[] | null; } @@ -369,6 +372,7 @@ export interface VariantUpdate_productVariantStocksUpdate_productVariant_nonSele name: string | null; slug: string | null; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; values: (VariantUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; } diff --git a/src/products/utils/data.ts b/src/products/utils/data.ts index 8ee4a6d6c..e6f8525e9 100644 --- a/src/products/utils/data.ts +++ b/src/products/utils/data.ts @@ -47,6 +47,7 @@ export function getAttributeInputFromProduct( (): AttributeInput[] => product.attributes.map(attribute => ({ data: { + entityType: attribute.attribute.entityType, inputType: attribute.attribute.inputType, isRequired: attribute.attribute.valueRequired, selectedValues: attribute.values, @@ -65,6 +66,7 @@ export function getAttributeInputFromProductType( ): AttributeInput[] { return productType.productAttributes.map(attribute => ({ data: { + entityType: attribute.entityType, inputType: attribute.inputType, isRequired: attribute.valueRequired, values: attribute.values @@ -81,6 +83,7 @@ export function getAttributeInputFromAttributes( ): AttributeInput[] { return variantAttributes?.map(attribute => ({ data: { + entityType: attribute.entityType, inputType: attribute.inputType, isRequired: attribute.valueRequired, values: attribute.values, @@ -98,6 +101,7 @@ export function getAttributeInputFromSelectedAttributes( ): AttributeInput[] { return variantAttributes?.map(attribute => ({ data: { + entityType: attribute.attribute.entityType, inputType: attribute.attribute.inputType, isRequired: attribute.attribute.valueRequired, selectedValues: attribute.values, diff --git a/src/products/views/ProductCreate/ProductCreate.tsx b/src/products/views/ProductCreate/ProductCreate.tsx index 03150579e..7f10487d9 100644 --- a/src/products/views/ProductCreate/ProductCreate.tsx +++ b/src/products/views/ProductCreate/ProductCreate.tsx @@ -27,6 +27,7 @@ import { import useCategorySearch from "@saleor/searches/useCategorySearch"; import useCollectionSearch from "@saleor/searches/useCollectionSearch"; import usePageSearch from "@saleor/searches/usePageSearch"; +import useProductSearch from "@saleor/searches/useProductSearch"; import useProductTypeSearch from "@saleor/searches/useProductTypeSearch"; import { useTaxTypeList } from "@saleor/taxes/queries"; import { getProductErrorMessage } from "@saleor/utils/errors"; @@ -89,6 +90,13 @@ export const ProductCreateView: React.FC<ProductCreateProps> = ({ params }) => { } = usePageSearch({ variables: DEFAULT_INITIAL_SEARCH_DATA }); + const { + loadMore: loadMoreProducts, + search: searchProducts, + result: searchProductsOpts + } = useProductSearch({ + variables: DEFAULT_INITIAL_SEARCH_DATA + }); const warehouses = useWarehouseList({ displayLoader: true, variables: { @@ -202,25 +210,30 @@ export const ProductCreateView: React.FC<ProductCreateProps> = ({ params }) => { }, [productCreateComplete]); const fetchMoreProductTypes = { - hasMore: searchProductTypesOpts.data?.search.pageInfo.hasNextPage, + hasMore: searchProductTypesOpts.data?.search?.pageInfo?.hasNextPage, loading: searchProductTypesOpts.loading, onFetchMore: loadMoreProductTypes }; const fetchMoreCollections = { - hasMore: searchCollectionOpts.data?.search.pageInfo.hasNextPage, + hasMore: searchCollectionOpts.data?.search?.pageInfo?.hasNextPage, loading: searchCollectionOpts.loading, onFetchMore: loadMoreCollections }; const fetchMoreCategories = { - hasMore: searchCategoryOpts.data?.search.pageInfo.hasNextPage, + hasMore: searchCategoryOpts.data?.search?.pageInfo?.hasNextPage, loading: searchCategoryOpts.loading, onFetchMore: loadMoreCategories }; const fetchMoreReferencePages = { - hasMore: searchPagesOpts.data?.search.pageInfo.hasNextPage, + hasMore: searchPagesOpts.data?.search?.pageInfo?.hasNextPage, loading: searchPagesOpts.loading, onFetchMore: loadMorePages }; + const fetchMoreReferenceProducts = { + hasMore: searchProductsOpts.data?.search?.pageInfo?.hasNextPage, + loading: searchProductsOpts.loading, + onFetchMore: loadMoreProducts + }; const loading = uploadFileOpts.loading || @@ -301,8 +314,13 @@ export const ProductCreateView: React.FC<ProductCreateProps> = ({ params }) => { referencePages={searchPagesOpts.data?.search.edges.map( edge => edge.node )} + referenceProducts={searchProductsOpts.data?.search.edges.map( + edge => edge.node + )} fetchReferencePages={searchPages} fetchMoreReferencePages={fetchMoreReferencePages} + fetchReferenceProducts={searchProducts} + fetchMoreReferenceProducts={fetchMoreReferenceProducts} onCloseDialog={() => navigate(productAddUrl())} /> </> diff --git a/src/products/views/ProductUpdate/ProductUpdate.tsx b/src/products/views/ProductUpdate/ProductUpdate.tsx index 9bca2a110..dc6621c02 100644 --- a/src/products/views/ProductUpdate/ProductUpdate.tsx +++ b/src/products/views/ProductUpdate/ProductUpdate.tsx @@ -40,6 +40,7 @@ import { import useCategorySearch from "@saleor/searches/useCategorySearch"; import useCollectionSearch from "@saleor/searches/useCollectionSearch"; import usePageSearch from "@saleor/searches/usePageSearch"; +import useProductSearch from "@saleor/searches/useProductSearch"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler"; import { @@ -106,6 +107,13 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => { } = usePageSearch({ variables: DEFAULT_INITIAL_SEARCH_DATA }); + const { + loadMore: loadMoreProducts, + search: searchProducts, + result: searchProductsOpts + } = useProductSearch({ + variables: DEFAULT_INITIAL_SEARCH_DATA + }); const warehouses = useWarehouseList({ displayLoader: true, variables: { @@ -356,20 +364,25 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => { ]; const fetchMoreCollections = { - hasMore: searchCollectionsOpts.data?.search.pageInfo.hasNextPage, + hasMore: searchCollectionsOpts.data?.search?.pageInfo?.hasNextPage, loading: searchCollectionsOpts.loading, onFetchMore: loadMoreCollections }; const fetchMoreCategories = { - hasMore: searchCategoriesOpts.data?.search.pageInfo.hasNextPage, + hasMore: searchCategoriesOpts.data?.search?.pageInfo?.hasNextPage, loading: searchCategoriesOpts.loading, onFetchMore: loadMoreCategories }; const fetchMoreReferencePages = { - hasMore: searchPagesOpts.data?.search.pageInfo.hasNextPage, + hasMore: searchPagesOpts.data?.search?.pageInfo?.hasNextPage, loading: searchPagesOpts.loading, onFetchMore: loadMorePages }; + const fetchMoreReferenceProducts = { + hasMore: searchProductsOpts.data?.search?.pageInfo?.hasNextPage, + loading: searchProductsOpts.loading, + onFetchMore: loadMoreProducts + }; return ( <> @@ -458,8 +471,13 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => { referencePages={searchPagesOpts.data?.search.edges.map( edge => edge.node )} + referenceProducts={searchProductsOpts.data?.search.edges.map( + edge => edge.node + )} fetchReferencePages={searchPages} fetchMoreReferencePages={fetchMoreReferencePages} + fetchReferenceProducts={searchProducts} + fetchMoreReferenceProducts={fetchMoreReferenceProducts} onCloseDialog={() => navigate(productUrl(id))} /> <ActionDialog diff --git a/src/products/views/ProductVariant.tsx b/src/products/views/ProductVariant.tsx index ab8396a29..6f0976f1a 100644 --- a/src/products/views/ProductVariant.tsx +++ b/src/products/views/ProductVariant.tsx @@ -24,6 +24,7 @@ import { commonMessages } from "@saleor/intl"; import { useProductVariantChannelListingUpdate } from "@saleor/products/mutations"; import { ProductVariantDetails_productVariant } from "@saleor/products/types/ProductVariantDetails"; import usePageSearch from "@saleor/searches/usePageSearch"; +import useProductSearch from "@saleor/searches/useProductSearch"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler"; import { @@ -287,12 +288,24 @@ export const ProductVariant: React.FC<ProductUpdateProps> = ({ } = usePageSearch({ variables: DEFAULT_INITIAL_SEARCH_DATA }); + const { + loadMore: loadMoreProducts, + search: searchProducts, + result: searchProductsOpts + } = useProductSearch({ + variables: DEFAULT_INITIAL_SEARCH_DATA + }); const fetchMoreReferencePages = { - hasMore: searchPagesOpts.data?.search.pageInfo.hasNextPage, + hasMore: searchPagesOpts.data?.search?.pageInfo?.hasNextPage, loading: searchPagesOpts.loading, onFetchMore: loadMorePages }; + const fetchMoreReferenceProducts = { + hasMore: searchProductsOpts.data?.search?.pageInfo?.hasNextPage, + loading: searchProductsOpts.loading, + onFetchMore: loadMoreProducts + }; return ( <> @@ -335,8 +348,13 @@ export const ProductVariant: React.FC<ProductUpdateProps> = ({ referencePages={searchPagesOpts.data?.search.edges.map( edge => edge.node )} + referenceProducts={searchProductsOpts.data?.search.edges.map( + edge => edge.node + )} fetchReferencePages={searchPages} fetchMoreReferencePages={fetchMoreReferencePages} + fetchReferenceProducts={searchProducts} + fetchMoreReferenceProducts={fetchMoreReferenceProducts} onCloseDialog={() => navigate(productVariantEditUrl(productId, variantId)) } diff --git a/src/products/views/ProductVariantCreate.tsx b/src/products/views/ProductVariantCreate.tsx index e5db0d460..25a273629 100644 --- a/src/products/views/ProductVariantCreate.tsx +++ b/src/products/views/ProductVariantCreate.tsx @@ -16,6 +16,7 @@ import { commonMessages } from "@saleor/intl"; import { useProductVariantChannelListingUpdate } from "@saleor/products/mutations"; import { ProductVariantChannelListingUpdate } from "@saleor/products/types/ProductVariantChannelListingUpdate"; import usePageSearch from "@saleor/searches/usePageSearch"; +import useProductSearch from "@saleor/searches/useProductSearch"; import createMetadataCreateHandler from "@saleor/utils/handlers/metadataCreateHandler"; import { useMetadataUpdate, @@ -198,12 +199,24 @@ export const ProductVariant: React.FC<ProductVariantCreateProps> = ({ } = usePageSearch({ variables: DEFAULT_INITIAL_SEARCH_DATA }); + const { + loadMore: loadMoreProducts, + search: searchProducts, + result: searchProductsOpts + } = useProductSearch({ + variables: DEFAULT_INITIAL_SEARCH_DATA + }); const fetchMoreReferencePages = { - hasMore: searchPagesOpts.data?.search.pageInfo.hasNextPage, + hasMore: searchPagesOpts.data?.search?.pageInfo?.hasNextPage, loading: searchPagesOpts.loading, onFetchMore: loadMorePages }; + const fetchMoreReferenceProducts = { + hasMore: searchProductsOpts.data?.search?.pageInfo?.hasNextPage, + loading: searchProductsOpts.loading, + onFetchMore: loadMoreProducts + }; const disableForm = productLoading || @@ -248,8 +261,13 @@ export const ProductVariant: React.FC<ProductVariantCreateProps> = ({ referencePages={searchPagesOpts.data?.search.edges.map( edge => edge.node )} + referenceProducts={searchProductsOpts.data?.search.edges.map( + edge => edge.node + )} fetchReferencePages={searchPages} fetchMoreReferencePages={fetchMoreReferencePages} + fetchReferenceProducts={searchProducts} + fetchMoreReferenceProducts={fetchMoreReferenceProducts} onCloseDialog={() => navigate(productVariantAddUrl(productId))} /> </> diff --git a/src/searches/types/SearchPageTypes.ts b/src/searches/types/SearchPageTypes.ts index 9fc09393d..faab63336 100644 --- a/src/searches/types/SearchPageTypes.ts +++ b/src/searches/types/SearchPageTypes.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL query operation: SearchPageTypes @@ -27,6 +27,7 @@ export interface SearchPageTypes_search_edges_node_attributes { __typename: "Attribute"; id: string; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; slug: string | null; name: string | null; valueRequired: boolean; diff --git a/src/searches/types/SearchProductTypes.ts b/src/searches/types/SearchProductTypes.ts index 91ef9d89a..01a45d57b 100644 --- a/src/searches/types/SearchProductTypes.ts +++ b/src/searches/types/SearchProductTypes.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL query operation: SearchProductTypes @@ -27,6 +27,7 @@ export interface SearchProductTypes_search_edges_node_productAttributes { __typename: "Attribute"; id: string; inputType: AttributeInputTypeEnum | null; + entityType: AttributeEntityTypeEnum | null; slug: string | null; name: string | null; valueRequired: boolean; diff --git a/src/searches/usePageTypeSearch.ts b/src/searches/usePageTypeSearch.ts index aef550016..30010a0bc 100644 --- a/src/searches/usePageTypeSearch.ts +++ b/src/searches/usePageTypeSearch.ts @@ -24,6 +24,7 @@ export const searchPageTypes = gql` attributes { id inputType + entityType slug name valueRequired diff --git a/src/searches/useProductTypeSearch.ts b/src/searches/useProductTypeSearch.ts index b25c8784b..bced1e221 100644 --- a/src/searches/useProductTypeSearch.ts +++ b/src/searches/useProductTypeSearch.ts @@ -27,6 +27,7 @@ export const searchProductTypes = gql` productAttributes { id inputType + entityType slug name valueRequired diff --git a/src/storybook/stories/pages/PageDetailsPage.tsx b/src/storybook/stories/pages/PageDetailsPage.tsx index 8e70f05ab..e68fdfd14 100644 --- a/src/storybook/stories/pages/PageDetailsPage.tsx +++ b/src/storybook/stories/pages/PageDetailsPage.tsx @@ -19,6 +19,7 @@ const props: PageDetailsPageProps = { onSubmit: () => undefined, page, referencePages: [], + referenceProducts: [], saveButtonBarState: "default" }; diff --git a/src/storybook/stories/products/ProductCreatePage.tsx b/src/storybook/stories/products/ProductCreatePage.tsx index ba6b9cc4b..6208fa4f6 100644 --- a/src/storybook/stories/products/ProductCreatePage.tsx +++ b/src/storybook/stories/products/ProductCreatePage.tsx @@ -46,6 +46,7 @@ storiesOf("Views / Products / Create product", module) taxTypes={taxTypes} weightUnit="kg" referencePages={[]} + referenceProducts={[]} onAssignReferencesClick={() => undefined} onCloseDialog={() => undefined} /> @@ -77,6 +78,7 @@ storiesOf("Views / Products / Create product", module) taxTypes={taxTypes} weightUnit="kg" referencePages={[]} + referenceProducts={[]} onAssignReferencesClick={() => undefined} onCloseDialog={() => undefined} /> @@ -125,6 +127,7 @@ storiesOf("Views / Products / Create product", module) taxTypes={taxTypes} weightUnit="kg" referencePages={[]} + referenceProducts={[]} onAssignReferencesClick={() => undefined} onCloseDialog={() => undefined} /> diff --git a/src/storybook/stories/products/ProductUpdatePage.tsx b/src/storybook/stories/products/ProductUpdatePage.tsx index da6134cb2..76e5d8421 100644 --- a/src/storybook/stories/products/ProductUpdatePage.tsx +++ b/src/storybook/stories/products/ProductUpdatePage.tsx @@ -60,6 +60,7 @@ const props: ProductUpdatePageProps = { placeholderImage, product, referencePages: [], + referenceProducts: [], saveButtonBarState: "default", selectedChannelId: "123", taxTypes, diff --git a/src/storybook/stories/products/ProductVariantCreatePage.tsx b/src/storybook/stories/products/ProductVariantCreatePage.tsx index e75232693..518f9722e 100644 --- a/src/storybook/stories/products/ProductVariantCreatePage.tsx +++ b/src/storybook/stories/products/ProductVariantCreatePage.tsx @@ -36,6 +36,7 @@ storiesOf("Views / Products / Create product variant", module) warehouses={warehouseList} onWarehouseConfigure={() => undefined} referencePages={[]} + referenceProducts={[]} onAssignReferencesClick={() => undefined} onCloseDialog={() => undefined} /> @@ -76,6 +77,7 @@ storiesOf("Views / Products / Create product variant", module) warehouses={warehouseList} onWarehouseConfigure={() => undefined} referencePages={[]} + referenceProducts={[]} onAssignReferencesClick={() => undefined} onCloseDialog={() => undefined} /> @@ -97,6 +99,7 @@ storiesOf("Views / Products / Create product variant", module) warehouses={warehouseList} onWarehouseConfigure={() => undefined} referencePages={[]} + referenceProducts={[]} onAssignReferencesClick={() => undefined} onCloseDialog={() => undefined} /> @@ -121,6 +124,7 @@ storiesOf("Views / Products / Create product variant", module) warehouses={warehouseList} onWarehouseConfigure={() => undefined} referencePages={[]} + referenceProducts={[]} onAssignReferencesClick={() => undefined} onCloseDialog={() => undefined} /> @@ -142,6 +146,7 @@ storiesOf("Views / Products / Create product variant", module) warehouses={[]} onWarehouseConfigure={() => undefined} referencePages={[]} + referenceProducts={[]} onAssignReferencesClick={() => undefined} onCloseDialog={() => undefined} /> diff --git a/src/storybook/stories/products/ProductVariantPage.tsx b/src/storybook/stories/products/ProductVariantPage.tsx index 0b0f50086..39ea8d700 100644 --- a/src/storybook/stories/products/ProductVariantPage.tsx +++ b/src/storybook/stories/products/ProductVariantPage.tsx @@ -34,6 +34,7 @@ storiesOf("Views / Products / Product variant details", module) warehouses={warehouseList} onWarehouseConfigure={() => undefined} referencePages={[]} + referenceProducts={[]} onAssignReferencesClick={() => undefined} onCloseDialog={() => undefined} /> @@ -59,6 +60,7 @@ storiesOf("Views / Products / Product variant details", module) warehouses={warehouseList} onWarehouseConfigure={() => undefined} referencePages={[]} + referenceProducts={[]} onAssignReferencesClick={() => undefined} onCloseDialog={() => undefined} /> @@ -83,6 +85,7 @@ storiesOf("Views / Products / Product variant details", module) warehouses={[]} onWarehouseConfigure={() => undefined} referencePages={[]} + referenceProducts={[]} onAssignReferencesClick={() => undefined} onCloseDialog={() => undefined} /> @@ -135,6 +138,7 @@ storiesOf("Views / Products / Product variant details", module) warehouses={warehouseList} onWarehouseConfigure={() => undefined} referencePages={[]} + referenceProducts={[]} onAssignReferencesClick={() => undefined} onCloseDialog={() => undefined} /> diff --git a/src/types/globalTypes.ts b/src/types/globalTypes.ts index ca344b83b..9e2f51f7d 100644 --- a/src/types/globalTypes.ts +++ b/src/types/globalTypes.ts @@ -72,6 +72,7 @@ export enum AppTypeEnum { export enum AttributeEntityTypeEnum { PAGE = "PAGE", + PRODUCT = "PRODUCT", } export enum AttributeErrorCode { @@ -1333,6 +1334,7 @@ export interface OrderDraftFilterInput { customer?: string | null; created?: DateRangeInput | null; search?: string | null; + channels?: (string | null)[] | null; } export interface OrderFilterInput { diff --git a/src/utils/maps.ts b/src/utils/maps.ts index 5fe3bad3e..0367e8599 100644 --- a/src/utils/maps.ts +++ b/src/utils/maps.ts @@ -2,6 +2,7 @@ import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocomplet import { ShopInfo_shop_countries } from "@saleor/components/Shop/types/ShopInfo"; import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; import { MetadataItem } from "@saleor/fragments/types/MetadataItem"; +import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; import { Node } from "@saleor/types"; import { MetadataInput } from "@saleor/types/globalTypes"; @@ -14,6 +15,15 @@ export function mapCountriesToChoices( })); } +export function mapPagesToChoices( + pages: SearchPages_search_edges_node[] +): Array<SingleAutocompleteChoiceType | MultiAutocompleteChoiceType> { + return pages.map(page => ({ + label: page.title, + value: page.id + })); +} + export function mapNodeToChoice( nodes: Array<Node & Record<"name", string>> ): Array<SingleAutocompleteChoiceType | MultiAutocompleteChoiceType> { diff --git a/src/utils/metadata/types/UpdateMetadata.ts b/src/utils/metadata/types/UpdateMetadata.ts index cd763ddc6..40628865d 100644 --- a/src/utils/metadata/types/UpdateMetadata.ts +++ b/src/utils/metadata/types/UpdateMetadata.ts @@ -38,7 +38,7 @@ export interface UpdateMetadata_deleteMetadata_item_privateMetadata { } export interface UpdateMetadata_deleteMetadata_item { - __typename: "App" | "ShippingZone" | "ShippingMethod" | "Product" | "ProductType" | "Attribute" | "Category" | "ProductVariant" | "DigitalContent" | "Collection" | "Page" | "PageType" | "User" | "Checkout" | "Order" | "Fulfillment" | "Invoice"; + __typename: "App" | "ShippingZone" | "ShippingMethod" | "Product" | "ProductType" | "Attribute" | "Category" | "ProductVariant" | "DigitalContent" | "Collection" | "Page" | "PageType" | "MenuItem" | "Menu" | "User" | "Checkout" | "Order" | "Fulfillment" | "Invoice"; metadata: (UpdateMetadata_deleteMetadata_item_metadata | null)[]; privateMetadata: (UpdateMetadata_deleteMetadata_item_privateMetadata | null)[]; id: string; diff --git a/src/utils/metadata/types/UpdatePrivateMetadata.ts b/src/utils/metadata/types/UpdatePrivateMetadata.ts index db0b72fbe..173568570 100644 --- a/src/utils/metadata/types/UpdatePrivateMetadata.ts +++ b/src/utils/metadata/types/UpdatePrivateMetadata.ts @@ -38,7 +38,7 @@ export interface UpdatePrivateMetadata_deletePrivateMetadata_item_privateMetadat } export interface UpdatePrivateMetadata_deletePrivateMetadata_item { - __typename: "App" | "ShippingZone" | "ShippingMethod" | "Product" | "ProductType" | "Attribute" | "Category" | "ProductVariant" | "DigitalContent" | "Collection" | "Page" | "PageType" | "User" | "Checkout" | "Order" | "Fulfillment" | "Invoice"; + __typename: "App" | "ShippingZone" | "ShippingMethod" | "Product" | "ProductType" | "Attribute" | "Category" | "ProductVariant" | "DigitalContent" | "Collection" | "Page" | "PageType" | "MenuItem" | "Menu" | "User" | "Checkout" | "Order" | "Fulfillment" | "Invoice"; metadata: (UpdatePrivateMetadata_deletePrivateMetadata_item_metadata | null)[]; privateMetadata: (UpdatePrivateMetadata_deletePrivateMetadata_item_privateMetadata | null)[]; id: string; From 77ed12664d48a626f823d2815716012e6b40c9c9 Mon Sep 17 00:00:00 2001 From: Jakub Majorek <majorek.jakub@gmail.com> Date: Thu, 21 Jan 2021 09:54:53 +0100 Subject: [PATCH 11/35] Fix rare editorjs race condition (#953) --- package-lock.json | 12 ++++----- package.json | 2 +- .../RichTextEditor/RichTextEditor.tsx | 25 ++++++++++++++----- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index aed8e5dc2..032ae5a10 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2112,9 +2112,9 @@ } }, "@editorjs/editorjs": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/@editorjs/editorjs/-/editorjs-2.19.0.tgz", - "integrity": "sha512-8PUVaBZx69IrG8dNrE+FZbHSiRTR8ql8L/cmEi1mOdEdTqnOLq5Wv9dgemK00mBWEgNoavMAjtGQpItGknAa8A==", + "version": "2.19.1", + "resolved": "https://registry.npmjs.org/@editorjs/editorjs/-/editorjs-2.19.1.tgz", + "integrity": "sha512-5lN7r5B2NCE8VJdsS3poX3Qg9rNwzpxZ+6Jjif3hAVZTYpQwg5wXEpAHFNbuavS0T5Ji+0ID31DQFotVI4PosA==", "requires": { "codex-notifier": "^1.1.2", "codex-tooltip": "^1.0.1" @@ -8283,9 +8283,9 @@ "integrity": "sha512-DCp6xe/LGueJ1N5sXEwcBc3r3PyVkEEDNWCVigfvywAkeXcZMk9K41a31tkEFBW0Ptlwji6/JlAb49E3Yrxbtg==" }, "codex-tooltip": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/codex-tooltip/-/codex-tooltip-1.0.1.tgz", - "integrity": "sha512-1xLb1NZbxguNtf02xBRhDphq/EXvMMeEbY0ievjQTHqf8UjXsD41evGk9rqcbjpl+JOjNgtwnp1OaU/X/h6fhQ==" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/codex-tooltip/-/codex-tooltip-1.0.2.tgz", + "integrity": "sha512-oC+Bu5X/zyhbPydgMSLWKoM/+vkJMqaLWu3Dt/jZgXS3MWK23INwC5DMBrVXZSufAFk0i0SUni38k9rLMyZn/w==" }, "collapse-white-space": { "version": "1.0.6", diff --git a/package.json b/package.json index f29f25df4..69bbb607d 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "npm": ">=6.11.0" }, "dependencies": { - "@editorjs/editorjs": "^2.19.0", + "@editorjs/editorjs": "^2.19.1", "@editorjs/header": "^2.6.1", "@editorjs/image": "^2.6.0", "@editorjs/list": "^1.6.1", diff --git a/src/components/RichTextEditor/RichTextEditor.tsx b/src/components/RichTextEditor/RichTextEditor.tsx index addc34fb6..f3186edff 100644 --- a/src/components/RichTextEditor/RichTextEditor.tsx +++ b/src/components/RichTextEditor/RichTextEditor.tsx @@ -3,7 +3,6 @@ import FormControl from "@material-ui/core/FormControl"; import FormHelperText from "@material-ui/core/FormHelperText"; import InputLabel from "@material-ui/core/InputLabel"; import classNames from "classnames"; -import Undo from "editorjs-undo"; import React from "react"; import { RichTextEditorContentProps, tools } from "./RichTextEditorContent"; @@ -34,6 +33,8 @@ const RichTextEditor: React.FC<RichTextEditorProps> = ({ const [isFocused, setFocus] = React.useState(false); const editor = React.useRef<EditorJS>(); const editorContainer = React.useRef<HTMLDivElement>(); + const prevTogglePromise = React.useRef<Promise<boolean>>(); // used to await subsequent toggle invocations + React.useEffect( () => { if (data) { @@ -46,8 +47,10 @@ const RichTextEditor: React.FC<RichTextEditorProps> = ({ onChange(savedData); }, onReady: () => { - const undo = new Undo({ editor }); - undo.initialize(data); + // FIXME: This throws an error and is not working + // const undo = new Undo({ editor }); + // undo.initialize(data); + if (onReady) { onReady(); } @@ -62,10 +65,20 @@ const RichTextEditor: React.FC<RichTextEditorProps> = ({ // Rerender editor only if changed from undefined to defined state [data === undefined] ); + React.useEffect(() => { - if (editor.current?.readOnly) { - editor.current.readOnly.toggle(disabled); - } + const toggle = async () => { + if (editor.current?.readOnly) { + // readOnly.toggle() by itself does not enqueue the events and will result in a broken output if invocations overlap + // Remove this logic when this is fixed in EditorJS + if (prevTogglePromise.current instanceof Promise) { + await prevTogglePromise.current; + } + prevTogglePromise.current = editor.current.readOnly.toggle(disabled); + } + }; + + toggle(); }, [disabled]); return ( From 37bb52d08706d9df34be6d16e8e8fe51cb1fc774 Mon Sep 17 00:00:00 2001 From: Jakub Majorek <majorek.jakub@gmail.com> Date: Thu, 21 Jan 2021 10:07:16 +0100 Subject: [PATCH 12/35] Fix issues with stocks (#954) --- src/products/components/ProductStocks/ProductStocks.tsx | 2 +- src/products/components/ProductUpdatePage/form.tsx | 5 +++-- src/products/components/ProductVariantCreatePage/form.tsx | 8 +++++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/products/components/ProductStocks/ProductStocks.tsx b/src/products/components/ProductStocks/ProductStocks.tsx index b1564c0de..84a72acbd 100644 --- a/src/products/components/ProductStocks/ProductStocks.tsx +++ b/src/products/components/ProductStocks/ProductStocks.tsx @@ -268,7 +268,7 @@ const ProductStocks: React.FC<ProductStocksProps> = ({ {stock.label} </TableCell> <TableCell className={classes.colQuantity}> - {stock.data.quantityAllocated} + {stock.data?.quantityAllocated || 0} </TableCell> <TableCell className={classes.colQuantity}> <TextField diff --git a/src/products/components/ProductUpdatePage/form.tsx b/src/products/components/ProductUpdatePage/form.tsx index 8873c597e..fb1be2279 100644 --- a/src/products/components/ProductUpdatePage/form.tsx +++ b/src/products/components/ProductUpdatePage/form.tsx @@ -274,7 +274,9 @@ function useProductUpdateForm( const handleStockAdd = (id: string) => { triggerChange(); stocks.add({ - data: null, + data: { + quantityAllocated: 0 + }, id, label: opts.warehouses.find(warehouse => warehouse.id === id).name, value: "0" @@ -317,7 +319,6 @@ function useProductUpdateForm( ...data, ...getStocksData(product, stocks.data), ...getMetadata(data, isMetadataModified, isPrivateMetadataModified), - addStocks: [], attributes: attributes.data, attributesWithNewFileValue: attributesWithNewFileValue.data, description: description.current diff --git a/src/products/components/ProductVariantCreatePage/form.tsx b/src/products/components/ProductVariantCreatePage/form.tsx index bad8c22a9..7e7b669ea 100644 --- a/src/products/components/ProductVariantCreatePage/form.tsx +++ b/src/products/components/ProductVariantCreatePage/form.tsx @@ -30,7 +30,7 @@ import { FetchMoreProps, ReorderEvent } from "@saleor/types"; import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger"; import React from "react"; -import { ProductStockInput } from "../ProductStocks"; +import { ProductStockFormsetData, ProductStockInput } from "../ProductStocks"; export interface ProductVariantCreateFormData extends MetadataFormData { sku: string; @@ -112,7 +112,7 @@ function useProductVariantCreateForm( const form = useForm(initial); const attributes = useFormset(attributeInput); const attributesWithNewFileValue = useFormset<null, File>([]); - const stocks = useFormset<null, string>([]); + const stocks = useFormset<ProductStockFormsetData, string>([]); const channels = useFormset(channelsInput); const { makeChangeHandler: makeMetadataChangeHandler @@ -163,7 +163,9 @@ function useProductVariantCreateForm( const handleStockAdd = (id: string) => { triggerChange(); stocks.add({ - data: null, + data: { + quantityAllocated: 0 + }, id, label: opts.warehouses.find(warehouse => warehouse.id === id).name, value: "0" From 5dd63128b45bd0975cbb38b09469b812d6f73aeb Mon Sep 17 00:00:00 2001 From: Magdalena Markusik <magdalena.markusik@mirumee.com> Date: Thu, 21 Jan 2021 13:55:49 +0100 Subject: [PATCH 13/35] Fix timeline event expand icon spacing --- src/components/Timeline/TimelineEvent.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/Timeline/TimelineEvent.tsx b/src/components/Timeline/TimelineEvent.tsx index d379d6eb9..6182bccb9 100644 --- a/src/components/Timeline/TimelineEvent.tsx +++ b/src/components/Timeline/TimelineEvent.tsx @@ -25,7 +25,6 @@ const useStyles = makeStyles( paddingTop: theme.spacing(2) }, "&.Mui-expanded": { - borderColor: "red", margin: 0, minHeight: 0 }, @@ -48,7 +47,7 @@ const useStyles = makeStyles( "&> .MuiExpansionPanelSummary-expandIcon": { padding: 0, position: "absolute", - right: theme.spacing(18) + right: theme.spacing(20) }, margin: 0, minHeight: 0, From 430f80ac58cc565b2d8183a628bf5efce6e55b77 Mon Sep 17 00:00:00 2001 From: Magdalena Markusik <magdalena.markusik@mirumee.com> Date: Thu, 21 Jan 2021 13:56:19 +0100 Subject: [PATCH 14/35] Add mmissing no return button and logic --- .../components/OrderRefundPage/form.tsx | 3 +- .../OrderRefundReturnAmount.tsx | 27 ++++++++++++++++- .../OrderReturnPage/OrderReturnPage.tsx | 1 + src/orders/utils/data.ts | 30 +++++++++---------- src/orders/views/OrderReturn/utils.tsx | 6 +++- 5 files changed, 49 insertions(+), 18 deletions(-) diff --git a/src/orders/components/OrderRefundPage/form.tsx b/src/orders/components/OrderRefundPage/form.tsx index 3a1917527..6f95ff7c4 100644 --- a/src/orders/components/OrderRefundPage/form.tsx +++ b/src/orders/components/OrderRefundPage/form.tsx @@ -14,7 +14,8 @@ export enum OrderRefundType { } export enum OrderRefundAmountCalculationMode { AUTOMATIC = "automatic", - MANUAL = "manual" + MANUAL = "manual", + NONE = "none" } export interface OrderRefundData { diff --git a/src/orders/components/OrderRefundReturnAmount/OrderRefundReturnAmount.tsx b/src/orders/components/OrderRefundReturnAmount/OrderRefundReturnAmount.tsx index e67629555..7c0404c15 100644 --- a/src/orders/components/OrderRefundReturnAmount/OrderRefundReturnAmount.tsx +++ b/src/orders/components/OrderRefundReturnAmount/OrderRefundReturnAmount.tsx @@ -82,6 +82,7 @@ interface OrderRefundAmountProps { isReturn?: boolean; errors: OrderErrorFragment[]; amountData: OrderRefundAmountValuesProps; + allowNoRefund?: boolean; onChange: (event: React.ChangeEvent<any>) => void; onRefund: () => void; } @@ -96,7 +97,8 @@ const OrderRefundAmount: React.FC<OrderRefundAmountProps> = props => { onRefund, isReturn = false, amountData, - disableSubmitButton + disableSubmitButton, + allowNoRefund = false } = props; const classes = useStyles(props); const intl = useIntl(); @@ -144,6 +146,17 @@ const OrderRefundAmount: React.FC<OrderRefundAmountProps> = props => { onChange={onChange} name="amountCalculationMode" > + {allowNoRefund && ( + <FormControlLabel + disabled={disabled} + value={OrderRefundAmountCalculationMode.NONE} + control={<Radio color="primary" />} + label={intl.formatMessage({ + defaultMessage: "No refund", + description: "label" + })} + /> + )} <FormControlLabel disabled={disabled} value={OrderRefundAmountCalculationMode.AUTOMATIC} @@ -153,6 +166,18 @@ const OrderRefundAmount: React.FC<OrderRefundAmountProps> = props => { description: "label" })} /> + {data.amountCalculationMode === + OrderRefundAmountCalculationMode.NONE && ( + <> + <CardSpacer /> + <OrderRefundAmountValues + authorizedAmount={authorizedAmount} + previouslyRefunded={previouslyRefunded} + maxRefund={maxRefund} + shipmentCost={data.refundShipmentCosts && shipmentCost} + /> + </> + )} {data.amountCalculationMode === OrderRefundAmountCalculationMode.AUTOMATIC && ( <> diff --git a/src/orders/components/OrderReturnPage/OrderReturnPage.tsx b/src/orders/components/OrderReturnPage/OrderReturnPage.tsx index 457ae52e6..169f5f0ca 100644 --- a/src/orders/components/OrderReturnPage/OrderReturnPage.tsx +++ b/src/orders/components/OrderReturnPage/OrderReturnPage.tsx @@ -107,6 +107,7 @@ const OrderRefundPage: React.FC<OrderReturnPageProps> = props => { </div> <div> <OrderAmount + allowNoRefund isReturn amountData={getReturnProductsAmountValues(order, data)} data={data} diff --git a/src/orders/utils/data.ts b/src/orders/utils/data.ts index 72c1954b3..ae486349c 100644 --- a/src/orders/utils/data.ts +++ b/src/orders/utils/data.ts @@ -137,24 +137,24 @@ const getPartialProductsValue = ({ itemsQuantities: FormsetData<LineItemData, number>; orderLines: OrderDetails_order_lines[]; }) => - itemsQuantities.reduce((resultAmount, { id, value: quantity }) => { - const { - value: isItemToBeReplaced, - data: { isRefunded } - } = itemsToBeReplaced.find(getById(id)); + itemsQuantities.reduce( + (resultAmount, { id, value: quantity, data: { isRefunded } }) => { + const { value: isItemToBeReplaced } = itemsToBeReplaced.find(getById(id)); - if (quantity < 1 || isItemToBeReplaced || isRefunded) { - return resultAmount; - } + if (quantity < 1 || isItemToBeReplaced || isRefunded) { + return resultAmount; + } - const { selectedQuantity, unitPrice } = getItemPriceAndQuantity({ - id, - itemsQuantities, - orderLines - }); + const { selectedQuantity, unitPrice } = getItemPriceAndQuantity({ + id, + itemsQuantities, + orderLines + }); - return resultAmount + unitPrice.gross.amount * selectedQuantity; - }, 0); + return resultAmount + unitPrice.gross.amount * selectedQuantity; + }, + 0 + ); export function getRefundedLinesPriceSum( lines: OrderRefundData_order_lines[], diff --git a/src/orders/views/OrderReturn/utils.tsx b/src/orders/views/OrderReturn/utils.tsx index 02f02f834..16c8c4923 100644 --- a/src/orders/views/OrderReturn/utils.tsx +++ b/src/orders/views/OrderReturn/utils.tsx @@ -78,7 +78,11 @@ class ReturnFormDataParser { orderLines: OrderReturnLineInput[], fulfillmentLines: OrderReturnFulfillmentLineInput[] ) => { - if (!this.order.totalCaptured?.amount) { + if ( + !this.order.totalCaptured?.amount || + this.formData.amountCalculationMode === + OrderRefundAmountCalculationMode.NONE + ) { return false; } From 93238f8f8572d2c4edcd4991c53f1b46fa177fd8 Mon Sep 17 00:00:00 2001 From: Magdalena Markusik <magdalena.markusik@mirumee.com> Date: Thu, 21 Jan 2021 14:02:47 +0100 Subject: [PATCH 15/35] Update messages --- locale/defaultMessages.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index c876de88e..796678ae6 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -3839,6 +3839,10 @@ "context": "checkbox", "string": "Refund shipment costs" }, + "src_dot_orders_dot_components_dot_OrderRefundReturnAmount_dot_982301568": { + "context": "label", + "string": "No refund" + }, "src_dot_orders_dot_components_dot_OrderRefundReturnAmount_dot_amountTooBig": { "context": "Amount error message", "string": "Amount cannot be bigger than max refund" From 02c2fdf9275e6befed29abd586c23d875f995be7 Mon Sep 17 00:00:00 2001 From: Magdalena Markusik <magdalena.markusik@mirumee.com> Date: Thu, 21 Jan 2021 14:21:58 +0100 Subject: [PATCH 16/35] Fix some types and tests --- .../components/OrderReturnPage/form.tsx | 8 +++---- src/orders/utils/data.test.ts | 24 +++++++++---------- src/orders/utils/data.ts | 14 +++++------ 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/orders/components/OrderReturnPage/form.tsx b/src/orders/components/OrderReturnPage/form.tsx index 6f33ecac4..8fc520bad 100644 --- a/src/orders/components/OrderReturnPage/form.tsx +++ b/src/orders/components/OrderReturnPage/form.tsx @@ -47,8 +47,8 @@ export interface OrderReturnHandlers { export interface OrderReturnFormData extends OrderReturnData { itemsToBeReplaced: FormsetReplacementData; - fulfiledItemsQuantities: FormsetQuantityData; - unfulfiledItemsQuantities: FormsetQuantityData; + fulfilledItemsQuantities: FormsetQuantityData; + unfulfilledItemsQuantities: FormsetQuantityData; } export type OrderRefundSubmitData = OrderReturnFormData; @@ -184,9 +184,9 @@ function useOrderReturnForm( }; const data: OrderReturnFormData = { - fulfiledItemsQuantities: fulfiledItemsQuatities.data, + fulfilledItemsQuantities: fulfiledItemsQuatities.data, itemsToBeReplaced: itemsToBeReplaced.data, - unfulfiledItemsQuantities: unfulfiledItemsQuantites.data, + unfulfilledItemsQuantities: unfulfiledItemsQuantites.data, ...form.data }; diff --git a/src/orders/utils/data.test.ts b/src/orders/utils/data.test.ts index 152e08a04..7a6604625 100644 --- a/src/orders/utils/data.test.ts +++ b/src/orders/utils/data.test.ts @@ -738,7 +738,7 @@ describe("Get the total value of all replaced products", () => { } ]; - const unfulfiledItemsQuantities: FormsetData<LineItemData, number> = [ + const unfulfilledItemsQuantities: FormsetData<LineItemData, number> = [ { data: { isFulfillment: false, isRefunded: false }, id: "1", @@ -759,7 +759,7 @@ describe("Get the total value of all replaced products", () => { } ]; - const fulfiledItemsQuantities: FormsetData<LineItemData, number> = [ + const fulfilledItemsQuantities: FormsetData<LineItemData, number> = [ { data: { isFulfillment: true, isRefunded: false }, id: "4", @@ -861,8 +861,8 @@ describe("Get the total value of all replaced products", () => { }, { itemsToBeReplaced, - unfulfiledItemsQuantities, - fulfiledItemsQuantities + unfulfilledItemsQuantities, + fulfilledItemsQuantities } ); @@ -1085,42 +1085,42 @@ describe("Get the total value of all selected products", () => { } ]; - const unfulfiledItemsQuantities: FormsetData<null, number> = [ + const unfulfiledItemsQuantities: FormsetData<LineItemData, number> = [ { - data: null, + data: { isFulfillment: false, isRefunded: false }, id: "1", label: null, value: 0 }, { - data: null, + data: { isFulfillment: false, isRefunded: false }, id: "2", label: null, value: 2 }, { - data: null, + data: { isFulfillment: false, isRefunded: false }, id: "3", label: null, value: 1 } ]; - const fulfiledItemsQuantities: FormsetData<null, number> = [ + const fulfiledItemsQuantities: FormsetData<LineItemData, number> = [ { - data: null, + data: { isFulfillment: true, isRefunded: false }, id: "4", label: null, value: 4 }, { - data: null, + data: { isFulfillment: true, isRefunded: false }, id: "5", label: null, value: 0 }, { - data: null, + data: { isFulfillment: true, isRefunded: false }, id: "6", label: null, value: 3 diff --git a/src/orders/utils/data.ts b/src/orders/utils/data.ts index ae486349c..413912d54 100644 --- a/src/orders/utils/data.ts +++ b/src/orders/utils/data.ts @@ -53,8 +53,8 @@ const getItemPriceAndQuantity = ({ const selectItemPriceAndQuantity = ( order: OrderDetails_order, { - fulfiledItemsQuantities, - unfulfiledItemsQuantities + fulfilledItemsQuantities, + unfulfilledItemsQuantities }: Partial<OrderReturnFormData>, id: string, isFulfillment: boolean @@ -62,12 +62,12 @@ const selectItemPriceAndQuantity = ( isFulfillment ? getItemPriceAndQuantity({ id, - itemsQuantities: fulfiledItemsQuantities, + itemsQuantities: fulfilledItemsQuantities, orderLines: getAllOrderFulfilledLines(order) }) : getItemPriceAndQuantity({ id, - itemsQuantities: unfulfiledItemsQuantities, + itemsQuantities: unfulfilledItemsQuantities, orderLines: order.lines }); @@ -75,8 +75,8 @@ export const getReplacedProductsAmount = ( order: OrderDetails_order, { itemsToBeReplaced, - unfulfiledItemsQuantities, - fulfiledItemsQuantities + unfulfilledItemsQuantities, + fulfilledItemsQuantities }: Partial<OrderReturnFormData> ) => { if (!order || !itemsToBeReplaced.length) { @@ -94,7 +94,7 @@ export const getReplacedProductsAmount = ( const { unitPrice, selectedQuantity } = selectItemPriceAndQuantity( order, - { fulfiledItemsQuantities, unfulfiledItemsQuantities }, + { fulfilledItemsQuantities, unfulfilledItemsQuantities }, id, isFulfillment ); From d9250a7aeaa2c041b6c3990e161e764c2b7ef812 Mon Sep 17 00:00:00 2001 From: Magdalena Markusik <magdalena.markusik@mirumee.com> Date: Thu, 21 Jan 2021 14:25:00 +0100 Subject: [PATCH 17/35] Fix some more types cause there was a typo --- .../components/OrderRefundReturnAmount/utils.ts | 8 ++++---- .../components/OrderReturnPage/OrderReturnPage.tsx | 12 ++++++------ src/orders/utils/data.test.ts | 8 ++++---- src/orders/utils/data.ts | 6 +++--- src/orders/views/OrderReturn/utils.tsx | 8 ++++---- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/orders/components/OrderRefundReturnAmount/utils.ts b/src/orders/components/OrderRefundReturnAmount/utils.ts index 96f343487..7a89ec46c 100644 --- a/src/orders/components/OrderRefundReturnAmount/utils.ts +++ b/src/orders/components/OrderRefundReturnAmount/utils.ts @@ -149,8 +149,8 @@ export const getReturnProductsAmountValues = ( const authorizedAmount = getAuthorizedAmount(order); const { - fulfiledItemsQuantities, - unfulfiledItemsQuantities, + fulfilledItemsQuantities, + unfulfilledItemsQuantities, refundShipmentCosts } = formData; @@ -177,8 +177,8 @@ export const getReturnProductsAmountValues = ( return { ...getProductsAmountValues( order, - fulfiledItemsQuantities, - unfulfiledItemsQuantities, + fulfilledItemsQuantities, + unfulfilledItemsQuantities, refundShipmentCosts ), refundTotalAmount, diff --git a/src/orders/components/OrderReturnPage/OrderReturnPage.tsx b/src/orders/components/OrderReturnPage/OrderReturnPage.tsx index 169f5f0ca..2311961ab 100644 --- a/src/orders/components/OrderReturnPage/OrderReturnPage.tsx +++ b/src/orders/components/OrderReturnPage/OrderReturnPage.tsx @@ -46,11 +46,11 @@ const OrderRefundPage: React.FC<OrderReturnPageProps> = props => { return ( <OrderRefundForm order={order} onSubmit={onSubmit}> {({ data, handlers, change, submit }) => { - const { fulfiledItemsQuantities, unfulfiledItemsQuantities } = data; + const { fulfilledItemsQuantities, unfulfilledItemsQuantities } = data; const hasAnyItemsSelected = - fulfiledItemsQuantities.some(({ value }) => !!value) || - unfulfiledItemsQuantities.some(({ value }) => !!value); + fulfilledItemsQuantities.some(({ value }) => !!value) || + unfulfilledItemsQuantities.some(({ value }) => !!value); return ( <Container> @@ -66,13 +66,13 @@ const OrderRefundPage: React.FC<OrderReturnPageProps> = props => { /> <Grid> <div> - {!!data.unfulfiledItemsQuantities.length && ( + {!!data.unfulfilledItemsQuantities.length && ( <> <ItemsCard errors={errors} order={order} lines={getUnfulfilledLines(order)} - itemsQuantities={data.unfulfiledItemsQuantities} + itemsQuantities={data.unfulfilledItemsQuantities} itemsSelections={data.itemsToBeReplaced} onChangeQuantity={handlers.changeUnfulfiledItemsQuantity} onSetMaxQuantity={ @@ -92,7 +92,7 @@ const OrderRefundPage: React.FC<OrderReturnPageProps> = props => { order={order} fulfilmentId={id} lines={getParsedFulfiledLines(lines)} - itemsQuantities={data.fulfiledItemsQuantities} + itemsQuantities={data.fulfilledItemsQuantities} itemsSelections={data.itemsToBeReplaced} onChangeQuantity={handlers.changeFulfiledItemsQuantity} onSetMaxQuantity={handlers.handleSetMaximalFulfiledItemsQuantities( diff --git a/src/orders/utils/data.test.ts b/src/orders/utils/data.test.ts index 7a6604625..37efa117f 100644 --- a/src/orders/utils/data.test.ts +++ b/src/orders/utils/data.test.ts @@ -1085,7 +1085,7 @@ describe("Get the total value of all selected products", () => { } ]; - const unfulfiledItemsQuantities: FormsetData<LineItemData, number> = [ + const unfulfilledItemsQuantities: FormsetData<LineItemData, number> = [ { data: { isFulfillment: false, isRefunded: false }, id: "1", @@ -1106,7 +1106,7 @@ describe("Get the total value of all selected products", () => { } ]; - const fulfiledItemsQuantities: FormsetData<LineItemData, number> = [ + const fulfilledItemsQuantities: FormsetData<LineItemData, number> = [ { data: { isFulfillment: true, isRefunded: false }, id: "4", @@ -1196,8 +1196,8 @@ describe("Get the total value of all selected products", () => { }, { itemsToBeReplaced, - unfulfiledItemsQuantities, - fulfiledItemsQuantities + unfulfilledItemsQuantities, + fulfilledItemsQuantities } ); diff --git a/src/orders/utils/data.ts b/src/orders/utils/data.ts index 413912d54..8980507a6 100644 --- a/src/orders/utils/data.ts +++ b/src/orders/utils/data.ts @@ -107,20 +107,20 @@ export const getReplacedProductsAmount = ( export const getReturnSelectedProductsAmount = ( order: OrderDetails_order, - { itemsToBeReplaced, unfulfiledItemsQuantities, fulfiledItemsQuantities } + { itemsToBeReplaced, unfulfilledItemsQuantities, fulfilledItemsQuantities } ) => { if (!order) { return 0; } const unfulfilledItemsValue = getPartialProductsValue({ - itemsQuantities: unfulfiledItemsQuantities, + itemsQuantities: unfulfilledItemsQuantities, itemsToBeReplaced, orderLines: order.lines }); const fulfiledItemsValue = getPartialProductsValue({ - itemsQuantities: fulfiledItemsQuantities, + itemsQuantities: fulfilledItemsQuantities, itemsToBeReplaced, orderLines: getAllOrderFulfilledLines(order) }); diff --git a/src/orders/views/OrderReturn/utils.tsx b/src/orders/views/OrderReturn/utils.tsx index 16c8c4923..cc3c4ba7e 100644 --- a/src/orders/views/OrderReturn/utils.tsx +++ b/src/orders/views/OrderReturn/utils.tsx @@ -23,17 +23,17 @@ class ReturnFormDataParser { public getParsedData = (): OrderReturnProductsInput => { const { - fulfiledItemsQuantities, - unfulfiledItemsQuantities, + fulfilledItemsQuantities, + unfulfilledItemsQuantities, refundShipmentCosts } = this.formData; const fulfillmentLines = this.getParsedLineData< OrderReturnFulfillmentLineInput - >(fulfiledItemsQuantities, "fulfillmentLineId"); + >(fulfilledItemsQuantities, "fulfillmentLineId"); const orderLines = this.getParsedLineData<OrderReturnLineInput>( - unfulfiledItemsQuantities, + unfulfilledItemsQuantities, "orderLineId" ); From 10131163c17a8e624edb37ee8385f5c681f0924e Mon Sep 17 00:00:00 2001 From: Jakub Majorek <majorek.jakub@gmail.com> Date: Fri, 22 Jan 2021 12:13:40 +0100 Subject: [PATCH 18/35] SALEOR-2036 Drop descriptionJson and contentJson fields (#950) * Drop descriptionJson and contentJson fields * Bump EditorJS version * Update changelog * Update tests --- CHANGELOG.md | 1 + .../recording.har | 132 ++++++++++++++++++ schema.graphql | 99 ++++++------- .../CategoryDetailsForm.tsx | 6 +- .../components/CategoryUpdatePage/form.tsx | 2 +- src/categories/fixtures.ts | 2 +- src/categories/types/CategoryCreate.ts | 2 +- src/categories/types/CategoryDetails.ts | 2 +- src/categories/types/CategoryUpdate.ts | 2 +- src/categories/views/CategoryCreate.tsx | 2 +- src/categories/views/CategoryDetails.tsx | 2 +- .../CollectionDetails/CollectionDetails.tsx | 6 +- .../components/CollectionDetailsPage/form.tsx | 2 +- src/collections/fixtures.ts | 2 +- src/collections/types/CollectionDetails.ts | 2 +- src/collections/types/CollectionUpdate.ts | 2 +- src/collections/types/CreateCollection.ts | 2 +- src/collections/views/CollectionCreate.tsx | 2 +- src/collections/views/CollectionDetails.tsx | 2 +- src/fragments/categories.ts | 2 +- src/fragments/collections.ts | 2 +- src/fragments/pages.ts | 2 +- src/fragments/products.ts | 2 +- src/fragments/translations.ts | 20 +-- .../types/CategoryDetailsFragment.ts | 2 +- .../types/CategoryTranslationFragment.ts | 4 +- .../types/CollectionDetailsFragment.ts | 2 +- .../types/CollectionTranslationFragment.ts | 4 +- src/fragments/types/PageDetailsFragment.ts | 2 +- .../types/PageTranslatableFragment.ts | 4 +- .../types/PageTranslationFragment.ts | 4 +- src/fragments/types/Product.ts | 2 +- .../types/ProductTranslationFragment.ts | 4 +- src/pages/components/PageDetailsPage/form.tsx | 2 +- src/pages/components/PageInfo/PageInfo.tsx | 6 +- src/pages/fixtures.ts | 2 +- src/pages/types/PageCreate.ts | 2 +- src/pages/types/PageDetails.ts | 2 +- src/pages/types/PageUpdate.ts | 2 +- src/pages/views/PageCreate.tsx | 2 +- src/pages/views/PageDetails.tsx | 2 +- .../ProductDetailsForm/ProductDetailsForm.tsx | 9 +- .../components/ProductUpdatePage/form.tsx | 2 +- src/products/fixtures.ts | 2 +- .../types/ProductChannelListingUpdate.ts | 2 +- src/products/types/ProductCreate.ts | 2 +- src/products/types/ProductDetails.ts | 2 +- src/products/types/ProductImageCreate.ts | 2 +- src/products/types/ProductImageUpdate.ts | 2 +- src/products/types/ProductUpdate.ts | 2 +- src/products/types/ProductVariantReorder.ts | 2 +- .../types/ProductVariantSetDefault.ts | 2 +- src/products/types/SimpleProductUpdate.ts | 2 +- src/products/views/ProductCreate/handlers.ts | 2 +- src/products/views/ProductUpdate/handlers.ts | 2 +- .../__snapshots__/Stories.test.ts.snap | 10 +- .../stories/categories/CategoryCreatePage.tsx | 2 +- .../stories/categories/CategoryUpdatePage.tsx | 2 +- .../collections/CollectionCreatePage.tsx | 2 +- .../collections/CollectionDetailsPage.tsx | 2 +- .../TranslationsCategoriesPage.tsx | 8 +- .../TranslationsCollectionsPage.tsx | 8 +- .../TranslationsPagesPage.tsx | 8 +- .../TranslationsProductsPage.tsx | 8 +- src/translations/mutations.ts | 12 +- .../types/CategoryTranslationDetails.ts | 4 +- .../types/CategoryTranslations.ts | 4 +- .../types/CollectionTranslationDetails.ts | 4 +- .../types/CollectionTranslations.ts | 4 +- .../types/PageTranslationDetails.ts | 4 +- src/translations/types/PageTranslations.ts | 4 +- .../types/ProductTranslationDetails.ts | 4 +- src/translations/types/ProductTranslations.ts | 4 +- .../types/UpdateCategoryTranslations.ts | 4 +- .../types/UpdateCollectionTranslations.ts | 4 +- .../types/UpdatePageTranslations.ts | 4 +- .../types/UpdateProductTranslations.ts | 4 +- .../views/TranslationsCategories.tsx | 4 +- .../views/TranslationsCollections.tsx | 4 +- .../views/TranslationsEntities.tsx | 8 +- src/translations/views/TranslationsPages.tsx | 4 +- .../views/TranslationsProducts.tsx | 4 +- src/types/globalTypes.ts | 24 ++-- 83 files changed, 329 insertions(+), 212 deletions(-) create mode 100644 recordings/User_3768991250/will-not-be-logged-in-if-is-non-staff_2544500193/recording.har diff --git a/CHANGELOG.md b/CHANGELOG.md index 404849087..2b0927617 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ All notable, unreleased changes to this project will be documented in this file. - Guard against non-staff users logging in - #947 by @jwm0 - Add reference attributes - #917 by @orzechdev - Add product reference attributes - #948 by @orzechdev +- Drop descriptionJson and contentJson fields - #950 by @jwm0 # 2.11.1 diff --git a/recordings/User_3768991250/will-not-be-logged-in-if-is-non-staff_2544500193/recording.har b/recordings/User_3768991250/will-not-be-logged-in-if-is-non-staff_2544500193/recording.har new file mode 100644 index 000000000..c9845a746 --- /dev/null +++ b/recordings/User_3768991250/will-not-be-logged-in-if-is-non-staff_2544500193/recording.har @@ -0,0 +1,132 @@ +{ + "log": { + "_recordingName": "User/will not be logged in if is non-staff", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "0b09ec35ecae5b17a2ccda062b1d6ef5", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 602, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "content-type", + "value": "application/json" + }, + { + "_fromType": "array", + "name": "content-length", + "value": "602" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "localhost:8000" + } + ], + "headersSize": 254, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/json", + "params": [], + "text": "[{\"operationName\":\"TokenAuth\",\"variables\":{\"email\":\"client@example.com\",\"password\":\"password\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n isStaff\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation TokenAuth($email: String!, $password: String!) {\\n tokenCreate(email: $email, password: $password) {\\n errors: accountErrors {\\n field\\n message\\n __typename\\n }\\n csrfToken\\n token\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" + }, + "queryString": [], + "url": "http://localhost:8000/graphql/" + }, + "response": { + "bodySize": 616, + "content": { + "mimeType": "application/json", + "size": 616, + "text": "[{\"data\": {\"tokenCreate\": {\"errors\": [], \"csrfToken\": \"ztCCq2djodzVWeaynv8ifQbIIB4nrM6HyInqcZ4xqoaIEfOOKUEhAqw9rVR8Cr8L\", \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTEwNjY1MTcsImV4cCI6MTYxMTA2NjgxNywidG9rZW4iOiJ6MDc2QndLZkNEMmYiLCJlbWFpbCI6ImNsaWVudEBleGFtcGxlLmNvbSIsInR5cGUiOiJhY2Nlc3MiLCJ1c2VyX2lkIjoiVlhObGNqb3pNQT09IiwiaXNfc3RhZmYiOmZhbHNlfQ.lXsNnIBxZCCL843TTjn84lkWpE05o88F5q811ApjdKA\", \"user\": {\"id\": \"VXNlcjozMA==\", \"email\": \"client@example.com\", \"firstName\": \"\", \"lastName\": \"\", \"isStaff\": false, \"userPermissions\": [], \"avatar\": null, \"__typename\": \"User\"}, \"__typename\": \"CreateToken\"}}}]" + }, + "cookies": [ + { + "httpOnly": true, + "name": "refreshToken", + "path": "/", + "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTEwNjY1MTcsImV4cCI6MTYxMzY1ODUxNywidG9rZW4iOiJ6MDc2QndLZkNEMmYiLCJlbWFpbCI6ImNsaWVudEBleGFtcGxlLmNvbSIsInR5cGUiOiJyZWZyZXNoIiwidXNlcl9pZCI6IlZYTmxjam96TUE9PSIsImlzX3N0YWZmIjpmYWxzZSwiY3NyZlRva2VuIjoienRDQ3EyZGpvZHpWV2VheW52OGlmUWJJSUI0bnJNNkh5SW5xY1o0eHFvYUlFZk9PS1VFaEFxdzlyVlI4Q3I4TCJ9.hDMEK3HFSLol7rLd9dfaSCLTWgvetNDAFsb039L9PXQ" + } + ], + "headers": [ + { + "name": "date", + "value": "Tue, 19 Jan 2021 14:28:37 GMT" + }, + { + "name": "server", + "value": "WSGIServer/0.2 CPython/3.9.1" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "content-length", + "value": "616" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "referrer-policy", + "value": "same-origin" + }, + { + "_fromType": "array", + "name": "set-cookie", + "value": "refreshToken=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTEwNjY1MTcsImV4cCI6MTYxMzY1ODUxNywidG9rZW4iOiJ6MDc2QndLZkNEMmYiLCJlbWFpbCI6ImNsaWVudEBleGFtcGxlLmNvbSIsInR5cGUiOiJyZWZyZXNoIiwidXNlcl9pZCI6IlZYTmxjam96TUE9PSIsImlzX3N0YWZmIjpmYWxzZSwiY3NyZlRva2VuIjoienRDQ3EyZGpvZHpWV2VheW52OGlmUWJJSUI0bnJNNkh5SW5xY1o0eHFvYUlFZk9PS1VFaEFxdzlyVlI4Q3I4TCJ9.hDMEK3HFSLol7rLd9dfaSCLTWgvetNDAFsb039L9PXQ; HttpOnly; Path=/" + } + ], + "headersSize": 619, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2021-01-19T14:28:37.164Z", + "time": 468, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 468 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/schema.graphql b/schema.graphql index 1ef686df7..0ed146de4 100644 --- a/schema.graphql +++ b/schema.graphql @@ -671,13 +671,13 @@ type Category implements Node & ObjectWithMetadata { seoDescription: String id: ID! name: String! - description: String! - descriptionJson: JSONString! + description: JSONString! slug: String! parent: Category level: Int! privateMetadata: [MetadataItem]! metadata: [MetadataItem]! + descriptionJson: String @deprecated(reason: "Will be removed in Saleor 4.0. Use the `description` field instead.") ancestors(before: String, after: String, first: Int, last: Int): CategoryCountableConnection products(channel: String, before: String, after: String, first: Int, last: Int): ProductCountableConnection url: String @deprecated(reason: "This field will be removed after 2020-07-31.") @@ -721,8 +721,7 @@ input CategoryFilterInput { } input CategoryInput { - description: String - descriptionJson: JSONString + description: JSONString name: String slug: String seo: SeoInput @@ -747,8 +746,8 @@ type CategoryTranslatableContent implements Node { seoDescription: String id: ID! name: String! - description: String! - descriptionJson: JSONString! + description: JSONString! + descriptionJson: String @deprecated(reason: "Will be removed in Saleor 4.0. Use the `description` field instead.") translation(languageCode: LanguageCodeEnum!): CategoryTranslation category: Category } @@ -764,9 +763,9 @@ type CategoryTranslation implements Node { seoDescription: String id: ID! name: String! - description: String! - descriptionJson: JSONString! + description: JSONString! language: LanguageDisplay! + descriptionJson: String @deprecated(reason: "Will be removed in Saleor 4.0. Use the `description` field instead.") } type CategoryUpdate { @@ -1051,11 +1050,11 @@ type Collection implements Node & ObjectWithMetadata { seoDescription: String id: ID! name: String! - description: String! - descriptionJson: JSONString! + description: JSONString! slug: String! privateMetadata: [MetadataItem]! metadata: [MetadataItem]! + descriptionJson: String @deprecated(reason: "Will be removed in Saleor 4.0. Use the `description` field instead.") products(filter: ProductFilterInput, sortBy: ProductOrder, before: String, after: String, first: Int, last: Int): ProductCountableConnection backgroundImage(size: Int): Image translation(languageCode: LanguageCodeEnum!): CollectionTranslation @@ -1122,8 +1121,7 @@ input CollectionCreateInput { isPublished: Boolean name: String slug: String - description: String - descriptionJson: JSONString + description: JSONString backgroundImage: Upload backgroundImageAlt: String seo: SeoInput @@ -1165,8 +1163,7 @@ input CollectionInput { isPublished: Boolean name: String slug: String - description: String - descriptionJson: JSONString + description: JSONString backgroundImage: Upload backgroundImageAlt: String seo: SeoInput @@ -1208,8 +1205,8 @@ type CollectionTranslatableContent implements Node { seoDescription: String id: ID! name: String! - description: String! - descriptionJson: JSONString! + description: JSONString! + descriptionJson: String @deprecated(reason: "Will be removed in Saleor 4.0. Use the `description` field instead.") translation(languageCode: LanguageCodeEnum!): CollectionTranslation collection: Collection } @@ -1225,9 +1222,9 @@ type CollectionTranslation implements Node { seoDescription: String id: ID! name: String! - description: String! - descriptionJson: JSONString! + description: JSONString! language: LanguageDisplay! + descriptionJson: String @deprecated(reason: "Will be removed in Saleor 4.0. Use the `description` field instead.") } type CollectionUpdate { @@ -2837,7 +2834,7 @@ type Order implements Node & ObjectWithMetadata { shippingMethod: ShippingMethod shippingMethodName: String channel: Channel! - shippingPrice: TaxedMoney + shippingPrice: TaxedMoney! shippingTaxRate: Float! token: String! voucher: Voucher @@ -2857,16 +2854,16 @@ type Order implements Node & ObjectWithMetadata { availableShippingMethods: [ShippingMethod] invoices: [Invoice] number: String - isPaid: Boolean - paymentStatus: PaymentChargeStatusEnum - paymentStatusDisplay: String + isPaid: Boolean! + paymentStatus: PaymentChargeStatusEnum! + paymentStatusDisplay: String! payments: [Payment] - total: TaxedMoney - subtotal: TaxedMoney + total: TaxedMoney! + subtotal: TaxedMoney! statusDisplay: String canFinalize: Boolean! - totalAuthorized: Money - totalCaptured: Money + totalAuthorized: Money! + totalCaptured: Money! events: [OrderEvent] totalBalance: Money! userEmail: String @@ -3109,8 +3106,8 @@ type OrderLine implements Node { taxRate: Float! digitalContentUrl: DigitalContentUrl thumbnail(size: Int): Image - unitPrice: TaxedMoney - totalPrice: TaxedMoney + unitPrice: TaxedMoney! + totalPrice: TaxedMoney! variant: ProductVariant translatedProductName: String! translatedVariantName: String! @@ -3266,8 +3263,7 @@ type Page implements Node & ObjectWithMetadata { seoDescription: String id: ID! title: String! - content: String! - contentJson: JSONString! + content: JSONString! publicationDate: Date isPublished: Boolean! slug: String! @@ -3275,6 +3271,7 @@ type Page implements Node & ObjectWithMetadata { created: DateTime! privateMetadata: [MetadataItem]! metadata: [MetadataItem]! + contentJson: String! @deprecated(reason: "Will be removed in Saleor 4.0. Use the `content` field instead.") translation(languageCode: LanguageCodeEnum!): PageTranslation attributes: [SelectedAttribute!]! } @@ -3323,8 +3320,7 @@ type PageCreate { input PageCreateInput { slug: String title: String - content: String - contentJson: JSONString + content: JSONString attributes: [AttributeValueInput!] isPublished: Boolean publicationDate: String @@ -3370,8 +3366,7 @@ type PageInfo { input PageInput { slug: String title: String - content: String - contentJson: JSONString + content: JSONString attributes: [AttributeValueInput!] isPublished: Boolean publicationDate: String @@ -3402,8 +3397,8 @@ type PageTranslatableContent implements Node { seoDescription: String id: ID! title: String! - content: String! - contentJson: JSONString! + content: JSONString! + contentJson: String @deprecated(reason: "Will be removed in Saleor 4.0. Use the `content` field instead.") translation(languageCode: LanguageCodeEnum!): PageTranslation page: Page } @@ -3419,17 +3414,16 @@ type PageTranslation implements Node { seoDescription: String id: ID! title: String! - content: String! - contentJson: JSONString! + content: JSONString! language: LanguageDisplay! + contentJson: String @deprecated(reason: "Will be removed in Saleor 4.0. Use the `content` field instead.") } input PageTranslationInput { seoTitle: String seoDescription: String title: String - content: String - contentJson: JSONString + content: JSONString } type PageType implements Node & ObjectWithMetadata { @@ -3797,8 +3791,7 @@ type Product implements Node & ObjectWithMetadata { seoTitle: String seoDescription: String name: String! - description: String! - descriptionJson: JSONString! + description: JSONString! productType: ProductType! slug: String! category: Category @@ -3809,6 +3802,7 @@ type Product implements Node & ObjectWithMetadata { rating: Float privateMetadata: [MetadataItem]! metadata: [MetadataItem]! + descriptionJson: String @deprecated(reason: "Will be removed in Saleor 4.0. Use the `description` field instead.") url: String! @deprecated(reason: "This field will be removed after 2020-07-31.") thumbnail(size: Int): Image pricing: ProductPricingInfo @@ -3918,8 +3912,7 @@ input ProductCreateInput { category: ID chargeTaxes: Boolean collections: [ID] - description: String - descriptionJson: JSONString + description: JSONString name: String slug: String taxCode: String @@ -4050,8 +4043,7 @@ input ProductInput { category: ID chargeTaxes: Boolean collections: [ID] - description: String - descriptionJson: JSONString + description: JSONString name: String slug: String taxCode: String @@ -4104,8 +4096,8 @@ type ProductTranslatableContent implements Node { seoTitle: String seoDescription: String name: String! - description: String! - descriptionJson: JSONString! + description: JSONString! + descriptionJson: String @deprecated(reason: "Will be removed in Saleor 4.0. Use the `description` field instead.") translation(languageCode: LanguageCodeEnum!): ProductTranslation product: Product } @@ -4121,9 +4113,9 @@ type ProductTranslation implements Node { seoTitle: String seoDescription: String name: String! - description: String! - descriptionJson: JSONString! + description: JSONString! language: LanguageDisplay! + descriptionJson: String @deprecated(reason: "Will be removed in Saleor 4.0. Use the `description` field instead.") } type ProductType implements Node & ObjectWithMetadata { @@ -5289,8 +5281,7 @@ input TranslationInput { seoTitle: String seoDescription: String name: String - description: String - descriptionJson: JSONString + description: JSONString } scalar UUID @@ -5621,7 +5612,7 @@ type VoucherUpdate { voucher: Voucher } -type Warehouse implements Node { +type Warehouse implements Node & ObjectWithMetadata { id: ID! name: String! slug: String! @@ -5629,6 +5620,8 @@ type Warehouse implements Node { shippingZones(before: String, after: String, first: Int, last: Int): ShippingZoneCountableConnection! address: Address! email: String! + privateMetadata: [MetadataItem]! + metadata: [MetadataItem]! } input WarehouseAddressInput { diff --git a/src/categories/components/CategoryDetailsForm/CategoryDetailsForm.tsx b/src/categories/components/CategoryDetailsForm/CategoryDetailsForm.tsx index d1e984b25..037426539 100644 --- a/src/categories/components/CategoryDetailsForm/CategoryDetailsForm.tsx +++ b/src/categories/components/CategoryDetailsForm/CategoryDetailsForm.tsx @@ -33,7 +33,7 @@ export const CategoryDetailsForm: React.FC<CategoryDetailsFormProps> = ({ }) => { const intl = useIntl(); - const formErrors = getFormErrors(["name", "descriptionJson"], errors); + const formErrors = getFormErrors(["name", "description"], errors); return ( <Card> @@ -59,8 +59,8 @@ export const CategoryDetailsForm: React.FC<CategoryDetailsFormProps> = ({ <RichTextEditor data={data.description} disabled={disabled} - error={!!formErrors.descriptionJson} - helperText={getProductErrorMessage(formErrors.descriptionJson, intl)} + error={!!formErrors.description} + helperText={getProductErrorMessage(formErrors.description, intl)} label={intl.formatMessage({ defaultMessage: "Category Description" })} diff --git a/src/categories/components/CategoryUpdatePage/form.tsx b/src/categories/components/CategoryUpdatePage/form.tsx index c2fdb854d..6c9b3695d 100644 --- a/src/categories/components/CategoryUpdatePage/form.tsx +++ b/src/categories/components/CategoryUpdatePage/form.tsx @@ -56,7 +56,7 @@ function useCategoryUpdateForm( slug: category?.slug || "" }); const [description, changeDescription] = useRichText({ - initial: category?.descriptionJson, + initial: category?.description, triggerChange }); diff --git a/src/categories/fixtures.ts b/src/categories/fixtures.ts index f1464237a..473cbd63d 100644 --- a/src/categories/fixtures.ts +++ b/src/categories/fixtures.ts @@ -105,7 +105,7 @@ export const category: ( startCursor: "YXJyYXljb25uZWN0aW9uOjA=" } }, - descriptionJson: JSON.stringify(content), + description: JSON.stringify(content), id: "Q2F0ZWdvcnk6NA==", metadata: [ { diff --git a/src/categories/types/CategoryCreate.ts b/src/categories/types/CategoryCreate.ts index a7f5336e7..3a9b822b7 100644 --- a/src/categories/types/CategoryCreate.ts +++ b/src/categories/types/CategoryCreate.ts @@ -39,7 +39,7 @@ export interface CategoryCreate_categoryCreate_category { backgroundImage: CategoryCreate_categoryCreate_category_backgroundImage | null; name: string; slug: string; - descriptionJson: any; + description: any; seoDescription: string | null; seoTitle: string | null; parent: CategoryCreate_categoryCreate_category_parent | null; diff --git a/src/categories/types/CategoryDetails.ts b/src/categories/types/CategoryDetails.ts index 94e345be9..d1c245f13 100644 --- a/src/categories/types/CategoryDetails.ts +++ b/src/categories/types/CategoryDetails.ts @@ -165,7 +165,7 @@ export interface CategoryDetails_category { backgroundImage: CategoryDetails_category_backgroundImage | null; name: string; slug: string; - descriptionJson: any; + description: any; seoDescription: string | null; seoTitle: string | null; parent: CategoryDetails_category_parent | null; diff --git a/src/categories/types/CategoryUpdate.ts b/src/categories/types/CategoryUpdate.ts index 2a7bb4429..bfd4ec9c9 100644 --- a/src/categories/types/CategoryUpdate.ts +++ b/src/categories/types/CategoryUpdate.ts @@ -39,7 +39,7 @@ export interface CategoryUpdate_categoryUpdate_category { backgroundImage: CategoryUpdate_categoryUpdate_category_backgroundImage | null; name: string; slug: string; - descriptionJson: any; + description: any; seoDescription: string | null; seoTitle: string | null; parent: CategoryUpdate_categoryUpdate_category_parent | null; diff --git a/src/categories/views/CategoryCreate.tsx b/src/categories/views/CategoryCreate.tsx index c31329714..a0f271eb2 100644 --- a/src/categories/views/CategoryCreate.tsx +++ b/src/categories/views/CategoryCreate.tsx @@ -48,7 +48,7 @@ export const CategoryCreateView: React.FC<CategoryCreateViewProps> = ({ const result = await createCategory({ variables: { input: { - descriptionJson: JSON.stringify(formData.description), + description: JSON.stringify(formData.description), name: formData.name, seo: { description: formData.seoDescription, diff --git a/src/categories/views/CategoryDetails.tsx b/src/categories/views/CategoryDetails.tsx index 30a97ff02..e97265d89 100644 --- a/src/categories/views/CategoryDetails.tsx +++ b/src/categories/views/CategoryDetails.tsx @@ -188,7 +188,7 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({ id, input: { backgroundImageAlt: formData.backgroundImageAlt, - descriptionJson: JSON.stringify(formData.description), + description: JSON.stringify(formData.description), name: formData.name, seo: { description: formData.seoDescription, diff --git a/src/collections/components/CollectionDetails/CollectionDetails.tsx b/src/collections/components/CollectionDetails/CollectionDetails.tsx index 7e3db7e73..c52bd4c26 100644 --- a/src/collections/components/CollectionDetails/CollectionDetails.tsx +++ b/src/collections/components/CollectionDetails/CollectionDetails.tsx @@ -33,7 +33,7 @@ const CollectionDetails: React.FC<CollectionDetailsProps> = ({ }) => { const intl = useIntl(); - const formErrors = getFormErrors(["name", "descriptionJson"], errors); + const formErrors = getFormErrors(["name", "description"], errors); return ( <Card> @@ -57,8 +57,8 @@ const CollectionDetails: React.FC<CollectionDetailsProps> = ({ <FormSpacer /> <RichTextEditor data={data.description} - error={!!formErrors.descriptionJson} - helperText={getProductErrorMessage(formErrors.descriptionJson, intl)} + error={!!formErrors.description} + helperText={getProductErrorMessage(formErrors.description, intl)} label={intl.formatMessage(commonMessages.description)} name="description" disabled={disabled} diff --git a/src/collections/components/CollectionDetailsPage/form.tsx b/src/collections/components/CollectionDetailsPage/form.tsx index 1812f8f0d..d65589a04 100644 --- a/src/collections/components/CollectionDetailsPage/form.tsx +++ b/src/collections/components/CollectionDetailsPage/form.tsx @@ -68,7 +68,7 @@ function useCollectionUpdateForm( slug: collection?.slug || "" }); const [description, changeDescription] = useRichText({ - initial: collection?.descriptionJson, + initial: collection?.description, triggerChange }); diff --git a/src/collections/fixtures.ts b/src/collections/fixtures.ts index 8ef6784c9..034667468 100644 --- a/src/collections/fixtures.ts +++ b/src/collections/fixtures.ts @@ -157,7 +157,7 @@ export const collection: ( publicationDate: null } ], - descriptionJson: JSON.stringify(content), + description: JSON.stringify(content), id: "Q29sbGVjdGlvbjox", metadata: [ { diff --git a/src/collections/types/CollectionDetails.ts b/src/collections/types/CollectionDetails.ts index 2886931c0..3d8dca339 100644 --- a/src/collections/types/CollectionDetails.ts +++ b/src/collections/types/CollectionDetails.ts @@ -102,7 +102,7 @@ export interface CollectionDetails_collection { privateMetadata: (CollectionDetails_collection_privateMetadata | null)[]; backgroundImage: CollectionDetails_collection_backgroundImage | null; slug: string; - descriptionJson: any; + description: any; seoDescription: string | null; seoTitle: string | null; products: CollectionDetails_collection_products | null; diff --git a/src/collections/types/CollectionUpdate.ts b/src/collections/types/CollectionUpdate.ts index 831a0c915..d0f28cc7f 100644 --- a/src/collections/types/CollectionUpdate.ts +++ b/src/collections/types/CollectionUpdate.ts @@ -48,7 +48,7 @@ export interface CollectionUpdate_collectionUpdate_collection { privateMetadata: (CollectionUpdate_collectionUpdate_collection_privateMetadata | null)[]; backgroundImage: CollectionUpdate_collectionUpdate_collection_backgroundImage | null; slug: string; - descriptionJson: any; + description: any; seoDescription: string | null; seoTitle: string | null; } diff --git a/src/collections/types/CreateCollection.ts b/src/collections/types/CreateCollection.ts index 0644ca478..e660d3a20 100644 --- a/src/collections/types/CreateCollection.ts +++ b/src/collections/types/CreateCollection.ts @@ -48,7 +48,7 @@ export interface CreateCollection_collectionCreate_collection { privateMetadata: (CreateCollection_collectionCreate_collection_privateMetadata | null)[]; backgroundImage: CreateCollection_collectionCreate_collection_backgroundImage | null; slug: string; - descriptionJson: any; + description: any; seoDescription: string | null; seoTitle: string | null; } diff --git a/src/collections/views/CollectionCreate.tsx b/src/collections/views/CollectionCreate.tsx index c87bdcf52..9995af199 100644 --- a/src/collections/views/CollectionCreate.tsx +++ b/src/collections/views/CollectionCreate.tsx @@ -102,7 +102,7 @@ export const CollectionCreate: React.FC<CollectionCreateProps> = ({ input: { backgroundImage: formData.backgroundImage.value, backgroundImageAlt: formData.backgroundImageAlt, - descriptionJson: JSON.stringify(formData.description), + description: JSON.stringify(formData.description), name: formData.name, seo: { description: formData.seoDescription, diff --git a/src/collections/views/CollectionDetails.tsx b/src/collections/views/CollectionDetails.tsx index 9c7425d6e..7e661ea52 100644 --- a/src/collections/views/CollectionDetails.tsx +++ b/src/collections/views/CollectionDetails.tsx @@ -198,7 +198,7 @@ export const CollectionDetails: React.FC<CollectionDetailsProps> = ({ const handleUpdate = async (formData: CollectionUpdateData) => { const input: CollectionInput = { backgroundImageAlt: formData.backgroundImageAlt, - descriptionJson: JSON.stringify(formData.description), + description: JSON.stringify(formData.description), name: formData.name, seo: { description: formData.seoDescription, diff --git a/src/fragments/categories.ts b/src/fragments/categories.ts index 5545d6220..18bc64c9e 100644 --- a/src/fragments/categories.ts +++ b/src/fragments/categories.ts @@ -25,7 +25,7 @@ export const categoryDetailsFragment = gql` } name slug - descriptionJson + description seoDescription seoTitle parent { diff --git a/src/fragments/collections.ts b/src/fragments/collections.ts index 72b65b6f2..e3733609b 100644 --- a/src/fragments/collections.ts +++ b/src/fragments/collections.ts @@ -29,7 +29,7 @@ export const collectionDetailsFragment = gql` url } slug - descriptionJson + description seoDescription seoTitle } diff --git a/src/fragments/pages.ts b/src/fragments/pages.ts index d91a1a43d..b9f49c980 100644 --- a/src/fragments/pages.ts +++ b/src/fragments/pages.ts @@ -56,7 +56,7 @@ export const pageDetailsFragment = gql` ...PageFragment ...PageAttributesFragment ...MetadataFragment - contentJson + content seoTitle seoDescription publicationDate diff --git a/src/fragments/products.ts b/src/fragments/products.ts index 9cec7123b..fdbb99234 100644 --- a/src/fragments/products.ts +++ b/src/fragments/products.ts @@ -172,7 +172,7 @@ export const productFragmentDetails = gql` ...MetadataFragment name slug - descriptionJson + description seoTitle seoDescription rating diff --git a/src/fragments/translations.ts b/src/fragments/translations.ts index 301356ccb..aa041d6ef 100644 --- a/src/fragments/translations.ts +++ b/src/fragments/translations.ts @@ -4,7 +4,7 @@ export const categoryTranslationFragment = gql` fragment CategoryTranslationFragment on CategoryTranslatableContent { translation(languageCode: $language) { id - descriptionJson + description language { language } @@ -15,7 +15,7 @@ export const categoryTranslationFragment = gql` category { id name - descriptionJson + description seoDescription seoTitle } @@ -26,13 +26,13 @@ export const collectionTranslationFragment = gql` collection { id name - descriptionJson + description seoDescription seoTitle } translation(languageCode: $language) { id - descriptionJson + description language { language } @@ -47,13 +47,13 @@ export const productTranslationFragment = gql` product { id name - descriptionJson + description seoDescription seoTitle } translation(languageCode: $language) { id - descriptionJson + description language { code language @@ -118,14 +118,14 @@ export const pageTranslationFragment = gql` fragment PageTranslationFragment on PageTranslatableContent { page { id - contentJson + content seoDescription seoTitle title } translation(languageCode: $language) { id - contentJson + content seoDescription seoTitle title @@ -139,14 +139,14 @@ export const pageTranslationFragment = gql` export const pageTranslatableFragment = gql` fragment PageTranslatableFragment on PageTranslatableContent { id - contentJson + content seoDescription seoTitle title translation(languageCode: $language) { id - contentJson + content seoDescription seoTitle title diff --git a/src/fragments/types/CategoryDetailsFragment.ts b/src/fragments/types/CategoryDetailsFragment.ts index e1b2d5fbb..1190c711d 100644 --- a/src/fragments/types/CategoryDetailsFragment.ts +++ b/src/fragments/types/CategoryDetailsFragment.ts @@ -37,7 +37,7 @@ export interface CategoryDetailsFragment { backgroundImage: CategoryDetailsFragment_backgroundImage | null; name: string; slug: string; - descriptionJson: any; + description: any; seoDescription: string | null; seoTitle: string | null; parent: CategoryDetailsFragment_parent | null; diff --git a/src/fragments/types/CategoryTranslationFragment.ts b/src/fragments/types/CategoryTranslationFragment.ts index 8729af0f2..ed3f656cd 100644 --- a/src/fragments/types/CategoryTranslationFragment.ts +++ b/src/fragments/types/CategoryTranslationFragment.ts @@ -14,7 +14,7 @@ export interface CategoryTranslationFragment_translation_language { export interface CategoryTranslationFragment_translation { __typename: "CategoryTranslation"; id: string; - descriptionJson: any; + description: any; language: CategoryTranslationFragment_translation_language; name: string; seoDescription: string | null; @@ -25,7 +25,7 @@ export interface CategoryTranslationFragment_category { __typename: "Category"; id: string; name: string; - descriptionJson: any; + description: any; seoDescription: string | null; seoTitle: string | null; } diff --git a/src/fragments/types/CollectionDetailsFragment.ts b/src/fragments/types/CollectionDetailsFragment.ts index 07db07c20..ae823d602 100644 --- a/src/fragments/types/CollectionDetailsFragment.ts +++ b/src/fragments/types/CollectionDetailsFragment.ts @@ -46,7 +46,7 @@ export interface CollectionDetailsFragment { privateMetadata: (CollectionDetailsFragment_privateMetadata | null)[]; backgroundImage: CollectionDetailsFragment_backgroundImage | null; slug: string; - descriptionJson: any; + description: any; seoDescription: string | null; seoTitle: string | null; } diff --git a/src/fragments/types/CollectionTranslationFragment.ts b/src/fragments/types/CollectionTranslationFragment.ts index 3488c0ce8..0b27b4a6a 100644 --- a/src/fragments/types/CollectionTranslationFragment.ts +++ b/src/fragments/types/CollectionTranslationFragment.ts @@ -10,7 +10,7 @@ export interface CollectionTranslationFragment_collection { __typename: "Collection"; id: string; name: string; - descriptionJson: any; + description: any; seoDescription: string | null; seoTitle: string | null; } @@ -23,7 +23,7 @@ export interface CollectionTranslationFragment_translation_language { export interface CollectionTranslationFragment_translation { __typename: "CollectionTranslation"; id: string; - descriptionJson: any; + description: any; language: CollectionTranslationFragment_translation_language; name: string; seoDescription: string | null; diff --git a/src/fragments/types/PageDetailsFragment.ts b/src/fragments/types/PageDetailsFragment.ts index 2ba241f78..8750634ac 100644 --- a/src/fragments/types/PageDetailsFragment.ts +++ b/src/fragments/types/PageDetailsFragment.ts @@ -109,7 +109,7 @@ export interface PageDetailsFragment { pageType: PageDetailsFragment_pageType; metadata: (PageDetailsFragment_metadata | null)[]; privateMetadata: (PageDetailsFragment_privateMetadata | null)[]; - contentJson: any; + content: any; seoTitle: string | null; seoDescription: string | null; publicationDate: any | null; diff --git a/src/fragments/types/PageTranslatableFragment.ts b/src/fragments/types/PageTranslatableFragment.ts index dc214e951..8088d7a2f 100644 --- a/src/fragments/types/PageTranslatableFragment.ts +++ b/src/fragments/types/PageTranslatableFragment.ts @@ -17,7 +17,7 @@ export interface PageTranslatableFragment_translation_language { export interface PageTranslatableFragment_translation { __typename: "PageTranslation"; id: string; - contentJson: any; + content: any; seoDescription: string | null; seoTitle: string | null; title: string; @@ -27,7 +27,7 @@ export interface PageTranslatableFragment_translation { export interface PageTranslatableFragment { __typename: "PageTranslatableContent"; id: string; - contentJson: any; + content: any; seoDescription: string | null; seoTitle: string | null; title: string; diff --git a/src/fragments/types/PageTranslationFragment.ts b/src/fragments/types/PageTranslationFragment.ts index 24ba7cc69..a0e87d4da 100644 --- a/src/fragments/types/PageTranslationFragment.ts +++ b/src/fragments/types/PageTranslationFragment.ts @@ -11,7 +11,7 @@ import { LanguageCodeEnum } from "./../../types/globalTypes"; export interface PageTranslationFragment_page { __typename: "Page"; id: string; - contentJson: any; + content: any; seoDescription: string | null; seoTitle: string | null; title: string; @@ -26,7 +26,7 @@ export interface PageTranslationFragment_translation_language { export interface PageTranslationFragment_translation { __typename: "PageTranslation"; id: string; - contentJson: any; + content: any; seoDescription: string | null; seoTitle: string | null; title: string; diff --git a/src/fragments/types/Product.ts b/src/fragments/types/Product.ts index f9ba2f490..8792e55a4 100644 --- a/src/fragments/types/Product.ts +++ b/src/fragments/types/Product.ts @@ -253,7 +253,7 @@ export interface Product { privateMetadata: (Product_privateMetadata | null)[]; name: string; slug: string; - descriptionJson: any; + description: any; seoTitle: string | null; seoDescription: string | null; rating: number | null; diff --git a/src/fragments/types/ProductTranslationFragment.ts b/src/fragments/types/ProductTranslationFragment.ts index 91a3d8d8c..6bd9548fd 100644 --- a/src/fragments/types/ProductTranslationFragment.ts +++ b/src/fragments/types/ProductTranslationFragment.ts @@ -12,7 +12,7 @@ export interface ProductTranslationFragment_product { __typename: "Product"; id: string; name: string; - descriptionJson: any; + description: any; seoDescription: string | null; seoTitle: string | null; } @@ -26,7 +26,7 @@ export interface ProductTranslationFragment_translation_language { export interface ProductTranslationFragment_translation { __typename: "ProductTranslation"; id: string; - descriptionJson: any; + description: any; language: ProductTranslationFragment_translation_language; name: string; seoDescription: string | null; diff --git a/src/pages/components/PageDetailsPage/form.tsx b/src/pages/components/PageDetailsPage/form.tsx index 758c7d598..e1c21856f 100644 --- a/src/pages/components/PageDetailsPage/form.tsx +++ b/src/pages/components/PageDetailsPage/form.tsx @@ -122,7 +122,7 @@ function usePageForm( title: page?.title || "" }); const [content, changeContent] = useRichText({ - initial: pageExists ? page?.contentJson : null, + initial: pageExists ? page?.content : null, triggerChange }); diff --git a/src/pages/components/PageInfo/PageInfo.tsx b/src/pages/components/PageInfo/PageInfo.tsx index bf211d3e0..a6fe26708 100644 --- a/src/pages/components/PageInfo/PageInfo.tsx +++ b/src/pages/components/PageInfo/PageInfo.tsx @@ -39,7 +39,7 @@ const PageInfo: React.FC<PageInfoProps> = props => { const classes = useStyles(props); const intl = useIntl(); - const formErrors = getFormErrors(["title", "contentJson"], errors); + const formErrors = getFormErrors(["title", "content"], errors); return ( <Card className={classes.root}> @@ -64,8 +64,8 @@ const PageInfo: React.FC<PageInfoProps> = props => { <RichTextEditor data={data.content} disabled={disabled} - error={!!formErrors.contentJson} - helperText={getPageErrorMessage(formErrors.contentJson, intl)} + error={!!formErrors.content} + helperText={getPageErrorMessage(formErrors.content, intl)} label={intl.formatMessage({ defaultMessage: "Content", description: "page content" diff --git a/src/pages/fixtures.ts b/src/pages/fixtures.ts index 0d1cd2e0b..172787bec 100644 --- a/src/pages/fixtures.ts +++ b/src/pages/fixtures.ts @@ -145,7 +145,7 @@ export const page: PageDetails_page = { __typename: "SelectedAttribute" } ], - contentJson: JSON.stringify(content), + content: JSON.stringify(content), id: "Kzx152sEm==", isPublished: false, metadata: [ diff --git a/src/pages/types/PageCreate.ts b/src/pages/types/PageCreate.ts index 8499600b3..f91bff45a 100644 --- a/src/pages/types/PageCreate.ts +++ b/src/pages/types/PageCreate.ts @@ -117,7 +117,7 @@ export interface PageCreate_pageCreate_page { pageType: PageCreate_pageCreate_page_pageType; metadata: (PageCreate_pageCreate_page_metadata | null)[]; privateMetadata: (PageCreate_pageCreate_page_privateMetadata | null)[]; - contentJson: any; + content: any; seoTitle: string | null; seoDescription: string | null; publicationDate: any | null; diff --git a/src/pages/types/PageDetails.ts b/src/pages/types/PageDetails.ts index 3526d05ac..a596547d6 100644 --- a/src/pages/types/PageDetails.ts +++ b/src/pages/types/PageDetails.ts @@ -109,7 +109,7 @@ export interface PageDetails_page { pageType: PageDetails_page_pageType; metadata: (PageDetails_page_metadata | null)[]; privateMetadata: (PageDetails_page_privateMetadata | null)[]; - contentJson: any; + content: any; seoTitle: string | null; seoDescription: string | null; publicationDate: any | null; diff --git a/src/pages/types/PageUpdate.ts b/src/pages/types/PageUpdate.ts index 8b78848f1..a32762485 100644 --- a/src/pages/types/PageUpdate.ts +++ b/src/pages/types/PageUpdate.ts @@ -116,7 +116,7 @@ export interface PageUpdate_pageUpdate_page { pageType: PageUpdate_pageUpdate_page_pageType; metadata: (PageUpdate_pageUpdate_page_metadata | null)[]; privateMetadata: (PageUpdate_pageUpdate_page_privateMetadata | null)[]; - contentJson: any; + content: any; seoTitle: string | null; seoDescription: string | null; publicationDate: any | null; diff --git a/src/pages/views/PageCreate.tsx b/src/pages/views/PageCreate.tsx index 2ee83d15b..5289ddc4a 100644 --- a/src/pages/views/PageCreate.tsx +++ b/src/pages/views/PageCreate.tsx @@ -128,7 +128,7 @@ export const PageCreate: React.FC<PageCreateProps> = ({ params }) => { attributes: formData.attributes, updatedFileAttributes }), - contentJson: JSON.stringify(formData.content), + content: JSON.stringify(formData.content), isPublished: formData.isPublished, pageType: formData.pageType, publicationDate: formData.publicationDate, diff --git a/src/pages/views/PageDetails.tsx b/src/pages/views/PageDetails.tsx index 687ddaf6f..32d2eb608 100644 --- a/src/pages/views/PageDetails.tsx +++ b/src/pages/views/PageDetails.tsx @@ -53,7 +53,7 @@ const createPageInput = ( attributes: data.attributes, updatedFileAttributes }), - contentJson: JSON.stringify(data.content), + content: JSON.stringify(data.content), isPublished: data.isPublished, publicationDate: data.publicationDate, seo: { diff --git a/src/products/components/ProductDetailsForm/ProductDetailsForm.tsx b/src/products/components/ProductDetailsForm/ProductDetailsForm.tsx index da39c93f7..0b56cca56 100644 --- a/src/products/components/ProductDetailsForm/ProductDetailsForm.tsx +++ b/src/products/components/ProductDetailsForm/ProductDetailsForm.tsx @@ -37,10 +37,7 @@ export const ProductDetailsForm: React.FC<ProductDetailsFormProps> = ({ }) => { const intl = useIntl(); - const formErrors = getFormErrors( - ["name", "descriptionJson", "rating"], - errors - ); + const formErrors = getFormErrors(["name", "description", "rating"], errors); return ( <Card> @@ -65,8 +62,8 @@ export const ProductDetailsForm: React.FC<ProductDetailsFormProps> = ({ <RichTextEditor data={data.description} disabled={disabled} - error={!!formErrors.descriptionJson} - helperText={getProductErrorMessage(formErrors.descriptionJson, intl)} + error={!!formErrors.description} + helperText={getProductErrorMessage(formErrors.description, intl)} label={intl.formatMessage(commonMessages.description)} name="description" onChange={onDescriptionChange} diff --git a/src/products/components/ProductUpdatePage/form.tsx b/src/products/components/ProductUpdatePage/form.tsx index fb1be2279..5aad36e97 100644 --- a/src/products/components/ProductUpdatePage/form.tsx +++ b/src/products/components/ProductUpdatePage/form.tsx @@ -205,7 +205,7 @@ function useProductUpdateForm( const attributesWithNewFileValue = useFormset<null, File>([]); const stocks = useFormset(getStockInputFromProduct(product)); const [description, changeDescription] = useRichText({ - initial: product?.descriptionJson, + initial: product?.description, triggerChange }); diff --git a/src/products/fixtures.ts b/src/products/fixtures.ts index 602232486..df62ab8a2 100644 --- a/src/products/fixtures.ts +++ b/src/products/fixtures.ts @@ -204,7 +204,7 @@ export const product: ( } ], defaultVariant: { __typename: "ProductVariant", id: "pv75934" }, - descriptionJson: JSON.stringify(content), + description: JSON.stringify(content), id: "p10171", images: [ { diff --git a/src/products/types/ProductChannelListingUpdate.ts b/src/products/types/ProductChannelListingUpdate.ts index d12f9953d..795e8368c 100644 --- a/src/products/types/ProductChannelListingUpdate.ts +++ b/src/products/types/ProductChannelListingUpdate.ts @@ -253,7 +253,7 @@ export interface ProductChannelListingUpdate_productChannelListingUpdate_product privateMetadata: (ProductChannelListingUpdate_productChannelListingUpdate_product_privateMetadata | null)[]; name: string; slug: string; - descriptionJson: any; + description: any; seoTitle: string | null; seoDescription: string | null; rating: number | null; diff --git a/src/products/types/ProductCreate.ts b/src/products/types/ProductCreate.ts index 174158d4b..531a73af0 100644 --- a/src/products/types/ProductCreate.ts +++ b/src/products/types/ProductCreate.ts @@ -260,7 +260,7 @@ export interface ProductCreate_productCreate_product { privateMetadata: (ProductCreate_productCreate_product_privateMetadata | null)[]; name: string; slug: string; - descriptionJson: any; + description: any; seoTitle: string | null; seoDescription: string | null; rating: number | null; diff --git a/src/products/types/ProductDetails.ts b/src/products/types/ProductDetails.ts index a8ed59463..960e25330 100644 --- a/src/products/types/ProductDetails.ts +++ b/src/products/types/ProductDetails.ts @@ -253,7 +253,7 @@ export interface ProductDetails_product { privateMetadata: (ProductDetails_product_privateMetadata | null)[]; name: string; slug: string; - descriptionJson: any; + description: any; seoTitle: string | null; seoDescription: string | null; rating: number | null; diff --git a/src/products/types/ProductImageCreate.ts b/src/products/types/ProductImageCreate.ts index 704823003..13e0a706b 100644 --- a/src/products/types/ProductImageCreate.ts +++ b/src/products/types/ProductImageCreate.ts @@ -259,7 +259,7 @@ export interface ProductImageCreate_productImageCreate_product { privateMetadata: (ProductImageCreate_productImageCreate_product_privateMetadata | null)[]; name: string; slug: string; - descriptionJson: any; + description: any; seoTitle: string | null; seoDescription: string | null; rating: number | null; diff --git a/src/products/types/ProductImageUpdate.ts b/src/products/types/ProductImageUpdate.ts index 219de6624..2545adbd8 100644 --- a/src/products/types/ProductImageUpdate.ts +++ b/src/products/types/ProductImageUpdate.ts @@ -259,7 +259,7 @@ export interface ProductImageUpdate_productImageUpdate_product { privateMetadata: (ProductImageUpdate_productImageUpdate_product_privateMetadata | null)[]; name: string; slug: string; - descriptionJson: any; + description: any; seoTitle: string | null; seoDescription: string | null; rating: number | null; diff --git a/src/products/types/ProductUpdate.ts b/src/products/types/ProductUpdate.ts index 661a7a0cf..b2780eff1 100644 --- a/src/products/types/ProductUpdate.ts +++ b/src/products/types/ProductUpdate.ts @@ -260,7 +260,7 @@ export interface ProductUpdate_productUpdate_product { privateMetadata: (ProductUpdate_productUpdate_product_privateMetadata | null)[]; name: string; slug: string; - descriptionJson: any; + description: any; seoTitle: string | null; seoDescription: string | null; rating: number | null; diff --git a/src/products/types/ProductVariantReorder.ts b/src/products/types/ProductVariantReorder.ts index 68fb0c764..5070100c3 100644 --- a/src/products/types/ProductVariantReorder.ts +++ b/src/products/types/ProductVariantReorder.ts @@ -259,7 +259,7 @@ export interface ProductVariantReorder_productVariantReorder_product { privateMetadata: (ProductVariantReorder_productVariantReorder_product_privateMetadata | null)[]; name: string; slug: string; - descriptionJson: any; + description: any; seoTitle: string | null; seoDescription: string | null; rating: number | null; diff --git a/src/products/types/ProductVariantSetDefault.ts b/src/products/types/ProductVariantSetDefault.ts index ed095108d..d59a4aaca 100644 --- a/src/products/types/ProductVariantSetDefault.ts +++ b/src/products/types/ProductVariantSetDefault.ts @@ -259,7 +259,7 @@ export interface ProductVariantSetDefault_productVariantSetDefault_product { privateMetadata: (ProductVariantSetDefault_productVariantSetDefault_product_privateMetadata | null)[]; name: string; slug: string; - descriptionJson: any; + description: any; seoTitle: string | null; seoDescription: string | null; rating: number | null; diff --git a/src/products/types/SimpleProductUpdate.ts b/src/products/types/SimpleProductUpdate.ts index 2703b3ed8..b4d7a0a70 100644 --- a/src/products/types/SimpleProductUpdate.ts +++ b/src/products/types/SimpleProductUpdate.ts @@ -260,7 +260,7 @@ export interface SimpleProductUpdate_productUpdate_product { privateMetadata: (SimpleProductUpdate_productUpdate_product_privateMetadata | null)[]; name: string; slug: string; - descriptionJson: any; + description: any; seoTitle: string | null; seoDescription: string | null; rating: number | null; diff --git a/src/products/views/ProductCreate/handlers.ts b/src/products/views/ProductCreate/handlers.ts index ad7f7233f..4cbe4b801 100644 --- a/src/products/views/ProductCreate/handlers.ts +++ b/src/products/views/ProductCreate/handlers.ts @@ -108,7 +108,7 @@ export function createHandler( category: formData.category, chargeTaxes: formData.chargeTaxes, collections: formData.collections, - descriptionJson: JSON.stringify(formData.description), + description: JSON.stringify(formData.description), name: formData.name, productType: formData.productType?.id, rating: formData.rating, diff --git a/src/products/views/ProductUpdate/handlers.ts b/src/products/views/ProductUpdate/handlers.ts index 7a9c3d7fb..47b36da63 100644 --- a/src/products/views/ProductUpdate/handlers.ts +++ b/src/products/views/ProductUpdate/handlers.ts @@ -185,7 +185,7 @@ export function createUpdateHandler( category: data.category, chargeTaxes: data.chargeTaxes, collections: data.collections, - descriptionJson: JSON.stringify(data.description), + description: JSON.stringify(data.description), name: data.name, rating: data.rating, seo: { diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index 2c714118b..a64488aef 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -136744,17 +136744,19 @@ exports[`Storyshots Views / Pages / Page details form errors 1`] = ` data-test-id="content" > <label - class="MuiFormLabel-root-id MuiInputLabel-root-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-shrink-id MuiInputLabel-outlined-id MuiFormLabel-focused-id MuiInputLabel-focused-id" + class="MuiFormLabel-root-id MuiInputLabel-root-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-shrink-id MuiInputLabel-outlined-id MuiFormLabel-error-id MuiInputLabel-error-id MuiFormLabel-focused-id MuiInputLabel-focused-id" data-shrink="true" > Content </label> <div - class="RichTextEditor-editor-id RichTextEditor-root-id" + class="RichTextEditor-editor-id RichTextEditor-root-id RichTextEditor-rootError-id" /> <p - class="MuiFormHelperText-root-id MuiFormHelperText-contained-id" - /> + class="MuiFormHelperText-root-id MuiFormHelperText-contained-id MuiFormHelperText-error-id" + > + Invalid value + </p> </div> </div> </div> diff --git a/src/storybook/stories/categories/CategoryCreatePage.tsx b/src/storybook/stories/categories/CategoryCreatePage.tsx index 456237d69..2492123a1 100644 --- a/src/storybook/stories/categories/CategoryCreatePage.tsx +++ b/src/storybook/stories/categories/CategoryCreatePage.tsx @@ -31,7 +31,7 @@ storiesOf("Views / Categories / Create category", module) }, { code: ProductErrorCode.REQUIRED, - field: "descriptionJson" + field: "description" } ].map(err => ({ __typename: "ProductError", diff --git a/src/storybook/stories/categories/CategoryUpdatePage.tsx b/src/storybook/stories/categories/CategoryUpdatePage.tsx index 72cb432db..b3a1b8eca 100644 --- a/src/storybook/stories/categories/CategoryUpdatePage.tsx +++ b/src/storybook/stories/categories/CategoryUpdatePage.tsx @@ -100,7 +100,7 @@ storiesOf("Views / Categories / Update category", module) }, { code: ProductErrorCode.REQUIRED, - field: "descriptionJson" + field: "description" } ].map(err => ({ __typename: "ProductError", diff --git a/src/storybook/stories/collections/CollectionCreatePage.tsx b/src/storybook/stories/collections/CollectionCreatePage.tsx index 3d910135e..50715681e 100644 --- a/src/storybook/stories/collections/CollectionCreatePage.tsx +++ b/src/storybook/stories/collections/CollectionCreatePage.tsx @@ -39,7 +39,7 @@ storiesOf("Views / Collections / Create collection", module) }, { code: CollectionErrorCode.REQUIRED, - field: "descriptionJson" + field: "description" } ].map(err => ({ __typename: "CollectionError", diff --git a/src/storybook/stories/collections/CollectionDetailsPage.tsx b/src/storybook/stories/collections/CollectionDetailsPage.tsx index e4da1623d..41b2e9c4a 100644 --- a/src/storybook/stories/collections/CollectionDetailsPage.tsx +++ b/src/storybook/stories/collections/CollectionDetailsPage.tsx @@ -57,7 +57,7 @@ storiesOf("Views / Collections / Collection details", module) }, { code: CollectionErrorCode.REQUIRED, - field: "descriptionJson" + field: "description" } ].map(err => ({ __typename: "CollectionError", diff --git a/src/translations/components/TranslationsCategoriesPage/TranslationsCategoriesPage.tsx b/src/translations/components/TranslationsCategoriesPage/TranslationsCategoriesPage.tsx index 81cf94eff..66a529d52 100644 --- a/src/translations/components/TranslationsCategoriesPage/TranslationsCategoriesPage.tsx +++ b/src/translations/components/TranslationsCategoriesPage/TranslationsCategoriesPage.tsx @@ -18,7 +18,7 @@ export interface TranslationsCategoriesPageProps } export const fieldNames = { - descriptionJson: "description", + description: "description", name: "name", seoDescription: "seoDescription", seoTitle: "seoTitle" @@ -79,10 +79,10 @@ const TranslationsCategoriesPage: React.FC<TranslationsCategoriesPageProps> = ({ }, { displayName: intl.formatMessage(commonMessages.description), - name: fieldNames.descriptionJson, - translation: data?.translation?.descriptionJson || null, + name: fieldNames.description, + translation: data?.translation?.description || null, type: "rich" as "rich", - value: data?.category?.descriptionJson + value: data?.category?.description } ]} saveButtonState={saveButtonState} diff --git a/src/translations/components/TranslationsCollectionsPage/TranslationsCollectionsPage.tsx b/src/translations/components/TranslationsCollectionsPage/TranslationsCollectionsPage.tsx index 41543ed83..ef3c962a6 100644 --- a/src/translations/components/TranslationsCollectionsPage/TranslationsCollectionsPage.tsx +++ b/src/translations/components/TranslationsCollectionsPage/TranslationsCollectionsPage.tsx @@ -18,7 +18,7 @@ export interface TranslationsCollectionsPageProps } export const fieldNames = { - descriptionJson: "description", + description: "description", name: "name", seoDescription: "seoDescription", seoTitle: "seoTitle" @@ -80,10 +80,10 @@ const TranslationsCollectionsPage: React.FC<TranslationsCollectionsPageProps> = }, { displayName: intl.formatMessage(commonMessages.description), - name: fieldNames.descriptionJson, - translation: data?.translation?.descriptionJson || null, + name: fieldNames.description, + translation: data?.translation?.description || null, type: "rich" as "rich", - value: data?.collection?.descriptionJson + value: data?.collection?.description } ]} saveButtonState={saveButtonState} diff --git a/src/translations/components/TranslationsPagesPage/TranslationsPagesPage.tsx b/src/translations/components/TranslationsPagesPage/TranslationsPagesPage.tsx index 64cedd9ff..2c8661cf8 100644 --- a/src/translations/components/TranslationsPagesPage/TranslationsPagesPage.tsx +++ b/src/translations/components/TranslationsPagesPage/TranslationsPagesPage.tsx @@ -18,7 +18,7 @@ export interface TranslationsPagesPageProps } export const fieldNames = { - contentJson: "content", + content: "content", seoDescription: "seoDescription", seoTitle: "seoTitle", title: "title" @@ -82,10 +82,10 @@ const TranslationsPagesPage: React.FC<TranslationsPagesPageProps> = ({ defaultMessage: "Content", description: "page content" }), - name: fieldNames.contentJson, - translation: data?.translation?.contentJson || null, + name: fieldNames.content, + translation: data?.translation?.content || null, type: "rich" as "rich", - value: data?.page?.contentJson + value: data?.page?.content } ]} saveButtonState={saveButtonState} diff --git a/src/translations/components/TranslationsProductsPage/TranslationsProductsPage.tsx b/src/translations/components/TranslationsProductsPage/TranslationsProductsPage.tsx index 3b40676df..93e2d850c 100644 --- a/src/translations/components/TranslationsProductsPage/TranslationsProductsPage.tsx +++ b/src/translations/components/TranslationsProductsPage/TranslationsProductsPage.tsx @@ -18,7 +18,7 @@ export interface TranslationsProductsPageProps } export const fieldNames = { - descriptionJson: "description", + description: "description", name: "name", seoDescription: "seoDescription", seoTitle: "seoTitle" @@ -82,10 +82,10 @@ const TranslationsProductsPage: React.FC<TranslationsProductsPageProps> = ({ displayName: intl.formatMessage({ defaultMessage: "Description" }), - name: fieldNames.descriptionJson, - translation: data?.translation?.descriptionJson || null, + name: fieldNames.description, + translation: data?.translation?.description || null, type: "rich" as "rich", - value: data?.product?.descriptionJson + value: data?.product?.description } ]} saveButtonState={saveButtonState} diff --git a/src/translations/mutations.ts b/src/translations/mutations.ts index 5c2777a4a..20fcd85bb 100644 --- a/src/translations/mutations.ts +++ b/src/translations/mutations.ts @@ -53,12 +53,12 @@ const updateProductTranslations = gql` product { id name - descriptionJson + description seoDescription seoTitle translation(languageCode: $language) { id - descriptionJson + description language { code language @@ -90,12 +90,12 @@ const updateCategoryTranslations = gql` category { id name - descriptionJson + description seoDescription seoTitle translation(languageCode: $language) { id - descriptionJson + description language { language } @@ -126,12 +126,12 @@ const updateCollectionTranslations = gql` collection { id name - descriptionJson + description seoDescription seoTitle translation(languageCode: $language) { id - descriptionJson + description language { language } diff --git a/src/translations/types/CategoryTranslationDetails.ts b/src/translations/types/CategoryTranslationDetails.ts index 46d3494ca..b3efd65b2 100644 --- a/src/translations/types/CategoryTranslationDetails.ts +++ b/src/translations/types/CategoryTranslationDetails.ts @@ -20,7 +20,7 @@ export interface CategoryTranslationDetails_translation_CategoryTranslatableCont export interface CategoryTranslationDetails_translation_CategoryTranslatableContent_translation { __typename: "CategoryTranslation"; id: string; - descriptionJson: any; + description: any; language: CategoryTranslationDetails_translation_CategoryTranslatableContent_translation_language; name: string; seoDescription: string | null; @@ -31,7 +31,7 @@ export interface CategoryTranslationDetails_translation_CategoryTranslatableCont __typename: "Category"; id: string; name: string; - descriptionJson: any; + description: any; seoDescription: string | null; seoTitle: string | null; } diff --git a/src/translations/types/CategoryTranslations.ts b/src/translations/types/CategoryTranslations.ts index fe128719b..8330f1b59 100644 --- a/src/translations/types/CategoryTranslations.ts +++ b/src/translations/types/CategoryTranslations.ts @@ -20,7 +20,7 @@ export interface CategoryTranslations_translations_edges_node_CategoryTranslatab export interface CategoryTranslations_translations_edges_node_CategoryTranslatableContent_translation { __typename: "CategoryTranslation"; id: string; - descriptionJson: any; + description: any; language: CategoryTranslations_translations_edges_node_CategoryTranslatableContent_translation_language; name: string; seoDescription: string | null; @@ -31,7 +31,7 @@ export interface CategoryTranslations_translations_edges_node_CategoryTranslatab __typename: "Category"; id: string; name: string; - descriptionJson: any; + description: any; seoDescription: string | null; seoTitle: string | null; } diff --git a/src/translations/types/CollectionTranslationDetails.ts b/src/translations/types/CollectionTranslationDetails.ts index e27056891..f8ce23280 100644 --- a/src/translations/types/CollectionTranslationDetails.ts +++ b/src/translations/types/CollectionTranslationDetails.ts @@ -16,7 +16,7 @@ export interface CollectionTranslationDetails_translation_CollectionTranslatable __typename: "Collection"; id: string; name: string; - descriptionJson: any; + description: any; seoDescription: string | null; seoTitle: string | null; } @@ -29,7 +29,7 @@ export interface CollectionTranslationDetails_translation_CollectionTranslatable export interface CollectionTranslationDetails_translation_CollectionTranslatableContent_translation { __typename: "CollectionTranslation"; id: string; - descriptionJson: any; + description: any; language: CollectionTranslationDetails_translation_CollectionTranslatableContent_translation_language; name: string; seoDescription: string | null; diff --git a/src/translations/types/CollectionTranslations.ts b/src/translations/types/CollectionTranslations.ts index 162d0f5a4..f25b8d4d1 100644 --- a/src/translations/types/CollectionTranslations.ts +++ b/src/translations/types/CollectionTranslations.ts @@ -16,7 +16,7 @@ export interface CollectionTranslations_translations_edges_node_CollectionTransl __typename: "Collection"; id: string; name: string; - descriptionJson: any; + description: any; seoDescription: string | null; seoTitle: string | null; } @@ -29,7 +29,7 @@ export interface CollectionTranslations_translations_edges_node_CollectionTransl export interface CollectionTranslations_translations_edges_node_CollectionTranslatableContent_translation { __typename: "CollectionTranslation"; id: string; - descriptionJson: any; + description: any; language: CollectionTranslations_translations_edges_node_CollectionTranslatableContent_translation_language; name: string; seoDescription: string | null; diff --git a/src/translations/types/PageTranslationDetails.ts b/src/translations/types/PageTranslationDetails.ts index b71c2a1ac..a40b072ca 100644 --- a/src/translations/types/PageTranslationDetails.ts +++ b/src/translations/types/PageTranslationDetails.ts @@ -15,7 +15,7 @@ export interface PageTranslationDetails_translation_ProductTranslatableContent { export interface PageTranslationDetails_translation_PageTranslatableContent_page { __typename: "Page"; id: string; - contentJson: any; + content: any; seoDescription: string | null; seoTitle: string | null; title: string; @@ -30,7 +30,7 @@ export interface PageTranslationDetails_translation_PageTranslatableContent_tran export interface PageTranslationDetails_translation_PageTranslatableContent_translation { __typename: "PageTranslation"; id: string; - contentJson: any; + content: any; seoDescription: string | null; seoTitle: string | null; title: string; diff --git a/src/translations/types/PageTranslations.ts b/src/translations/types/PageTranslations.ts index 9b6cec9b8..a2a9aa242 100644 --- a/src/translations/types/PageTranslations.ts +++ b/src/translations/types/PageTranslations.ts @@ -15,7 +15,7 @@ export interface PageTranslations_translations_edges_node_ProductTranslatableCon export interface PageTranslations_translations_edges_node_PageTranslatableContent_page { __typename: "Page"; id: string; - contentJson: any; + content: any; seoDescription: string | null; seoTitle: string | null; title: string; @@ -30,7 +30,7 @@ export interface PageTranslations_translations_edges_node_PageTranslatableConten export interface PageTranslations_translations_edges_node_PageTranslatableContent_translation { __typename: "PageTranslation"; id: string; - contentJson: any; + content: any; seoDescription: string | null; seoTitle: string | null; title: string; diff --git a/src/translations/types/ProductTranslationDetails.ts b/src/translations/types/ProductTranslationDetails.ts index 08d1fbebe..d7f4b0dc7 100644 --- a/src/translations/types/ProductTranslationDetails.ts +++ b/src/translations/types/ProductTranslationDetails.ts @@ -16,7 +16,7 @@ export interface ProductTranslationDetails_translation_ProductTranslatableConten __typename: "Product"; id: string; name: string; - descriptionJson: any; + description: any; seoDescription: string | null; seoTitle: string | null; } @@ -30,7 +30,7 @@ export interface ProductTranslationDetails_translation_ProductTranslatableConten export interface ProductTranslationDetails_translation_ProductTranslatableContent_translation { __typename: "ProductTranslation"; id: string; - descriptionJson: any; + description: any; language: ProductTranslationDetails_translation_ProductTranslatableContent_translation_language; name: string; seoDescription: string | null; diff --git a/src/translations/types/ProductTranslations.ts b/src/translations/types/ProductTranslations.ts index ccc726de3..46c1989b4 100644 --- a/src/translations/types/ProductTranslations.ts +++ b/src/translations/types/ProductTranslations.ts @@ -16,7 +16,7 @@ export interface ProductTranslations_translations_edges_node_ProductTranslatable __typename: "Product"; id: string; name: string; - descriptionJson: any; + description: any; seoDescription: string | null; seoTitle: string | null; } @@ -30,7 +30,7 @@ export interface ProductTranslations_translations_edges_node_ProductTranslatable export interface ProductTranslations_translations_edges_node_ProductTranslatableContent_translation { __typename: "ProductTranslation"; id: string; - descriptionJson: any; + description: any; language: ProductTranslations_translations_edges_node_ProductTranslatableContent_translation_language; name: string; seoDescription: string | null; diff --git a/src/translations/types/UpdateCategoryTranslations.ts b/src/translations/types/UpdateCategoryTranslations.ts index 0b3020389..3d5e977ad 100644 --- a/src/translations/types/UpdateCategoryTranslations.ts +++ b/src/translations/types/UpdateCategoryTranslations.ts @@ -22,7 +22,7 @@ export interface UpdateCategoryTranslations_categoryTranslate_category_translati export interface UpdateCategoryTranslations_categoryTranslate_category_translation { __typename: "CategoryTranslation"; id: string; - descriptionJson: any; + description: any; language: UpdateCategoryTranslations_categoryTranslate_category_translation_language; name: string; seoDescription: string | null; @@ -33,7 +33,7 @@ export interface UpdateCategoryTranslations_categoryTranslate_category { __typename: "Category"; id: string; name: string; - descriptionJson: any; + description: any; seoDescription: string | null; seoTitle: string | null; translation: UpdateCategoryTranslations_categoryTranslate_category_translation | null; diff --git a/src/translations/types/UpdateCollectionTranslations.ts b/src/translations/types/UpdateCollectionTranslations.ts index 58f166c7e..3f72c6f8d 100644 --- a/src/translations/types/UpdateCollectionTranslations.ts +++ b/src/translations/types/UpdateCollectionTranslations.ts @@ -22,7 +22,7 @@ export interface UpdateCollectionTranslations_collectionTranslate_collection_tra export interface UpdateCollectionTranslations_collectionTranslate_collection_translation { __typename: "CollectionTranslation"; id: string; - descriptionJson: any; + description: any; language: UpdateCollectionTranslations_collectionTranslate_collection_translation_language; name: string; seoDescription: string | null; @@ -33,7 +33,7 @@ export interface UpdateCollectionTranslations_collectionTranslate_collection { __typename: "Collection"; id: string; name: string; - descriptionJson: any; + description: any; seoDescription: string | null; seoTitle: string | null; translation: UpdateCollectionTranslations_collectionTranslate_collection_translation | null; diff --git a/src/translations/types/UpdatePageTranslations.ts b/src/translations/types/UpdatePageTranslations.ts index 1abe4da2a..48154c0d4 100644 --- a/src/translations/types/UpdatePageTranslations.ts +++ b/src/translations/types/UpdatePageTranslations.ts @@ -17,7 +17,7 @@ export interface UpdatePageTranslations_pageTranslate_errors { export interface UpdatePageTranslations_pageTranslate_page_page { __typename: "Page"; id: string; - contentJson: any; + content: any; seoDescription: string | null; seoTitle: string | null; title: string; @@ -32,7 +32,7 @@ export interface UpdatePageTranslations_pageTranslate_page_translation_language export interface UpdatePageTranslations_pageTranslate_page_translation { __typename: "PageTranslation"; id: string; - contentJson: any; + content: any; seoDescription: string | null; seoTitle: string | null; title: string; diff --git a/src/translations/types/UpdateProductTranslations.ts b/src/translations/types/UpdateProductTranslations.ts index c0b35ba40..0be7c75ac 100644 --- a/src/translations/types/UpdateProductTranslations.ts +++ b/src/translations/types/UpdateProductTranslations.ts @@ -23,7 +23,7 @@ export interface UpdateProductTranslations_productTranslate_product_translation_ export interface UpdateProductTranslations_productTranslate_product_translation { __typename: "ProductTranslation"; id: string; - descriptionJson: any; + description: any; language: UpdateProductTranslations_productTranslate_product_translation_language; name: string; seoDescription: string | null; @@ -34,7 +34,7 @@ export interface UpdateProductTranslations_productTranslate_product { __typename: "Product"; id: string; name: string; - descriptionJson: any; + description: any; seoDescription: string | null; seoTitle: string | null; translation: UpdateProductTranslations_productTranslate_product_translation | null; diff --git a/src/translations/views/TranslationsCategories.tsx b/src/translations/views/TranslationsCategories.tsx index 11bbecc57..2aa9260ff 100644 --- a/src/translations/views/TranslationsCategories.tsx +++ b/src/translations/views/TranslationsCategories.tsx @@ -69,8 +69,8 @@ const TranslationsCategories: React.FC<TranslationsCategoriesProps> = ({ {(updateTranslations, updateTranslationsOpts) => { const handleSubmit = (field: string, data: string) => { const input: TranslationInput = {}; - if (field === fieldNames.descriptionJson) { - input.descriptionJson = JSON.stringify(data); + if (field === fieldNames.description) { + input.description = JSON.stringify(data); } else if (field === fieldNames.name) { input.name = data; } else if (field === fieldNames.seoDescription) { diff --git a/src/translations/views/TranslationsCollections.tsx b/src/translations/views/TranslationsCollections.tsx index 61c537521..747e5bc7e 100644 --- a/src/translations/views/TranslationsCollections.tsx +++ b/src/translations/views/TranslationsCollections.tsx @@ -71,8 +71,8 @@ const TranslationsCollections: React.FC<TranslationsCollectionsProps> = ({ {(updateTranslations, updateTranslationsOpts) => { const handleSubmit = (field: string, data: string) => { const input: TranslationInput = {}; - if (field === fieldNames.descriptionJson) { - input.descriptionJson = JSON.stringify(data); + if (field === fieldNames.description) { + input.description = JSON.stringify(data); } else if (field === fieldNames.name) { input.name = data; } else if (field === fieldNames.seoDescription) { diff --git a/src/translations/views/TranslationsEntities.tsx b/src/translations/views/TranslationsEntities.tsx index 4fc268679..1aac279c0 100644 --- a/src/translations/views/TranslationsEntities.tsx +++ b/src/translations/views/TranslationsEntities.tsx @@ -147,7 +147,7 @@ const TranslationsEntities: React.FC<TranslationsEntitiesProps> = ({ completion: { current: node.translation ? [ - node.translation.descriptionJson, + node.translation.description, node.translation.name, node.translation.seoDescription, node.translation.seoTitle @@ -197,7 +197,7 @@ const TranslationsEntities: React.FC<TranslationsEntitiesProps> = ({ completion: { current: node.translation ? [ - node.translation.descriptionJson, + node.translation.description, node.translation.name, node.translation.seoDescription, node.translation.seoTitle @@ -248,7 +248,7 @@ const TranslationsEntities: React.FC<TranslationsEntitiesProps> = ({ completion: { current: node.translation ? [ - node.translation.descriptionJson, + node.translation.description, node.translation.name, node.translation.seoDescription, node.translation.seoTitle @@ -385,7 +385,7 @@ const TranslationsEntities: React.FC<TranslationsEntitiesProps> = ({ completion: { current: node.translation ? [ - node.translation.contentJson, + node.translation.content, node.translation.seoDescription, node.translation.seoTitle, node.translation.title diff --git a/src/translations/views/TranslationsPages.tsx b/src/translations/views/TranslationsPages.tsx index 90c679cb8..8b7e735db 100644 --- a/src/translations/views/TranslationsPages.tsx +++ b/src/translations/views/TranslationsPages.tsx @@ -72,8 +72,8 @@ const TranslationsPages: React.FC<TranslationsPagesProps> = ({ {(updateTranslations, updateTranslationsOpts) => { const handleSubmit = (field: string, data: string) => { const input: PageTranslationInput = {}; - if (field === fieldNames.contentJson) { - input.contentJson = JSON.stringify(data); + if (field === fieldNames.content) { + input.content = JSON.stringify(data); } else if (field === fieldNames.title) { input.title = data; } else if (field === fieldNames.seoDescription) { diff --git a/src/translations/views/TranslationsProducts.tsx b/src/translations/views/TranslationsProducts.tsx index d0a2bc22a..aede683aa 100644 --- a/src/translations/views/TranslationsProducts.tsx +++ b/src/translations/views/TranslationsProducts.tsx @@ -70,8 +70,8 @@ const TranslationsProducts: React.FC<TranslationsProductsProps> = ({ {(updateTranslations, updateTranslationsOpts) => { const handleSubmit = (field: string, data: string) => { const input: TranslationInput = {}; - if (field === fieldNames.descriptionJson) { - input.descriptionJson = JSON.stringify(data); + if (field === fieldNames.description) { + input.description = JSON.stringify(data); } else if (field === fieldNames.name) { input.name = data; } else if (field === fieldNames.seoDescription) { diff --git a/src/types/globalTypes.ts b/src/types/globalTypes.ts index 9e2f51f7d..a971e3497 100644 --- a/src/types/globalTypes.ts +++ b/src/types/globalTypes.ts @@ -1119,8 +1119,7 @@ export interface CategoryFilterInput { } export interface CategoryInput { - description?: string | null; - descriptionJson?: any | null; + description?: any | null; name?: string | null; slug?: string | null; seo?: SeoInput | null; @@ -1160,8 +1159,7 @@ export interface CollectionCreateInput { isPublished?: boolean | null; name?: string | null; slug?: string | null; - description?: string | null; - descriptionJson?: any | null; + description?: any | null; backgroundImage?: any | null; backgroundImageAlt?: string | null; seo?: SeoInput | null; @@ -1180,8 +1178,7 @@ export interface CollectionInput { isPublished?: boolean | null; name?: string | null; slug?: string | null; - description?: string | null; - descriptionJson?: any | null; + description?: any | null; backgroundImage?: any | null; backgroundImageAlt?: string | null; seo?: SeoInput | null; @@ -1429,8 +1426,7 @@ export interface OrderUpdateShippingInput { export interface PageCreateInput { slug?: string | null; title?: string | null; - content?: string | null; - contentJson?: any | null; + content?: any | null; attributes?: AttributeValueInput[] | null; isPublished?: boolean | null; publicationDate?: string | null; @@ -1441,8 +1437,7 @@ export interface PageCreateInput { export interface PageInput { slug?: string | null; title?: string | null; - content?: string | null; - contentJson?: any | null; + content?: any | null; attributes?: AttributeValueInput[] | null; isPublished?: boolean | null; publicationDate?: string | null; @@ -1551,8 +1546,7 @@ export interface ProductCreateInput { category?: string | null; chargeTaxes?: boolean | null; collections?: (string | null)[] | null; - description?: string | null; - descriptionJson?: any | null; + description?: any | null; name?: string | null; slug?: string | null; taxCode?: string | null; @@ -1584,8 +1578,7 @@ export interface ProductInput { category?: string | null; chargeTaxes?: boolean | null; collections?: (string | null)[] | null; - description?: string | null; - descriptionJson?: any | null; + description?: any | null; name?: string | null; slug?: string | null; taxCode?: string | null; @@ -1818,8 +1811,7 @@ export interface TranslationInput { seoTitle?: string | null; seoDescription?: string | null; name?: string | null; - description?: string | null; - descriptionJson?: any | null; + description?: any | null; } export interface UserCreateInput { From 383055a2de546e84ad9787420aa94ec16f66675a Mon Sep 17 00:00:00 2001 From: Jakub Majorek <majorek.jakub@gmail.com> Date: Fri, 22 Jan 2021 15:05:26 +0100 Subject: [PATCH 19/35] SALEOR-2064 Add generic error tracker with Sentry adapter (#956) * Add generic error tracker with Sentry extension * Add Sentry webpack plugin * Update variable names and README * Update deploy-staging template * Update changelog --- .github/workflows/deploy-staging.yaml | 7 +- CHANGELOG.md | 1 + README.md | 41 +++++- package-lock.json | 120 ++++++++++++++++++ package.json | 5 +- src/auth/AuthProvider.tsx | 22 +++- src/components/AppLayout/AppLayout.tsx | 7 +- src/components/ErrorPage/ErrorPage.tsx | 11 +- src/containers/AppState/reducer.ts | 20 ++- src/containers/AppState/state.ts | 5 +- src/index.tsx | 12 +- src/services/errorTracking/adapters/Sentry.ts | 33 +++++ src/services/errorTracking/adapters/index.ts | 1 + src/services/errorTracking/index.ts | 11 ++ .../errorTracking/trackerFactory.test.ts | 83 ++++++++++++ src/services/errorTracking/trackerFactory.ts | 52 ++++++++ src/services/errorTracking/types.ts | 15 +++ webpack.config.js | 28 +++- 18 files changed, 451 insertions(+), 23 deletions(-) create mode 100644 src/services/errorTracking/adapters/Sentry.ts create mode 100644 src/services/errorTracking/adapters/index.ts create mode 100644 src/services/errorTracking/index.ts create mode 100644 src/services/errorTracking/trackerFactory.test.ts create mode 100644 src/services/errorTracking/trackerFactory.ts create mode 100644 src/services/errorTracking/types.ts diff --git a/.github/workflows/deploy-staging.yaml b/.github/workflows/deploy-staging.yaml index b3f52ea67..f38df4db9 100644 --- a/.github/workflows/deploy-staging.yaml +++ b/.github/workflows/deploy-staging.yaml @@ -11,6 +11,12 @@ jobs: API_URI: https://master.staging.saleor.cloud/graphql/ APP_MOUNT_URI: /dashboard/ STATIC_URL: /dashboard/static/ + SENTRY_ORG: sentry + SENTRY_PROJECT: dashboard + SENTRY_URL_PREFIX: "~/static" + ENVIRONMENT: master-staging + SENTRY_DSN: ${{ secrets.SENTRY_DSN }} + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} steps: - uses: actions/checkout@v2 - name: Package @@ -41,4 +47,3 @@ jobs: aws s3 sync build/dashboard s3://${{ secrets.AWS_STAGING_DEPLOYMENT_BUCKET }}/saleor-master-staging/static/ aws s3 cp build/dashboard/index.html s3://${{ secrets.AWS_STAGING_DEPLOYMENT_BUCKET }}/saleor-master-staging/ aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_STAGING_CF_DIST_ID }} --paths "/dashboard*" - diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b0927617..e3deeb342 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ All notable, unreleased changes to this project will be documented in this file. - Add reference attributes - #917 by @orzechdev - Add product reference attributes - #948 by @orzechdev - Drop descriptionJson and contentJson fields - #950 by @jwm0 +- Add error tracking with Sentry adapter - #956 by @jwm0 # 2.11.1 diff --git a/README.md b/README.md index 1bdccfccd..4cda35bd8 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ $ npm i ### Configuration -There are two environment variables available for configuration: +There following environment variables are available for configuration: - `API_URI` (required) - URI of a running instance of Saleor GraphQL API. If you are running Saleor locally with the default settings, set `API_URI` to: `http://localhost:8000/graphql/`. @@ -88,4 +88,43 @@ To build the application bundle run: $ npm run build ``` +### Error Tracking + +Saleor Dashboard is using a generic error tracking wrapper function that takes care of the most popular use cases: + +- initializing the tracker +- capturing exceptions and (optionally) displaying the event id +- setting basic user data (this is opt-in and disabled by default) + +By default it ships with a Sentry adapter but any kind of error tracking software can be used by creating a custom adapter (using Sentry and TS types as an example). + +Example: + +```javascript +// src/services/errorTracking/index.ts + +import { CustomAdapter } from "./adapters/"; + +const errorTracker = ErrorTrackerFactory(CustomAdapter(config)); +``` + +##### Usage with Sentry adapter: + +Sentry is used as the default tracker so no changes in code are necessary and the configuration is done via environment variables. + +The following environment variables are available: + +``` +# Required +SENTRY_DSN= + +# Optional +# https://docs.sentry.io/product/cli/configuration/ +SENTRY_AUTH_TOKEN= +SENTRY_ORG= +SENTRY_PROJECT= +SENTRY_URL_PREFIX= +ENVIRONMENT= +``` + #### Crafted with ❤️ by [Mirumee Software](https://mirumee.com) diff --git a/package-lock.json b/package-lock.json index 032ae5a10..c07e8cbbe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3153,6 +3153,120 @@ "any-observable": "^0.3.0" } }, + "@sentry/browser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.0.0.tgz", + "integrity": "sha512-R4+MHb5FyVZCz3EVnaquvT1mwOM2MWP4gBqjYEADY5m0XWoHiJf0skFkWt8iEKJanzGbhl4PMb9gHuJj6YfVLw==", + "requires": { + "@sentry/core": "6.0.0", + "@sentry/types": "6.0.0", + "@sentry/utils": "6.0.0", + "tslib": "^1.9.3" + } + }, + "@sentry/cli": { + "version": "1.61.0", + "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-1.61.0.tgz", + "integrity": "sha512-pHEhqP1bB4sdO7N5ow/IkRBrPbKT9HZRinq4PhTVIvmG+NW4VVuVZ6k4tlbp+JXmzMcUc/iXynVkTL7zJIlTQw==", + "dev": true, + "requires": { + "https-proxy-agent": "^5.0.0", + "mkdirp": "^0.5.5", + "node-fetch": "^2.6.0", + "progress": "^2.0.3", + "proxy-from-env": "^1.1.0" + }, + "dependencies": { + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + } + } + }, + "@sentry/core": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.0.0.tgz", + "integrity": "sha512-afAiOachs/WfGWc9LsJBFnJMhqQVENyzfSMnf7sLRvxPAw8n7IrXY0R09MKmG0SlAnTKN2pWoQFzFF+J3NuHBA==", + "requires": { + "@sentry/hub": "6.0.0", + "@sentry/minimal": "6.0.0", + "@sentry/types": "6.0.0", + "@sentry/utils": "6.0.0", + "tslib": "^1.9.3" + } + }, + "@sentry/hub": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.0.0.tgz", + "integrity": "sha512-s8IsW6LvEH7ACnniQcxxb/9uEyjmoQ/TAoryTJN2qyPzzrHTw8NCyMuJvK+8ivUvRViz5AvtuOFf8AJlh9lzeA==", + "requires": { + "@sentry/types": "6.0.0", + "@sentry/utils": "6.0.0", + "tslib": "^1.9.3" + } + }, + "@sentry/minimal": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.0.0.tgz", + "integrity": "sha512-daYdEzTr+ERMwViu6RpWHOfk0oZrSNqdx+7bejTqmFHqO4pt+9ZrMiw3vinL+MWQcKXwD95uXBz6O/ryrVdPtg==", + "requires": { + "@sentry/hub": "6.0.0", + "@sentry/types": "6.0.0", + "tslib": "^1.9.3" + } + }, + "@sentry/react": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@sentry/react/-/react-6.0.0.tgz", + "integrity": "sha512-GYX110NSodd8wGUbnyxemndTijM+U7dI/WjFSPOyJdLB2hzzPjJ9kUqtuobT/JlGzbWE2278WysAuySne6bUGw==", + "requires": { + "@sentry/browser": "6.0.0", + "@sentry/minimal": "6.0.0", + "@sentry/types": "6.0.0", + "@sentry/utils": "6.0.0", + "hoist-non-react-statics": "^3.3.2", + "tslib": "^1.9.3" + }, + "dependencies": { + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + } + } + } + }, + "@sentry/types": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.0.0.tgz", + "integrity": "sha512-yueRSRGPCahuju/UMdtOt8LIIncbpwLINQd9Q8E4OXtoPpMHR6Oun8sMKCPd+Wq3piI5yRDzKkGCl+sH7mHVrA==" + }, + "@sentry/utils": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.0.0.tgz", + "integrity": "sha512-dMMWOT69bQ4CF1R33dOnXIOyiHRWsUAON3nFVljV1JNNTDA69YwaF9f5FIT0DKpO4qhgTlElsm8WgHI9prAVEQ==", + "requires": { + "@sentry/types": "6.0.0", + "tslib": "^1.9.3" + } + }, + "@sentry/webpack-plugin": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@sentry/webpack-plugin/-/webpack-plugin-1.14.0.tgz", + "integrity": "sha512-1cS99mnHqASYtMlHi2J107p6x3lfC5NmLOgA0iI6avaaFes8RTQMlW8YT2CyrvhtQod1bViPZOlh3NOVC8vnOA==", + "dev": true, + "requires": { + "@sentry/cli": "^1.58.0" + } + }, "@sindresorhus/fnv1a": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@sindresorhus/fnv1a/-/fnv1a-1.2.0.tgz", @@ -18614,6 +18728,12 @@ "ipaddr.js": "1.9.0" } }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", diff --git a/package.json b/package.json index 69bbb607d..88b73b4a1 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@material-ui/icons": "^4.5.1", "@material-ui/styles": "^4.5.2", "@saleor/macaw-ui": "^0.1.1-9", + "@sentry/react": "^6.0.0", "apollo": "^2.21.2", "apollo-cache-inmemory": "^1.6.5", "apollo-client": "^2.6.8", @@ -92,6 +93,7 @@ "@pollyjs/adapter-node-http": "^5.0.0", "@pollyjs/core": "^5.0.0", "@pollyjs/persister-fs": "^5.0.0", + "@sentry/webpack-plugin": "^1.14.0", "@storybook/addon-storyshots": "^5.2.8", "@storybook/react": "^5.1.9", "@testing-library/react-hooks": "^1.1.0", @@ -226,6 +228,7 @@ "test:e2e:dev": "start-server-and-test start http://localhost:9000 cy:open", "test": "jest src/", "transpile-messages": "node scripts/transpile-tx.js", - "lint": "npx eslint \"src/**/*.@(tsx|ts|jsx|js)\" --fix ; npx prettier --check \"src/**/*.@(tsx|ts|jsx|js)\" --write" + "lint": "npx eslint \"src/**/*.@(tsx|ts|jsx|js)\" --fix ; npx prettier --check \"src/**/*.@(tsx|ts|jsx|js)\" --write", + "postbuild": "rimraf ./build/**/*.js.map" } } diff --git a/src/auth/AuthProvider.tsx b/src/auth/AuthProvider.tsx index 37b75a5b9..7cfe3a77a 100644 --- a/src/auth/AuthProvider.tsx +++ b/src/auth/AuthProvider.tsx @@ -4,6 +4,7 @@ import { User } from "@saleor/fragments/types/User"; import useNotifier from "@saleor/hooks/useNotifier"; import { commonMessages } from "@saleor/intl"; import { getMutationStatus } from "@saleor/misc"; +import errorTracker from "@saleor/services/errorTracking"; import { isSupported as isCredentialsManagementAPISupported, login as loginWithCredentialsManagementAPI, @@ -52,13 +53,22 @@ export function useAuthProvider( }, []); useEffect(() => { - if (userContext && !userContext.isStaff) { - logout(); - notify({ - status: "error", - text: intl.formatMessage(commonMessages.unauthorizedDashboardAccess), - title: intl.formatMessage(commonMessages.insufficientPermissions) + if (userContext) { + const { id, email, firstName, lastName } = userContext; + errorTracker.setUserData({ + email, + id, + username: `${firstName} ${lastName}` }); + + if (!userContext.isStaff) { + logout(); + notify({ + status: "error", + text: intl.formatMessage(commonMessages.unauthorizedDashboardAccess), + title: intl.formatMessage(commonMessages.insufficientPermissions) + }); + } } }, [userContext]); diff --git a/src/components/AppLayout/AppLayout.tsx b/src/components/AppLayout/AppLayout.tsx index 4be74a99f..11ee9ce45 100644 --- a/src/components/AppLayout/AppLayout.tsx +++ b/src/components/AppLayout/AppLayout.tsx @@ -239,8 +239,11 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => { </div> <main className={classes.view}> {appState.error - ? appState.error === "unhandled" && ( - <ErrorPage onBack={handleErrorBack} /> + ? appState.error.type === "unhandled" && ( + <ErrorPage + id={appState.error.id} + onBack={handleErrorBack} + /> ) : children} </main> diff --git a/src/components/ErrorPage/ErrorPage.tsx b/src/components/ErrorPage/ErrorPage.tsx index 856230375..b6447ba8b 100644 --- a/src/components/ErrorPage/ErrorPage.tsx +++ b/src/components/ErrorPage/ErrorPage.tsx @@ -7,6 +7,7 @@ import SVG from "react-inlinesvg"; import { FormattedMessage } from "react-intl"; export interface ErrorPageProps { + id?: string | null; onBack: () => void; } @@ -31,6 +32,9 @@ const useStyles = makeStyles( margin: "0 auto", width: 830 }, + errorId: { + marginTop: theme.spacing(3) + }, innerContainer: { [theme.breakpoints.down("sm")]: { order: 1, @@ -58,7 +62,7 @@ const useStyles = makeStyles( ); const ErrorPage: React.FC<ErrorPageProps> = props => { - const { onBack } = props; + const { onBack, id } = props; const classes = useStyles(props); @@ -79,6 +83,11 @@ const ErrorPage: React.FC<ErrorPageProps> = props => { <Typography> <FormattedMessage defaultMessage="Don't worry, everything is gonna be fine" /> </Typography> + {!!id && ( + <Typography variant="subtitle2" className={classes.errorId}> + Error ID: {id} + </Typography> + )} </div> <div> <Button diff --git a/src/containers/AppState/reducer.ts b/src/containers/AppState/reducer.ts index 1ed4654d4..a6b90948b 100644 --- a/src/containers/AppState/reducer.ts +++ b/src/containers/AppState/reducer.ts @@ -4,16 +4,24 @@ export type AppStateReducerActionType = "displayError" | "displayLoader"; export interface AppStateReducerAction { payload: Partial<{ - error: AppError; + error: AppError["type"]; + errorId: AppError["id"]; value: boolean; }>; type: AppStateReducerActionType; } -function displayError(prevState: IAppState, error: AppError): IAppState { +function displayError( + prevState: IAppState, + errorType: AppError["type"], + errorId?: AppError["id"] +): IAppState { return { ...prevState, - error, + error: { + id: errorId, + type: errorType + }, loading: false }; } @@ -31,7 +39,11 @@ function reduceAppState( ): IAppState { switch (action.type) { case "displayError": - return displayError(prevState, action.payload.error); + return displayError( + prevState, + action.payload.error, + action.payload.errorId + ); case "displayLoader": return displayLoader(prevState, action.payload.value); default: diff --git a/src/containers/AppState/state.ts b/src/containers/AppState/state.ts index efa2daa86..0214bac0a 100644 --- a/src/containers/AppState/state.ts +++ b/src/containers/AppState/state.ts @@ -1,4 +1,7 @@ -export type AppError = "unhandled"; +export interface AppError { + type: "unhandled"; + id: string | null | undefined; +} interface IAppState { error: AppError | null; diff --git a/src/index.tsx b/src/index.tsx index b199afe70..eb9a26f03 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -54,6 +54,7 @@ import PermissionGroupSection from "./permissionGroups"; import PluginsSection from "./plugins"; import ProductSection from "./products"; import ProductTypesSection from "./productTypes"; +import errorTracker from "./services/errorTracking"; import ShippingSection from "./shipping"; import SiteSettingsSection from "./siteSettings"; import StaffSection from "./staff"; @@ -67,6 +68,8 @@ if (process.env.GTM_ID) { TagManager.initialize({ gtmId: GTM_ID }); } +errorTracker.init(); + // DON'T TOUCH THIS // These are separate clients and do not share configs between themselves // so we need to explicitly set them @@ -159,14 +162,17 @@ const Routes: React.FC = () => { {homePageLoaded ? ( <AppLayout> <ErrorBoundary - onError={() => + onError={e => { + const errorId = errorTracker.captureException(e); + dispatchAppState({ payload: { + errorId, error: "unhandled" }, type: "displayError" - }) - } + }); + }} > <Switch> <SectionRoute exact path="/" component={HomePage} /> diff --git a/src/services/errorTracking/adapters/Sentry.ts b/src/services/errorTracking/adapters/Sentry.ts new file mode 100644 index 000000000..42a3955a4 --- /dev/null +++ b/src/services/errorTracking/adapters/Sentry.ts @@ -0,0 +1,33 @@ +import * as Sentry from "@sentry/react"; + +import { TrackerMethods } from "../types"; + +interface Config { + dsn: string; + environment?: string; +} + +export const SentryAdapter = (config: Config): TrackerMethods => { + const init: TrackerMethods["init"] = () => { + if (config?.dsn) { + Sentry.init({ + dsn: config.dsn, + environment: config.environment + }); + return true; + } + return false; + }; + + const setUserData: TrackerMethods["setUserData"] = userData => + Sentry.setUser(userData); + + const captureException: TrackerMethods["captureException"] = (e: Error) => + Sentry.captureException(e); + + return { + captureException, + init, + setUserData + }; +}; diff --git a/src/services/errorTracking/adapters/index.ts b/src/services/errorTracking/adapters/index.ts new file mode 100644 index 000000000..ab00fdd10 --- /dev/null +++ b/src/services/errorTracking/adapters/index.ts @@ -0,0 +1 @@ +export { SentryAdapter } from "./Sentry"; diff --git a/src/services/errorTracking/index.ts b/src/services/errorTracking/index.ts new file mode 100644 index 000000000..23db64011 --- /dev/null +++ b/src/services/errorTracking/index.ts @@ -0,0 +1,11 @@ +import { SentryAdapter } from "./adapters"; +import { ErrorTrackerFactory } from "./trackerFactory"; + +const errorTracker = ErrorTrackerFactory( + SentryAdapter({ + dsn: process.env.SENTRY_DSN, + environment: process.env.ENVIRONMENT + }) +); + +export default errorTracker; diff --git a/src/services/errorTracking/trackerFactory.test.ts b/src/services/errorTracking/trackerFactory.test.ts new file mode 100644 index 000000000..8bfc3b268 --- /dev/null +++ b/src/services/errorTracking/trackerFactory.test.ts @@ -0,0 +1,83 @@ +import { ErrorTrackerFactory } from "./trackerFactory"; +import { TrackerMethods, TrackerPermission } from "./types"; + +const testErrorId = "testId"; +const initMockFn = jest.fn(); +const captureExceptionMockFn = jest.fn(_ => testErrorId); +const setUserDataMockFn = jest.fn(); + +const TestAdapter = (): TrackerMethods => { + const init: TrackerMethods["init"] = () => { + initMockFn(); + return true; + }; + + const setUserData: TrackerMethods["setUserData"] = userData => + setUserDataMockFn(userData); + + const captureException: TrackerMethods["captureException"] = (e: Error) => + captureExceptionMockFn(e); + + return { + captureException, + init, + setUserData + }; +}; + +describe("Error Tracking", () => { + it("Initiates the tracker", () => { + const errorTracking = ErrorTrackerFactory(TestAdapter()); + const enabled = errorTracking.init(); + + expect(enabled).toBe(true); + expect(initMockFn).toHaveBeenCalled(); + }); + + it("Does not fire events when is not initiated", () => { + const errorTracking = ErrorTrackerFactory(TestAdapter()); + const sampleError = new Error("test"); + const id = errorTracking.captureException(sampleError); + + expect(id).toBe(undefined); + expect(captureExceptionMockFn).toHaveBeenCalledTimes(0); + }); + + it("Sends a captured exception", () => { + const errorTracking = ErrorTrackerFactory(TestAdapter()); + errorTracking.init(); + const sampleError = new Error("test"); + const id = errorTracking.captureException(sampleError); + + expect(id).toBe(testErrorId); + expect(captureExceptionMockFn).toHaveBeenCalledWith(sampleError); + }); + + it("Does not save user data without permission", () => { + const errorTracking = ErrorTrackerFactory(TestAdapter()); + errorTracking.init(); + const userData = { + email: "john@example.com", + id: "id", + username: "John Doe" + }; + errorTracking.setUserData(userData); + + expect(setUserDataMockFn).toHaveBeenCalledTimes(0); + }); + + it("Does save user data with proper permission", () => { + const errorTracking = ErrorTrackerFactory(TestAdapter(), [ + TrackerPermission.USER_DATA + ]); + errorTracking.init(); + const userData = { + email: "john@example.com", + id: "id", + username: "John Doe" + }; + errorTracking.setUserData(userData); + + expect(setUserDataMockFn).toHaveBeenCalledWith(userData); + }); +}); diff --git a/src/services/errorTracking/trackerFactory.ts b/src/services/errorTracking/trackerFactory.ts new file mode 100644 index 000000000..0f9efe8a6 --- /dev/null +++ b/src/services/errorTracking/trackerFactory.ts @@ -0,0 +1,52 @@ +import { TrackerMethods, TrackerPermission, UserData } from "./types"; + +type ErrorTrackerFactory = ( + ExtensionFactory: TrackerMethods, + permissions?: TrackerPermission[] +) => TrackerMethods; + +export const ErrorTrackerFactory: ErrorTrackerFactory = ( + extension, + permissions = [] +) => { + let ENABLED = false; + + const safelyInvoke = <T extends () => any>( + fn: T, + permission?: TrackerPermission + ): ReturnType<T> => { + const hasPermission = + permission !== undefined ? permissions.includes(permission) : true; + + if (ENABLED && hasPermission) { + try { + return fn(); + } catch (e) { + throw new Error(`Tracking Extension Error: ${e}`); + } + } + }; + + const init: TrackerMethods["init"] = () => { + if (!ENABLED) { + ENABLED = extension.init(); + } + + return ENABLED; + }; + + const setUserData: TrackerMethods["setUserData"] = (userData: UserData) => + safelyInvoke( + () => extension.setUserData(userData), + TrackerPermission.USER_DATA + ); + + const captureException: TrackerMethods["captureException"] = (e: Error) => + safelyInvoke(() => extension.captureException(e)); + + return { + captureException, + init, + setUserData + }; +}; diff --git a/src/services/errorTracking/types.ts b/src/services/errorTracking/types.ts new file mode 100644 index 000000000..ad51cd447 --- /dev/null +++ b/src/services/errorTracking/types.ts @@ -0,0 +1,15 @@ +export type UserData = { + id: string; + email: string; + username: string; +} | null; + +export interface TrackerMethods { + init: () => boolean; + setUserData: (userData: UserData) => void; + captureException: (e: Error) => string | null | undefined; +} + +export enum TrackerPermission { + USER_DATA +} diff --git a/webpack.config.js b/webpack.config.js index db1920d20..6f313e373 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -4,6 +4,7 @@ const CheckerPlugin = require("fork-ts-checker-webpack-plugin"); const webpack = require("webpack"); const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); +const SentryWebpackPlugin = require("@sentry/webpack-plugin"); require("dotenv").config(); @@ -26,7 +27,9 @@ const environmentPlugin = new webpack.EnvironmentPlugin({ API_URI: "", APP_MOUNT_URI: "/", DEMO_MODE: false, - GTM_ID: "" + ENVIRONMENT: "", + GTM_ID: "", + SENTRY_DSN: "" }); const dashboardBuildPath = "build/dashboard/"; @@ -60,6 +63,20 @@ module.exports = (env, argv) => { fileLoaderPath = "file-loader?name=[name].[ext]"; } + // Create release if sentry config is set + let sentryPlugin; + if ( + process.env.SENTRY_ORG && + process.env.SENTRY_PROJECT && + process.env.SENTRY_DSN && + process.env.SENTRY_AUTH_TOKEN + ) { + sentryPlugin = new SentryWebpackPlugin({ + include: "./build/dashboard/", + urlPrefix: process.env.SENTRY_URL_PREFIX + }); + } + return { devServer: { compress: true, @@ -68,7 +85,7 @@ module.exports = (env, argv) => { hot: true, port: 9000 }, - devtool: "sourceMap", + devtool: "source-map", entry: { dashboard: "./src/index.tsx" }, @@ -100,7 +117,12 @@ module.exports = (env, argv) => { splitChunks: false }, output, - plugins: [checkerPlugin, environmentPlugin, htmlWebpackPlugin], + plugins: [ + checkerPlugin, + environmentPlugin, + htmlWebpackPlugin, + sentryPlugin + ].filter(Boolean), resolve: { extensions: [".js", ".jsx", ".ts", ".tsx"], plugins: [pathsPlugin] From 2d3a859db6ca76dc77907bdc17407f04c92b7b8d Mon Sep 17 00:00:00 2001 From: Jakub Majorek <majorek.jakub@gmail.com> Date: Fri, 22 Jan 2021 15:15:22 +0100 Subject: [PATCH 20/35] Fix typo in deploy template (#959) --- .github/workflows/deploy-staging.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-staging.yaml b/.github/workflows/deploy-staging.yaml index f38df4db9..329b2b761 100644 --- a/.github/workflows/deploy-staging.yaml +++ b/.github/workflows/deploy-staging.yaml @@ -11,7 +11,7 @@ jobs: API_URI: https://master.staging.saleor.cloud/graphql/ APP_MOUNT_URI: /dashboard/ STATIC_URL: /dashboard/static/ - SENTRY_ORG: sentry + SENTRY_ORG: saleor SENTRY_PROJECT: dashboard SENTRY_URL_PREFIX: "~/static" ENVIRONMENT: master-staging From 3eb940839df3a7a561bc810cc9a9ebb961800cbc Mon Sep 17 00:00:00 2001 From: Jakub Majorek <majorek.jakub@gmail.com> Date: Fri, 22 Jan 2021 15:39:58 +0100 Subject: [PATCH 21/35] Update sentry url prefix (#960) --- .github/workflows/deploy-staging.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-staging.yaml b/.github/workflows/deploy-staging.yaml index 329b2b761..f098f9a69 100644 --- a/.github/workflows/deploy-staging.yaml +++ b/.github/workflows/deploy-staging.yaml @@ -13,7 +13,7 @@ jobs: STATIC_URL: /dashboard/static/ SENTRY_ORG: saleor SENTRY_PROJECT: dashboard - SENTRY_URL_PREFIX: "~/static" + SENTRY_URL_PREFIX: "~/dashboard/static" ENVIRONMENT: master-staging SENTRY_DSN: ${{ secrets.SENTRY_DSN }} SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} From b894487c63d60fac0e9a9b4dc9c0140bb7c30835 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cezary=20Mi=C4=85cz?= <czarek.miacz@gmail.com> Date: Mon, 25 Jan 2021 14:51:13 +0100 Subject: [PATCH 22/35] Update staging API_URI in PR template and test env workflow (#962) --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- .github/workflows/test-env-deploy.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 9a5806962..7bb5cbabf 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -27,4 +27,4 @@ greatly reduce the amount of work needed to review your work. --> <!-- Do not remove this section. It is required to properly setup test instance. Modify API_URI if you want test instance to use custom backend. --> -API_URI=https://master.staging.saleor.rocks/graphql/ +API_URI=https://master.staging.saleor.cloud/graphql/ diff --git a/.github/workflows/test-env-deploy.yml b/.github/workflows/test-env-deploy.yml index 744b55b66..17055ec08 100644 --- a/.github/workflows/test-env-deploy.yml +++ b/.github/workflows/test-env-deploy.yml @@ -59,7 +59,7 @@ jobs: - name: Run build env: # Use custom API_URI or the default one - API_URI: ${{ steps.api_uri.outputs.custom_api_uri || 'https://master.staging.saleor.rocks/graphql/' }} + API_URI: ${{ steps.api_uri.outputs.custom_api_uri || 'https://master.staging.saleor.cloud/graphql/' }} APP_MOUNT_URI: / STATIC_URL: / run: | From 6608ac81227c75621ab4cd43e194b508489afdc5 Mon Sep 17 00:00:00 2001 From: Magdalena Markusik <magdalena.markusik@mirumee.com> Date: Fri, 22 Jan 2021 16:30:01 +0100 Subject: [PATCH 23/35] Add returned products to refund page --- .../OrderRefundFulfilledProducts.tsx | 14 ++++++++++---- .../components/OrderRefundPage/OrderRefundPage.tsx | 10 ++++++++-- src/orders/components/OrderRefundPage/form.tsx | 4 +++- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/orders/components/OrderRefundFulfilledProducts/OrderRefundFulfilledProducts.tsx b/src/orders/components/OrderRefundFulfilledProducts/OrderRefundFulfilledProducts.tsx index bc464f64b..33cb46f2a 100644 --- a/src/orders/components/OrderRefundFulfilledProducts/OrderRefundFulfilledProducts.tsx +++ b/src/orders/components/OrderRefundFulfilledProducts/OrderRefundFulfilledProducts.tsx @@ -17,6 +17,7 @@ import TableCellAvatar from "@saleor/components/TableCellAvatar"; import { FormsetChange } from "@saleor/hooks/useFormset"; import { renderCollection } from "@saleor/misc"; import { OrderRefundData_order_fulfillments } from "@saleor/orders/types/OrderRefundData"; +import { FulfillmentStatus } from "@saleor/types/globalTypes"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; @@ -91,10 +92,15 @@ const OrderRefundFulfilledProducts: React.FC<OrderRefundFulfilledProductsProps> <CardTitle title={ <> - {intl.formatMessage({ - defaultMessage: "Fulfillment", - description: "section header" - })} + {fulfillment.status === FulfillmentStatus.RETURNED + ? intl.formatMessage({ + defaultMessage: "Fulfillment returned", + description: "section header returned" + }) + : intl.formatMessage({ + defaultMessage: "Fulfillment", + description: "section header" + })} {fulfillment && ( <Typography className={classes.orderNumber} variant="body1"> {`#${orderNumber}-${fulfillment?.fulfillmentOrder}`} diff --git a/src/orders/components/OrderRefundPage/OrderRefundPage.tsx b/src/orders/components/OrderRefundPage/OrderRefundPage.tsx index 676cd399b..7576e2ee8 100644 --- a/src/orders/components/OrderRefundPage/OrderRefundPage.tsx +++ b/src/orders/components/OrderRefundPage/OrderRefundPage.tsx @@ -24,6 +24,11 @@ import OrderRefundForm, { OrderRefundType } from "./form"; +export const refundFulfilledStatuses = [ + FulfillmentStatus.FULFILLED, + FulfillmentStatus.RETURNED +]; + export interface OrderRefundPageProps { order: OrderRefundData_order; defaultType?: OrderRefundType; @@ -48,9 +53,10 @@ const OrderRefundPage: React.FC<OrderRefundPageProps> = props => { const unfulfilledLines = order?.lines.filter( line => line.quantity !== line.quantityFulfilled ); + const fulfilledFulfillemnts = - order?.fulfillments.filter( - fulfillment => fulfillment.status === FulfillmentStatus.FULFILLED + order?.fulfillments.filter(({ status }) => + refundFulfilledStatuses.includes(status) ) || []; return ( diff --git a/src/orders/components/OrderRefundPage/form.tsx b/src/orders/components/OrderRefundPage/form.tsx index 6f95ff7c4..a3de89063 100644 --- a/src/orders/components/OrderRefundPage/form.tsx +++ b/src/orders/components/OrderRefundPage/form.tsx @@ -8,6 +8,8 @@ import { FulfillmentStatus } from "@saleor/types/globalTypes"; import handleFormSubmit from "@saleor/utils/handlers/handleFormSubmit"; import React from "react"; +import { refundFulfilledStatuses } from "./OrderRefundPage"; + export enum OrderRefundType { MISCELLANEOUS = "miscellaneous", PRODUCTS = "products" @@ -87,7 +89,7 @@ function useOrderRefundForm( ); const refundedFulfilledProductQuantities = useFormset<null, string>( order?.fulfillments - .filter(fulfillment => fulfillment.status === FulfillmentStatus.FULFILLED) + .filter(({ status }) => refundFulfilledStatuses.includes(status)) .reduce( (linesQty, fulfillemnt) => linesQty.concat( From 708e38157691cd44893c6895c8617897c21b12de Mon Sep 17 00:00:00 2001 From: Magdalena Markusik <magdalena.markusik@mirumee.com> Date: Fri, 22 Jan 2021 16:31:56 +0100 Subject: [PATCH 24/35] Update messages --- locale/defaultMessages.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index 796678ae6..aba381313 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -3776,6 +3776,10 @@ "context": "order line total price", "string": "Total" }, + "src_dot_orders_dot_components_dot_OrderRefundFulfilledProducts_dot_1097582574": { + "context": "section header returned", + "string": "Fulfillment returned" + }, "src_dot_orders_dot_components_dot_OrderRefundFulfilledProducts_dot_1134347598": { "context": "tabel column header", "string": "Price" From 94d08dde7d19b92269590eed2d379dfb00ee88fb Mon Sep 17 00:00:00 2001 From: Magdalena Markusik <magdalena.markusik@mirumee.com> Date: Fri, 22 Jan 2021 16:36:35 +0100 Subject: [PATCH 25/35] Remove unnecessary import --- src/orders/components/OrderRefundPage/form.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/orders/components/OrderRefundPage/form.tsx b/src/orders/components/OrderRefundPage/form.tsx index a3de89063..f51e237b1 100644 --- a/src/orders/components/OrderRefundPage/form.tsx +++ b/src/orders/components/OrderRefundPage/form.tsx @@ -4,7 +4,6 @@ import useFormset, { FormsetData } from "@saleor/hooks/useFormset"; import { OrderRefundData_order } from "@saleor/orders/types/OrderRefundData"; -import { FulfillmentStatus } from "@saleor/types/globalTypes"; import handleFormSubmit from "@saleor/utils/handlers/handleFormSubmit"; import React from "react"; From f7c7f4e5184015ff5963739d408883d339fa040d Mon Sep 17 00:00:00 2001 From: Magdalena Markusik <magdalena.markusik@mirumee.com> Date: Mon, 25 Jan 2021 11:27:45 +0100 Subject: [PATCH 26/35] Fix marked as paid item not displaying correctly in order history --- .../OrderHistory/ExtendedTimelineEvent.tsx | 24 +++++++++++++++++++ src/orders/components/OrderHistory/utils.tsx | 3 ++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/orders/components/OrderHistory/ExtendedTimelineEvent.tsx b/src/orders/components/OrderHistory/ExtendedTimelineEvent.tsx index 36bbc5519..c47be5f9b 100644 --- a/src/orders/components/OrderHistory/ExtendedTimelineEvent.tsx +++ b/src/orders/components/OrderHistory/ExtendedTimelineEvent.tsx @@ -78,6 +78,11 @@ export const titles = defineMessages({ defaultMessage: "Products were returned by", description: "returned event title", id: "event title returned" + }, + orderMarkedAsPaid: { + defaultMessage: "Order was marked as paid by", + description: "order marked as paid event title", + id: "event title marked as paid" } }); @@ -96,6 +101,11 @@ export const messages = defineMessages({ defaultMessage: "Shipment was refunded", description: "shipment refund title", id: "shipment refund title" + }, + transactionReference: { + defaultMessage: "Transaction reference", + description: "transaction reference subtitle", + id: "transaction reference subtitle" } }); @@ -115,6 +125,7 @@ const ExtendedTimelineEvent: React.FC<ExtendedTimelineEventProps> = ({ user, lines, amount, + transactionReference, shippingCostsIncluded, relatedOrder } = event; @@ -204,6 +215,19 @@ const ExtendedTimelineEvent: React.FC<ExtendedTimelineEventProps> = ({ )} </> )} + + {!!transactionReference && ( + <> + <Typography + variant="caption" + color="textSecondary" + className={classNames(classes.eventSubtitle)} + > + {intl.formatMessage(messages.transactionReference)} + </Typography> + <Typography>{transactionReference}</Typography> + </> + )} </TimelineEvent> ); }; diff --git a/src/orders/components/OrderHistory/utils.tsx b/src/orders/components/OrderHistory/utils.tsx index 3d1c3030f..c0d166bc5 100644 --- a/src/orders/components/OrderHistory/utils.tsx +++ b/src/orders/components/OrderHistory/utils.tsx @@ -24,7 +24,8 @@ const timelineEventTypes = { OrderEventsEnum.FULFILLMENT_REFUNDED, OrderEventsEnum.FULFILLMENT_REPLACED, OrderEventsEnum.FULFILLMENT_RETURNED, - OrderEventsEnum.DRAFT_CREATED_FROM_REPLACE + OrderEventsEnum.DRAFT_CREATED_FROM_REPLACE, + OrderEventsEnum.ORDER_MARKED_AS_PAID ], linked: [OrderEventsEnum.ORDER_REPLACEMENT_CREATED], note: [OrderEventsEnum.NOTE_ADDED], From 679b532860ec672367cd9cb8221491a77d8484f7 Mon Sep 17 00:00:00 2001 From: Magdalena Markusik <magdalena.markusik@mirumee.com> Date: Mon, 25 Jan 2021 11:28:44 +0100 Subject: [PATCH 27/35] Update messages --- locale/defaultMessages.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index aba381313..bafa10e42 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -70,6 +70,10 @@ "context": "draft created from replace event title", "string": "Draft was reissued from order " }, + "event title marked as paid": { + "context": "order marked as paid event title", + "string": "Order was marked as paid by" + }, "event title refunded": { "context": "refunded event title", "string": "Products were refunded by " @@ -6885,6 +6889,10 @@ "context": "table column header", "string": "Quantity" }, + "transaction reference subtitle": { + "context": "transaction reference subtitle", + "string": "Transaction reference" + }, "voucherDetailsUnassignCategory": { "context": "unassign category from voucher, button", "string": "Unassign" From 2e9dcd52dd12572c659237734edf3af1549f89f7 Mon Sep 17 00:00:00 2001 From: Magdalena Markusik <magdalena.markusik@mirumee.com> Date: Mon, 25 Jan 2021 11:50:51 +0100 Subject: [PATCH 28/35] Update snapshots --- .../__snapshots__/Stories.test.ts.snap | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index a64488aef..d9bbf9dae 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -14487,6 +14487,16 @@ exports[`Storyshots Orders / OrderHistory default 1`] = ` > Shipment was refunded </div> + <div + class="MuiTypography-root-id OrderHistory-eventSubtitle-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" + > + Transaction reference + </div> + <div + class="MuiTypography-root-id MuiTypography-body1-id" + > + 123 + </div> </div> </div> </div> @@ -102501,6 +102511,16 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = ` > Shipment was refunded </div> + <div + class="MuiTypography-root-id OrderHistory-eventSubtitle-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" + > + Transaction reference + </div> + <div + class="MuiTypography-root-id MuiTypography-body1-id" + > + 123 + </div> </div> </div> </div> @@ -104279,6 +104299,16 @@ exports[`Storyshots Views / Orders / Order details default 1`] = ` > Shipment was refunded </div> + <div + class="MuiTypography-root-id OrderHistory-eventSubtitle-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" + > + Transaction reference + </div> + <div + class="MuiTypography-root-id MuiTypography-body1-id" + > + 123 + </div> </div> </div> </div> @@ -106087,6 +106117,16 @@ exports[`Storyshots Views / Orders / Order details fulfilled 1`] = ` > Shipment was refunded </div> + <div + class="MuiTypography-root-id OrderHistory-eventSubtitle-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" + > + Transaction reference + </div> + <div + class="MuiTypography-root-id MuiTypography-body1-id" + > + 123 + </div> </div> </div> </div> @@ -108463,6 +108503,16 @@ exports[`Storyshots Views / Orders / Order details no customer note 1`] = ` > Shipment was refunded </div> + <div + class="MuiTypography-root-id OrderHistory-eventSubtitle-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" + > + Transaction reference + </div> + <div + class="MuiTypography-root-id MuiTypography-body1-id" + > + 123 + </div> </div> </div> </div> @@ -110271,6 +110321,16 @@ exports[`Storyshots Views / Orders / Order details no payment 1`] = ` > Shipment was refunded </div> + <div + class="MuiTypography-root-id OrderHistory-eventSubtitle-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" + > + Transaction reference + </div> + <div + class="MuiTypography-root-id MuiTypography-body1-id" + > + 123 + </div> </div> </div> </div> @@ -112079,6 +112139,16 @@ exports[`Storyshots Views / Orders / Order details no shipping address 1`] = ` > Shipment was refunded </div> + <div + class="MuiTypography-root-id OrderHistory-eventSubtitle-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" + > + Transaction reference + </div> + <div + class="MuiTypography-root-id MuiTypography-body1-id" + > + 123 + </div> </div> </div> </div> @@ -113887,6 +113957,16 @@ exports[`Storyshots Views / Orders / Order details partially fulfilled 1`] = ` > Shipment was refunded </div> + <div + class="MuiTypography-root-id OrderHistory-eventSubtitle-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" + > + Transaction reference + </div> + <div + class="MuiTypography-root-id MuiTypography-body1-id" + > + 123 + </div> </div> </div> </div> @@ -115695,6 +115775,16 @@ exports[`Storyshots Views / Orders / Order details payment confirmed 1`] = ` > Shipment was refunded </div> + <div + class="MuiTypography-root-id OrderHistory-eventSubtitle-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" + > + Transaction reference + </div> + <div + class="MuiTypography-root-id MuiTypography-body1-id" + > + 123 + </div> </div> </div> </div> @@ -117503,6 +117593,16 @@ exports[`Storyshots Views / Orders / Order details payment error 1`] = ` > Shipment was refunded </div> + <div + class="MuiTypography-root-id OrderHistory-eventSubtitle-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" + > + Transaction reference + </div> + <div + class="MuiTypography-root-id MuiTypography-body1-id" + > + 123 + </div> </div> </div> </div> @@ -119311,6 +119411,16 @@ exports[`Storyshots Views / Orders / Order details pending payment 1`] = ` > Shipment was refunded </div> + <div + class="MuiTypography-root-id OrderHistory-eventSubtitle-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" + > + Transaction reference + </div> + <div + class="MuiTypography-root-id MuiTypography-body1-id" + > + 123 + </div> </div> </div> </div> @@ -121119,6 +121229,16 @@ exports[`Storyshots Views / Orders / Order details refunded payment 1`] = ` > Shipment was refunded </div> + <div + class="MuiTypography-root-id OrderHistory-eventSubtitle-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" + > + Transaction reference + </div> + <div + class="MuiTypography-root-id MuiTypography-body1-id" + > + 123 + </div> </div> </div> </div> @@ -122927,6 +123047,16 @@ exports[`Storyshots Views / Orders / Order details rejected payment 1`] = ` > Shipment was refunded </div> + <div + class="MuiTypography-root-id OrderHistory-eventSubtitle-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" + > + Transaction reference + </div> + <div + class="MuiTypography-root-id MuiTypography-body1-id" + > + 123 + </div> </div> </div> </div> @@ -124735,6 +124865,16 @@ exports[`Storyshots Views / Orders / Order details unfulfilled 1`] = ` > Shipment was refunded </div> + <div + class="MuiTypography-root-id OrderHistory-eventSubtitle-id MuiTypography-caption-id MuiTypography-colorTextSecondary-id" + > + Transaction reference + </div> + <div + class="MuiTypography-root-id MuiTypography-body1-id" + > + 123 + </div> </div> </div> </div> From 9a694071ce757d0f8a269b52b4fcfd2c31e4d102 Mon Sep 17 00:00:00 2001 From: mmarkusik <magdalena.markusik@mirumee.com> Date: Tue, 26 Jan 2021 11:21:54 +0100 Subject: [PATCH 29/35] Saleor 2078/create cypress test ids (#958) * Add first id * Add more ids * Add more test ids * Fix typing * Remove unnecessary import * Update snapshots --- .../components/ChannelForm/ChannelForm.tsx | 12 ++++++++++++ src/channels/components/ChannelForm/types.ts | 5 +++++ src/components/AppHeader/AppHeader.tsx | 6 +++++- src/components/AppLayout/AppChannelSelect.tsx | 1 + .../ChannelsAvailability/ChannelsAvailability.tsx | 6 +++++- .../ChannelsAvailabilityContent.tsx | 5 ++++- .../SingleAutocompleteSelectField.tsx | 4 ++++ .../SingleSelectField/SingleSelectField.tsx | 5 ++++- .../OrderDraftDetails/OrderDraftDetails.tsx | 7 ++++++- .../components/OrderListPage/OrderListPage.tsx | 7 ++++++- src/storybook/__snapshots__/Stories.test.ts.snap | 15 +++++++++++++++ 11 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 src/channels/components/ChannelForm/types.ts diff --git a/src/channels/components/ChannelForm/ChannelForm.tsx b/src/channels/components/ChannelForm/ChannelForm.tsx index 07fca80a3..2d362acaa 100644 --- a/src/channels/components/ChannelForm/ChannelForm.tsx +++ b/src/channels/components/ChannelForm/ChannelForm.tsx @@ -20,6 +20,7 @@ import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; import { useStyles } from "../styles"; +import { ExtendedFormHelperTextProps } from "./types"; export interface FormData { name: string; @@ -80,6 +81,11 @@ export const ChannelForm: React.FC<ChannelFormProps> = ({ helperText={getChannelsErrorMessage(formErrors?.slug, intl)} disabled={disabled} fullWidth + FormHelperTextProps={ + { + "data-testid": "slug-text-input-helper-text" + } as ExtendedFormHelperTextProps + } label={intl.formatMessage({ defaultMessage: "Slug", description: "channel slug" @@ -124,8 +130,14 @@ export const ChannelForm: React.FC<ChannelFormProps> = ({ <CardContent> {!!currencyCodes ? ( <SingleAutocompleteSelectField + data-test-id="channel-currency-select-input" allowCustomValues error={!!formErrors.currencyCode} + FormHelperTextProps={ + { + "data-testid": "currency-text-input-helper-text" + } as ExtendedFormHelperTextProps + } helperText={getChannelsErrorMessage( formErrors?.currencyCode, intl diff --git a/src/channels/components/ChannelForm/types.ts b/src/channels/components/ChannelForm/types.ts new file mode 100644 index 000000000..fffa9d986 --- /dev/null +++ b/src/channels/components/ChannelForm/types.ts @@ -0,0 +1,5 @@ +import { FormHelperTextProps } from "@material-ui/core/FormHelperText"; + +export type ExtendedFormHelperTextProps = FormHelperTextProps & { + "data-testid": string; +}; diff --git a/src/components/AppHeader/AppHeader.tsx b/src/components/AppHeader/AppHeader.tsx index d38731269..686deea8e 100644 --- a/src/components/AppHeader/AppHeader.tsx +++ b/src/components/AppHeader/AppHeader.tsx @@ -61,7 +61,11 @@ const AppHeader: React.FC<AppHeaderProps> = props => { {anchor => anchor ? ( <Portal container={anchor.current}> - <div className={classes.root} onClick={onBack}> + <div + className={classes.root} + onClick={onBack} + data-test-id="app-header-back-button" + > <ArrowBackIcon className={classes.backArrow} /> {children ? ( <Typography className={classes.title}>{children}</Typography> diff --git a/src/components/AppLayout/AppChannelSelect.tsx b/src/components/AppLayout/AppChannelSelect.tsx index c9385288a..944443585 100644 --- a/src/components/AppLayout/AppChannelSelect.tsx +++ b/src/components/AppLayout/AppChannelSelect.tsx @@ -38,6 +38,7 @@ const AppChannelSelect: React.FC<AppChannelSelectProps> = ({ return ( <div className={classes.root}> <SingleSelectField + testId="app-channel-select" choices={mapNodeToChoice(channels)} disabled={disabled} value={selectedChannelId} diff --git a/src/components/ChannelsAvailability/ChannelsAvailability.tsx b/src/components/ChannelsAvailability/ChannelsAvailability.tsx index 87c1a415e..b2f36976d 100644 --- a/src/components/ChannelsAvailability/ChannelsAvailability.tsx +++ b/src/components/ChannelsAvailability/ChannelsAvailability.tsx @@ -417,7 +417,11 @@ export const ChannelsAvailability: React.FC<ChannelsAvailabilityProps> = props = userPermissions={user?.userPermissions || []} requiredPermissions={[PermissionEnum.MANAGE_CHANNELS]} > - <Button color="primary" onClick={openModal}> + <Button + color="primary" + onClick={openModal} + data-test-id="channels-availiability-manage-button" + > {intl.formatMessage({ defaultMessage: "Manage", description: "section header button" diff --git a/src/components/ChannelsAvailabilityContent/ChannelsAvailabilityContent.tsx b/src/components/ChannelsAvailabilityContent/ChannelsAvailabilityContent.tsx index be3d3d119..7b7ef4902 100644 --- a/src/components/ChannelsAvailabilityContent/ChannelsAvailabilityContent.tsx +++ b/src/components/ChannelsAvailabilityContent/ChannelsAvailabilityContent.tsx @@ -77,7 +77,10 @@ export const ChannelsAvailabilityContent: React.FC<ChannelsAvailabilityContentPr <Typography className={classes.contentTitle}> <FormattedMessage defaultMessage="Channels A to Z" /> </Typography> - <div className={classes.scrollArea}> + <div + className={classes.scrollArea} + data-test-id="manage-products-channels-availiability-list" + > {filteredChannels?.length ? ( filteredChannels.map(option => ( <div key={option.id} className={classes.option}> diff --git a/src/components/SingleAutocompleteSelectField/SingleAutocompleteSelectField.tsx b/src/components/SingleAutocompleteSelectField/SingleAutocompleteSelectField.tsx index d9c551daa..389fa570f 100644 --- a/src/components/SingleAutocompleteSelectField/SingleAutocompleteSelectField.tsx +++ b/src/components/SingleAutocompleteSelectField/SingleAutocompleteSelectField.tsx @@ -1,6 +1,7 @@ import { InputProps } from "@material-ui/core/Input"; import { makeStyles } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; +import { ExtendedFormHelperTextProps } from "@saleor/channels/components/ChannelForm/types"; import { FetchMoreProps } from "@saleor/types"; import classNames from "classnames"; import Downshift, { ControllerStateAndHelpers } from "downshift"; @@ -42,6 +43,7 @@ export interface SingleAutocompleteSelectFieldProps InputProps?: InputProps; fetchChoices?: (value: string) => void; onChange: (event: React.ChangeEvent<any>) => void; + FormHelperTextProps?: ExtendedFormHelperTextProps; } const DebounceAutocomplete: React.ComponentType<DebounceProps< @@ -69,6 +71,7 @@ const SingleAutocompleteSelectFieldComponent: React.FC<SingleAutocompleteSelectF fetchChoices, onChange, onFetchMore, + FormHelperTextProps, ...rest } = props; const classes = useStyles(props); @@ -178,6 +181,7 @@ const SingleAutocompleteSelectFieldComponent: React.FC<SingleAutocompleteSelectF error={error} disabled={disabled} helperText={helperText} + FormHelperTextProps={FormHelperTextProps} label={label} fullWidth={true} /> diff --git a/src/components/SingleSelectField/SingleSelectField.tsx b/src/components/SingleSelectField/SingleSelectField.tsx index a42723d64..351b2bd55 100644 --- a/src/components/SingleSelectField/SingleSelectField.tsx +++ b/src/components/SingleSelectField/SingleSelectField.tsx @@ -35,6 +35,7 @@ export interface Choice { export type Choices = Choice[]; interface SingleSelectFieldProps { + testId?: string; choices: Choices; className?: string; disabled?: boolean; @@ -62,7 +63,8 @@ export const SingleSelectField: React.FC<SingleSelectFieldProps> = props => { hint, selectProps, placeholder, - InputProps + InputProps, + testId } = props; const classes = useStyles(props); @@ -84,6 +86,7 @@ export const SingleSelectField: React.FC<SingleSelectFieldProps> = props => { {label} </InputLabel> <Select + data-test-id={testId} variant="outlined" fullWidth renderValue={choiceValue => diff --git a/src/orders/components/OrderDraftDetails/OrderDraftDetails.tsx b/src/orders/components/OrderDraftDetails/OrderDraftDetails.tsx index eed8d429f..f94a64705 100644 --- a/src/orders/components/OrderDraftDetails/OrderDraftDetails.tsx +++ b/src/orders/components/OrderDraftDetails/OrderDraftDetails.tsx @@ -44,7 +44,12 @@ const OrderDraftDetails: React.FC<OrderDraftDetailsProps> = ({ toolbar={ !disabled && order?.channel?.isActive && ( - <Button color="primary" variant="text" onClick={onOrderLineAdd}> + <Button + color="primary" + variant="text" + onClick={onOrderLineAdd} + data-test-id="add-products-button" + > <FormattedMessage defaultMessage="Add products" description="button" diff --git a/src/orders/components/OrderListPage/OrderListPage.tsx b/src/orders/components/OrderListPage/OrderListPage.tsx index 555be172e..51a187d02 100644 --- a/src/orders/components/OrderListPage/OrderListPage.tsx +++ b/src/orders/components/OrderListPage/OrderListPage.tsx @@ -72,7 +72,12 @@ const OrderListPage: React.FC<OrderListPageProps> = ({ ]} /> )} - <Button color="primary" variant="contained" onClick={onAdd}> + <Button + color="primary" + variant="contained" + onClick={onAdd} + data-test-id="create-order-button" + > <FormattedMessage defaultMessage="Create order" description="button" diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index a64488aef..3cbf73a2b 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -2688,6 +2688,7 @@ exports[`Storyshots Generics / ChannelsAvailability default 1`] = ` > <button class="MuiButtonBase-root-id MuiButton-root-id MuiButton-text-id MuiButton-textPrimary-id" + data-test-id="channels-availiability-manage-button" tabindex="0" type="button" > @@ -2773,6 +2774,7 @@ exports[`Storyshots Generics / ChannelsAvailability with onChange 1`] = ` > <button class="MuiButtonBase-root-id MuiButton-root-id MuiButton-text-id MuiButton-textPrimary-id" + data-test-id="channels-availiability-manage-button" tabindex="0" type="button" > @@ -46366,6 +46368,7 @@ exports[`Storyshots Views / Channels / Channel details default 1`] = ` > <div class="SingleAutocompleteSelectField-container-id" + data-test-id="channel-currency-select-input" > <div class="MuiFormControl-root-id MuiTextField-root-id MuiFormControl-fullWidth-id" @@ -46627,6 +46630,7 @@ exports[`Storyshots Views / Channels / Channel details disabled 1`] = ` > <div class="SingleAutocompleteSelectField-container-id" + data-test-id="channel-currency-select-input" > <div class="MuiFormControl-root-id MuiTextField-root-id MuiFormControl-fullWidth-id" @@ -46887,6 +46891,7 @@ exports[`Storyshots Views / Channels / Channel details loading 1`] = ` > <div class="SingleAutocompleteSelectField-container-id" + data-test-id="channel-currency-select-input" > <div class="MuiFormControl-root-id MuiTextField-root-id MuiFormControl-fullWidth-id" @@ -47146,6 +47151,7 @@ exports[`Storyshots Views / Channels / Channel details with data 1`] = ` > <div class="SingleAutocompleteSelectField-container-id" + data-test-id="channel-currency-select-input" > <div class="MuiFormControl-root-id MuiTextField-root-id MuiFormControl-fullWidth-id" @@ -47372,6 +47378,7 @@ exports[`Storyshots Views / Channels / Channel details with errors 1`] = ` </div> <p class="MuiFormHelperText-root-id MuiFormHelperText-contained-id MuiFormHelperText-error-id" + data-testid="slug-text-input-helper-text" > Slug must be unique </p> @@ -47410,6 +47417,7 @@ exports[`Storyshots Views / Channels / Channel details with errors 1`] = ` > <div class="SingleAutocompleteSelectField-container-id" + data-test-id="channel-currency-select-input" > <div class="MuiFormControl-root-id MuiTextField-root-id MuiFormControl-fullWidth-id" @@ -48149,6 +48157,7 @@ exports[`Storyshots Views / Channels / Channel form with errors 1`] = ` </div> <p class="MuiFormHelperText-root-id MuiFormHelperText-contained-id MuiFormHelperText-error-id MuiFormHelperText-filled-id" + data-testid="slug-text-input-helper-text" > Slug must be unique </p> @@ -125137,6 +125146,7 @@ exports[`Storyshots Views / Orders / Order draft default 1`] = ` > <button class="MuiButtonBase-root-id MuiButton-root-id MuiButton-text-id MuiButton-textPrimary-id" + data-test-id="add-products-button" tabindex="0" type="button" > @@ -126212,6 +126222,7 @@ exports[`Storyshots Views / Orders / Order draft no user permissions 1`] = ` > <button class="MuiButtonBase-root-id MuiButton-root-id MuiButton-text-id MuiButton-textPrimary-id" + data-test-id="add-products-button" tabindex="0" type="button" > @@ -126855,6 +126866,7 @@ exports[`Storyshots Views / Orders / Order draft without lines 1`] = ` > <button class="MuiButtonBase-root-id MuiButton-root-id MuiButton-text-id MuiButton-textPrimary-id" + data-test-id="add-products-button" tabindex="0" type="button" > @@ -127191,6 +127203,7 @@ exports[`Storyshots Views / Orders / Order list default 1`] = ` </div> <button class="MuiButtonBase-root-id MuiButton-root-id MuiButton-contained-id MuiButton-containedPrimary-id" + data-test-id="create-order-button" tabindex="0" type="button" > @@ -128539,6 +128552,7 @@ exports[`Storyshots Views / Orders / Order list loading 1`] = ` </div> <button class="MuiButtonBase-root-id MuiButton-root-id MuiButton-contained-id MuiButton-containedPrimary-id" + data-test-id="create-order-button" tabindex="0" type="button" > @@ -128997,6 +129011,7 @@ exports[`Storyshots Views / Orders / Order list when no data 1`] = ` </div> <button class="MuiButtonBase-root-id MuiButton-root-id MuiButton-contained-id MuiButton-containedPrimary-id" + data-test-id="create-order-button" tabindex="0" type="button" > From 0649879873a420a97459ee958272426161ebed34 Mon Sep 17 00:00:00 2001 From: Magdalena Markusik <magdalena.markusik@mirumee.com> Date: Tue, 26 Jan 2021 11:27:33 +0100 Subject: [PATCH 30/35] Trigger deployment From 6477d62409d15a20f8f48cd84bc0372a5a0553b4 Mon Sep 17 00:00:00 2001 From: Maciej Korycinski <korycinski.maciej@gmail.com> Date: Wed, 27 Jan 2021 11:42:08 +0100 Subject: [PATCH 31/35] Add missing error (#965) --- src/apps/mutations.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/apps/mutations.ts b/src/apps/mutations.ts index cc2f79976..185383caf 100644 --- a/src/apps/mutations.ts +++ b/src/apps/mutations.ts @@ -79,6 +79,7 @@ export const appDeleteFailedInstallationMutation = gql` `; export const appFetchMutation = gql` + ${appErrorFragment} mutation AppFetch($manifestUrl: String!) { appFetchManifest(manifestUrl: $manifestUrl) { manifest { From 1645e2fdab136c0211b43a318b1a6caca60784b3 Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk <tarasiukdawid@gmail.com> Date: Tue, 26 Jan 2021 23:04:54 +0100 Subject: [PATCH 32/35] Implement Oauth2 flow with OpenID --- .github/workflows/test-env-deploy.yml | 2 +- CHANGELOG.md | 1 + locale/defaultMessages.json | 17 +- schema.graphql | 49 +++ src/auth/AuthProvider.test.ts | 14 +- src/auth/AuthProvider.tsx | 220 +---------- .../LoginLoading/LoginLoading.stories.tsx} | 4 +- .../LoginPage/LoginPage.stories.tsx} | 19 +- src/auth/components/LoginPage/LoginPage.tsx | 112 ++++-- src/auth/components/LoginPage/form.tsx | 73 ++++ src/auth/hooks/useAuthProvider.ts | 58 +++ src/auth/hooks/useExternalAuthProvider.ts | 262 +++++++++++++ src/auth/hooks/useSaleorAuthProvider.ts | 204 ++++++++++ src/auth/index.tsx | 33 +- src/auth/mutations.ts | 49 +++ src/auth/queries.ts | 12 + .../types/AvailableExternalAuthentications.ts | 22 ++ src/auth/types/ExternalAuthenticationUrl.ts | 30 ++ src/auth/types/ExternalObtainAccessTokens.ts | 54 +++ src/auth/types/ExternalRefreshToken.ts | 21 + src/auth/types/ExternalVerifyToken.ts | 46 +++ src/auth/urls.ts | 9 + src/auth/views/Login.tsx | 89 ++++- src/config.ts | 1 + src/storybook/UserDecorator.tsx | 2 + .../__snapshots__/Stories.test.ts.snap | 362 ++++++++++++------ src/storybook/config.js | 4 - 27 files changed, 1391 insertions(+), 378 deletions(-) rename src/{storybook/stories/auth/LoginLoading.tsx => auth/components/LoginLoading/LoginLoading.stories.tsx} (66%) rename src/{storybook/stories/auth/LoginPage.tsx => auth/components/LoginPage/LoginPage.stories.tsx} (51%) create mode 100644 src/auth/components/LoginPage/form.tsx create mode 100644 src/auth/hooks/useAuthProvider.ts create mode 100644 src/auth/hooks/useExternalAuthProvider.ts create mode 100644 src/auth/hooks/useSaleorAuthProvider.ts create mode 100644 src/auth/queries.ts create mode 100644 src/auth/types/AvailableExternalAuthentications.ts create mode 100644 src/auth/types/ExternalAuthenticationUrl.ts create mode 100644 src/auth/types/ExternalObtainAccessTokens.ts create mode 100644 src/auth/types/ExternalRefreshToken.ts create mode 100644 src/auth/types/ExternalVerifyToken.ts diff --git a/.github/workflows/test-env-deploy.yml b/.github/workflows/test-env-deploy.yml index 17055ec08..d7603e822 100644 --- a/.github/workflows/test-env-deploy.yml +++ b/.github/workflows/test-env-deploy.yml @@ -88,7 +88,7 @@ jobs: aws s3 sync ./build/storybook s3://${{ secrets.AWS_TEST_DEPLOYMENT_BUCKET }}/${{ steps.set-domain.outputs.domain }}/storybook - name: Invalidate cache - run: aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_TEST_CF_DIST_ID }} --paths "/${{ env.domain }}/*" + run: aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_TEST_CF_DIST_ID }} --paths "/${{ steps.set-domain.outputs.domain }}/*" - name: Update deployment status uses: bobheadxi/deployments@v0.4.2 diff --git a/CHANGELOG.md b/CHANGELOG.md index e3deeb342..88c8d972c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ All notable, unreleased changes to this project will be documented in this file. - Add product reference attributes - #948 by @orzechdev - Drop descriptionJson and contentJson fields - #950 by @jwm0 - Add error tracking with Sentry adapter - #956 by @jwm0 +- Add OAuth2 login with OpenID support - #963 by @orzechdev # 2.11.1 diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index bafa10e42..7ea290962 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -1016,12 +1016,23 @@ "src_dot_auth_dot_components_dot_LoginPage_dot_2237029987": { "string": "Password" }, + "src_dot_auth_dot_components_dot_LoginPage_dot_2981302356": { + "context": "link", + "string": "Use this link to recover it" + }, "src_dot_auth_dot_components_dot_LoginPage_dot_3476994590": { "string": "Sorry, your username and/or password are incorrect. Please try again." }, - "src_dot_auth_dot_components_dot_LoginPage_dot_4028609483": { - "context": "button", - "string": "Reset your password" + "src_dot_auth_dot_components_dot_LoginPage_dot_3762459576": { + "context": "description", + "string": "or login using" + }, + "src_dot_auth_dot_components_dot_LoginPage_dot_534894384": { + "string": "Sorry, login went wrong. Please try again." + }, + "src_dot_auth_dot_components_dot_LoginPage_dot_599516345": { + "context": "description", + "string": "Forgot password? {resetPasswordLink}" }, "src_dot_auth_dot_components_dot_NewPasswordPage_dot_1254879564": { "string": "New Password" diff --git a/schema.graphql b/schema.graphql index 0ed146de4..2945ad6ca 100644 --- a/schema.graphql +++ b/schema.graphql @@ -1943,6 +1943,49 @@ enum ExportScope { FILTER } +type ExternalAuthentication { + id: String! + name: String +} + +type ExternalAuthenticationUrl { + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") + authenticationData: JSONString + accountErrors: [AccountError!]! +} + +type ExternalLogout { + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") + logoutData: JSONString + accountErrors: [AccountError!]! +} + +type ExternalObtainAccessTokens { + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") + token: String + refreshToken: String + csrfToken: String + user: User + accountErrors: [AccountError!]! +} + +type ExternalRefresh { + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") + token: String + refreshToken: String + csrfToken: String + user: User + accountErrors: [AccountError!]! +} + +type ExternalVerify { + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") + user: User + isValid: Boolean! + verifyData: JSONString + accountErrors: [AccountError!]! +} + type File { url: String! contentType: String @@ -2765,6 +2808,11 @@ type Mutation { tokenRefresh(csrfToken: String, refreshToken: String): RefreshToken tokenVerify(token: String!): VerifyToken tokensDeactivateAll: DeactivateAllUserTokens + externalAuthenticationUrl(input: JSONString!, pluginId: String!): ExternalAuthenticationUrl + externalObtainAccessTokens(input: JSONString!, pluginId: String!): ExternalObtainAccessTokens + externalRefresh(input: JSONString!, pluginId: String!): ExternalRefresh + externalLogout(input: JSONString!, pluginId: String!): ExternalLogout + externalVerify(input: JSONString!, pluginId: String!): ExternalVerify requestPasswordReset(email: String!, redirectUrl: String!): RequestPasswordReset confirmAccount(email: String!, token: String!): ConfirmAccount setPassword(email: String!, password: String!, token: String!): SetPassword @@ -4906,6 +4954,7 @@ input ShippingZoneUpdateInput { type Shop { availablePaymentGateways(currency: String): [PaymentGateway!]! + availableExternalAuthentications: [ExternalAuthentication!]! availableShippingMethods(channel: String!, address: AddressInput): [ShippingMethod] geolocalization: Geolocalization countries(languageCode: LanguageCodeEnum): [CountryDisplay!]! diff --git a/src/auth/AuthProvider.test.ts b/src/auth/AuthProvider.test.ts index 058ad8b08..7a418071d 100644 --- a/src/auth/AuthProvider.test.ts +++ b/src/auth/AuthProvider.test.ts @@ -2,7 +2,7 @@ import setupApi from "@test/api"; import { act, renderHook } from "@testing-library/react-hooks"; import ApolloClient from "apollo-client"; -import { useAuthProvider } from "./AuthProvider"; +import { useAuthProvider } from "./hooks/useAuthProvider"; import { getTokens, setAuthToken } from "./utils"; const apolloClient = setupApi(); @@ -14,7 +14,7 @@ function renderAuthProvider(apolloClient: ApolloClient<any>) { const notify = jest.fn(); const { result } = renderHook(() => - useAuthProvider(intl as any, notify, apolloClient) + useAuthProvider({ apolloClient, intl: intl as any, notify }) ); return result; @@ -43,7 +43,7 @@ describe("User", () => { await act(() => hook.current.login(adminCredentials.email, adminCredentials.password) ); - expect(hook.current.userContext.email).toBe(adminCredentials.email); + expect(hook.current.user.email).toBe(adminCredentials.email); adminCredentials.token = getTokens().auth; done(); @@ -55,7 +55,7 @@ describe("User", () => { await act(() => hook.current.login(adminCredentials.email, "NotAValidPassword123!") ); - expect(hook.current.userContext).toBe(null); + expect(hook.current.user).toBe(null); done(); }); @@ -69,7 +69,7 @@ describe("User", () => { nonStaffUserCredentials.password ) ); - expect(hook.current.userContext).toBe(undefined); + expect(hook.current.user).toBe(undefined); done(); }); @@ -79,7 +79,7 @@ describe("User", () => { const hook = renderAuthProvider(apolloClient); await act(() => hook.current.autologinPromise.current); - expect(hook.current.userContext.email).toBe(adminCredentials.email); + expect(hook.current.user.email).toBe(adminCredentials.email); done(); }); @@ -89,7 +89,7 @@ describe("User", () => { const hook = renderAuthProvider(apolloClient); await act(() => hook.current.autologinPromise.current); - expect(hook.current.userContext).toBe(undefined); + expect(hook.current.user).toBe(undefined); done(); }); diff --git a/src/auth/AuthProvider.tsx b/src/auth/AuthProvider.tsx index 7cfe3a77a..974cee3f3 100644 --- a/src/auth/AuthProvider.tsx +++ b/src/auth/AuthProvider.tsx @@ -1,197 +1,11 @@ -import { IMessageContext } from "@saleor/components/messages"; -import { DEMO_MODE } from "@saleor/config"; -import { User } from "@saleor/fragments/types/User"; import useNotifier from "@saleor/hooks/useNotifier"; -import { commonMessages } from "@saleor/intl"; -import { getMutationStatus } from "@saleor/misc"; -import errorTracker from "@saleor/services/errorTracking"; -import { - isSupported as isCredentialsManagementAPISupported, - login as loginWithCredentialsManagementAPI, - saveCredentials -} from "@saleor/utils/credentialsManagement"; -import ApolloClient from "apollo-client"; -import React, { useContext, useEffect, useRef, useState } from "react"; -import { useApolloClient, useMutation } from "react-apollo"; -import { IntlShape, useIntl } from "react-intl"; +import React, { useContext } from "react"; +import { useApolloClient } from "react-apollo"; +import { useIntl } from "react-intl"; import { UserContext } from "./"; -import { - tokenAuthMutation, - tokenRefreshMutation, - tokenVerifyMutation -} from "./mutations"; -import { RefreshToken, RefreshTokenVariables } from "./types/RefreshToken"; -import { TokenAuth, TokenAuthVariables } from "./types/TokenAuth"; -import { VerifyToken, VerifyTokenVariables } from "./types/VerifyToken"; -import { - displayDemoMessage, - getTokens, - removeTokens, - setAuthToken, - setTokens -} from "./utils"; - -const persistToken = false; - -export function useAuthProvider( - intl: IntlShape, - notify: IMessageContext, - apolloClient: ApolloClient<any> -) { - const [userContext, setUserContext] = useState<undefined | User>(undefined); - const autologinPromise = useRef<Promise<any>>(); - const refreshPromise = useRef<Promise<boolean>>(); - - useEffect(() => { - const token = getTokens().auth; - if (!!token && !userContext) { - autologinPromise.current = tokenVerify({ variables: { token } }); - } else { - autologinPromise.current = loginWithCredentialsManagementAPI(login); - } - }, []); - - useEffect(() => { - if (userContext) { - const { id, email, firstName, lastName } = userContext; - errorTracker.setUserData({ - email, - id, - username: `${firstName} ${lastName}` - }); - - if (!userContext.isStaff) { - logout(); - notify({ - status: "error", - text: intl.formatMessage(commonMessages.unauthorizedDashboardAccess), - title: intl.formatMessage(commonMessages.insufficientPermissions) - }); - } - } - }, [userContext]); - - const logout = () => { - setUserContext(undefined); - if (isCredentialsManagementAPISupported) { - navigator.credentials.preventSilentAccess(); - } - removeTokens(); - }; - - const [tokenAuth, tokenAuthResult] = useMutation< - TokenAuth, - TokenAuthVariables - >(tokenAuthMutation, { - client: apolloClient, - onCompleted: ({ tokenCreate }) => { - if (tokenCreate.errors.length > 0) { - logout(); - } - - const user = tokenCreate.user; - - // FIXME: Now we set state also when auth fails and returned user is - // `null`, because the LoginView uses this `null` to display error. - setUserContext(user); - if (user) { - setTokens(tokenCreate.token, tokenCreate.csrfToken, persistToken); - } - }, - onError: logout - }); - const [tokenRefresh] = useMutation<RefreshToken, RefreshTokenVariables>( - tokenRefreshMutation, - { - client: apolloClient, - onError: logout - } - ); - const [tokenVerify, tokenVerifyResult] = useMutation< - VerifyToken, - VerifyTokenVariables - >(tokenVerifyMutation, { - client: apolloClient, - onCompleted: result => { - if (result.tokenVerify === null) { - logout(); - } else { - const user = result.tokenVerify?.user; - - if (!!user) { - setUserContext(user); - } - } - }, - onError: logout - }); - - const tokenAuthOpts = { - ...tokenAuthResult, - status: getMutationStatus(tokenAuthResult) - }; - const tokenVerifyOpts = { - ...tokenVerifyResult, - status: getMutationStatus(tokenVerifyResult) - }; - - const onLogin = () => { - if (DEMO_MODE) { - displayDemoMessage(intl, notify); - } - }; - - const login = async (email: string, password: string) => { - const result = await tokenAuth({ variables: { email, password } }); - - if (result && !result.data.tokenCreate.errors.length) { - if (!!onLogin) { - onLogin(); - } - saveCredentials(result.data.tokenCreate.user, password); - - return result.data.tokenCreate.user; - } - - return null; - }; - - const loginByToken = (auth: string, refresh: string, user: User) => { - setUserContext(user); - setTokens(auth, refresh, persistToken); - }; - - const refreshToken = (): Promise<boolean> => { - if (!!refreshPromise.current) { - return refreshPromise.current; - } - - return new Promise(resolve => { - const token = getTokens().refresh; - - return tokenRefresh({ variables: { token } }).then(refreshData => { - if (!!refreshData.data.tokenRefresh?.token) { - setAuthToken(refreshData.data.tokenRefresh.token, persistToken); - return resolve(true); - } - - return resolve(false); - }); - }); - }; - - return { - autologinPromise, - login, - loginByToken, - logout, - refreshToken, - tokenAuthOpts, - tokenVerifyOpts, - userContext - }; -} +import { useAuthProvider } from "./hooks/useAuthProvider"; +import { getTokens } from "./utils"; interface AuthProviderProps { children: React.ReactNode; @@ -202,30 +16,10 @@ const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => { const intl = useIntl(); const notify = useNotifier(); - const { - login, - loginByToken, - logout, - tokenAuthOpts, - refreshToken, - tokenVerifyOpts, - userContext - } = useAuthProvider(intl, notify, apolloClient); + const authProvider = useAuthProvider({ apolloClient, intl, notify }); return ( - <UserContext.Provider - value={{ - login, - loginByToken, - logout, - tokenAuthLoading: tokenAuthOpts.loading, - tokenRefresh: refreshToken, - tokenVerifyLoading: tokenVerifyOpts.loading, - user: userContext - }} - > - {children} - </UserContext.Provider> + <UserContext.Provider value={authProvider}>{children}</UserContext.Provider> ); }; diff --git a/src/storybook/stories/auth/LoginLoading.tsx b/src/auth/components/LoginLoading/LoginLoading.stories.tsx similarity index 66% rename from src/storybook/stories/auth/LoginLoading.tsx rename to src/auth/components/LoginLoading/LoginLoading.stories.tsx index a3cb8fb09..8945047ff 100644 --- a/src/storybook/stories/auth/LoginLoading.tsx +++ b/src/auth/components/LoginLoading/LoginLoading.stories.tsx @@ -1,8 +1,8 @@ +import Decorator from "@saleor/storybook/Decorator"; import { storiesOf } from "@storybook/react"; import React from "react"; -import LoginLoading from "../../../auth/components/LoginLoading"; -import Decorator from "../../Decorator"; +import LoginLoading from "."; storiesOf("Views / Authentication / Verifying remembered user", module) .addDecorator(Decorator) diff --git a/src/storybook/stories/auth/LoginPage.tsx b/src/auth/components/LoginPage/LoginPage.stories.tsx similarity index 51% rename from src/storybook/stories/auth/LoginPage.tsx rename to src/auth/components/LoginPage/LoginPage.stories.tsx index 68a3d0add..a1d05ccdb 100644 --- a/src/storybook/stories/auth/LoginPage.tsx +++ b/src/auth/components/LoginPage/LoginPage.stories.tsx @@ -1,14 +1,24 @@ import { Omit } from "@material-ui/core"; +import CardDecorator from "@saleor/storybook/CardDecorator"; +import Decorator from "@saleor/storybook/Decorator"; import { storiesOf } from "@storybook/react"; import React from "react"; import LoginPage, { LoginCardProps } from "../../../auth/components/LoginPage"; -import CardDecorator from "../../CardDecorator"; -import Decorator from "../../Decorator"; const props: Omit<LoginCardProps, "classes"> = { - disableLoginButton: true, + disabled: false, error: false, + externalAuthentications: [ + { + __typename: "ExternalAuthentication", + id: "auth.plugin.example", + name: "Example auth plugin" + } + ], + externalError: false, + loading: false, + onExternalAuthentication: () => undefined, onPasswordRecovery: undefined, onSubmit: () => undefined }; @@ -18,4 +28,5 @@ storiesOf("Views / Authentication / Log in", module) .addDecorator(Decorator) .add("default", () => <LoginPage {...props} />) .add("error", () => <LoginPage {...props} error={true} />) - .add("loading", () => <LoginPage {...props} disableLoginButton={true} />); + .add("disabled", () => <LoginPage {...props} disabled={true} />) + .add("loading", () => <LoginPage {...props} loading={true} />); diff --git a/src/auth/components/LoginPage/LoginPage.tsx b/src/auth/components/LoginPage/LoginPage.tsx index 700a9d0f4..1cda9ef13 100644 --- a/src/auth/components/LoginPage/LoginPage.tsx +++ b/src/auth/components/LoginPage/LoginPage.tsx @@ -1,18 +1,17 @@ import Button from "@material-ui/core/Button"; +import CircularProgress from "@material-ui/core/CircularProgress"; +import Divider from "@material-ui/core/Divider"; import { makeStyles } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; import Typography from "@material-ui/core/Typography"; -import Form from "@saleor/components/Form"; +import { AvailableExternalAuthentications_shop_availableExternalAuthentications } from "@saleor/auth/types/AvailableExternalAuthentications"; import { FormSpacer } from "@saleor/components/FormSpacer"; -import { DEMO_MODE } from "@saleor/config"; +import { SubmitPromise } from "@saleor/hooks/useForm"; import { commonMessages } from "@saleor/intl"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; -export interface FormData { - email: string; - password: string; -} +import LoginForm, { LoginFormData } from "./form"; const useStyles = makeStyles( theme => ({ @@ -23,7 +22,13 @@ const useStyles = makeStyles( link: { color: theme.palette.primary.main, cursor: "pointer", - textAlign: "center" + textDecoration: "underline" + }, + loading: { + alignItems: "center", + display: "flex", + height: "100vh", + justifyContent: "center" }, loginButton: { width: 140 @@ -43,27 +48,40 @@ const useStyles = makeStyles( export interface LoginCardProps { error: boolean; - disableLoginButton: boolean; + externalError: boolean; + disabled: boolean; + loading: boolean; + externalAuthentications?: AvailableExternalAuthentications_shop_availableExternalAuthentications[]; + onExternalAuthentication: (pluginId: string) => void; onPasswordRecovery: () => void; - onSubmit?(event: FormData); + onSubmit?: (event: LoginFormData) => SubmitPromise; } const LoginCard: React.FC<LoginCardProps> = props => { - const { error, disableLoginButton, onPasswordRecovery, onSubmit } = props; + const { + error, + externalError, + disabled, + loading, + externalAuthentications = [], + onExternalAuthentication, + onPasswordRecovery, + onSubmit + } = props; const classes = useStyles(props); const intl = useIntl(); - let initialFormData = { email: "", password: "" }; - if (DEMO_MODE) { - initialFormData = { - email: "admin@example.com", - password: "admin" - }; + if (loading) { + return ( + <div className={classes.loading}> + <CircularProgress size={128} /> + </div> + ); } return ( - <Form initial={initialFormData} onSubmit={onSubmit}> + <LoginForm onSubmit={onSubmit}> {({ change: handleChange, data, submit: handleSubmit }) => ( <> {error && ( @@ -73,6 +91,13 @@ const LoginCard: React.FC<LoginCardProps> = props => { </Typography> </div> )} + {externalError && ( + <div className={classes.panel} data-test="loginErrorMessage"> + <Typography variant="caption"> + <FormattedMessage defaultMessage="Sorry, login went wrong. Please try again." /> + </Typography> + </div> + )} <TextField autoFocus fullWidth @@ -84,6 +109,7 @@ const LoginCard: React.FC<LoginCardProps> = props => { inputProps={{ "data-test": "email" }} + disabled={disabled} /> <FormSpacer /> <TextField @@ -99,13 +125,14 @@ const LoginCard: React.FC<LoginCardProps> = props => { inputProps={{ "data-test": "password" }} + disabled={disabled} /> <FormSpacer /> <div className={classes.buttonContainer}> <Button className={classes.loginButton} color="primary" - disabled={disableLoginButton} + disabled={disabled} variant="contained" onClick={handleSubmit} type="submit" @@ -115,15 +142,56 @@ const LoginCard: React.FC<LoginCardProps> = props => { </Button> </div> <FormSpacer /> - <Typography className={classes.link} onClick={onPasswordRecovery}> + <Typography> <FormattedMessage - defaultMessage="Reset your password" - description="button" + defaultMessage="Forgot password? {resetPasswordLink}" + description="description" + values={{ + resetPasswordLink: ( + <a className={classes.link} onClick={onPasswordRecovery}> + <FormattedMessage + defaultMessage="Use this link to recover it" + description="link" + /> + </a> + ) + }} /> </Typography> + {externalAuthentications.length > 0 && ( + <> + <FormSpacer /> + <Divider /> + <FormSpacer /> + <Typography> + <FormattedMessage + defaultMessage="or login using" + description="description" + /> + </Typography> + </> + )} + {externalAuthentications.map(externalAuthentication => ( + <React.Fragment key={externalAuthentication.id}> + <FormSpacer /> + <Button + color="primary" + fullWidth + variant="outlined" + size="large" + onClick={() => + onExternalAuthentication(externalAuthentication.id) + } + data-test="external-authentication" + disabled={disabled} + > + {externalAuthentication.name} + </Button> + </React.Fragment> + ))} </> )} - </Form> + </LoginForm> ); }; LoginCard.displayName = "LoginCard"; diff --git a/src/auth/components/LoginPage/form.tsx b/src/auth/components/LoginPage/form.tsx new file mode 100644 index 000000000..8dbd7389d --- /dev/null +++ b/src/auth/components/LoginPage/form.tsx @@ -0,0 +1,73 @@ +import { DEMO_MODE } from "@saleor/config"; +import useForm, { FormChange, SubmitPromise } from "@saleor/hooks/useForm"; +import handleFormSubmit from "@saleor/utils/handlers/handleFormSubmit"; +import React from "react"; + +export interface LoginFormData { + email: string; + password: string; +} + +export interface UseLoginFormResult { + change: FormChange; + data: LoginFormData; + hasChanged: boolean; + submit: () => Promise<boolean>; +} + +export interface LoginFormProps { + children: (props: UseLoginFormResult) => React.ReactNode; + onSubmit: (data: LoginFormData) => SubmitPromise; +} + +const getLoginFormData = () => { + if (DEMO_MODE) { + return { + email: "admin@example.com", + password: "admin" + }; + } + return { email: "", password: "" }; +}; + +function useLoginForm( + onSubmit: (data: LoginFormData) => SubmitPromise +): UseLoginFormResult { + const [changed, setChanged] = React.useState(false); + const triggerChange = () => setChanged(true); + + const form = useForm(getLoginFormData()); + + const handleChange: FormChange = (event, cb) => { + form.change(event, cb); + triggerChange(); + }; + + const data: LoginFormData = { + ...form.data + }; + + const handleSubmit = async (data: LoginFormData) => { + const errors = await onSubmit(data); + + return errors; + }; + + const submit = async () => handleFormSubmit(data, handleSubmit, setChanged); + + return { + change: handleChange, + data, + hasChanged: changed, + submit + }; +} + +const LoginForm: React.FC<LoginFormProps> = ({ children, onSubmit }) => { + const props = useLoginForm(onSubmit); + + return <form onSubmit={props.submit}>{children(props)}</form>; +}; + +LoginForm.displayName = "LoginForm"; +export default LoginForm; diff --git a/src/auth/hooks/useAuthProvider.ts b/src/auth/hooks/useAuthProvider.ts new file mode 100644 index 000000000..ea3260469 --- /dev/null +++ b/src/auth/hooks/useAuthProvider.ts @@ -0,0 +1,58 @@ +import { IMessageContext } from "@saleor/components/messages"; +import { User } from "@saleor/fragments/types/User"; +import useLocalStorage from "@saleor/hooks/useLocalStorage"; +import ApolloClient from "apollo-client"; +import { MutableRefObject } from "react"; +import { IntlShape } from "react-intl"; + +import { useExternalAuthProvider } from "./useExternalAuthProvider"; +import { useSaleorAuthProvider } from "./useSaleorAuthProvider"; + +export interface UseAuthProvider { + logout: () => void; + tokenAuthLoading: boolean; + tokenRefresh: () => Promise<boolean>; + tokenVerifyLoading: boolean; + user?: User; + autologinPromise?: MutableRefObject<Promise<any>>; +} +export interface UseAuthProviderOpts { + intl: IntlShape; + notify: IMessageContext; + apolloClient: ApolloClient<any>; +} + +export function useAuthProvider(opts: UseAuthProviderOpts) { + const [authPlugin, setAuthPlugin] = useLocalStorage("authPlugin", undefined); + + const saleorAuth = useSaleorAuthProvider({ + authPlugin, + setAuthPlugin, + ...opts + }); + + const externalAuth = useExternalAuthProvider({ + authPlugin, + setAuthPlugin, + ...opts + }); + + const loginAuth = { + login: saleorAuth.login, + loginByExternalPlugin: externalAuth.loginByExternalPlugin, + loginByToken: saleorAuth.loginByToken, + requestLoginByExternalPlugin: externalAuth.requestLoginByExternalPlugin + }; + + if (authPlugin) { + return { + ...externalAuth, + ...loginAuth + }; + } + + return { + ...saleorAuth, + ...loginAuth + }; +} diff --git a/src/auth/hooks/useExternalAuthProvider.ts b/src/auth/hooks/useExternalAuthProvider.ts new file mode 100644 index 000000000..72d06e4d6 --- /dev/null +++ b/src/auth/hooks/useExternalAuthProvider.ts @@ -0,0 +1,262 @@ +import { DEMO_MODE } from "@saleor/config"; +import { User } from "@saleor/fragments/types/User"; +import { SetLocalStorage } from "@saleor/hooks/useLocalStorage"; +import { commonMessages } from "@saleor/intl"; +import { getMutationStatus } from "@saleor/misc"; +import errorTracker from "@saleor/services/errorTracking"; +import { useEffect, useRef, useState } from "react"; +import { useMutation } from "react-apollo"; + +import { + externalAuthenticationUrlMutation, + externalObtainAccessTokensMutation, + externalTokenRefreshMutation, + externalTokenVerifyMutation +} from "../mutations"; +import { + ExternalAuthenticationUrl, + ExternalAuthenticationUrlVariables +} from "../types/ExternalAuthenticationUrl"; +import { + ExternalObtainAccessTokens, + ExternalObtainAccessTokens_externalObtainAccessTokens, + ExternalObtainAccessTokensVariables +} from "../types/ExternalObtainAccessTokens"; +import { + ExternalRefreshToken, + ExternalRefreshTokenVariables +} from "../types/ExternalRefreshToken"; +import { + ExternalVerifyToken, + ExternalVerifyTokenVariables +} from "../types/ExternalVerifyToken"; +import { + displayDemoMessage, + getTokens, + removeTokens, + setAuthToken, + setTokens +} from "../utils"; +import { UseAuthProvider, UseAuthProviderOpts } from "./useAuthProvider"; + +export interface RequestExternalLoginInput { + redirectUri: string; +} +export interface ExternalLoginInput { + code: string; + state: string; +} + +export interface UseExternalAuthProvider extends UseAuthProvider { + requestLoginByExternalPlugin: ( + pluginId: string, + input: RequestExternalLoginInput + ) => Promise<void>; + loginByExternalPlugin: ( + input: ExternalLoginInput + ) => Promise<ExternalObtainAccessTokens_externalObtainAccessTokens>; +} +export interface UseExternalAuthProviderOpts extends UseAuthProviderOpts { + setAuthPlugin: SetLocalStorage<any>; + authPlugin: string; +} + +const persistToken = false; + +export function useExternalAuthProvider({ + apolloClient, + authPlugin, + intl, + notify, + setAuthPlugin +}: UseExternalAuthProviderOpts): UseExternalAuthProvider { + const [userContext, setUserContext] = useState<undefined | User>(undefined); + const autologinPromise = useRef<Promise<any>>(); + const refreshPromise = useRef<Promise<boolean>>(); + + useEffect(() => { + const token = getTokens().auth; + if (authPlugin && !!token && !userContext) { + const input = JSON.stringify({ + token + }); + autologinPromise.current = tokenVerify({ + variables: { input, pluginId: authPlugin } + }); + } + }, []); + + useEffect(() => { + if (authPlugin && userContext) { + const { id, email, firstName, lastName } = userContext; + errorTracker.setUserData({ + email, + id, + username: `${firstName} ${lastName}` + }); + + if (!userContext.isStaff) { + logout(); + notify({ + status: "error", + text: intl.formatMessage(commonMessages.unauthorizedDashboardAccess), + title: intl.formatMessage(commonMessages.insufficientPermissions) + }); + } + } + }, [userContext]); + + const logout = () => { + setUserContext(undefined); + setAuthPlugin(undefined); + removeTokens(); + }; + + const [externalAuthenticationUrl] = useMutation< + ExternalAuthenticationUrl, + ExternalAuthenticationUrlVariables + >(externalAuthenticationUrlMutation, { + client: apolloClient, + onError: logout + }); + const [obtainAccessTokens, obtainAccessTokensResult] = useMutation< + ExternalObtainAccessTokens, + ExternalObtainAccessTokensVariables + >(externalObtainAccessTokensMutation, { + client: apolloClient, + onCompleted: ({ externalObtainAccessTokens }) => { + if (externalObtainAccessTokens.errors.length > 0) { + logout(); + } + + const user = externalObtainAccessTokens.user; + + setUserContext(user); + if (user) { + setTokens( + externalObtainAccessTokens.token, + externalObtainAccessTokens.csrfToken, + persistToken + ); + } + }, + onError: logout + }); + const [tokenRefresh] = useMutation< + ExternalRefreshToken, + ExternalRefreshTokenVariables + >(externalTokenRefreshMutation, { + client: apolloClient, + onError: logout + }); + const [tokenVerify, tokenVerifyResult] = useMutation< + ExternalVerifyToken, + ExternalVerifyTokenVariables + >(externalTokenVerifyMutation, { + client: apolloClient, + onCompleted: result => { + if (result.externalVerify === null) { + logout(); + } else { + const user = result.externalVerify?.user; + + if (!!user) { + setUserContext(user); + } + } + }, + onError: logout + }); + + const obtainAccessTokensOpts = { + ...obtainAccessTokensResult, + status: getMutationStatus(obtainAccessTokensResult) + }; + const tokenVerifyOpts = { + ...tokenVerifyResult, + status: getMutationStatus(tokenVerifyResult) + }; + + const onLogin = () => { + if (DEMO_MODE) { + displayDemoMessage(intl, notify); + } + }; + + const requestLoginByExternalPlugin = async ( + pluginId: string, + pluginInput: RequestExternalLoginInput + ) => { + const input = JSON.stringify(pluginInput); + const result = await externalAuthenticationUrl({ + variables: { + input, + pluginId + } + }); + + if (result && !result.data.externalAuthenticationUrl.errors.length) { + setAuthPlugin(pluginId); + + const authenticationData = JSON.parse( + result.data.externalAuthenticationUrl.authenticationData + ); + + location.href = authenticationData.authorizationUrl; + } else { + setAuthPlugin(undefined); + } + }; + + const loginByExternalPlugin = async (loginInput: ExternalLoginInput) => { + const input = JSON.stringify(loginInput); + const result = await obtainAccessTokens({ + variables: { input, pluginId: authPlugin } + }); + + if (result && !result.data?.externalObtainAccessTokens?.errors?.length) { + if (!!onLogin) { + onLogin(); + } + } else { + setAuthPlugin(undefined); + } + + return result?.data?.externalObtainAccessTokens; + }; + + const refreshToken = (): Promise<boolean> => { + if (!!refreshPromise.current) { + return refreshPromise.current; + } + + return new Promise(resolve => { + const token = getTokens().refresh; + const input = JSON.stringify({ + refreshToken: token + }); + + return tokenRefresh({ variables: { input, pluginId: authPlugin } }).then( + refreshData => { + if (!!refreshData.data.externalRefresh?.token) { + setAuthToken(refreshData.data.externalRefresh.token, persistToken); + return resolve(true); + } + + return resolve(false); + } + ); + }); + }; + + return { + autologinPromise, + loginByExternalPlugin, + logout, + requestLoginByExternalPlugin, + tokenAuthLoading: obtainAccessTokensOpts.loading, + tokenRefresh: refreshToken, + tokenVerifyLoading: tokenVerifyOpts.loading, + user: userContext + }; +} diff --git a/src/auth/hooks/useSaleorAuthProvider.ts b/src/auth/hooks/useSaleorAuthProvider.ts new file mode 100644 index 000000000..e671c2e69 --- /dev/null +++ b/src/auth/hooks/useSaleorAuthProvider.ts @@ -0,0 +1,204 @@ +import { DEMO_MODE } from "@saleor/config"; +import { User } from "@saleor/fragments/types/User"; +import { SetLocalStorage } from "@saleor/hooks/useLocalStorage"; +import { commonMessages } from "@saleor/intl"; +import { getMutationStatus } from "@saleor/misc"; +import errorTracker from "@saleor/services/errorTracking"; +import { + isSupported as isCredentialsManagementAPISupported, + login as loginWithCredentialsManagementAPI, + saveCredentials +} from "@saleor/utils/credentialsManagement"; +import { useEffect, useRef, useState } from "react"; +import { useMutation } from "react-apollo"; + +import { + tokenAuthMutation, + tokenRefreshMutation, + tokenVerifyMutation +} from "../mutations"; +import { RefreshToken, RefreshTokenVariables } from "../types/RefreshToken"; +import { + TokenAuth, + TokenAuth_tokenCreate, + TokenAuthVariables +} from "../types/TokenAuth"; +import { VerifyToken, VerifyTokenVariables } from "../types/VerifyToken"; +import { + displayDemoMessage, + getTokens, + removeTokens, + setAuthToken, + setTokens +} from "../utils"; +import { UseAuthProvider, UseAuthProviderOpts } from "./useAuthProvider"; + +export interface UseSaleorAuthProvider extends UseAuthProvider { + login: (username: string, password: string) => Promise<TokenAuth_tokenCreate>; + loginByToken: (auth: string, csrf: string, user: User) => void; +} +export interface UseSaleorAuthProviderOpts extends UseAuthProviderOpts { + setAuthPlugin: SetLocalStorage<any>; + authPlugin: string; +} + +const persistToken = false; + +export function useSaleorAuthProvider({ + apolloClient, + authPlugin, + intl, + notify, + setAuthPlugin +}: UseSaleorAuthProviderOpts): UseSaleorAuthProvider { + const [userContext, setUserContext] = useState<undefined | User>(undefined); + const autologinPromise = useRef<Promise<any>>(); + const refreshPromise = useRef<Promise<boolean>>(); + + useEffect(() => { + const token = getTokens().auth; + if (!authPlugin && !!token && !userContext) { + autologinPromise.current = tokenVerify({ variables: { token } }); + } else if (!authPlugin) { + autologinPromise.current = loginWithCredentialsManagementAPI(login); + } + }, []); + + useEffect(() => { + if (!authPlugin && userContext) { + const { id, email, firstName, lastName } = userContext; + errorTracker.setUserData({ + email, + id, + username: `${firstName} ${lastName}` + }); + + if (!userContext.isStaff) { + logout(); + notify({ + status: "error", + text: intl.formatMessage(commonMessages.unauthorizedDashboardAccess), + title: intl.formatMessage(commonMessages.insufficientPermissions) + }); + } + } + }, [userContext]); + + const logout = () => { + setUserContext(undefined); + if (isCredentialsManagementAPISupported) { + navigator.credentials.preventSilentAccess(); + } + removeTokens(); + }; + + const [tokenAuth, tokenAuthResult] = useMutation< + TokenAuth, + TokenAuthVariables + >(tokenAuthMutation, { + client: apolloClient, + onCompleted: ({ tokenCreate }) => { + if (tokenCreate.errors.length > 0) { + logout(); + } + + const user = tokenCreate.user; + + setUserContext(user); + if (user) { + setTokens(tokenCreate.token, tokenCreate.csrfToken, persistToken); + } + }, + onError: logout + }); + const [tokenRefresh] = useMutation<RefreshToken, RefreshTokenVariables>( + tokenRefreshMutation, + { + client: apolloClient, + onError: logout + } + ); + const [tokenVerify, tokenVerifyResult] = useMutation< + VerifyToken, + VerifyTokenVariables + >(tokenVerifyMutation, { + client: apolloClient, + onCompleted: result => { + if (result.tokenVerify === null) { + logout(); + } else { + const user = result.tokenVerify?.user; + + if (!!user) { + setUserContext(user); + } + } + }, + onError: logout + }); + + const tokenAuthOpts = { + ...tokenAuthResult, + status: getMutationStatus(tokenAuthResult) + }; + const tokenVerifyOpts = { + ...tokenVerifyResult, + status: getMutationStatus(tokenVerifyResult) + }; + + const onLogin = () => { + if (DEMO_MODE) { + displayDemoMessage(intl, notify); + } + }; + + const login = async (email: string, password: string) => { + setAuthPlugin(undefined); + const result = await tokenAuth({ variables: { email, password } }); + + if (result && !result.data.tokenCreate.errors.length) { + if (!!onLogin) { + onLogin(); + } + saveCredentials(result.data.tokenCreate.user, password); + } + + return result.data.tokenCreate; + }; + + const loginByToken = (auth: string, refresh: string, user: User) => { + setAuthPlugin(undefined); + setUserContext(user); + setTokens(auth, refresh, persistToken); + }; + + const refreshToken = (): Promise<boolean> => { + if (!!refreshPromise.current) { + return refreshPromise.current; + } + + return new Promise(resolve => { + const token = getTokens().refresh; + + return tokenRefresh({ variables: { token } }).then(refreshData => { + if (!!refreshData.data.tokenRefresh?.token) { + setAuthToken(refreshData.data.tokenRefresh.token, persistToken); + return resolve(true); + } + + return resolve(false); + }); + }); + }; + + return { + autologinPromise, + login, + loginByToken, + logout, + tokenAuthLoading: tokenAuthOpts.loading, + tokenRefresh: refreshToken, + tokenVerifyLoading: tokenVerifyOpts.loading, + user: userContext + }; +} diff --git a/src/auth/index.tsx b/src/auth/index.tsx index 4865edc02..b46640c16 100644 --- a/src/auth/index.tsx +++ b/src/auth/index.tsx @@ -1,32 +1,57 @@ import { User } from "@saleor/fragments/types/User"; -import React from "react"; -import { Route, Switch } from "react-router-dom"; +import { parse as parseQs } from "qs"; +import React, { MutableRefObject } from "react"; +import { Route, RouteComponentProps, Switch } from "react-router-dom"; import Layout from "./components/Layout"; import { + ExternalLoginInput, + RequestExternalLoginInput +} from "./hooks/useExternalAuthProvider"; +import { ExternalObtainAccessTokens_externalObtainAccessTokens } from "./types/ExternalObtainAccessTokens"; +import { TokenAuth_tokenCreate } from "./types/TokenAuth"; +import { + LoginUrlQueryParams, newPasswordPath, passwordResetPath, passwordResetSuccessPath } from "./urls"; -import LoginView from "./views/Login"; +import LoginViewComponent from "./views/Login"; import NewPassword from "./views/NewPassword"; import ResetPassword from "./views/ResetPassword"; import ResetPasswordSuccess from "./views/ResetPasswordSuccess"; +const LoginView: React.FC<RouteComponentProps<any>> = () => { + const qs = parseQs(location.search.substr(1)); + const params: LoginUrlQueryParams = qs; + + return <LoginViewComponent params={params} />; +}; + interface UserContext { - login: (username: string, password: string) => void; + login: (username: string, password: string) => Promise<TokenAuth_tokenCreate>; + loginByExternalPlugin: ( + input: ExternalLoginInput + ) => Promise<ExternalObtainAccessTokens_externalObtainAccessTokens>; loginByToken: (auth: string, csrf: string, user: User) => void; logout: () => void; + requestLoginByExternalPlugin: ( + pluginId: string, + input: RequestExternalLoginInput + ) => Promise<void>; tokenAuthLoading: boolean; tokenRefresh: () => Promise<boolean>; tokenVerifyLoading: boolean; user?: User; + autologinPromise?: MutableRefObject<Promise<any>>; } export const UserContext = React.createContext<UserContext>({ login: undefined, + loginByExternalPlugin: undefined, loginByToken: undefined, logout: undefined, + requestLoginByExternalPlugin: undefined, tokenAuthLoading: false, tokenRefresh: undefined, tokenVerifyLoading: false diff --git a/src/auth/mutations.ts b/src/auth/mutations.ts index 8c21ce32d..226c23a37 100644 --- a/src/auth/mutations.ts +++ b/src/auth/mutations.ts @@ -82,3 +82,52 @@ export const SetPasswordMutation = TypedMutation< SetPassword, SetPasswordVariables >(setPassword); + +export const externalAuthenticationUrlMutation = gql` + ${accountErrorFragment} + mutation ExternalAuthenticationUrl($pluginId: String!, $input: JSONString!) { + externalAuthenticationUrl(pluginId: $pluginId, input: $input) { + authenticationData + errors: accountErrors { + ...AccountErrorFragment + } + } + } +`; + +export const externalObtainAccessTokensMutation = gql` + ${accountErrorFragment} + ${fragmentUser} + mutation ExternalObtainAccessTokens($pluginId: String!, $input: JSONString!) { + externalObtainAccessTokens(pluginId: $pluginId, input: $input) { + token + csrfToken + user { + ...User + } + errors: accountErrors { + ...AccountErrorFragment + } + } + } +`; + +export const externalTokenRefreshMutation = gql` + mutation ExternalRefreshToken($pluginId: String!, $input: JSONString!) { + externalRefresh(pluginId: $pluginId, input: $input) { + token + } + } +`; + +export const externalTokenVerifyMutation = gql` + ${fragmentUser} + mutation ExternalVerifyToken($pluginId: String!, $input: JSONString!) { + externalVerify(pluginId: $pluginId, input: $input) { + verifyData + user { + ...User + } + } + } +`; diff --git a/src/auth/queries.ts b/src/auth/queries.ts new file mode 100644 index 000000000..18304273e --- /dev/null +++ b/src/auth/queries.ts @@ -0,0 +1,12 @@ +import gql from "graphql-tag"; + +export const availableExternalAuthentications = gql` + query AvailableExternalAuthentications { + shop { + availableExternalAuthentications { + id + name + } + } + } +`; diff --git a/src/auth/types/AvailableExternalAuthentications.ts b/src/auth/types/AvailableExternalAuthentications.ts new file mode 100644 index 000000000..886830ab0 --- /dev/null +++ b/src/auth/types/AvailableExternalAuthentications.ts @@ -0,0 +1,22 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL query operation: AvailableExternalAuthentications +// ==================================================== + +export interface AvailableExternalAuthentications_shop_availableExternalAuthentications { + __typename: "ExternalAuthentication"; + id: string; + name: string | null; +} + +export interface AvailableExternalAuthentications_shop { + __typename: "Shop"; + availableExternalAuthentications: AvailableExternalAuthentications_shop_availableExternalAuthentications[]; +} + +export interface AvailableExternalAuthentications { + shop: AvailableExternalAuthentications_shop; +} diff --git a/src/auth/types/ExternalAuthenticationUrl.ts b/src/auth/types/ExternalAuthenticationUrl.ts new file mode 100644 index 000000000..1fb2fd723 --- /dev/null +++ b/src/auth/types/ExternalAuthenticationUrl.ts @@ -0,0 +1,30 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { AccountErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: ExternalAuthenticationUrl +// ==================================================== + +export interface ExternalAuthenticationUrl_externalAuthenticationUrl_errors { + __typename: "AccountError"; + code: AccountErrorCode; + field: string | null; +} + +export interface ExternalAuthenticationUrl_externalAuthenticationUrl { + __typename: "ExternalAuthenticationUrl"; + authenticationData: any | null; + errors: ExternalAuthenticationUrl_externalAuthenticationUrl_errors[]; +} + +export interface ExternalAuthenticationUrl { + externalAuthenticationUrl: ExternalAuthenticationUrl_externalAuthenticationUrl | null; +} + +export interface ExternalAuthenticationUrlVariables { + pluginId: string; + input: any; +} diff --git a/src/auth/types/ExternalObtainAccessTokens.ts b/src/auth/types/ExternalObtainAccessTokens.ts new file mode 100644 index 000000000..8497ab2b8 --- /dev/null +++ b/src/auth/types/ExternalObtainAccessTokens.ts @@ -0,0 +1,54 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { PermissionEnum, AccountErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: ExternalObtainAccessTokens +// ==================================================== + +export interface ExternalObtainAccessTokens_externalObtainAccessTokens_user_userPermissions { + __typename: "UserPermission"; + code: PermissionEnum; + name: string; +} + +export interface ExternalObtainAccessTokens_externalObtainAccessTokens_user_avatar { + __typename: "Image"; + url: string; +} + +export interface ExternalObtainAccessTokens_externalObtainAccessTokens_user { + __typename: "User"; + id: string; + email: string; + firstName: string; + lastName: string; + isStaff: boolean; + userPermissions: (ExternalObtainAccessTokens_externalObtainAccessTokens_user_userPermissions | null)[] | null; + avatar: ExternalObtainAccessTokens_externalObtainAccessTokens_user_avatar | null; +} + +export interface ExternalObtainAccessTokens_externalObtainAccessTokens_errors { + __typename: "AccountError"; + code: AccountErrorCode; + field: string | null; +} + +export interface ExternalObtainAccessTokens_externalObtainAccessTokens { + __typename: "ExternalObtainAccessTokens"; + token: string | null; + csrfToken: string | null; + user: ExternalObtainAccessTokens_externalObtainAccessTokens_user | null; + errors: ExternalObtainAccessTokens_externalObtainAccessTokens_errors[]; +} + +export interface ExternalObtainAccessTokens { + externalObtainAccessTokens: ExternalObtainAccessTokens_externalObtainAccessTokens | null; +} + +export interface ExternalObtainAccessTokensVariables { + pluginId: string; + input: any; +} diff --git a/src/auth/types/ExternalRefreshToken.ts b/src/auth/types/ExternalRefreshToken.ts new file mode 100644 index 000000000..2c2822403 --- /dev/null +++ b/src/auth/types/ExternalRefreshToken.ts @@ -0,0 +1,21 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL mutation operation: ExternalRefreshToken +// ==================================================== + +export interface ExternalRefreshToken_externalRefresh { + __typename: "ExternalRefresh"; + token: string | null; +} + +export interface ExternalRefreshToken { + externalRefresh: ExternalRefreshToken_externalRefresh | null; +} + +export interface ExternalRefreshTokenVariables { + pluginId: string; + input: any; +} diff --git a/src/auth/types/ExternalVerifyToken.ts b/src/auth/types/ExternalVerifyToken.ts new file mode 100644 index 000000000..8e2851aed --- /dev/null +++ b/src/auth/types/ExternalVerifyToken.ts @@ -0,0 +1,46 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { PermissionEnum } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: ExternalVerifyToken +// ==================================================== + +export interface ExternalVerifyToken_externalVerify_user_userPermissions { + __typename: "UserPermission"; + code: PermissionEnum; + name: string; +} + +export interface ExternalVerifyToken_externalVerify_user_avatar { + __typename: "Image"; + url: string; +} + +export interface ExternalVerifyToken_externalVerify_user { + __typename: "User"; + id: string; + email: string; + firstName: string; + lastName: string; + isStaff: boolean; + userPermissions: (ExternalVerifyToken_externalVerify_user_userPermissions | null)[] | null; + avatar: ExternalVerifyToken_externalVerify_user_avatar | null; +} + +export interface ExternalVerifyToken_externalVerify { + __typename: "ExternalVerify"; + verifyData: any | null; + user: ExternalVerifyToken_externalVerify_user | null; +} + +export interface ExternalVerifyToken { + externalVerify: ExternalVerifyToken_externalVerify | null; +} + +export interface ExternalVerifyTokenVariables { + pluginId: string; + input: any; +} diff --git a/src/auth/urls.ts b/src/auth/urls.ts index c0ac7c68b..fc7c39e08 100644 --- a/src/auth/urls.ts +++ b/src/auth/urls.ts @@ -7,9 +7,18 @@ export const passwordResetSuccessPath = "/reset-password/success/"; export const passwordResetSuccessUrl = passwordResetSuccessPath; export const newPasswordPath = "/new-password/"; + +export const loginCallbackPath = "/login/callback/"; + export interface NewPasswordUrlQueryParams { email: string; token: string; } export const newPasswordUrl = (params?: NewPasswordUrlQueryParams) => newPasswordPath + "?" + stringifyQs(params); + +export interface LoginOpenidconnectUrlQueryParams { + code: string; + state: string; +} +export type LoginUrlQueryParams = LoginOpenidconnectUrlQueryParams; diff --git a/src/auth/views/Login.tsx b/src/auth/views/Login.tsx index 70a7a8dda..2f7b9e294 100644 --- a/src/auth/views/Login.tsx +++ b/src/auth/views/Login.tsx @@ -1,20 +1,93 @@ +import { APP_DEFAULT_URI, APP_MOUNT_URI } from "@saleor/config"; import useNavigator from "@saleor/hooks/useNavigator"; import useUser from "@saleor/hooks/useUser"; -import React from "react"; +import React, { useEffect, useState } from "react"; +import { useQuery } from "react-apollo"; +import urlJoin from "url-join"; +import useRouter from "use-react-router"; -import LoginPage, { FormData } from "../components/LoginPage"; -import { passwordResetUrl } from "../urls"; +import LoginPage from "../components/LoginPage"; +import { LoginFormData } from "../components/LoginPage/form"; +import { availableExternalAuthentications } from "../queries"; +import { AvailableExternalAuthentications } from "../types/AvailableExternalAuthentications"; +import { + loginCallbackPath, + LoginUrlQueryParams, + passwordResetUrl +} from "../urls"; -const LoginView: React.FC = () => { +interface LoginViewProps { + params: LoginUrlQueryParams; +} + +const LoginView: React.FC<LoginViewProps> = ({ params }) => { const navigate = useNavigator(); - const { login, user, tokenAuthLoading } = useUser(); + const { location } = useRouter(); + const { + login, + requestLoginByExternalPlugin, + loginByExternalPlugin, + tokenAuthLoading + } = useUser(); + const [isError, setIsError] = useState(false); + const [isExternalError, setIsExternalError] = useState(false); + const { + data: externalAuthentications, + loading: externalAuthenticationsLoading + } = useQuery<AvailableExternalAuthentications>( + availableExternalAuthentications + ); - const handleSubmit = (data: FormData) => login(data.email, data.password); + const handleSubmit = async (data: LoginFormData) => { + const result = await login(data.email, data.password); + const errors = result?.errors || []; + + setIsExternalError(false); + setIsError(!result || errors?.length > 0); + return errors; + }; + + const handleRequestExternalAuthentication = (pluginId: string) => + requestLoginByExternalPlugin(pluginId, { + redirectUri: urlJoin( + window.location.origin, + APP_MOUNT_URI === APP_DEFAULT_URI ? "" : APP_MOUNT_URI, + loginCallbackPath + ) + }); + + const handleExternalAuthentication = async (code: string, state: string) => { + const result = await loginByExternalPlugin({ code, state }); + const errors = result?.errors || []; + + setIsError(false); + if (!result || errors?.length > 0) { + setIsExternalError(true); + } else { + navigate(APP_DEFAULT_URI); + } + return errors; + }; + + useEffect(() => { + const { code, state } = params; + const isCallbackPath = location.pathname.includes(loginCallbackPath); + + if (code && state && isCallbackPath) { + handleExternalAuthentication(code, state); + } + }, []); return ( <LoginPage - error={user === null} - disableLoginButton={tokenAuthLoading} + error={isError} + externalError={isExternalError} + disabled={tokenAuthLoading} + externalAuthentications={ + externalAuthentications?.shop?.availableExternalAuthentications + } + loading={externalAuthenticationsLoading} + onExternalAuthentication={handleRequestExternalAuthentication} onPasswordRecovery={() => navigate(passwordResetUrl)} onSubmit={handleSubmit} /> diff --git a/src/config.ts b/src/config.ts index f36ee4d6c..d28d7d027 100644 --- a/src/config.ts +++ b/src/config.ts @@ -3,6 +3,7 @@ import { SearchVariables } from "./hooks/makeSearch"; import { ListSettings, ListViews, Pagination } from "./types"; export const APP_MOUNT_URI = process.env.APP_MOUNT_URI; +export const APP_DEFAULT_URI = "/"; export const API_URI = process.env.API_URI; export const DEFAULT_INITIAL_SEARCH_DATA: SearchVariables = { diff --git a/src/storybook/UserDecorator.tsx b/src/storybook/UserDecorator.tsx index 744223271..a420d1b10 100644 --- a/src/storybook/UserDecorator.tsx +++ b/src/storybook/UserDecorator.tsx @@ -6,8 +6,10 @@ export const UserDecorator = (user: User) => storyFn => ( <UserContext.Provider value={{ login: undefined, + loginByExternalPlugin: undefined, loginByToken: undefined, logout: undefined, + requestLoginByExternalPlugin: undefined, tokenAuthLoading: false, tokenRefresh: undefined, tokenVerifyLoading: false, diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index 2116a822c..bf8586f6b 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -36858,6 +36858,165 @@ exports[`Storyshots Views / Authentication / Log in default 1`] = ` <div class="FormSpacer-spacer-id" /> + <div + class="LoginCard-buttonContainer-id" + > + <button + class="MuiButtonBase-root-id MuiButton-root-id MuiButton-contained-id LoginCard-loginButton-id MuiButton-containedPrimary-id" + data-test="submit" + tabindex="0" + type="submit" + > + <span + class="MuiButton-label-id" + > + Login + </span> + </button> + </div> + <div + class="FormSpacer-spacer-id" + /> + <div + class="MuiTypography-root-id MuiTypography-body1-id" + > + Forgot password? + <a + class="LoginCard-link-id" + > + Use this link to recover it + </a> + </div> + <div + class="FormSpacer-spacer-id" + /> + <hr + class="MuiDivider-root-id" + /> + <div + class="FormSpacer-spacer-id" + /> + <div + class="MuiTypography-root-id MuiTypography-body1-id" + > + or login using + </div> + <div + class="FormSpacer-spacer-id" + /> + <button + class="MuiButtonBase-root-id MuiButton-root-id MuiButton-outlined-id MuiButton-outlinedPrimary-id MuiButton-outlinedSizeLarge-id MuiButton-sizeLarge-id MuiButton-fullWidth-id" + data-test="external-authentication" + tabindex="0" + type="button" + > + <span + class="MuiButton-label-id" + > + Example auth plugin + </span> + </button> + </form> + </div> + </div> +</div> +`; + +exports[`Storyshots Views / Authentication / Log in disabled 1`] = ` +<div + style="padding:24px" +> + <div + class="MuiPaper-root-id MuiPaper-elevation0-id MuiCard-root-id MuiPaper-rounded-id" + style="margin:auto;overflow:visible;position:relative;width:400px" + > + <div + class="MuiCardContent-root-id" + > + <form> + <div + class="MuiFormControl-root-id MuiTextField-root-id MuiFormControl-fullWidth-id" + > + <label + class="MuiFormLabel-root-id MuiInputLabel-root-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-outlined-id MuiFormLabel-disabled-id MuiInputLabel-disabled-id" + data-shrink="false" + > + E-mail Address + </label> + <div + class="MuiInputBase-root-id MuiOutlinedInput-root-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id MuiInputBase-fullWidth-id MuiInputBase-formControl-id" + > + <input + aria-invalid="false" + autocomplete="username" + autofocus="" + class="MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id" + data-test="email" + disabled="" + name="email" + type="text" + value="" + /> + <fieldset + aria-hidden="true" + class="PrivateNotchedOutline-root-id MuiOutlinedInput-notchedOutline-id" + style="padding-left:8px" + > + <legend + class="PrivateNotchedOutline-legend-id" + style="width:0.01px" + > + <span> + ​ + </span> + </legend> + </fieldset> + </div> + </div> + <div + class="FormSpacer-spacer-id" + /> + <div + class="MuiFormControl-root-id MuiTextField-root-id MuiFormControl-fullWidth-id" + > + <label + class="MuiFormLabel-root-id MuiInputLabel-root-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-outlined-id MuiFormLabel-disabled-id MuiInputLabel-disabled-id" + data-shrink="false" + > + Password + </label> + <div + class="MuiInputBase-root-id MuiOutlinedInput-root-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id MuiInputBase-fullWidth-id MuiInputBase-formControl-id" + > + <input + aria-invalid="false" + autocomplete="password" + class="MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id" + data-test="password" + disabled="" + name="password" + type="password" + value="" + /> + <fieldset + aria-hidden="true" + class="PrivateNotchedOutline-root-id MuiOutlinedInput-notchedOutline-id" + style="padding-left:8px" + > + <legend + class="PrivateNotchedOutline-legend-id" + style="width:0.01px" + > + <span> + ​ + </span> + </legend> + </fieldset> + </div> + </div> + <div + class="FormSpacer-spacer-id" + /> <div class="LoginCard-buttonContainer-id" > @@ -36879,10 +37038,45 @@ exports[`Storyshots Views / Authentication / Log in default 1`] = ` class="FormSpacer-spacer-id" /> <div - class="MuiTypography-root-id LoginCard-link-id MuiTypography-body1-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - Reset your password + Forgot password? + <a + class="LoginCard-link-id" + > + Use this link to recover it + </a> </div> + <div + class="FormSpacer-spacer-id" + /> + <hr + class="MuiDivider-root-id" + /> + <div + class="FormSpacer-spacer-id" + /> + <div + class="MuiTypography-root-id MuiTypography-body1-id" + > + or login using + </div> + <div + class="FormSpacer-spacer-id" + /> + <button + class="MuiButtonBase-root-id MuiButton-root-id MuiButton-outlined-id MuiButton-outlinedPrimary-id MuiButton-outlinedSizeLarge-id MuiButton-sizeLarge-id MuiButton-disabled-id MuiButton-fullWidth-id MuiButtonBase-disabled-id" + data-test="external-authentication" + disabled="" + tabindex="-1" + type="button" + > + <span + class="MuiButton-label-id" + > + Example auth plugin + </span> + </button> </form> </div> </div> @@ -36996,10 +37190,9 @@ exports[`Storyshots Views / Authentication / Log in error 1`] = ` class="LoginCard-buttonContainer-id" > <button - class="MuiButtonBase-root-id MuiButton-root-id MuiButton-contained-id LoginCard-loginButton-id MuiButton-containedPrimary-id MuiButton-disabled-id MuiButtonBase-disabled-id" + class="MuiButtonBase-root-id MuiButton-root-id MuiButton-contained-id LoginCard-loginButton-id MuiButton-containedPrimary-id" data-test="submit" - disabled="" - tabindex="-1" + tabindex="0" type="submit" > <span @@ -37013,10 +37206,44 @@ exports[`Storyshots Views / Authentication / Log in error 1`] = ` class="FormSpacer-spacer-id" /> <div - class="MuiTypography-root-id LoginCard-link-id MuiTypography-body1-id" + class="MuiTypography-root-id MuiTypography-body1-id" > - Reset your password + Forgot password? + <a + class="LoginCard-link-id" + > + Use this link to recover it + </a> </div> + <div + class="FormSpacer-spacer-id" + /> + <hr + class="MuiDivider-root-id" + /> + <div + class="FormSpacer-spacer-id" + /> + <div + class="MuiTypography-root-id MuiTypography-body1-id" + > + or login using + </div> + <div + class="FormSpacer-spacer-id" + /> + <button + class="MuiButtonBase-root-id MuiButton-root-id MuiButton-outlined-id MuiButton-outlinedPrimary-id MuiButton-outlinedSizeLarge-id MuiButton-sizeLarge-id MuiButton-fullWidth-id" + data-test="external-authentication" + tabindex="0" + type="button" + > + <span + class="MuiButton-label-id" + > + Example auth plugin + </span> + </button> </form> </div> </div> @@ -37034,114 +37261,29 @@ exports[`Storyshots Views / Authentication / Log in loading 1`] = ` <div class="MuiCardContent-root-id" > - <form> + <div + class="LoginCard-loading-id" + > <div - class="MuiFormControl-root-id MuiTextField-root-id MuiFormControl-fullWidth-id" + class="MuiCircularProgress-root-id MuiCircularProgress-colorPrimary-id MuiCircularProgress-indeterminate-id" + role="progressbar" + style="width:128px;height:128px" > - <label - class="MuiFormLabel-root-id MuiInputLabel-root-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-outlined-id" - data-shrink="false" + <svg + class="MuiCircularProgress-svg-id" + viewBox="22 22 44 44" > - E-mail Address - </label> - <div - class="MuiInputBase-root-id MuiOutlinedInput-root-id MuiInputBase-fullWidth-id MuiInputBase-formControl-id" - > - <input - aria-invalid="false" - autocomplete="username" - autofocus="" - class="MuiInputBase-input-id MuiOutlinedInput-input-id" - data-test="email" - name="email" - type="text" - value="" + <circle + class="MuiCircularProgress-circle-id MuiCircularProgress-circleIndeterminate-id" + cx="44" + cy="44" + fill="none" + r="20.2" + stroke-width="3.6" /> - <fieldset - aria-hidden="true" - class="PrivateNotchedOutline-root-id MuiOutlinedInput-notchedOutline-id" - style="padding-left:8px" - > - <legend - class="PrivateNotchedOutline-legend-id" - style="width:0.01px" - > - <span> - ​ - </span> - </legend> - </fieldset> - </div> + </svg> </div> - <div - class="FormSpacer-spacer-id" - /> - <div - class="MuiFormControl-root-id MuiTextField-root-id MuiFormControl-fullWidth-id" - > - <label - class="MuiFormLabel-root-id MuiInputLabel-root-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-outlined-id" - data-shrink="false" - > - Password - </label> - <div - class="MuiInputBase-root-id MuiOutlinedInput-root-id MuiInputBase-fullWidth-id MuiInputBase-formControl-id" - > - <input - aria-invalid="false" - autocomplete="password" - class="MuiInputBase-input-id MuiOutlinedInput-input-id" - data-test="password" - name="password" - type="password" - value="" - /> - <fieldset - aria-hidden="true" - class="PrivateNotchedOutline-root-id MuiOutlinedInput-notchedOutline-id" - style="padding-left:8px" - > - <legend - class="PrivateNotchedOutline-legend-id" - style="width:0.01px" - > - <span> - ​ - </span> - </legend> - </fieldset> - </div> - </div> - <div - class="FormSpacer-spacer-id" - /> - <div - class="LoginCard-buttonContainer-id" - > - <button - class="MuiButtonBase-root-id MuiButton-root-id MuiButton-contained-id LoginCard-loginButton-id MuiButton-containedPrimary-id MuiButton-disabled-id MuiButtonBase-disabled-id" - data-test="submit" - disabled="" - tabindex="-1" - type="submit" - > - <span - class="MuiButton-label-id" - > - Login - </span> - </button> - </div> - <div - class="FormSpacer-spacer-id" - /> - <div - class="MuiTypography-root-id LoginCard-link-id MuiTypography-body1-id" - > - Reset your password - </div> - </form> + </div> </div> </div> </div> diff --git a/src/storybook/config.js b/src/storybook/config.js index be8d805e8..daaff30c3 100644 --- a/src/storybook/config.js +++ b/src/storybook/config.js @@ -44,10 +44,6 @@ function loadStories() { require("./stories/components/WeightRange"); require("./stories/components/messages"); - // Authentication - require("./stories/auth/LoginPage"); - require("./stories/auth/LoginLoading"); - // Attributes require("./stories/attributes/AttributeBulkDeleteDialog"); require("./stories/attributes/AttributeDeleteDialog"); From 8ece366660f730bee4f9edac0cef7020ad35630d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20=C5=BBegle=C5=84?= <flesz3@o2.pl> Date: Tue, 2 Feb 2021 10:38:43 +0100 Subject: [PATCH 33/35] Show channel picker to all staff members (#969) --- src/channels/queries.ts | 18 ++++++++++++++++- src/channels/types/BaseChannels.ts | 20 +++++++++++++++++++ .../AppLayout/AppChannelContext.tsx | 10 +++++----- src/components/AppLayout/AppChannelSelect.tsx | 4 ++-- src/customers/types/CustomerDetails.ts | 4 ++-- src/fragments/channels.ts | 12 +++++++++-- src/fragments/types/ChannelFragment.ts | 16 +++++++++++++++ src/fragments/types/FulfillmentFragment.ts | 2 +- src/fragments/types/MetadataFragment.ts | 2 +- src/fragments/types/OrderDetailsFragment.ts | 18 ++++++++--------- src/fragments/types/OrderLineFragment.ts | 2 +- .../types/RefundOrderLineFragment.ts | 2 +- src/orders/types/FulfillOrder.ts | 18 ++++++++--------- src/orders/types/OrderCancel.ts | 18 ++++++++--------- src/orders/types/OrderCapture.ts | 18 ++++++++--------- src/orders/types/OrderConfirm.ts | 18 ++++++++--------- src/orders/types/OrderDetails.ts | 18 ++++++++--------- src/orders/types/OrderDraftCancel.ts | 18 ++++++++--------- src/orders/types/OrderDraftFinalize.ts | 18 ++++++++--------- src/orders/types/OrderDraftList.ts | 4 ++-- src/orders/types/OrderDraftUpdate.ts | 18 ++++++++--------- src/orders/types/OrderFulfillmentCancel.ts | 18 ++++++++--------- .../types/OrderFulfillmentRefundProducts.ts | 20 +++++++++---------- .../types/OrderFulfillmentUpdateTracking.ts | 18 ++++++++--------- src/orders/types/OrderLineDelete.ts | 18 ++++++++--------- src/orders/types/OrderLineUpdate.ts | 18 ++++++++--------- src/orders/types/OrderLinesAdd.ts | 18 ++++++++--------- src/orders/types/OrderList.ts | 4 ++-- src/orders/types/OrderMarkAsPaid.ts | 18 ++++++++--------- src/orders/types/OrderRefund.ts | 18 ++++++++--------- src/orders/types/OrderRefundData.ts | 10 +++++----- src/orders/types/OrderShippingMethodUpdate.ts | 2 +- src/orders/types/OrderUpdate.ts | 18 ++++++++--------- src/orders/types/OrderVoid.ts | 18 ++++++++--------- .../ProductExportDialog.tsx | 8 ++++---- .../ProductExportDialogInfo.tsx | 10 +++++----- .../views/ProductList/ProductList.tsx | 5 +++-- src/types/globalTypes.ts | 3 +-- src/utils/metadata/types/UpdateMetadata.ts | 2 +- .../metadata/types/UpdatePrivateMetadata.ts | 2 +- 40 files changed, 273 insertions(+), 213 deletions(-) create mode 100644 src/channels/types/BaseChannels.ts create mode 100644 src/fragments/types/ChannelFragment.ts diff --git a/src/channels/queries.ts b/src/channels/queries.ts index c373d0d20..efe2e46b1 100644 --- a/src/channels/queries.ts +++ b/src/channels/queries.ts @@ -1,10 +1,23 @@ -import { channelDetailsFragment } from "@saleor/fragments/channels"; +import { + channelDetailsFragment, + channelFragment +} from "@saleor/fragments/channels"; import makeQuery from "@saleor/hooks/makeQuery"; import gql from "graphql-tag"; +import { BaseChannels } from "./types/BaseChannels"; import { Channel, ChannelVariables } from "./types/Channel"; import { Channels } from "./types/Channels"; +export const channelsListBase = gql` + ${channelFragment} + query BaseChannels { + channels { + ...ChannelFragment + } + } +`; + export const channelsList = gql` ${channelDetailsFragment} query Channels { @@ -23,6 +36,9 @@ export const channelDetails = gql` } `; +export const useBaseChannelsList = makeQuery<BaseChannels, {}>( + channelsListBase +); export const useChannelsList = makeQuery<Channels, {}>(channelsList); export const useChannelDetails = makeQuery<Channel, ChannelVariables>( channelDetails diff --git a/src/channels/types/BaseChannels.ts b/src/channels/types/BaseChannels.ts new file mode 100644 index 000000000..aa43e7ac1 --- /dev/null +++ b/src/channels/types/BaseChannels.ts @@ -0,0 +1,20 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL query operation: BaseChannels +// ==================================================== + +export interface BaseChannels_channels { + __typename: "Channel"; + id: string; + isActive: boolean; + name: string; + slug: string; + currencyCode: string; +} + +export interface BaseChannels { + channels: BaseChannels_channels[] | null; +} diff --git a/src/components/AppLayout/AppChannelContext.tsx b/src/components/AppLayout/AppChannelContext.tsx index 3bfb62fa1..3b479efe7 100644 --- a/src/components/AppLayout/AppChannelContext.tsx +++ b/src/components/AppLayout/AppChannelContext.tsx @@ -1,12 +1,12 @@ import { useAuth } from "@saleor/auth/AuthProvider"; -import { useChannelsList } from "@saleor/channels/queries"; -import { ChannelDetailsFragment } from "@saleor/fragments/types/ChannelDetailsFragment"; +import { useBaseChannelsList } from "@saleor/channels/queries"; +import { ChannelFragment } from "@saleor/fragments/types/ChannelFragment"; import useLocalStorage from "@saleor/hooks/useLocalStorage"; import React from "react"; interface UseAppChannel { - availableChannels: ChannelDetailsFragment[]; - channel: ChannelDetailsFragment; + availableChannels: ChannelFragment[]; + channel: ChannelFragment; isPickerActive: boolean; refreshChannels: () => void; setChannel: (id: string) => void; @@ -27,7 +27,7 @@ const AppChannelContext = React.createContext<AppChannelContextData>({ export const AppChannelProvider: React.FC = ({ children }) => { const { isAuthenticated } = useAuth(); const [selectedChannel, setSelectedChannel] = useLocalStorage("channel", ""); - const { data: channelData, refetch } = useChannelsList({ + const { data: channelData, refetch } = useBaseChannelsList({ skip: !isAuthenticated }); diff --git a/src/components/AppLayout/AppChannelSelect.tsx b/src/components/AppLayout/AppChannelSelect.tsx index 944443585..0bdfeb5f3 100644 --- a/src/components/AppLayout/AppChannelSelect.tsx +++ b/src/components/AppLayout/AppChannelSelect.tsx @@ -1,5 +1,5 @@ import makeStyles from "@material-ui/core/styles/makeStyles"; -import { ChannelDetailsFragment } from "@saleor/fragments/types/ChannelDetailsFragment"; +import { ChannelFragment } from "@saleor/fragments/types/ChannelFragment"; import { ChannelProps } from "@saleor/types"; import { mapNodeToChoice } from "@saleor/utils/maps"; import React from "react"; @@ -22,7 +22,7 @@ const useStyles = makeStyles( ); export interface AppChannelSelectProps extends ChannelProps { - channels: ChannelDetailsFragment[]; + channels: ChannelFragment[]; disabled: boolean; onChannelSelect: (id: string) => void; } diff --git a/src/customers/types/CustomerDetails.ts b/src/customers/types/CustomerDetails.ts index 840e0a996..89532aa61 100644 --- a/src/customers/types/CustomerDetails.ts +++ b/src/customers/types/CustomerDetails.ts @@ -80,8 +80,8 @@ export interface CustomerDetails_user_orders_edges_node { id: string; created: any; number: string | null; - paymentStatus: PaymentChargeStatusEnum | null; - total: CustomerDetails_user_orders_edges_node_total | null; + paymentStatus: PaymentChargeStatusEnum; + total: CustomerDetails_user_orders_edges_node_total; } export interface CustomerDetails_user_orders_edges { diff --git a/src/fragments/channels.ts b/src/fragments/channels.ts index 20b468ded..494dec1cd 100644 --- a/src/fragments/channels.ts +++ b/src/fragments/channels.ts @@ -8,13 +8,21 @@ export const channelErrorFragment = gql` } `; -export const channelDetailsFragment = gql` - fragment ChannelDetailsFragment on Channel { +export const channelFragment = gql` + fragment ChannelFragment on Channel { id isActive name slug currencyCode + } +`; + +export const channelDetailsFragment = gql` + ${channelFragment} + + fragment ChannelDetailsFragment on Channel { + ...ChannelFragment hasOrders } `; diff --git a/src/fragments/types/ChannelFragment.ts b/src/fragments/types/ChannelFragment.ts new file mode 100644 index 000000000..629e7e94f --- /dev/null +++ b/src/fragments/types/ChannelFragment.ts @@ -0,0 +1,16 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL fragment: ChannelFragment +// ==================================================== + +export interface ChannelFragment { + __typename: "Channel"; + id: string; + isActive: boolean; + name: string; + slug: string; + currencyCode: string; +} diff --git a/src/fragments/types/FulfillmentFragment.ts b/src/fragments/types/FulfillmentFragment.ts index 9299ac8f2..94066f13b 100644 --- a/src/fragments/types/FulfillmentFragment.ts +++ b/src/fragments/types/FulfillmentFragment.ts @@ -46,7 +46,7 @@ export interface FulfillmentFragment_lines_orderLine { productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: FulfillmentFragment_lines_orderLine_unitPrice | null; + unitPrice: FulfillmentFragment_lines_orderLine_unitPrice; thumbnail: FulfillmentFragment_lines_orderLine_thumbnail | null; } diff --git a/src/fragments/types/MetadataFragment.ts b/src/fragments/types/MetadataFragment.ts index 53ae1cb06..e5952e01d 100644 --- a/src/fragments/types/MetadataFragment.ts +++ b/src/fragments/types/MetadataFragment.ts @@ -19,7 +19,7 @@ export interface MetadataFragment_privateMetadata { } export interface MetadataFragment { - __typename: "App" | "ShippingZone" | "ShippingMethod" | "Product" | "ProductType" | "Attribute" | "Category" | "ProductVariant" | "DigitalContent" | "Collection" | "Page" | "PageType" | "MenuItem" | "Menu" | "User" | "Checkout" | "Order" | "Fulfillment" | "Invoice"; + __typename: "App" | "Warehouse" | "ShippingZone" | "ShippingMethod" | "Product" | "ProductType" | "Attribute" | "Category" | "ProductVariant" | "DigitalContent" | "Collection" | "Page" | "PageType" | "MenuItem" | "Menu" | "User" | "Checkout" | "Order" | "Fulfillment" | "Invoice"; metadata: (MetadataFragment_metadata | null)[]; privateMetadata: (MetadataFragment_privateMetadata | null)[]; } diff --git a/src/fragments/types/OrderDetailsFragment.ts b/src/fragments/types/OrderDetailsFragment.ts index 104e2109f..1fa3cf605 100644 --- a/src/fragments/types/OrderDetailsFragment.ts +++ b/src/fragments/types/OrderDetailsFragment.ts @@ -125,7 +125,7 @@ export interface OrderDetailsFragment_fulfillments_lines_orderLine { productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderDetailsFragment_fulfillments_lines_orderLine_unitPrice | null; + unitPrice: OrderDetailsFragment_fulfillments_lines_orderLine_unitPrice; thumbnail: OrderDetailsFragment_fulfillments_lines_orderLine_thumbnail | null; } @@ -190,7 +190,7 @@ export interface OrderDetailsFragment_lines { productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderDetailsFragment_lines_unitPrice | null; + unitPrice: OrderDetailsFragment_lines_unitPrice; thumbnail: OrderDetailsFragment_lines_thumbnail | null; } @@ -328,22 +328,22 @@ export interface OrderDetailsFragment { fulfillments: (OrderDetailsFragment_fulfillments | null)[]; lines: (OrderDetailsFragment_lines | null)[]; number: string | null; - paymentStatus: PaymentChargeStatusEnum | null; + paymentStatus: PaymentChargeStatusEnum; shippingAddress: OrderDetailsFragment_shippingAddress | null; shippingMethod: OrderDetailsFragment_shippingMethod | null; shippingMethodName: string | null; - shippingPrice: OrderDetailsFragment_shippingPrice | null; + shippingPrice: OrderDetailsFragment_shippingPrice; status: OrderStatus; - subtotal: OrderDetailsFragment_subtotal | null; - total: OrderDetailsFragment_total | null; + subtotal: OrderDetailsFragment_subtotal; + total: OrderDetailsFragment_total; actions: (OrderAction | null)[]; - totalAuthorized: OrderDetailsFragment_totalAuthorized | null; - totalCaptured: OrderDetailsFragment_totalCaptured | null; + totalAuthorized: OrderDetailsFragment_totalAuthorized; + totalCaptured: OrderDetailsFragment_totalCaptured; user: OrderDetailsFragment_user | null; userEmail: string | null; availableShippingMethods: (OrderDetailsFragment_availableShippingMethods | null)[] | null; discount: OrderDetailsFragment_discount | null; invoices: (OrderDetailsFragment_invoices | null)[] | null; channel: OrderDetailsFragment_channel; - isPaid: boolean | null; + isPaid: boolean; } diff --git a/src/fragments/types/OrderLineFragment.ts b/src/fragments/types/OrderLineFragment.ts index e61e981a1..737dceff4 100644 --- a/src/fragments/types/OrderLineFragment.ts +++ b/src/fragments/types/OrderLineFragment.ts @@ -44,6 +44,6 @@ export interface OrderLineFragment { productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderLineFragment_unitPrice | null; + unitPrice: OrderLineFragment_unitPrice; thumbnail: OrderLineFragment_thumbnail | null; } diff --git a/src/fragments/types/RefundOrderLineFragment.ts b/src/fragments/types/RefundOrderLineFragment.ts index aeb89fa1b..aa7d15d0a 100644 --- a/src/fragments/types/RefundOrderLineFragment.ts +++ b/src/fragments/types/RefundOrderLineFragment.ts @@ -27,6 +27,6 @@ export interface RefundOrderLineFragment { id: string; productName: string; quantity: number; - unitPrice: RefundOrderLineFragment_unitPrice | null; + unitPrice: RefundOrderLineFragment_unitPrice; thumbnail: RefundOrderLineFragment_thumbnail | null; } diff --git a/src/orders/types/FulfillOrder.ts b/src/orders/types/FulfillOrder.ts index 45aeb0896..7693b56ce 100644 --- a/src/orders/types/FulfillOrder.ts +++ b/src/orders/types/FulfillOrder.ts @@ -133,7 +133,7 @@ export interface FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine { productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine_unitPrice | null; + unitPrice: FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine_unitPrice; thumbnail: FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine_thumbnail | null; } @@ -198,7 +198,7 @@ export interface FulfillOrder_orderFulfill_order_lines { productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: FulfillOrder_orderFulfill_order_lines_unitPrice | null; + unitPrice: FulfillOrder_orderFulfill_order_lines_unitPrice; thumbnail: FulfillOrder_orderFulfill_order_lines_thumbnail | null; } @@ -336,24 +336,24 @@ export interface FulfillOrder_orderFulfill_order { fulfillments: (FulfillOrder_orderFulfill_order_fulfillments | null)[]; lines: (FulfillOrder_orderFulfill_order_lines | null)[]; number: string | null; - paymentStatus: PaymentChargeStatusEnum | null; + paymentStatus: PaymentChargeStatusEnum; shippingAddress: FulfillOrder_orderFulfill_order_shippingAddress | null; shippingMethod: FulfillOrder_orderFulfill_order_shippingMethod | null; shippingMethodName: string | null; - shippingPrice: FulfillOrder_orderFulfill_order_shippingPrice | null; + shippingPrice: FulfillOrder_orderFulfill_order_shippingPrice; status: OrderStatus; - subtotal: FulfillOrder_orderFulfill_order_subtotal | null; - total: FulfillOrder_orderFulfill_order_total | null; + subtotal: FulfillOrder_orderFulfill_order_subtotal; + total: FulfillOrder_orderFulfill_order_total; actions: (OrderAction | null)[]; - totalAuthorized: FulfillOrder_orderFulfill_order_totalAuthorized | null; - totalCaptured: FulfillOrder_orderFulfill_order_totalCaptured | null; + totalAuthorized: FulfillOrder_orderFulfill_order_totalAuthorized; + totalCaptured: FulfillOrder_orderFulfill_order_totalCaptured; user: FulfillOrder_orderFulfill_order_user | null; userEmail: string | null; availableShippingMethods: (FulfillOrder_orderFulfill_order_availableShippingMethods | null)[] | null; discount: FulfillOrder_orderFulfill_order_discount | null; invoices: (FulfillOrder_orderFulfill_order_invoices | null)[] | null; channel: FulfillOrder_orderFulfill_order_channel; - isPaid: boolean | null; + isPaid: boolean; } export interface FulfillOrder_orderFulfill { diff --git a/src/orders/types/OrderCancel.ts b/src/orders/types/OrderCancel.ts index 822c51b6f..649fb708f 100644 --- a/src/orders/types/OrderCancel.ts +++ b/src/orders/types/OrderCancel.ts @@ -131,7 +131,7 @@ export interface OrderCancel_orderCancel_order_fulfillments_lines_orderLine { productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderCancel_orderCancel_order_fulfillments_lines_orderLine_unitPrice | null; + unitPrice: OrderCancel_orderCancel_order_fulfillments_lines_orderLine_unitPrice; thumbnail: OrderCancel_orderCancel_order_fulfillments_lines_orderLine_thumbnail | null; } @@ -196,7 +196,7 @@ export interface OrderCancel_orderCancel_order_lines { productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderCancel_orderCancel_order_lines_unitPrice | null; + unitPrice: OrderCancel_orderCancel_order_lines_unitPrice; thumbnail: OrderCancel_orderCancel_order_lines_thumbnail | null; } @@ -334,24 +334,24 @@ export interface OrderCancel_orderCancel_order { fulfillments: (OrderCancel_orderCancel_order_fulfillments | null)[]; lines: (OrderCancel_orderCancel_order_lines | null)[]; number: string | null; - paymentStatus: PaymentChargeStatusEnum | null; + paymentStatus: PaymentChargeStatusEnum; shippingAddress: OrderCancel_orderCancel_order_shippingAddress | null; shippingMethod: OrderCancel_orderCancel_order_shippingMethod | null; shippingMethodName: string | null; - shippingPrice: OrderCancel_orderCancel_order_shippingPrice | null; + shippingPrice: OrderCancel_orderCancel_order_shippingPrice; status: OrderStatus; - subtotal: OrderCancel_orderCancel_order_subtotal | null; - total: OrderCancel_orderCancel_order_total | null; + subtotal: OrderCancel_orderCancel_order_subtotal; + total: OrderCancel_orderCancel_order_total; actions: (OrderAction | null)[]; - totalAuthorized: OrderCancel_orderCancel_order_totalAuthorized | null; - totalCaptured: OrderCancel_orderCancel_order_totalCaptured | null; + totalAuthorized: OrderCancel_orderCancel_order_totalAuthorized; + totalCaptured: OrderCancel_orderCancel_order_totalCaptured; user: OrderCancel_orderCancel_order_user | null; userEmail: string | null; availableShippingMethods: (OrderCancel_orderCancel_order_availableShippingMethods | null)[] | null; discount: OrderCancel_orderCancel_order_discount | null; invoices: (OrderCancel_orderCancel_order_invoices | null)[] | null; channel: OrderCancel_orderCancel_order_channel; - isPaid: boolean | null; + isPaid: boolean; } export interface OrderCancel_orderCancel { diff --git a/src/orders/types/OrderCapture.ts b/src/orders/types/OrderCapture.ts index 3420640b0..0212eeffe 100644 --- a/src/orders/types/OrderCapture.ts +++ b/src/orders/types/OrderCapture.ts @@ -131,7 +131,7 @@ export interface OrderCapture_orderCapture_order_fulfillments_lines_orderLine { productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderCapture_orderCapture_order_fulfillments_lines_orderLine_unitPrice | null; + unitPrice: OrderCapture_orderCapture_order_fulfillments_lines_orderLine_unitPrice; thumbnail: OrderCapture_orderCapture_order_fulfillments_lines_orderLine_thumbnail | null; } @@ -196,7 +196,7 @@ export interface OrderCapture_orderCapture_order_lines { productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderCapture_orderCapture_order_lines_unitPrice | null; + unitPrice: OrderCapture_orderCapture_order_lines_unitPrice; thumbnail: OrderCapture_orderCapture_order_lines_thumbnail | null; } @@ -334,24 +334,24 @@ export interface OrderCapture_orderCapture_order { fulfillments: (OrderCapture_orderCapture_order_fulfillments | null)[]; lines: (OrderCapture_orderCapture_order_lines | null)[]; number: string | null; - paymentStatus: PaymentChargeStatusEnum | null; + paymentStatus: PaymentChargeStatusEnum; shippingAddress: OrderCapture_orderCapture_order_shippingAddress | null; shippingMethod: OrderCapture_orderCapture_order_shippingMethod | null; shippingMethodName: string | null; - shippingPrice: OrderCapture_orderCapture_order_shippingPrice | null; + shippingPrice: OrderCapture_orderCapture_order_shippingPrice; status: OrderStatus; - subtotal: OrderCapture_orderCapture_order_subtotal | null; - total: OrderCapture_orderCapture_order_total | null; + subtotal: OrderCapture_orderCapture_order_subtotal; + total: OrderCapture_orderCapture_order_total; actions: (OrderAction | null)[]; - totalAuthorized: OrderCapture_orderCapture_order_totalAuthorized | null; - totalCaptured: OrderCapture_orderCapture_order_totalCaptured | null; + totalAuthorized: OrderCapture_orderCapture_order_totalAuthorized; + totalCaptured: OrderCapture_orderCapture_order_totalCaptured; user: OrderCapture_orderCapture_order_user | null; userEmail: string | null; availableShippingMethods: (OrderCapture_orderCapture_order_availableShippingMethods | null)[] | null; discount: OrderCapture_orderCapture_order_discount | null; invoices: (OrderCapture_orderCapture_order_invoices | null)[] | null; channel: OrderCapture_orderCapture_order_channel; - isPaid: boolean | null; + isPaid: boolean; } export interface OrderCapture_orderCapture { diff --git a/src/orders/types/OrderConfirm.ts b/src/orders/types/OrderConfirm.ts index fe95a9613..a2e096f95 100644 --- a/src/orders/types/OrderConfirm.ts +++ b/src/orders/types/OrderConfirm.ts @@ -131,7 +131,7 @@ export interface OrderConfirm_orderConfirm_order_fulfillments_lines_orderLine { productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderConfirm_orderConfirm_order_fulfillments_lines_orderLine_unitPrice | null; + unitPrice: OrderConfirm_orderConfirm_order_fulfillments_lines_orderLine_unitPrice; thumbnail: OrderConfirm_orderConfirm_order_fulfillments_lines_orderLine_thumbnail | null; } @@ -196,7 +196,7 @@ export interface OrderConfirm_orderConfirm_order_lines { productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderConfirm_orderConfirm_order_lines_unitPrice | null; + unitPrice: OrderConfirm_orderConfirm_order_lines_unitPrice; thumbnail: OrderConfirm_orderConfirm_order_lines_thumbnail | null; } @@ -334,24 +334,24 @@ export interface OrderConfirm_orderConfirm_order { fulfillments: (OrderConfirm_orderConfirm_order_fulfillments | null)[]; lines: (OrderConfirm_orderConfirm_order_lines | null)[]; number: string | null; - paymentStatus: PaymentChargeStatusEnum | null; + paymentStatus: PaymentChargeStatusEnum; shippingAddress: OrderConfirm_orderConfirm_order_shippingAddress | null; shippingMethod: OrderConfirm_orderConfirm_order_shippingMethod | null; shippingMethodName: string | null; - shippingPrice: OrderConfirm_orderConfirm_order_shippingPrice | null; + shippingPrice: OrderConfirm_orderConfirm_order_shippingPrice; status: OrderStatus; - subtotal: OrderConfirm_orderConfirm_order_subtotal | null; - total: OrderConfirm_orderConfirm_order_total | null; + subtotal: OrderConfirm_orderConfirm_order_subtotal; + total: OrderConfirm_orderConfirm_order_total; actions: (OrderAction | null)[]; - totalAuthorized: OrderConfirm_orderConfirm_order_totalAuthorized | null; - totalCaptured: OrderConfirm_orderConfirm_order_totalCaptured | null; + totalAuthorized: OrderConfirm_orderConfirm_order_totalAuthorized; + totalCaptured: OrderConfirm_orderConfirm_order_totalCaptured; user: OrderConfirm_orderConfirm_order_user | null; userEmail: string | null; availableShippingMethods: (OrderConfirm_orderConfirm_order_availableShippingMethods | null)[] | null; discount: OrderConfirm_orderConfirm_order_discount | null; invoices: (OrderConfirm_orderConfirm_order_invoices | null)[] | null; channel: OrderConfirm_orderConfirm_order_channel; - isPaid: boolean | null; + isPaid: boolean; } export interface OrderConfirm_orderConfirm { diff --git a/src/orders/types/OrderDetails.ts b/src/orders/types/OrderDetails.ts index 34b754218..8030eca4a 100644 --- a/src/orders/types/OrderDetails.ts +++ b/src/orders/types/OrderDetails.ts @@ -125,7 +125,7 @@ export interface OrderDetails_order_fulfillments_lines_orderLine { productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderDetails_order_fulfillments_lines_orderLine_unitPrice | null; + unitPrice: OrderDetails_order_fulfillments_lines_orderLine_unitPrice; thumbnail: OrderDetails_order_fulfillments_lines_orderLine_thumbnail | null; } @@ -190,7 +190,7 @@ export interface OrderDetails_order_lines { productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderDetails_order_lines_unitPrice | null; + unitPrice: OrderDetails_order_lines_unitPrice; thumbnail: OrderDetails_order_lines_thumbnail | null; } @@ -328,24 +328,24 @@ export interface OrderDetails_order { fulfillments: (OrderDetails_order_fulfillments | null)[]; lines: (OrderDetails_order_lines | null)[]; number: string | null; - paymentStatus: PaymentChargeStatusEnum | null; + paymentStatus: PaymentChargeStatusEnum; shippingAddress: OrderDetails_order_shippingAddress | null; shippingMethod: OrderDetails_order_shippingMethod | null; shippingMethodName: string | null; - shippingPrice: OrderDetails_order_shippingPrice | null; + shippingPrice: OrderDetails_order_shippingPrice; status: OrderStatus; - subtotal: OrderDetails_order_subtotal | null; - total: OrderDetails_order_total | null; + subtotal: OrderDetails_order_subtotal; + total: OrderDetails_order_total; actions: (OrderAction | null)[]; - totalAuthorized: OrderDetails_order_totalAuthorized | null; - totalCaptured: OrderDetails_order_totalCaptured | null; + totalAuthorized: OrderDetails_order_totalAuthorized; + totalCaptured: OrderDetails_order_totalCaptured; user: OrderDetails_order_user | null; userEmail: string | null; availableShippingMethods: (OrderDetails_order_availableShippingMethods | null)[] | null; discount: OrderDetails_order_discount | null; invoices: (OrderDetails_order_invoices | null)[] | null; channel: OrderDetails_order_channel; - isPaid: boolean | null; + isPaid: boolean; } export interface OrderDetails_shop_countries { diff --git a/src/orders/types/OrderDraftCancel.ts b/src/orders/types/OrderDraftCancel.ts index 515e4061b..58100ff8a 100644 --- a/src/orders/types/OrderDraftCancel.ts +++ b/src/orders/types/OrderDraftCancel.ts @@ -131,7 +131,7 @@ export interface OrderDraftCancel_draftOrderDelete_order_fulfillments_lines_orde productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderDraftCancel_draftOrderDelete_order_fulfillments_lines_orderLine_unitPrice | null; + unitPrice: OrderDraftCancel_draftOrderDelete_order_fulfillments_lines_orderLine_unitPrice; thumbnail: OrderDraftCancel_draftOrderDelete_order_fulfillments_lines_orderLine_thumbnail | null; } @@ -196,7 +196,7 @@ export interface OrderDraftCancel_draftOrderDelete_order_lines { productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderDraftCancel_draftOrderDelete_order_lines_unitPrice | null; + unitPrice: OrderDraftCancel_draftOrderDelete_order_lines_unitPrice; thumbnail: OrderDraftCancel_draftOrderDelete_order_lines_thumbnail | null; } @@ -334,24 +334,24 @@ export interface OrderDraftCancel_draftOrderDelete_order { fulfillments: (OrderDraftCancel_draftOrderDelete_order_fulfillments | null)[]; lines: (OrderDraftCancel_draftOrderDelete_order_lines | null)[]; number: string | null; - paymentStatus: PaymentChargeStatusEnum | null; + paymentStatus: PaymentChargeStatusEnum; shippingAddress: OrderDraftCancel_draftOrderDelete_order_shippingAddress | null; shippingMethod: OrderDraftCancel_draftOrderDelete_order_shippingMethod | null; shippingMethodName: string | null; - shippingPrice: OrderDraftCancel_draftOrderDelete_order_shippingPrice | null; + shippingPrice: OrderDraftCancel_draftOrderDelete_order_shippingPrice; status: OrderStatus; - subtotal: OrderDraftCancel_draftOrderDelete_order_subtotal | null; - total: OrderDraftCancel_draftOrderDelete_order_total | null; + subtotal: OrderDraftCancel_draftOrderDelete_order_subtotal; + total: OrderDraftCancel_draftOrderDelete_order_total; actions: (OrderAction | null)[]; - totalAuthorized: OrderDraftCancel_draftOrderDelete_order_totalAuthorized | null; - totalCaptured: OrderDraftCancel_draftOrderDelete_order_totalCaptured | null; + totalAuthorized: OrderDraftCancel_draftOrderDelete_order_totalAuthorized; + totalCaptured: OrderDraftCancel_draftOrderDelete_order_totalCaptured; user: OrderDraftCancel_draftOrderDelete_order_user | null; userEmail: string | null; availableShippingMethods: (OrderDraftCancel_draftOrderDelete_order_availableShippingMethods | null)[] | null; discount: OrderDraftCancel_draftOrderDelete_order_discount | null; invoices: (OrderDraftCancel_draftOrderDelete_order_invoices | null)[] | null; channel: OrderDraftCancel_draftOrderDelete_order_channel; - isPaid: boolean | null; + isPaid: boolean; } export interface OrderDraftCancel_draftOrderDelete { diff --git a/src/orders/types/OrderDraftFinalize.ts b/src/orders/types/OrderDraftFinalize.ts index d896114bd..508ff56a2 100644 --- a/src/orders/types/OrderDraftFinalize.ts +++ b/src/orders/types/OrderDraftFinalize.ts @@ -131,7 +131,7 @@ export interface OrderDraftFinalize_draftOrderComplete_order_fulfillments_lines_ productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderDraftFinalize_draftOrderComplete_order_fulfillments_lines_orderLine_unitPrice | null; + unitPrice: OrderDraftFinalize_draftOrderComplete_order_fulfillments_lines_orderLine_unitPrice; thumbnail: OrderDraftFinalize_draftOrderComplete_order_fulfillments_lines_orderLine_thumbnail | null; } @@ -196,7 +196,7 @@ export interface OrderDraftFinalize_draftOrderComplete_order_lines { productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderDraftFinalize_draftOrderComplete_order_lines_unitPrice | null; + unitPrice: OrderDraftFinalize_draftOrderComplete_order_lines_unitPrice; thumbnail: OrderDraftFinalize_draftOrderComplete_order_lines_thumbnail | null; } @@ -334,24 +334,24 @@ export interface OrderDraftFinalize_draftOrderComplete_order { fulfillments: (OrderDraftFinalize_draftOrderComplete_order_fulfillments | null)[]; lines: (OrderDraftFinalize_draftOrderComplete_order_lines | null)[]; number: string | null; - paymentStatus: PaymentChargeStatusEnum | null; + paymentStatus: PaymentChargeStatusEnum; shippingAddress: OrderDraftFinalize_draftOrderComplete_order_shippingAddress | null; shippingMethod: OrderDraftFinalize_draftOrderComplete_order_shippingMethod | null; shippingMethodName: string | null; - shippingPrice: OrderDraftFinalize_draftOrderComplete_order_shippingPrice | null; + shippingPrice: OrderDraftFinalize_draftOrderComplete_order_shippingPrice; status: OrderStatus; - subtotal: OrderDraftFinalize_draftOrderComplete_order_subtotal | null; - total: OrderDraftFinalize_draftOrderComplete_order_total | null; + subtotal: OrderDraftFinalize_draftOrderComplete_order_subtotal; + total: OrderDraftFinalize_draftOrderComplete_order_total; actions: (OrderAction | null)[]; - totalAuthorized: OrderDraftFinalize_draftOrderComplete_order_totalAuthorized | null; - totalCaptured: OrderDraftFinalize_draftOrderComplete_order_totalCaptured | null; + totalAuthorized: OrderDraftFinalize_draftOrderComplete_order_totalAuthorized; + totalCaptured: OrderDraftFinalize_draftOrderComplete_order_totalCaptured; user: OrderDraftFinalize_draftOrderComplete_order_user | null; userEmail: string | null; availableShippingMethods: (OrderDraftFinalize_draftOrderComplete_order_availableShippingMethods | null)[] | null; discount: OrderDraftFinalize_draftOrderComplete_order_discount | null; invoices: (OrderDraftFinalize_draftOrderComplete_order_invoices | null)[] | null; channel: OrderDraftFinalize_draftOrderComplete_order_channel; - isPaid: boolean | null; + isPaid: boolean; } export interface OrderDraftFinalize_draftOrderComplete { diff --git a/src/orders/types/OrderDraftList.ts b/src/orders/types/OrderDraftList.ts index d8ddd8ab5..2ea43c3ad 100644 --- a/src/orders/types/OrderDraftList.ts +++ b/src/orders/types/OrderDraftList.ts @@ -47,9 +47,9 @@ export interface OrderDraftList_draftOrders_edges_node { created: any; id: string; number: string | null; - paymentStatus: PaymentChargeStatusEnum | null; + paymentStatus: PaymentChargeStatusEnum; status: OrderStatus; - total: OrderDraftList_draftOrders_edges_node_total | null; + total: OrderDraftList_draftOrders_edges_node_total; userEmail: string | null; } diff --git a/src/orders/types/OrderDraftUpdate.ts b/src/orders/types/OrderDraftUpdate.ts index 739060f42..2d3a06c0d 100644 --- a/src/orders/types/OrderDraftUpdate.ts +++ b/src/orders/types/OrderDraftUpdate.ts @@ -131,7 +131,7 @@ export interface OrderDraftUpdate_draftOrderUpdate_order_fulfillments_lines_orde productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderDraftUpdate_draftOrderUpdate_order_fulfillments_lines_orderLine_unitPrice | null; + unitPrice: OrderDraftUpdate_draftOrderUpdate_order_fulfillments_lines_orderLine_unitPrice; thumbnail: OrderDraftUpdate_draftOrderUpdate_order_fulfillments_lines_orderLine_thumbnail | null; } @@ -196,7 +196,7 @@ export interface OrderDraftUpdate_draftOrderUpdate_order_lines { productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderDraftUpdate_draftOrderUpdate_order_lines_unitPrice | null; + unitPrice: OrderDraftUpdate_draftOrderUpdate_order_lines_unitPrice; thumbnail: OrderDraftUpdate_draftOrderUpdate_order_lines_thumbnail | null; } @@ -334,24 +334,24 @@ export interface OrderDraftUpdate_draftOrderUpdate_order { fulfillments: (OrderDraftUpdate_draftOrderUpdate_order_fulfillments | null)[]; lines: (OrderDraftUpdate_draftOrderUpdate_order_lines | null)[]; number: string | null; - paymentStatus: PaymentChargeStatusEnum | null; + paymentStatus: PaymentChargeStatusEnum; shippingAddress: OrderDraftUpdate_draftOrderUpdate_order_shippingAddress | null; shippingMethod: OrderDraftUpdate_draftOrderUpdate_order_shippingMethod | null; shippingMethodName: string | null; - shippingPrice: OrderDraftUpdate_draftOrderUpdate_order_shippingPrice | null; + shippingPrice: OrderDraftUpdate_draftOrderUpdate_order_shippingPrice; status: OrderStatus; - subtotal: OrderDraftUpdate_draftOrderUpdate_order_subtotal | null; - total: OrderDraftUpdate_draftOrderUpdate_order_total | null; + subtotal: OrderDraftUpdate_draftOrderUpdate_order_subtotal; + total: OrderDraftUpdate_draftOrderUpdate_order_total; actions: (OrderAction | null)[]; - totalAuthorized: OrderDraftUpdate_draftOrderUpdate_order_totalAuthorized | null; - totalCaptured: OrderDraftUpdate_draftOrderUpdate_order_totalCaptured | null; + totalAuthorized: OrderDraftUpdate_draftOrderUpdate_order_totalAuthorized; + totalCaptured: OrderDraftUpdate_draftOrderUpdate_order_totalCaptured; user: OrderDraftUpdate_draftOrderUpdate_order_user | null; userEmail: string | null; availableShippingMethods: (OrderDraftUpdate_draftOrderUpdate_order_availableShippingMethods | null)[] | null; discount: OrderDraftUpdate_draftOrderUpdate_order_discount | null; invoices: (OrderDraftUpdate_draftOrderUpdate_order_invoices | null)[] | null; channel: OrderDraftUpdate_draftOrderUpdate_order_channel; - isPaid: boolean | null; + isPaid: boolean; } export interface OrderDraftUpdate_draftOrderUpdate { diff --git a/src/orders/types/OrderFulfillmentCancel.ts b/src/orders/types/OrderFulfillmentCancel.ts index 54ecd69ee..97708d011 100644 --- a/src/orders/types/OrderFulfillmentCancel.ts +++ b/src/orders/types/OrderFulfillmentCancel.ts @@ -131,7 +131,7 @@ export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillment productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillments_lines_orderLine_unitPrice | null; + unitPrice: OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillments_lines_orderLine_unitPrice; thumbnail: OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillments_lines_orderLine_thumbnail | null; } @@ -196,7 +196,7 @@ export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_lines { productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderFulfillmentCancel_orderFulfillmentCancel_order_lines_unitPrice | null; + unitPrice: OrderFulfillmentCancel_orderFulfillmentCancel_order_lines_unitPrice; thumbnail: OrderFulfillmentCancel_orderFulfillmentCancel_order_lines_thumbnail | null; } @@ -334,24 +334,24 @@ export interface OrderFulfillmentCancel_orderFulfillmentCancel_order { fulfillments: (OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillments | null)[]; lines: (OrderFulfillmentCancel_orderFulfillmentCancel_order_lines | null)[]; number: string | null; - paymentStatus: PaymentChargeStatusEnum | null; + paymentStatus: PaymentChargeStatusEnum; shippingAddress: OrderFulfillmentCancel_orderFulfillmentCancel_order_shippingAddress | null; shippingMethod: OrderFulfillmentCancel_orderFulfillmentCancel_order_shippingMethod | null; shippingMethodName: string | null; - shippingPrice: OrderFulfillmentCancel_orderFulfillmentCancel_order_shippingPrice | null; + shippingPrice: OrderFulfillmentCancel_orderFulfillmentCancel_order_shippingPrice; status: OrderStatus; - subtotal: OrderFulfillmentCancel_orderFulfillmentCancel_order_subtotal | null; - total: OrderFulfillmentCancel_orderFulfillmentCancel_order_total | null; + subtotal: OrderFulfillmentCancel_orderFulfillmentCancel_order_subtotal; + total: OrderFulfillmentCancel_orderFulfillmentCancel_order_total; actions: (OrderAction | null)[]; - totalAuthorized: OrderFulfillmentCancel_orderFulfillmentCancel_order_totalAuthorized | null; - totalCaptured: OrderFulfillmentCancel_orderFulfillmentCancel_order_totalCaptured | null; + totalAuthorized: OrderFulfillmentCancel_orderFulfillmentCancel_order_totalAuthorized; + totalCaptured: OrderFulfillmentCancel_orderFulfillmentCancel_order_totalCaptured; user: OrderFulfillmentCancel_orderFulfillmentCancel_order_user | null; userEmail: string | null; availableShippingMethods: (OrderFulfillmentCancel_orderFulfillmentCancel_order_availableShippingMethods | null)[] | null; discount: OrderFulfillmentCancel_orderFulfillmentCancel_order_discount | null; invoices: (OrderFulfillmentCancel_orderFulfillmentCancel_order_invoices | null)[] | null; channel: OrderFulfillmentCancel_orderFulfillmentCancel_order_channel; - isPaid: boolean | null; + isPaid: boolean; } export interface OrderFulfillmentCancel_orderFulfillmentCancel { diff --git a/src/orders/types/OrderFulfillmentRefundProducts.ts b/src/orders/types/OrderFulfillmentRefundProducts.ts index 5cc66f5a3..6f516cd1e 100644 --- a/src/orders/types/OrderFulfillmentRefundProducts.ts +++ b/src/orders/types/OrderFulfillmentRefundProducts.ts @@ -52,7 +52,7 @@ export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_f productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_fulfillment_lines_orderLine_unitPrice | null; + unitPrice: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_fulfillment_lines_orderLine_unitPrice; thumbnail: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_fulfillment_lines_orderLine_thumbnail | null; } @@ -196,7 +196,7 @@ export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_o productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_fulfillments_lines_orderLine_unitPrice | null; + unitPrice: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_fulfillments_lines_orderLine_unitPrice; thumbnail: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_fulfillments_lines_orderLine_thumbnail | null; } @@ -261,7 +261,7 @@ export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_o productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_lines_unitPrice | null; + unitPrice: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_lines_unitPrice; thumbnail: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_lines_thumbnail | null; } @@ -399,24 +399,24 @@ export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_o fulfillments: (OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_fulfillments | null)[]; lines: (OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_lines | null)[]; number: string | null; - paymentStatus: PaymentChargeStatusEnum | null; + paymentStatus: PaymentChargeStatusEnum; shippingAddress: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_shippingAddress | null; shippingMethod: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_shippingMethod | null; shippingMethodName: string | null; - shippingPrice: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_shippingPrice | null; + shippingPrice: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_shippingPrice; status: OrderStatus; - subtotal: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_subtotal | null; - total: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_total | null; + subtotal: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_subtotal; + total: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_total; actions: (OrderAction | null)[]; - totalAuthorized: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_totalAuthorized | null; - totalCaptured: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_totalCaptured | null; + totalAuthorized: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_totalAuthorized; + totalCaptured: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_totalCaptured; user: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_user | null; userEmail: string | null; availableShippingMethods: (OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_availableShippingMethods | null)[] | null; discount: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_discount | null; invoices: (OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_invoices | null)[] | null; channel: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_channel; - isPaid: boolean | null; + isPaid: boolean; } export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts { diff --git a/src/orders/types/OrderFulfillmentUpdateTracking.ts b/src/orders/types/OrderFulfillmentUpdateTracking.ts index 810f4c66f..f3bc4a753 100644 --- a/src/orders/types/OrderFulfillmentUpdateTracking.ts +++ b/src/orders/types/OrderFulfillmentUpdateTracking.ts @@ -131,7 +131,7 @@ export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_o productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_fulfillments_lines_orderLine_unitPrice | null; + unitPrice: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_fulfillments_lines_orderLine_unitPrice; thumbnail: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_fulfillments_lines_orderLine_thumbnail | null; } @@ -196,7 +196,7 @@ export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_o productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_lines_unitPrice | null; + unitPrice: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_lines_unitPrice; thumbnail: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_lines_thumbnail | null; } @@ -334,24 +334,24 @@ export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_o fulfillments: (OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_fulfillments | null)[]; lines: (OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_lines | null)[]; number: string | null; - paymentStatus: PaymentChargeStatusEnum | null; + paymentStatus: PaymentChargeStatusEnum; shippingAddress: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_shippingAddress | null; shippingMethod: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_shippingMethod | null; shippingMethodName: string | null; - shippingPrice: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_shippingPrice | null; + shippingPrice: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_shippingPrice; status: OrderStatus; - subtotal: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_subtotal | null; - total: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_total | null; + subtotal: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_subtotal; + total: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_total; actions: (OrderAction | null)[]; - totalAuthorized: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_totalAuthorized | null; - totalCaptured: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_totalCaptured | null; + totalAuthorized: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_totalAuthorized; + totalCaptured: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_totalCaptured; user: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_user | null; userEmail: string | null; availableShippingMethods: (OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_availableShippingMethods | null)[] | null; discount: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_discount | null; invoices: (OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_invoices | null)[] | null; channel: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_channel; - isPaid: boolean | null; + isPaid: boolean; } export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking { diff --git a/src/orders/types/OrderLineDelete.ts b/src/orders/types/OrderLineDelete.ts index adf804d37..1e92b971c 100644 --- a/src/orders/types/OrderLineDelete.ts +++ b/src/orders/types/OrderLineDelete.ts @@ -131,7 +131,7 @@ export interface OrderLineDelete_draftOrderLineDelete_order_fulfillments_lines_o productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderLineDelete_draftOrderLineDelete_order_fulfillments_lines_orderLine_unitPrice | null; + unitPrice: OrderLineDelete_draftOrderLineDelete_order_fulfillments_lines_orderLine_unitPrice; thumbnail: OrderLineDelete_draftOrderLineDelete_order_fulfillments_lines_orderLine_thumbnail | null; } @@ -196,7 +196,7 @@ export interface OrderLineDelete_draftOrderLineDelete_order_lines { productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderLineDelete_draftOrderLineDelete_order_lines_unitPrice | null; + unitPrice: OrderLineDelete_draftOrderLineDelete_order_lines_unitPrice; thumbnail: OrderLineDelete_draftOrderLineDelete_order_lines_thumbnail | null; } @@ -334,24 +334,24 @@ export interface OrderLineDelete_draftOrderLineDelete_order { fulfillments: (OrderLineDelete_draftOrderLineDelete_order_fulfillments | null)[]; lines: (OrderLineDelete_draftOrderLineDelete_order_lines | null)[]; number: string | null; - paymentStatus: PaymentChargeStatusEnum | null; + paymentStatus: PaymentChargeStatusEnum; shippingAddress: OrderLineDelete_draftOrderLineDelete_order_shippingAddress | null; shippingMethod: OrderLineDelete_draftOrderLineDelete_order_shippingMethod | null; shippingMethodName: string | null; - shippingPrice: OrderLineDelete_draftOrderLineDelete_order_shippingPrice | null; + shippingPrice: OrderLineDelete_draftOrderLineDelete_order_shippingPrice; status: OrderStatus; - subtotal: OrderLineDelete_draftOrderLineDelete_order_subtotal | null; - total: OrderLineDelete_draftOrderLineDelete_order_total | null; + subtotal: OrderLineDelete_draftOrderLineDelete_order_subtotal; + total: OrderLineDelete_draftOrderLineDelete_order_total; actions: (OrderAction | null)[]; - totalAuthorized: OrderLineDelete_draftOrderLineDelete_order_totalAuthorized | null; - totalCaptured: OrderLineDelete_draftOrderLineDelete_order_totalCaptured | null; + totalAuthorized: OrderLineDelete_draftOrderLineDelete_order_totalAuthorized; + totalCaptured: OrderLineDelete_draftOrderLineDelete_order_totalCaptured; user: OrderLineDelete_draftOrderLineDelete_order_user | null; userEmail: string | null; availableShippingMethods: (OrderLineDelete_draftOrderLineDelete_order_availableShippingMethods | null)[] | null; discount: OrderLineDelete_draftOrderLineDelete_order_discount | null; invoices: (OrderLineDelete_draftOrderLineDelete_order_invoices | null)[] | null; channel: OrderLineDelete_draftOrderLineDelete_order_channel; - isPaid: boolean | null; + isPaid: boolean; } export interface OrderLineDelete_draftOrderLineDelete { diff --git a/src/orders/types/OrderLineUpdate.ts b/src/orders/types/OrderLineUpdate.ts index 4f373e6ea..53e7fae4e 100644 --- a/src/orders/types/OrderLineUpdate.ts +++ b/src/orders/types/OrderLineUpdate.ts @@ -131,7 +131,7 @@ export interface OrderLineUpdate_draftOrderLineUpdate_order_fulfillments_lines_o productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderLineUpdate_draftOrderLineUpdate_order_fulfillments_lines_orderLine_unitPrice | null; + unitPrice: OrderLineUpdate_draftOrderLineUpdate_order_fulfillments_lines_orderLine_unitPrice; thumbnail: OrderLineUpdate_draftOrderLineUpdate_order_fulfillments_lines_orderLine_thumbnail | null; } @@ -196,7 +196,7 @@ export interface OrderLineUpdate_draftOrderLineUpdate_order_lines { productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderLineUpdate_draftOrderLineUpdate_order_lines_unitPrice | null; + unitPrice: OrderLineUpdate_draftOrderLineUpdate_order_lines_unitPrice; thumbnail: OrderLineUpdate_draftOrderLineUpdate_order_lines_thumbnail | null; } @@ -334,24 +334,24 @@ export interface OrderLineUpdate_draftOrderLineUpdate_order { fulfillments: (OrderLineUpdate_draftOrderLineUpdate_order_fulfillments | null)[]; lines: (OrderLineUpdate_draftOrderLineUpdate_order_lines | null)[]; number: string | null; - paymentStatus: PaymentChargeStatusEnum | null; + paymentStatus: PaymentChargeStatusEnum; shippingAddress: OrderLineUpdate_draftOrderLineUpdate_order_shippingAddress | null; shippingMethod: OrderLineUpdate_draftOrderLineUpdate_order_shippingMethod | null; shippingMethodName: string | null; - shippingPrice: OrderLineUpdate_draftOrderLineUpdate_order_shippingPrice | null; + shippingPrice: OrderLineUpdate_draftOrderLineUpdate_order_shippingPrice; status: OrderStatus; - subtotal: OrderLineUpdate_draftOrderLineUpdate_order_subtotal | null; - total: OrderLineUpdate_draftOrderLineUpdate_order_total | null; + subtotal: OrderLineUpdate_draftOrderLineUpdate_order_subtotal; + total: OrderLineUpdate_draftOrderLineUpdate_order_total; actions: (OrderAction | null)[]; - totalAuthorized: OrderLineUpdate_draftOrderLineUpdate_order_totalAuthorized | null; - totalCaptured: OrderLineUpdate_draftOrderLineUpdate_order_totalCaptured | null; + totalAuthorized: OrderLineUpdate_draftOrderLineUpdate_order_totalAuthorized; + totalCaptured: OrderLineUpdate_draftOrderLineUpdate_order_totalCaptured; user: OrderLineUpdate_draftOrderLineUpdate_order_user | null; userEmail: string | null; availableShippingMethods: (OrderLineUpdate_draftOrderLineUpdate_order_availableShippingMethods | null)[] | null; discount: OrderLineUpdate_draftOrderLineUpdate_order_discount | null; invoices: (OrderLineUpdate_draftOrderLineUpdate_order_invoices | null)[] | null; channel: OrderLineUpdate_draftOrderLineUpdate_order_channel; - isPaid: boolean | null; + isPaid: boolean; } export interface OrderLineUpdate_draftOrderLineUpdate { diff --git a/src/orders/types/OrderLinesAdd.ts b/src/orders/types/OrderLinesAdd.ts index 4d2390378..7bc72649e 100644 --- a/src/orders/types/OrderLinesAdd.ts +++ b/src/orders/types/OrderLinesAdd.ts @@ -131,7 +131,7 @@ export interface OrderLinesAdd_draftOrderLinesCreate_order_fulfillments_lines_or productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderLinesAdd_draftOrderLinesCreate_order_fulfillments_lines_orderLine_unitPrice | null; + unitPrice: OrderLinesAdd_draftOrderLinesCreate_order_fulfillments_lines_orderLine_unitPrice; thumbnail: OrderLinesAdd_draftOrderLinesCreate_order_fulfillments_lines_orderLine_thumbnail | null; } @@ -196,7 +196,7 @@ export interface OrderLinesAdd_draftOrderLinesCreate_order_lines { productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderLinesAdd_draftOrderLinesCreate_order_lines_unitPrice | null; + unitPrice: OrderLinesAdd_draftOrderLinesCreate_order_lines_unitPrice; thumbnail: OrderLinesAdd_draftOrderLinesCreate_order_lines_thumbnail | null; } @@ -334,24 +334,24 @@ export interface OrderLinesAdd_draftOrderLinesCreate_order { fulfillments: (OrderLinesAdd_draftOrderLinesCreate_order_fulfillments | null)[]; lines: (OrderLinesAdd_draftOrderLinesCreate_order_lines | null)[]; number: string | null; - paymentStatus: PaymentChargeStatusEnum | null; + paymentStatus: PaymentChargeStatusEnum; shippingAddress: OrderLinesAdd_draftOrderLinesCreate_order_shippingAddress | null; shippingMethod: OrderLinesAdd_draftOrderLinesCreate_order_shippingMethod | null; shippingMethodName: string | null; - shippingPrice: OrderLinesAdd_draftOrderLinesCreate_order_shippingPrice | null; + shippingPrice: OrderLinesAdd_draftOrderLinesCreate_order_shippingPrice; status: OrderStatus; - subtotal: OrderLinesAdd_draftOrderLinesCreate_order_subtotal | null; - total: OrderLinesAdd_draftOrderLinesCreate_order_total | null; + subtotal: OrderLinesAdd_draftOrderLinesCreate_order_subtotal; + total: OrderLinesAdd_draftOrderLinesCreate_order_total; actions: (OrderAction | null)[]; - totalAuthorized: OrderLinesAdd_draftOrderLinesCreate_order_totalAuthorized | null; - totalCaptured: OrderLinesAdd_draftOrderLinesCreate_order_totalCaptured | null; + totalAuthorized: OrderLinesAdd_draftOrderLinesCreate_order_totalAuthorized; + totalCaptured: OrderLinesAdd_draftOrderLinesCreate_order_totalCaptured; user: OrderLinesAdd_draftOrderLinesCreate_order_user | null; userEmail: string | null; availableShippingMethods: (OrderLinesAdd_draftOrderLinesCreate_order_availableShippingMethods | null)[] | null; discount: OrderLinesAdd_draftOrderLinesCreate_order_discount | null; invoices: (OrderLinesAdd_draftOrderLinesCreate_order_invoices | null)[] | null; channel: OrderLinesAdd_draftOrderLinesCreate_order_channel; - isPaid: boolean | null; + isPaid: boolean; } export interface OrderLinesAdd_draftOrderLinesCreate { diff --git a/src/orders/types/OrderList.ts b/src/orders/types/OrderList.ts index e1fcdb607..71a1bb178 100644 --- a/src/orders/types/OrderList.ts +++ b/src/orders/types/OrderList.ts @@ -47,9 +47,9 @@ export interface OrderList_orders_edges_node { created: any; id: string; number: string | null; - paymentStatus: PaymentChargeStatusEnum | null; + paymentStatus: PaymentChargeStatusEnum; status: OrderStatus; - total: OrderList_orders_edges_node_total | null; + total: OrderList_orders_edges_node_total; userEmail: string | null; } diff --git a/src/orders/types/OrderMarkAsPaid.ts b/src/orders/types/OrderMarkAsPaid.ts index 4ac19d2b8..0b664b6a9 100644 --- a/src/orders/types/OrderMarkAsPaid.ts +++ b/src/orders/types/OrderMarkAsPaid.ts @@ -131,7 +131,7 @@ export interface OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments_lines_orderL productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments_lines_orderLine_unitPrice | null; + unitPrice: OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments_lines_orderLine_unitPrice; thumbnail: OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments_lines_orderLine_thumbnail | null; } @@ -196,7 +196,7 @@ export interface OrderMarkAsPaid_orderMarkAsPaid_order_lines { productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderMarkAsPaid_orderMarkAsPaid_order_lines_unitPrice | null; + unitPrice: OrderMarkAsPaid_orderMarkAsPaid_order_lines_unitPrice; thumbnail: OrderMarkAsPaid_orderMarkAsPaid_order_lines_thumbnail | null; } @@ -334,24 +334,24 @@ export interface OrderMarkAsPaid_orderMarkAsPaid_order { fulfillments: (OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments | null)[]; lines: (OrderMarkAsPaid_orderMarkAsPaid_order_lines | null)[]; number: string | null; - paymentStatus: PaymentChargeStatusEnum | null; + paymentStatus: PaymentChargeStatusEnum; shippingAddress: OrderMarkAsPaid_orderMarkAsPaid_order_shippingAddress | null; shippingMethod: OrderMarkAsPaid_orderMarkAsPaid_order_shippingMethod | null; shippingMethodName: string | null; - shippingPrice: OrderMarkAsPaid_orderMarkAsPaid_order_shippingPrice | null; + shippingPrice: OrderMarkAsPaid_orderMarkAsPaid_order_shippingPrice; status: OrderStatus; - subtotal: OrderMarkAsPaid_orderMarkAsPaid_order_subtotal | null; - total: OrderMarkAsPaid_orderMarkAsPaid_order_total | null; + subtotal: OrderMarkAsPaid_orderMarkAsPaid_order_subtotal; + total: OrderMarkAsPaid_orderMarkAsPaid_order_total; actions: (OrderAction | null)[]; - totalAuthorized: OrderMarkAsPaid_orderMarkAsPaid_order_totalAuthorized | null; - totalCaptured: OrderMarkAsPaid_orderMarkAsPaid_order_totalCaptured | null; + totalAuthorized: OrderMarkAsPaid_orderMarkAsPaid_order_totalAuthorized; + totalCaptured: OrderMarkAsPaid_orderMarkAsPaid_order_totalCaptured; user: OrderMarkAsPaid_orderMarkAsPaid_order_user | null; userEmail: string | null; availableShippingMethods: (OrderMarkAsPaid_orderMarkAsPaid_order_availableShippingMethods | null)[] | null; discount: OrderMarkAsPaid_orderMarkAsPaid_order_discount | null; invoices: (OrderMarkAsPaid_orderMarkAsPaid_order_invoices | null)[] | null; channel: OrderMarkAsPaid_orderMarkAsPaid_order_channel; - isPaid: boolean | null; + isPaid: boolean; } export interface OrderMarkAsPaid_orderMarkAsPaid { diff --git a/src/orders/types/OrderRefund.ts b/src/orders/types/OrderRefund.ts index 55c3b8887..7919326e4 100644 --- a/src/orders/types/OrderRefund.ts +++ b/src/orders/types/OrderRefund.ts @@ -131,7 +131,7 @@ export interface OrderRefund_orderRefund_order_fulfillments_lines_orderLine { productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderRefund_orderRefund_order_fulfillments_lines_orderLine_unitPrice | null; + unitPrice: OrderRefund_orderRefund_order_fulfillments_lines_orderLine_unitPrice; thumbnail: OrderRefund_orderRefund_order_fulfillments_lines_orderLine_thumbnail | null; } @@ -196,7 +196,7 @@ export interface OrderRefund_orderRefund_order_lines { productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderRefund_orderRefund_order_lines_unitPrice | null; + unitPrice: OrderRefund_orderRefund_order_lines_unitPrice; thumbnail: OrderRefund_orderRefund_order_lines_thumbnail | null; } @@ -334,24 +334,24 @@ export interface OrderRefund_orderRefund_order { fulfillments: (OrderRefund_orderRefund_order_fulfillments | null)[]; lines: (OrderRefund_orderRefund_order_lines | null)[]; number: string | null; - paymentStatus: PaymentChargeStatusEnum | null; + paymentStatus: PaymentChargeStatusEnum; shippingAddress: OrderRefund_orderRefund_order_shippingAddress | null; shippingMethod: OrderRefund_orderRefund_order_shippingMethod | null; shippingMethodName: string | null; - shippingPrice: OrderRefund_orderRefund_order_shippingPrice | null; + shippingPrice: OrderRefund_orderRefund_order_shippingPrice; status: OrderStatus; - subtotal: OrderRefund_orderRefund_order_subtotal | null; - total: OrderRefund_orderRefund_order_total | null; + subtotal: OrderRefund_orderRefund_order_subtotal; + total: OrderRefund_orderRefund_order_total; actions: (OrderAction | null)[]; - totalAuthorized: OrderRefund_orderRefund_order_totalAuthorized | null; - totalCaptured: OrderRefund_orderRefund_order_totalCaptured | null; + totalAuthorized: OrderRefund_orderRefund_order_totalAuthorized; + totalCaptured: OrderRefund_orderRefund_order_totalCaptured; user: OrderRefund_orderRefund_order_user | null; userEmail: string | null; availableShippingMethods: (OrderRefund_orderRefund_order_availableShippingMethods | null)[] | null; discount: OrderRefund_orderRefund_order_discount | null; invoices: (OrderRefund_orderRefund_order_invoices | null)[] | null; channel: OrderRefund_orderRefund_order_channel; - isPaid: boolean | null; + isPaid: boolean; } export interface OrderRefund_orderRefund { diff --git a/src/orders/types/OrderRefundData.ts b/src/orders/types/OrderRefundData.ts index 31c134628..7eb447030 100644 --- a/src/orders/types/OrderRefundData.ts +++ b/src/orders/types/OrderRefundData.ts @@ -57,7 +57,7 @@ export interface OrderRefundData_order_lines { id: string; productName: string; quantity: number; - unitPrice: OrderRefundData_order_lines_unitPrice | null; + unitPrice: OrderRefundData_order_lines_unitPrice; thumbnail: OrderRefundData_order_lines_thumbnail | null; quantityFulfilled: number; } @@ -83,7 +83,7 @@ export interface OrderRefundData_order_fulfillments_lines_orderLine { id: string; productName: string; quantity: number; - unitPrice: OrderRefundData_order_fulfillments_lines_orderLine_unitPrice | null; + unitPrice: OrderRefundData_order_fulfillments_lines_orderLine_unitPrice; thumbnail: OrderRefundData_order_fulfillments_lines_orderLine_thumbnail | null; } @@ -106,9 +106,9 @@ export interface OrderRefundData_order { __typename: "Order"; id: string; number: string | null; - total: OrderRefundData_order_total | null; - totalCaptured: OrderRefundData_order_totalCaptured | null; - shippingPrice: OrderRefundData_order_shippingPrice | null; + total: OrderRefundData_order_total; + totalCaptured: OrderRefundData_order_totalCaptured; + shippingPrice: OrderRefundData_order_shippingPrice; lines: (OrderRefundData_order_lines | null)[]; fulfillments: (OrderRefundData_order_fulfillments | null)[]; } diff --git a/src/orders/types/OrderShippingMethodUpdate.ts b/src/orders/types/OrderShippingMethodUpdate.ts index 7a91fdbf9..7d6687ab9 100644 --- a/src/orders/types/OrderShippingMethodUpdate.ts +++ b/src/orders/types/OrderShippingMethodUpdate.ts @@ -50,7 +50,7 @@ export interface OrderShippingMethodUpdate_orderUpdateShipping_order { id: string; shippingMethod: OrderShippingMethodUpdate_orderUpdateShipping_order_shippingMethod | null; shippingMethodName: string | null; - shippingPrice: OrderShippingMethodUpdate_orderUpdateShipping_order_shippingPrice | null; + shippingPrice: OrderShippingMethodUpdate_orderUpdateShipping_order_shippingPrice; } export interface OrderShippingMethodUpdate_orderUpdateShipping { diff --git a/src/orders/types/OrderUpdate.ts b/src/orders/types/OrderUpdate.ts index 3e496557c..06cf8928d 100644 --- a/src/orders/types/OrderUpdate.ts +++ b/src/orders/types/OrderUpdate.ts @@ -131,7 +131,7 @@ export interface OrderUpdate_orderUpdate_order_fulfillments_lines_orderLine { productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderUpdate_orderUpdate_order_fulfillments_lines_orderLine_unitPrice | null; + unitPrice: OrderUpdate_orderUpdate_order_fulfillments_lines_orderLine_unitPrice; thumbnail: OrderUpdate_orderUpdate_order_fulfillments_lines_orderLine_thumbnail | null; } @@ -196,7 +196,7 @@ export interface OrderUpdate_orderUpdate_order_lines { productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderUpdate_orderUpdate_order_lines_unitPrice | null; + unitPrice: OrderUpdate_orderUpdate_order_lines_unitPrice; thumbnail: OrderUpdate_orderUpdate_order_lines_thumbnail | null; } @@ -334,24 +334,24 @@ export interface OrderUpdate_orderUpdate_order { fulfillments: (OrderUpdate_orderUpdate_order_fulfillments | null)[]; lines: (OrderUpdate_orderUpdate_order_lines | null)[]; number: string | null; - paymentStatus: PaymentChargeStatusEnum | null; + paymentStatus: PaymentChargeStatusEnum; shippingAddress: OrderUpdate_orderUpdate_order_shippingAddress | null; shippingMethod: OrderUpdate_orderUpdate_order_shippingMethod | null; shippingMethodName: string | null; - shippingPrice: OrderUpdate_orderUpdate_order_shippingPrice | null; + shippingPrice: OrderUpdate_orderUpdate_order_shippingPrice; status: OrderStatus; - subtotal: OrderUpdate_orderUpdate_order_subtotal | null; - total: OrderUpdate_orderUpdate_order_total | null; + subtotal: OrderUpdate_orderUpdate_order_subtotal; + total: OrderUpdate_orderUpdate_order_total; actions: (OrderAction | null)[]; - totalAuthorized: OrderUpdate_orderUpdate_order_totalAuthorized | null; - totalCaptured: OrderUpdate_orderUpdate_order_totalCaptured | null; + totalAuthorized: OrderUpdate_orderUpdate_order_totalAuthorized; + totalCaptured: OrderUpdate_orderUpdate_order_totalCaptured; user: OrderUpdate_orderUpdate_order_user | null; userEmail: string | null; availableShippingMethods: (OrderUpdate_orderUpdate_order_availableShippingMethods | null)[] | null; discount: OrderUpdate_orderUpdate_order_discount | null; invoices: (OrderUpdate_orderUpdate_order_invoices | null)[] | null; channel: OrderUpdate_orderUpdate_order_channel; - isPaid: boolean | null; + isPaid: boolean; } export interface OrderUpdate_orderUpdate { diff --git a/src/orders/types/OrderVoid.ts b/src/orders/types/OrderVoid.ts index a772e25e1..2f6f3ed8f 100644 --- a/src/orders/types/OrderVoid.ts +++ b/src/orders/types/OrderVoid.ts @@ -131,7 +131,7 @@ export interface OrderVoid_orderVoid_order_fulfillments_lines_orderLine { productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderVoid_orderVoid_order_fulfillments_lines_orderLine_unitPrice | null; + unitPrice: OrderVoid_orderVoid_order_fulfillments_lines_orderLine_unitPrice; thumbnail: OrderVoid_orderVoid_order_fulfillments_lines_orderLine_thumbnail | null; } @@ -196,7 +196,7 @@ export interface OrderVoid_orderVoid_order_lines { productSku: string; quantity: number; quantityFulfilled: number; - unitPrice: OrderVoid_orderVoid_order_lines_unitPrice | null; + unitPrice: OrderVoid_orderVoid_order_lines_unitPrice; thumbnail: OrderVoid_orderVoid_order_lines_thumbnail | null; } @@ -334,24 +334,24 @@ export interface OrderVoid_orderVoid_order { fulfillments: (OrderVoid_orderVoid_order_fulfillments | null)[]; lines: (OrderVoid_orderVoid_order_lines | null)[]; number: string | null; - paymentStatus: PaymentChargeStatusEnum | null; + paymentStatus: PaymentChargeStatusEnum; shippingAddress: OrderVoid_orderVoid_order_shippingAddress | null; shippingMethod: OrderVoid_orderVoid_order_shippingMethod | null; shippingMethodName: string | null; - shippingPrice: OrderVoid_orderVoid_order_shippingPrice | null; + shippingPrice: OrderVoid_orderVoid_order_shippingPrice; status: OrderStatus; - subtotal: OrderVoid_orderVoid_order_subtotal | null; - total: OrderVoid_orderVoid_order_total | null; + subtotal: OrderVoid_orderVoid_order_subtotal; + total: OrderVoid_orderVoid_order_total; actions: (OrderAction | null)[]; - totalAuthorized: OrderVoid_orderVoid_order_totalAuthorized | null; - totalCaptured: OrderVoid_orderVoid_order_totalCaptured | null; + totalAuthorized: OrderVoid_orderVoid_order_totalAuthorized; + totalCaptured: OrderVoid_orderVoid_order_totalCaptured; user: OrderVoid_orderVoid_order_user | null; userEmail: string | null; availableShippingMethods: (OrderVoid_orderVoid_order_availableShippingMethods | null)[] | null; discount: OrderVoid_orderVoid_order_discount | null; invoices: (OrderVoid_orderVoid_order_invoices | null)[] | null; channel: OrderVoid_orderVoid_order_channel; - isPaid: boolean | null; + isPaid: boolean; } export interface OrderVoid_orderVoid { diff --git a/src/products/components/ProductExportDialog/ProductExportDialog.tsx b/src/products/components/ProductExportDialog/ProductExportDialog.tsx index d5403ea02..16d608598 100644 --- a/src/products/components/ProductExportDialog/ProductExportDialog.tsx +++ b/src/products/components/ProductExportDialog/ProductExportDialog.tsx @@ -4,12 +4,12 @@ import DialogActions from "@material-ui/core/DialogActions"; import DialogContent from "@material-ui/core/DialogContent"; import DialogTitle from "@material-ui/core/DialogTitle"; import Typography from "@material-ui/core/Typography"; -import { Channels_channels } from "@saleor/channels/types/Channels"; import ConfirmButton, { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import makeCreatorSteps, { Step } from "@saleor/components/CreatorSteps"; import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField"; +import { ChannelFragment } from "@saleor/fragments/types/ChannelFragment"; import { ExportErrorFragment } from "@saleor/fragments/types/ExportErrorFragment"; import useForm, { FormChange } from "@saleor/hooks/useForm"; import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors"; @@ -79,7 +79,7 @@ const ProductExportSteps = makeCreatorSteps<ProductExportStep>(); export interface ProductExportDialogProps extends DialogProps, FetchMoreProps { attributes: SearchAttributes_search_edges_node[]; - channels: Channels_channels[]; + channels: ChannelFragment[]; confirmButtonState: ConfirmButtonTransitionState; errors: ExportErrorFragment[]; productQuantity: ProductQuantity; @@ -146,7 +146,7 @@ const ProductExportDialog: React.FC<ProductExportDialogProps> = ({ ); }; - const handleChannelSelect = (option: Channels_channels) => { + const handleChannelSelect = (option: ChannelFragment) => { change({ target: { name: "exportInfo", @@ -168,7 +168,7 @@ const ProductExportDialog: React.FC<ProductExportDialogProps> = ({ }; const handleToggleAllChannels = ( - items: Channels_channels[], + items: ChannelFragment[], selected: number ) => { setSelectedChannels(selected === items.length ? [] : channels); diff --git a/src/products/components/ProductExportDialog/ProductExportDialogInfo.tsx b/src/products/components/ProductExportDialog/ProductExportDialogInfo.tsx index e317938bf..849f67d20 100644 --- a/src/products/components/ProductExportDialog/ProductExportDialogInfo.tsx +++ b/src/products/components/ProductExportDialog/ProductExportDialogInfo.tsx @@ -4,13 +4,13 @@ import FormControlLabel from "@material-ui/core/FormControlLabel"; import makeStyles from "@material-ui/core/styles/makeStyles"; import TextField from "@material-ui/core/TextField"; import Typography from "@material-ui/core/Typography"; -import { Channels_channels } from "@saleor/channels/types/Channels"; import Accordion, { AccordionProps } from "@saleor/components/Accordion"; import ChannelsAvailabilityContent from "@saleor/components/ChannelsAvailabilityContent"; import Checkbox from "@saleor/components/Checkbox"; import Chip from "@saleor/components/Chip"; import Hr from "@saleor/components/Hr"; import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField"; +import { ChannelFragment } from "@saleor/fragments/types/ChannelFragment"; import { ChangeEvent, FormChange } from "@saleor/hooks/useForm"; import useSearchQuery from "@saleor/hooks/useSearchQuery"; import { sectionNames } from "@saleor/intl"; @@ -207,8 +207,8 @@ const FieldAccordion: React.FC<AccordionProps & { export interface ProductExportDialogInfoProps extends FetchMoreProps { attributes: MultiAutocompleteChoiceType[]; - channels: Channels_channels[]; - selectedChannels: Channels_channels[]; + channels: ChannelFragment[]; + selectedChannels: ChannelFragment[]; warehouses: MultiAutocompleteChoiceType[]; data: ExportProductsInput; selectedAttributes: MultiAutocompleteChoiceType[]; @@ -217,8 +217,8 @@ export interface ProductExportDialogInfoProps extends FetchMoreProps { onChange: FormChange; onFetch: (query: string) => void; onSelectAllWarehouses: FormChange; - onSelectAllChannels: (items: Channels_channels[], selected: number) => void; - onChannelSelect: (option: Channels_channels) => void; + onSelectAllChannels: (items: ChannelFragment[], selected: number) => void; + onChannelSelect: (option: ChannelFragment) => void; } const ProductExportDialogInfo: React.FC<ProductExportDialogInfoProps> = ({ diff --git a/src/products/views/ProductList/ProductList.tsx b/src/products/views/ProductList/ProductList.tsx index 98cbce0d4..dac6be8da 100644 --- a/src/products/views/ProductList/ProductList.tsx +++ b/src/products/views/ProductList/ProductList.tsx @@ -224,8 +224,9 @@ export const ProductList: React.FC<ProductListProps> = ({ params }) => { ); const paginationState = createPaginationState(settings.rowNumber, params); - const filter = !noChannel ? getFilterVariables(params, channel.slug) : null; - const sort = !noChannel ? getSortQueryVariables(params, channel.slug) : null; + const channelSlug = noChannel ? null : channel.slug; + const filter = getFilterVariables(params, channelSlug); + const sort = getSortQueryVariables(params, channelSlug); const queryVariables = React.useMemo<ProductListVariables>( () => ({ ...paginationState, diff --git a/src/types/globalTypes.ts b/src/types/globalTypes.ts index a971e3497..e4db5bd07 100644 --- a/src/types/globalTypes.ts +++ b/src/types/globalTypes.ts @@ -1453,8 +1453,7 @@ export interface PageTranslationInput { seoTitle?: string | null; seoDescription?: string | null; title?: string | null; - content?: string | null; - contentJson?: any | null; + content?: any | null; } export interface PageTypeCreateInput { diff --git a/src/utils/metadata/types/UpdateMetadata.ts b/src/utils/metadata/types/UpdateMetadata.ts index 40628865d..b716158a1 100644 --- a/src/utils/metadata/types/UpdateMetadata.ts +++ b/src/utils/metadata/types/UpdateMetadata.ts @@ -38,7 +38,7 @@ export interface UpdateMetadata_deleteMetadata_item_privateMetadata { } export interface UpdateMetadata_deleteMetadata_item { - __typename: "App" | "ShippingZone" | "ShippingMethod" | "Product" | "ProductType" | "Attribute" | "Category" | "ProductVariant" | "DigitalContent" | "Collection" | "Page" | "PageType" | "MenuItem" | "Menu" | "User" | "Checkout" | "Order" | "Fulfillment" | "Invoice"; + __typename: "App" | "Warehouse" | "ShippingZone" | "ShippingMethod" | "Product" | "ProductType" | "Attribute" | "Category" | "ProductVariant" | "DigitalContent" | "Collection" | "Page" | "PageType" | "MenuItem" | "Menu" | "User" | "Checkout" | "Order" | "Fulfillment" | "Invoice"; metadata: (UpdateMetadata_deleteMetadata_item_metadata | null)[]; privateMetadata: (UpdateMetadata_deleteMetadata_item_privateMetadata | null)[]; id: string; diff --git a/src/utils/metadata/types/UpdatePrivateMetadata.ts b/src/utils/metadata/types/UpdatePrivateMetadata.ts index 173568570..1b1ebc89d 100644 --- a/src/utils/metadata/types/UpdatePrivateMetadata.ts +++ b/src/utils/metadata/types/UpdatePrivateMetadata.ts @@ -38,7 +38,7 @@ export interface UpdatePrivateMetadata_deletePrivateMetadata_item_privateMetadat } export interface UpdatePrivateMetadata_deletePrivateMetadata_item { - __typename: "App" | "ShippingZone" | "ShippingMethod" | "Product" | "ProductType" | "Attribute" | "Category" | "ProductVariant" | "DigitalContent" | "Collection" | "Page" | "PageType" | "MenuItem" | "Menu" | "User" | "Checkout" | "Order" | "Fulfillment" | "Invoice"; + __typename: "App" | "Warehouse" | "ShippingZone" | "ShippingMethod" | "Product" | "ProductType" | "Attribute" | "Category" | "ProductVariant" | "DigitalContent" | "Collection" | "Page" | "PageType" | "MenuItem" | "Menu" | "User" | "Checkout" | "Order" | "Fulfillment" | "Invoice"; metadata: (UpdatePrivateMetadata_deletePrivateMetadata_item_metadata | null)[]; privateMetadata: (UpdatePrivateMetadata_deletePrivateMetadata_item_privateMetadata | null)[]; id: string; From db4fc1d8c594eb76d338bc93d4e367d9c8eec9d4 Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk <tarasiukdawid@gmail.com> Date: Wed, 3 Feb 2021 09:22:04 +0100 Subject: [PATCH 34/35] Fix failing login in Firefox browser (#970) The bug was introduced in OAuth2 implementation (1645e2fd), where the tokenAuthLoading flag from AuthProvider was not used and it did not blocked UI in loading state. --- src/auth/views/Login.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auth/views/Login.tsx b/src/auth/views/Login.tsx index 2f7b9e294..59f7b1878 100644 --- a/src/auth/views/Login.tsx +++ b/src/auth/views/Login.tsx @@ -86,7 +86,7 @@ const LoginView: React.FC<LoginViewProps> = ({ params }) => { externalAuthentications={ externalAuthentications?.shop?.availableExternalAuthentications } - loading={externalAuthenticationsLoading} + loading={externalAuthenticationsLoading || tokenAuthLoading} onExternalAuthentication={handleRequestExternalAuthentication} onPasswordRecovery={() => navigate(passwordResetUrl)} onSubmit={handleSubmit} From b36160edb1c7f9e8ce87cbf121d096c8a2c08124 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20G=C4=99bala?= <5421321+maarcingebala@users.noreply.github.com> Date: Wed, 10 Feb 2021 09:50:12 +0100 Subject: [PATCH 35/35] Update PR template (#975) --- .github/PULL_REQUEST_TEMPLATE.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 7bb5cbabf..56f936bee 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -21,6 +21,8 @@ greatly reduce the amount of work needed to review your work. --> 1. [ ] Data-test are added for new elements. 1. [ ] Type definitions are up to date. 1. [ ] Changes are mentioned in the changelog. +1. [ ] The changes are tested in different browsers (Chrome, Firefox, Safari). +1. [ ] The changes are tested in light and dark mode. ### Test environment config