From f428679fa9b98e002e8bb1f670af65595ae64845 Mon Sep 17 00:00:00 2001 From: Pritish Budhiraja <1805317@kiit.ac.in> Date: Wed, 17 Jan 2024 17:55:49 +0530 Subject: [PATCH 1/6] chore: sidebar refactoring & permissions added (#264) --- src/components/EntityScaffold.res | 4 - .../hyperswitch/HyperSwitchApp.res | 8 +- .../hyperswitch/HyperswitchAtom.res | 5 +- src/entryPoints/hyperswitch/SidebarValues.res | 11 +- .../UserManagement/PermissionUtils.res | 107 ++++++++++++++++++ .../Utils/HSwitchGlobalSearchBar.res | 9 +- src/screens/login/AuthTypes.res | 2 +- 7 files changed, 119 insertions(+), 27 deletions(-) create mode 100644 src/screens/HyperSwitch/UserManagement/PermissionUtils.res diff --git a/src/components/EntityScaffold.res b/src/components/EntityScaffold.res index bea10957d..d9d048b33 100644 --- a/src/components/EntityScaffold.res +++ b/src/components/EntityScaffold.res @@ -52,7 +52,6 @@ let make = ( | list{"new"} => switch access { | ReadWrite => renderNewForm() - | Checker => isAdminAccount ? renderNewForm() : | _ => } | list{id, "clone"} => @@ -69,14 +68,11 @@ let make = ( | list{id, "edit"} => switch access { | ReadWrite => renderEdit(id) - | Checker => isAdminAccount ? renderEdit(id) : | _ => } | list{id1, id2, "edit"} => switch access { | ReadWrite => renderEditWithMultiId(id1, id2) - | Checker => - isAdminAccount ? renderEditWithMultiId(id1, id2) : | _ => } | list{"order", id} => diff --git a/src/entryPoints/hyperswitch/HyperSwitchApp.res b/src/entryPoints/hyperswitch/HyperSwitchApp.res index 795a31657..c692c94d8 100644 --- a/src/entryPoints/hyperswitch/HyperSwitchApp.res +++ b/src/entryPoints/hyperswitch/HyperSwitchApp.res @@ -40,7 +40,6 @@ let make = () => { ->QuickStartUtils.getTypedValueFromDict let featureFlagDetails = HyperswitchAtom.featureFlagAtom->Recoil.useRecoilValueFromAtom - let getEnumDetails = EnumVariantHook.useFetchEnumDetails() let verificationDays = getFromMerchantDetails("verification")->LogicUtils.getIntFromString(-1) let userRole = getFromUserDetails("user_role") @@ -55,12 +54,7 @@ let make = () => { let isReconEnabled = (merchantDetailsValue->MerchantAccountUtils.getMerchantDetails).recon_status === Active - let hyperSwitchAppSidebars = SidebarValues.getHyperSwitchAppSidebars( - ~isReconEnabled, - ~featureFlagDetails, - ~userRole, - (), - ) + let hyperSwitchAppSidebars = SidebarValues.useGetSidebarValues(~isReconEnabled) let comingSoonPage = = Recoil.at "featureFlag", Js.Json.null->FeatureFlagUtils.featureFlagType, ) - let paypalAccountStatusAtom: Recoil.recoilAtom = Recoil.atom(. "paypalAccountStatusAtom", PayPalFlowTypes.Account_not_found, ) +let userPermissionAtom: Recoil.recoilAtom> = Recoil.atom(. + "userPermissionAtom", + [], +) diff --git a/src/entryPoints/hyperswitch/SidebarValues.res b/src/entryPoints/hyperswitch/SidebarValues.res index 3463c58bc..3717fc222 100644 --- a/src/entryPoints/hyperswitch/SidebarValues.res +++ b/src/entryPoints/hyperswitch/SidebarValues.res @@ -311,12 +311,10 @@ let reconTag = (recon, isReconEnabled) => }) : emptyComponent -let getHyperSwitchAppSidebars = ( - ~isReconEnabled: bool, - ~featureFlagDetails: FeatureFlagUtils.featureFlag, - ~userRole, - (), -) => { +let useGetSidebarValues = (~isReconEnabled: bool) => { + let userRole = HSLocalStorage.getFromUserDetails("user_role") + let featureFlagDetails = HyperswitchAtom.featureFlagAtom->Recoil.useRecoilValueFromAtom + let { productionAccess, frm, @@ -332,6 +330,7 @@ let getHyperSwitchAppSidebars = ( isLiveMode, customersModule, } = featureFlagDetails + let sidebar = [ productionAccess->productionAccessComponent, default->home, diff --git a/src/screens/HyperSwitch/UserManagement/PermissionUtils.res b/src/screens/HyperSwitch/UserManagement/PermissionUtils.res new file mode 100644 index 000000000..d75e19b33 --- /dev/null +++ b/src/screens/HyperSwitch/UserManagement/PermissionUtils.res @@ -0,0 +1,107 @@ +type permissionType = + | PaymentRead + | PaymentWrite + | RefundRead + | RefundWrite + | ApiKeyRead + | ApiKeyWrite + | MerchantAccountRead + | MerchantAccountWrite + | MerchantConnectorAccountRead + | MerchantConnectorAccountWrite + | ForexRead + | RoutingRead + | RoutingWrite + | DisputeRead + | DisputeWrite + | MandateRead + | MandateWrite + | CustomerRead + | CustomerWrite + | FileRead + | FileWrite + | Analytics + | ThreeDsDecisionManagerWrite + | ThreeDsDecisionManagerRead + | SurchargeDecisionManagerWrite + | SurchargeDecisionManagerRead + | UsersRead + | UsersWrite + | UnknownPermission(string) + +let mapPermissionTypeToString = permissionType => { + switch permissionType { + | PaymentRead => "PaymentRead" + | PaymentWrite => "PaymentWrite" + | RefundRead => "RefundRead" + | RefundWrite => "RefundWrite" + | ApiKeyRead => "ApiKeyRead" + | ApiKeyWrite => "ApiKeyWrite" + | MerchantAccountRead => "MerchantAccountRead" + | MerchantAccountWrite => "MerchantAccountWrite" + | MerchantConnectorAccountRead => "MerchantConnectorAccountRead" + | MerchantConnectorAccountWrite => "MerchantConnectorAccountWrite" + | ForexRead => "ForexRead" + | RoutingRead => "RoutingRead" + | RoutingWrite => "RoutingWrite" + | DisputeRead => "DisputeRead" + | DisputeWrite => "DisputeWrite" + | MandateRead => "MandateRead" + | MandateWrite => "MandateWrite" + | CustomerRead => "CustomerRead" + | CustomerWrite => "CustomerWrite" + | FileRead => "FileRead" + | FileWrite => "FileWrite" + | Analytics => "Analytics" + | ThreeDsDecisionManagerWrite => "ThreeDsDecisionManagerWrite" + | ThreeDsDecisionManagerRead => "ThreeDsDecisionManagerRead" + | SurchargeDecisionManagerWrite => "SurchargeDecisionManagerWrite" + | SurchargeDecisionManagerRead => "SurchargeDecisionManagerRead" + | UsersRead => "UsersRead" + | UsersWrite => "UsersWrite" + | UnknownPermission(val) => val + } +} + +let mapStringToPermissionType = val => { + switch val { + | "PaymentRead" => PaymentRead + | "PaymentWrite" => PaymentWrite + | "RefundRead" => RefundRead + | "RefundWrite" => RefundWrite + | "ApiKeyRead" => ApiKeyRead + | "ApiKeyWrite" => ApiKeyWrite + | "MerchantAccountRead" => MerchantAccountRead + | "MerchantAccountWrite" => MerchantAccountWrite + | "MerchantConnectorAccountRead" => MerchantConnectorAccountRead + | "MerchantConnectorAccountWrite" => MerchantConnectorAccountWrite + | "ForexRead" => ForexRead + | "RoutingRead" => RoutingRead + | "RoutingWrite" => RoutingWrite + | "DisputeRead" => DisputeRead + | "DisputeWrite" => DisputeWrite + | "MandateRead" => MandateRead + | "MandateWrite" => MandateWrite + | "CustomerRead" => CustomerRead + | "CustomerWrite" => CustomerWrite + | "FileRead" => FileRead + | "FileWrite" => FileWrite + | "Analytics" => Analytics + | "ThreeDsDecisionManagerWrite" => ThreeDsDecisionManagerWrite + | "ThreeDsDecisionManagerRead" => ThreeDsDecisionManagerRead + | "SurchargeDecisionManagerWrite" => SurchargeDecisionManagerWrite + | "SurchargeDecisionManagerRead" => SurchargeDecisionManagerRead + | "UsersRead" => UsersRead + | "UsersWrite" => UsersWrite + | val => UnknownPermission(val) + } +} + +let getAccessValue = (~permissionValue: permissionType, permissionList) => { + open AuthTypes + let isPermissionFound = permissionList->Array.find(ele => { + ele === permissionValue + }) + + isPermissionFound->Option.isSome ? Read : NoAccess +} diff --git a/src/screens/HyperSwitch/Utils/HSwitchGlobalSearchBar.res b/src/screens/HyperSwitch/Utils/HSwitchGlobalSearchBar.res index 9e4829184..d6e8f24ab 100644 --- a/src/screens/HyperSwitch/Utils/HSwitchGlobalSearchBar.res +++ b/src/screens/HyperSwitch/Utils/HSwitchGlobalSearchBar.res @@ -57,18 +57,11 @@ let make = () => { let (showModal, setShowModal) = React.useState(_ => false) let (searchText, setSearchText) = React.useState(_ => "") let (arr, setArr) = React.useState(_ => []) - let featureFlagDetails = HyperswitchAtom.featureFlagAtom->Recoil.useRecoilValueFromAtom let merchentDetails = HSwitchUtils.useMerchantDetailsValue() - let userRole = HSLocalStorage.getFromUserDetails("user_role") let isReconEnabled = (merchentDetails->MerchantAccountUtils.getMerchantDetails).recon_status === Active - let hswitchTabs = SidebarValues.getHyperSwitchAppSidebars( - ~isReconEnabled, - ~featureFlagDetails, - ~userRole, - (), - ) + let hswitchTabs = SidebarValues.useGetSidebarValues(~isReconEnabled) let searchText = searchText->String.trim React.useEffect1(_ => { let matchedList = hswitchTabs->Array.reduce([], (acc, item) => { diff --git a/src/screens/login/AuthTypes.res b/src/screens/login/AuthTypes.res index cf51ce9fc..a28e0757d 100644 --- a/src/screens/login/AuthTypes.res +++ b/src/screens/login/AuthTypes.res @@ -1 +1 @@ -type authorization = NoAccess | Read | ReadWrite | Checker +type authorization = NoAccess | Read | ReadWrite From 59d0b5387476536ed3a7f6a9f3468a3bd115232f Mon Sep 17 00:00:00 2001 From: Pritish Budhiraja <1805317@kiit.ac.in> Date: Wed, 17 Jan 2024 18:49:23 +0530 Subject: [PATCH 2/6] chore: Access Control Module added (#266) --- .../hyperswitch/HyperSwitchApp.res | 54 +++++++------------ src/utils/AccessControl.res | 11 ++++ 2 files changed, 31 insertions(+), 34 deletions(-) create mode 100644 src/utils/AccessControl.res diff --git a/src/entryPoints/hyperswitch/HyperSwitchApp.res b/src/entryPoints/hyperswitch/HyperSwitchApp.res index c692c94d8..210af2a7c 100644 --- a/src/entryPoints/hyperswitch/HyperSwitchApp.res +++ b/src/entryPoints/hyperswitch/HyperSwitchApp.res @@ -4,19 +4,6 @@ open HSLocalStorage open HSwitchGlobalVars open APIUtils -module FeatureFlagEnabledComponent = { - @react.component - let make = (~isEnabled, ~children) => { - let {setDashboardPageState} = React.useContext(GlobalProvider.defaultContext) - let updateRoute = () => { - setDashboardPageState(_ => #HOME) - RescriptReactRouter.replace("/home") - React.null - } - <> {isEnabled ? children : updateRoute()} - } -} - @react.component let make = () => { let url = RescriptReactRouter.useUrl() @@ -219,11 +206,11 @@ let make = () => { {switch url.path { | list{"home"} => - + {featureFlagDetails.quickStart ? : } - + | list{"fraud-risk-management", ...remainingPath} => - + { renderNewForm={() => } renderShow={_ => } /> - + | list{"connectors", ...remainingPath} => { renderShow={id => } /> | list{"customers", ...remainingPath} => - + { renderList={() => } renderShow={id => } /> - + | list{"routing", ...remainingPath} => { | list{"analytics-user-journey"} => - + - + | list{"monitoring"} => comingSoonPage | list{"developer-api-keys"} => | list{"developer-system-metrics"} => @@ -336,31 +322,31 @@ let make = () => { } /> | list{"recon"} => - + - + | list{"sdk"} => - + - + | list{"3ds"} => | list{"surcharge"} => - + - + | list{"account-settings"} => - + - + | list{"account-settings", "profile"} => | list{"business-details"} => - + - + | list{"business-profiles"} => - + - + | list{"quick-start"} => determineQuickStartPageState() | list{"woocommerce"} => determineWooCommerce() | list{"stripe-plus-paypal"} => determineStripePlusPayPal() diff --git a/src/utils/AccessControl.res b/src/utils/AccessControl.res new file mode 100644 index 000000000..93d9c0951 --- /dev/null +++ b/src/utils/AccessControl.res @@ -0,0 +1,11 @@ +@react.component +let make = (~isEnabled, ~children) => { + let {setDashboardPageState} = React.useContext(GlobalProvider.defaultContext) + + let updateRoute = () => { + setDashboardPageState(_ => #HOME) + RescriptReactRouter.replace("/home") + React.null + } + isEnabled ? children : updateRoute() +} From 1b75528192254918fe839cfc8d129ad118c26c69 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 17 Jan 2024 14:33:54 +0000 Subject: [PATCH 3/6] chore(version): v1.25.1 --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9549ffeb6..5dc051fa9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [conven - - - +## 1.25.1 (2024-01-17) + +### Miscellaneous Tasks + +- Sidebar refactoring & permissions added ([#264](https://github.com/juspay/hyperswitch-control-center/pull/264)) ([`f428679`](https://github.com/juspay/hyperswitch-control-center/commit/f428679fa9b98e002e8bb1f670af65595ae64845)) +- Access Control Module added ([#266](https://github.com/juspay/hyperswitch-control-center/pull/266)) ([`59d0b53`](https://github.com/juspay/hyperswitch-control-center/commit/59d0b5387476536ed3a7f6a9f3468a3bd115232f)) + +**Full Changelog:** [`v1.25.0...v1.25.1`](https://github.com/juspay/hyperswitch-control-center/compare/v1.25.0...v1.25.1) + +- - - + + ## 1.25.0 (2024-01-16) ### Features From 3749e3278602c2ad514079db3d6d78226cfa9391 Mon Sep 17 00:00:00 2001 From: Pritish Budhiraja <1805317@kiit.ac.in> Date: Thu, 18 Jan 2024 13:09:21 +0530 Subject: [PATCH 4/6] feat: add new user (#237) Co-authored-by: Riddhi Agrawal Co-authored-by: Jeeva Ramachandran --- .../SwitchMerchant/SwitchMerchant.res | 13 +-- .../UserManagement/InviteUsers.res | 82 ++++++++++++++----- src/utils/LoaderModal.res | 16 ++++ src/utils/PromiseUtils.res | 21 +++++ 4 files changed, 103 insertions(+), 29 deletions(-) create mode 100644 src/utils/LoaderModal.res create mode 100644 src/utils/PromiseUtils.res diff --git a/src/screens/HyperSwitch/SwitchMerchant/SwitchMerchant.res b/src/screens/HyperSwitch/SwitchMerchant/SwitchMerchant.res index 0c24b62f4..f8c8f3ca0 100644 --- a/src/screens/HyperSwitch/SwitchMerchant/SwitchMerchant.res +++ b/src/screens/HyperSwitch/SwitchMerchant/SwitchMerchant.res @@ -318,16 +318,9 @@ let make = (~userRole, ~isAddMerchantEnabled=false) => { } else { <> - - {
-

{"Switching merchant..."->React.string}

-
} -
+ } } diff --git a/src/screens/HyperSwitch/UserManagement/InviteUsers.res b/src/screens/HyperSwitch/UserManagement/InviteUsers.res index 9823d77ac..f8e9b5375 100644 --- a/src/screens/HyperSwitch/UserManagement/InviteUsers.res +++ b/src/screens/HyperSwitch/UserManagement/InviteUsers.res @@ -5,6 +5,7 @@ module InviteEmailForm = { open LogicUtils open APIUtils let fetchDetails = useGetMethod() + let {magicLink} = HyperswitchAtom.featureFlagAtom->Recoil.useRecoilValueFromAtom let (roleListData, setRoleListData) = React.useState(_ => []) let emailList = @@ -53,7 +54,7 @@ module InviteEmailForm = {
{emailList->React.string}
- +
{ let fetchDetails = useGetMethod() let updateDetails = useUpdateMethod() let showToast = ToastState.useShowToast() + let {magicLink} = HyperswitchAtom.featureFlagAtom->Recoil.useRecoilValueFromAtom let {permissionInfo, setPermissionInfo} = React.useContext(GlobalProvider.defaultContext) let (screenState, setScreenState) = React.useState(_ => PageLoaderWrapper.Loading) let (roleTypeValue, setRoleTypeValue) = React.useState(_ => "merchant_view_only") let (roleDict, setRoleDict) = React.useState(_ => Dict.make()) + let (loaderForInviteUsers, setLoaderForInviteUsers) = React.useState(_ => false) let initialValues = React.useMemo0(() => { [("roleType", ["merchant_view_only"->Js.Json.string]->Js.Json.array)] @@ -85,36 +88,70 @@ let make = () => { ->Js.Json.object_ }) - let inviteUserReq = async (body, index) => { - try { - let url = getURL(~entityName=USERS, ~userType=#INVITE, ~methodType=Post, ()) - let _ = await updateDetails(url, body, Post) - if index === 0 { - showToast(~message=`Invite(s) sent successfully via Email`, ~toastType=ToastSuccess, ()) - } - } catch { - | _ => () - } + let inviteUserReq = body => { + let url = getURL(~entityName=USERS, ~userType=#INVITE, ~methodType=Post, ()) + let response = updateDetails(url, body, Post) + response } let inviteListOfUsers = async values => { + if !magicLink { + setLoaderForInviteUsers(_ => true) + } let valDict = values->getDictFromJsonObject let role = valDict->getStrArray("roleType")->LogicUtils.getValueFromArray(0, "") + let emailList = valDict->getStrArray("emailList") + let emailPasswordsArray = [] - valDict - ->getStrArray("emailList") - ->Array.forEachWithIndex((ele, index) => { + let arrayOfPromises = emailList->Array.map(ele => { let body = [ ("email", ele->String.toLowerCase->Js.Json.string), ("name", ele->getNameFromEmail->Js.Json.string), ("role_id", role->Js.Json.string), - ] - ->Dict.fromArray - ->Js.Json.object_ - let _ = inviteUserReq(body, index) + ]->LogicUtils.getJsonFromArrayOfJson + inviteUserReq(body) }) - await HyperSwitchUtils.delay(400) + + let response = await PromiseUtils.allSettledPolyfill(arrayOfPromises) + if !magicLink { + response->Array.forEachWithIndex((ele, index) => { + switch Js.Json.classify(ele) { + | Js.Json.JSONObject(jsonDict) => { + let passwordFromResponse = jsonDict->getString("password", "") + emailPasswordsArray->Array.push( + [ + ("email", emailList[index]->Option.getWithDefault("")->Js.Json.string), + ("password", passwordFromResponse->Js.Json.string), + ]->LogicUtils.getJsonFromArrayOfJson, + ) + } + | _ => () + } + }) + } + + showToast( + ~message=magicLink + ? `Invite(s) sent successfully via Email` + : `The user accounts have been successfully created. The file with their credentials has been downloaded.`, + ~toastType=ToastSuccess, + (), + ) + + if !magicLink && emailPasswordsArray->Array.length > 0 { + DownloadUtils.download( + ~fileName=`invited-users.txt`, + ~content=emailPasswordsArray->Js.Json.array->Js.Json.stringifyWithSpace(3), + ~fileType="application/json", + ) + } + + if !magicLink { + setLoaderForInviteUsers(_ => false) + } else { + await HyperSwitchUtils.delay(400) + } RescriptReactRouter.push("/users") Js.Nullable.null } @@ -222,5 +259,12 @@ let make = () => { + + + } diff --git a/src/utils/LoaderModal.res b/src/utils/LoaderModal.res new file mode 100644 index 000000000..48799a00d --- /dev/null +++ b/src/utils/LoaderModal.res @@ -0,0 +1,16 @@ +@react.component +let make = (~showModal, ~setShowModal, ~text) => { + + +
+ +
{text->React.string}
+
+
+
+} diff --git a/src/utils/PromiseUtils.res b/src/utils/PromiseUtils.res new file mode 100644 index 000000000..3309ebc7b --- /dev/null +++ b/src/utils/PromiseUtils.res @@ -0,0 +1,21 @@ +// Pollyfill for Promise.allSettled() +// Promise.allSettled() takes an iterable of promises and returns a single promise that is fulfilled with an array of promise settlement result + +let allSettledPolyfill = (arr: array>) => { + arr + ->Array.map(promise => + promise + ->Promise.then(val => { + Promise.resolve(val) + }) + ->Promise.catch(err => { + switch err { + | Js.Exn.Error(e) => + let err = Js.Exn.message(e)->Belt.Option.getWithDefault("Failed to Fetch!") + err->Js.Json.string + | _ => Js.Json.null + }->Promise.resolve + }) + ) + ->Promise.all +} From 49e3eaeb9bd14a3c788649b81cf561386a5ce006 Mon Sep 17 00:00:00 2001 From: Pritish Budhiraja <1805317@kiit.ac.in> Date: Thu, 18 Jan 2024 14:57:47 +0530 Subject: [PATCH 5/6] chore: Added Access Type & Remove the Read & ReadWrite Type (#267) --- src/components/EntityScaffold.res | 75 +++---------------- src/components/UnauthorizedPage.res | 19 +++++ src/components/form/BoolInput.res | 11 +-- src/context/FormAuthContext.res | 2 +- .../hyperswitch/HyperSwitchApp.res | 12 +-- src/entryPoints/hyperswitch/SidebarValues.res | 44 +++++------ .../UserManagement/PermissionUtils.res | 2 +- src/screens/login/AuthTypes.res | 2 +- 8 files changed, 63 insertions(+), 104 deletions(-) create mode 100644 src/components/UnauthorizedPage.res diff --git a/src/components/EntityScaffold.res b/src/components/EntityScaffold.res index d9d048b33..68afbe809 100644 --- a/src/components/EntityScaffold.res +++ b/src/components/EntityScaffold.res @@ -1,85 +1,32 @@ -module ComingSoon = { - @react.component - let make = (~title) => { -
-
{React.string(title)}
-
{React.string("Coming soon...")}
-
- } -} - -module ShowPage = { - @react.component - let make = (~entityName, ~id) => { -
-
- {React.string(`Show ${entityName} `)} - {React.string(`#${id}`)} -
-
{React.string("Coming soon...")}
-
- } -} - -module UnauthorizedPage = { - @react.component - let make = (~entityName) => { -
-
{React.string(entityName)}
-
{React.string("You don't have access to this module. Contact admin for access")}
-
- } -} - @react.component let make = ( ~entityName="", ~remainingPath, ~isAdminAccount=false, - ~access: AuthTypes.authorization=ReadWrite, - ~renderList=() => , - ~renderNewForm=() => , + ~access: AuthTypes.authorization=Access, + ~renderList, + ~renderNewForm=?, ~renderShow=?, - ~renderOrder=?, - ~renderEdit=_id => , - ~renderEditWithMultiId=(_id1, _id2) => , - ~renderClone=_id => , ) => { if access === NoAccess { - + } else { switch remainingPath { | list{"new"} => switch access { - | ReadWrite => renderNewForm() - | _ => - } - | list{id, "clone"} => - switch access { - | ReadWrite => renderClone(id) - | _ => + | Access => + switch renderNewForm { + | Some(element) => element() + | None => React.null + } + | NoAccess => } | list{id} => let page = switch renderShow { | Some(fn) => fn(id) - | None => + | None => React.null } page - | list{id, "edit"} => - switch access { - | ReadWrite => renderEdit(id) - | _ => - } - | list{id1, id2, "edit"} => - switch access { - | ReadWrite => renderEditWithMultiId(id1, id2) - | _ => - } - | list{"order", id} => - switch renderOrder { - | Some(fn) => fn(id) - | None => - } | list{} => renderList() | _ => } diff --git a/src/components/UnauthorizedPage.res b/src/components/UnauthorizedPage.res new file mode 100644 index 000000000..55bddadc9 --- /dev/null +++ b/src/components/UnauthorizedPage.res @@ -0,0 +1,19 @@ +@react.component +let make = (~message="You don't have access to this module. Contact admin for access") => { + let {setDashboardPageState} = React.useContext(GlobalProvider.defaultContext) + React.useEffect0(() => { + RescriptReactRouter.replace("/unauthorized") + None + }) + +