Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions src/screens/Transaction/Disputes/DisputeTypes.res
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type disputes = {
connector_created_at: string,
connector_updated_at: string,
created_at: string,
is_already_refunded: bool,
}

type disputesColsType =
Expand Down
10 changes: 8 additions & 2 deletions src/screens/Transaction/Disputes/Disputes.res
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ let make = () => {
let response = await fetchDetails(disputesUrl)
let disputesValue = response->getArrayDataFromJson(DisputesEntity.itemToObjMapper)
if disputesValue->Array.length > 0 {
setDisputesData(_ => disputesValue->Array.map(Nullable.make))
setDisputesData(_ => disputesValue)
setScreenState(_ => Success)
} else {
setScreenState(_ => Custom)
Expand Down Expand Up @@ -129,12 +129,18 @@ let make = () => {
<TransactionView entity=TransactionViewTypes.Disputes />
</div>
<div className="flex-1"> {filtersUI} </div>
<RenderIf
condition={disputesData->Array.some(dispute => {
dispute.is_already_refunded
})}>
<DisputesHelper.DualRefundsAlert subText="Click on Dispute ID to learn more" />
</RenderIf>
<PageLoaderWrapper screenState customUI>
<div className="flex flex-col gap-4">
<LoadedTableWithCustomColumns
title="Disputes"
hideTitle=true
actualData=disputesData
actualData={disputesData->Array.map(Nullable.make)}
entity={DisputesEntity.disputesEntity(merchantId, orgId)}
resultsPerPage=10
showSerialNumber=true
Expand Down
6 changes: 6 additions & 0 deletions src/screens/Transaction/Disputes/DisputesEntity.res
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ let getCell = (disputesData, colType, merchantId, orgId): Table.cell => {
url={`/disputes/${disputesData.dispute_id}/${disputesData.profile_id}/${merchantId}/${orgId}`}
displayValue={disputesData.dispute_id}
copyValue={Some(disputesData.dispute_id)}
leftIcon={disputesData.is_already_refunded
? CustomIcon(
<Icon name="nd-alert-triangle-outline" size={16} className="text-nd_red-600" />,
)
: NoIcon}
/>,
"",
)
Expand Down Expand Up @@ -155,6 +160,7 @@ let itemToObjMapper = dict => {
connector_created_at: dict->getString("connector_created_at", ""),
connector_updated_at: dict->getString("connector_updated_at", ""),
created_at: dict->getString("created_at", ""),
is_already_refunded: dict->getBool("is_already_refunded", false),
}
}

Expand Down
38 changes: 38 additions & 0 deletions src/screens/Transaction/Disputes/DisputesHelper.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
open Typography

module DualRefundsAlert = {
@react.component
let make = (~subText, ~customLearnMoreComponent=React.null) => {
<div
className="my-4 flex flex-col gap-2 rounded-lg border border-nd_yellow-500 bg-nd_yellow-50 p-4">
<div className="flex flex-row justify-between items-center">
<div className="flex flex-col gap-2">
<div className="flex flex-row items-center gap-2">
<Icon name="nd-alert-triangle-outline" size={16} className="text-nd_gray-800" />
<p className={`${body.md.semibold} text-nd_gray-800`}>
{"Dual Refunds Detected"->React.string}
</p>
</div>
<p className={`${body.md.regular} text-nd_gray-600 pl-6`}> {subText->React.string} </p>
</div>
{customLearnMoreComponent}
</div>
</div>
}
}

module LearnMoreComponent = {
@react.component
let make = (~disputesData: DisputeTypes.disputes, ~merchantId, ~orgId) => {
<RenderIf condition={merchantId->Option.isSome && orgId->Option.isSome}>
<Link
to_={GlobalVars.appendDashboardPath(
~url=`/payments/${disputesData.payment_id}/${disputesData.profile_id}/${merchantId->Option.getOr(
"",
)}/${orgId->Option.getOr("")}`,
)}>
<p className={`${body.md.semibold} text-nd_yellow-700`}> {"Learn More"->React.string} </p>
</Link>
</RenderIf>
}
}
12 changes: 10 additions & 2 deletions src/screens/Transaction/Disputes/ShowDisputes.res
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ module Details = {
}
module DisputesInfo = {
@react.component
let make = (~orderDict, ~setDisputeData) => {
let make = (~orderDict, ~setDisputeData, ~merchantId, ~orgId) => {
let disputesData = DisputesEntity.itemToObjMapper(orderDict)
let connectorName = disputesData.connector->ConnectorUtils.getConnectorNameTypeFromString

Expand All @@ -161,6 +161,14 @@ module DisputesInfo = {
<div className={`font-bold text-fs-16 dark:text-white dark:text-opacity-75 mt-4 mb-4`}>
{"Summary"->React.string}
</div>
<RenderIf condition={disputesData.is_already_refunded}>
<DisputesHelper.DualRefundsAlert
customLearnMoreComponent={<DisputesHelper.LearnMoreComponent
disputesData merchantId orgId
/>}
subText="The chargeback has exceeded the dispute amount. Go to the Payments tab to learn more."
/>
</RenderIf>
<Details data=disputesData getHeading getCell detailsFields=allColumns setDisputeData />
<RenderIf condition={!showNoteComponentCondition}>
<DisputesNoteComponent disputesData />
Expand Down Expand Up @@ -223,7 +231,7 @@ let make = (~id, ~profileId, ~merchantId, ~orgId) => {
<div />
</div>
</div>
<DisputesInfo orderDict={data} setDisputeData />
<DisputesInfo orderDict={data} setDisputeData merchantId orgId />
<div className="mt-5" />
<RenderIf
condition={featureFlagDetails.auditTrail &&
Expand Down
7 changes: 6 additions & 1 deletion src/screens/Transaction/Order/HSwitchOrderUtils.res
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ let paymentAttemptStatusVariantMapper: string => paymentAttemptStatus = statusLa

let refundStatusVariantMapper: string => refundStatus = statusLabel => {
switch statusLabel->String.toUpperCase {
| "SUCCESS" => Success
| "SUCCESS" | "SUCCEEDED" => Success
| "PENDING" => Pending
| "FAILURE" => Failure
| _ => None
Expand Down Expand Up @@ -259,6 +259,7 @@ module CopyLinkTableCell = {
~customOnCopyClick=() => (),
~customTextCss="w-36",
~endValue=20,
~leftIcon: Button.iconType=NoIcon,
) => {
let (isTextVisible, setIsTextVisible) = React.useState(_ => false)
let showToast = ToastState.useShowToast()
Expand All @@ -281,6 +282,10 @@ module CopyLinkTableCell = {
<div className="flex items-center">
{if displayValue->isNonEmptyString {
<div className=customParentClass>
{switch leftIcon {
| CustomIcon(element) => element
| _ => React.null
}}
<RenderIf condition={isTextVisible || displayValue->String.length <= endValue}>
<div> {displayValue->React.string} </div>
</RenderIf>
Expand Down
6 changes: 3 additions & 3 deletions src/screens/Transaction/Order/OrderRefundForm.res
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ let make = (
~setShowModal,
~requestedRefundAmount,
~amountRefunded,
~amoutAvailableToRefund,
~amountAvailableToRefund,
~refetch,
) => {
let getURL = useGetURL()
Expand Down Expand Up @@ -120,10 +120,10 @@ let make = (
switch amountValue->Option.flatMap(obj => obj->JSON.Decode.float) {
| Some(floatVal) =>
let enteredAmountInMinorUnits = Math.round(floatVal *. conversionFactor)
let remainingAmountInMinorUnits = Math.round(amoutAvailableToRefund *. conversionFactor)
let remainingAmountInMinorUnits = Math.round(amountAvailableToRefund *. conversionFactor)
if enteredAmountInMinorUnits > remainingAmountInMinorUnits {
let formatted_amount = Float.toFixedWithPrecision(
amoutAvailableToRefund,
amountAvailableToRefund,
~digits=precisionDigits,
)
Dict.set(
Expand Down
27 changes: 19 additions & 8 deletions src/screens/Transaction/Order/ShowOrder.res
Original file line number Diff line number Diff line change
Expand Up @@ -414,27 +414,38 @@ module Disputes = {
module OrderActions = {
@react.component
let make = (~orderData, ~refetch, ~showModal, ~setShowModal) => {
let (amoutAvailableToRefund, setAmoutAvailableToRefund) = React.useState(_ => 0.0)
let (amountAvailableToRefund, setAmountAvailableToRefund) = React.useState(_ => 0.0)
let refundData = orderData.refunds
let disputeData = orderData.disputes

let conversionFactor = CurrencyUtils.getCurrencyConversionFactor(orderData.currency)

let amountRefunded = ref(0.0)
let requestedRefundAmount = ref(0.0)
let disputeAmount = ref(0.0)

let _ = refundData->Array.map(ele => {
if ele.status === "pending" {
let refundStatus = ele.status->HSwitchOrderUtils.refundStatusVariantMapper
if refundStatus === Pending {
requestedRefundAmount := requestedRefundAmount.contents +. ele.amount
} else if ele.status === "succeeded" {
} else if refundStatus === Success {
amountRefunded := amountRefunded.contents +. ele.amount
}
})

let _ = disputeData->Array.map(ele => {
let disputeStatus = ele.dispute_status->DisputesUtils.disputeStatusVariantMapper
if disputeStatus === DisputeLost {
disputeAmount := disputeAmount.contents +. ele.amount->Float.fromString->Option.getOr(0.0)
}
})

React.useEffect(_ => {
setAmoutAvailableToRefund(_ =>
let amountToBeRefunded =
orderData.amount_captured /. conversionFactor -.
amountRefunded.contents /. conversionFactor -.
disputeAmount.contents /. conversionFactor -.
requestedRefundAmount.contents /. conversionFactor
)

setAmountAvailableToRefund(_ => amountToBeRefunded > 0.0 ? amountToBeRefunded : 0.0)
None
}, [orderData])

Expand All @@ -451,7 +462,7 @@ module OrderActions = {
setShowModal
requestedRefundAmount
amountRefunded
amoutAvailableToRefund
amountAvailableToRefund
refetch
/>
</Modal>
Expand Down
3 changes: 3 additions & 0 deletions tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -475,10 +475,13 @@ module.exports = {
600: "#9E5400",
},
nd_yellow: {
50: "#FEFCE8",
100: "#FFFBEE",
200: "#5E4200",
300: "#FFDF20",
500: "#EFB100",
600: "#D08700",
700: "#A65F00",
800: "#998335",
},
nd_purple: {
Expand Down
Loading