Content-Length: 497931 | pFad | http://github.com/lowcoder-org/lowcoder/pull/1687/commits/548e73b3586959c15cfbf68823f7b4c989909d85

E2 Revamp the Environments UI and Refactor by iamfaran · Pull Request #1687 · lowcoder-org/lowcoder · GitHub
Skip to content

Revamp the Environments UI and Refactor #1687

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 39 commits into from
May 20, 2025
Merged
Changes from 1 commit
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
0f5440d
Add managed/unmanged filtering on column
iamfaran May 9, 2025
8e0f94b
Add search filter for the objects
iamfaran May 9, 2025
9883336
Improve the Environment Not Found UI
iamfaran May 9, 2025
b6d74d2
create a util function and add tags in Deployment Modal
iamfaran May 12, 2025
bb81f1f
fix endpoint for DS and ql
iamfaran May 14, 2025
b5647a7
updated managed endpoints
iamfaran May 15, 2025
d08218f
fix switch for objects
iamfaran May 15, 2025
c1a74fe
refactor AppsTab component
iamfaran May 15, 2025
0a1879b
Seperate DS tab
iamfaran May 15, 2025
dffc683
Seperate Queries Tab
iamfaran May 15, 2025
7410765
Add seperate workspace and usergroups tab
iamfaran May 15, 2025
6383d7f
remove unnecessary code
iamfaran May 15, 2025
199c869
add audit buttons in tabs
iamfaran May 15, 2025
f4fba8e
Update Apps UI
iamfaran May 15, 2025
c3a770e
update UI for DS and queries
iamfaran May 15, 2025
3e35b5d
update UI for workspaces tab
iamfaran May 16, 2025
548e73b
update UI user groups tab
iamfaran May 16, 2025
7f4d11f
update environment detail header
iamfaran May 16, 2025
9a8329e
create utils for different env colors
iamfaran May 16, 2025
b3799a0
update environments listing page
iamfaran May 16, 2025
933878e
add breadcrumbs component and fix tabs styling
iamfaran May 16, 2025
8880f3e
fix environments icon on listing page
iamfaran May 16, 2025
b97f125
fix refresh buttons
iamfaran May 16, 2025
f3a36d8
fix tabs rendering issue
iamfaran May 16, 2025
4b9f37d
fix refresh button for the environment listing
iamfaran May 16, 2025
2b4a718
setup for new managed-obj endpoint
iamfaran May 16, 2025
7660432
implement new managed obj endpoints
iamfaran May 16, 2025
6fcafe4
update workspace header UI
iamfaran May 17, 2025
009d6fa
fix width issue for Workspace detai page
iamfaran May 19, 2025
cec515c
add managed filter
iamfaran May 19, 2025
cc3e96a
update UI for workspace header banner
iamfaran May 19, 2025
7a311e6
fix search UI for datasources
iamfaran May 19, 2025
66365b3
add managed filter for Workspaces Tab
iamfaran May 19, 2025
359d94d
change action button positions for TABS
iamfaran May 19, 2025
cde8c0c
update breadcrumbs position
iamfaran May 19, 2025
c1c874b
test managed linked object
iamfaran May 19, 2025
23f526d
make a util function to link the managed object
iamfaran May 19, 2025
266def6
Merge branch 'ee-setup' of github.com:lowcoder-org/lowcoder into feat…
iamfaran May 19, 2025
c1d68dc
Merge branch 'ui/environments' into feat/environments
iamfaran May 19, 2025
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
Prev Previous commit
Next Next commit
update UI user groups tab
  • Loading branch information
iamfaran committed May 16, 2025
commit 548e73b3586959c15cfbf68823f7b4c989909d85
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useState, useEffect } from 'react';
import { Card, Button, Divider, Alert, message, Table, Tag, Input, Space } from 'antd';
import { SyncOutlined, TeamOutlined } from '@ant-design/icons';
import { Card, Button, Alert, message, Table, Tag, Input, Space, Row, Col, Avatar, Tooltip } from 'antd';
import { SyncOutlined, TeamOutlined, UserOutlined, UsergroupAddOutlined, SettingOutlined, CodeOutlined } from '@ant-design/icons';
import Title from 'antd/lib/typography/Title';
import { Environment } from '../types/environment.types';
import { UserGroup, UserGroupsTabStats } from '../types/userGroup.types';
Expand Down Expand Up @@ -89,91 +89,166 @@ const UserGroupsTab: React.FC<UserGroupsTabProps> = ({ environment }) => {
group.groupId.toLowerCase().includes(searchText.toLowerCase()))
: userGroups;

// Helper function to generate colors from strings
const stringToColor = (str: string) => {
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
}

const hue = Math.abs(hash % 360);
return `hsl(${hue}, 70%, 50%)`;
};

// Stat card component
const StatCard = ({ title, value, icon }: { title: string; value: number; icon: React.ReactNode }) => (
<Card
style={{
height: '100%',
borderRadius: '8px',
boxShadow: '0 2px 8px rgba(0,0,0,0.05)'
}}
>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<div>
<div style={{ fontSize: '14px', color: '#8c8c8c', marginBottom: '8px' }}>{title}</div>
<div style={{ fontSize: '24px', fontWeight: 600 }}>{value}</div>
</div>
<div style={{
fontSize: '28px',
opacity: 0.8,
color: '#722ed1',
padding: '12px',
backgroundColor: 'rgba(114, 46, 209, 0.1)',
borderRadius: '50%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}>
{icon}
</div>
</div>
</Card>
);

// Table columns
const columns = [
{
title: 'Name',
dataIndex: 'groupName',
key: 'groupName',
render: (text: string) => <span className="group-name">{text}</span>
},
{
title: 'ID',
dataIndex: 'groupId',
key: 'groupId',
ellipsis: true,
title: 'User Group',
key: 'group',
render: (group: UserGroup) => (
<div style={{ display: 'flex', alignItems: 'center' }}>
<Avatar
style={{
backgroundColor: stringToColor(group.groupName),
marginRight: 12
}}
shape="square"
>
{group.groupName.charAt(0).toUpperCase()}
</Avatar>
<div>
<div style={{ fontWeight: 500 }}>{group.groupName}</div>
<div style={{ fontSize: 12, color: '#8c8c8c', marginTop: 4 }}>
{group.groupId}
</div>
</div>
</div>
),
},
{
title: 'Type',
key: 'type',
render: (_: any, group: UserGroup) => {
if (group.allUsersGroup) return <Tag color="blue">All Users</Tag>;
if (group.devGroup) return <Tag color="purple">Developers</Tag>;
return <Tag color="default">Custom</Tag>;
if (group.allUsersGroup) return (
<Tag color="blue" style={{ borderRadius: '12px' }}>
<UserOutlined style={{ marginRight: 4 }} /> All Users
</Tag>
);
if (group.devGroup) return (
<Tag color="purple" style={{ borderRadius: '12px' }}>
<CodeOutlined style={{ marginRight: 4 }} /> Developers
</Tag>
);
return (
<Tag color="default" style={{ borderRadius: '12px' }}>
<SettingOutlined style={{ marginRight: 4 }} /> Custom
</Tag>
);
},
},
{
title: 'Members',
key: 'members',
render: (_: any, group: UserGroup) => group.stats?.userCount || 0,
render: (_: any, group: UserGroup) => (
<Tooltip title="Total number of members in this group">
<Tag style={{ borderRadius: '12px', backgroundColor: '#f6f6f6', color: '#333' }}>
<UserOutlined style={{ marginRight: 4 }} /> {group.stats?.userCount || 0}
</Tag>
</Tooltip>
),
},
{
title: 'Admin Members',
key: 'adminMembers',
render: (_: any, group: UserGroup) => group.stats?.adminUserCount || 0,
render: (_: any, group: UserGroup) => (
<Tooltip title="Number of admin users in this group">
<Tag style={{ borderRadius: '12px', backgroundColor: '#fff1f0', color: '#cf1322' }}>
<UserOutlined style={{ marginRight: 4 }} /> {group.stats?.adminUserCount || 0}
</Tag>
</Tooltip>
),
},
{
title: 'Created',
dataIndex: 'createTime',
key: 'createTime',
render: (createTime: number) => new Date(createTime).toLocaleDateString(),
render: (createTime: number) => (
<span style={{ color: '#8c8c8c' }}>
{new Date(createTime).toLocaleDateString()}
</span>
),
}
];

return (
<Card>
{/* Header with refresh button */}
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "16px" }}>
<Title level={5}>User Groups in this Environment</Title>
<div style={{ padding: '16px 0' }}>
{/* Header */}
<div style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: "24px",
background: 'linear-gradient(135deg, #722ed1 0%, #eb2f96 100%)',
padding: '20px 24px',
borderRadius: '8px',
color: 'white'
}}>
<div>
<Title level={4} style={{ color: 'white', margin: 0 }}>
<UsergroupAddOutlined style={{ marginRight: 10 }} /> User Groups
</Title>
<p style={{ marginBottom: 0 }}>Manage user groups in this environment</p>
</div>
<Button
icon={<SyncOutlined spin={refreshing} />}
onClick={handleRefresh}
loading={loading}
type="primary"
ghost
>
Refresh
</Button>
</div>

