Skip to content

Commit

Permalink
feat: paymants failure reasons (#1892)
Browse files Browse the repository at this point in the history
  • Loading branch information
sagarnaikjuspay authored Dec 9, 2024
1 parent 3ba2b47 commit 807e047
Show file tree
Hide file tree
Showing 9 changed files with 381 additions and 7 deletions.
11 changes: 6 additions & 5 deletions src/entryPoints/SidebarValues.res
Original file line number Diff line number Diff line change
Expand Up @@ -265,10 +265,10 @@ let paymentAnalytcis = SubLevelLink({
})

let performanceMonitor = SubLevelLink({
name: "Performance Monitor",
name: "Performance",
link: `/performance-monitor`,
access: Access,
searchOptions: [("View Performance Monitor", "")],
searchOptions: [("View Performance", "")],
})

let newAnalytics = SubLevelLink({
Expand Down Expand Up @@ -330,14 +330,15 @@ let analytics = (
if disputeAnalyticsFlag {
links->Array.push(disputeAnalytics)
}
if performanceMonitorFlag {
links->Array.push(performanceMonitor)
}

if newAnalyticsflag {
links->Array.push(newAnalytics)
}

if performanceMonitorFlag {
links->Array.push(performanceMonitor)
}

isAnalyticsEnabled
? Section({
name: "Analytics",
Expand Down
2 changes: 2 additions & 0 deletions src/screens/NewAnalytics/NewAnalyticsTypes.res
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type dimension = [
| #payment_method_type
| #card_network
| #authentication_type
| #error_reason
]
type status = [#charged | #failure]
type metrics = [
Expand All @@ -26,6 +27,7 @@ type metrics = [
| #failure_reasons
| #payments_distribution
| #payment_success_rate
| #failure_reasons
]
type granularity = [
| #G_ONEDAY
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
open NewAnalyticsTypes
open FailureReasonsPaymentsTypes
open NewPaymentAnalyticsEntity
open FailureReasonsPaymentsUtils
open NewAnalyticsHelper

module TableModule = {
@react.component
let make = (~data, ~className="", ~selectedTab: string) => {
let (offset, setOffset) = React.useState(_ => 0)

let defaultSort: Table.sortedObject = {
key: "",
order: Table.INC,
}
let tableBorderClass = "border-2 border-solid border-jp-gray-940 border-collapse border-opacity-30 dark:border-jp-gray-dark_table_border_color dark:border-opacity-30"

let defaultCols = [Error_Reason, Failure_Reason_Count, Reasons_Count_Ratio]
let extraTabs = selectedTab->String.split(",")->Array.map(getColumn)
let visibleColumns = defaultCols->Array.concat(extraTabs)
let tableData = getTableData(data)

<div className>
<LoadedTable
visibleColumns
title=" "
hideTitle=true
actualData={tableData}
entity=failureReasonsTableEntity
resultsPerPage=10
totalResults={tableData->Array.length}
offset
setOffset
defaultSort
currrentFetchCount={tableData->Array.length}
tableLocalFilter=false
tableheadingClass=tableBorderClass
tableBorderClass
ignoreHeaderBg=true
tableDataBorderClass=tableBorderClass
isAnalyticsModule=true
/>
</div>
}
}

module FailureReasonsPaymentsHeader = {
@react.component
let make = (~groupBy, ~setGroupBy) => {
let setGroupBy = value => {
setGroupBy(_ => value)
}

<div className="w-full px-7 py-8 flex justify-between">
<Tabs option={groupBy} setOption={setGroupBy} options={tabs} />
</div>
}
}

@react.component
let make = (~entity: moduleEntity) => {
open LogicUtils
open APIUtils
let getURL = useGetURL()
let updateDetails = useUpdateMethod()
let (screenState, setScreenState) = React.useState(_ => PageLoaderWrapper.Loading)
let {filterValueJson} = React.useContext(FilterContext.filterContext)
let (tableData, setTableData) = React.useState(_ => JSON.Encode.array([]))
let (groupBy, setGroupBy) = React.useState(_ => defaulGroupBy)
let startTimeVal = filterValueJson->getString("startTime", "")
let endTimeVal = filterValueJson->getString("endTime", "")

let getPaymentsProcessed = async () => {
setScreenState(_ => PageLoaderWrapper.Loading)
try {
let url = getURL(
~entityName=ANALYTICS_PAYMENTS,
~methodType=Post,
~id=Some((entity.domain: domain :> string)),
)

let groupByNames = switch entity.requestBodyConfig.groupBy {
| Some(dimentions) =>
dimentions
->Array.map(item => (item: dimension :> string))
->Array.concat(groupBy.value->String.split(","))
->Some
| _ => None
}

let body = NewAnalyticsUtils.requestBody(
~startTime=startTimeVal,
~endTime=endTimeVal,
~delta=entity.requestBodyConfig.delta,
~metrics=entity.requestBodyConfig.metrics,
~groupByNames,
)

let response = await updateDetails(url, body, Post)

let metaData = response->getDictFromJsonObject->getArrayFromDict("metaData", [])

let data =
response
->getDictFromJsonObject
->getArrayFromDict("queryData", [])
->modifyQuery(metaData)

if data->Array.length > 0 {
setTableData(_ => data->JSON.Encode.array)
setScreenState(_ => PageLoaderWrapper.Success)
} else {
setScreenState(_ => PageLoaderWrapper.Custom)
}
} catch {
| _ => setScreenState(_ => PageLoaderWrapper.Custom)
}
}
React.useEffect(() => {
if startTimeVal->isNonEmptyString && endTimeVal->isNonEmptyString {
getPaymentsProcessed()->ignore
}
None
}, [startTimeVal, endTimeVal, groupBy.value])

<div>
<ModuleHeader title={entity.title} />
<Card>
<PageLoaderWrapper
screenState customLoader={<Shimmer layoutId=entity.title />} customUI={<NoData />}>
<FailureReasonsPaymentsHeader groupBy setGroupBy />
<div className="mb-5">
<TableModule data={tableData} className="mx-7" selectedTab={groupBy.value} />
</div>
</PageLoaderWrapper>
</Card>
</div>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
type failreResonsColsTypes =
| Error_Reason
| Failure_Reason_Count
| Reasons_Count_Ratio
| Total_Failure_Reasons_Count
| Connector
| Payment_Method
| Payment_Method_Type
| Authentication_Type

type failreResonsObjectType = {
error_reason: string,
failure_reason_count: int,
total_failure_reasons_count: int,
reasons_count_ratio: float,
connector: string,
payment_method: string,
payment_method_type: string,
authentication_type: string,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
open NewAnalyticsTypes
open FailureReasonsPaymentsTypes
open LogicUtils

let getStringFromVariant = value => {
switch value {
| Error_Reason => "error_reason"
| Failure_Reason_Count => "failure_reason_count"
| Reasons_Count_Ratio => "reasons_count_ratio"
| Total_Failure_Reasons_Count => "total_failure_reasons_count"
| Connector => "connector"
| Payment_Method => "payment_method"
| Payment_Method_Type => "payment_method_type"
| Authentication_Type => "authentication_type"
}
}

let getColumn = string => {
switch string {
| "connector" => Connector
| "payment_method" => Payment_Method
| "payment_method_type" => Payment_Method_Type
| "authentication_type" => Authentication_Type
| _ => Connector
}
}

let tableItemToObjMapper: Dict.t<JSON.t> => failreResonsObjectType = dict => {
{
error_reason: dict->getString(Error_Reason->getStringFromVariant, ""),
failure_reason_count: dict->getInt(Failure_Reason_Count->getStringFromVariant, 0),
total_failure_reasons_count: dict->getInt(Total_Failure_Reasons_Count->getStringFromVariant, 0),
reasons_count_ratio: dict->getFloat(Reasons_Count_Ratio->getStringFromVariant, 0.0),
connector: dict->getString(Connector->getStringFromVariant, ""),
payment_method: dict->getString(Payment_Method->getStringFromVariant, ""),
payment_method_type: dict->getString(Payment_Method_Type->getStringFromVariant, ""),
authentication_type: dict->getString(Authentication_Type->getStringFromVariant, ""),
}
}

let getObjects: JSON.t => array<failreResonsObjectType> = json => {
json
->LogicUtils.getArrayFromJson([])
->Array.map(item => {
tableItemToObjMapper(item->getDictFromJsonObject)
})
}

let getHeading = colType => {
switch colType {
| Error_Reason =>
Table.makeHeaderInfo(
~key=Error_Reason->getStringFromVariant,
~title="Error Reason",
~dataType=TextType,
)
| Failure_Reason_Count =>
Table.makeHeaderInfo(
~key=Failure_Reason_Count->getStringFromVariant,
~title="Count",
~dataType=TextType,
)
| Reasons_Count_Ratio =>
Table.makeHeaderInfo(
~key=Reasons_Count_Ratio->getStringFromVariant,
~title="Ratio (%)",
~dataType=TextType,
)
| Total_Failure_Reasons_Count =>
Table.makeHeaderInfo(
~key=Total_Failure_Reasons_Count->getStringFromVariant,
~title="",
~dataType=TextType,
)
| Connector =>
Table.makeHeaderInfo(
~key=Connector->getStringFromVariant,
~title="Connector",
~dataType=TextType,
)
| Payment_Method =>
Table.makeHeaderInfo(
~key=Payment_Method->getStringFromVariant,
~title="Payment Method",
~dataType=TextType,
)
| Payment_Method_Type =>
Table.makeHeaderInfo(
~key=Payment_Method_Type->getStringFromVariant,
~title="Payment Method Type",
~dataType=TextType,
)
| Authentication_Type =>
Table.makeHeaderInfo(
~key=Authentication_Type->getStringFromVariant,
~title="Authentication Type",
~dataType=TextType,
)
}
}

let getCell = (obj, colType): Table.cell => {
open NewAnalyticsUtils
switch colType {
| Error_Reason => Text(obj.error_reason)
| Failure_Reason_Count => Text(obj.failure_reason_count->Int.toString)
| Reasons_Count_Ratio => Text(obj.reasons_count_ratio->valueFormatter(Rate))
| Total_Failure_Reasons_Count => Text(obj.total_failure_reasons_count->Int.toString)
| Connector => Text(obj.connector)
| Payment_Method => Text(obj.payment_method)
| Payment_Method_Type => Text(obj.payment_method_type)
| Authentication_Type => Text(obj.authentication_type)
}
}

let getTableData = json => {
json->getArrayDataFromJson(tableItemToObjMapper)->Array.map(Nullable.make)
}

let tabs = [
{
label: "Connector",
value: Connector->getStringFromVariant,
},
{
label: "Payment Method",
value: Payment_Method->getStringFromVariant,
},
{
label: "Payment Method Type",
value: Payment_Method_Type->getStringFromVariant,
},
{
label: "Authentication Type",
value: Authentication_Type->getStringFromVariant,
},
{
label: "Payment Method + Payment Method Type",
value: `${Payment_Method->getStringFromVariant},${Payment_Method_Type->getStringFromVariant}`,
},
]

let defaulGroupBy = {
label: "Connector",
value: Connector->getStringFromVariant,
}

let modifyQuery = (queryData, metaData) => {
let totalCount = switch metaData->Array.get(0) {
| Some(val) => {
let valueDict = val->getDictFromJsonObject
let failure_reason_count =
valueDict->getInt(Total_Failure_Reasons_Count->getStringFromVariant, 0)
failure_reason_count
}
| _ => 0
}

if totalCount > 0 {
queryData->Array.map(query => {
let valueDict = query->getDictFromJsonObject
let failure_reason_count = valueDict->getInt(Failure_Reason_Count->getStringFromVariant, 0)
let ratio = failure_reason_count->Int.toFloat /. totalCount->Int.toFloat *. 100.0

valueDict->Dict.set(Reasons_Count_Ratio->getStringFromVariant, ratio->JSON.Encode.float)
valueDict->JSON.Encode.object
})
} else {
queryData
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@ let make = () => {
<FailedPaymentsDistribution
entity={failedPaymentsDistributionEntity} chartEntity={failedPaymentsDistributionChartEntity}
/>
<FailureReasonsPayments entity={failureReasonsEntity} />
</div>
}
Loading

0 comments on commit 807e047

Please sign in to comment.