Skip to content

Commit

Permalink
Transaction Details → Show steps to resolve for inquiries (#7292)
Browse files Browse the repository at this point in the history
Co-authored-by: Rua Haszard <[email protected]>
  • Loading branch information
Jinksi and haszari authored Oct 4, 2023
1 parent e1b3b62 commit 1ab3115
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 98 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Significance: patch
Type: add
Comment: Behind feature flag: add steps to resolve section to the transaction dispute details


Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import { getAdminUrl } from 'wcpay/utils';
import DisputeNotice from './dispute-notice';
import IssuerEvidenceList from './evidence-list';
import DisputeSummaryRow from './dispute-summary-row';
import DisputeSteps from './dispute-steps';
import { DisputeSteps, InquirySteps } from './dispute-steps';
import InlineNotice from 'components/inline-notice';
import './style.scss';

Expand Down Expand Up @@ -164,8 +164,6 @@ const DisputeAwaitingResponseDetails: React.FC< Props > = ( {
const countdownDays = Math.floor( dueBy.diff( now, 'days', true ) );
const hasStagedEvidence = dispute.evidence_details?.has_evidence;
const { createErrorNotice } = useDispatch( 'core/notices' );
// This is a temporary restriction and can be removed once steps and actions for inquiries are implemented.
const showDisputeSteps = ! isInquiry( dispute );

const onModalClose = () => {
setModalOpen( false );
Expand Down Expand Up @@ -207,18 +205,23 @@ const DisputeAwaitingResponseDetails: React.FC< Props > = ( {
) }
</InlineNotice>
) }
<DisputeSummaryRow
dispute={ dispute }
daysRemaining={ countdownDays }
/>
{ showDisputeSteps && (

<DisputeSummaryRow dispute={ dispute } />

{ isInquiry( dispute ) ? (
<InquirySteps
dispute={ dispute }
customer={ customer }
chargeCreated={ chargeCreated }
/>
) : (
<DisputeSteps
dispute={ dispute }
customer={ customer }
chargeCreated={ chargeCreated }
daysRemaining={ countdownDays }
/>
) }

<IssuerEvidenceList
issuerEvidence={ dispute.issuer_evidence }
/>
Expand Down
52 changes: 52 additions & 0 deletions client/payment-details/dispute-details/dispute-due-by-date.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* External dependencies
*/
import React from 'react';
import { dateI18n } from '@wordpress/date';
import { __, _n, sprintf } from '@wordpress/i18n';
import classNames from 'classnames';
import moment from 'moment';

const DisputeDueByDate: React.FC< {
dueBy: number;
} > = ( { dueBy } ) => {
const daysRemaining = Math.floor(
moment.unix( dueBy ).diff( moment(), 'days', true )
);
const respondByDate = dateI18n(
'M j, Y, g:ia',
moment( dueBy * 1000 ).toISOString()
);
return (
<span className="dispute-steps__steps__response-date">
{ respondByDate }
<span
className={ classNames( {
'dispute-steps__steps__response-date--urgent':
daysRemaining < 3,
'dispute-steps__steps__response-date--warning':
daysRemaining < 7 && daysRemaining > 2,
} ) }
>
{ daysRemaining > 0 &&
sprintf(
// Translators: %d is the number of days left to respond to the dispute.
_n(
'(%d day left to respond)',
'(%d days left to respond)',
daysRemaining,
'woocommerce-payments'
),
daysRemaining
) }

{ daysRemaining === 0 &&
__( '(Last day today)', 'woocommerce-payments' ) }
{ daysRemaining < 0 &&
__( '(Past due)', 'woocommerce-payments' ) }
</span>
</span>
);
};

export default DisputeDueByDate;
172 changes: 124 additions & 48 deletions client/payment-details/dispute-details/dispute-steps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@
* External dependencies
*/
import React from 'react';
import { __, _n, sprintf } from '@wordpress/i18n';
import { __, sprintf } from '@wordpress/i18n';
import { createInterpolateElement } from '@wordpress/element';
import { ExternalLink } from '@wordpress/components';
import { dateI18n } from '@wordpress/date';
import moment from 'moment';
import HelpOutlineIcon from 'gridicons/dist/help-outline';
import classNames from 'classnames';

/**
* Internal dependencies
Expand All @@ -20,19 +19,18 @@ import { ChargeBillingDetails } from 'wcpay/types/charges';
import { formatExplicitCurrency } from 'utils/currency';
import { ClickTooltip } from 'wcpay/components/tooltip';
import { getDisputeFeeFormatted } from 'wcpay/disputes/utils';
import DisputeDueByDate from './dispute-due-by-date';

interface Props {
dispute: Dispute;
customer: ChargeBillingDetails | null;
chargeCreated: number;
daysRemaining: number;
}

const DisputeSteps: React.FC< Props > = ( {
export const DisputeSteps: React.FC< Props > = ( {
dispute,
customer,
chargeCreated,
daysRemaining,
} ) => {
let emailLink;
if ( customer?.email ) {
Expand All @@ -57,7 +55,7 @@ const DisputeSteps: React.FC< Props > = ( {
const emailBody = sprintf(
// Translators: %1$s is the customer name, %2$s is the dispute date, %3$s is the dispute amount with currency-code e.g. $15 USD, %4$s is the charge date.
__(
`Hello %1$s\n\n` +
`Hello %1$s,\n\n` +
`We noticed that on %2$s, you disputed a %3$s charge on %4$s. We wanted to contact you to make sure everything was all right with your purchase and see if there's anything else we can do to resolve any problems you might have had.\n\n` +
`Alternatively, if the dispute was a mistake, you can easily withdraw it by calling the number on the back of your card. Thank you so much - we appreciate your business and look forward to working with you.`,
'woocommerce-payments'
Expand All @@ -72,13 +70,6 @@ const DisputeSteps: React.FC< Props > = ( {
) }&body=${ encodeURIComponent( emailBody ) }`;
}

const respondByDate = dispute.evidence_details?.due_by
? dateI18n(
'M j, Y, g:ia',
moment( dispute.evidence_details?.due_by * 1000 ).toISOString()
)
: '–';

return (
<div className="dispute-steps">
<div className="dispute-steps__header">
Expand Down Expand Up @@ -124,15 +115,15 @@ const DisputeSteps: React.FC< Props > = ( {
<li>
{ createInterpolateElement(
__(
'Challenge <challengeicon/> or accept <accepticon/> the dispute by <disputeduedate/>.',
'Challenge <challengeIcon/> or accept <acceptIcon/> the dispute by <dueByDate/>.',
'woocommerce-payments'
),
{
challengeicon: (
challengeIcon: (
<ClickTooltip
buttonIcon={ <HelpOutlineIcon /> }
buttonLabel={ __(
'Challenge the dispute',
'Challenge the dispute tooltip',
'woocommerce-payments'
) }
content={ __(
Expand All @@ -141,11 +132,11 @@ const DisputeSteps: React.FC< Props > = ( {
) }
/>
),
accepticon: (
acceptIcon: (
<ClickTooltip
buttonIcon={ <HelpOutlineIcon /> }
buttonLabel={ __(
'Accept the dispute',
'Accept the dispute tooltip',
'woocommerce-payments'
) }
content={ sprintf(
Expand All @@ -161,35 +152,10 @@ const DisputeSteps: React.FC< Props > = ( {
) }
/>
),
disputeduedate: (
<span className="dispute-steps__steps__response-date">
{ respondByDate }
<span
className={ classNames( {
'dispute-steps__steps__response-date--urgent':
daysRemaining < 3,
'dispute-steps__steps__response-date--warning':
daysRemaining < 7 &&
daysRemaining > 2,
} ) }
>
{ daysRemaining === 0
? __(
'(Last day today)',
'woocommerce-payments'
)
: sprintf(
// Translators: %s is the number of days left to respond to the dispute.
_n(
'(%s day left to respond)',
'(%s days left to respond)',
daysRemaining,
'woocommerce-payments'
),
daysRemaining
) }
</span>
</span>
dueByDate: (
<DisputeDueByDate
dueBy={ dispute.evidence_details.due_by }
/>
),
}
) }
Expand All @@ -199,4 +165,114 @@ const DisputeSteps: React.FC< Props > = ( {
);
};

export default DisputeSteps;
export const InquirySteps: React.FC< Props > = ( {
dispute,
customer,
chargeCreated,
} ) => {
let emailLink;
if ( customer?.email ) {
const chargeDate = dateI18n(
'Y-m-d',
moment( chargeCreated * 1000 ).toISOString()
);
const disputeDate = dateI18n(
'Y-m-d',
moment( dispute.created * 1000 ).toISOString()
);
const emailSubject = sprintf(
// Translators: %1$s is the store name, %2$s is the charge date.
__(
`Problem with your purchase from %1$s on %2$s?`,
'woocommerce-payments'
),
wcpaySettings.storeName,
chargeDate
);
const customerName = customer?.name || '';
const emailBody = sprintf(
// Translators: %1$s is the customer name, %2$s is the dispute date, %3$s is the dispute amount with currency-code e.g. $15 USD, %4$s is the charge date.
__(
`Hello %1$s,\n\n` +
`We noticed that on %2$s, you disputed a %3$s charge on %4$s. We wanted to contact you to make sure everything was all right with your purchase and see if there's anything else we can do to resolve any problems you might have had.\n\n` +
`Alternatively, if the dispute was a mistake, you can easily withdraw it by calling the number on the back of your card. Thank you so much - we appreciate your business and look forward to working with you.`,
'woocommerce-payments'
),
customerName,
disputeDate,
formatExplicitCurrency( dispute.amount, dispute.currency ),
chargeDate
);
emailLink = `mailto:${ customer.email }?subject=${ encodeURIComponent(
emailSubject
) }&body=${ encodeURIComponent( emailBody ) }`;
}

return (
<div className="dispute-steps">
<div className="dispute-steps__header">
{ __( 'Steps to resolve:', 'woocommerce-payments' ) }
</div>
<ol className="dispute-steps__steps">
<li>
{ customer?.email
? createInterpolateElement(
__(
'<a>Email the customer</a> to identify the issue and work towards a resolution where possible.',
'woocommerce-payments'
),
{
a: (
// eslint-disable-next-line jsx-a11y/anchor-has-content
<a
target="_blank"
rel="noopener noreferrer"
href={ emailLink }
/>
),
}
)
: __(
'Email the customer to identify the issue and work towards a resolution where possible.',
'woocommerce-payments'
) }
</li>
<li>
{ createInterpolateElement(
__(
'Submit evidence <submitEvidenceIcon/> or issue a refund by <dueByDate/>.',
'woocommerce-payments'
),
{
submitEvidenceIcon: (
<ClickTooltip
buttonIcon={ <HelpOutlineIcon /> }
buttonLabel={ __(
'Submit evidence tooltip',
'woocommerce-payments'
) }
content={ createInterpolateElement(
__(
"To submit evidence, provide documentation that supports your case. Keep in mind that submitting evidence doesn't ensure a favorable outcome. If the cardholder agrees to withdraw the inquiry, you'll still need to officially submit your evidence to prevent bank escalation. <learnMoreLink>Learn more</learnMoreLink>",
'woocommerce-payments'
),
{
learnMoreLink: (
<ExternalLink href="https://woocommerce.com/document/woopayments/fraud-and-disputes/managing-disputes/#inquiries" />
),
}
) }
/>
),
dueByDate: (
<DisputeDueByDate
dueBy={ dispute.evidence_details.due_by }
/>
),
}
) }
</li>
</ol>
</div>
);
};
Loading

0 comments on commit 1ab3115

Please sign in to comment.