Skip to content

[Fix]: Update more UI Pages for the Environments + Add API Call Count in UI #1724

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
May 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import {
Button,
Tag,
Result,
Row,
Col,
Statistic,
Progress,
} from "antd";
import {
LinkOutlined,
Expand All @@ -21,6 +25,11 @@ import {
CloseCircleOutlined,
ExclamationCircleOutlined,
SyncOutlined,
CloudServerOutlined,
UserOutlined,
SafetyOutlined,
CrownOutlined,
ApiOutlined,
} from "@ant-design/icons";

import { useSingleEnvironmentContext } from "./context/SingleEnvironmentContext";
Expand All @@ -31,10 +40,12 @@ import history from "@lowcoder-ee/util/history";
import WorkspacesTab from "./components/WorkspacesTab";
import UserGroupsTab from "./components/UserGroupsTab";
import EnvironmentHeader from "./components/EnvironmentHeader";
import StatsCard from "./components/StatsCard";
import ModernBreadcrumbs from "./components/ModernBreadcrumbs";
import { getEnvironmentTagColor } from "./utils/environmentUtils";
import { formatAPICalls, getAPICallsStatusColor } from "./services/license.service";
import ErrorComponent from './components/ErrorComponent';
const { TabPane } = Tabs;
import { Level1SettingPageContent } from "../styled";

/**
* Environment Detail Page Component
Expand Down Expand Up @@ -124,33 +135,80 @@ const EnvironmentDetail: React.FC = () => {
);
}

const breadcrumbItems = [
// Stats data for the cards
const statsData = [
{
key: 'environments',
title: (
title: "Type",
value: environment.environmentType || "Unknown",
icon: <CloudServerOutlined />,
color: getEnvironmentTagColor(environment.environmentType)
},
{
title: "Status",
value: environment.isLicensed ? "Licensed" : "Unlicensed",
icon: environment.isLicensed ? <CheckCircleOutlined /> : <CloseCircleOutlined />,
color: environment.isLicensed ? "#52c41a" : "#ff4d4f"
},
{
title: "API Key",
value: environment.environmentApikey ? "Configured" : "Not Set",
icon: <SafetyOutlined />,
color: environment.environmentApikey ? "#1890ff" : "#faad14"
},
{
title: "Master Env",
value: environment.isMaster ? "Yes" : "No",
icon: <UserOutlined />,
color: environment.isMaster ? "#722ed1" : "#8c8c8c"
}
];

const tabItems = [
{
key: 'workspaces',
label: (
<span>
<HomeOutlined /> Environments
<AppstoreOutlined /> Workspaces
</span>
),
onClick: () => history.push("/setting/environments")
children: <WorkspacesTab environment={environment} />
},
{
key: 'currentEnvironment',
title: environment.environmentName
key: 'userGroups',
label: (
<span>
<UsergroupAddOutlined /> User Groups
</span>
),
children: <UserGroupsTab environment={environment} />
}
];

return (
<div
className="environment-detail-container"
style={{ padding: "24px", flex: 1, minWidth: "1000px" }}
>
<Level1SettingPageContent style={{ minWidth: "1000px" }}>
{/* Breadcrumbs */}


{/* Environment Header Component */}
<EnvironmentHeader
environment={environment}
onEditClick={handleEditClick}
/>

{/* Stats Cards Row */}
<Row gutter={[16, 16]} style={{ marginBottom: "24px" }}>
{statsData.map((stat, index) => (
<Col xs={24} sm={12} lg={6} key={index}>
<StatsCard
title={stat.title}
value={stat.value}
icon={stat.icon}
color={stat.color}
/>
</Col>
))}
</Row>

{/* Basic Environment Information Card */}
<Card
title="Environment Overview"
Expand Down Expand Up @@ -180,13 +238,10 @@ const EnvironmentDetail: React.FC = () => {
"No domain set"
)}
</Descriptions.Item>
<Descriptions.Item label="Environment Type">
<Tag
color={getEnvironmentTagColor(environment.environmentType)}
style={{ borderRadius: '4px' }}
>
{environment.environmentType}
</Tag>
<Descriptions.Item label="Environment ID">
<code style={{ padding: '2px 6px', background: '#f5f5f5', borderRadius: '3px' }}>
{environment.environmentId}
</code>
</Descriptions.Item>
<Descriptions.Item label="License Status">
{(() => {
Expand All @@ -196,29 +251,178 @@ const EnvironmentDetail: React.FC = () => {
case 'licensed':
return <Tag icon={<CheckCircleOutlined />} color="green" style={{ borderRadius: '4px' }}>Licensed</Tag>;
case 'unlicensed':
return <Tag icon={<CloseCircleOutlined />} color="red" style={{ borderRadius: '4px' }}>Not Licensed</Tag>;
return <Tag icon={<CloseCircleOutlined />} color="orange" style={{ borderRadius: '4px' }}>License Needed</Tag>;
case 'error':
return <Tag icon={<ExclamationCircleOutlined />} color="orange" style={{ borderRadius: '4px' }}>License Error</Tag>;
return <Tag icon={<ExclamationCircleOutlined />} color="orange" style={{ borderRadius: '4px' }}>Setup Required</Tag>;
default:
return <Tag color="default" style={{ borderRadius: '4px' }}>Unknown</Tag>;
}
})()}
</Descriptions.Item>
<Descriptions.Item label="API Key Status">
{environment.environmentApikey ? (
<Tag color="green" style={{ borderRadius: '4px' }}>Configured</Tag>
) : (
<Tag color="red" style={{ borderRadius: '4px' }}>Not Configured</Tag>
)}
</Descriptions.Item>
<Descriptions.Item label="Master Environment">
{environment.isMaster ? "Yes" : "No"}
<Descriptions.Item label="Created">
{environment.createdAt ? new Date(environment.createdAt).toLocaleDateString() : "Unknown"}
</Descriptions.Item>
</Descriptions>
</Card>

