Skip to content

Commit 88ec6f4

Browse files
authored
Merge pull request #1286 from lowcoder-org/subscription-users-count
Subscription user count to include admins & developers
2 parents 6bb1d91 + 4ccb1d1 commit 88ec6f4

File tree

7 files changed

+34
-37
lines changed

7 files changed

+34
-37
lines changed

client/packages/lowcoder/src/api/apiResponses.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ export interface GenericApiResponse<T> {
1212
data: T;
1313
}
1414

15+
export interface FetchGroupApiResponse<T> extends GenericApiResponse<T> {
16+
totalAdmins: number,
17+
totalAdminsAndDevelopers: number,
18+
totalDevelopersOnly: number,
19+
totalOtherMembers: number,
20+
}
21+
1522
// NO_DATASOURCES_FOUND, 1000, "Unable to find {0} with id {1}"
1623
// INVALID_PARAMTER, 4000, "Invalid parameter {0} provided in the input"
1724
// PLUGIN_NOT_INSTALLED, 4001, "Plugin {0} not installed"

client/packages/lowcoder/src/api/subscriptionApi.ts

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import axios, { AxiosInstance, AxiosRequestConfig, CancelToken } from "axios";
33
import { useDispatch, useSelector } from "react-redux";
44
import { useEffect, useState} from "react";
55
import { calculateFlowCode } from "./apiUtils";
6-
import { fetchOrgUsersAction } from "redux/reduxActions/orgActions";
6+
import { fetchGroupsAction, fetchOrgUsersAction } from "redux/reduxActions/orgActions";
77
import { getOrgUsers } from "redux/selectors/orgSelectors";
88
import { AppState } from "@lowcoder-ee/redux/reducers";
99
import type {
@@ -289,28 +289,4 @@ export const getCustomerPortalSession = async (customerId: string) => {
289289
}
290290
};
291291

292-
// Hooks
293-
294-
export const useOrgUserCount = (orgId: string) => {
295-
const dispatch = useDispatch();
296-
const orgUsers = useSelector((state: AppState) => getOrgUsers(state)); // Use selector to get orgUsers from state
297-
const [userCount, setUserCount] = useState<number>(0);
298-
299-
useEffect(() => {
300-
// Dispatch action to fetch organization users
301-
if (orgId) {
302-
dispatch(fetchOrgUsersAction(orgId));
303-
}
304-
}, [dispatch, orgId]);
305-
306-
useEffect(() => {
307-
// Update user count when orgUsers state changes
308-
if (orgUsers && orgUsers.length > 0) {
309-
setUserCount(orgUsers.length);
310-
}
311-
}, [orgUsers]);
312-
313-
return userCount;
314-
};
315-
316292
export default SubscriptionApi;

client/packages/lowcoder/src/redux/reducers/uiReducers/orgReducer.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,17 @@ const initialState: OrgReduxState = {
2828
orgCreateStatus: "init",
2929
apiUsage: 0,
3030
lastMonthApiUsage: 0,
31+
orgUserStats: {},
3132
};
3233

