Content-Length: 13738 | pFad | http://github.com/lowcoder-org/lowcoder/pull/1817.patch
thub.com
From 552bbff99cc61d192f5bc1569a6b46082e677c37 Mon Sep 17 00:00:00 2001
From: Kamal Qureshi
Date: Sat, 28 Jun 2025 03:01:02 +0500
Subject: [PATCH] Search in Publish Permission Dialog
---
.../lowcoder/src/api/applicationApi.ts | 5 +
.../PermissionDialog/Permission.tsx | 140 +++++++++++-------
.../src/constants/applicationConstants.ts | 14 +-
.../packages/lowcoder/src/i18n/locales/en.ts | 1 +
.../lowcoder/src/util/pagination/axios.ts | 16 ++
5 files changed, 118 insertions(+), 58 deletions(-)
diff --git a/client/packages/lowcoder/src/api/applicationApi.ts b/client/packages/lowcoder/src/api/applicationApi.ts
index 2411b50d80..8ed818b371 100644
--- a/client/packages/lowcoder/src/api/applicationApi.ts
+++ b/client/packages/lowcoder/src/api/applicationApi.ts
@@ -99,6 +99,7 @@ class ApplicationApi extends Api {
static publicToMarketplaceURL = (applicationId: string) => `/applications/${applicationId}/public-to-marketplace`;
static getMarketplaceAppURL = (applicationId: string) => `/applications/${applicationId}/view_marketplace`;
static setAppEditingStateURL = (applicationId: string) => `/applications/editState/${applicationId}`;
+ static getAvailableGroupsMembersURL = (applicationId: string) => `/applications/${applicationId}/groups-members/available`;
static serverSettingsURL = () => `/serverSettings`;
static fetchHomeData(request: HomeDataPayload): AxiosPromise {
@@ -217,6 +218,10 @@ class ApplicationApi extends Api {
});
}
+ static getAvailableGroupsMembers(applicationId: string, search: string): AxiosPromise {
+ return Api.get(ApplicationApi.getAvailableGroupsMembersURL(applicationId), {search})
+ }
+
/**
* set app as public
*/
diff --git a/client/packages/lowcoder/src/components/PermissionDialog/Permission.tsx b/client/packages/lowcoder/src/components/PermissionDialog/Permission.tsx
index 82ea45beb9..6425d3afc6 100644
--- a/client/packages/lowcoder/src/components/PermissionDialog/Permission.tsx
+++ b/client/packages/lowcoder/src/components/PermissionDialog/Permission.tsx
@@ -3,16 +3,15 @@ import {
CloseIcon,
CommonTextLabel,
CustomSelect,
+ Search,
TacoButton,
} from "lowcoder-design";
-import { useEffect, useRef, useState } from "react";
+import { useEffect, useRef, useState, useCallback } from "react";
import styled from "styled-components";
+import { debounce } from "lodash";
import ProfileImage from "pages/common/profileImage";
-import { useDispatch, useSelector } from "react-redux";
-import { fetchGroupsAction, fetchOrgUsersAction } from "redux/reduxActions/orgActions";
-import { getOrgGroups, getOrgUsers } from "redux/selectors/orgSelectors";
-import { OrgGroup, OrgUser } from "constants/orgConstants";
-import { ApplicationPermissionType, ApplicationRoleType } from "constants/applicationConstants";
+import { useSelector } from "react-redux";
+import { ApplicationPermissionType, ApplicationRoleType, GroupsMembersPermission } from "constants/applicationConstants";
import {
PermissionItemName,
RoleSelectOption,
@@ -27,6 +26,8 @@ import { getUser } from "redux/selectors/usersSelectors";
import { EmptyContent } from "pages/common/styledComponent";
import { trans } from "i18n";
import { PermissionItem } from "./PermissionList";
+import { currentApplication } from "@lowcoder-ee/redux/selectors/applicationSelector";
+import { fetchAvailableGroupsMembers } from "@lowcoder-ee/util/pagination/axios";
const AddAppUserContent = styled.div`
display: flex;
@@ -86,8 +87,7 @@ const PermissionSelectWrapper = styled.div`
padding: 4px 8px;
margin-top: 8px;
background: #fdfdfd;
- outline: 1px solid #d7d9e0;
- border-radius: 4px;
+ outline: 1px dashed #d7d9e0;
.ant-select {
font-size: 13px;
@@ -95,11 +95,11 @@ const PermissionSelectWrapper = styled.div`
}
&:hover {
- outline: 1px solid #8b8fa3;
+ outline: 1px dashed #8b8fa3;
}
&:focus-within {
- outline: 1px solid #315efb;
+ outline: 1px dashed rgb(203, 212, 245);
border-radius: 4px;
box-shadow: 0 0 0 3px rgb(24 144 255 / 20%);
}
@@ -199,48 +199,34 @@ type PermissionAddEntity = {
key: string;
};
-/**
- * compose users and groups's permissions, filter the data
- *
- * @param orgGroups groups
- * @param orgUsers users
- * @param currentUser currentUser
- * @param filterItems filterItems
- */
+function isGroup(data: GroupsMembersPermission) {
+ return data?.type === "Group"
+}
+
function getPermissionOptionView(
- orgGroups: OrgGroup[],
- orgUsers: OrgUser[],
- currentUser: User,
+ groupsMembers: GroupsMembersPermission[],
filterItems: PermissionItem[]
): AddAppOptionView[] {
- let permissionViews: AddAppOptionView[] = orgGroups.map((group) => {
+
+ let permissionsViews = groupsMembers?.map((user) => {
return {
- type: "GROUP",
- id: group.groupId,
- name: group.groupName,
- };
- });
- permissionViews = permissionViews.concat(
- orgUsers.map((user) => {
- return {
- type: "USER",
- id: user.userId,
- name: user.name,
- avatarUrl: user.avatarUrl,
- };
- })
- );
- permissionViews = permissionViews.filter(
- (v) =>
- !filterItems.find((i) => i.id === v.id && i.type === v.type) &&
- !(v.type === "USER" && v.id === currentUser.id)
+ type: user.type as ApplicationPermissionType,
+ id: isGroup(user) ? user.data.groupId : user.data.userId,
+ name: isGroup(user) ? user.data.groupName : user.data.name,
+ ...(isGroup(user) ? {} : { avatarUrl: user.data.avatarUrl })
+ }
+ })
+
+ permissionsViews = permissionsViews.filter((v) =>
+ !filterItems.find((i) => i.id === v.id && i.type === v.type)
);
- return permissionViews;
+
+ return permissionsViews.filter((v) => v.id && v.name) as AddAppOptionView[];
}
function PermissionSelectorOption(props: { optionView: AddAppOptionView }) {
const { optionView } = props;
- const groupIcon = optionView.type === "GROUP" && (
+ const groupIcon = optionView.type === "Group" && (
);
return (
@@ -258,7 +244,7 @@ function PermissionSelectorOption(props: { optionView: AddAppOptionView }) {
function PermissionSelectorLabel(props: { view: AddAppOptionView }) {
const { view } = props;
- const groupIcon = view.type === "GROUP" && (
+ const groupIcon = view.type === "Group" && (
);
return (
@@ -309,12 +295,52 @@ const PermissionSelector = (props: {
filterItems: PermissionItem[];
supportRoles: { label: string; value: PermissionRole }[];
}) => {
- const orgGroups = useSelector(getOrgGroups);
- const orgUsers = useSelector(getOrgUsers);
const { selectedItems, setSelectRole, setSelectedItems, user } = props;
- const optionViews = getPermissionOptionView(orgGroups, orgUsers, user, props.filterItems);
const [roleSelectVisible, setRoleSelectVisible] = useState(false);
const selectRef = useRef(null);
+ const [optionViews, setOptionViews] = useState()
+ const [searchValue, setSearchValue] = useState("");
+ const [isLoading, setIsLoading] = useState(false);
+ const application = useSelector(currentApplication)
+
+ const debouncedUserSearch = useCallback(
+ debounce((searchTerm: string) => {
+ if (!application) return;
+
+ setIsLoading(true);
+ fetchAvailableGroupsMembers(application.applicationId, searchTerm).then(res => {
+ if(res.success) {
+ setOptionViews(getPermissionOptionView(res.data, props.filterItems))
+ }
+ setIsLoading(false);
+ }).catch(() => {
+ setIsLoading(false);
+ });
+ }, 500),
+ [application, props.filterItems]
+ );
+
+ useEffect(() => {
+ debouncedUserSearch(searchValue);
+
+ return () => {
+ debouncedUserSearch.cancel();
+ };
+ }, [searchValue, debouncedUserSearch]);
+
+ useEffect(() => {
+ if (!application) return;
+
+ setIsLoading(true);
+ fetchAvailableGroupsMembers(application.applicationId, "").then(res => {
+ if(res.success) {
+ setOptionViews(getPermissionOptionView(res.data, props.filterItems))
+ }
+ setIsLoading(false);
+ }).catch(() => {
+ setIsLoading(false);
+ });
+ }, [application, props.filterItems]);
useEffect(() => {
setRoleSelectVisible(selectedItems.length > 0);
@@ -325,12 +351,18 @@ const PermissionSelector = (props: {
return (
<>
+ setSearchValue(e.target.value)}
+ />
document.getElementById("add-app-user-permission-dropdown")!}
optionLabelProp="label"
tagRender={PermissionTagRender}
@@ -350,7 +382,7 @@ const PermissionSelector = (props: {
setSelectedItems(selectedItems.filter((item) => item.key !== option.key));
}}
>
- {optionViews.map((view) => {
+ {optionViews?.map((view) => {
return (
void;
}) => {
const { onCancel } = props;
- const dispatch = useDispatch();
const user = useSelector(getUser);
const [selectRole, setSelectRole] = useState("viewer");
const [selectedItems, setSelectedItems] = useState([]);
- useEffect(() => {
- dispatch(fetchOrgUsersAction(user.currentOrgId));
- dispatch(fetchGroupsAction(user.currentOrgId));
- }, []);
-
return (
@@ -426,10 +452,10 @@ export const Permission = (props: {
buttonType="primary"
onClick={() => {
const uids = selectedItems
- .filter((item) => item.type === "USER")
+ .filter((item) => item.type === "User")
.map((item) => item.id);
const gids = selectedItems
- .filter((item) => item.type === "GROUP")
+ .filter((item) => item.type === "Group")
.map((item) => item.id);
if (uids.length === 0 && gids.length === 0) {
onCancel();
diff --git a/client/packages/lowcoder/src/constants/applicationConstants.ts b/client/packages/lowcoder/src/constants/applicationConstants.ts
index f29dce24b6..f685eeb8e6 100644
--- a/client/packages/lowcoder/src/constants/applicationConstants.ts
+++ b/client/packages/lowcoder/src/constants/applicationConstants.ts
@@ -62,7 +62,7 @@ export const AppUILayoutType: Record = {
export type ApplicationDSLType = "editing" | "published" | "view_marketplace";
export type ApplicationRoleType = "viewer" | "editor" | "owner";
-export type ApplicationPermissionType = "USER" | "GROUP" | "ORG_ADMIN";
+export type ApplicationPermissionType = "User" | "Group" | "ORG_ADMIN" | "USER" | "GROUP";
export interface ApplicationExtra {
moduleHeight?: number;
@@ -70,6 +70,18 @@ export interface ApplicationExtra {
layers?: boolean;
}
+export type GroupsMembersPermission = {
+ type: string
+ data: {
+ groupGid?: string
+ groupId?: string
+ userId?: string
+ groupName?: string
+ name?: string
+ avatarUrl?: string
+ }
+}
+
export interface ApplicationMeta {
name: string;
applicationType: AppTypeEnum;
diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts
index 44f5f4b1dd..470e8ec178 100644
--- a/client/packages/lowcoder/src/i18n/locales/en.ts
+++ b/client/packages/lowcoder/src/i18n/locales/en.ts
@@ -4010,6 +4010,7 @@ export const en = {
"orgName": "{orgName} admins",
"addMember": "Add members",
"addPermissionPlaceholder": "Please enter a name to search members",
+ "selectedUsersAndGroups":"Selected Users and Groups",
"searchMemberOrGroup": "Search for members or groups: ",
"addPermissionErrorMessage": "Failed to add permission, {message}",
"copyModalTitle": 'Clone "{name}"',
diff --git a/client/packages/lowcoder/src/util/pagination/axios.ts b/client/packages/lowcoder/src/util/pagination/axios.ts
index d03bf1800c..e59ccbfec1 100644
--- a/client/packages/lowcoder/src/util/pagination/axios.ts
+++ b/client/packages/lowcoder/src/util/pagination/axios.ts
@@ -102,6 +102,22 @@ export const fetchGroupUsrPagination = async (request: fetchGroupUserRequestType
}
}
+export const fetchAvailableGroupsMembers = async (applicationId: string, search: string) => {
+ try{
+ const response = await ApplicationApi.getAvailableGroupsMembers(applicationId, search)
+ return {
+ success: true,
+ data: response.data.data
+ }
+ } catch (error: any) {
+ console.error('Failed to fetch data: ', error)
+ return {
+ success: false,
+ error: error
+ }
+ }
+}
+
export const fetchOrgUsrPagination = async (request: fetchOrgUserRequestType)=> {
try {
const response = await OrgApi.fetchOrgUsersPagination(request);
--- a PPN by Garber Painting Akron. With Image Size Reduction included!Fetched URL: http://github.com/lowcoder-org/lowcoder/pull/1817.patch
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy