Skip to content

Commit f5987e2

Browse files
committed
setup data sources structure
1 parent d39b6a0 commit f5987e2

File tree

5 files changed

+360
-7
lines changed

5 files changed

+360
-7
lines changed

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

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import React, { useEffect, useState } from "react";
22
import { useParams, useHistory } from "react-router-dom";
33
import history from "@lowcoder-ee/util/history";
44
import { useWorkspaceDetail } from "./hooks/useWorkspaceDetail";
5+
import DataSourcesList from './components/DataSourcesList';
6+
57

68

79
import {
@@ -44,7 +46,6 @@ const WorkspaceDetail: React.FC = () => {
4446
workspaceId: string;
4547
}>();
4648

47-
// Use the custom hook
4849
const {
4950
environment,
5051
workspace,
@@ -53,6 +54,11 @@ const WorkspaceDetail: React.FC = () => {
5354
appsError,
5455
refreshApps,
5556
appStats,
57+
dataSources,
58+
dataSourcesLoading,
59+
dataSourcesError,
60+
refreshDataSources,
61+
dataSourceStats,
5662
isLoading,
5763
hasError
5864
} = useWorkspaceDetail(environmentId, workspaceId);
@@ -185,16 +191,66 @@ if (isLoading) {
185191
</Card>
186192
</TabPane>
187193

194+
{/* Update the TabPane in WorkspaceDetail.tsx */}
188195
<TabPane
189196
tab={<span><DatabaseOutlined /> Data Sources</span>}
190197
key="dataSources"
191198
>
192199
<Card>
193-
<Alert
194-
message="Data Sources"
195-
description="Data Sources feature will be implemented in the next phase."
196-
type="info"
197-
showIcon
200+
{/* Header with refresh button */}
201+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '16px' }}>
202+
<Title level={5}>Data Sources in this Workspace</Title>
203+
<Button
204+
icon={<SyncOutlined />}
205+
onClick={refreshDataSources}
206+
size="small"
207+
loading={dataSourcesLoading}
208+
>
209+
Refresh Data Sources
210+
</Button>
211+
</div>
212+
213+
{/* Data Source Statistics */}
214+
<Row gutter={16} style={{ marginBottom: '24px' }}>
215+
<Col span={8}>
216+
<Statistic
217+
title="Total Data Sources"
218+
value={dataSourceStats.total}
219+
prefix={<DatabaseOutlined />}
220+
/>
221+
</Col>
222+
<Col span={8}>
223+
<Statistic
224+
title="Data Source Types"
225+
value={dataSourceStats.types}
226+
prefix={<DatabaseOutlined />}
227+
/>
228+
</Col>
229+
</Row>
230+
231+
<Divider style={{ margin: '16px 0' }} />
232+
233+
{/* Show error if data sources loading failed */}
234+
{dataSourcesError && (
235+
<Alert
236+
message="Error loading data sources"
237+
description={dataSourcesError}
238+
type="error"
239+
showIcon
240+
style={{ marginBottom: '16px' }}
241+
action={
242+
<Button size="small" type="primary" onClick={refreshDataSources}>
243+
Try Again
244+
</Button>
245+
}
246+
/>
247+
)}
248+
249+
{/* Data Sources List */}
250+
<DataSourcesList
251+
dataSources={dataSources}
252+
loading={dataSourcesLoading}
253+
error={dataSourcesError}
198254
/>
199255
</Card>
200256
</TabPane>
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import React from 'react';
2+
import { Table, Tag, Empty, Spin, Badge, Tooltip } from 'antd';
3+
import {
4+
DatabaseOutlined,
5+
UserOutlined,
6+
CheckCircleOutlined,
7+
CloseCircleOutlined
8+
} from '@ant-design/icons';
9+
import { DataSourceWithMeta } from '../types/datasource.types';
10+
11+
interface DataSourcesListProps {
12+
dataSources: DataSourceWithMeta[];
13+
loading: boolean;
14+
error?: string | null;
15+
}
16+
17+
/**
18+
* Component to display a list of data sources in a table
19+
*/
20+
const DataSourcesList: React.FC<DataSourcesListProps> = ({
21+
dataSources,
22+
loading,
23+
error,
24+
}) => {
25+
// Format timestamp to date string
26+
const formatDate = (timestamp?: number): string => {
27+
if (!timestamp) return 'N/A';
28+
const date = new Date(timestamp);
29+
return `${date.getMonth() + 1}/${date.getDate()}/${date.getFullYear()}`;
30+
};
31+
32+
// Get icon for data source type
33+
const getDataSourceTypeIcon = (type: string) => {
34+
return <DatabaseOutlined />;
35+
};
36+
37+
// Get color for data source status
38+
const getStatusColor = (status: string) => {
39+
switch (status) {
40+
case 'NORMAL':
41+
return 'green';
42+
case 'ERROR':
43+
return 'red';
44+
case 'WARNING':
45+
return 'orange';
46+
default:
47+
return 'default';
48+
}
49+
};
50+
51+
// Table columns definition
52+
const columns = [
53+
{
54+
title: 'Name',
55+
key: 'name',
56+
render: (record: DataSourceWithMeta) => (
57+
<div style={{ display: 'flex', alignItems: 'center' }}>
58+
{getDataSourceTypeIcon(record.datasource.type)}
59+
<span style={{ marginLeft: 8 }}>{record.datasource.name}</span>
60+
</div>
61+
),
62+
},
63+
{
64+
title: 'Type',
65+
dataIndex: ['datasource', 'type'],
66+
key: 'type',
67+
render: (type: string) => (
68+
<Tag color="blue">{type.toUpperCase()}</Tag>
69+
),
70+
},
71+
{
72+
title: 'Created By',
73+
dataIndex: 'creatorName',
74+
key: 'creatorName',
75+
render: (creatorName: string) => (
76+
<div style={{ display: 'flex', alignItems: 'center' }}>
77+
<UserOutlined style={{ marginRight: 8 }} />
78+
<span>{creatorName}</span>
79+
</div>
80+
),
81+
},
82+
{
83+
title: 'Created',
84+
key: 'createTime',
85+
render: (record: DataSourceWithMeta) => formatDate(record.datasource.createTime),
86+
},
87+
{
88+
title: 'Status',
89+
key: 'status',
90+
render: (record: DataSourceWithMeta) => (
91+
<Tag color={getStatusColor(record.datasource.datasourceStatus)}>
92+
{record.datasource.datasourceStatus}
93+
</Tag>
94+
),
95+
},
96+
{
97+
title: 'Edit Access',
98+
dataIndex: 'edit',
99+
key: 'edit',
100+
render: (edit: boolean) => (
101+
<Tooltip title={edit ? 'You can edit this data source' : 'You cannot edit this data source'}>
102+
{edit ?
103+
<CheckCircleOutlined style={{ color: '#52c41a' }} /> :
104+
<CloseCircleOutlined style={{ color: '#f5222d' }} />
105+
}
106+
</Tooltip>
107+
),
108+
},
109+
];
110+
111+
// If loading, show spinner
112+
if (loading) {
113+
return (
114+
<div style={{ display: 'flex', justifyContent: 'center', padding: '20px' }}>
115+
<Spin tip="Loading data sources..." />
116+
</div>
117+
);
118+
}
119+
120+
// If no data sources or error, show empty state
121+
if (!dataSources || dataSources.length === 0 || error) {
122+
return (
123+
<Empty
124+
description={error || "No data sources found"}
125+
image={Empty.PRESENTED_IMAGE_SIMPLE}
126+
/>
127+
);
128+
}
129+
130+
return (
131+
<Table
132+
columns={columns}
133+
dataSource={dataSources}
134+
rowKey={(record) => record.datasource.id}
135+
pagination={{ pageSize: 10 }}
136+
size="middle"
137+
/>
138+
);
139+
};
140+
141+
export default DataSourcesList;

client/packages/lowcoder/src/pages/setting/environments/hooks/useWorkspaceDetail.ts

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { useState, useEffect, useCallback } from "react";
2-
import { getEnvironmentById, fetchWorkspaceById, getWorkspaceApps} from "../services/environments.service";
2+
import { getEnvironmentById, fetchWorkspaceById, getWorkspaceApps, getWorkspaceDataSources} from "../services/environments.service";
33
import { Environment } from "../types/environment.types";
44
import { Workspace } from "../types/workspace.types";
55
import { App } from "../types/app.types";
6+
import { DataSourceWithMeta } from '../types/datasource.types';
67
export const useWorkspaceDetail = (
78
environmentId: string,
89
workspaceId: string
@@ -22,6 +23,11 @@ export const useWorkspaceDetail = (
2223
const [appsLoading, setAppsLoading] = useState<boolean>(false);
2324
const [appsError, setAppsError] = useState<string | null>(null);
2425

26+
// Data Sources state
27+
const [dataSources, setDataSources] = useState<DataSourceWithMeta[]>([]);
28+
const [dataSourcesLoading, setDataSourcesLoading] = useState<boolean>(false);
29+
const [dataSourcesError, setDataSourcesError] = useState<string | null>(null);
30+
2531
// Function to fetch environment data
2632
const fetchEnvironmentData = useCallback(async () => {
2733
// Similar to your existing function
@@ -95,6 +101,36 @@ export const useWorkspaceDetail = (
95101
}
96102
}, [environment, workspace]);
97103

104+
105+
// Function to fetch data sources
106+
const fetchDataSourcesData = useCallback(async () => {
107+
if (!environment || !workspace) return;
108+
109+
setDataSourcesLoading(true);
110+
setDataSourcesError(null);
111+
112+
try {
113+
const apiKey = environment.environmentApikey;
114+
const apiServiceUrl = environment.environmentApiServiceUrl;
115+
116+
if (!apiKey || !apiServiceUrl) {
117+
setDataSourcesError("Missing API key or service URL");
118+
setDataSourcesLoading(false);
119+
return;
120+
}
121+
122+
const data = await getWorkspaceDataSources(workspace.id, apiKey, apiServiceUrl);
123+
setDataSources(data);
124+
} catch (err) {
125+
setDataSourcesError(err instanceof Error ? err.message : "Failed to fetch data sources");
126+
} finally {
127+
setDataSourcesLoading(false);
128+
}
129+
}, [environment, workspace]);
130+
131+
132+
133+
98134
// Chain the useEffects to sequence the data fetching
99135
useEffect(() => {
100136
fetchEnvironmentData();
@@ -109,6 +145,8 @@ export const useWorkspaceDetail = (
109145
useEffect(() => {
110146
if (environment && workspace) {
111147
fetchAppsData();
148+
fetchDataSourcesData();
149+
112150
}
113151
}, [environment, workspace, fetchAppsData]);
114152

@@ -118,6 +156,12 @@ export const useWorkspaceDetail = (
118156
published: apps.filter((app) => app.published).length,
119157
};
120158

159+
// Data Source statistics
160+
const dataSourceStats = {
161+
total: dataSources.length,
162+
types: [...new Set(dataSources.map(ds => ds.datasource.type))].length,
163+
};
164+
121165
return {
122166
// Environment data
123167
environment,
@@ -138,6 +182,13 @@ export const useWorkspaceDetail = (
138182
refreshApps: fetchAppsData,
139183
appStats,
140184

185+
// Data Sources data
186+
dataSources,
187+
dataSourcesLoading,
188+
dataSourcesError,
189+
refreshDataSources: fetchDataSourcesData,
190+
dataSourceStats,
191+
141192
// Overall loading state
142193
isLoading: environmentLoading || workspaceLoading,
143194
hasError: !!(environmentError || workspaceError),

0 commit comments

Comments
 (0)