Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Transaction Details dispute footer for disputes not awaiting a response #7047

Merged
merged 82 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
a69c6d7
Add dispute notice to transactions page
brucealdridge Aug 15, 2023
ec813fb
Add isAwaitingResponse dispute helper
brucealdridge Aug 15, 2023
5090921
Merge branch 'develop' into add/6923-dispute-details-notice
shendy-a8c Aug 15, 2023
3990811
Fix padding in dispute details on transaction details page.
shendy-a8c Aug 15, 2023
625274f
Merge branch 'add/6923-dispute-details-notice' of github.com:Automatt…
shendy-a8c Aug 15, 2023
065f0f4
Fix moment library import
Jinksi Aug 16, 2023
1e805b6
Merge branch 'develop' into add/6923-dispute-details-notice
shendy-a8c Aug 16, 2023
1dd9101
Merge remote-tracking branch 'origin/develop' into add/6923-dispute-d…
brucealdridge Aug 17, 2023
294f025
Adding inquiry text
brucealdridge Aug 18, 2023
e00704a
Merge branch 'develop' into add/6923-dispute-details-notice
shendy-a8c Aug 20, 2023
45bd7fb
Add dispute IssuerEvidence type
Jinksi Aug 23, 2023
d6a74bc
Add metadata key/values to Dispute TS interface
Jinksi Aug 23, 2023
3697992
Add reporting_category to BalanceTransaction TS type
Jinksi Aug 23, 2023
bf60ee3
Init DisputeFooter element
Jinksi Aug 23, 2023
ee4d692
Add buttonLabel and onClick handler
Jinksi Aug 23, 2023
a737326
Rename variable to `message`
Jinksi Aug 23, 2023
9f910c1
Fix switch statement
Jinksi Aug 23, 2023
b4da52c
Fix under review submission date
Jinksi Aug 23, 2023
18b78ce
Add custom background colours and styles
Jinksi Aug 23, 2023
c4f0787
Only render DisputeFooter for supported statuses
Jinksi Aug 23, 2023
e1c1930
Alter text for inquires
brucealdridge Aug 24, 2023
fe0cee4
removing unneeded import
brucealdridge Aug 24, 2023
dee17ad
Adding link details to translators note
brucealdridge Aug 24, 2023
8fa0cf6
Merge branch 'add/6923-dispute-details-notice' into add/6927-dispute-…
Jinksi Aug 24, 2023
0f246e3
Simplify logic for determining non/actionable dispute details
Jinksi Aug 24, 2023
0586881
Add tracks event for footer button click
Jinksi Aug 24, 2023
da4e5ec
Merge branch 'develop' into add/6923-dispute-details-notice
Jinksi Aug 25, 2023
fd9fc97
Merge branch 'develop' into add/6923-dispute-details-notice
Jinksi Aug 25, 2023
33a090f
Merge branch 'add/6923-dispute-details-notice' into add/6927-dispute-…
Jinksi Aug 25, 2023
ef9d697
Re-order imports to improve code readability
Jinksi Aug 25, 2023
958a2fd
Change `ExternalLink` → `<a />` to match design
Jinksi Aug 25, 2023
cb738d4
Add message for `Inquiry: Under Review`
Jinksi Aug 25, 2023
069f54c
Create date formatting util func to improve code readability
Jinksi Aug 25, 2023
a6c7984
Merge branch 'develop' into add/6927-dispute-details-not-awaiting-res…
Jinksi Aug 28, 2023
850b3b8
Fix redundant imports after merge
Jinksi Aug 28, 2023
8b925f5
Merge branch 'develop' into add/6927-dispute-details-not-awaiting-res…
Jinksi Aug 29, 2023
2a5e672
Add changelog entry
Jinksi Aug 29, 2023
7298ab7
Merge branch 'develop' into add/6927-dispute-details-not-awaiting-res…
Jinksi Aug 30, 2023
faab3ca
Merge branch 'develop' into add/6927-dispute-details-not-awaiting-res…
Jinksi Sep 6, 2023
229e804
Merge branch 'develop' into add/6927-dispute-details-not-awaiting-res…
Jinksi Sep 7, 2023
cb29af6
Merge branch 'develop' into add/6927-dispute-details-not-awaiting-res…
Jinksi Sep 7, 2023
beeb5c2
Merge branch 'develop' into add/6927-dispute-details-not-awaiting-res…
Jinksi Sep 11, 2023
7168b03
Fix import and TS issues after merge conflict resolution
Jinksi Sep 11, 2023
fbdfb97
Merge branch 'develop' into add/6927-dispute-details-not-awaiting-res…
Jinksi Sep 12, 2023
f9048d3
Merge branch 'develop' into add/6927-dispute-details-not-awaiting-res…
Jinksi Sep 13, 2023
4539ee8
Remove issuer_evidence additions, no longer in PR scope
Jinksi Sep 13, 2023
2db2172
Use shared util function `getDisputeFee`
Jinksi Sep 13, 2023
d72f66d
Remove redundant <div>
Jinksi Sep 13, 2023
936c17c
Leave inquiries out of PR scope
Jinksi Sep 13, 2023
dd50429
Add tests for non-awaiting-response dispute messages
Jinksi Sep 13, 2023
bb492db
Hoist up conditional for rendering DIsputeFooter to improve code read…
Jinksi Sep 13, 2023
2389724
Don't render details button if no challenge submitted
Jinksi Sep 13, 2023
38c50a7
Remove redundant test property definition
Jinksi Sep 13, 2023
a129715
Tidy up file formatting
Jinksi Sep 13, 2023
2307832
Fix tracks event name
Jinksi Sep 13, 2023
f754c66
Add tests for expected button visibility
Jinksi Sep 13, 2023
62f8c55
Test for correct fee in footer message
Jinksi Sep 13, 2023
3c00b06
Use '-' fallback for undefined string variables
Jinksi Sep 13, 2023
00254a1
Tests: use different timestamps for submitted/closed dates
Jinksi Sep 13, 2023
01e5c03
Merge branch 'develop' into add/6927-dispute-details-not-awaiting-res…
Jinksi Sep 13, 2023
e81a61d
Remove redundant CSS (from legacy BannerNotice component)
Jinksi Sep 13, 2023
56a1d63
Simplify code to improve readability
Jinksi Sep 13, 2023
9b4dea5
Add responsive styles for small viewports
Jinksi Sep 13, 2023
05f086a
Hoist logic for DisputeFooter to parent component
Jinksi Sep 14, 2023
dcae9a7
Rename component to DisputeResolvedMessage
Jinksi Sep 14, 2023
d20f023
Refactor DisputeResolvedMessage logic to PaymentDetailsSummary
Jinksi Sep 14, 2023
2682ee0
Move DIsputeDetails tests to PaymentDetailsSummary test suite
Jinksi Sep 14, 2023
1be791e
Revert changes to unrelated component DisputeDetails
Jinksi Sep 14, 2023
7523a8d
Rename tracks event to give more context
Jinksi Sep 14, 2023
03b464c
Fix typo in staged dispute challenge notice
Jinksi Sep 14, 2023
d532d23
Refactor each dispute footer type to own component
Jinksi Sep 15, 2023
ba5829d
Rename DisputeDetails comp to clarify intended use
Jinksi Sep 15, 2023
0a6eb66
Merge branch 'develop' into add/6927-dispute-details-not-awaiting-res…
Jinksi Sep 15, 2023
814fdcd
Remove redundant component `DisputeResolvedMessage`
Jinksi Sep 15, 2023
6f78871
Merge branch 'develop' into add/6927-dispute-details-not-awaiting-res…
Jinksi Sep 15, 2023
b167054
Revert "Fix typo in staged dispute challenge notice"
Jinksi Sep 15, 2023
fa71276
Use `Link` element to allow native browser navigation
Jinksi Sep 18, 2023
e6e38ea
Ensure tracks event is recorded when CMD+clicking link
Jinksi Sep 18, 2023
d414429
Merge branch 'develop' into add/6927-dispute-details-not-awaiting-res…
Jinksi Sep 18, 2023
4712f3e
Merge branch 'develop' into add/6927-dispute-details-not-awaiting-res…
Jinksi Sep 19, 2023
582655a
Add getDisputeFeeFormatted util function
Jinksi Sep 19, 2023
4910837
Explicitly import DisputeResolutionFooter styles
Jinksi Sep 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelog/add-6927-dispute-details-not-awaiting-response
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: add

