Skip to content

Commit c682cc0

Browse files
committed
Merge branch 'dev' into tags_component
2 parents d571a7f + 67643f6 commit c682cc0

File tree

50 files changed

+5802
-252
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+5802
-252
lines changed

client/packages/lowcoder-design/src/components/customSelect.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ const SelectWrapper = styled.div<{ $border?: boolean }>`
2020
padding: ${(props) => (props.$border ? "0px" : "0 0 0 12px")};
2121
height: 100%;
2222
align-items: center;
23-
margin-right: 8px;
23+
margin-right: 10px;
24+
padding-right: 5px;
2425
background-color: #fff;
2526
2627
.ant-select-selection-item {
@@ -46,9 +47,9 @@ const SelectWrapper = styled.div<{ $border?: boolean }>`
4647
}
4748
4849
.ant-select-arrow {
49-
width: 20px;
50-
height: 20px;
51-
right: 8px;
50+
width: 17px;
51+
height: 17px;
52+
right: 10px;
5253
top: 0;
5354
bottom: 0;
5455
margin: auto;

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ class ApplicationApi extends Api {
9999
static publicToMarketplaceURL = (applicationId: string) => `/applications/${applicationId}/public-to-marketplace`;
100100
static getMarketplaceAppURL = (applicationId: string) => `/applications/${applicationId}/view_marketplace`;
101101
static setAppEditingStateURL = (applicationId: string) => `/applications/editState/${applicationId}`;
102+
static getAvailableGroupsMembersURL = (applicationId: string) => `/applications/${applicationId}/groups-members/available`;
102103
static serverSettingsURL = () => `/serverSettings`;
103104

104105
static fetchHomeData(request: HomeDataPayload): AxiosPromise<HomeDataResponse> {
@@ -217,6 +218,10 @@ class ApplicationApi extends Api {
217218
});
218219
}
219220

221+
static getAvailableGroupsMembers(applicationId: string, search: string): AxiosPromise<any> {
222+
return Api.get(ApplicationApi.getAvailableGroupsMembersURL(applicationId), {search})
223+
}
224+
220225
/**
221226
* set app as public
222227
*/

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,13 @@ export type GetCurrentUserResponse = GenericApiResponse<CurrentUser>;
6363
export interface GetMyOrgsResponse extends ApiResponse {
6464
data: {
6565
data: Array<{
66-
orgId: string;
67-
orgName: string;
66+
isCurrentOrg: boolean;
67+
orgView: {
68+
orgId: string;
69+
orgName: string;
70+
createdAt?: number;
71+
updatedAt?: number;
72+
};
6873
}>;
6974
pageNum: number;
7075
pageSize: number;

client/packages/lowcoder/src/components/PermissionDialog/Permission.tsx

Lines changed: 84 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,15 @@ import {
33
CloseIcon,
44
CommonTextLabel,
55
CustomSelect,
6+
Search,
67
TacoButton,
78
} from "lowcoder-design";
8-
import { useEffect, useRef, useState } from "react";
9+
import { useEffect, useRef, useState, useCallback } from "react";
910
import styled from "styled-components";
11+
import { debounce } from "lodash";
1012
import ProfileImage from "pages/common/profileImage";
11-
import { useDispatch, useSelector } from "react-redux";
12-
import { fetchGroupsAction, fetchOrgUsersAction } from "redux/reduxActions/orgActions";
13-
import { getOrgGroups, getOrgUsers } from "redux/selectors/orgSelectors";
14-
import { OrgGroup, OrgUser } from "constants/orgConstants";
15-
import { ApplicationPermissionType, ApplicationRoleType } from "constants/applicationConstants";
13+
import { useSelector } from "react-redux";
14+
import { ApplicationPermissionType, ApplicationRoleType, GroupsMembersPermission } from "constants/applicationConstants";
1615
import {
1716
PermissionItemName,
1817
RoleSelectOption,
@@ -27,6 +26,8 @@ import { getUser } from "redux/selectors/usersSelectors";
2726
import { EmptyContent } from "pages/common/styledComponent";
2827
import { trans } from "i18n";
2928
import { PermissionItem } from "./PermissionList";
29+
import { currentApplication } from "@lowcoder-ee/redux/selectors/applicationSelector";
30+
import { fetchAvailableGroupsMembers } from "@lowcoder-ee/util/pagination/axios";
3031

3132
const AddAppUserContent = styled.div`
3233
display: flex;
@@ -86,20 +87,19 @@ const PermissionSelectWrapper = styled.div`
8687
padding: 4px 8px;
8788
margin-top: 8px;
8889
background: #fdfdfd;
89-
outline: 1px solid #d7d9e0;
90-
border-radius: 4px;
90+
outline: 1px dashed #d7d9e0;
9191
9292
.ant-select {
9393
font-size: 13px;
9494
line-height: 13px;
9595
}
9696
9797
&:hover {
98-
outline: 1px solid #8b8fa3;
98+
outline: 1px dashed #8b8fa3;
9999
}
100100
101101
&:focus-within {
102-
outline: 1px solid #315efb;
102+
outline: 1px dashed rgb(203, 212, 245);
103103
border-radius: 4px;
104104
box-shadow: 0 0 0 3px rgb(24 144 255 / 20%);
105105
}
@@ -199,48 +199,34 @@ type PermissionAddEntity = {
199199
key: string;
200200
};
201201

202-
/**
203-
* compose users and groups's permissions, filter the data
204-
*
205-
* @param orgGroups groups
206-
* @param orgUsers users
207-
* @param currentUser currentUser
208-
* @param filterItems filterItems
209-
*/
202+
function isGroup(data: GroupsMembersPermission) {
203+
return data?.type === "Group"
204+
}
205+
210206
function getPermissionOptionView(
211-
orgGroups: OrgGroup[],
212-
orgUsers: OrgUser[],
213-
currentUser: User,
207+
groupsMembers: GroupsMembersPermission[],
214208
filterItems: PermissionItem[]
215209
): AddAppOptionView[] {
216-
let permissionViews: AddAppOptionView[] = orgGroups.map((group) => {
210+
211+
let permissionsViews = groupsMembers?.map((user) => {
217212
return {
218-
type: "GROUP",
219-
id: group.groupId,
220-
name: group.groupName,
221-
};
222-
});
223-
permissionViews = permissionViews.concat(
224-
orgUsers.map((user) => {
225-
return {
226-
type: "USER",
227-
id: user.userId,
228-
name: user.name,
229-
avatarUrl: user.avatarUrl,
230-
};
231-
})
232-
);
233-
permissionViews = permissionViews.filter(
234-
(v) =>
235-
!filterItems.find((i) => i.id === v.id && i.type === v.type) &&
236-
!(v.type === "USER" && v.id === currentUser.id)
213+
type: user.type as ApplicationPermissionType,
214+
id: isGroup(user) ? user.data.groupId : user.data.userId,
215+
name: isGroup(user) ? user.data.groupName : user.data.name,
216+
...(isGroup(user) ? {} : { avatarUrl: user.data.avatarUrl })
217+
}
218+
})
219+
220+
permissionsViews = permissionsViews.filter((v) =>
221+
!filterItems.find((i) => i.id === v.id && i.type === v.type)
237222
);
238-
return permissionViews;
223+
224+
return permissionsViews.filter((v) => v.id && v.name) as AddAppOptionView[];
239225
}
240226

241227
function PermissionSelectorOption(props: { optionView: AddAppOptionView }) {
242228
const { optionView } = props;
243-
const groupIcon = optionView.type === "GROUP" && (
229+
const groupIcon = optionView.type === "Group" && (
244230
<StyledGroupIcon $color={getInitialsAndColorCode(optionView.name)[1]} />
245231
);
246232
return (
@@ -258,7 +244,7 @@ function PermissionSelectorOption(props: { optionView: AddAppOptionView }) {
258244

259245
function PermissionSelectorLabel(props: { view: AddAppOptionView }) {
260246
const { view } = props;
261-
const groupIcon = view.type === "GROUP" && (
247+
const groupIcon = view.type === "Group" && (
262248
<StyledGroupIcon $color={getInitialsAndColorCode(view.name)[1]} $side={9} />
263249
);
264250
return (
@@ -288,7 +274,7 @@ function PermissionTagRender(props: CustomTagProps) {
288274
color={value}
289275
closable={closable}
290276
onClose={onClose}
291-
style={{ marginRight: 3 }}
277+
style={{ marginRight: 3, display: "flex", alignItems: "center" }}
292278
>
293279
{label}
294280
</StyledTag>
@@ -309,12 +295,52 @@ const PermissionSelector = (props: {
309295
filterItems: PermissionItem[];
310296
supportRoles: { label: string; value: PermissionRole }[];
311297
}) => {
312-
const orgGroups = useSelector(getOrgGroups);
313-
const orgUsers = useSelector(getOrgUsers);
314298
const { selectedItems, setSelectRole, setSelectedItems, user } = props;
315-
const optionViews = getPermissionOptionView(orgGroups, orgUsers, user, props.filterItems);
316299
const [roleSelectVisible, setRoleSelectVisible] = useState(false);
317300
const selectRef = useRef<HTMLDivElement>(null);
301+
const [optionViews, setOptionViews] = useState<AddAppOptionView[]>()
302+
const [searchValue, setSearchValue] = useState("");
303+
const [isLoading, setIsLoading] = useState(false);
304+
const application = useSelector(currentApplication)
305+
306+
const debouncedUserSearch = useCallback(
307+
debounce((searchTerm: string) => {
308+
if (!application) return;
309+
310+
setIsLoading(true);
311+
fetchAvailableGroupsMembers(application.applicationId, searchTerm).then(res => {
312+
if(res.success) {
313+
setOptionViews(getPermissionOptionView(res.data, props.filterItems))
314+
}
315+
setIsLoading(false);
316+
}).catch(() => {
317+
setIsLoading(false);
318+
});
319+
}, 500),
320+
[application, props.filterItems]
321+
);
322+
323+
useEffect(() => {
324+
debouncedUserSearch(searchValue);
325+
326+
return () => {
327+
debouncedUserSearch.cancel();
328+
};
329+
}, [searchValue, debouncedUserSearch]);
330+
331+
useEffect(() => {
332+
if (!application) return;
333+
334+
setIsLoading(true);
335+
fetchAvailableGroupsMembers(application.applicationId, "").then(res => {
336+
if(res.success) {
337+
setOptionViews(getPermissionOptionView(res.data, props.filterItems))
338+
}
339+
setIsLoading(false);
340+
}).catch(() => {
341+
setIsLoading(false);
342+
});
343+
}, [application, props.filterItems]);
318344

319345
useEffect(() => {
320346
setRoleSelectVisible(selectedItems.length > 0);
@@ -325,12 +351,18 @@ const PermissionSelector = (props: {
325351

326352
return (
327353
<>
354+
<Search
355+
placeholder={trans("home.addPermissionPlaceholder")}
356+
value={searchValue}
357+
onChange={(e) => setSearchValue(e.target.value)}
358+
/>
328359
<PermissionSelectWrapper>
329360
<AddPermissionsSelect
330361
open
331362
ref={selectRef}
332-
placeholder={trans("home.addPermissionPlaceholder")}
363+
placeholder={trans("home.selectedUsersAndGroups")}
333364
mode="multiple"
365+
showSearch={false}
334366
getPopupContainer={() => document.getElementById("add-app-user-permission-dropdown")!}
335367
optionLabelProp="label"
336368
tagRender={PermissionTagRender}
@@ -350,7 +382,7 @@ const PermissionSelector = (props: {
350382
setSelectedItems(selectedItems.filter((item) => item.key !== option.key));
351383
}}
352384
>
353-
{optionViews.map((view) => {
385+
{optionViews?.map((view) => {
354386
return (
355387
<CustomSelect.Option
356388
key={`${view.type}-${view.id}`}
@@ -395,16 +427,10 @@ export const Permission = (props: {
395427
addPermission: (userIds: string[], groupIds: string[], role: string) => void;
396428
}) => {
397429
const { onCancel } = props;
398-
const dispatch = useDispatch();
399430
const user = useSelector(getUser);
400431
const [selectRole, setSelectRole] = useState<ApplicationRoleType>("viewer");
401432
const [selectedItems, setSelectedItems] = useState<PermissionAddEntity[]>([]);
402433

403-
useEffect(() => {
404-
dispatch(fetchOrgUsersAction(user.currentOrgId));
405-
dispatch(fetchGroupsAction(user.currentOrgId));
406-
}, []);
407-
408434
return (
409435
<AddAppUserContent>
410436
<CommonTextLabel style={{ marginTop: "16px" }}>
@@ -426,10 +452,10 @@ export const Permission = (props: {
426452
buttonType="primary"
427453
onClick={() => {
428454
const uids = selectedItems
429-
.filter((item) => item.type === "USER")
455+
.filter((item) => item.type === "User")
430456
.map((item) => item.id);
431457
const gids = selectedItems
432-
.filter((item) => item.type === "GROUP")
458+
.filter((item) => item.type === "Group")
433459
.map((item) => item.id);
434460
if (uids.length === 0 && gids.length === 0) {
435461
onCancel();

client/packages/lowcoder/src/comps/comps/buttonComp/buttonComp.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
ButtonCompWrapper,
2222
buttonRefMethods,
2323
ButtonStyleControl,
24+
DisabledButtonStyleControl,
2425
} from "./buttonCompConstants";
2526
import { RefControl } from "comps/controls/refControl";
2627
import { Tooltip } from "antd";
@@ -133,6 +134,7 @@ const childrenMap = {
133134
prefixIcon: IconControl,
134135
suffixIcon: IconControl,
135136
style: ButtonStyleControl,
137+
disabledStyle: DisabledButtonStyleControl,
136138
animationStyle: styleControl(AnimationStyle, 'animationStyle'),
137139
viewRef: RefControl<HTMLElement>,
138140
tooltip: StringControl
@@ -173,6 +175,7 @@ const ButtonPropertyView = React.memo((props: {
173175
{props.children.suffixIcon.propertyView({ label: trans("button.suffixIcon") })}
174176
</Section>
175177
<Section name={sectionNames.style}>{props.children.style.getPropertyView()}</Section>
178+
<Section name={trans("prop.disabledStyle")}>{props.children.disabledStyle.getPropertyView()}</Section>
176179
</>
177180
)}
178181
</>
@@ -212,6 +215,7 @@ const ButtonView = React.memo((props: ToViewReturn<ChildrenType>) => {
212215
<Button100
213216
ref={props.viewRef}
214217
$buttonStyle={props.style}
218+
$disabledStyle={props.disabledStyle}
215219
loading={props.loading}
216220
disabled={
217221
props.disabled ||

0 commit comments

Comments
 (0)