{/* Stats display */}
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '24px', marginBottom: '16px' }}>
<div>
<div style={{ fontSize: '14px', color: '#8c8c8c' }}>Total Groups</div>
<div style={{ fontSize: '24px', fontWeight: 600 }}>{stats.total}</div>
</div>
<div>
<div style={{ fontSize: '14px', color: '#8c8c8c' }}>All Users Groups</div>
<div style={{ fontSize: '24px', fontWeight: 600 }}>{stats.allUsers}</div>
</div>
<div>
<div style={{ fontSize: '14px', color: '#8c8c8c' }}>Developer Groups</div>
<div style={{ fontSize: '24px', fontWeight: 600 }}>{stats.developers}</div>
</div>
<div>
<div style={{ fontSize: '14px', color: '#8c8c8c' }}>Custom Groups</div>
<div style={{ fontSize: '24px', fontWeight: 600 }}>{stats.custom}</div>
</div>
</div>

<Divider style={{ margin: "16px 0" }} />

{/* Error display */}
{error && (
<Alert
message="Error loading user groups"
description={error}
type="error"
showIcon
style={{ marginBottom: "16px" }}
style={{ marginBottom: "20px" }}
/>
)}

Expand All @@ -184,49 +259,95 @@ const UserGroupsTab: React.FC<UserGroupsTabProps> = ({ environment }) => {
description="Missing required configuration: API key or API service URL"
type="warning"
showIcon
style={{ marginBottom: "16px" }}
style={{ marginBottom: "20px" }}
/>
)}

{/* Stats display */}
<Row gutter={[16, 16]} style={{ marginBottom: '24px' }}>
<Col xs={24} sm={12} md={6}>
<StatCard
title="Total Groups"
value={stats.total}
icon={<TeamOutlined />}
/>
</Col>
<Col xs={24} sm={12} md={6}>
<StatCard
title="All Users Groups"
value={stats.allUsers}
icon={<UserOutlined />}
/>
</Col>
<Col xs={24} sm={12} md={6}>
<StatCard
title="Developer Groups"
value={stats.developers}
icon={<CodeOutlined />}
/>
</Col>
<Col xs={24} sm={12} md={6}>
<StatCard
title="Custom Groups"
value={stats.custom}
icon={<SettingOutlined />}
/>
</Col>
</Row>

{/* Content */}
{loading ? (
<div style={{ display: 'flex', justifyContent: 'center', padding: '20px' }}>
<Spin tip="Loading user groups..." />
</div>
) : userGroups.length === 0 ? (
<Empty
description={error || "No user groups found in this environment"}
image={Empty.PRESENTED_IMAGE_SIMPLE}
/>
) : (
<>
{/* Search Bar */}
<div style={{ marginBottom: 16 }}>
<Search
placeholder="Search user groups by name or ID"
allowClear
onSearch={value => setSearchText(value)}
onChange={e => setSearchText(e.target.value)}
style={{ width: 300 }}
/>
{searchText && filteredUserGroups.length !== userGroups.length && (
<div style={{ marginTop: 8 }}>
Showing {filteredUserGroups.length} of {userGroups.length} user groups
</div>
)}
<Card
style={{
borderRadius: '8px',
boxShadow: '0 2px 8px rgba(0,0,0,0.05)'
}}
>
{loading ? (
<div style={{ display: 'flex', justifyContent: 'center', padding: '40px' }}>
<Spin size="large" tip="Loading user groups..." />
</div>

<Table
columns={columns}
dataSource={filteredUserGroups}
rowKey="groupId"
pagination={{ pageSize: 10 }}
size="middle"
scroll={{ x: 'max-content' }}
) : userGroups.length === 0 ? (
<Empty
description={error || "No user groups found in this environment"}
image={Empty.PRESENTED_IMAGE_SIMPLE}
/>
</>
)}
</Card>
) : (
<>
{/* Search Bar */}
<div style={{ marginBottom: 20 }}>
<Search
placeholder="Search user groups by name or ID"
allowClear
onSearch={value => setSearchText(value)}
onChange={e => setSearchText(e.target.value)}
style={{ width: 300 }}
size="large"
/>
{searchText && filteredUserGroups.length !== userGroups.length && (
<div style={{ marginTop: 8, color: '#8c8c8c' }}>
Showing {filteredUserGroups.length} of {userGroups.length} user groups
</div>
)}
</div>

<Table
columns={columns}
dataSource={filteredUserGroups}
rowKey="groupId"
pagination={{
pageSize: 10,
showTotal: (total, range) => `${range[0]}-${range[1]} of ${total} user groups`
}}
style={{
borderRadius: '8px',
overflow: 'hidden'
}}
rowClassName={() => 'group-row'}
/>
</>
)}
</Card>
</div>
);
};

Expand Down








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/lowcoder-org/lowcoder/pull/1687/commits/548e73b3586959c15cfbf68823f7b4c989909d85

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy