diff --git a/config/config.toml b/config/config.toml index f19518d3c..5a599d6a7 100644 --- a/config/config.toml +++ b/config/config.toml @@ -11,6 +11,7 @@ agreement_url="" agreement_version="" apple_pay_certificate_url="" mixpanel_token="" +recon_iframe_url="" [default.features] test_live_toggle=false is_live_mode=false @@ -38,4 +39,5 @@ configure_pmts=false branding=false totp=false live_users_counter=false -granularity=false \ No newline at end of file +granularity=false +recon_v2=false \ No newline at end of file diff --git a/src/entryPoints/FeatureFlagUtils.res b/src/entryPoints/FeatureFlagUtils.res index abab138f6..48d18a8b0 100644 --- a/src/entryPoints/FeatureFlagUtils.res +++ b/src/entryPoints/FeatureFlagUtils.res @@ -28,6 +28,7 @@ type featureFlag = { totp: bool, liveUsersCounter: bool, granularity: bool, + reconV2: bool, } let featureFlagType = (featureFlags: JSON.t) => { @@ -63,6 +64,7 @@ let featureFlagType = (featureFlags: JSON.t) => { totp: dict->getBool("totp", false), liveUsersCounter: dict->getBool("live_users_counter", false), granularity: dict->getBool("granularity", false), + reconV2: dict->getBool("recon_v2", false), } typedFeatureFlag } diff --git a/src/entryPoints/HyperSwitchApp.res b/src/entryPoints/HyperSwitchApp.res index 8f7fcd08c..c5590e4b7 100644 --- a/src/entryPoints/HyperSwitchApp.res +++ b/src/entryPoints/HyperSwitchApp.res @@ -462,6 +462,16 @@ let make = () => { + | list{"upload-files"} + | list{"run-recon"} + | list{"recon-analytics"} + | list{"reports"} + | list{"config-settings"} + | list{"file-processor"} => + + urlPath} /> + + | list{"sdk"} => diff --git a/src/entryPoints/HyperSwitchEntry.res b/src/entryPoints/HyperSwitchEntry.res index d7029730b..4ca4d430f 100644 --- a/src/entryPoints/HyperSwitchEntry.res +++ b/src/entryPoints/HyperSwitchEntry.res @@ -40,6 +40,7 @@ module HyperSwitchEntryComponent = { ->getString("apple_pay_certificate_url", "") ->getNonEmptyString, agreementVersion: dict->getString("agreement_version", "")->getNonEmptyString, + reconIframeUrl: dict->getString("recon_iframe_url", "")->getNonEmptyString, } DOMUtils.window._env_ = value configureFavIcon(value.faviconUrl)->ignore diff --git a/src/entryPoints/SidebarValues.res b/src/entryPoints/SidebarValues.res index caa00a049..f46052fbb 100644 --- a/src/entryPoints/SidebarValues.res +++ b/src/entryPoints/SidebarValues.res @@ -466,7 +466,59 @@ let developers = (isDevelopersEnabled, userRole, systemMetrics, ~permissionJson) : emptyComponent } -let reconTag = (recon, isReconEnabled) => +let uploadReconFiles = { + SubLevelLink({ + name: "Upload Recon Files", + link: `/upload-files`, + access: Access, + searchOptions: [("Upload recon files", "")], + }) +} + +let runRecon = { + SubLevelLink({ + name: "Run Recon", + link: `/run-recon`, + access: Access, + searchOptions: [("Run recon", "")], + }) +} + +let reconAnalytics = { + SubLevelLink({ + name: "Analytics", + link: `/recon-analytics`, + access: Access, + searchOptions: [("Recon analytics", "")], + }) +} +let reconReports = { + SubLevelLink({ + name: "Reports", + link: `reports`, + access: Access, + searchOptions: [("Recon reports", "")], + }) +} + +let reconConfigurator = { + SubLevelLink({ + name: "Configurator", + link: `config-settings`, + access: Access, + searchOptions: [("Recon configurator", "")], + }) +} +let reconFileProcessor = { + SubLevelLink({ + name: "File Processor", + link: `file-processor`, + access: Access, + searchOptions: [("Recon file processor", "")], + }) +} + +let reconTag = (recon, isReconEnabled) => { recon ? Link({ name: "Reconcilation", @@ -475,6 +527,25 @@ let reconTag = (recon, isReconEnabled) => access: Access, }) : emptyComponent +} + +let reconAndSettlement = (recon_v2, isReconEnabled) => { + recon_v2 && isReconEnabled + ? Section({ + name: "Recon And Settlement", + icon: "recon", + showSection: true, + links: [ + uploadReconFiles, + runRecon, + reconAnalytics, + reconReports, + reconConfigurator, + reconFileProcessor, + ], + }) + : emptyComponent +} let useGetSidebarValues = (~isReconEnabled: bool) => { let {user_role: userRole} = @@ -497,6 +568,7 @@ let useGetSidebarValues = (~isReconEnabled: bool) => { quickStart, disputeAnalytics, configurePmts, + reconV2, } = featureFlagDetails let sidebar = [ @@ -518,6 +590,7 @@ let useGetSidebarValues = (~isReconEnabled: bool) => { ), default->workflow(isSurchargeEnabled, ~permissionJson, ~isPayoutEnabled=payOut), recon->reconTag(isReconEnabled), + reconV2->reconAndSettlement(isReconEnabled), default->developers(userRole, systemMetrics, ~permissionJson), settings( ~isSampleDataEnabled=sampleData, diff --git a/src/entryPoints/configs/HyperSwitchConfigTypes.res b/src/entryPoints/configs/HyperSwitchConfigTypes.res index abd6a8e48..3df8a99bf 100644 --- a/src/entryPoints/configs/HyperSwitchConfigTypes.res +++ b/src/entryPoints/configs/HyperSwitchConfigTypes.res @@ -7,6 +7,7 @@ type urlConfig = { agreementUrl: option, agreementVersion: option, applePayCertificateUrl: option, + reconIframeUrl: option, } type customStyle = { diff --git a/src/libraries/Window.res b/src/libraries/Window.res index ffa2f4cfe..6e5ed83d6 100644 --- a/src/libraries/Window.res +++ b/src/libraries/Window.res @@ -2,6 +2,8 @@ type t type listener<'ev> = 'ev => unit +type event = {data: string} + @val @scope("window") external parent: 't = "parent" diff --git a/src/screens/Recon/ReconModule.res b/src/screens/Recon/ReconModule.res new file mode 100644 index 000000000..0a5497e2a --- /dev/null +++ b/src/screens/Recon/ReconModule.res @@ -0,0 +1,95 @@ +@react.component +let make = (~urlList) => { + open APIUtils + open LogicUtils + + let getURL = useGetURL() + let fetchDetails = useGetMethod() + let (redirectToken, setRedirectToken) = React.useState(_ => "") + let (screenState, setScreenState) = React.useState(_ => PageLoaderWrapper.Loading) + let (iframeLoaded, setIframeLoaded) = React.useState(_ => false) + let iframeRef = React.useRef(Js.Nullable.null) + + let getReconToken = async () => { + try { + let url = getURL(~entityName=RECON, ~reconType=#TOKEN, ~methodType=Get, ()) + let res = await fetchDetails(url) + let token = res->LogicUtils.getDictFromJsonObject->LogicUtils.getString("token", "") + setRedirectToken(_ => token) + setScreenState(_ => PageLoaderWrapper.Success) + } catch { + | _ => setScreenState(_ => PageLoaderWrapper.Error("Something went wrong!")) + } + } + + let redirectUrl = switch urlList { + | list{"upload-files"} + | list{"run-recon"} + | list{"reports"} + | list{"config-settings"} + | list{"file-processor"} => + urlList->List.toArray->Array.joinWithUnsafe("/") + | list{"recon-analytics"} => "analytics" + | _ => "" + } + + React.useEffect2(() => { + getReconToken()->ignore + None + }, (iframeLoaded, redirectUrl)) + + <> + { + switch iframeRef.current->Js.Nullable.toOption { + | Some(iframeEl) => { + let tokenDict = [("token", redirectToken->JSON.Encode.string)]->Dict.fromArray + let dict = + [ + ("eventType", "AuthenticationDetails"->JSON.Encode.string), + ("payload", tokenDict->JSON.Encode.object), + ]->Dict.fromArray + iframeEl->IframeUtils.iframePostMessage(dict) + } + + | None => () + } + + {if redirectToken->isNonEmptyString { +
+