Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a42f83c

Browse files
committed
testing workspaces endpoint
1 parent 035fc26 commit a42f83c

File tree

7 files changed

+185
-15
lines changed

7 files changed

+185
-15
lines changed

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

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Api from "api/api";
22
import { AxiosPromise } from "axios";
3-
import { OrgAndRole } from "constants/orgConstants";
3+
import { Org, OrgAndRole } from "constants/orgConstants";
44
import { BaseUserInfo, CurrentUser } from "constants/userConstants";
55
import { MarkUserStatusPayload, UpdateUserPayload } from "redux/reduxActions/userActions";
66
import { ApiResponse, GenericApiResponse } from "./apiResponses";
@@ -60,10 +60,21 @@ export interface FetchApiKeysResponse extends ApiResponse {
6060

6161
export type GetCurrentUserResponse = GenericApiResponse<CurrentUser>;
6262

63+
export interface GetMyOrgsResponse extends ApiResponse {
64+
data: {
65+
items: Org[];
66+
totalCount: number;
67+
currentPage: number;
68+
pageSize: number;
69+
hasMore: boolean;
70+
};
71+
}
72+
6373
class UserApi extends Api {
6474
static thirdPartyLoginURL = "/auth/tp/login";
6575
static thirdPartyBindURL = "/auth/tp/bind";
6676
static usersURL = "/users";
77+
static myOrgsURL = "/users/myorg";
6778
static sendVerifyCodeURL = "/auth/otp/send";
6879
static logoutURL = "/auth/logout";
6980
static userURL = "/users/me";
@@ -127,6 +138,19 @@ class UserApi extends Api {
127138
static getCurrentUser(): AxiosPromise<GetCurrentUserResponse> {
128139
return Api.get(UserApi.currentUserURL);
129140
}
141+
static getMyOrgs(
142+
page: number = 1,
143+
pageSize: number = 20,
144+
search?: string
145+
): AxiosPromise<GetMyOrgsResponse> {
146+
const params = new URLSearchParams({
147+
page: page.toString(),
148+
pageSize: pageSize.toString(),
149+
...(search && { search })
150+
});
151+
152+
return Api.get(`${UserApi.myOrgsURL}?${params}`);
153+
}
130154

131155
static getRawCurrentUser(): AxiosPromise<GetCurrentUserResponse> {
132156
return Api.get(UserApi.rawCurrentUserURL);

‎client/packages/lowcoder/src/constants/reduxActionConstants.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@ export const ReduxActionTypes = {
1111
FETCH_API_KEYS_SUCCESS: "FETCH_API_KEYS_SUCCESS",
1212
MOVE_TO_FOLDER2_SUCCESS: "MOVE_TO_FOLDER2_SUCCESS",
1313

14+
/* workspace RELATED */
15+
FETCH_WORKSPACES_INIT: "FETCH_WORKSPACES_INIT",
16+
FETCH_WORKSPACES_SUCCESS: "FETCH_WORKSPACES_SUCCESS",
17+
FETCH_WORKSPACES_ERROR: "FETCH_WORKSPACES_ERROR",
18+
LOAD_MORE_WORKSPACES_SUCCESS: "LOAD_MORE_WORKSPACES_SUCCESS",
19+
SEARCH_WORKSPACES_INIT: "SEARCH_WORKSPACES_INIT",
20+
21+
1422
/* plugin RELATED */
1523
FETCH_DATA_SOURCE_TYPES: "FETCH_DATA_SOURCE_TYPES",
1624
FETCH_DATA_SOURCE_TYPES_SUCCESS: "FETCH_DATA_SOURCE_TYPES_SUCCESS",

‎client/packages/lowcoder/src/pages/common/profileDropdown.tsx

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ import {
1717
SearchIcon,
1818
} from "lowcoder-design";
1919
import ProfileSettingModal from "pages/setting/profile";
20-
import React, { useMemo, useState } from "react";
20+
import React, { useEffect, useMemo, useState } from "react";
2121
import { useDispatch, useSelector } from "react-redux";
22-
import { createOrgAction, switchOrg } from "redux/reduxActions/orgActions";
22+
import { createOrgAction, fetchWorkspacesAction, switchOrg } from "redux/reduxActions/orgActions";
2323
import styled from "styled-components";
2424
import history from "util/history";
2525
import ProfileImage from "pages/common/profileImage";
@@ -30,6 +30,7 @@ import { showSwitchOrg } from "@lowcoder-ee/pages/common/customerService";
3030
import { checkIsMobile } from "util/commonUtils";
3131
import { selectSystemConfig } from "redux/selectors/configSelectors";
3232
import type { ItemType } from "antd/es/menu/interface";
33+
import { getCurrentOrg, getWorkspaces } from "@lowcoder-ee/redux/selectors/orgSelectors";
3334

3435
const { Item } = Menu;
3536

@@ -219,24 +220,29 @@ type DropDownProps = {
219220
};
220221

221222
export default function ProfileDropdown(props: DropDownProps) {
222-
const { avatarUrl, username, orgs, currentOrgId } = props.user;
223+
const { avatarUrl, username, currentOrgId } = props.user;
223224
const currentOrgRoleId = props.user.orgRoleMap.get(currentOrgId);
224-
const currentOrg = useMemo(
225-
() => props.user.orgs.find((o) => o.id === currentOrgId),
226-
[props.user, currentOrgId]
227-
);
225+
const currentOrg = useSelector(getCurrentOrg);
226+
const workspaces = useSelector(getWorkspaces);
228227
const settingModalVisible = useSelector(isProfileSettingModalVisible);
229228
const sysConfig = useSelector(selectSystemConfig);
230229
const dispatch = useDispatch();
231230
const [searchTerm, setSearchTerm] = useState("");
232231
const [dropdownVisible, setDropdownVisible] = useState(false);
233232

233+
// Load workspaces when dropdown opens
234+
useEffect(() => {
235+
if (dropdownVisible && workspaces.items.length === 0) {
236+
dispatch(fetchWorkspacesAction(1));
237+
}
238+
}, [dropdownVisible]);
239+
// Use workspaces.items instead of props.user.orgs
234240
const filteredOrgs = useMemo(() => {
235-
if (!searchTerm.trim()) return orgs;
236-
return orgs.filter(org =>
241+
if (!searchTerm.trim()) return workspaces.items;
242+
return workspaces.items.filter(org =>
237243
org.name.toLowerCase().includes(searchTerm.toLowerCase())
238244
);
239-
}, [orgs, searchTerm]);
245+
}, [workspaces.items, searchTerm]);
240246

241247
const handleProfileClick = () => {
242248
if (checkIsMobile(window.innerWidth)) {
@@ -266,7 +272,7 @@ export default function ProfileDropdown(props: DropDownProps) {
266272
};
267273

268274
const handleCreateOrg = () => {
269-
dispatch(createOrgAction(orgs));
275+
dispatch(createOrgAction(workspaces.items));
270276
history.push(ORGANIZATION_SETTING);
271277
setDropdownVisible(false);
272278
};
@@ -293,11 +299,11 @@ export default function ProfileDropdown(props: DropDownProps) {
293299
</ProfileSection>
294300

295301
{/* Workspaces Section */}
296-
{orgs && orgs.length > 0 && showSwitchOrg(props.user, sysConfig) && (
302+
{workspaces.items.length > 0 && showSwitchOrg(props.user, sysConfig) && (
297303
<WorkspaceSection>
298304
<SectionHeader>{trans("profile.switchOrg")}</SectionHeader>
299305

300-
{orgs.length > 3 && (
306+
{workspaces.items.length > 3 && (
301307
<SearchContainer>
302308
<StyledSearchInput
303309
placeholder="Search workspaces..."

‎client/packages/lowcoder/src/redux/reducers/uiReducers/usersReducer.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Org } from "@lowcoder-ee/constants/orgConstants";
12
import {
23
ReduxAction,
34
ReduxActionErrorTypes,
@@ -21,6 +22,15 @@ const initialState: UsersReduxState = {
2122
rawCurrentUser: defaultCurrentUser,
2223
profileSettingModalVisible: false,
2324
apiKeys: [],
25+
workspaces: {
26+
items: [],
27+
currentPage: 1,
28+
pageSize: 20,
29+
totalCount: 0,
30+
hasMore: false,
31+
loading: false,
32+
searchQuery: ""
33+
}
2434
};
2535

2636
const usersReducer = createReducer(initialState, {
@@ -190,6 +200,53 @@ const usersReducer = createReducer(initialState, {
190200
...state,
191201
apiKeys: action.payload,
192202
}),
203+
204+
205+
[ReduxActionTypes.FETCH_WORKSPACES_INIT]: (state: UsersReduxState) => ({
206+
...state,
207+
workspaces: {
208+
...state.workspaces,
209+
loading: true
210+
}
211+
}),
212+
213+
[ReduxActionTypes.FETCH_WORKSPACES_SUCCESS]: (
214+
state: UsersReduxState,
215+
action: ReduxAction<any>
216+
) => ({
217+
...state,
218+
workspaces: {
219+
items: action.payload.items,
220+
currentPage: action.payload.currentPage,
221+
pageSize: action.payload.pageSize,
222+
totalCount: action.payload.totalCount,
223+
hasMore: action.payload.hasMore,
224+
loading: false,
225+
searchQuery: action.payload.searchQuery
226+
}
227+
}),
228+
229+
[ReduxActionTypes.LOAD_MORE_WORKSPACES_SUCCESS]: (
230+
state: UsersReduxState,
231+
action: ReduxAction<any>
232+
) => ({
233+
...state,
234+
workspaces: {
235+
...state.workspaces,
236+
items: [...state.workspaces.items, ...action.payload.items], // Append new items
237+
currentPage: action.payload.currentPage,
238+
hasMore: action.payload.hasMore,
239+
loading: false
240+
}
241+
}),
242+
243+
[ReduxActionTypes.FETCH_WORKSPACES_ERROR]: (state: UsersReduxState) => ({
244+
...state,
245+
workspaces: {
246+
...state.workspaces,
247+
loading: false
248+
}
249+
}),
193250
});
194251

195252
export interface UsersReduxState {
@@ -205,6 +262,18 @@ export interface UsersReduxState {
205262
error: string;
206263
profileSettingModalVisible: boolean;
207264
apiKeys: Array<ApiKey>;
265+
266+
// NEW state for workspaces
267+
// NEW: Separate workspace state
268+
workspaces: {
269+
items: Org[]; // Current page of workspaces
270+
currentPage: number; // Which page we're on
271+
pageSize: number; // Items per page (e.g., 20)
272+
totalCount: number; // Total workspaces available
273+
hasMore: boolean; // Are there more pages?
274+
loading: boolean; // Loading state
275+
searchQuery: string; // Current search term
276+
};
208277
}
209278

210279
export default usersReducer;

‎client/packages/lowcoder/src/redux/reduxActions/orgActions.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,4 +191,14 @@ export const fetchLastMonthAPIUsageActionSuccess = (payload: OrgLastMonthAPIUsag
191191
type: ReduxActionTypes.FETCH_ORG_LAST_MONTH_API_USAGE_SUCCESS,
192192
payload: payload,
193193
};
194-
};
194+
};
195+
196+
export const fetchWorkspacesAction = (page: number = 1, search?: string) => ({
197+
type: ReduxActionTypes.FETCH_WORKSPACES_INIT,
198+
payload: { page, search }
199+
});
200+
201+
export const loadMoreWorkspacesAction = (page: number, search?: string) => ({
202+
type: ReduxActionTypes.FETCH_WORKSPACES_INIT,
203+
payload: { page, search, isLoadMore: true }
204+
});

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import { getUser } from "redux/selectors/usersSelectors";
3030
import { validateResponse } from "api/apiUtils";
3131
import { User } from "constants/userConstants";
3232
import { getUserSaga } from "redux/sagas/userSagas";
33+
import { GetMyOrgsResponse } from "@lowcoder-ee/api/userApi";
34+
import UserApi from "@lowcoder-ee/api/userApi";
3335

3436
export function* updateGroupSaga(action: ReduxAction<UpdateGroupActionPayload>) {
3537
try {
@@ -324,6 +326,43 @@ export function* fetchLastMonthAPIUsageSaga(action: ReduxAction<{
324326
}
325327
}
326328

329+
// fetch my orgs
330+
// In userSagas.ts
331+
export function* fetchWorkspacesSaga(action: ReduxAction<{page: number, search?: string, isLoadMore?: boolean}>) {
332+
try {
333+
const { page, search, isLoadMore } = action.payload;
334+
335+
const response: AxiosResponse<GetMyOrgsResponse> = yield call(
336+
UserApi.getMyOrgs,
337+
page,
338+
20, // pageSize
339+
search
340+
);
341+
342+
if (validateResponse(response)) {
343+
const actionType = isLoadMore
344+
? ReduxActionTypes.LOAD_MORE_WORKSPACES_SUCCESS
345+
: ReduxActionTypes.FETCH_WORKSPACES_SUCCESS;
346+
347+
yield put({
348+
type: actionType,
349+
payload: {
350+
items: response.data.data.items,
351+
totalCount: response.data.data.totalCount,
352+
currentPage: response.data.data.currentPage,
353+
pageSize: response.data.data.pageSize,
354+
hasMore: response.data.data.hasMore,
355+
searchQuery: search || ""
356+
}
357+
});
358+
}
359+
} catch (error: any) {
360+
yield put({
361+
type: ReduxActionTypes.FETCH_WORKSPACES_ERROR,
362+
});
363+
}
364+
}
365+
327366
export default function* orgSagas() {
328367
yield all([
329368
takeLatest(ReduxActionTypes.UPDATE_GROUP_INFO, updateGroupSaga),
@@ -343,5 +382,8 @@ export default function* orgSagas() {
343382
takeLatest(ReduxActionTypes.UPDATE_ORG, updateOrgSaga),
344383
takeLatest(ReduxActionTypes.FETCH_ORG_API_USAGE, fetchAPIUsageSaga),
345384
takeLatest(ReduxActionTypes.FETCH_ORG_LAST_MONTH_API_USAGE, fetchLastMonthAPIUsageSaga),
385+
takeLatest(ReduxActionTypes.FETCH_WORKSPACES_INIT, fetchWorkspacesSaga),
386+
387+
346388
]);
347389
}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { Org } from "@lowcoder-ee/constants/orgConstants";
2+
import { getUser } from "./usersSelectors";
13
import { AppState } from "redux/reducers";
24

35
export const getOrgUsers = (state: AppState) => {
@@ -27,3 +29,12 @@ export const getOrgApiUsage = (state: AppState) => {
2729
export const getOrgLastMonthApiUsage = (state: AppState) => {
2830
return state.ui.org.lastMonthApiUsage;
2931
}
32+
33+
// Add to usersSelectors.ts
34+
export const getWorkspaces = (state: AppState) => state.ui.users.workspaces;
35+
36+
export const getCurrentOrg = (state: AppState): Org | undefined => {
37+
const user = getUser(state);
38+
const workspaces = getWorkspaces(state);
39+
return workspaces.items.find(org => org.id === user.currentOrgId);
40+
};

0 commit comments

Comments
 (0)