Skip to content

Commit d90330b

Browse files
authored
Merge pull request #1724 from iamfaran/fix/ui-and-deployment-issues
[Fix]: Update more UI Pages for the Environments + Add API Call Count in UI
2 parents 18822d7 + c28c62f commit d90330b

21 files changed

+923
-585
lines changed

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

Lines changed: 239 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ import {
1010
Button,
1111
Tag,
1212
Result,
13+
Row,
14+
Col,
15+
Statistic,
16+
Progress,
1317
} from "antd";
1418
import {
1519
LinkOutlined,
@@ -21,6 +25,11 @@ import {
2125
CloseCircleOutlined,
2226
ExclamationCircleOutlined,
2327
SyncOutlined,
28+
CloudServerOutlined,
29+
UserOutlined,
30+
SafetyOutlined,
31+
CrownOutlined,
32+
ApiOutlined,
2433
} from "@ant-design/icons";
2534

2635
import { useSingleEnvironmentContext } from "./context/SingleEnvironmentContext";
@@ -31,10 +40,12 @@ import history from "@lowcoder-ee/util/history";
3140
import WorkspacesTab from "./components/WorkspacesTab";
3241
import UserGroupsTab from "./components/UserGroupsTab";
3342
import EnvironmentHeader from "./components/EnvironmentHeader";
43+
import StatsCard from "./components/StatsCard";
3444
import ModernBreadcrumbs from "./components/ModernBreadcrumbs";
3545
import { getEnvironmentTagColor } from "./utils/environmentUtils";
46+
import { formatAPICalls, getAPICallsStatusColor } from "./services/license.service";
3647
import ErrorComponent from './components/ErrorComponent';
37-
const { TabPane } = Tabs;
48+
import { Level1SettingPageContent } from "../styled";
3849

3950
/**
4051
* Environment Detail Page Component
@@ -124,33 +135,80 @@ const EnvironmentDetail: React.FC = () => {
124135
);
125136
}
126137

127-
const breadcrumbItems = [
138+
// Stats data for the cards
139+
const statsData = [
128140
{
129-
key: 'environments',
130-
title: (
141+
title: "Type",
142+
value: environment.environmentType || "Unknown",
143+
icon: <CloudServerOutlined />,
144+
color: getEnvironmentTagColor(environment.environmentType)
145+
},
146+
{
147+
title: "Status",
148+
value: environment.isLicensed ? "Licensed" : "Unlicensed",
149+
icon: environment.isLicensed ? <CheckCircleOutlined /> : <CloseCircleOutlined />,
150+
color: environment.isLicensed ? "#52c41a" : "#ff4d4f"
151+
},
152+
{
153+
title: "API Key",
154+
value: environment.environmentApikey ? "Configured" : "Not Set",
155+
icon: <SafetyOutlined />,
156+
color: environment.environmentApikey ? "#1890ff" : "#faad14"
157+
},
158+
{
159+
title: "Master Env",
160+
value: environment.isMaster ? "Yes" : "No",
161+
icon: <UserOutlined />,
162+
color: environment.isMaster ? "#722ed1" : "#8c8c8c"
163+
}
164+
];
165+
166+
const tabItems = [
167+
{
168+
key: 'workspaces',
169+
label: (
131170
<span>
132-
<HomeOutlined /> Environments
171+
<AppstoreOutlined /> Workspaces
133172
</span>
134173
),
135-
onClick: () => history.push("/setting/environments")
174+
children: <WorkspacesTab environment={environment} />
136175
},
137176
{
138-
key: 'currentEnvironment',
139-
title: environment.environmentName
177+
key: 'userGroups',
178+
label: (
179+
<span>
180+
<UsergroupAddOutlined /> User Groups
181+
</span>
182+
),
183+
children: <UserGroupsTab environment={environment} />
140184
}
141185
];
142186

143187
return (
144-
<div
145-
className="environment-detail-container"
146-
style={{ padding: "24px", flex: 1, minWidth: "1000px" }}
147-
>
188+
<Level1SettingPageContent style={{ minWidth: "1000px" }}>
189+
{/* Breadcrumbs */}
190+
191+
148192
{/* Environment Header Component */}
149193
<EnvironmentHeader
150194
environment={environment}
151195
onEditClick={handleEditClick}
152196
/>
153197

198+
{/* Stats Cards Row */}
199+
<Row gutter={[16, 16]} style={{ marginBottom: "24px" }}>
200+
{statsData.map((stat, index) => (
201+
<Col xs={24} sm={12} lg={6} key={index}>
202+
<StatsCard
203+
title={stat.title}
204+
value={stat.value}
205+
icon={stat.icon}
206+
color={stat.color}
207+
/>
208+
</Col>
209+
))}
210+
</Row>
211+
154212
{/* Basic Environment Information Card */}
155213
<Card
156214
title="Environment Overview"
@@ -180,13 +238,10 @@ const EnvironmentDetail: React.FC = () => {
180238
"No domain set"
181239
)}
182240
</Descriptions.Item>
183-
<Descriptions.Item label="Environment Type">
184-
<Tag
185-
color={getEnvironmentTagColor(environment.environmentType)}
186-
style={{ borderRadius: '4px' }}
187-
>
188-
{environment.environmentType}
189-
</Tag>
241+
<Descriptions.Item label="Environment ID">
242+
<code style={{ padding: '2px 6px', background: '#f5f5f5', borderRadius: '3px' }}>
243+
{environment.environmentId}
244+
</code>
190245
</Descriptions.Item>
191246
<Descriptions.Item label="License Status">
192247
{(() => {
@@ -196,29 +251,178 @@ const EnvironmentDetail: React.FC = () => {
196251
case 'licensed':
197252
return <Tag icon={<CheckCircleOutlined />} color="green" style={{ borderRadius: '4px' }}>Licensed</Tag>;
198253
case 'unlicensed':
199-
return <Tag icon={<CloseCircleOutlined />} color="red" style={{ borderRadius: '4px' }}>Not Licensed</Tag>;
254+
return <Tag icon={<CloseCircleOutlined />} color="orange" style={{ borderRadius: '4px' }}>License Needed</Tag>;
200255
case 'error':
201-
return <Tag icon={<ExclamationCircleOutlined />} color="orange" style={{ borderRadius: '4px' }}>License Error</Tag>;
256+
return <Tag icon={<ExclamationCircleOutlined />} color="orange" style={{ borderRadius: '4px' }}>Setup Required</Tag>;
202257
default:
203258
return <Tag color="default" style={{ borderRadius: '4px' }}>Unknown</Tag>;
204259
}
205260
})()}
206261
</Descriptions.Item>
207-
<Descriptions.Item label="API Key Status">
208-
{environment.environmentApikey ? (
209-
<Tag color="green" style={{ borderRadius: '4px' }}>Configured</Tag>
210-
) : (
211-
<Tag color="red" style={{ borderRadius: '4px' }}>Not Configured</Tag>
212-
)}
213-
</Descriptions.Item>
214-
<Descriptions.Item label="Master Environment">
215-
{environment.isMaster ? "Yes" : "No"}
262+
<Descriptions.Item label="Created">
263+
{environment.createdAt ? new Date(environment.createdAt).toLocaleDateString() : "Unknown"}
216264
</Descriptions.Item>
217265
</Descriptions>
218266
</Card>
219267

220-
{/* Modern Breadcrumbs navigation */}
221-
<ModernBreadcrumbs items={breadcrumbItems} />
268+
<ModernBreadcrumbs
269+
items={[
270+
{
271+
key: 'environments',
272+
title: 'Environments',
273+
onClick: () => history.push('/setting/environments')
274+
},
275+
{
276+
key: 'current',
277+
title: environment.environmentName || "Environment Detail"
278+
}
279+
]}
280+
/>
281+
{/* Detailed License Information Card - only show for licensed environments with details */}
282+
{environment.isLicensed && environment.licenseDetails && (
283+
<Card
284+
title={
285+
<span>
286+
<CrownOutlined style={{ color: '#52c41a', marginRight: '8px' }} />
287+
License Details
288+
</span>
289+
}
290+
style={{
291+
marginBottom: "24px",
292+
borderRadius: '4px',
293+
border: '1px solid #f0f0f0'
294+
}}
295+
className="license-details-card"
296+
>
297+
<Row gutter={[24, 16]}>
298+
{/* API Calls Status */}
299+
<Col xs={24} sm={12} md={8}>
300+
<Card
301+
size="small"
302+
style={{ height: '100%', textAlign: 'center' }}
303+
styles={{ body: { padding: '16px' } }}
304+
>
305+
<Statistic
306+
title="API Calls Remaining"
307+
value={environment.licenseDetails.remainingAPICalls}
308+
formatter={(value) => (
309+
<span style={{
310+
color: getAPICallsStatusColor(
311+
environment.licenseDetails?.remainingAPICalls || 0,
312+
environment.licenseDetails?.totalAPICallsLimit || 0
313+
)
314+
}}>
315+
{value?.toLocaleString()}
316+
</span>
317+
)}
318+
prefix={<ApiOutlined />}
319+
/>
320+
<div style={{ marginTop: '12px' }}>
321+
<Progress
322+
percent={environment.licenseDetails.apiCallsUsage || 0}
323+
strokeColor={getAPICallsStatusColor(
324+
environment.licenseDetails.remainingAPICalls,
325+
environment.licenseDetails.totalAPICallsLimit || 0
326+
)}
327+
size="small"
328+
showInfo={false}
329+
/>
330+
<div style={{
331+
fontSize: '12px',
332+
color: '#8c8c8c',
333+
marginTop: '4px'
334+
}}>
335+
{environment.licenseDetails.apiCallsUsage || 0}% used
336+
</div>
337+
</div>
338+
</Card>
339+
</Col>
340+
341+
{/* Total License Limit */}
342+
<Col xs={24} sm={12} md={8}>
343+
<Card
344+
size="small"
345+
style={{ height: '100%', textAlign: 'center' }}
346+
styles={{ body: { padding: '16px' } }}
347+
>
348+
<Statistic
349+
title="Total API Calls Limit"
350+
value={environment.licenseDetails.totalAPICallsLimit}
351+
formatter={(value) => value?.toLocaleString()}
352+
prefix={<ApiOutlined />}
353+
/>
354+
<Tag
355+
color="blue"
356+
style={{ marginTop: '12px' }}
357+
>
358+
{environment.licenseDetails.eeLicenses.length} License{environment.licenseDetails.eeLicenses.length !== 1 ? 's' : ''}
359+
</Tag>
360+
</Card>
361+
</Col>
362+
363+
{/* Enterprise Edition Status */}
364+
<Col xs={24} sm={12} md={8}>
365+
<Card
366+
size="small"
367+
style={{ height: '100%', textAlign: 'center' }}
368+
styles={{ body: { padding: '16px' } }}
369+
>
370+
<Statistic
371+
title="Enterprise Edition"
372+
value={environment.licenseDetails.eeActive ? "Active" : "Inactive"}
373+
formatter={(value) => (
374+
<Tag
375+
color={environment.licenseDetails?.eeActive ? "green" : "red"}
376+
icon={environment.licenseDetails?.eeActive ? <CheckCircleOutlined /> : <CloseCircleOutlined />}
377+
>
378+
{value}
379+
</Tag>
380+
)}
381+
/>
382+
</Card>
383+
</Col>
384+
</Row>
385+
386+
{/* License Details */}
387+
<div style={{ marginTop: '24px' }}>
388+
<Typography.Title level={5} style={{ marginBottom: '16px' }}>
389+
<UserOutlined style={{ marginRight: '8px' }} />
390+
License Information
391+
</Typography.Title>
392+
393+
<Row gutter={[16, 16]}>
394+
{environment.licenseDetails.eeLicenses.map((license, index) => (
395+
<Col xs={24} sm={12} md={8} key={license.uuid}>
396+
<Card
397+
size="small"
398+
style={{
399+
border: '1px solid #f0f0f0',
400+
borderRadius: '6px'
401+
}}
402+
styles={{ body: { padding: '12px' } }}
403+
>
404+
<div style={{ marginBottom: '8px' }}>
405+
<strong style={{ color: '#262626' }}>
406+
{license.customerName}
407+
</strong>
408+
</div>
409+
<div style={{ fontSize: '12px', color: '#8c8c8c', marginBottom: '8px' }}>
410+
ID: {license.customerId}
411+
</div>
412+
<div style={{ fontSize: '12px', color: '#8c8c8c', marginBottom: '8px' }}>
413+
UUID: <span style={{ fontFamily: 'monospace' }}>{license.uuid.substring(0, 8)}...</span>
414+
</div>
415+
<Tag color="blue">
416+
{license.apiCallsLimit.toLocaleString()} calls
417+
</Tag>
418+
</Card>
419+
</Col>
420+
))}
421+
</Row>
422+
</div>
423+
</Card>
424+
)}
425+
222426

223427
{/* Tabs for Workspaces and User Groups */}
224428
<Tabs
@@ -227,29 +431,8 @@ const EnvironmentDetail: React.FC = () => {
227431
onChange={setActiveTab}
228432
className="modern-tabs"
229433
type="line"
230-
>
231-
<TabPane
232-
tab={
233-
<span>
234-
<AppstoreOutlined /> Workspaces
235-
</span>
236-
}
237-
key="workspaces"
238-
>
239-
<WorkspacesTab environment={environment} />
240-
</TabPane>
241-
242-
<TabPane
243-
tab={
244-
<span>
245-
<UsergroupAddOutlined /> User Groups
246-
</span>
247-
}
248-
key="userGroups"
249-
>
250-
<UserGroupsTab environment={environment} />
251-
</TabPane>
252-
</Tabs>
434+
items={tabItems}
435+
/>
253436

254437
{/* Edit Environment Modal */}
255438
{environment && (
@@ -261,7 +444,7 @@ const EnvironmentDetail: React.FC = () => {
261444
loading={isUpdating}
262445
/>
263446
)}
264-
</div>
447+
</Level1SettingPageContent>
265448
);
266449
};
267450

0 commit comments

Comments
 (0)