From 9363ec985a1ef213ac7f26c50b721d8668cb523b Mon Sep 17 00:00:00 2001 From: edyespinal Date: Sat, 6 Aug 2022 15:32:47 +0200 Subject: [PATCH 1/3] wip: easypost integration --- graphql/schema.graphql | 1 + manifest.json | 3 +- messages/context.json | 6 +- messages/en.json | 6 +- node/clients/mail.ts | 5 +- node/resolvers/index.ts | 2 + node/resolvers/sendReturnLabel.ts | 16 ++ node/services/createReturnRequestService.ts | 10 +- node/services/sendReturnLabelService.ts | 89 +++++++++ node/services/updateRequestStatusService.ts | 8 +- node/typings/mailClient.d.ts | 85 +++++---- node/utils/constants.ts | 19 -- node/utils/emailTemplates.ts | 21 +++ node/utils/templates/index.ts | 53 +++++- node/utils/templates/templateMessages.ts | 29 +++ .../ReturnDetails/ReturnDetailsContainer.tsx | 2 + .../components/ReturnLabel/ReturnLabel.tsx | 174 ++++++++++++++++++ .../ReturnLabel}/graphql/createLabel.gql | 0 .../ReturnLabel/graphql/getEasyPostConfig.gql | 13 ++ .../ReturnLabel/graphql/getInstalledApp.gql | 5 + .../ReturnLabel/graphql/sendLabel.gql | 3 + 21 files changed, 473 insertions(+), 77 deletions(-) create mode 100644 node/resolvers/sendReturnLabel.ts create mode 100644 node/services/sendReturnLabelService.ts create mode 100644 node/utils/emailTemplates.ts create mode 100644 react/admin/ReturnDetails/components/ReturnLabel/ReturnLabel.tsx rename react/{ => admin/ReturnDetails/components/ReturnLabel}/graphql/createLabel.gql (100%) create mode 100644 react/admin/ReturnDetails/components/ReturnLabel/graphql/getEasyPostConfig.gql create mode 100644 react/admin/ReturnDetails/components/ReturnLabel/graphql/getInstalledApp.gql create mode 100644 react/admin/ReturnDetails/components/ReturnLabel/graphql/sendLabel.gql diff --git a/graphql/schema.graphql b/graphql/schema.graphql index 1a5180efd..4d9906825 100644 --- a/graphql/schema.graphql +++ b/graphql/schema.graphql @@ -41,4 +41,5 @@ type Mutation { comment: ReturnRequestCommentInput refundData: RefundDataInput ): ReturnRequestResponse @withUserProfile + sendReturnLabel(requestId: String!, labelUrl: String!): Boolean } diff --git a/manifest.json b/manifest.json index 62f10c661..1a7a27242 100644 --- a/manifest.json +++ b/manifest.json @@ -14,7 +14,8 @@ "vtex.css-handles": "0.x", "vtex.easypost": "0.x", "vtex.tenant-graphql": "0.x", - "vtex.catalog-graphql": "1.x" + "vtex.catalog-graphql": "1.x", + "vtex.apps-graphql": "2.x" }, "builders": { "admin": "0.x", diff --git a/messages/context.json b/messages/context.json index 05011c775..ce7315c6a 100644 --- a/messages/context.json +++ b/messages/context.json @@ -311,5 +311,9 @@ "store/return-app.return-order-details.pickup-address.drop-off-points.tooltip": "You can return your items in one of our selected drop off points", "store/return-app.return-order-details.pickup-address.drop-off-points.dropdown.placehoder": "Select a drop off point", "store/return-app.return-order-details.pickup-address.drop-off-points.dropdown.error": "There was an error loading the drop off points", - "admin/return-app.return-request-details.localized-product-name.tooltip": "Tooltip for localized product names" + "admin/return-app.return-request-details.localized-product-name.tooltip": "Tooltip for localized product names", + "admin/return-app.return-request-details.return-label.modal-title": "Are you sure you want to create a return shipping label?", + "admin/return-app.return-request-details.return-label.modal-message": "This action is irreversible. The created label will be sent via email to the customer.", + "admin/return-app.return-request-details.return-label.create-return-label": "Create return label", + "admin/return-app.return-request-details.return-label.see-return-label": "See return label" } diff --git a/messages/en.json b/messages/en.json index 251a8e727..6d79f40b8 100644 --- a/messages/en.json +++ b/messages/en.json @@ -311,5 +311,9 @@ "store/return-app.return-order-details.pickup-address.drop-off-points.tooltip": "You can return your items in one of our selected drop off points", "store/return-app.return-order-details.pickup-address.drop-off-points.dropdown.placehoder": "Select a drop off point", "store/return-app.return-order-details.pickup-address.drop-off-points.dropdown.error": "There was an error loading the drop off points", - "admin/return-app.return-request-details.localized-product-name.tooltip": "Original item name from the order" + "admin/return-app.return-request-details.localized-product-name.tooltip": "Original item name from the order", + "admin/return-app.return-request-details.return-label.modal-title": "Are you sure you want to create a return shipping label?", + "admin/return-app.return-request-details.return-label.modal-message": "This action is irreversible. The created label will be sent via email to the customer.", + "admin/return-app.return-request-details.return-label.create-return-label": "Create return label", + "admin/return-app.return-request-details.return-label.see-return-label": "See return label" } diff --git a/node/clients/mail.ts b/node/clients/mail.ts index 8aee9ad4c..aeaeca055 100644 --- a/node/clients/mail.ts +++ b/node/clients/mail.ts @@ -5,6 +5,7 @@ import type { StatusUpdateMailData, ConfirmationMailData, Template, + ReturnLabelMailData, } from '../typings/mailClient' const MAIL_SERVICE_PATH = '/api/mail-service/pvt/sendmail' @@ -21,7 +22,7 @@ export class MailClient extends JanusClient { } public sendMail( - mailData: StatusUpdateMailData | ConfirmationMailData + mailData: StatusUpdateMailData | ConfirmationMailData | ReturnLabelMailData ): Promise { return this.http.post(MAIL_SERVICE_PATH, mailData, { metric: 'mail-post-send', @@ -34,7 +35,7 @@ export class MailClient extends JanusClient { }) } - public publishTemplate(template: Template): Promise { + public publishTemplate(template: Template): Promise { return this.http.post(TEMPLATE_RENDER_PATH, template, { headers: { ...this.options?.headers, diff --git a/node/resolvers/index.ts b/node/resolvers/index.ts index e462ffcbf..30159af0d 100644 --- a/node/resolvers/index.ts +++ b/node/resolvers/index.ts @@ -11,10 +11,12 @@ import { returnRequestList } from './returnRequestList' import { ReturnRequestResponse } from './ReturnRequestResponse' import { updateReturnRequestStatus } from './updateReturnRequestStatus' import { nearestPickupPoints } from './nearestPickupPoints' +import { sendReturnLabel } from './sendReturnLabel' export const mutations = { createReturnRequest, updateReturnRequestStatus, + sendReturnLabel, ...settingsMutation, } diff --git a/node/resolvers/sendReturnLabel.ts b/node/resolvers/sendReturnLabel.ts new file mode 100644 index 000000000..e9bd88204 --- /dev/null +++ b/node/resolvers/sendReturnLabel.ts @@ -0,0 +1,16 @@ +import { sendReturnLabelService } from '../services/sendReturnLabelService' + +interface QuerySendReturnLabelArgs { + requestId: string + labelUrl: string +} + +export const sendReturnLabel = async ( + _: unknown, + args: QuerySendReturnLabelArgs, + ctx: Context +) => { + const { requestId, labelUrl } = args + + return sendReturnLabelService(ctx, requestId, labelUrl) +} diff --git a/node/services/createReturnRequestService.ts b/node/services/createReturnRequestService.ts index b6ed2ed64..654b715f5 100644 --- a/node/services/createReturnRequestService.ts +++ b/node/services/createReturnRequestService.ts @@ -2,10 +2,7 @@ import type { ReturnRequestCreated, ReturnRequestInput } from 'vtex.return-app' import { UserInputError, ResolverError } from '@vtex/api' import type { DocumentResponse } from '@vtex/clients' -import { - SETTINGS_PATH, - OMS_RETURN_REQUEST_CONFIRMATION, -} from '../utils/constants' +import { SETTINGS_PATH } from '../utils/constants' import { isUserAllowed } from '../utils/isUserAllowed' import { canOrderBeReturned } from '../utils/canOrderBeReturned' import { canReturnAllItems } from '../utils/canReturnAllItems' @@ -17,6 +14,7 @@ import { createRefundableTotals } from '../utils/createRefundableTotals' import { OMS_RETURN_REQUEST_CONFIRMATION_TEMPLATE } from '../utils/templates' import type { ConfirmationMailData } from '../typings/mailClient' import { getCustomerEmail } from '../utils/getCostumerEmail' +import { templateName } from '../utils/emailTemplates' export const createReturnRequestService = async ( ctx: Context, @@ -261,7 +259,7 @@ export const createReturnRequestService = async ( // We add a try/catch here so we avoid sending an error to the browser only if the email fails. try { const templateExists = await mail.getTemplate( - OMS_RETURN_REQUEST_CONFIRMATION(locale) + templateName('confirmation', locale) ) if (!templateExists) { @@ -281,7 +279,7 @@ export const createReturnRequestService = async ( } = shippingData const mailData: ConfirmationMailData = { - templateName: OMS_RETURN_REQUEST_CONFIRMATION(locale), + templateName: templateName('confirmation', locale), jsonData: { data: { status: 'new', diff --git a/node/services/sendReturnLabelService.ts b/node/services/sendReturnLabelService.ts new file mode 100644 index 000000000..39889df7f --- /dev/null +++ b/node/services/sendReturnLabelService.ts @@ -0,0 +1,89 @@ +import { ResolverError } from '@vtex/api' +import type { ReturnRequest } from 'vtex.return-app' + +import type { ReturnLabelMailData } from '../typings/mailClient' +import { templateName } from '../utils/emailTemplates' +import { OMS_RETURN_REQUEST_LABEL_TEMPLATE } from '../utils/templates' +import { formatRequestToPartialUpdate } from './updateRequestStatusService' + +export const sendReturnLabelService = async ( + ctx: Context, + requestId: string, + labelUrl: string +) => { + const { + clients: { mail, returnRequest: returnRequestClient }, + vtex: { logger }, + } = ctx + + const returnRequest = (await returnRequestClient.get(requestId, [ + '_all', + ])) as ReturnRequest + + const { + pickupReturnData, + cultureInfoData: { locale }, + } = returnRequest + + const updatedPickupReturnData = { + ...pickupReturnData, + labelUrl, + } + + const updatedRequest = { + ...formatRequestToPartialUpdate(returnRequest), + pickupReturnData: updatedPickupReturnData, + } + + try { + await returnRequestClient.update(requestId, updatedRequest) + } catch (error) { + const mdValidationErrors = error?.response?.data?.errors[0]?.errors + + const errorMessageString = mdValidationErrors + ? JSON.stringify( + { + message: 'Schema Validation error', + errors: mdValidationErrors, + }, + null, + 2 + ) + : error.message + + throw new ResolverError(errorMessageString, error.response?.status || 500) + } + + try { + const templateExists = await mail.getTemplate(templateName('label', locale)) + + if (!templateExists) { + await mail.publishTemplate(OMS_RETURN_REQUEST_LABEL_TEMPLATE(locale)) + } + + const { customerProfileData } = updatedRequest + + const mailData: ReturnLabelMailData = { + templateName: templateName('label', locale), + jsonData: { + data: { + name: customerProfileData?.name ?? '', + DocumentId: requestId, + email: customerProfileData?.email ?? '', + labelUrl, + }, + }, + } + + await mail.sendMail(mailData) + } catch (error) { + logger.warn({ + message: `Failed to send email for return request ${requestId}`, + error, + }) + + return false + } + + return true +} diff --git a/node/services/updateRequestStatusService.ts b/node/services/updateRequestStatusService.ts index b4a126552..15c18ff5f 100644 --- a/node/services/updateRequestStatusService.ts +++ b/node/services/updateRequestStatusService.ts @@ -15,14 +15,14 @@ import { validateStatusUpdate } from '../utils/validateStatusUpdate' import { createOrUpdateStatusPayload } from '../utils/createOrUpdateStatusPayload' import { createRefundData } from '../utils/createRefundData' import { handleRefund } from '../utils/handleRefund' -import { OMS_RETURN_REQUEST_STATUS_UPDATE } from '../utils/constants' import { OMS_RETURN_REQUEST_STATUS_UPDATE_TEMPLATE } from '../utils/templates' import type { StatusUpdateMailData } from '../typings/mailClient' +import { templateName } from '../utils/emailTemplates' // A partial update on MD requires all required field to be sent. https://vtex.slack.com/archives/C8EE14F1C/p1644422359807929 // And the request to update fails when we pass the auto generated ones. // If any new field is added to the ReturnRequest as required, it has to be added here too. -const formatRequestToPartialUpdate = ( +export const formatRequestToPartialUpdate = ( request: ReturnRequest ): ReturnRequest => { const { @@ -228,7 +228,7 @@ export const updateRequestStatusService = async ( // We add a try/catch here so we avoid sending an error to the browser only if the email fails. try { const templateExists = await mail.getTemplate( - OMS_RETURN_REQUEST_STATUS_UPDATE(cultureInfoData?.locale) + templateName('status-update', cultureInfoData?.locale) ) if (!templateExists) { @@ -247,7 +247,7 @@ export const updateRequestStatusService = async ( } = updatedRequest const mailData: StatusUpdateMailData = { - templateName: OMS_RETURN_REQUEST_STATUS_UPDATE(cultureInfoData?.locale), + templateName: templateName('status-update', cultureInfoData?.locale), jsonData: { data: { status: updatedStatus, diff --git a/node/typings/mailClient.d.ts b/node/typings/mailClient.d.ts index ddf9d8987..610f9873e 100644 --- a/node/typings/mailClient.d.ts +++ b/node/typings/mailClient.d.ts @@ -1,45 +1,64 @@ -import type { ReturnRequestItem, Status } from 'vtex.return-app' +import type { + ReturnRequest, + RefundStatusData, + ReturnRequestItem, + Status, +} from 'vtex.return-app' -export type ReturnRequestConfirmation = string +interface MailData { + templateName: string + jsonData: DataType +} -export type ReturnRequestStatusUpdate = string +interface ConfirmationData { + data: { + status: Status['new'] + name: string + DocumentId: string + email: string + phoneNumber: string + country: string + locality: string + address: string + paymentMethod: string + } + products: ReturnRequestItem[] + refundStatusData: RefundStatusData[] +} -export interface ConfirmationMailData { - templateName: ReturnRequestConfirmation - jsonData: { - data: { - status: Status['new'] - name: string - DocumentId: string - email: string - phoneNumber: string - country: string - locality: string - address: string - paymentMethod: string - } - products: ReturnRequestItem[] - refundStatusData: ReturnRequest['refundStatusData'] +interface StatusUpdateData { + data: { + status: Status + name: string + DocumentId: string + email: string + paymentMethod: string + iban: string + refundedAmount: number } + products: ReturnRequest['items'] + refundStatusData: RefundStatusData[] } -export interface StatusUpdateMailData { - templateName: ReturnRequestStatusUpdate - jsonData: { - data: { - status: Status - name: string - DocumentId: string - email: string - paymentMethod: string - iban: string - refundedAmount: number - } - products: ReturnRequest['items'] - refundStatusData: ReturnRequest['refundStatusData'] +interface ReturnLabelData { + data: { + name: string + DocumentId: string + email: string + labelUrl: string } } +export type TemplateName = 'confirmation' | 'status-update' | 'label' + +export type TemplateFriendlyName = 'Confirmation' | 'Status Update' | 'Label' + +export type ConfirmationMailData = MailData + +export type StatusUpdateMailData = MailData + +export type ReturnLabelMailData = MailData + export interface Template { AccountId: string | null AccountName: string | null diff --git a/node/utils/constants.ts b/node/utils/constants.ts index 6ddfc5ddd..8ac906035 100644 --- a/node/utils/constants.ts +++ b/node/utils/constants.ts @@ -1,10 +1,5 @@ import type { OrderToReturnValidation } from 'vtex.return-app' -import type { - ReturnRequestConfirmation, - ReturnRequestStatusUpdate, -} from '../typings/mailClient' - export const SETTINGS_PATH = 'app-settings' export const ORDER_TO_RETURN_VALIDATON: Record< @@ -14,17 +9,3 @@ export const ORDER_TO_RETURN_VALIDATON: Record< OUT_OF_MAX_DAYS: 'OUT_OF_MAX_DAYS', ORDER_NOT_INVOICED: 'ORDER_NOT_INVOICED', } - -export const OMS_RETURN_REQUEST_CONFIRMATION = ( - locale = 'en-GB' -): ReturnRequestConfirmation => `oms-return-request-confirmation_${locale}` -export const OMS_RETURN_REQUEST_CONFIRMATION_FRIENDLY_NAME = ( - locale = 'en-GB' -) => `[OMS] Return Request Confirmation_${locale}` - -export const OMS_RETURN_REQUEST_STATUS_UPDATE = ( - locale = 'en-GB' -): ReturnRequestStatusUpdate => `oms-return-request-status-update_${locale}` -export const OMS_RETURN_REQUEST_STATUS_UPDATE_FRIENDLY_NAME = ( - locale = 'en-GB' -) => `[OMS] Return Request Status Update_${locale}` diff --git a/node/utils/emailTemplates.ts b/node/utils/emailTemplates.ts new file mode 100644 index 000000000..edce00dba --- /dev/null +++ b/node/utils/emailTemplates.ts @@ -0,0 +1,21 @@ +import type { TemplateFriendlyName, TemplateName } from '../typings/mailClient' + +/** + * + * @param name Template name (omit the prefix - oms-return-request-) + * @param locale Locale (defaults to 'en-GB') + * @returns {String} Template name + */ +export const templateName = (name: TemplateName, locale: string): string => + `oms-return-request-${name}_${locale}` + +/** + * + * @param { String } name Template friendly name (omit the prefix - [OMS] Return Request) + * @param { String } locale Locale (defaults to 'en-GB') + * @returns { String } Template friendly name + */ +export const templateFriendlyName = ( + name: TemplateFriendlyName, + locale: string +): string => `[OMS] Return Request ${name} (${locale})` diff --git a/node/utils/templates/index.ts b/node/utils/templates/index.ts index eebd291ef..5a86089ef 100644 --- a/node/utils/templates/index.ts +++ b/node/utils/templates/index.ts @@ -1,20 +1,16 @@ import type { Template } from '../../typings/mailClient' -import { - OMS_RETURN_REQUEST_CONFIRMATION, - OMS_RETURN_REQUEST_CONFIRMATION_FRIENDLY_NAME, - OMS_RETURN_REQUEST_STATUS_UPDATE, - OMS_RETURN_REQUEST_STATUS_UPDATE_FRIENDLY_NAME, -} from '../constants' +import { templateFriendlyName, templateName } from '../emailTemplates' import { OMS_RETURN_REQUEST_CONFIRMATION_TEMPLATE_MESSAGE, + OMS_RETURN_REQUEST_LABEL_TEMPLATE_MESSAGE, OMS_RETURN_REQUEST_STATUS_UPDATE_TEMPLATE_MESSAGE, } from './templateMessages' export const OMS_RETURN_REQUEST_CONFIRMATION_TEMPLATE = ( locale = 'en-GB' ): Template => ({ - Name: OMS_RETURN_REQUEST_CONFIRMATION(locale), - FriendlyName: OMS_RETURN_REQUEST_CONFIRMATION_FRIENDLY_NAME(locale), + Name: templateName('confirmation', locale), + FriendlyName: templateFriendlyName('Confirmation', locale), Description: '[OMS] Return requests confirmation message', IsDefaultTemplate: false, AccountId: null, @@ -50,8 +46,8 @@ export const OMS_RETURN_REQUEST_CONFIRMATION_TEMPLATE = ( export const OMS_RETURN_REQUEST_STATUS_UPDATE_TEMPLATE = ( locale = 'en-GB' ): Template => ({ - Name: OMS_RETURN_REQUEST_STATUS_UPDATE(locale), - FriendlyName: OMS_RETURN_REQUEST_STATUS_UPDATE_FRIENDLY_NAME(locale), + Name: templateName('status-update', locale), + FriendlyName: templateFriendlyName('Status Update', locale), Description: '[OMS] Return requests status update message', IsDefaultTemplate: false, AccountId: null, @@ -83,3 +79,40 @@ export const OMS_RETURN_REQUEST_STATUS_UPDATE_TEMPLATE = ( }, }, }) + +export const OMS_RETURN_REQUEST_LABEL_TEMPLATE = ( + locale = 'en-GB' +): Template => ({ + Name: templateName('label', locale), + FriendlyName: templateFriendlyName('Label', locale), + Description: '[OMS] Return requests shipping label message', + IsDefaultTemplate: false, + AccountId: null, + AccountName: null, + ApplicationId: null, + IsPersisted: true, + IsRemoved: false, + Type: '', + Templates: { + email: { + To: '{{data.email}}', + CC: null, + BCC: '{{#compare data.status "==" \'new\'}}{{/compare}}', + Subject: 'Return request shipping label {{data.DocumentId}}', + Message: OMS_RETURN_REQUEST_LABEL_TEMPLATE_MESSAGE, + Type: 'E', + ProviderId: '00000000-0000-0000-0000-000000000000', + ProviderName: null, + IsActive: true, + withError: false, + }, + sms: { + Type: 'S', + ProviderId: null, + ProviderName: null, + IsActive: false, + withError: false, + Parameters: [], + }, + }, +}) diff --git a/node/utils/templates/templateMessages.ts b/node/utils/templates/templateMessages.ts index 7b00e3e72..1a8dfda2f 100644 --- a/node/utils/templates/templateMessages.ts +++ b/node/utils/templates/templateMessages.ts @@ -405,6 +405,29 @@ const STATUS_TIMELINE = html` {{/compare}} ` +const RETURN_LABEL = html` + + +
+

+ Return shipping label +

+

+ You can find the return shipping label to return your items in the + next link: +

+ Return shipping label +
+ + +` + const createTemplate = (headAndStyle: string, ...bodyBlocks: string[]) => { return ` { {detailsPage !== 'return-details' ? null : ( <> + diff --git a/react/admin/ReturnDetails/components/ReturnLabel/ReturnLabel.tsx b/react/admin/ReturnDetails/components/ReturnLabel/ReturnLabel.tsx new file mode 100644 index 000000000..724e41f29 --- /dev/null +++ b/react/admin/ReturnDetails/components/ReturnLabel/ReturnLabel.tsx @@ -0,0 +1,174 @@ +import React, { useEffect, useState } from 'react' +import { useMutation, useQuery } from 'react-apollo' +import { FormattedMessage } from 'react-intl' +import { ButtonPlain, ModalDialog } from 'vtex.styleguide' + +import { useReturnDetails } from '../../../../common/hooks/useReturnDetails' +import GET_APP from './graphql/getInstalledApp.gql' +import SEND_LABEL from './graphql/sendLabel.gql' +// import CREATE_LABEL from './graphql/createLabel.gql' + +const ReturnLabel = () => { + const [displayButton, setDisplayButton] = useState(false) + const [disableLabelUrl, setDisbleLabelUrl] = useState(true) + const [labelUrl, setLabelUrl] = useState('') + const [disableCreateLabel, setDisableCreateLabel] = useState(true) + const [isModalOpen, setIsModalOpen] = useState(false) + const [, setReturnAddress] = useState({ + street1: '', + street2: '', + city: '', + state: '', + zip: '', + country: '', + name: '', + phone: '', + }) + + const { data } = useReturnDetails() + + const { data: installedApp } = useQuery(GET_APP, { + variables: { + slug: 'vtex.easypost', + }, + }) + + useEffect(() => { + if (!installedApp?.app) return + + const { app } = installedApp + + const appSettings = JSON.parse(app.settings) + const { street1, street2, city, state, zip, country, name, phone } = + appSettings + + setReturnAddress({ + street1, + street2, + city, + state, + zip, + country, + name, + phone, + }) + + setDisplayButton(true) + }, [installedApp]) + + useEffect(() => { + if (!data) return + + const { + status, + pickupReturnData: { returnLabel }, + } = data.returnRequestDetails + + if (status === 'processing') { + setDisableCreateLabel(false) + + if (returnLabel) { + setLabelUrl(returnLabel) + setDisbleLabelUrl(false) + } + } + }, [data]) + + useEffect(() => { + if (labelUrl) { + setDisbleLabelUrl(false) + } + }, [labelUrl]) + + const handleToggleModal = () => { + setIsModalOpen(!isModalOpen) + } + + const handleCancelation = () => { + setIsModalOpen(false) + } + + // const [createLabel] = useMutation(CREATE_LABEL) + const [sendLabel] = useMutation(SEND_LABEL) + + const handleConfirmation = async () => { + // Send mutation to EasyPost + // const createdLabelUrl = await createLabel({ + // variables: { + // ...returnAddress, + // }, + // }) + + // temp labelUrl + const createdLabelUrl = + 'https://assets.easypost.com/assets/images/usps-international-label.c7c603e0b25b12e4489a8c75db0d34b8.png' + + setLabelUrl(createdLabelUrl) + + await sendLabel({ + variables: { + returnId: data?.returnRequestDetails.id, + labelUrl: createdLabelUrl, + }, + }) + + // TODO: alert admin if label was sent + + setIsModalOpen(false) + } + + const handleOpenLabelUrl = () => { + window.open(labelUrl, '_blank') + } + + if (!displayButton) return null + + return ( +
+ {displayButton && ( +
+
+ + + +
+ + + +
+ )} + + + ), + onClick: handleConfirmation, + }} + cancelation={{ + label: ( + + ), + onClick: handleCancelation, + }} + > +
+

+ +

+

+ +

+
+
+
+ ) +} + +export { ReturnLabel } diff --git a/react/graphql/createLabel.gql b/react/admin/ReturnDetails/components/ReturnLabel/graphql/createLabel.gql similarity index 100% rename from react/graphql/createLabel.gql rename to react/admin/ReturnDetails/components/ReturnLabel/graphql/createLabel.gql diff --git a/react/admin/ReturnDetails/components/ReturnLabel/graphql/getEasyPostConfig.gql b/react/admin/ReturnDetails/components/ReturnLabel/graphql/getEasyPostConfig.gql new file mode 100644 index 000000000..26dfc8d1c --- /dev/null +++ b/react/admin/ReturnDetails/components/ReturnLabel/graphql/getEasyPostConfig.gql @@ -0,0 +1,13 @@ +query GetEasyPostConfig { + config @context(provider: "vtex.easypost") { + street1 + street2 + city + state + zip + country + name + phone + weight + } +} diff --git a/react/admin/ReturnDetails/components/ReturnLabel/graphql/getInstalledApp.gql b/react/admin/ReturnDetails/components/ReturnLabel/graphql/getInstalledApp.gql new file mode 100644 index 000000000..c95ac99b5 --- /dev/null +++ b/react/admin/ReturnDetails/components/ReturnLabel/graphql/getInstalledApp.gql @@ -0,0 +1,5 @@ +query GetInstalleApp($slug: String!) { + app: installedAppPublic(slug: $slug) @context(provider: "vtex.apps-graphql") { + settings + } +} diff --git a/react/admin/ReturnDetails/components/ReturnLabel/graphql/sendLabel.gql b/react/admin/ReturnDetails/components/ReturnLabel/graphql/sendLabel.gql new file mode 100644 index 000000000..b3fdc9483 --- /dev/null +++ b/react/admin/ReturnDetails/components/ReturnLabel/graphql/sendLabel.gql @@ -0,0 +1,3 @@ +mutation SendLabel($requestId: String, $labelUrl: String) { + sendReturnLabel(requestId: $requestId, labelUrl: $labelUrl) +} From da2f666cb691b13b8b53773dd7558cd5ba68eee9 Mon Sep 17 00:00:00 2001 From: Ezequias Calvo Date: Mon, 29 Aug 2022 12:35:35 +0200 Subject: [PATCH 2/3] feat: add ux ui improvements --- manifest.json | 2 +- messages/context.json | 3 + messages/en.json | 3 + node/package.json | 1 + node/services/sendReturnLabelService.ts | 7 ++ node/yarn.lock | 4 + .../components/ReturnLabel/ReturnLabel.tsx | 76 ++++++++++++------- .../ReturnLabel/graphql/getEasyPostConfig.gql | 13 ---- .../ReturnLabel/graphql/sendLabel.gql | 3 +- react/package.json | 1 + react/yarn.lock | 4 + 11 files changed, 74 insertions(+), 43 deletions(-) delete mode 100644 react/admin/ReturnDetails/components/ReturnLabel/graphql/getEasyPostConfig.gql diff --git a/manifest.json b/manifest.json index c4c113177..d9b4bfed5 100644 --- a/manifest.json +++ b/manifest.json @@ -15,7 +15,7 @@ "vtex.easypost": "0.x", "vtex.tenant-graphql": "0.x", "vtex.catalog-graphql": "1.x", - "vtex.apps-graphql": "2.x" + "vtex.apps-graphql": "3.x" }, "builders": { "admin": "0.x", diff --git a/messages/context.json b/messages/context.json index c7c1892c2..baa717279 100644 --- a/messages/context.json +++ b/messages/context.json @@ -324,6 +324,9 @@ "admin/return-app.return-request-details.return-label.modal-message": "This action is irreversible. The created label will be sent via email to the customer.", "admin/return-app.return-request-details.return-label.create-return-label": "Create return label", "admin/return-app.return-request-details.return-label.see-return-label": "See return label", + "admin/return-app.return-request-details.return-label-info.tooltip": "Here you can create a return shipping label, this label will be send by email to the customer and he can paste it on the package. For this integration to work properly you must install and configure vtex.easypost.", + "admin/return-app.return-request-details.return-label.alert.success": "The return label was created successfully", + "admin/return-app.return-request-details.return-label.alert.error": "There was an error creating the return label, please try again.", "admin/return-app.settings.section.payment-options.refund-method-strategy.checkbox.label": "Automatically refund requests", "admin/return-app.settings.section.payment-options.refund-method-strategy.checkbox.description": "Automatically create the return invoice in the OMS for the requests", "return-app.return-request-details.payent-method.refund-option.refund-process": "Refund process: {automaticallyRefundPaymentMethod, select, true{Automatically} false{Manually}}", diff --git a/messages/en.json b/messages/en.json index 93eccee0e..3eccc31f0 100644 --- a/messages/en.json +++ b/messages/en.json @@ -324,6 +324,9 @@ "admin/return-app.return-request-details.return-label.modal-message": "This action is irreversible. The created label will be sent via email to the customer.", "admin/return-app.return-request-details.return-label.create-return-label": "Create return label", "admin/return-app.return-request-details.return-label.see-return-label": "See return label", + "admin/return-app.return-request-details.return-label-info.tooltip": "Here you can create a return shipping label, this label will be send by email to the customer and he can paste it on the package. For this integration to work properly you must install and configure vtex.easypost.", + "admin/return-app.return-request-details.return-label.alert.success": "The return label was created successfully", + "admin/return-app.return-request-details.return-label.alert.error": "There was an error creating the return label, please try again.", "admin/return-app.settings.section.payment-options.refund-method-strategy.checkbox.label": "Automatically refund requests", "admin/return-app.settings.section.payment-options.refund-method-strategy.checkbox.description": "Automatically create a return invoice in OMS for the requests", "return-app.return-request-details.payent-method.refund-option.refund-process": "Refund process: {automaticallyRefundPaymentMethod, select, true{Automatic} false{Manual}}", diff --git a/node/package.json b/node/package.json index a366b2464..cd0a7a245 100644 --- a/node/package.json +++ b/node/package.json @@ -16,6 +16,7 @@ "@vtex/tsconfig": "^0.6.0", "tslint": "^6.1.3", "tslint-config-vtex": "^2.1.0", + "vtex.apps-graphql": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.apps-graphql@3.12.1/public/@types/vtex.apps-graphql", "vtex.catalog-graphql": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.catalog-graphql@1.101.1/public/@types/vtex.catalog-graphql", "vtex.css-handles": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.css-handles@0.4.4/public/@types/vtex.css-handles", "vtex.easypost": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.easypost@0.1.1/public/@types/vtex.easypost", diff --git a/node/services/sendReturnLabelService.ts b/node/services/sendReturnLabelService.ts index 39889df7f..938a75b2b 100644 --- a/node/services/sendReturnLabelService.ts +++ b/node/services/sendReturnLabelService.ts @@ -16,6 +16,13 @@ export const sendReturnLabelService = async ( vtex: { logger }, } = ctx + if (!requestId || !labelUrl) { + throw new ResolverError( + 'The requestId or the labelUrl was not provided', + 400 + ) + } + const returnRequest = (await returnRequestClient.get(requestId, [ '_all', ])) as ReturnRequest diff --git a/node/yarn.lock b/node/yarn.lock index f7e68b3e7..3ba51a757 100644 --- a/node/yarn.lock +++ b/node/yarn.lock @@ -6127,6 +6127,10 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +"vtex.apps-graphql@http://vtex.vtexassets.com/_v/public/typings/v1/vtex.apps-graphql@3.12.1/public/@types/vtex.apps-graphql": + version "3.12.1" + resolved "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.apps-graphql@3.12.1/public/@types/vtex.apps-graphql#5e6ab3c2fa3d154d11798d6243585056bd2f7711" + "vtex.catalog-graphql@http://vtex.vtexassets.com/_v/public/typings/v1/vtex.catalog-graphql@1.101.1/public/@types/vtex.catalog-graphql": version "1.101.1" resolved "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.catalog-graphql@1.101.1/public/@types/vtex.catalog-graphql#8f4d8981c04429e94b30188acb9c6f1a6b912197" diff --git a/react/admin/ReturnDetails/components/ReturnLabel/ReturnLabel.tsx b/react/admin/ReturnDetails/components/ReturnLabel/ReturnLabel.tsx index 724e41f29..c87ea7497 100644 --- a/react/admin/ReturnDetails/components/ReturnLabel/ReturnLabel.tsx +++ b/react/admin/ReturnDetails/components/ReturnLabel/ReturnLabel.tsx @@ -1,12 +1,13 @@ import React, { useEffect, useState } from 'react' import { useMutation, useQuery } from 'react-apollo' import { FormattedMessage } from 'react-intl' -import { ButtonPlain, ModalDialog } from 'vtex.styleguide' +import { ButtonPlain, ModalDialog, Tooltip, IconInfo } from 'vtex.styleguide' import { useReturnDetails } from '../../../../common/hooks/useReturnDetails' +import { useAlert } from '../../../hooks/userAlert' import GET_APP from './graphql/getInstalledApp.gql' import SEND_LABEL from './graphql/sendLabel.gql' -// import CREATE_LABEL from './graphql/createLabel.gql' +import CREATE_LABEL from './graphql/createLabel.gql' const ReturnLabel = () => { const [displayButton, setDisplayButton] = useState(false) @@ -26,6 +27,7 @@ const ReturnLabel = () => { }) const { data } = useReturnDetails() + const { openAlert } = useAlert() const { data: installedApp } = useQuery(GET_APP, { variables: { @@ -70,16 +72,11 @@ const ReturnLabel = () => { if (returnLabel) { setLabelUrl(returnLabel) setDisbleLabelUrl(false) + setDisableCreateLabel(true) } } }, [data]) - useEffect(() => { - if (labelUrl) { - setDisbleLabelUrl(false) - } - }, [labelUrl]) - const handleToggleModal = () => { setIsModalOpen(!isModalOpen) } @@ -88,31 +85,39 @@ const ReturnLabel = () => { setIsModalOpen(false) } - // const [createLabel] = useMutation(CREATE_LABEL) - const [sendLabel] = useMutation(SEND_LABEL) + const [, { loading: creatingLabel }] = useMutation(CREATE_LABEL) + const [sendLabel, { loading: sendingEmail }] = useMutation(SEND_LABEL) const handleConfirmation = async () => { - // Send mutation to EasyPost - // const createdLabelUrl = await createLabel({ - // variables: { - // ...returnAddress, - // }, - // }) - - // temp labelUrl + // temp labelUrl [DELETE when we have the client Key from easypost] const createdLabelUrl = 'https://assets.easypost.com/assets/images/usps-international-label.c7c603e0b25b12e4489a8c75db0d34b8.png' - setLabelUrl(createdLabelUrl) - - await sendLabel({ - variables: { - returnId: data?.returnRequestDetails.id, - labelUrl: createdLabelUrl, - }, - }) - - // TODO: alert admin if label was sent + try { + // Send mutation to EasyPost + // const createdLabelUrl = await createLabel({ + // variables: { + // ...returnAddress, + // }, + // }) + + await sendLabel({ + variables: { + requestId: data?.returnRequestDetails.id, + labelUrl: createdLabelUrl, + }, + }) + + openAlert( + 'success', + + ) + } catch (error) { + openAlert( + 'error', + + ) + } setIsModalOpen(false) } @@ -139,12 +144,27 @@ const ReturnLabel = () => { +
+ + } + position="left" + > +
+ + + +
+
+
)} diff --git a/react/admin/ReturnDetails/components/ReturnLabel/graphql/getEasyPostConfig.gql b/react/admin/ReturnDetails/components/ReturnLabel/graphql/getEasyPostConfig.gql deleted file mode 100644 index 26dfc8d1c..000000000 --- a/react/admin/ReturnDetails/components/ReturnLabel/graphql/getEasyPostConfig.gql +++ /dev/null @@ -1,13 +0,0 @@ -query GetEasyPostConfig { - config @context(provider: "vtex.easypost") { - street1 - street2 - city - state - zip - country - name - phone - weight - } -} diff --git a/react/admin/ReturnDetails/components/ReturnLabel/graphql/sendLabel.gql b/react/admin/ReturnDetails/components/ReturnLabel/graphql/sendLabel.gql index b3fdc9483..2a176d0d0 100644 --- a/react/admin/ReturnDetails/components/ReturnLabel/graphql/sendLabel.gql +++ b/react/admin/ReturnDetails/components/ReturnLabel/graphql/sendLabel.gql @@ -1,3 +1,4 @@ -mutation SendLabel($requestId: String, $labelUrl: String) { +mutation SendLabel($requestId: String!, $labelUrl: String!) { sendReturnLabel(requestId: $requestId, labelUrl: $labelUrl) + @context(provider: "vtex.return-app") } diff --git a/react/package.json b/react/package.json index 615e4e268..45c358662 100644 --- a/react/package.json +++ b/react/package.json @@ -26,6 +26,7 @@ "@vtex/test-tools": "^0.3.2", "@vtex/tsconfig": "^0.6.0", "prop-types": "^15.7.2", + "vtex.apps-graphql": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.apps-graphql@3.12.1/public/@types/vtex.apps-graphql", "vtex.catalog-graphql": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.catalog-graphql@1.101.1/public/@types/vtex.catalog-graphql", "vtex.css-handles": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.css-handles@0.4.4/public/@types/vtex.css-handles", "vtex.easypost": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.easypost@0.1.1/public/@types/vtex.easypost", diff --git a/react/yarn.lock b/react/yarn.lock index 494192068..eb8f6e2b6 100644 --- a/react/yarn.lock +++ b/react/yarn.lock @@ -5178,6 +5178,10 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +"vtex.apps-graphql@http://vtex.vtexassets.com/_v/public/typings/v1/vtex.apps-graphql@3.12.1/public/@types/vtex.apps-graphql": + version "3.12.1" + resolved "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.apps-graphql@3.12.1/public/@types/vtex.apps-graphql#5e6ab3c2fa3d154d11798d6243585056bd2f7711" + "vtex.catalog-graphql@http://vtex.vtexassets.com/_v/public/typings/v1/vtex.catalog-graphql@1.101.1/public/@types/vtex.catalog-graphql": version "1.101.1" resolved "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.catalog-graphql@1.101.1/public/@types/vtex.catalog-graphql#8f4d8981c04429e94b30188acb9c6f1a6b912197" From d12fee0d8428c5f99dcde2de039e9c327ee097fe Mon Sep 17 00:00:00 2001 From: edyespinal Date: Tue, 6 Sep 2022 19:12:43 +0200 Subject: [PATCH 3/3] chore: comments made on PR --- graphql/types/ReturnRequest.graphql | 3 +- messages/context.json | 3 +- messages/en.json | 3 +- node/package.json | 4 +- node/services/sendReturnLabelService.ts | 4 +- node/services/updateRequestStatusService.ts | 17 +- node/typings/mailClient.d.ts | 2 +- node/yarn.lock | 10 +- .../ReturnDetails/ReturnDetailsContainer.tsx | 2 +- .../components/ReturnLabel/ReturnLabel.tsx | 193 +++++++++--------- .../components/ReturnDetails/ReturnLabel.tsx | 47 +++++ .../common/graphql/returnDetailsFragment.gql | 1 + react/package.json | 4 +- .../ReturnRequest/StoreReturnDetails.tsx | 2 + react/store/provider/OrderToReturnReducer.ts | 1 + react/typings/vtex.return-app.d.ts | 11 + react/yarn.lock | 10 +- 17 files changed, 194 insertions(+), 123 deletions(-) create mode 100644 react/common/components/ReturnDetails/ReturnLabel.tsx diff --git a/graphql/types/ReturnRequest.graphql b/graphql/types/ReturnRequest.graphql index 7ae4911d0..9025c5a82 100644 --- a/graphql/types/ReturnRequest.graphql +++ b/graphql/types/ReturnRequest.graphql @@ -45,6 +45,7 @@ input PickupReturnDataInput { country: String! zipCode: String! addressType: AddressType! + labelUrl: String! } enum AddressType { @@ -110,7 +111,7 @@ type PickupReturnData { country: String! zipCode: String! addressType: AddressType! - returnLabel: String + labelUrl: String } type RefundPaymentData { diff --git a/messages/context.json b/messages/context.json index baa717279..082f12303 100644 --- a/messages/context.json +++ b/messages/context.json @@ -308,6 +308,7 @@ "return-app.return-request-details.order-id.link": "Order {orderId}", "return-app.return-request-details.current-status.request-id": "Request id: {id}", "return-app.return-request-details.current-status.status": "Status:", + "return-app.return-request-details.current-status.see-return-label": "See return label", "admin/return-app.return-request-list.table-data.requestId.tooltip": "Tooltip with an explanation of how the customer can access the data", "admin/return-app.settings.modal-warning.title": "Title of a modal to warn about max days settings", "admin/return-app.settings.modal-warning.first-paragraph": "First parahraph describing the warning", @@ -324,7 +325,7 @@ "admin/return-app.return-request-details.return-label.modal-message": "This action is irreversible. The created label will be sent via email to the customer.", "admin/return-app.return-request-details.return-label.create-return-label": "Create return label", "admin/return-app.return-request-details.return-label.see-return-label": "See return label", - "admin/return-app.return-request-details.return-label-info.tooltip": "Here you can create a return shipping label, this label will be send by email to the customer and he can paste it on the package. For this integration to work properly you must install and configure vtex.easypost.", + "admin/return-app.return-request-details.return-label-info.tooltip": "Create a return shipping label and send it to the customer via email. The request status has to be processing. For this integration to work properly you must install and configure vtex.easypost.", "admin/return-app.return-request-details.return-label.alert.success": "The return label was created successfully", "admin/return-app.return-request-details.return-label.alert.error": "There was an error creating the return label, please try again.", "admin/return-app.settings.section.payment-options.refund-method-strategy.checkbox.label": "Automatically refund requests", diff --git a/messages/en.json b/messages/en.json index 3eccc31f0..4aec6cc35 100644 --- a/messages/en.json +++ b/messages/en.json @@ -308,6 +308,7 @@ "return-app.return-request-details.order-id.link": "Order {orderId}", "return-app.return-request-details.current-status.request-id": "Request ID: {id}", "return-app.return-request-details.current-status.status": "Status:", + "return-app.return-request-details.current-status.see-return-label": "See return label", "admin/return-app.return-request-list.table-data.requestId.tooltip": "Customers can see their request ID inside the request details", "admin/return-app.settings.modal-warning.title": "Please review your settings", "admin/return-app.settings.modal-warning.first-paragraph": "It looks like you are trying to save custom return options, none of which reach {maxDays}. This is the maximum days set for a return.", @@ -324,7 +325,7 @@ "admin/return-app.return-request-details.return-label.modal-message": "This action is irreversible. The created label will be sent via email to the customer.", "admin/return-app.return-request-details.return-label.create-return-label": "Create return label", "admin/return-app.return-request-details.return-label.see-return-label": "See return label", - "admin/return-app.return-request-details.return-label-info.tooltip": "Here you can create a return shipping label, this label will be send by email to the customer and he can paste it on the package. For this integration to work properly you must install and configure vtex.easypost.", + "admin/return-app.return-request-details.return-label-info.tooltip": "Create a return shipping label and send it to the customer via email. The request status has to be processing. For this integration to work properly you must install and configure vtex.easypost.", "admin/return-app.return-request-details.return-label.alert.success": "The return label was created successfully", "admin/return-app.return-request-details.return-label.alert.error": "There was an error creating the return label, please try again.", "admin/return-app.settings.section.payment-options.refund-method-strategy.checkbox.label": "Automatically refund requests", diff --git a/node/package.json b/node/package.json index cd0a7a245..706da46b1 100644 --- a/node/package.json +++ b/node/package.json @@ -24,9 +24,9 @@ "vtex.my-account": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.my-account@1.25.0/public/@types/vtex.my-account", "vtex.my-account-commons": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.my-account-commons@1.6.0/public/@types/vtex.my-account-commons", "vtex.render-runtime": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.render-runtime@8.132.4/public/@types/vtex.render-runtime", - "vtex.return-app": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.return-app@3.5.0/public/@types/vtex.return-app", + "vtex.return-app": "https://rraep--powerplanet.myvtex.com/_v/private/typings/linked/v1/vtex.return-app@3.5.0+build1662483831/public/@types/vtex.return-app", "vtex.store-graphql": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.store-graphql@2.155.32/public/@types/vtex.store-graphql", - "vtex.styleguide": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.styleguide@9.146.1/public/@types/vtex.styleguide", + "vtex.styleguide": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.styleguide@9.146.2/public/@types/vtex.styleguide", "vtex.tenant-graphql": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.tenant-graphql@0.1.2/public/@types/vtex.tenant-graphql" }, "scripts": { diff --git a/node/services/sendReturnLabelService.ts b/node/services/sendReturnLabelService.ts index 938a75b2b..e9738e03c 100644 --- a/node/services/sendReturnLabelService.ts +++ b/node/services/sendReturnLabelService.ts @@ -74,9 +74,9 @@ export const sendReturnLabelService = async ( templateName: templateName('label', locale), jsonData: { data: { - name: customerProfileData?.name ?? '', + name: customerProfileData.name, DocumentId: requestId, - email: customerProfileData?.email ?? '', + email: customerProfileData.email, labelUrl, }, }, diff --git a/node/services/updateRequestStatusService.ts b/node/services/updateRequestStatusService.ts index 085da5ff5..e9c7d6392 100644 --- a/node/services/updateRequestStatusService.ts +++ b/node/services/updateRequestStatusService.ts @@ -232,7 +232,7 @@ export const updateRequestStatusService = async ( // We add a try/catch here so we avoid sending an error to the browser only if the email fails. try { const templateExists = await mail.getTemplate( - templateName('status-update', cultureInfoData?.locale) + templateName('status-update', cultureInfoData.locale) ) if (!templateExists) { @@ -251,7 +251,7 @@ export const updateRequestStatusService = async ( } = updatedRequest const mailData: StatusUpdateMailData = { - templateName: templateName('status-update', cultureInfoData?.locale), + templateName: templateName('status-update', cultureInfoData.locale), jsonData: { data: { status: updatedStatus, @@ -260,15 +260,22 @@ export const updateRequestStatusService = async ( email: customerProfileData?.email ?? '', paymentMethod: refundPaymentData?.refundPaymentMethod ?? '', iban: refundPaymentData?.iban ?? '', - refundedAmount: - Number(updatedRefundData?.refundedItemsValue) + - Number(updatedRefundData?.refundedShippingValue), }, products: items, refundStatusData: updatedRefundStatusData, }, } + if (updatedRefundData?.refundedItemsValue) { + let refundedAmount = Number(updatedRefundData.refundedItemsValue) + + if (updatedRefundData.refundedShippingValue) { + refundedAmount += Number(updatedRefundData.refundedShippingValue) + } + + mailData.jsonData.data.refundedAmount = refundedAmount + } + await mail.sendMail(mailData) } catch (error) { logger.warn({ diff --git a/node/typings/mailClient.d.ts b/node/typings/mailClient.d.ts index 610f9873e..a14342de3 100644 --- a/node/typings/mailClient.d.ts +++ b/node/typings/mailClient.d.ts @@ -34,7 +34,7 @@ interface StatusUpdateData { email: string paymentMethod: string iban: string - refundedAmount: number + refundedAmount?: number } products: ReturnRequest['items'] refundStatusData: RefundStatusData[] diff --git a/node/yarn.lock b/node/yarn.lock index 3ba51a757..d461f6afc 100644 --- a/node/yarn.lock +++ b/node/yarn.lock @@ -6159,17 +6159,17 @@ verror@1.10.0: version "8.132.4" resolved "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.render-runtime@8.132.4/public/@types/vtex.render-runtime#66bb41bd4d342e37c9d85172aad5f7eefebfb6dc" -"vtex.return-app@http://vtex.vtexassets.com/_v/public/typings/v1/vtex.return-app@3.5.0/public/@types/vtex.return-app": +"vtex.return-app@https://rraep--powerplanet.myvtex.com/_v/private/typings/linked/v1/vtex.return-app@3.5.0+build1662483831/public/@types/vtex.return-app": version "3.5.0" - resolved "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.return-app@3.5.0/public/@types/vtex.return-app#29e8ca8294b52af366586fcea7ae995870bc0271" + resolved "https://rraep--powerplanet.myvtex.com/_v/private/typings/linked/v1/vtex.return-app@3.5.0+build1662483831/public/@types/vtex.return-app#2a0c0e4df36fe5fcebd79598dd0df7a3ee62dee7" "vtex.store-graphql@http://vtex.vtexassets.com/_v/public/typings/v1/vtex.store-graphql@2.155.32/public/@types/vtex.store-graphql": version "2.155.32" resolved "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.store-graphql@2.155.32/public/@types/vtex.store-graphql#abafbe1d80f453bbebc70024a829e4226bf6cc20" -"vtex.styleguide@http://vtex.vtexassets.com/_v/public/typings/v1/vtex.styleguide@9.146.1/public/@types/vtex.styleguide": - version "9.146.1" - resolved "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.styleguide@9.146.1/public/@types/vtex.styleguide#66db40ca1b78ad77ce4beedabecee320e1ef2ead" +"vtex.styleguide@http://vtex.vtexassets.com/_v/public/typings/v1/vtex.styleguide@9.146.2/public/@types/vtex.styleguide": + version "9.146.2" + resolved "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.styleguide@9.146.2/public/@types/vtex.styleguide#b0f097f3fef06316a916d6782cf077349bb34a3b" "vtex.tenant-graphql@http://vtex.vtexassets.com/_v/public/typings/v1/vtex.tenant-graphql@0.1.2/public/@types/vtex.tenant-graphql": version "0.1.2" diff --git a/react/admin/ReturnDetails/ReturnDetailsContainer.tsx b/react/admin/ReturnDetails/ReturnDetailsContainer.tsx index e424a4d84..49cfed42a 100644 --- a/react/admin/ReturnDetails/ReturnDetailsContainer.tsx +++ b/react/admin/ReturnDetails/ReturnDetailsContainer.tsx @@ -68,8 +68,8 @@ export const ReturnDetailsContainer = () => { {detailsPage !== 'return-details' ? null : ( <> - +
diff --git a/react/admin/ReturnDetails/components/ReturnLabel/ReturnLabel.tsx b/react/admin/ReturnDetails/components/ReturnLabel/ReturnLabel.tsx index c87ea7497..4ea6cb2fa 100644 --- a/react/admin/ReturnDetails/components/ReturnLabel/ReturnLabel.tsx +++ b/react/admin/ReturnDetails/components/ReturnLabel/ReturnLabel.tsx @@ -1,7 +1,14 @@ -import React, { useEffect, useState } from 'react' +import React, { Fragment, useState } from 'react' import { useMutation, useQuery } from 'react-apollo' import { FormattedMessage } from 'react-intl' -import { ButtonPlain, ModalDialog, Tooltip, IconInfo } from 'vtex.styleguide' +import { + ButtonPlain, + Collapsible, + Link, + ModalDialog, + Tooltip, + IconInfo, +} from 'vtex.styleguide' import { useReturnDetails } from '../../../../common/hooks/useReturnDetails' import { useAlert } from '../../../hooks/userAlert' @@ -10,82 +17,62 @@ import SEND_LABEL from './graphql/sendLabel.gql' import CREATE_LABEL from './graphql/createLabel.gql' const ReturnLabel = () => { - const [displayButton, setDisplayButton] = useState(false) - const [disableLabelUrl, setDisbleLabelUrl] = useState(true) - const [labelUrl, setLabelUrl] = useState('') - const [disableCreateLabel, setDisableCreateLabel] = useState(true) - const [isModalOpen, setIsModalOpen] = useState(false) - const [, setReturnAddress] = useState({ - street1: '', - street2: '', - city: '', - state: '', - zip: '', - country: '', - name: '', - phone: '', - }) - const { data } = useReturnDetails() const { openAlert } = useAlert() - const { data: installedApp } = useQuery(GET_APP, { + const [labelUrl, setLabelUrl] = useState( + data?.returnRequestDetails.pickupReturnData.labelUrl ?? '' + ) + + const [returnAddress, setReturnAddress] = useState( + null + ) + + const [isModalOpen, setIsModalOpen] = useState(false) + const [isCollapsibleOpen, setIsCollapsibleOpen] = useState(false) + + const { loading, error } = useQuery<{ + app: { + settings: string + } + }>(GET_APP, { variables: { slug: 'vtex.easypost', }, + onCompleted(installedApp) { + const { + app: { settings }, + } = installedApp + + const { street1, street2, city, state, zip, country, name, phone } = + JSON.parse(settings) as ReturnLabelAddress + + setReturnAddress({ + street1, + street2, + city, + state, + zip, + country, + name, + phone, + }) + }, }) - useEffect(() => { - if (!installedApp?.app) return - - const { app } = installedApp - - const appSettings = JSON.parse(app.settings) - const { street1, street2, city, state, zip, country, name, phone } = - appSettings - - setReturnAddress({ - street1, - street2, - city, - state, - zip, - country, - name, - phone, - }) - - setDisplayButton(true) - }, [installedApp]) - - useEffect(() => { - if (!data) return - - const { - status, - pickupReturnData: { returnLabel }, - } = data.returnRequestDetails - - if (status === 'processing') { - setDisableCreateLabel(false) - - if (returnLabel) { - setLabelUrl(returnLabel) - setDisbleLabelUrl(false) - setDisableCreateLabel(true) - } - } - }, [data]) - const handleToggleModal = () => { setIsModalOpen(!isModalOpen) } + const handleToggleCollapsible = () => { + setIsCollapsibleOpen(!isCollapsibleOpen) + } + const handleCancelation = () => { setIsModalOpen(false) } - const [, { loading: creatingLabel }] = useMutation(CREATE_LABEL) + const [, { loading: loadingLabel }] = useMutation(CREATE_LABEL) const [sendLabel, { loading: sendingEmail }] = useMutation(SEND_LABEL) const handleConfirmation = async () => { @@ -108,11 +95,13 @@ const ReturnLabel = () => { }, }) + setLabelUrl(createdLabelUrl) + openAlert( 'success', ) - } catch (error) { + } catch (err) { openAlert( 'error', @@ -122,49 +111,59 @@ const ReturnLabel = () => { setIsModalOpen(false) } - const handleOpenLabelUrl = () => { - window.open(labelUrl, '_blank') - } - - if (!displayButton) return null + if (loading || error) return null return ( -
- {displayButton && ( -
-
- - - -
- - - -
- +
+
+ {returnAddress && + (labelUrl === '' ? ( + + + + +
+ + } + position="right" + > +
+ + + +
+
+
+
+ ) : ( + + +
} - position="left" + isOpen={isCollapsibleOpen} + onClick={handleToggleCollapsible} > -
- - - +
+ + {labelUrl} +
- -
-
- )} + + ))} +
diff --git a/react/common/components/ReturnDetails/ReturnLabel.tsx b/react/common/components/ReturnDetails/ReturnLabel.tsx new file mode 100644 index 000000000..8e9b84bfa --- /dev/null +++ b/react/common/components/ReturnDetails/ReturnLabel.tsx @@ -0,0 +1,47 @@ +import React, { useState } from 'react' +import { FormattedMessage } from 'react-intl' +import { useCssHandles } from 'vtex.css-handles' +import { Collapsible, Link } from 'vtex.styleguide' + +import { useReturnDetails } from '../../hooks/useReturnDetails' + +const CSS_HANDLES = ['returnLabelContainer'] as const + +const ReturnLabel = () => { + const [isOpen, setIsOpen] = useState(false) + const handles = useCssHandles(CSS_HANDLES) + + const { data } = useReturnDetails() + + const handleToggleCollapsible = () => { + setIsOpen(!isOpen) + } + + if (!data) return null + + const { + pickupReturnData: { labelUrl }, + } = data.returnRequestDetails + + return ( +
+
+ + +
+ } + isOpen={isOpen} + onClick={handleToggleCollapsible} + > + + {labelUrl} + + +
+
+ ) +} + +export { ReturnLabel } diff --git a/react/common/graphql/returnDetailsFragment.gql b/react/common/graphql/returnDetailsFragment.gql index 429b52db7..8712e3740 100644 --- a/react/common/graphql/returnDetailsFragment.gql +++ b/react/common/graphql/returnDetailsFragment.gql @@ -45,6 +45,7 @@ fragment ReturnDetailsAdminFragment on ReturnRequestResponse { state zipCode addressType + labelUrl } refundPaymentData { refundPaymentMethod diff --git a/react/package.json b/react/package.json index 45c358662..11b28d970 100644 --- a/react/package.json +++ b/react/package.json @@ -34,9 +34,9 @@ "vtex.my-account": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.my-account@1.25.0/public/@types/vtex.my-account", "vtex.my-account-commons": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.my-account-commons@1.6.0/public/@types/vtex.my-account-commons", "vtex.render-runtime": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.render-runtime@8.132.4/public/@types/vtex.render-runtime", - "vtex.return-app": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.return-app@3.5.0/public/@types/vtex.return-app", + "vtex.return-app": "https://rraep--powerplanet.myvtex.com/_v/private/typings/linked/v1/vtex.return-app@3.5.0+build1662483831/public/@types/vtex.return-app", "vtex.store-graphql": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.store-graphql@2.155.32/public/@types/vtex.store-graphql", - "vtex.styleguide": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.styleguide@9.146.1/public/@types/vtex.styleguide", + "vtex.styleguide": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.styleguide@9.146.2/public/@types/vtex.styleguide", "vtex.tenant-graphql": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.tenant-graphql@0.1.2/public/@types/vtex.tenant-graphql" }, "version": "3.5.0" diff --git a/react/store/ReturnRequest/StoreReturnDetails.tsx b/react/store/ReturnRequest/StoreReturnDetails.tsx index ba29d9538..cd6d02509 100644 --- a/react/store/ReturnRequest/StoreReturnDetails.tsx +++ b/react/store/ReturnRequest/StoreReturnDetails.tsx @@ -20,6 +20,7 @@ import { CurrentRequestStatus } from '../../common/components/ReturnDetails/Curr import RequestCancellation from '../../common/components/ReturnDetails/RequestCancellation' import { UpdateRequestStatusProvider } from '../../admin/provider/UpdateRequestStatusProvider' import { AlertProvider } from '../../admin/provider/AlertProvider' +import { ReturnLabel } from '../../common/components/ReturnDetails/ReturnLabel' const CSS_HANDLES = ['contactPickupContainer'] as const @@ -53,6 +54,7 @@ const StoreReturnDetails = () => { +
= T | null type GeoCoordinates = Array> diff --git a/react/yarn.lock b/react/yarn.lock index eb8f6e2b6..8b932bde7 100644 --- a/react/yarn.lock +++ b/react/yarn.lock @@ -5210,17 +5210,17 @@ verror@1.10.0: version "8.132.4" resolved "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.render-runtime@8.132.4/public/@types/vtex.render-runtime#66bb41bd4d342e37c9d85172aad5f7eefebfb6dc" -"vtex.return-app@http://vtex.vtexassets.com/_v/public/typings/v1/vtex.return-app@3.5.0/public/@types/vtex.return-app": +"vtex.return-app@https://rraep--powerplanet.myvtex.com/_v/private/typings/linked/v1/vtex.return-app@3.5.0+build1662483831/public/@types/vtex.return-app": version "3.5.0" - resolved "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.return-app@3.5.0/public/@types/vtex.return-app#29e8ca8294b52af366586fcea7ae995870bc0271" + resolved "https://rraep--powerplanet.myvtex.com/_v/private/typings/linked/v1/vtex.return-app@3.5.0+build1662483831/public/@types/vtex.return-app#2a0c0e4df36fe5fcebd79598dd0df7a3ee62dee7" "vtex.store-graphql@http://vtex.vtexassets.com/_v/public/typings/v1/vtex.store-graphql@2.155.32/public/@types/vtex.store-graphql": version "2.155.32" resolved "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.store-graphql@2.155.32/public/@types/vtex.store-graphql#abafbe1d80f453bbebc70024a829e4226bf6cc20" -"vtex.styleguide@http://vtex.vtexassets.com/_v/public/typings/v1/vtex.styleguide@9.146.1/public/@types/vtex.styleguide": - version "9.146.1" - resolved "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.styleguide@9.146.1/public/@types/vtex.styleguide#66db40ca1b78ad77ce4beedabecee320e1ef2ead" +"vtex.styleguide@http://vtex.vtexassets.com/_v/public/typings/v1/vtex.styleguide@9.146.2/public/@types/vtex.styleguide": + version "9.146.2" + resolved "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.styleguide@9.146.2/public/@types/vtex.styleguide#b0f097f3fef06316a916d6782cf077349bb34a3b" "vtex.tenant-graphql@http://vtex.vtexassets.com/_v/public/typings/v1/vtex.tenant-graphql@0.1.2/public/@types/vtex.tenant-graphql": version "0.1.2"