diff --git a/client/packages/lowcoder/src/pages/setting/environments/EnvironmentDetail.tsx b/client/packages/lowcoder/src/pages/setting/environments/EnvironmentDetail.tsx index b772bf373..a3a15ec03 100644 --- a/client/packages/lowcoder/src/pages/setting/environments/EnvironmentDetail.tsx +++ b/client/packages/lowcoder/src/pages/setting/environments/EnvironmentDetail.tsx @@ -93,12 +93,37 @@ const EnvironmentDetail: React.FC = () => { if (error || !environment) { return ( - +
+ + + history.push("/setting/environments")} + > + Environments + + + Not Found + + + +
+ + Environment Not Found + + + {error || "The environment you're looking for doesn't exist or you don't have permission to view it."} + + +
+
+
); } diff --git a/client/packages/lowcoder/src/pages/setting/environments/components/DeployItemModal.tsx b/client/packages/lowcoder/src/pages/setting/environments/components/DeployItemModal.tsx index a58ac7d78..b7ae9bb06 100644 --- a/client/packages/lowcoder/src/pages/setting/environments/components/DeployItemModal.tsx +++ b/client/packages/lowcoder/src/pages/setting/environments/components/DeployItemModal.tsx @@ -1,9 +1,11 @@ // components/DeployItemModal.tsx import React, { useState, useEffect } from 'react'; -import { Modal, Form, Select, Checkbox, Button, message, Spin, Input } from 'antd'; +import { Modal, Form, Select, Checkbox, Button, message, Spin, Input, Tag, Space } from 'antd'; import { Environment } from '../types/environment.types'; import { DeployableItem, BaseStats, DeployableItemConfig } from '../types/deployable-item.types'; import { useEnvironmentContext } from '../context/EnvironmentContext'; +import { getEnvironmentTagColor, formatEnvironmentType } from '../utils/environmentUtils'; + interface DeployItemModalProps { visible: boolean; item: T | null; @@ -84,6 +86,18 @@ function DeployItemModal({ form={form} layout="vertical" > + {/* Source environment display */} + + + {sourceEnvironment.environmentName} + {sourceEnvironment.environmentType && ( + + {formatEnvironmentType(sourceEnvironment.environmentType)} + + )} + + + ({ diff --git a/client/packages/lowcoder/src/pages/setting/environments/components/DeployableItemsList.tsx b/client/packages/lowcoder/src/pages/setting/environments/components/DeployableItemsList.tsx index 63f8dda72..bed49855d 100644 --- a/client/packages/lowcoder/src/pages/setting/environments/components/DeployableItemsList.tsx +++ b/client/packages/lowcoder/src/pages/setting/environments/components/DeployableItemsList.tsx @@ -1,12 +1,14 @@ // components/DeployableItemsList.tsx -import React from 'react'; -import { Table, Tag, Empty, Spin, Switch, Space, Button, Tooltip } from 'antd'; -import { CloudUploadOutlined } from '@ant-design/icons'; +import React, { useState } from 'react'; +import { Table, Tag, Empty, Spin, Switch, Space, Button, Tooltip, Input } from 'antd'; +import { CloudUploadOutlined, SearchOutlined } from '@ant-design/icons'; import history from '@lowcoder-ee/util/history'; import { DeployableItem, BaseStats, DeployableItemConfig } from '../types/deployable-item.types'; import { Environment } from '../types/environment.types'; import { useDeployModal } from '../context/DeployModalContext'; +const { Search } = Input; + interface DeployableItemsListProps { items: T[]; loading: boolean; @@ -30,6 +32,14 @@ function DeployableItemsList({ }: DeployableItemsListProps) { const { openDeployModal } = useDeployModal(); + const [searchText, setSearchText] = useState(''); + + // Filter items based on search + const filteredItems = searchText + ? items.filter(item => + item.name.toLowerCase().includes(searchText.toLowerCase()) || + item.id.toLowerCase().includes(searchText.toLowerCase())) + : items; // Handle row click for navigation const handleRowClick = (item: T) => { @@ -53,8 +63,7 @@ function DeployableItemsList({ onToggleManaged, openDeployModal, additionalParams - }) - + }); if (loading) { return ( @@ -76,18 +85,36 @@ function DeployableItemsList({ const hasNavigation = config.buildDetailRoute({}) !== '#'; return ( - ({ - onClick: hasNavigation ? () => handleRowClick(record) : undefined, - style: hasNavigation ? { cursor: 'pointer' } : undefined, - })} - /> + <> + {/* Search Bar */} +
+ setSearchText(value)} + onChange={e => setSearchText(e.target.value)} + style={{ width: 300 }} + /> + {searchText && filteredItems.length !== items.length && ( +
+ Showing {filteredItems.length} of {items.length} {config.pluralLabel.toLowerCase()} +
+ )} +
+ +
({ + onClick: hasNavigation ? () => handleRowClick(record) : undefined, + style: hasNavigation ? { cursor: 'pointer' } : undefined, + })} + /> + ); } diff --git a/client/packages/lowcoder/src/pages/setting/environments/components/EnvironmentsTable.tsx b/client/packages/lowcoder/src/pages/setting/environments/components/EnvironmentsTable.tsx index 0208932d7..4f9150a9b 100644 --- a/client/packages/lowcoder/src/pages/setting/environments/components/EnvironmentsTable.tsx +++ b/client/packages/lowcoder/src/pages/setting/environments/components/EnvironmentsTable.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { Table, Tag, Button, Tooltip, Space } from 'antd'; import { EditOutlined, AuditOutlined} from '@ant-design/icons'; import { Environment } from '../types/environment.types'; +import { getEnvironmentTagColor, formatEnvironmentType } from '../utils/environmentUtils'; @@ -20,19 +21,6 @@ const EnvironmentsTable: React.FC = ({ loading, onRowClick, }) => { - // Get color for environment type/stage - const getTypeColor = (type: string): string => { - if (!type) return 'default'; - - switch (type.toUpperCase()) { - case 'DEV': return 'blue'; - case 'TEST': return 'orange'; - case 'PREPROD': return 'purple'; - case 'PROD': return 'green'; - default: return 'default'; - } - }; - // Open audit page in new tab const openAuditPage = (environmentId: string, e: React.MouseEvent) => { e.stopPropagation(); // Prevent row click from triggering @@ -65,8 +53,8 @@ const EnvironmentsTable: React.FC = ({ dataIndex: 'environmentType', key: 'environmentType', render: (type: string) => ( - - {type ? type.toUpperCase() : 'UNKNOWN'} + + {formatEnvironmentType(type)} ), }, diff --git a/client/packages/lowcoder/src/pages/setting/environments/utils/columnFactories.tsx b/client/packages/lowcoder/src/pages/setting/environments/utils/columnFactories.tsx index b33685ab7..45c69c580 100644 --- a/client/packages/lowcoder/src/pages/setting/environments/utils/columnFactories.tsx +++ b/client/packages/lowcoder/src/pages/setting/environments/utils/columnFactories.tsx @@ -69,6 +69,13 @@ export function createManagedColumn( return { title: 'Managed', key: 'managed', + filterMode: 'menu', + filters: [ + { text: 'Managed', value: true }, + { text: 'Unmanaged', value: false }, + ], + onFilter: (value, record) => record.managed === value, + filterMultiple: false, render: (_, record: T) => ( @@ -178,6 +185,13 @@ export function createPublishedColumn(): Colu title: 'Status', dataIndex: 'published', key: 'published', + filterMode: 'menu', + filters: [ + { text: 'Published', value: true }, + { text: 'Unpublished', value: false }, + ], + onFilter: (value, record) => record.published === value, + filterMultiple: false, render: (published: boolean) => ( {published ? 'Published' : 'Unpublished'} diff --git a/client/packages/lowcoder/src/pages/setting/environments/utils/environmentUtils.ts b/client/packages/lowcoder/src/pages/setting/environments/utils/environmentUtils.ts new file mode 100644 index 000000000..f3f5d5c1b --- /dev/null +++ b/client/packages/lowcoder/src/pages/setting/environments/utils/environmentUtils.ts @@ -0,0 +1,46 @@ +/** + * Utility functions for environment-related features + */ + +/** + * Get the appropriate color for an environment tag based on its type + * @param envType The environment type/stage (e.g. 'PROD', 'DEV', 'STAGING') + * @returns A color string to use with Ant Design's Tag component + */ +export const getEnvironmentTagColor = (envType: string | undefined): string => { + if (!envType) return 'default'; + + // Normalize to uppercase for consistent comparison + const type = envType.toUpperCase(); + + switch (type) { + // Production environment + case 'PROD': + return 'red'; // Red for production - indicates caution + + // Pre-production environment + case 'PREPROD': + return 'orange'; // Orange for pre-production + + // Test environment + case 'TEST': + return 'purple'; // Purple for test environment + + // Development environment + case 'DEV': + return 'green'; // Green for development - safe to use + + default: + return 'default'; // Default gray for unknown types + } +}; + +/** + * Format an environment type for display + * @param envType The environment type string + * @returns Formatted environment type string + */ +export const formatEnvironmentType = (envType: string | undefined): string => { + if (!envType) return 'UNKNOWN'; + return envType.toUpperCase(); +}; \ No newline at end of file