diff --git a/src/screens/Transaction/Disputes/DisputeTypes.res b/src/screens/Transaction/Disputes/DisputeTypes.res index b7a8d62b2f..7d5af0847c 100644 --- a/src/screens/Transaction/Disputes/DisputeTypes.res +++ b/src/screens/Transaction/Disputes/DisputeTypes.res @@ -15,6 +15,7 @@ type disputes = { connector_created_at: string, connector_updated_at: string, created_at: string, + is_already_refunded: bool, } type disputesColsType = diff --git a/src/screens/Transaction/Disputes/Disputes.res b/src/screens/Transaction/Disputes/Disputes.res index b32c91ea69..ba68ddc3c8 100644 --- a/src/screens/Transaction/Disputes/Disputes.res +++ b/src/screens/Transaction/Disputes/Disputes.res @@ -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) @@ -129,12 +129,18 @@ let make = () => {
{filtersUI}
+ Array.some(dispute => { + dispute.is_already_refunded + })}> + +
Array.map(Nullable.make)} entity={DisputesEntity.disputesEntity(merchantId, orgId)} resultsPerPage=10 showSerialNumber=true diff --git a/src/screens/Transaction/Disputes/DisputesEntity.res b/src/screens/Transaction/Disputes/DisputesEntity.res index 4c2c766db2..917f4cbb9e 100644 --- a/src/screens/Transaction/Disputes/DisputesEntity.res +++ b/src/screens/Transaction/Disputes/DisputesEntity.res @@ -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( + , + ) + : NoIcon} />, "", ) @@ -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), } } diff --git a/src/screens/Transaction/Disputes/DisputesHelper.res b/src/screens/Transaction/Disputes/DisputesHelper.res new file mode 100644 index 0000000000..c89bc2b877 --- /dev/null +++ b/src/screens/Transaction/Disputes/DisputesHelper.res @@ -0,0 +1,38 @@ +open Typography + +module DualRefundsAlert = { + @react.component + let make = (~subText, ~customLearnMoreComponent=React.null) => { +
+
+
+
+ +

+ {"Dual Refunds Detected"->React.string} +

+
+

{subText->React.string}

+
+ {customLearnMoreComponent} +
+
+ } +} + +module LearnMoreComponent = { + @react.component + let make = (~disputesData: DisputeTypes.disputes, ~merchantId, ~orgId) => { + Option.isSome && orgId->Option.isSome}> + Option.getOr( + "", + )}/${orgId->Option.getOr("")}`, + )}> +

{"Learn More"->React.string}

+ +
+ } +} diff --git a/src/screens/Transaction/Disputes/ShowDisputes.res b/src/screens/Transaction/Disputes/ShowDisputes.res index 07a9ceac97..2e2a0dfd73 100644 --- a/src/screens/Transaction/Disputes/ShowDisputes.res +++ b/src/screens/Transaction/Disputes/ShowDisputes.res @@ -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 @@ -161,6 +161,14 @@ module DisputesInfo = {
{"Summary"->React.string}
+ + } + subText="The chargeback has exceeded the dispute amount. Go to the Payments tab to learn more." + /> +
@@ -223,7 +231,7 @@ let make = (~id, ~profileId, ~merchantId, ~orgId) => {
- +
paymentAttemptStatus = statusLa let refundStatusVariantMapper: string => refundStatus = statusLabel => { switch statusLabel->String.toUpperCase { - | "SUCCESS" => Success + | "SUCCESS" | "SUCCEEDED" => Success | "PENDING" => Pending | "FAILURE" => Failure | _ => None @@ -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() @@ -281,6 +282,10 @@ module CopyLinkTableCell = {
{if displayValue->isNonEmptyString {
+ {switch leftIcon { + | CustomIcon(element) => element + | _ => React.null + }} String.length <= endValue}>
{displayValue->React.string}
diff --git a/src/screens/Transaction/Order/OrderRefundForm.res b/src/screens/Transaction/Order/OrderRefundForm.res index 94d9c11757..540695aae0 100644 --- a/src/screens/Transaction/Order/OrderRefundForm.res +++ b/src/screens/Transaction/Order/OrderRefundForm.res @@ -10,7 +10,7 @@ let make = ( ~setShowModal, ~requestedRefundAmount, ~amountRefunded, - ~amoutAvailableToRefund, + ~amountAvailableToRefund, ~refetch, ) => { let getURL = useGetURL() @@ -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( diff --git a/src/screens/Transaction/Order/ShowOrder.res b/src/screens/Transaction/Order/ShowOrder.res index 8124e4124e..a7d2620765 100644 --- a/src/screens/Transaction/Order/ShowOrder.res +++ b/src/screens/Transaction/Order/ShowOrder.res @@ -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]) @@ -451,7 +462,7 @@ module OrderActions = { setShowModal requestedRefundAmount amountRefunded - amoutAvailableToRefund + amountAvailableToRefund refetch /> diff --git a/tailwind.config.js b/tailwind.config.js index c37dce937a..8e03f9bfd7 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -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: {