Skip to content

Commit 2690ddf

Browse files
committed
add useWorkspaces hook that returns merged workspaces
1 parent aa27421 commit 2690ddf

File tree

3 files changed

+220
-69
lines changed

3 files changed

+220
-69
lines changed

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

Lines changed: 23 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -4,78 +4,32 @@ import { Card, Button, Row, Col, Statistic, Divider, Alert, message } from 'antd
44
import { ClusterOutlined, SyncOutlined } from '@ant-design/icons';
55
import Title from 'antd/lib/typography/Title';
66
import { Environment } from '../types/environment.types';
7+
import { useWorkspaces } from '../hooks/useWorkspaces';
8+
import WorkspacesList from './WorkspacesList';
79
import { Workspace } from '../types/workspace.types';
8-
import { useEnvironmentWorkspaces } from '../hooks/useEnvironmentWorkspaces';
9-
import { useManagedWorkspaces } from '../hooks/enterprise/useManagedWorkspaces';
10-
import WorkspacesList from './WorkspacesList';
11-
import { connectManagedWorkspace, unconnectManagedWorkspace } from '../services/enterprise.service';
12-
import { getMergedWorkspaces } from '../utils/getMergedWorkspaces';
13-
14-
interface WorkspaceStats {
15-
total: number;
16-
managed: number;
17-
unmanaged: number;
18-
}
1910

2011
interface WorkspacesTabProps {
2112
environment: Environment;
2213
}
2314

2415
const WorkspacesTab: React.FC<WorkspacesTabProps> = ({ environment }) => {
25-
// Keep the existing hooks for now - we'll optimize these later
16+
// Use the new hook that handles both regular and managed workspaces
2617
const {
2718
workspaces,
28-
loading: workspacesLoading,
29-
error: workspacesError,
30-
} = useEnvironmentWorkspaces(environment);
31-
32-
const {
33-
managedWorkspaces,
34-
managedLoading,
35-
managedError,
36-
} = useManagedWorkspaces(environment);
37-
38-
// Keep the merging logic for now - we'll optimize this later
39-
const [mergedWorkspaces, setMergedWorkspaces] = React.useState<Workspace[]>([]);
40-
const [workspaceStats, setWorkspaceStats] = React.useState<WorkspaceStats>({
41-
total: 0,
42-
managed: 0,
43-
unmanaged: 0,
44-
});
45-
46-
React.useEffect(() => {
47-
if (workspaces && managedWorkspaces) {
48-
const { merged, stats } = getMergedWorkspaces(workspaces, managedWorkspaces);
49-
setMergedWorkspaces(merged);
50-
setWorkspaceStats(stats);
51-
}
52-
}, [workspaces, managedWorkspaces]);
19+
stats,
20+
loading,
21+
error,
22+
toggleManagedStatus
23+
} = useWorkspaces(environment);
5324

