From 4a9d29271e74d445a74cdef6eaff86eb87a6fdc8 Mon Sep 17 00:00:00 2001 From: Kamal Qureshi Date: Mon, 23 Jun 2025 13:25:18 +0500 Subject: [PATCH 1/2] Adds search bar in "Add Members" for groups --- client/packages/lowcoder/src/api/orgApi.ts | 5 +++ .../src/constants/reduxActionConstants.ts | 1 + .../packages/lowcoder/src/i18n/locales/en.ts | 1 + .../setting/permission/addGroupUserDialog.tsx | 33 ++++++++++++++++--- .../src/redux/reduxActions/orgActions.ts | 8 +++++ .../lowcoder/src/redux/sagas/orgSagas.ts | 22 ++++++++++++- 6 files changed, 65 insertions(+), 5 deletions(-) diff --git a/client/packages/lowcoder/src/api/orgApi.ts b/client/packages/lowcoder/src/api/orgApi.ts index 588a20df51..379234b32e 100644 --- a/client/packages/lowcoder/src/api/orgApi.ts +++ b/client/packages/lowcoder/src/api/orgApi.ts @@ -62,6 +62,7 @@ export class OrgApi extends Api { static updateOrgURL = (orgId: string) => `/organizations/${orgId}/update`; static fetchUsage = (orgId: string) => `/organizations/${orgId}/api-usage`; static fetchOrgsByEmailURL = (email: string) => `organizations/byuser/${email}`; + static fetchGroupPotentialMembersURL = (groupId: string) => `/groups/${groupId}/potential-members`; static createGroup(request: { name: string }): AxiosPromise> { return Api.post(OrgApi.createGroupURL, request); @@ -110,6 +111,10 @@ export class OrgApi extends Api { return Api.get(OrgApi.fetchGroupUsersURL(groupId)); } + static fetchGroupPotentialMembers(searchName: string, groupId: string): AxiosPromise { + return Api.get(OrgApi.fetchGroupPotentialMembersURL(groupId), {searchName}) + } + static fetchGroupUsersPagination(request: fetchGroupUserRequestType): AxiosPromise { const {groupId, ...res} = request; return Api.get(OrgApi.fetchGroupUsersURL(groupId), {...res}); diff --git a/client/packages/lowcoder/src/constants/reduxActionConstants.ts b/client/packages/lowcoder/src/constants/reduxActionConstants.ts index f14f40c73d..821470ac56 100644 --- a/client/packages/lowcoder/src/constants/reduxActionConstants.ts +++ b/client/packages/lowcoder/src/constants/reduxActionConstants.ts @@ -86,6 +86,7 @@ export const ReduxActionTypes = { UPDATE_USER_ORG_ROLE: "UPDATE_USER_ORG_ROLE", UPDATE_USER_GROUP_ROLE: "UPDATE_USER_GROUP_ROLE", FETCH_ORG_ALL_USERS: "FETCH_ORG_ALL_USERS", + FETCH_GROUP_POTENTIAL_MEMBERS: "FETCH_ORG_ALL_GROUP_MEMBERS", FETCH_ORG_ALL_USERS_SUCCESS: "FETCH_ORG_ALL_USERS_SUCCESS", FETCH_GROUP_USERS: "FETCH_GROUP_USERS", FETCH_GROUP_USERS_SUCCESS: "FETCH_GROUP_USERS_SUCCESS", diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts index fee16d1030..48774170c9 100644 --- a/client/packages/lowcoder/src/i18n/locales/en.ts +++ b/client/packages/lowcoder/src/i18n/locales/en.ts @@ -3008,6 +3008,7 @@ export const en = { "deleteModalTitle": "Delete This Group", "deleteModalContent": "The Deleted Group Cannot Be Restored. Are You Sure to Delete the Group?", "addMember": "Add Members", + "searchMember": "Search Members", "nameColumn": "User Name", "joinTimeColumn": "Joining Time", "actionColumn": "Operation", diff --git a/client/packages/lowcoder/src/pages/setting/permission/addGroupUserDialog.tsx b/client/packages/lowcoder/src/pages/setting/permission/addGroupUserDialog.tsx index 8af0e3cecc..0a11b018b5 100644 --- a/client/packages/lowcoder/src/pages/setting/permission/addGroupUserDialog.tsx +++ b/client/packages/lowcoder/src/pages/setting/permission/addGroupUserDialog.tsx @@ -1,11 +1,14 @@ import Column from "antd/es/table/Column"; import OrgApi from "api/orgApi"; import { GroupUser, MEMBER_ROLE, OrgUser } from "constants/orgConstants"; -import { CheckBox, CustomModal } from "lowcoder-design"; +import { CheckBox, CustomModal, Search } from "lowcoder-design"; import { CSSProperties, ReactNode, useEffect, useRef, useState } from "react"; import { connect, useDispatch } from "react-redux"; import { AppState } from "redux/reducers"; -import { fetchGroupUsersAction, fetchOrgUsersAction } from "redux/reduxActions/orgActions"; +import { fetchGroupUsersAction, + fetchOrgUsersAction, + fetchGroupPotentialMembersAction +} from "redux/reduxActions/orgActions"; import styled from "styled-components"; import { StyledTable, UserTableCellWrapper } from "./styledComponents"; import { formatTimestamp } from "util/dateTimeUtils"; @@ -40,7 +43,18 @@ function AddGroupUserDialog(props: { const addableUsers = orgUsers.filter((user) => !groupUserIdMap.has(user.userId)); const toAddUserIdRecord = useRef>({}); const [confirmLoading, setConfirmLoading] = useState(false); + const [searchValue, setSearchValue] = useState("") const dispatch = useDispatch(); + + useEffect(() => { + const timer = setTimeout(() => { + if (searchValue.length > 2 || searchValue === "") + dispatch(fetchGroupPotentialMembersAction(searchValue, groupId)); + return + }, 500); + return () => clearTimeout(timer); + }, [searchValue]) + useEffect(() => { if (dialogVisible) { dispatch(fetchOrgUsersAction(orgId)); @@ -92,7 +106,18 @@ function AddGroupUserDialog(props: { setDialogVisible(false); }} > - {!addableUsers || addableUsers.length === 0 ? ( + setSearchValue(e.target.value)} + style={{ + width: "100%", + height: "32px", + paddingRight: "20px", + marginBottom: "10px" + }} + /> + {(!addableUsers || addableUsers.length === 0) ? ( ) : ( @@ -106,7 +131,7 @@ function AddGroupUserDialog(props: { scroll={{ y: 309 }} > ({ payload: payload, }); +export const fetchGroupPotentialMembersAction = (searchName: string, groupId: string) => ({ + type: ReduxActionTypes.FETCH_GROUP_POTENTIAL_MEMBERS, + payload: { + searchName, + groupId + }, +}); + export type AddGroupUserPayload = { role: string; groupId: string; diff --git a/client/packages/lowcoder/src/redux/sagas/orgSagas.ts b/client/packages/lowcoder/src/redux/sagas/orgSagas.ts index b259f12a00..a2339dca92 100644 --- a/client/packages/lowcoder/src/redux/sagas/orgSagas.ts +++ b/client/packages/lowcoder/src/redux/sagas/orgSagas.ts @@ -107,11 +107,30 @@ export function* updateUserGroupRoleSaga(action: ReduxAction) { + try { + const response: AxiosResponse = yield call( + OrgApi.fetchGroupPotentialMembers, + action.payload.searchName, + action.payload.groupId + ); + const isValidResponse: boolean = validateResponse(response); + if (isValidResponse) { + yield put({ + type: ReduxActionTypes.FETCH_ORG_ALL_USERS_SUCCESS, + payload: response.data.data, + }); + } + } catch (error) { + log.error(error); + } +} + export function* fetchOrgUsersSaga(action: ReduxAction<{ orgId: string }>) { try { const response: AxiosResponse = yield call( OrgApi.fetchOrgUsers, - action.payload.orgId + action.payload.orgId, ); const isValidResponse: boolean = validateResponse(response); if (isValidResponse) { @@ -377,6 +396,7 @@ export default function* orgSagas() { takeLatest(ReduxActionTypes.UPDATE_USER_ORG_ROLE, updateUserOrgRoleSaga), takeLatest(ReduxActionTypes.UPDATE_USER_GROUP_ROLE, updateUserGroupRoleSaga), takeLatest(ReduxActionTypes.FETCH_ORG_ALL_USERS, fetchOrgUsersSaga), + takeLatest(ReduxActionTypes.FETCH_GROUP_POTENTIAL_MEMBERS, fetchGroupPotentialMembersSaga), takeLatest(ReduxActionTypes.DELETE_ORG_USER, deleteOrgUserSaga), takeLatest(ReduxActionTypes.QUIT_GROUP, quitGroupSaga), takeLatest(ReduxActionTypes.QUIT_ORG, quitOrgSaga), From 28161b046b89ff96150f858317f2812ea60df76f Mon Sep 17 00:00:00 2001 From: Kamal Qureshi Date: Mon, 23 Jun 2025 13:39:05 +0500 Subject: [PATCH 2/2] Added debounce for searching --- .../setting/permission/addGroupUserDialog.tsx | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/client/packages/lowcoder/src/pages/setting/permission/addGroupUserDialog.tsx b/client/packages/lowcoder/src/pages/setting/permission/addGroupUserDialog.tsx index 0a11b018b5..46cad16b67 100644 --- a/client/packages/lowcoder/src/pages/setting/permission/addGroupUserDialog.tsx +++ b/client/packages/lowcoder/src/pages/setting/permission/addGroupUserDialog.tsx @@ -2,7 +2,7 @@ import Column from "antd/es/table/Column"; import OrgApi from "api/orgApi"; import { GroupUser, MEMBER_ROLE, OrgUser } from "constants/orgConstants"; import { CheckBox, CustomModal, Search } from "lowcoder-design"; -import { CSSProperties, ReactNode, useEffect, useRef, useState } from "react"; +import { CSSProperties, ReactNode, useEffect, useRef, useState, useCallback } from "react"; import { connect, useDispatch } from "react-redux"; import { AppState } from "redux/reducers"; import { fetchGroupUsersAction, @@ -17,6 +17,7 @@ import { isGroupAdmin } from "util/permissionUtils"; import { SuperUserIcon } from "lowcoder-design"; import { EmptyContent } from "pages/common/styledComponent"; import { trans } from "i18n"; +import { debounce } from "lodash"; const TableWrapper = styled.div` margin-right: -16px; @@ -46,14 +47,21 @@ function AddGroupUserDialog(props: { const [searchValue, setSearchValue] = useState("") const dispatch = useDispatch(); + const debouncedFetchPotentialMembers = useCallback( + debounce((searchVal: string) => { + dispatch(fetchGroupPotentialMembersAction(searchVal, groupId)); + }, 500), + [dispatch, groupId] + ); + useEffect(() => { - const timer = setTimeout(() => { - if (searchValue.length > 2 || searchValue === "") - dispatch(fetchGroupPotentialMembersAction(searchValue, groupId)); - return - }, 500); - return () => clearTimeout(timer); - }, [searchValue]) + if (searchValue.length > 2 || searchValue === "") { + debouncedFetchPotentialMembers(searchValue); + } + return () => { + debouncedFetchPotentialMembers.cancel(); + }; + }, [searchValue, debouncedFetchPotentialMembers]); useEffect(() => { if (dialogVisible) { pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy