Skip to content

Commit c3a770e

Browse files
committed
update UI for DS and queries
1 parent f4fba8e commit c3a770e

File tree

2 files changed

+395
-157
lines changed

2 files changed

+395
-157
lines changed

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

Lines changed: 200 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
11
import React, { useState, useEffect } from 'react';
2-
import { Card, Button, Divider, Alert, message, Table, Tag, Input, Space, Tooltip } from 'antd';
3-
import { SyncOutlined, CloudUploadOutlined, DatabaseOutlined, AuditOutlined } from '@ant-design/icons';
2+
import { Card, Button, Divider, Alert, message, Table, Tag, Input, Space, Tooltip, Row, Col } from 'antd';
3+
import {
4+
SyncOutlined,
5+
CloudUploadOutlined,
6+
DatabaseOutlined,
7+
AuditOutlined,
8+
ApiOutlined,
9+
CheckCircleFilled,
10+
CloudServerOutlined,
11+
DisconnectOutlined
12+
} from '@ant-design/icons';
413
import Title from 'antd/lib/typography/Title';
514
import { Environment } from '../types/environment.types';
615
import { Workspace } from '../types/workspace.types';
716
import { DataSource } from '../types/datasource.types';
817
import { getMergedWorkspaceDataSources } from '../services/datasources.service';
9-
import { Switch, Spin, Empty } from 'antd';
18+
import { Switch, Spin, Empty, Avatar } from 'antd';
1019
import { ManagedObjectType, setManagedObject, unsetManagedObject } from '../services/managed-objects.service';
1120
import { useDeployModal } from '../context/DeployModalContext';
1221
import { dataSourcesConfig } from '../config/data-sources.config';
@@ -125,23 +134,47 @@ const DataSourcesTab: React.FC<DataSourcesTabProps> = ({ environment, workspace
125134
// Table columns
126135
const columns = [
127136
{
128-
title: 'Name',
129-
dataIndex: 'name',
130-
key: 'name',
131-
render: (text: string) => <span className="datasource-name">{text}</span>
132-
},
133-
{
134-
title: 'ID',
135-
dataIndex: 'id',
136-
key: 'id',
137-
ellipsis: true,
137+
title: 'Data Source',
138+
key: 'datasource',
139+
render: (dataSource: DataSource) => (
140+
<div style={{ display: 'flex', alignItems: 'center' }}>
141+
<Avatar
142+
style={{
143+
backgroundColor: getDataSourceColor(dataSource.type),
144+
marginRight: 12
145+
}}
146+
shape="square"
147+
icon={<DatabaseOutlined />}
148+
/>
149+
<div>
150+
<div style={{ fontWeight: 500 }}>{dataSource.name}</div>
151+
<div style={{ fontSize: 12, color: '#8c8c8c', marginTop: 4 }}>
152+
{dataSource.id}
153+
</div>
154+
</div>
155+
</div>
156+
),
138157
},
139158
{
140159
title: 'Type',
141160
dataIndex: 'type',
142161
key: 'type',
143162
render: (type: string) => (
144-
<Tag color="blue">{type}</Tag>
163+
<Tag color={getDataSourceColor(type)} style={{ borderRadius: '12px', padding: '2px 12px' }}>
164+
{type}
165+
</Tag>
166+
),
167+
},
168+
{
169+
title: 'Status',
170+
key: 'status',
171+
render: (dataSource: DataSource) => (
172+
<Tag
173+
color={dataSource.managed ? 'processing' : 'default'}
174+
style={{ borderRadius: '12px' }}
175+
>
176+
{dataSource.managed ? <CloudServerOutlined /> : <DisconnectOutlined />} {dataSource.managed ? 'Managed' : 'Unmanaged'}
177+
</Tag>
145178
),
146179
},
147180
{
@@ -152,7 +185,6 @@ const DataSourcesTab: React.FC<DataSourcesTabProps> = ({ environment, workspace
152185
checked={!!dataSource.managed}
153186
onChange={(checked: boolean) => handleToggleManaged(dataSource, checked)}
154187
loading={refreshing}
155-
size="small"
156188
/>
157189
),
158190
},
@@ -164,7 +196,6 @@ const DataSourcesTab: React.FC<DataSourcesTabProps> = ({ environment, workspace
164196
<Tooltip title="View Audit Logs">
165197
<Button
166198
icon={<AuditOutlined />}
167-
size="small"
168199
onClick={(e) => {
169200
e.stopPropagation();
170201
const auditUrl = `/setting/audit?environmentId=${environment.environmentId}&orgId=${workspace.id}&datasourceId=${dataSource.id}&pageSize=100&pageNum=1`;
@@ -177,7 +208,6 @@ const DataSourcesTab: React.FC<DataSourcesTabProps> = ({ environment, workspace
177208
<Tooltip title={!dataSource.managed ? "Data source must be managed before it can be deployed" : "Deploy this data source to another environment"}>
178209
<Button
179210
type="primary"
180-
size="small"
181211
icon={<CloudUploadOutlined />}
182212
onClick={() => openDeployModal(dataSource, dataSourcesConfig, environment)}
183213
disabled={!dataSource.managed}
@@ -190,50 +220,93 @@ const DataSourcesTab: React.FC<DataSourcesTabProps> = ({ environment, workspace
190220
}
191221
];
192222

223+
// Helper function to get color based on data source type
224+
const getDataSourceColor = (type: string) => {
225+
const colorMap: {[key: string]: string} = {
226+
'mysql': '#4479A1',
227+
'postgres': '#336791',
228+
'mongodb': '#4DB33D',
229+
'redis': '#DC382D',
230+
'rest': '#FF6C37',
231+
'graphql': '#E10098',
232+
'elasticsearch': '#005571',
233+
'oracle': '#F80000',
234+
'mssql': '#CC2927',
235+
'snowflake': '#29B5E8'
236+
};
237+
238+
return colorMap[type.toLowerCase()] || '#1890ff';
239+
};
240+
241+
// Stat card component
242+
const StatCard = ({ title, value, icon }: { title: string; value: number; icon: React.ReactNode }) => (
243+
<Card
244+
style={{
245+
height: '100%',
246+
borderRadius: '8px',
247+
boxShadow: '0 2px 8px rgba(0,0,0,0.05)'
248+
}}
249+
>
250+
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
251+
<div>
252+
<div style={{ fontSize: '14px', color: '#8c8c8c', marginBottom: '8px' }}>{title}</div>
253+
<div style={{ fontSize: '24px', fontWeight: 600 }}>{value}</div>
254+
</div>
255+
<div style={{
256+
fontSize: '28px',
257+
opacity: 0.8,
258+
color: '#1890ff',
259+
padding: '12px',
260+
backgroundColor: 'rgba(24, 144, 255, 0.1)',
261+
borderRadius: '50%',
262+
display: 'flex',
263+
alignItems: 'center',
264+
justifyContent: 'center'
265+
}}>
266+
{icon}
267+
</div>
268+
</div>
269+
</Card>
270+
);
271+
193272
return (
194-
<Card>
195-
{/* Header with refresh button */}
196-
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "16px" }}>
197-
<Title level={5}>Data Sources in this Workspace</Title>
273+
<div style={{ padding: '16px 0' }}>
274+
{/* Header */}
275+
<div style={{
276+
display: "flex",
277+
justifyContent: "space-between",
278+
alignItems: "center",
279+
marginBottom: "24px",
280+
background: 'linear-gradient(135deg, #1890ff 0%, #13c2c2 100%)',
281+
padding: '20px 24px',
282+
borderRadius: '8px',
283+
color: 'white'
284+
}}>
285+
<div>
286+
<Title level={4} style={{ color: 'white', margin: 0 }}>
287+
<DatabaseOutlined style={{ marginRight: 10 }} /> Data Sources
288+
</Title>
289+
<p style={{ marginBottom: 0 }}>Manage your workspace data connections</p>
290+
</div>
198291
<Button
199292
icon={<SyncOutlined spin={refreshing} />}
200293
onClick={handleRefresh}
201294
loading={loading}
295+
type="primary"
296+
ghost
202297
>
203298
Refresh
204299
</Button>
205300
</div>
206301

207-
{/* Stats display */}
208-
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '24px', marginBottom: '16px' }}>
209-
<div>
210-
<div style={{ fontSize: '14px', color: '#8c8c8c' }}>Total Data Sources</div>
211-
<div style={{ fontSize: '24px', fontWeight: 600 }}>{stats.total}</div>
212-
</div>
213-
<div>
214-
<div style={{ fontSize: '14px', color: '#8c8c8c' }}>Types</div>
215-
<div style={{ fontSize: '24px', fontWeight: 600 }}>{stats.types}</div>
216-
</div>
217-
<div>
218-
<div style={{ fontSize: '14px', color: '#8c8c8c' }}>Managed</div>
219-
<div style={{ fontSize: '24px', fontWeight: 600 }}>{stats.managed}</div>
220-
</div>
221-
<div>
222-
<div style={{ fontSize: '14px', color: '#8c8c8c' }}>Unmanaged</div>
223-
<div style={{ fontSize: '24px', fontWeight: 600 }}>{stats.unmanaged}</div>
224-
</div>
225-
</div>
226-
227-
<Divider style={{ margin: "16px 0" }} />
228-
229302
{/* Error display */}
230303
{error && (
231304
<Alert
232305
message="Error loading data sources"
233306
description={error}
234307
type="error"
235308
showIcon
236-
style={{ marginBottom: "16px" }}
309+
style={{ marginBottom: "20px" }}
237310
/>
238311
)}
239312

@@ -244,49 +317,95 @@ const DataSourcesTab: React.FC<DataSourcesTabProps> = ({ environment, workspace
244317
description="Missing required configuration: API key or API service URL"
245318
type="warning"
246319
showIcon
247-
style={{ marginBottom: "16px" }}
320+
style={{ marginBottom: "20px" }}
248321
/>
249322
)}
250323

324+
{/* Stats display */}
325+
<Row gutter={[16, 16]} style={{ marginBottom: '24px' }}>
326+
<Col xs={12} sm={12} md={6}>
327+
<StatCard
328+
title="Total Data Sources"
329+
value={stats.total}
330+
icon={<DatabaseOutlined />}
331+
/>
332+
</Col>
333+
<Col xs={12} sm={12} md={6}>
334+
<StatCard
335+
title="Available Types"
336+
value={stats.types}
337+
icon={<ApiOutlined />}
338+
/>
339+
</Col>
340+
<Col xs={12} sm={12} md={6}>
341+
<StatCard
342+
title="Managed"
343+
value={stats.managed}
344+
icon={<CloudServerOutlined />}
345+
/>
346+
</Col>
347+
<Col xs={12} sm={12} md={6}>
348+
<StatCard
349+
title="Unmanaged"
350+
value={stats.unmanaged}
351+
icon={<DisconnectOutlined />}
352+
/>
353+
</Col>
354+
</Row>
355+
251356
{/* Content */}
252-
{loading ? (
253-
<div style={{ display: 'flex', justifyContent: 'center', padding: '20px' }}>
254-
<Spin tip="Loading data sources..." />
255-
</div>
256-
) : dataSources.length === 0 ? (
257-
<Empty
258-
description={error || "No data sources found in this workspace"}
259-
image={Empty.PRESENTED_IMAGE_SIMPLE}
260-
/>
261-
) : (
262-
<>
263-
{/* Search Bar */}
264-
<div style={{ marginBottom: 16 }}>
265-
<Search
266-
placeholder="Search data sources by name or ID"
267-
allowClear
268-
onSearch={value => setSearchText(value)}
269-
onChange={e => setSearchText(e.target.value)}
270-
style={{ width: 300 }}
271-
/>
272-
{searchText && filteredDataSources.length !== dataSources.length && (
273-
<div style={{ marginTop: 8 }}>
274-
Showing {filteredDataSources.length} of {dataSources.length} data sources
275-
</div>
276-
)}
357+
<Card
358+
style={{
359+
borderRadius: '8px',
360+
boxShadow: '0 2px 8px rgba(0,0,0,0.05)'
361+
}}
362+
>
363+
{loading ? (
364+
<div style={{ display: 'flex', justifyContent: 'center', padding: '40px' }}>
365+
<Spin size="large" tip="Loading data sources..." />
277366
</div>
278-
279-
<Table
280-
columns={columns}
281-
dataSource={filteredDataSources}
282-
rowKey="id"
283-
pagination={{ pageSize: 10 }}
284-
size="middle"
285-
scroll={{ x: 'max-content' }}
367+
) : dataSources.length === 0 ? (
368+
<Empty
369+
description={error || "No data sources found in this workspace"}
370+
image={Empty.PRESENTED_IMAGE_SIMPLE}
286371
/>
287-
</>
288-
)}
289-
</Card>
372+
) : (
373+
<>
374+
{/* Search Bar */}
375+
<div style={{ marginBottom: 20 }}>
376+
<Search
377+
placeholder="Search data sources by name or ID"
378+
allowClear
379+
onSearch={value => setSearchText(value)}
380+
onChange={e => setSearchText(e.target.value)}
381+
style={{ width: 300 }}
382+
size="large"
383+
/>
384+
{searchText && filteredDataSources.length !== dataSources.length && (
385+
<div style={{ marginTop: 8, color: '#8c8c8c' }}>
386+
Showing {filteredDataSources.length} of {dataSources.length} data sources
387+
</div>
388+
)}
389+
</div>
390+
391+
<Table
392+
columns={columns}
393+
dataSource={filteredDataSources}
394+
rowKey="id"
395+
pagination={{
396+
pageSize: 10,
397+
showTotal: (total, range) => `${range[0]}-${range[1]} of ${total} data sources`
398+
}}
399+
rowClassName={() => 'datasource-row'}
400+
style={{
401+
borderRadius: '8px',
402+
overflow: 'hidden'
403+
}}
404+
/>
405+
</>
406+
)}
407+
</Card>
408+
</div>
290409
);
291410
};
292411

0 commit comments

Comments
 (0)