54-
const handleToggleManaged = async (workspace: Workspace, checked: boolean) => {
55-
try {
56-
if (checked) {
57-
await connectManagedWorkspace(environment.environmentId, workspace.name, workspace.gid!);
58-
} else {
59-
await unconnectManagedWorkspace(workspace.gid!);
60-
}
61-
62-
// Optimistically update the local state
63-
const updatedList = mergedWorkspaces.map((w) =>
64-
w.id === workspace.id ? { ...w, managed: checked } : w
65-
);
66-
67-
const updatedManagedCount = updatedList.filter((w) => w.managed).length;
68-
69-
setMergedWorkspaces(updatedList);
70-
setWorkspaceStats({
71-
total: updatedList.length,
72-
managed: updatedManagedCount,
73-
unmanaged: updatedList.length - updatedManagedCount,
74-
});
75-
25+
const handleToggleManaged = async (workspace: Workspace, checked:boolean) => {
26+
const success = await toggleManagedStatus(workspace, checked);
27+
28+
if (success) {
7629
message.success(`${workspace.name} is now ${checked ? 'Managed' : 'Unmanaged'}`);
77-
} catch (err) {
30+
} else {
7831
message.error(`Failed to toggle managed state for ${workspace.name}`);
32+
// Optionally refresh to ensure UI is in sync with backend
7933
}
8034
};
8135

@@ -98,21 +52,21 @@ const WorkspacesTab: React.FC<WorkspacesTabProps> = ({ environment }) => {
9852
<Col span={8}>
9953
<Statistic
10054
title="Total Workspaces"
101-
value={workspaceStats.total}
55+
value={stats.total}
10256
prefix={<ClusterOutlined />}
10357
/>
10458
</Col>
10559
<Col span={8}>
10660
<Statistic
10761
title="Managed Workspaces"
108-
value={workspaceStats.managed}
62+
value={stats.managed}
10963
prefix={<ClusterOutlined />}
11064
/>
11165
</Col>
11266
<Col span={8}>
11367
<Statistic
11468
title="Unmanaged Workspaces"
115-
value={workspaceStats.unmanaged}
69+
value={stats.unmanaged}
11670
prefix={<ClusterOutlined />}
11771
/>
11872
</Col>
@@ -121,10 +75,10 @@ const WorkspacesTab: React.FC<WorkspacesTabProps> = ({ environment }) => {
12175
<Divider style={{ margin: "16px 0" }} />
12276

12377
{/* Show error if workspace loading failed */}
124-
{workspacesError && (
78+
{error && (
12579
<Alert
12680
message="Error loading workspaces"
127-
description={workspacesError}
81+
description={error}
12882
type="error"
12983
showIcon
13084
style={{ marginBottom: "16px" }}
@@ -133,7 +87,7 @@ const WorkspacesTab: React.FC<WorkspacesTabProps> = ({ environment }) => {
13387

13488
{(!environment.environmentApikey ||
13589
!environment.environmentApiServiceUrl) &&
136-
!workspacesError && (
90+
!error && (
13791
<Alert
13892
message="Configuration Issue"
13993
description={
@@ -149,9 +103,9 @@ const WorkspacesTab: React.FC<WorkspacesTabProps> = ({ environment }) => {
149103

150104
{/* Workspaces List */}
151105
<WorkspacesList
152-
workspaces={mergedWorkspaces}
153-
loading={workspacesLoading && !workspacesError}
154-
error={workspacesError}
106+
workspaces={workspaces}
107+
loading={loading && !error}
108+
error={error}
155109
environmentId={environment.environmentId}
156110
onToggleManaged={handleToggleManaged}
157111
/>
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// hooks/useWorkspaces.ts
2+
import { useState, useEffect, useCallback } from "react";
3+
import { getMergedEnvironmentWorkspaces, MergedWorkspacesResult } from "../services/workspace.service";
4+
import { connectManagedWorkspace, unconnectManagedWorkspace } from "../services/enterprise.service";
5+
import { Environment } from "../types/environment.types";
6+
import { Workspace } from "../types/workspace.types";
7+
8+
interface WorkspacesState extends MergedWorkspacesResult {
9+
loading: boolean;
10+
error: string | null;
11+
}
12+
13+
export const useWorkspaces = (environment: Environment | null) => {
14+
const [state, setState] = useState<WorkspacesState>({
15+
workspaces: [],
16+
stats: {
17+
total: 0,
18+
managed: 0,
19+
unmanaged: 0,
20+
},
21+
loading: false,
22+
error: null
23+
});
24+
25+
const fetchWorkspaces = useCallback(async () => {
26+
if (!environment) return;
27+
28+
setState(prev => ({ ...prev, loading: true, error: null }));
29+
30+
try {
31+
const { environmentId, environmentApikey, environmentApiServiceUrl } = environment;
32+
33+
// Validate required configuration
34+
if (!environmentApikey) {
35+
setState(prev => ({
36+
...prev,
37+
loading: false,
38+
error: "No API key configured for this environment. Workspaces cannot be fetched."
39+
}));
40+
return;
41+
}
42+
43+
if (!environmentApiServiceUrl) {
44+
setState(prev => ({
45+
...prev,
46+
loading: false,
47+
error: "No API service URL configured for this environment. Workspaces cannot be fetched."
48+
}));
49+
return;
50+
}
51+
52+
// Use the merged utility function
53+
const result = await getMergedEnvironmentWorkspaces(
54+
environmentId,
55+
environmentApikey,
56+
environmentApiServiceUrl
57+
);
58+
59+
// Update state with result
60+
setState({
61+
...result,
62+
loading: false,
63+
error: null
64+
});
65+
} catch (err) {
66+
setState(prev => ({
67+
...prev,
68+
loading: false,
69+
error: err instanceof Error ? err.message : "Failed to fetch workspaces"
70+
}));
71+
}
72+
}, [environment]);
73+
74+
useEffect(() => {
75+
if (environment) {
76+
fetchWorkspaces();
77+
}
78+
}, [environment, fetchWorkspaces]);
79+
80+
const toggleManagedStatus = async (workspace: Workspace, checked: boolean) => {
81+
try {
82+
if (!environment) return false;
83+
84+
if (checked) {
85+
await connectManagedWorkspace(environment.environmentId, workspace.name, workspace.gid!);
86+
} else {
87+
await unconnectManagedWorkspace(workspace.gid!);
88+
}
89+
90+
// Optimistically update the state
91+
setState(prev => {
92+
// Update workspaces with the new managed status
93+
const updatedWorkspaces = prev.workspaces.map(w =>
94+
w.id === workspace.id ? { ...w, managed: checked } : w
95+
);
96+
97+
// Recalculate stats
98+
const managedCount = updatedWorkspaces.filter(w => w.managed).length;
99+
100+
return {
101+
...prev,
102+
workspaces: updatedWorkspaces,
103+
stats: {
104+
total: updatedWorkspaces.length,
105+
managed: managedCount,
106+
unmanaged: updatedWorkspaces.length - managedCount
107+
}
108+
};
109+
});
110+
111+
return true; // Success indicator
112+
} catch (err) {
113+
return false; // Failure indicator
114+
}
115+
};
116+
117+
return {
118+
...state,
119+
toggleManagedStatus,
120+
};
121+
};
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// services/workspacesService.ts (or wherever makes sense in your structure)
2+
import { message } from "antd";
3+
import { getEnvironmentWorkspaces } from "./environments.service";
4+
import { getManagedWorkspaces } from "./enterprise.service";
5+
import { Workspace } from "../types/workspace.types";
6+
import { ManagedOrg } from "../types/enterprise.types";
7+
8+
export interface WorkspaceStats {
9+
total: number;
10+
managed: number;
11+
unmanaged: number;
12+
}
13+
14+
export interface MergedWorkspacesResult {
15+
workspaces: Workspace[];
16+
stats: WorkspaceStats;
17+
}
18+
19+
export async function getMergedEnvironmentWorkspaces(
20+
environmentId: string,
21+
apiKey: string,
22+
apiServiceUrl: string
23+
): Promise<MergedWorkspacesResult> {
24+
try {
25+
// First, get regular workspaces
26+
const regularWorkspaces = await getEnvironmentWorkspaces(
27+
environmentId,
28+
apiKey,
29+
apiServiceUrl
30+
);
31+
32+
// If no workspaces, return early with empty result
33+
if (!regularWorkspaces.length) {
34+
return {
35+
workspaces: [],
36+
stats: {
37+
total: 0,
38+
managed: 0,
39+
unmanaged: 0
40+
}
41+
};
42+
}
43+
44+
// Only fetch managed workspaces if we have regular workspaces
45+
let managedOrgs: ManagedOrg[] = [];
46+
try {
47+
managedOrgs = await getManagedWorkspaces(environmentId);
48+
} catch (error) {
49+
console.error("Failed to fetch managed workspaces:", error);
50+
// Continue with empty managed list
51+
}
52+
53+
// Merge the workspaces
54+
const mergedWorkspaces = regularWorkspaces.map(ws => ({
55+
...ws,
56+
managed: managedOrgs.some(org => org.orgGid === ws.gid)
57+
}));
58+
59+
// Calculate stats
60+
const managedCount = mergedWorkspaces.filter(ws => ws.managed).length;
61+
62+
return {
63+
workspaces: mergedWorkspaces,
64+
stats: {
65+
total: mergedWorkspaces.length,
66+
managed: managedCount,
67+
unmanaged: mergedWorkspaces.length - managedCount
68+
}
69+
};
70+
} catch (error) {
71+
const errorMessage =
72+
error instanceof Error ? error.message : "Failed to fetch workspaces";
73+
message.error(errorMessage);
74+
throw error;
75+
}
76+
}

0 commit comments

Comments
 (0)