Skip to content

Commit 19c99c7

Browse files
committed
add managed/unmanged workspaces
1 parent c96b993 commit 19c99c7

File tree

3 files changed

+98
-33
lines changed

3 files changed

+98
-33
lines changed

client/packages/lowcoder/src/pages/setting/environments/EnvironmentDetail.tsx

+61-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from "react";
1+
import React, {useState} from "react";
22
import { useParams } from "react-router-dom";
33
import {
44
Spin,
@@ -13,6 +13,7 @@ import {
1313
Button,
1414
Statistic,
1515
Divider,
16+
message
1617
} from "antd";
1718
import {
1819
ReloadOutlined,
@@ -29,6 +30,8 @@ import { useEnvironmentWorkspaces } from "./hooks/useEnvironmentWorkspaces";
2930
import { useEnvironmentUserGroups } from "./hooks/useEnvironmentUserGroups";
3031
import { useManagedWorkspaces } from "./hooks/enterprise/useManagedWorkspaces";
3132
import { getMergedWorkspaces } from "./utils/getMergedWorkspaces";
33+
import { Workspace } from "./types/workspace.types";
34+
import { connectManagedWorkspace, unconnectManagedWorkspace } from "./services/enterprise.service";
3235

3336

3437
const { Title, Text } = Typography;
@@ -38,6 +41,12 @@ const { TabPane } = Tabs;
3841
* Environment Detail Page Component
3942
* Shows detailed information about a specific environment
4043
*/
44+
45+
type WorkspaceStats = {
46+
total: number;
47+
managed: number;
48+
unmanaged: number;
49+
};
4150
const EnvironmentDetail: React.FC = () => {
4251
// Get environment ID from URL params
4352
const {
@@ -72,6 +81,22 @@ const EnvironmentDetail: React.FC = () => {
7281

7382
// Use the custom hook to handle data fetching and state management
7483
// Use the custom hook to handle data fetching and state management
84+
85+
const [mergedWorkspaces, setMergedWorkspaces] = useState<Workspace[]>([]);
86+
const [workspaceStats, setWorkspaceStats] = useState<WorkspaceStats>({
87+
total: 0,
88+
managed: 0,
89+
unmanaged: 0,
90+
});
91+
92+
93+
React.useEffect(() => {
94+
if (workspaces && managedWorkspaces) {
95+
const { merged, stats } = getMergedWorkspaces(workspaces, managedWorkspaces);
96+
setMergedWorkspaces(merged);
97+
setWorkspaceStats(stats);
98+
}
99+
}, [workspaces, managedWorkspaces]);
75100

76101
// If loading, show spinner
77102
if (envLoading) {
@@ -121,7 +146,39 @@ const EnvironmentDetail: React.FC = () => {
121146
);
122147
}
123148

124-
const { merged, stats: workspaceStats } = getMergedWorkspaces(workspaces, managedWorkspaces);
149+
const { merged, stats: initialStats } = getMergedWorkspaces(workspaces, managedWorkspaces);
150+
151+
152+
153+
const handleToggleManaged = async (workspace: Workspace, checked: boolean) => {
154+
try {
155+
console.log("WORKSPACE", workspace);
156+
if (checked) {
157+
await connectManagedWorkspace(environment.environmentId, workspace.name, workspace.gid!);
158+
} else {
159+
await unconnectManagedWorkspace(workspace.gid!);
160+
}
161+
162+
// Optimistically update the local state
163+
const updatedList = mergedWorkspaces.map((w) =>
164+
w.id === workspace.id ? { ...w, managed: checked } : w
165+
);
166+
167+
const updatedManagedCount = updatedList.filter((w) => w.managed).length;
168+
169+
setMergedWorkspaces(updatedList);
170+
setWorkspaceStats({
171+
total: updatedList.length,
172+
managed: updatedManagedCount,
173+
unmanaged: updatedList.length - updatedManagedCount,
174+
});
175+
176+
message.success(`${workspace.name} is now ${checked ? 'Managed' : 'Unmanaged'}`);
177+
} catch (err) {
178+
message.error(`Failed to toggle managed state for ${workspace.name}`);
179+
}
180+
};
181+
125182

126183
return (
127184
<div className="environment-detail-container" style={{ padding: "24px" }}>
@@ -297,10 +354,11 @@ const EnvironmentDetail: React.FC = () => {
297354

298355
{/* Workspaces List */}
299356
<WorkspacesList
300-
workspaces={merged}
357+
workspaces={mergedWorkspaces} // ⬅️ Use local state!
301358
loading={workspacesLoading && !workspacesError}
302359
error={workspacesError}
303360
environmentId={environment.environmentId}
361+
onToggleManaged={handleToggleManaged} // ⬅️ Add this to enable toggles
304362
/>
305363
</Card>
306364
</TabPane>

client/packages/lowcoder/src/pages/setting/environments/components/WorkspacesList.tsx

+24-15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react';
2-
import { Table, Tag, Empty, Spin } from 'antd';
2+
import { Table, Tag, Empty, Spin, Switch, Space } from 'antd';
33
import { Workspace } from '../types/workspace.types';
44
import history from '@lowcoder-ee/util/history';
55
import { buildEnvironmentWorkspaceId } from '@lowcoder-ee/constants/routesURL';
@@ -9,18 +9,18 @@ interface WorkspacesListProps {
99
loading: boolean;
1010
error?: string | null;
1111
environmentId: string;
12+
onToggleManaged?: (workspace: Workspace, checked: boolean) => void;
13+
refreshing?: boolean;
1214
}
1315

14-
/**
15-
* Component to display a list of workspaces in a table
16-
*/
1716
const WorkspacesList: React.FC<WorkspacesListProps> = ({
1817
workspaces,
1918
loading,
2019
error,
2120
environmentId,
21+
onToggleManaged,
22+
refreshing = false,
2223
}) => {
23-
// Format timestamp to date string
2424
const formatDate = (timestamp?: number): string => {
2525
if (!timestamp) return 'N/A';
2626
const date = new Date(timestamp);
@@ -31,7 +31,6 @@ const WorkspacesList: React.FC<WorkspacesListProps> = ({
3131
history.push(`${buildEnvironmentWorkspaceId(environmentId, workspace.id)}`);
3232
};
3333

34-
// Table columns definition
3534
const columns = [
3635
{
3736
title: 'Name',
@@ -48,9 +47,7 @@ const WorkspacesList: React.FC<WorkspacesListProps> = ({
4847
title: 'Role',
4948
dataIndex: 'role',
5049
key: 'role',
51-
render: (role: string) => (
52-
<span>{role}</span>
53-
),
50+
render: (role: string) => <span>{role}</span>,
5451
},
5552
{
5653
title: 'Creation Date',
@@ -71,14 +68,27 @@ const WorkspacesList: React.FC<WorkspacesListProps> = ({
7168
title: 'Managed',
7269
key: 'managed',
7370
render: (record: Workspace) => (
74-
<Tag color={record.managed ? 'green' : 'default'}>
75-
{record.managed ? 'Managed' : 'Unmanaged'}
76-
</Tag>
71+
<Space>
72+
<Tag color={record.managed ? 'green' : 'default'}>
73+
{record.managed ? 'Managed' : 'Unmanaged'}
74+
</Tag>
75+
{onToggleManaged && (
76+
<Switch
77+
size="small"
78+
checked={record.managed}
79+
loading={refreshing}
80+
onClick={(checked,e) => {
81+
e.stopPropagation(); // ✅ THIS STOPS the row from being triggered
82+
onToggleManaged(record, checked);
83+
}}
84+
onChange={() => {}}
85+
/>
86+
)}
87+
</Space>
7788
),
7889
},
7990
];
8091

81-
// If loading, show spinner
8292
if (loading) {
8393
return (
8494
<div style={{ display: 'flex', justifyContent: 'center', padding: '20px' }}>
@@ -87,7 +97,6 @@ const WorkspacesList: React.FC<WorkspacesListProps> = ({
8797
);
8898
}
8999

90-
// If no workspaces or error, show empty state
91100
if (!workspaces || workspaces.length === 0 || error) {
92101
return (
93102
<Empty
@@ -106,7 +115,7 @@ const WorkspacesList: React.FC<WorkspacesListProps> = ({
106115
size="middle"
107116
onRow={(record) => ({
108117
onClick: () => handleRowClick(record),
109-
style: { cursor: 'pointer' }, // Add pointer cursor to indicate clickable rows
118+
style: { cursor: 'pointer' },
110119
})}
111120
/>
112121
);

client/packages/lowcoder/src/pages/setting/environments/services/enterprise.service.ts

+13-15
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,11 @@ export async function getManagedWorkspaces(
4141

4242
export async function connectManagedWorkspace(
4343
environmentId: string,
44-
apiServiceUrl: string,
4544
orgName: string,
46-
orgTags: string[] = []
45+
org_gid: string, // ✅ not optional
46+
orgTags: string[] = [],
4747
) {
48-
if (!environmentId || !apiServiceUrl || !orgName) {
48+
if (!environmentId || !orgName || !org_gid) {
4949
throw new Error("Missing required params to connect org");
5050
}
5151

@@ -54,9 +54,10 @@ export async function connectManagedWorkspace(
5454
environment_id: environmentId,
5555
org_name: orgName,
5656
org_tags: orgTags,
57+
org_gid,
5758
};
5859

59-
const res = await axios.post(`${apiServiceUrl}/api/plugins/enterprise/org`, payload);
60+
const res = await axios.post(`/api/plugins/enterprise/org`, payload);
6061
return res.data;
6162
} catch (err) {
6263
const errorMsg = err instanceof Error ? err.message : "Failed to connect org";
@@ -67,27 +68,24 @@ export async function connectManagedWorkspace(
6768

6869

6970

70-
71-
72-
7371
/**
7472
* Fetch workspaces for a specific environment
7573
* @param apiServiceUrl - API service URL for the environment
7674
* @param orgId - ID of the workspace
7775
*
7876
*/
79-
export async function unconnectManagedWorkspace(
80-
apiServiceUrl: string,
81-
orgId: string
82-
) {
83-
if (!apiServiceUrl || !orgId) {
84-
throw new Error("Missing apiServiceUrl or orgId");
77+
export async function unconnectManagedWorkspace(orgGid: string) {
78+
if (!orgGid) {
79+
throw new Error("Missing orgGid to unconnect workspace");
8580
}
8681

8782
try {
88-
await axios.delete(`${apiServiceUrl}/api/plugins/enterprise/org/${orgId}`);
83+
await axios.delete(`/api/plugins/enterprise/org`, {
84+
params: { orgGid }, // ✅ pass as query param
85+
});
8986
} catch (err) {
90-
const errorMsg = err instanceof Error ? err.message : "Failed to unconnect org";
87+
const errorMsg =
88+
err instanceof Error ? err.message : "Failed to unconnect org";
9189
message.error(errorMsg);
9290
throw err;
9391
}

0 commit comments

Comments
 (0)