Behind a feature flag: dispute message added to transactions screen for disputes not needing a response.
29 changes: 29 additions & 0 deletions client/disputes/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ import type {
DisputeStatus,
EvidenceDetails,
} from 'wcpay/types/disputes';
import type { BalanceTransaction } from 'wcpay/types/balance-transactions';
import {
disputeAwaitingResponseStatuses,
disputeUnderReviewStatuses,
} from 'wcpay/disputes/filters/config';
import { formatExplicitCurrency } from 'wcpay/utils/currency';

interface IsDueWithinProps {
dueBy: CachedDispute[ 'due_by' ] | EvidenceDetails[ 'due_by' ];
Expand Down Expand Up @@ -69,3 +71,30 @@ export const isInquiry = ( dispute: Dispute | CachedDispute ): boolean => {
// Inquiry dispute statuses are one of `warning_needs_response`, `warning_under_review` or `warning_closed`.
return dispute.status.startsWith( 'warning' );
};

/**
* Returns the dispute fee balance transaction for a dispute if it exists.
*/
export const getDisputeFee = (
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: this util function is also used in the WIP PRs #7118 and #7093

dispute: Dispute
): BalanceTransaction | undefined => {
const disputeFee = dispute.balance_transactions.find(
( transaction ) => transaction.reporting_category === 'dispute'
);
return disputeFee;
};

/**
* Returns the dispute fee balance transaction for a dispute if it exists
* formatted as a currency string.
*/
export const getDisputeFeeFormatted = (
dispute: Dispute
): string | undefined => {
const disputeFee = getDisputeFee( dispute );

return (
disputeFee &&
formatExplicitCurrency( disputeFee.fee, disputeFee.currency )
);
};
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This DisputeDetails component has been renamed to DisputeAwaitingResponseDetails to clarify it's intended use and content.

Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ import DisputeSummaryRow from './dispute-summary-row';
import InlineNotice from 'components/inline-notice';
import './style.scss';

interface DisputeDetailsProps {
interface Props {
dispute: Dispute;
}

const DisputeDetails: React.FC< DisputeDetailsProps > = ( { dispute } ) => {
const DisputeAwaitingResponseDetails: React.FC< Props > = ( { dispute } ) => {
const now = moment();
const dueBy = moment.unix( dispute.evidence_details?.due_by ?? 0 );
const countdownDays = Math.floor( dueBy.diff( now, 'days', true ) );
Expand Down Expand Up @@ -67,4 +67,4 @@ const DisputeDetails: React.FC< DisputeDetailsProps > = ( { dispute } ) => {
);
};

export default DisputeDetails;
export default DisputeAwaitingResponseDetails;
292 changes: 292 additions & 0 deletions client/payment-details/dispute-details/dispute-resolution-footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
/**
* External dependencies
*/
import React from 'react';
import moment from 'moment';
import { dateI18n } from '@wordpress/date';
import { __, sprintf } from '@wordpress/i18n';
import { Link } from '@woocommerce/components';
import { createInterpolateElement } from '@wordpress/element';
import { Button, CardFooter, Flex, FlexItem } from '@wordpress/components';

/**
* Internal dependencies
*/
import type { Dispute } from 'wcpay/types/disputes';
import wcpayTracks from 'tracks';
import { getAdminUrl } from 'wcpay/utils';
import { getDisputeFeeFormatted, isInquiry } from 'wcpay/disputes/utils';
import './style.scss';

const DisputeUnderReviewFooter: React.FC< {
dispute: Dispute;
} > = ( { dispute } ) => {
const submissionDateFormatted = dispute.metadata.__evidence_submitted_at
? dateI18n(
'M j, Y',
moment
.unix(
parseInt( dispute.metadata.__evidence_submitted_at, 10 )
)
.toISOString()
)
: '-';

return (
<CardFooter className="transaction-details-dispute-footer transaction-details-dispute-footer--primary">
<Flex justify="space-between">
<FlexItem>
{ createInterpolateElement(
sprintf(
isInquiry( dispute )
? /* Translators: %s - formatted date, <a> - link to documentation page */
__(
'You submitted evidence for this inquiry on %s. The cardholder’s bank is reviewing the case, which can take 120 days or more. You will be alerted when they make their final decision. <a>Learn more</a>.',
'woocommerce-payments'
)
: /* Translators: %s - formatted date, <a> - link to documentation page */
__(
'You submitted evidence for this dispute on %s. The cardholder’s bank is reviewing the case, which can take 60 days or more. You will be alerted when they make their final decision. <a>Learn more about the dispute process</a>.',
'woocommerce-payments'
),
submissionDateFormatted
),
{
a: (
// eslint-disable-next-line jsx-a11y/anchor-has-content -- Link content is provided by createInterpolateElement
<a
target="_blank"
rel="noopener noreferrer"
href="https://woocommerce.com/document/woopayments/fraud-and-disputes/"
/>
),
}
) }
</FlexItem>
<FlexItem className="transaction-details-dispute-footer__actions">
<Link
href={ getAdminUrl( {
page: 'wc-admin',
path: '/payments/disputes/challenge',
id: dispute?.id,
} ) }
>
<Button
variant="secondary"
onClick={ () => {
wcpayTracks.recordEvent(
wcpayTracks.events
.PAYMENT_DETAILS_VIEW_DISPUTE_EVIDENCE_BUTTON_CLICK,
{
dispute_status: dispute.status,
}
);
} }
>
{ __(
'View submitted evidence',
'woocommerce-payments'
) }
</Button>
</Link>
</FlexItem>
</Flex>
</CardFooter>
);
};

const DisputeWonFooter: React.FC< {
dispute: Dispute;
} > = ( { dispute } ) => {
const closedDateFormatted = dispute?.metadata.__dispute_closed_at
? dateI18n(
'M j, Y',
moment
.unix(
parseInt( dispute.metadata.__dispute_closed_at, 10 )
)
.toISOString()
)
: '-';

return (
<CardFooter className="transaction-details-dispute-footer">
<Flex justify="space-between">
<FlexItem>
{ createInterpolateElement(
sprintf(
/* Translators: %s - formatted date, <a> - link to documentation page */
__(
'Good news! You won this dispute on %s. The disputed amount and the dispute fee have been credited back to your account. <a>Learn more about preventing disputes</a>.',
'woocommerce-payments'
),
closedDateFormatted
),
{
a: (
// eslint-disable-next-line jsx-a11y/anchor-has-content -- Link content is provided by createInterpolateElement
<a
target="_blank"
rel="noopener noreferrer"
href="https://woocommerce.com/document/woopayments/fraud-and-disputes/"
/>
),
}
) }
</FlexItem>
<FlexItem className="transaction-details-dispute-footer__actions">
<Link
href={ getAdminUrl( {
page: 'wc-admin',
path: '/payments/disputes/challenge',
id: dispute?.id,
} ) }
>
<Button
variant="secondary"
onClick={ () => {
wcpayTracks.recordEvent(
wcpayTracks.events
.PAYMENT_DETAILS_VIEW_DISPUTE_EVIDENCE_BUTTON_CLICK,
{
dispute_status: dispute.status,
}
);
} }
>
{ __(
'View dispute details',
'woocommerce-payments'
) }
</Button>
</Link>
</FlexItem>
</Flex>
</CardFooter>
);
};

const DisputeLostFooter: React.FC< {
dispute: Dispute;
} > = ( { dispute } ) => {
const isSubmitted = !! dispute?.metadata.__evidence_submitted_at;
const isAccepted = dispute?.metadata.__closed_by_merchant === '1';
const disputeFeeFormatted = getDisputeFeeFormatted( dispute ) ?? '-';

const closedDateFormatted = dispute?.metadata.__dispute_closed_at
? dateI18n(
'M j, Y',
moment
.unix(
parseInt( dispute.metadata.__dispute_closed_at, 10 )
)
.toISOString()
)
: '-';

let messagePrefix = sprintf(
/* Translators: %1$s - formatted date */
__(
'This dispute was lost on %1$s due to non-response.',
'woocommerce-payments'
),
closedDateFormatted
);

if ( isAccepted ) {
messagePrefix = sprintf(
/* Translators: %1$s - formatted date */
__(
'This dispute was accepted and lost on %1$s.',
'woocommerce-payments'
),
closedDateFormatted
);
}

if ( isSubmitted ) {
messagePrefix = sprintf(
/* Translators: %1$s - formatted date */
__( 'This dispute was lost on %1$s.', 'woocommerce-payments' ),
closedDateFormatted
);
}

return (
<CardFooter className="transaction-details-dispute-footer">
<Flex justify="space-between">
<FlexItem>
{ messagePrefix }{ ' ' }
{ createInterpolateElement(
sprintf(
/* Translators: %1$s – the formatted dispute fee amount, <a> - link to documentation page */
__(
'The %1$s fee has been deducted from your account, and the disputed amount returned to the cardholder. <a>Learn more about preventing disputes</a>.',
'woocommerce-payments'
),
disputeFeeFormatted
),
{
a: (
// eslint-disable-next-line jsx-a11y/anchor-has-content -- Link content is provided by createInterpolateElement
<a
target="_blank"
rel="noopener noreferrer"
href="https://woocommerce.com/document/woopayments/fraud-and-disputes/"
/>
),
}
) }
</FlexItem>

{ isSubmitted && (
<FlexItem className="transaction-details-dispute-footer__actions">
<Link
href={ getAdminUrl( {
page: 'wc-admin',
path: '/payments/disputes/challenge',
id: dispute?.id,
} ) }
>
<Button
variant="secondary"
onClick={ () => {
wcpayTracks.recordEvent(
wcpayTracks.events
.PAYMENT_DETAILS_VIEW_DISPUTE_EVIDENCE_BUTTON_CLICK,
{
dispute_status: dispute.status,
}
);
} }
>
{ __(
'View dispute details',
'woocommerce-payments'
) }
</Button>
</Link>
</FlexItem>
) }
</Flex>
</CardFooter>
);
};

const DisputeResolutionFooter: React.FC< {
dispute: Dispute;
} > = ( { dispute } ) => {
if ( dispute.status === 'under_review' ) {
return <DisputeUnderReviewFooter dispute={ dispute } />;
}
if ( dispute.status === 'won' ) {
return <DisputeWonFooter dispute={ dispute } />;
}
if ( dispute.status === 'lost' ) {
return <DisputeLostFooter dispute={ dispute } />;
}

return null;
Jinksi marked this conversation as resolved.
Show resolved Hide resolved
};

export default DisputeResolutionFooter;
23 changes: 23 additions & 0 deletions client/payment-details/dispute-details/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,29 @@
}
}

.transaction-details-dispute-footer {
background-color: #f2f4f5;

&__actions {
flex-shrink: 0;
}

&--primary {
background-color: $wp-blue-0;
}

@media screen and ( max-width: $break-small ) {
.components-flex {
flex-direction: column;
align-items: flex-start;
}

.components-flex-item {
margin: 10px 0;
}
}
}

.dispute-evidence {
// Override WordPress core PanelBody boxy styles. Ours is more inline content.
&.components-panel__body {
Expand Down
Loading