{/* Modern Breadcrumbs navigation */}
<ModernBreadcrumbs items={breadcrumbItems} />
<ModernBreadcrumbs
items={[
{
key: 'environments',
title: 'Environments',
onClick: () => history.push('/setting/environments')
},
{
key: 'current',
title: environment.environmentName || "Environment Detail"
}
]}
/>
{/* Detailed License Information Card - only show for licensed environments with details */}
{environment.isLicensed && environment.licenseDetails && (
<Card
title={
<span>
<CrownOutlined style={{ color: '#52c41a', marginRight: '8px' }} />
License Details
</span>
}
style={{
marginBottom: "24px",
borderRadius: '4px',
border: '1px solid #f0f0f0'
}}
className="license-details-card"
>
<Row gutter={[24, 16]}>
{/* API Calls Status */}
<Col xs={24} sm={12} md={8}>
<Card
size="small"
style={{ height: '100%', textAlign: 'center' }}
styles={{ body: { padding: '16px' } }}
>
<Statistic
title="API Calls Remaining"
value={environment.licenseDetails.remainingAPICalls}
formatter={(value) => (
<span style={{
color: getAPICallsStatusColor(
environment.licenseDetails?.remainingAPICalls || 0,
environment.licenseDetails?.totalAPICallsLimit || 0
)
}}>
{value?.toLocaleString()}
</span>
)}
prefix={<ApiOutlined />}
/>
<div style={{ marginTop: '12px' }}>
<Progress
percent={environment.licenseDetails.apiCallsUsage || 0}
strokeColor={getAPICallsStatusColor(
environment.licenseDetails.remainingAPICalls,
environment.licenseDetails.totalAPICallsLimit || 0
)}
size="small"
showInfo={false}
/>
<div style={{
fontSize: '12px',
color: '#8c8c8c',
marginTop: '4px'
}}>
{environment.licenseDetails.apiCallsUsage || 0}% used
</div>
</div>
</Card>
</Col>

{/* Total License Limit */}
<Col xs={24} sm={12} md={8}>
<Card
size="small"
style={{ height: '100%', textAlign: 'center' }}
styles={{ body: { padding: '16px' } }}
>
<Statistic
title="Total API Calls Limit"
value={environment.licenseDetails.totalAPICallsLimit}
formatter={(value) => value?.toLocaleString()}
prefix={<ApiOutlined />}
/>
<Tag
color="blue"
style={{ marginTop: '12px' }}
>
{environment.licenseDetails.eeLicenses.length} License{environment.licenseDetails.eeLicenses.length !== 1 ? 's' : ''}
</Tag>
</Card>
</Col>

{/* Enterprise Edition Status */}
<Col xs={24} sm={12} md={8}>
<Card
size="small"
style={{ height: '100%', textAlign: 'center' }}
styles={{ body: { padding: '16px' } }}
>
<Statistic
title="Enterprise Edition"
value={environment.licenseDetails.eeActive ? "Active" : "Inactive"}
formatter={(value) => (
<Tag
color={environment.licenseDetails?.eeActive ? "green" : "red"}
icon={environment.licenseDetails?.eeActive ? <CheckCircleOutlined /> : <CloseCircleOutlined />}
>
{value}
</Tag>
)}
/>
</Card>
</Col>
</Row>

{/* License Details */}
<div style={{ marginTop: '24px' }}>
<Typography.Title level={5} style={{ marginBottom: '16px' }}>
<UserOutlined style={{ marginRight: '8px' }} />
License Information
</Typography.Title>

<Row gutter={[16, 16]}>
{environment.licenseDetails.eeLicenses.map((license, index) => (
<Col xs={24} sm={12} md={8} key={license.uuid}>
<Card
size="small"
style={{
border: '1px solid #f0f0f0',
borderRadius: '6px'
}}
styles={{ body: { padding: '12px' } }}
>
<div style={{ marginBottom: '8px' }}>
<strong style={{ color: '#262626' }}>
{license.customerName}
</strong>
</div>
<div style={{ fontSize: '12px', color: '#8c8c8c', marginBottom: '8px' }}>
ID: {license.customerId}
</div>
<div style={{ fontSize: '12px', color: '#8c8c8c', marginBottom: '8px' }}>
UUID: <span style={{ fontFamily: 'monospace' }}>{license.uuid.substring(0, 8)}...</span>
</div>
<Tag color="blue">
{license.apiCallsLimit.toLocaleString()} calls
</Tag>
</Card>
</Col>
))}
</Row>
</div>
</Card>
)}


{/* Tabs for Workspaces and User Groups */}
<Tabs
Expand All @@ -227,29 +431,8 @@ const EnvironmentDetail: React.FC = () => {
onChange={setActiveTab}
className="modern-tabs"
type="line"
>
<TabPane
tab={
<span>
<AppstoreOutlined /> Workspaces
</span>
}
key="workspaces"
>
<WorkspacesTab environment={environment} />
</TabPane>

<TabPane
tab={
<span>
<UsergroupAddOutlined /> User Groups
</span>
}
key="userGroups"
>
<UserGroupsTab environment={environment} />
</TabPane>
</Tabs>
items={tabItems}
/>

{/* Edit Environment Modal */}
{environment && (
Expand All @@ -261,7 +444,7 @@ const EnvironmentDetail: React.FC = () => {
loading={isUpdating}
/>
)}
</div>
</Level1SettingPageContent>
);
};

Expand Down
Loading
Loading