From fec51dd7adf23ebab8378bdb16efa7155bb98680 Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Wed, 6 Nov 2024 18:17:08 +0500 Subject: [PATCH 1/2] count subscription users from groups list --- .../lowcoder/src/api/subscriptionApi.ts | 26 +------------------ .../src/util/context/SubscriptionContext.tsx | 3 ++- client/packages/lowcoder/src/util/hooks.ts | 14 +++++----- 3 files changed, 10 insertions(+), 33 deletions(-) diff --git a/client/packages/lowcoder/src/api/subscriptionApi.ts b/client/packages/lowcoder/src/api/subscriptionApi.ts index 38b1d7c49..bb5208d0a 100644 --- a/client/packages/lowcoder/src/api/subscriptionApi.ts +++ b/client/packages/lowcoder/src/api/subscriptionApi.ts @@ -3,7 +3,7 @@ import axios, { AxiosInstance, AxiosRequestConfig, CancelToken } from "axios"; import { useDispatch, useSelector } from "react-redux"; import { useEffect, useState} from "react"; import { calculateFlowCode } from "./apiUtils"; -import { fetchOrgUsersAction } from "redux/reduxActions/orgActions"; +import { fetchGroupsAction, fetchOrgUsersAction } from "redux/reduxActions/orgActions"; import { getOrgUsers } from "redux/selectors/orgSelectors"; import { AppState } from "@lowcoder-ee/redux/reducers"; import type { @@ -289,28 +289,4 @@ export const getCustomerPortalSession = async (customerId: string) => { } }; -// Hooks - -export const useOrgUserCount = (orgId: string) => { - const dispatch = useDispatch(); - const orgUsers = useSelector((state: AppState) => getOrgUsers(state)); // Use selector to get orgUsers from state - const [userCount, setUserCount] = useState(0); - - useEffect(() => { - // Dispatch action to fetch organization users - if (orgId) { - dispatch(fetchOrgUsersAction(orgId)); - } - }, [dispatch, orgId]); - - useEffect(() => { - // Update user count when orgUsers state changes - if (orgUsers && orgUsers.length > 0) { - setUserCount(orgUsers.length); - } - }, [orgUsers]); - - return userCount; -}; - export default SubscriptionApi; diff --git a/client/packages/lowcoder/src/util/context/SubscriptionContext.tsx b/client/packages/lowcoder/src/util/context/SubscriptionContext.tsx index d57190904..8fb008e82 100644 --- a/client/packages/lowcoder/src/util/context/SubscriptionContext.tsx +++ b/client/packages/lowcoder/src/util/context/SubscriptionContext.tsx @@ -1,10 +1,11 @@ -import { createCheckoutLink, createCustomer, getProducts, searchCustomer, useOrgUserCount } from "@lowcoder-ee/api/subscriptionApi"; +import { createCheckoutLink, createCustomer, getProducts, searchCustomer } from "@lowcoder-ee/api/subscriptionApi"; import { StripeCustomer, SubscriptionProduct, InitSubscriptionProducts, LowcoderSearchCustomer, LowcoderNewCustomer, Subscription } from "@lowcoder-ee/constants/subscriptionConstants"; import { getDeploymentId } from "@lowcoder-ee/redux/selectors/configSelectors"; import { getFetchSubscriptionsFinished, getSubscriptions, getSubscriptionsError } from "@lowcoder-ee/redux/selectors/subscriptionSelectors"; import { getCurrentUser, getUser } from "@lowcoder-ee/redux/selectors/usersSelectors"; import { createContext, ReactNode, useContext, useEffect, useState } from "react"; import { useSelector } from "react-redux"; +import { useOrgUserCount } from "../hooks"; export interface SubscriptionContextType { products: SubscriptionProduct[]; diff --git a/client/packages/lowcoder/src/util/hooks.ts b/client/packages/lowcoder/src/util/hooks.ts index 3c3870aab..a8df8a860 100644 --- a/client/packages/lowcoder/src/util/hooks.ts +++ b/client/packages/lowcoder/src/util/hooks.ts @@ -27,8 +27,8 @@ import { ThemeDetail } from "@lowcoder-ee/api/commonSettingApi"; import { uniq } from "lodash"; import { constantColors } from "components/colorSelect/colorUtils"; import { AppState } from "@lowcoder-ee/redux/reducers"; -import { getOrgUsers } from "@lowcoder-ee/redux/selectors/orgSelectors"; -import { fetchOrgUsersAction } from "@lowcoder-ee/redux/reduxActions/orgActions"; +import { getOrgGroups, getOrgUsers } from "@lowcoder-ee/redux/selectors/orgSelectors"; +import { fetchGroupsAction, fetchOrgUsersAction } from "@lowcoder-ee/redux/reduxActions/orgActions"; export const ForceViewModeContext = React.createContext(false); @@ -263,22 +263,22 @@ export function useThemeColors(allowGradient?: boolean) { export const useOrgUserCount = (orgId: string) => { const dispatch = useDispatch(); - const orgUsers = useSelector((state: AppState) => getOrgUsers(state)); // Use selector to get orgUsers from state + const orgGroups = useSelector((state: AppState) => getOrgGroups(state)); // Use selector to get orgUsers from state const [userCount, setUserCount] = useState(0); useEffect(() => { // Dispatch action to fetch organization users if (orgId) { - dispatch(fetchOrgUsersAction(orgId)); + dispatch(fetchGroupsAction(orgId)); } }, [dispatch, orgId]); useEffect(() => { // Update user count when orgUsers state changes - if (orgUsers && orgUsers.length > 0) { - setUserCount(orgUsers.length); + if (orgGroups && orgGroups.length > 0) { + setUserCount(orgGroups.length); } - }, [orgUsers]); + }, [orgGroups]); return userCount; }; From 4ccb1d1226ada2d5c876f0a02a44c51b5bf1460c Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Fri, 8 Nov 2024 15:07:52 +0500 Subject: [PATCH 2/2] save org user stats and use totalAdminsAndDevelopers count for subscription --- client/packages/lowcoder/src/api/apiResponses.ts | 7 +++++++ .../src/redux/reducers/uiReducers/orgReducer.ts | 5 ++++- .../packages/lowcoder/src/redux/sagas/orgSagas.ts | 10 ++++++++-- .../lowcoder/src/redux/selectors/orgSelectors.ts | 4 ++++ client/packages/lowcoder/src/util/hooks.ts | 14 +++++++------- 5 files changed, 30 insertions(+), 10 deletions(-) diff --git a/client/packages/lowcoder/src/api/apiResponses.ts b/client/packages/lowcoder/src/api/apiResponses.ts index c985bcb9d..5999bfcca 100644 --- a/client/packages/lowcoder/src/api/apiResponses.ts +++ b/client/packages/lowcoder/src/api/apiResponses.ts @@ -12,6 +12,13 @@ export interface GenericApiResponse { data: T; } +export interface FetchGroupApiResponse extends GenericApiResponse { + totalAdmins: number, + totalAdminsAndDevelopers: number, + totalDevelopersOnly: number, + totalOtherMembers: number, +} + // NO_DATASOURCES_FOUND, 1000, "Unable to find {0} with id {1}" // INVALID_PARAMTER, 4000, "Invalid parameter {0} provided in the input" // PLUGIN_NOT_INSTALLED, 4001, "Plugin {0} not installed" diff --git a/client/packages/lowcoder/src/redux/reducers/uiReducers/orgReducer.ts b/client/packages/lowcoder/src/redux/reducers/uiReducers/orgReducer.ts index 47ad06220..db2a7918d 100644 --- a/client/packages/lowcoder/src/redux/reducers/uiReducers/orgReducer.ts +++ b/client/packages/lowcoder/src/redux/reducers/uiReducers/orgReducer.ts @@ -28,15 +28,17 @@ const initialState: OrgReduxState = { orgCreateStatus: "init", apiUsage: 0, lastMonthApiUsage: 0, + orgUserStats: {}, }; const orgReducer = createImmerReducer(initialState, { [ReduxActionTypes.FETCH_ORG_GROUPS_SUCCESS]: ( state: OrgReduxState, - action: ReduxAction<{ orgGroups: OrgGroup[] }> + action: ReduxAction<{ orgGroups: OrgGroup[], orgUserStats: Record }> ): OrgReduxState => ({ ...state, orgGroups: action.payload.orgGroups, + orgUserStats: action.payload.orgUserStats, fetchOrgGroupsFinished: true, }), [ReduxActionErrorTypes.FETCH_ORG_GROUPS_ERROR]: (state: OrgReduxState): OrgReduxState => ({ @@ -137,6 +139,7 @@ export interface OrgReduxState { orgCreateStatus: ApiRequestStatus; apiUsage: number; lastMonthApiUsage: number; + orgUserStats: Record; } export default orgReducer; diff --git a/client/packages/lowcoder/src/redux/sagas/orgSagas.ts b/client/packages/lowcoder/src/redux/sagas/orgSagas.ts index 9ab6ad64a..f4b2d3d3f 100644 --- a/client/packages/lowcoder/src/redux/sagas/orgSagas.ts +++ b/client/packages/lowcoder/src/redux/sagas/orgSagas.ts @@ -1,6 +1,6 @@ import { messageInstance } from "lowcoder-design/src/components/GlobalInstances"; -import { ApiResponse, GenericApiResponse } from "api/apiResponses"; +import { ApiResponse, FetchGroupApiResponse, GenericApiResponse } from "api/apiResponses"; import OrgApi, { CreateOrgResponse, GroupUsersResponse, OrgAPIUsageResponse, OrgUsersResponse } from "api/orgApi"; import { AxiosResponse } from "axios"; import { OrgGroup } from "constants/orgConstants"; @@ -49,7 +49,7 @@ export function* updateGroupSaga(action: ReduxAction) export function* fetchGroupsSaga(action: ReduxAction<{ orgId: string }>) { try { - const response: AxiosResponse> = yield call(OrgApi.fetchGroup); + const response: AxiosResponse> = yield call(OrgApi.fetchGroup); const isValidResponse: boolean = validateResponse(response); if (isValidResponse) { const groups = response.data.data; @@ -57,6 +57,12 @@ export function* fetchGroupsSaga(action: ReduxAction<{ orgId: string }>) { type: ReduxActionTypes.FETCH_ORG_GROUPS_SUCCESS, payload: { orgGroups: groups, + orgUserStats: { + totalAdmins: response.data.totalAdmins, + totalAdminsAndDevelopers: response.data.totalAdminsAndDevelopers, + totalDevelopersOnly: response.data.totalDevelopersOnly, + totalOtherMembers: response.data.totalOtherMembers, + } }, }); } diff --git a/client/packages/lowcoder/src/redux/selectors/orgSelectors.ts b/client/packages/lowcoder/src/redux/selectors/orgSelectors.ts index 2e69cb7be..2115f1499 100644 --- a/client/packages/lowcoder/src/redux/selectors/orgSelectors.ts +++ b/client/packages/lowcoder/src/redux/selectors/orgSelectors.ts @@ -8,6 +8,10 @@ export const getOrgGroups = (state: AppState) => { return state.ui.org.orgGroups; }; +export const getOrgUserStats = (state: AppState) => { + return state.ui.org.orgUserStats; +}; + export const getFetchOrgGroupsFinished = (state: AppState) => { return state.ui.org.fetchOrgGroupsFinished; }; diff --git a/client/packages/lowcoder/src/util/hooks.ts b/client/packages/lowcoder/src/util/hooks.ts index a8df8a860..9c9b7777c 100644 --- a/client/packages/lowcoder/src/util/hooks.ts +++ b/client/packages/lowcoder/src/util/hooks.ts @@ -27,8 +27,8 @@ import { ThemeDetail } from "@lowcoder-ee/api/commonSettingApi"; import { uniq } from "lodash"; import { constantColors } from "components/colorSelect/colorUtils"; import { AppState } from "@lowcoder-ee/redux/reducers"; -import { getOrgGroups, getOrgUsers } from "@lowcoder-ee/redux/selectors/orgSelectors"; -import { fetchGroupsAction, fetchOrgUsersAction } from "@lowcoder-ee/redux/reduxActions/orgActions"; +import { getOrgUserStats } from "@lowcoder-ee/redux/selectors/orgSelectors"; +import { fetchGroupsAction } from "@lowcoder-ee/redux/reduxActions/orgActions"; export const ForceViewModeContext = React.createContext(false); @@ -188,7 +188,7 @@ export function useMergeCompStyles( const preventAppStylesOverwriting = appSettingsComp?.getView()?.preventAppStylesOverwriting; const { preventStyleOverwriting, appliedThemeId } = props; - const styleKeys = Object.keys(props).filter(key => key.toLowerCase().endsWith('style' || 'styles')); + const styleKeys = Object.keys(props).filter(key => key.toLowerCase().endsWith('style') || key.toLowerCase().endsWith('styles')); const styleProps: Record = {}; styleKeys.forEach((key: string) => { styleProps[key] = (props as any)[key]; @@ -263,7 +263,7 @@ export function useThemeColors(allowGradient?: boolean) { export const useOrgUserCount = (orgId: string) => { const dispatch = useDispatch(); - const orgGroups = useSelector((state: AppState) => getOrgGroups(state)); // Use selector to get orgUsers from state + const orgUserStats = useSelector((state: AppState) => getOrgUserStats(state)); // Use selector to get orgUsers from state const [userCount, setUserCount] = useState(0); useEffect(() => { @@ -275,10 +275,10 @@ export const useOrgUserCount = (orgId: string) => { useEffect(() => { // Update user count when orgUsers state changes - if (orgGroups && orgGroups.length > 0) { - setUserCount(orgGroups.length); + if (Object.values(orgUserStats).length && orgUserStats.hasOwnProperty('totalAdminsAndDevelopers')) { + setUserCount(orgUserStats.totalAdminsAndDevelopers); } - }, [orgGroups]); + }, [orgUserStats]); return userCount; };