3334
const orgReducer = createImmerReducer(initialState, {
3435
[ReduxActionTypes.FETCH_ORG_GROUPS_SUCCESS]: (
3536
state: OrgReduxState,
36-
action: ReduxAction<{ orgGroups: OrgGroup[] }>
37+
action: ReduxAction<{ orgGroups: OrgGroup[], orgUserStats: Record<string, number> }>
3738
): OrgReduxState => ({
3839
...state,
3940
orgGroups: action.payload.orgGroups,
41+
orgUserStats: action.payload.orgUserStats,
4042
fetchOrgGroupsFinished: true,
4143
}),
4244
[ReduxActionErrorTypes.FETCH_ORG_GROUPS_ERROR]: (state: OrgReduxState): OrgReduxState => ({
@@ -137,6 +139,7 @@ export interface OrgReduxState {
137139
orgCreateStatus: ApiRequestStatus;
138140
apiUsage: number;
139141
lastMonthApiUsage: number;
142+
orgUserStats: Record<string, number>;
140143
}
141144

142145
export default orgReducer;

client/packages/lowcoder/src/redux/sagas/orgSagas.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { messageInstance } from "lowcoder-design/src/components/GlobalInstances";
22

3-
import { ApiResponse, GenericApiResponse } from "api/apiResponses";
3+
import { ApiResponse, FetchGroupApiResponse, GenericApiResponse } from "api/apiResponses";
44
import OrgApi, { CreateOrgResponse, GroupUsersResponse, OrgAPIUsageResponse, OrgUsersResponse } from "api/orgApi";
55
import { AxiosResponse } from "axios";
66
import { OrgGroup } from "constants/orgConstants";
@@ -49,14 +49,20 @@ export function* updateGroupSaga(action: ReduxAction<UpdateGroupActionPayload>)
4949

5050
export function* fetchGroupsSaga(action: ReduxAction<{ orgId: string }>) {
5151
try {
52-
const response: AxiosResponse<GenericApiResponse<OrgGroup[]>> = yield call(OrgApi.fetchGroup);
52+
const response: AxiosResponse<FetchGroupApiResponse<OrgGroup[]>> = yield call(OrgApi.fetchGroup);
5353
const isValidResponse: boolean = validateResponse(response);
5454
if (isValidResponse) {
5555
const groups = response.data.data;
5656
yield put({
5757
type: ReduxActionTypes.FETCH_ORG_GROUPS_SUCCESS,
5858
payload: {
5959
orgGroups: groups,
60+
orgUserStats: {
61+
totalAdmins: response.data.totalAdmins,
62+
totalAdminsAndDevelopers: response.data.totalAdminsAndDevelopers,
63+
totalDevelopersOnly: response.data.totalDevelopersOnly,
64+
totalOtherMembers: response.data.totalOtherMembers,
65+
}
6066
},
6167
});
6268
}

client/packages/lowcoder/src/redux/selectors/orgSelectors.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ export const getOrgGroups = (state: AppState) => {
88
return state.ui.org.orgGroups;
99
};
1010

11+
export const getOrgUserStats = (state: AppState) => {
12+
return state.ui.org.orgUserStats;
13+
};
14+
1115
export const getFetchOrgGroupsFinished = (state: AppState) => {
1216
return state.ui.org.fetchOrgGroupsFinished;
1317
};

client/packages/lowcoder/src/util/context/SubscriptionContext.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import { createCheckoutLink, createCustomer, getProducts, searchCustomer, useOrgUserCount } from "@lowcoder-ee/api/subscriptionApi";
1+
import { createCheckoutLink, createCustomer, getProducts, searchCustomer } from "@lowcoder-ee/api/subscriptionApi";
22
import { StripeCustomer, SubscriptionProduct, InitSubscriptionProducts, LowcoderSearchCustomer, LowcoderNewCustomer, Subscription } from "@lowcoder-ee/constants/subscriptionConstants";
33
import { getDeploymentId } from "@lowcoder-ee/redux/selectors/configSelectors";
44
import { getFetchSubscriptionsFinished, getSubscriptions, getSubscriptionsError } from "@lowcoder-ee/redux/selectors/subscriptionSelectors";
55
import { getCurrentUser, getUser } from "@lowcoder-ee/redux/selectors/usersSelectors";
66
import { createContext, ReactNode, useContext, useEffect, useState } from "react";
77
import { useSelector } from "react-redux";
8+
import { useOrgUserCount } from "../hooks";
89

910
export interface SubscriptionContextType {
1011
products: SubscriptionProduct[];

client/packages/lowcoder/src/util/hooks.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ import { ThemeDetail } from "@lowcoder-ee/api/commonSettingApi";
2727
import { uniq } from "lodash";
2828
import { constantColors } from "components/colorSelect/colorUtils";
2929
import { AppState } from "@lowcoder-ee/redux/reducers";
30-
import { getOrgUsers } from "@lowcoder-ee/redux/selectors/orgSelectors";
31-
import { fetchOrgUsersAction } from "@lowcoder-ee/redux/reduxActions/orgActions";
30+
import { getOrgUserStats } from "@lowcoder-ee/redux/selectors/orgSelectors";
31+
import { fetchGroupsAction } from "@lowcoder-ee/redux/reduxActions/orgActions";
3232

3333
export const ForceViewModeContext = React.createContext<boolean>(false);
3434

@@ -188,7 +188,7 @@ export function useMergeCompStyles(
188188
const preventAppStylesOverwriting = appSettingsComp?.getView()?.preventAppStylesOverwriting;
189189
const { preventStyleOverwriting, appliedThemeId } = props;
190190

191-
const styleKeys = Object.keys(props).filter(key => key.toLowerCase().endsWith('style' || 'styles'));
191+
const styleKeys = Object.keys(props).filter(key => key.toLowerCase().endsWith('style') || key.toLowerCase().endsWith('styles'));
192192
const styleProps: Record<string, any> = {};
193193
styleKeys.forEach((key: string) => {
194194
styleProps[key] = (props as any)[key];
@@ -263,22 +263,22 @@ export function useThemeColors(allowGradient?: boolean) {
263263

264264
export const useOrgUserCount = (orgId: string) => {
265265
const dispatch = useDispatch();
266-
const orgUsers = useSelector((state: AppState) => getOrgUsers(state)); // Use selector to get orgUsers from state
266+
const orgUserStats = useSelector((state: AppState) => getOrgUserStats(state)); // Use selector to get orgUsers from state
267267
const [userCount, setUserCount] = useState<number>(0);
268268

269269
useEffect(() => {
270270
// Dispatch action to fetch organization users
271271
if (orgId) {
272-
dispatch(fetchOrgUsersAction(orgId));
272+
dispatch(fetchGroupsAction(orgId));
273273
}
274274
}, [dispatch, orgId]);
275275

276276
useEffect(() => {
277277
// Update user count when orgUsers state changes
278-
if (orgUsers && orgUsers.length > 0) {
279-
setUserCount(orgUsers.length);
278+
if (Object.values(orgUserStats).length && orgUserStats.hasOwnProperty('totalAdminsAndDevelopers')) {
279+
setUserCount(orgUserStats.totalAdminsAndDevelopers);
280280
}
281-
}, [orgUsers]);
281+
}, [orgUserStats]);
282282

283283
return userCount;
284284
};

0 commit comments

Comments
 (0)