Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit0a1879b

Browse files
committed
Seperate DS tab
1 parentc1a74fe commit0a1879b

File tree

2 files changed

+282
-4
lines changed

2 files changed

+282
-4
lines changed

‎client/packages/lowcoder/src/pages/setting/environments/WorkspaceDetail.tsx‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import { dataSourcesConfig } from "./config/data-sources.config";
3535
import{queryConfig}from"./config/query.config";
3636

3737
importAppsTabfrom"./components/AppsTab";
38+
importDataSourcesTabfrom"./components/DataSourcesTab";
3839
const{ Title, Text}=Typography;
3940
const{ TabPane}=Tabs;
4041

@@ -170,11 +171,9 @@ const WorkspaceDetail: React.FC = () => {
170171
</TabPane>
171172

172173
<TabPanetab={<span><DatabaseOutlined/> Data Sources</span>}key="dataSources">
173-
<DeployableItemsTab
174+
<DataSourcesTab
174175
environment={environment}
175-
config={dataSourcesConfig}
176-
additionalParams={{workspaceId:workspace.id}}
177-
title="Data Sources in this Workspace"
176+
workspace={workspace}
178177
/>
179178
</TabPane>
180179

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
importReact,{useState,useEffect}from'react';
2+
import{Card,Button,Divider,Alert,message,Table,Tag,Input,Space,Tooltip}from'antd';
3+
import{SyncOutlined,CloudUploadOutlined,DatabaseOutlined}from'@ant-design/icons';
4+
importTitlefrom'antd/lib/typography/Title';
5+
import{Environment}from'../types/environment.types';
6+
import{Workspace}from'../types/workspace.types';
7+
import{DataSource}from'../types/datasource.types';
8+
import{getMergedWorkspaceDataSources}from'../services/datasources.service';
9+
import{Switch,Spin,Empty}from'antd';
10+
import{ManagedObjectType,setManagedObject,unsetManagedObject}from'../services/managed-objects.service';
11+
import{useDeployModal}from'../context/DeployModalContext';
12+
import{dataSourcesConfig}from'../config/data-sources.config';
13+
14+
const{ Search}=Input;
15+
16+
interfaceDataSourcesTabProps{
17+
environment:Environment;
18+
workspace:Workspace;
19+
}
20+
21+
constDataSourcesTab:React.FC<DataSourcesTabProps>=({ environment, workspace})=>{
22+
const[dataSources,setDataSources]=useState<DataSource[]>([]);
23+
const[stats,setStats]=useState({
24+
total:0,
25+
types:0,
26+
managed:0,
27+
unmanaged:0
28+
});
29+
const[loading,setLoading]=useState(false);
30+
const[refreshing,setRefreshing]=useState(false);
31+
const[error,setError]=useState<string|null>(null);
32+
const[searchText,setSearchText]=useState('');
33+
const{ openDeployModal}=useDeployModal();
34+
35+
// Fetch data sources
36+
constfetchDataSources=async()=>{
37+
if(!workspace.id||!environment)return;
38+
39+
setLoading(true);
40+
setError(null);
41+
42+
try{
43+
constresult=awaitgetMergedWorkspaceDataSources(
44+
workspace.id,
45+
environment.environmentId,
46+
environment.environmentApikey,
47+
environment.environmentApiServiceUrl!
48+
);
49+
50+
setDataSources(result.dataSources);
51+
setStats(result.stats);
52+
}catch(err){
53+
setError(errinstanceofError ?err.message :"Failed to fetch data sources");
54+
}finally{
55+
setLoading(false);
56+
setRefreshing(false);
57+
}
58+
};
59+
60+
useEffect(()=>{
61+
fetchDataSources();
62+
},[environment,workspace]);
63+
64+
// Handle refresh
65+
consthandleRefresh=()=>{
66+
setRefreshing(true);
67+
fetchDataSources();
68+
};
69+
70+
// Toggle managed status
71+
consthandleToggleManaged=async(dataSource:DataSource,checked:boolean)=>{
72+
setRefreshing(true);
73+
try{
74+
if(checked){
75+
awaitsetManagedObject(
76+
dataSource.gid,
77+
environment.environmentId,
78+
ManagedObjectType.DATASOURCE,
79+
dataSource.name
80+
);
81+
}else{
82+
awaitunsetManagedObject(
83+
dataSource.gid,
84+
environment.environmentId,
85+
ManagedObjectType.DATASOURCE
86+
);
87+
}
88+
89+
// Update the data source in state
90+
constupdatedDataSources=dataSources.map(item=>{
91+
if(item.id===dataSource.id){
92+
return{ ...item,managed:checked};
93+
}
94+
returnitem;
95+
});
96+
97+
setDataSources(updatedDataSources);
98+
99+
// Update stats
100+
constmanaged=updatedDataSources.filter(ds=>ds.managed).length;
101+
setStats(prev=>({
102+
...prev,
103+
managed,
104+
unmanaged:prev.total-managed
105+
}));
106+
107+
message.success(`${dataSource.name} is now${checked ?'Managed' :'Unmanaged'}`);
108+
returntrue;
109+
}catch(error){
110+
message.error(`Failed to change managed status for${dataSource.name}`);
111+
returnfalse;
112+
}finally{
113+
setRefreshing(false);
114+
}
115+
};
116+
117+
// Filter data sources based on search
118+
constfilteredDataSources=searchText
119+
?dataSources.filter(ds=>
120+
ds.name.toLowerCase().includes(searchText.toLowerCase())||
121+
ds.id.toString().toLowerCase().includes(searchText.toLowerCase()))
122+
:dataSources;
123+
124+
// Table columns
125+
constcolumns=[
126+
{
127+
title:'Name',
128+
dataIndex:'name',
129+
key:'name',
130+
render:(text:string)=><spanclassName="datasource-name">{text}</span>
131+
},
132+
{
133+
title:'ID',
134+
dataIndex:'id',
135+
key:'id',
136+
ellipsis:true,
137+
},
138+
{
139+
title:'Type',
140+
dataIndex:'type',
141+
key:'type',
142+
render:(type:string)=>(
143+
<Tagcolor="blue">{type}</Tag>
144+
),
145+
},
146+
{
147+
title:'Managed',
148+
key:'managed',
149+
render:(_:any,dataSource:DataSource)=>(
150+
<Switch
151+
checked={!!dataSource.managed}
152+
onChange={(checked:boolean)=>handleToggleManaged(dataSource,checked)}
153+
loading={refreshing}
154+
size="small"
155+
/>
156+
),
157+
},
158+
{
159+
title:'Actions',
160+
key:'actions',
161+
render:(_:any,dataSource:DataSource)=>(
162+
<SpaceonClick={(e)=>e.stopPropagation()}>
163+
<Tooltiptitle={!dataSource.managed ?"Data source must be managed before it can be deployed" :"Deploy this data source to another environment"}>
164+
<Button
165+
type="primary"
166+
size="small"
167+
icon={<CloudUploadOutlined/>}
168+
onClick={()=>openDeployModal(dataSource,dataSourcesConfig,environment)}
169+
disabled={!dataSource.managed}
170+
>
171+
Deploy
172+
</Button>
173+
</Tooltip>
174+
</Space>
175+
),
176+
}
177+
];
178+
179+
return(
180+
<Card>
181+
{/* Header with refresh button */}
182+
<divstyle={{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:"16px"}}>
183+
<Titlelevel={5}>Data Sources in this Workspace</Title>
184+
<Button
185+
icon={<SyncOutlinedspin={refreshing}/>}
186+
onClick={handleRefresh}
187+
loading={loading}
188+
>
189+
Refresh
190+
</Button>
191+
</div>
192+
193+
{/* Stats display */}
194+
<divstyle={{display:'flex',flexWrap:'wrap',gap:'24px',marginBottom:'16px'}}>
195+
<div>
196+
<divstyle={{fontSize:'14px',color:'#8c8c8c'}}>Total Data Sources</div>
197+
<divstyle={{fontSize:'24px',fontWeight:600}}>{stats.total}</div>
198+
</div>
199+
<div>
200+
<divstyle={{fontSize:'14px',color:'#8c8c8c'}}>Types</div>
201+
<divstyle={{fontSize:'24px',fontWeight:600}}>{stats.types}</div>
202+
</div>
203+
<div>
204+
<divstyle={{fontSize:'14px',color:'#8c8c8c'}}>Managed</div>
205+
<divstyle={{fontSize:'24px',fontWeight:600}}>{stats.managed}</div>
206+
</div>
207+
<div>
208+
<divstyle={{fontSize:'14px',color:'#8c8c8c'}}>Unmanaged</div>
209+
<divstyle={{fontSize:'24px',fontWeight:600}}>{stats.unmanaged}</div>
210+
</div>
211+
</div>
212+
213+
<Dividerstyle={{margin:"16px 0"}}/>
214+
215+
{/* Error display */}
216+
{error&&(
217+
<Alert
218+
message="Error loading data sources"
219+
description={error}
220+
type="error"
221+
showIcon
222+
style={{marginBottom:"16px"}}
223+
/>
224+
)}
225+
226+
{/* Configuration warnings */}
227+
{(!environment.environmentApikey||!environment.environmentApiServiceUrl)&&!error&&(
228+
<Alert
229+
message="Configuration Issue"
230+
description="Missing required configuration: API key or API service URL"
231+
type="warning"
232+
showIcon
233+
style={{marginBottom:"16px"}}
234+
/>
235+
)}
236+
237+
{/* Content */}
238+
{loading ?(
239+
<divstyle={{display:'flex',justifyContent:'center',padding:'20px'}}>
240+
<Spintip="Loading data sources..."/>
241+
</div>
242+
) :dataSources.length===0 ?(
243+
<Empty
244+
description={error||"No data sources found in this workspace"}
245+
image={Empty.PRESENTED_IMAGE_SIMPLE}
246+
/>
247+
) :(
248+
<>
249+
{/* Search Bar */}
250+
<divstyle={{marginBottom:16}}>
251+
<Search
252+
placeholder="Search data sources by name or ID"
253+
allowClear
254+
onSearch={value=>setSearchText(value)}
255+
onChange={e=>setSearchText(e.target.value)}
256+
style={{width:300}}
257+
/>
258+
{searchText&&filteredDataSources.length!==dataSources.length&&(
259+
<divstyle={{marginTop:8}}>
260+
Showing{filteredDataSources.length} of{dataSources.length} data sources
261+
</div>
262+
)}
263+
</div>
264+
265+
<Table
266+
columns={columns}
267+
dataSource={filteredDataSources}
268+
rowKey="id"
269+
pagination={{pageSize:10}}
270+
size="middle"
271+
scroll={{x:'max-content'}}
272+
/>
273+
</>
274+
)}
275+
</Card>
276+
);
277+
};
278+
279+
exportdefaultDataSourcesTab;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp