From 5900070eff25b78ad9c3cdf2d46b14c77b627285 Mon Sep 17 00:00:00 2001 From: Gitanjli <96485413+gitanjli525@users.noreply.github.com> Date: Thu, 23 May 2024 12:24:52 +0530 Subject: [PATCH] chore: filter & list api for payment & refunds (#620) --- src/components/Filter.res | 112 ++------------------- src/components/FilterSelectBox.res | 2 +- src/screens/HSwitchRemoteFilter.res | 61 ++++++----- src/screens/Order/OrderUIUtils.res | 150 ++++++++++++++++++++++++++-- src/screens/Order/Orders.res | 7 +- src/screens/Refunds/Refund.res | 6 +- src/screens/Refunds/RefundUtils.res | 113 +++++++++++++++++++-- 7 files changed, 295 insertions(+), 156 deletions(-) diff --git a/src/components/Filter.res b/src/components/Filter.res index 5532cc0c5..456c89d71 100644 --- a/src/components/Filter.res +++ b/src/components/Filter.res @@ -55,15 +55,14 @@ module ClearFilters = { ->Option.getOr(Dict.make()) ->Dict.toArray ->Array.filter(entry => { - let (key, value) = entry + let (_, value) = entry let isEmptyValue = switch value->JSON.Classify.classify { | String(str) => str->LogicUtils.isEmptyString | Array(arr) => arr->Array.length === 0 | Null => true | _ => false } - - !(defaultFilterKeys->Array.includes(key)) && !isEmptyValue + !isEmptyValue }) ->Array.length > 0 }, (formState.initialValues, defaultFilterKeys)) @@ -119,97 +118,6 @@ let getStrFromJson = (key, val) => { } } -module ApplyFilterButton = { - @react.component - let make = ( - ~autoApply, - ~totalFilters, - ~filterButtonStyle, - ~defaultFilterKeys, - ~allFilters: array, - ) => { - let defaultinputField = FormRenderer.makeInputFieldInfo(~name="-", ()) - let inputFieldsDict = - allFilters - ->Array.map(filter => { - let inputFieldsArr = filter.inputFields - let inputField = inputFieldsArr->LogicUtils.getValueFromArray(0, defaultinputField) - (inputField.name, inputField) - }) - ->Dict.fromArray - - let formState: ReactFinalForm.formState = ReactFinalForm.useFormState( - ReactFinalForm.useFormSubscription(["values", "dirtyFields", "initialValues"])->Nullable.make, - ) - - let formCurrentValues = - formState.values - ->LogicUtils.getDictFromJsonObject - ->DictionaryUtils.deleteKeys(defaultFilterKeys) - let formInitalValues = - formState.initialValues - ->LogicUtils.getDictFromJsonObject - ->DictionaryUtils.deleteKeys(defaultFilterKeys) - let dirtyFields = formState.dirtyFields->Dict.keysToArray - - let getFormattedDict = dict => { - dict - ->Dict.toArray - ->Array.map(entry => { - let (key, value) = entry - let inputField = inputFieldsDict->Dict.get(key)->Option.getOr(defaultinputField) - let formattor = inputField.format - let value = switch formattor { - | Some(fn) => fn(. ~value, ~name=key) - | None => value - } - (key, value) - }) - ->Dict.fromArray - } - - let showApplyFilter = { - let formattedInitialValues = formInitalValues->getFormattedDict - let formattedCurrentValues = formCurrentValues->getFormattedDict - - let equalDictCheck = DictionaryUtils.checkEqualJsonDicts( - formattedInitialValues, - formattedCurrentValues, - ~checkKeys=dirtyFields, - ~ignoreKeys=["opt"], - ) - - let otherCheck = - formattedCurrentValues - ->Dict.toArray - ->Array.reduce(true, (acc, item) => { - let (_, value) = item - switch value->JSON.Classify.classify { - | String(str) => str->LogicUtils.isEmptyString - | Array(arr) => arr->Array.length === 0 - | Object(dict) => dict->Dict.toArray->Array.length === 0 - | Null => true - | _ => false - } && - acc - }) - !equalDictCheck && !otherCheck - } - - // if all values are empty then don't show the apply filters let it be the clear filters visible - - if autoApply || totalFilters === 0 { - React.null - } else if showApplyFilter { -
- -
- } else { - React.null - } - } -} - @react.component let make = ( ~defaultFilters, @@ -251,22 +159,21 @@ let make = ( let verticalGap = !isMobileView ? "gap-y-3" : "" React.useEffect1(_ => { - if remoteFilters->Array.length >= allFilters->Array.length { - setAllFilters(_ => remoteFilters->Array.map(item => item.field)) - } + let updatedAllFilters = remoteFilters->Array.map(item => item.field) + setAllFilters(_ => updatedAllFilters) None }, remoteFilters) let localFilterJson = RemoteFiltersUtils.getInitialValuesFromUrl( ~searchParams, - ~initialFilters=localFilters, + ~initialFilters={Array.concat(localFilters, fixedFilters)}, (), ) let clearFilterJson = RemoteFiltersUtils.getInitialValuesFromUrl( ~searchParams, - ~initialFilters=localFilters, + ~initialFilters={Array.concat(localFilters, fixedFilters)}, ~options=remoteOptions, (), ) @@ -437,12 +344,7 @@ let make = ( } `${activeClasses} font-medium` }> -
- {option.inputNames - ->Array.get(0) - ->Option.getOr("") - ->React.string} -
+
{option.label->React.string}
} diff --git a/src/components/FilterSelectBox.res b/src/components/FilterSelectBox.res index 5c93fdced..b47cb5b14 100644 --- a/src/components/FilterSelectBox.res +++ b/src/components/FilterSelectBox.res @@ -681,7 +681,7 @@ module BaseSelect = {
+ className={`${widthClass} ${outerClass} ${borderClass} ${animationClass} ${dropdownClassName} max-h-80`}> {switch searchable { | Some(val) => if val { diff --git a/src/screens/HSwitchRemoteFilter.res b/src/screens/HSwitchRemoteFilter.res index 718b18850..6e206e00d 100644 --- a/src/screens/HSwitchRemoteFilter.res +++ b/src/screens/HSwitchRemoteFilter.res @@ -114,10 +114,9 @@ module SearchBarFilter = { } module RemoteTableFilters = { - open LogicUtils @react.component let make = ( - ~filterUrl, + ~filterUrlV2, ~setFilters, ~endTimeFilterKey, ~startTimeFilterKey, @@ -130,6 +129,7 @@ module RemoteTableFilters = { let {filterValue, updateExistingKeys, filterValueJson, reset} = FilterContext.filterContext->React.useContext let defaultFilters = {""->JSON.Encode.string} + let showToast = ToastState.useShowToast() React.useEffect0(() => { if filterValueJson->Dict.keysToArray->Array.length === 0 { @@ -139,40 +139,27 @@ module RemoteTableFilters = { None }) - let endTimeVal = filterValueJson->getString(endTimeFilterKey, "") - let startTimeVal = filterValueJson->getString(startTimeFilterKey, "") - - let filterBody = React.useMemo3(() => { - [ - (startTimeFilterKey, startTimeVal->JSON.Encode.string), - (endTimeFilterKey, endTimeVal->JSON.Encode.string), - ]->Dict.fromArray - }, (startTimeVal, endTimeVal, filterValue)) - open APIUtils let (filterDataJson, setFilterDataJson) = React.useState(_ => None) - let updateDetails = useUpdateMethod() - let {filterValueJson} = FilterContext.filterContext->React.useContext - let startTimeVal = filterValueJson->getString("start_time", "") - let endTimeVal = filterValueJson->getString("end_time", "") - - let getFilters = async () => { - let json = await updateDetails(filterUrl, filterBody->JSON.Encode.object, Post, ()) - setFilterDataJson(_ => json->Some) - } - React.useEffect3(() => { - setFilterDataJson(_ => None) - if startTimeVal->isNonEmptyString && endTimeVal->isNonEmptyString { - try { - getFilters()->ignore - } catch { - | _ => () - } + let fetchDetails = useGetMethod() + + let fetchAllFilters = async () => { + try { + setFilterDataJson(_ => None) + let response = await fetchDetails(filterUrlV2) + setFilterDataJson(_ => response->Some) + } catch { + | _ => showToast(~message="Failed to load filters", ~toastType=ToastError, ()) } + } + + React.useEffect0(() => { + fetchAllFilters()->ignore None - }, (startTimeVal, endTimeVal, filterBody->JSON.Encode.object->JSON.stringify)) + }) + let filterData = filterDataJson->Option.getOr(Dict.make()->JSON.Encode.object) let setInitialFilters = useSetInitialFilters( @@ -201,7 +188,19 @@ module RemoteTableFilters = { None }, [filterValue]) - let remoteFilters = filterData->initialFilters + let getAllFilter = + filterValue + ->Dict.toArray + ->Array.map(item => { + let (key, value) = item + (key, value->UrlFetchUtils.getFilterValue) + }) + ->Dict.fromArray + + let remoteFilters = React.useMemo1(() => { + filterData->initialFilters(getAllFilter) + }, [getAllFilter]) + let initialDisplayFilters = remoteFilters->Array.filter((item: EntityType.initialFilters<'t>) => item.localFilter->Option.isSome diff --git a/src/screens/Order/OrderUIUtils.res b/src/screens/Order/OrderUIUtils.res index 7af207cb0..79fcfd54b 100644 --- a/src/screens/Order/OrderUIUtils.res +++ b/src/screens/Order/OrderUIUtils.res @@ -1,3 +1,37 @@ +type filterTypes = { + connector: array, + currency: array, + authentication_type: array, + payment_method: array, + payment_method_type: array, + status: array, + connector_label: array, +} + +type filter = [ + | #connector + | #payment_method + | #currency + | #authentication_type + | #status + | #payment_method_type + | #connector_label + | #unknown +] + +let getFilterTypeFromString = filterType => { + switch filterType { + | "connector" => #connector + | "payment_method" => #payment_method + | "currency" => #currency + | "status" => #status + | "authentication_type" => #authentication_type + | "payment_method_type" => #payment_method_type + | "connector_label" => #connector_label + | _ => #unknown + } +} + module RenderAccordian = { @react.component let make = (~initialExpandedArray=[], ~accordion) => { @@ -98,22 +132,122 @@ let filterByData = (txnArr, value) => { }) } -let initialFilters = json => { +let getConditionalFilter = (key, dict, filterValues) => { + open LogicUtils + + let filtersArr = switch key->getFilterTypeFromString { + | #connector_label => { + let arr = filterValues->getArrayFromDict("connector", [])->getStrArrayFromJsonArray + let newArr = arr->Array.flatMap(connector => { + let connectorLabelArr = dict->getDictfromDict("connector")->getArrayFromDict(connector, []) + connectorLabelArr->Array.map(item => { + item->getDictFromJsonObject->getString("connector_label", "") + }) + }) + newArr + } + | #payment_method_type => { + let arr = filterValues->getArrayFromDict("payment_method", [])->getStrArrayFromJsonArray + let newArr = arr->Array.flatMap(paymentMethod => { + let paymentMethodTypeArr = + dict + ->getDictfromDict("payment_method") + ->getArrayFromDict(paymentMethod, []) + ->getStrArrayFromJsonArray + paymentMethodTypeArr->Array.map(item => { + item + }) + }) + newArr + } + | _ => [] + } + + filtersArr +} + +let getOptionsForOrderFilters = (dict, filterValues) => { open LogicUtils + let arr = filterValues->getArrayFromDict("connector", [])->getStrArrayFromJsonArray + let newArr = arr->Array.flatMap(connector => { + let connectorLabelArr = dict->getDictfromDict("connector")->getArrayFromDict(connector, []) + connectorLabelArr->Array.map(item => { + let label = item->getDictFromJsonObject->getString("connector_label", "") + let value = item->getDictFromJsonObject->getString("merchant_connector_id", "") + let option: FilterSelectBox.dropdownOption = { + label, + value, + } + option + }) + }) + newArr +} + +let itemToObjMapper = dict => { + open LogicUtils + { + connector: dict->getDictfromDict("connector")->Dict.keysToArray, + currency: dict->getArrayFromDict("currency", [])->getStrArrayFromJsonArray, + authentication_type: dict + ->getArrayFromDict("authentication_type", []) + ->getStrArrayFromJsonArray, + status: dict->getArrayFromDict("status", [])->getStrArrayFromJsonArray, + payment_method: dict->getDictfromDict("payment_method")->Dict.keysToArray, + payment_method_type: [], + connector_label: [], + } +} + +let initialFilters = (json, filtervalues) => { + open LogicUtils + + let connectorFilter = filtervalues->getArrayFromDict("connector", [])->getStrArrayFromJsonArray + + let paymentMethodFilter = + filtervalues->getArrayFromDict("payment_method", [])->getStrArrayFromJsonArray + let filterDict = json->getDictFromJsonObject + let filterArr = filterDict->itemToObjMapper + let arr = filterDict->Dict.keysToArray + + if connectorFilter->Array.length !== 0 { + arr->Array.push("connector_label") + } + if paymentMethodFilter->Array.length !== 0 { + arr->Array.push("payment_method_type") + } + + arr->Array.map((key): EntityType.initialFilters<'t> => { + let values = switch key->getFilterTypeFromString { + | #connector => filterArr.connector + | #payment_method => filterArr.payment_method + | #currency => filterArr.currency + | #authentication_type => filterArr.authentication_type + | #status => filterArr.status + | #payment_method_type => getConditionalFilter(key, filterDict, filtervalues) + | #connector_label => getConditionalFilter(key, filterDict, filtervalues) + | _ => [] + } - filterDict - ->Dict.keysToArray - ->Array.map((key): EntityType.initialFilters<'t> => { let title = `Select ${key->snakeToTitle}` - let values = filterDict->getArrayFromDict(key, [])->getStrArrayFromJsonArray + + let options = switch key->getFilterTypeFromString { + | #connector_label => getOptionsForOrderFilters(filterDict, filtervalues) + | _ => values->FilterSelectBox.makeOptions + } + + let name = switch key->getFilterTypeFromString { + | #connector_label => "merchant_connector_id" + | _ => key + } { field: FormRenderer.makeFieldInfo( - ~label="", - ~name=key, + ~label=key, + ~name, ~customInput=InputFields.filterMultiSelectInput( - ~options=values->FilterSelectBox.makeOptions, + ~options, ~buttonText=title, ~showSelectionAsChips=false, ~searchable=true, diff --git a/src/screens/Order/Orders.res b/src/screens/Order/Orders.res index 2b6d697dc..7c3f97f8c 100644 --- a/src/screens/Order/Orders.res +++ b/src/screens/Order/Orders.res @@ -84,11 +84,14 @@ let make = (~previewOnly=false) => { let customTitleStyle = previewOnly ? "py-0 !pt-0" : "" let customUI = - let filterUrl = `${Window.env.apiBaseUrl}/payments/filter` + + let filterUrlV2 = React.useMemo1(() => { + `${Window.env.apiBaseUrl}/payments/v2/filter` + }, [Window.env.apiBaseUrl]) let filtersUI = React.useMemo0(() => { { let {generateReport} = HyperswitchAtom.featureFlagAtom->Recoil.useRecoilValueFromAtom + let filterUrlV2 = React.useMemo1(() => { + `${Window.env.apiBaseUrl}/payments/v2/filter` + }, [Window.env.apiBaseUrl]) +
, + currency: array, + status: array, + connector_label: array, +} + +type filter = [ + | #connector + | #currency + | #status + | #connector_label + | #unknown +] + +let getFilterTypeFromString = filterType => { + switch filterType { + | "connector" => #connector + | "currency" => #currency + | "connector_label" => #connector_label + | "status" => #status + | _ => #unknown + } +} + let getRefundsList = async ( filterValueJson, ~updateDetails: ( @@ -114,23 +139,95 @@ let initialFixedFilter = () => [ ), ] -let initialFilters = json => { +let getConditionalFilter = (key, dict, filterValues) => { + open LogicUtils + + let filtersArr = switch key->getFilterTypeFromString { + | #connector_label => { + let arr = filterValues->getArrayFromDict("connector", [])->getStrArrayFromJsonArray + let newArr = arr->Array.flatMap(connector => { + let connectorLabelArr = dict->getDictfromDict("connector")->getArrayFromDict(connector, []) + connectorLabelArr->Array.map(item => { + item->getDictFromJsonObject->getString("connector_label", "") + }) + }) + newArr + } + | _ => [] + } + + filtersArr +} + +let getOptionsForRefundFilters = (dict, filterValues) => { + open LogicUtils + let arr = filterValues->getArrayFromDict("connector", [])->getStrArrayFromJsonArray + let newArr = arr->Array.flatMap(connector => { + let connectorLabelArr = dict->getDictfromDict("connector")->getArrayFromDict(connector, []) + connectorLabelArr->Array.map(item => { + let label = item->getDictFromJsonObject->getString("connector_label", "") + let value = item->getDictFromJsonObject->getString("merchant_connector_id", "") + let option: FilterSelectBox.dropdownOption = { + label, + value, + } + option + }) + }) + newArr +} + +let itemToObjMapper = dict => { open LogicUtils + { + connector: dict->getDictfromDict("connector")->Dict.keysToArray, + currency: dict->getArrayFromDict("currency", [])->getStrArrayFromJsonArray, + status: dict->getArrayFromDict("status", [])->getStrArrayFromJsonArray, + connector_label: [], + } +} + +let initialFilters = (json, filtervalues) => { + open LogicUtils + + let connectorFilter = filtervalues->getArrayFromDict("connector", [])->getStrArrayFromJsonArray + let filterDict = json->getDictFromJsonObject + let arr = filterDict->Dict.keysToArray->Array.filterWithIndex((_item, index) => index <= 2) - filterDict - ->Dict.keysToArray - ->Array.filterWithIndex((_item, index) => index <= 2) - ->Array.map((key): EntityType.initialFilters<'t> => { + if connectorFilter->Array.length !== 0 { + arr->Array.push("connector_label") + } + + let filterArr = filterDict->itemToObjMapper + + arr->Array.map((key): EntityType.initialFilters<'t> => { let title = `Select ${key->snakeToTitle}` - let values = filterDict->getArrayFromDict(key, [])->getStrArrayFromJsonArray + + let values = switch key->getFilterTypeFromString { + | #connector => filterArr.connector + | #currency => filterArr.currency + | #status => filterArr.status + | #connector_label => getConditionalFilter(key, filterDict, filtervalues) + | _ => [] + } + + let options = switch key->getFilterTypeFromString { + | #connector_label => getOptionsForRefundFilters(filterDict, filtervalues) + | _ => values->FilterSelectBox.makeOptions + } + + let name = switch key->getFilterTypeFromString { + | #connector_label => "merchant_connector_id" + | _ => key + } { field: FormRenderer.makeFieldInfo( ~label="", - ~name=key, + ~name, ~customInput=InputFields.filterMultiSelectInput( - ~options=values->FilterSelectBox.makeOptions, + ~options, ~buttonText=title, ~showSelectionAsChips=false, ~searchable=true,