Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 83973af

Browse files
committed
add DeployModal in context
1 parent b4b8c1c commit 83973af

File tree

6 files changed

+337
-23
lines changed

6 files changed

+337
-23
lines changed
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
// components/DeployItemModal.tsx
2+
import React, { useState, useEffect } from 'react';
3+
import { Modal, Form, Select, Checkbox, Button, message, Spin, Input } from 'antd';
4+
import { Environment } from '../types/environment.types';
5+
import { DeployableItem, BaseStats, DeployableItemConfig } from '../types/deployable-item.types';
6+
import { useEnvironmentContext } from '../context/EnvironmentContext';
7+
8+
interface DeployItemModalProps<T extends DeployableItem, S extends BaseStats> {
9+
visible: boolean;
10+
item: T | null;
11+
sourceEnvironment: Environment;
12+
config: DeployableItemConfig<T, S>;
13+
onClose: () => void;
14+
onSuccess?: () => void;
15+
}
16+
17+
function DeployItemModal<T extends DeployableItem, S extends BaseStats>({
18+
visible,
19+
item,
20+
sourceEnvironment,
21+
config,
22+
onClose,
23+
onSuccess
24+
}: DeployItemModalProps<T, S>) {
25+
const [form] = Form.useForm();
26+
const { environments, isLoadingEnvironments } = useEnvironmentContext();
27+
const [deploying, setDeploying] = useState(false);
28+
29+
useEffect(() => {
30+
if (visible) {
31+
form.resetFields();
32+
}
33+
}, [visible, form]);
34+
35+
// Filter out source environment from target list
36+
const targetEnvironments = environments.filter(
37+
env => env.environmentId !== sourceEnvironment.environmentId
38+
);
39+
40+
const handleDeploy = async () => {
41+
if (!config.deploy?.enabled || !item) return;
42+
43+
try {
44+
const values = await form.validateFields();
45+
const targetEnv = environments.find(env => env.environmentId === values.targetEnvId);
46+
47+
if (!targetEnv) {
48+
message.error('Target environment not found');
49+
return;
50+
}
51+
52+
setDeploying(true);
53+
54+
// Prepare parameters based on item type
55+
const params = config.deploy.prepareParams(item, values, sourceEnvironment, targetEnv);
56+
57+
// Execute deployment
58+
await config.deploy.execute(params);
59+
60+
message.success(`Successfully deployed ${item.name} to target environment`);
61+
if (onSuccess) onSuccess();
62+
onClose();
63+
} catch (error) {
64+
console.error('Deployment error:', error);
65+
message.error(`Failed to deploy ${config.singularLabel.toLowerCase()}`);
66+
} finally {
67+
setDeploying(false);
68+
}
69+
};
70+
71+
return (
72+
<Modal
73+
title={`Deploy ${config.singularLabel}: ${item?.name || ''}`}
74+
open={visible}
75+
onCancel={onClose}
76+
footer={null}
77+
destroyOnClose
78+
>
79+
{isLoadingEnvironments ? (
80+
<div style={{ textAlign: 'center', padding: '20px' }}>
81+
<Spin tip="Loading environments..." />
82+
</div>
83+
) : (
84+
<Form
85+
form={form}
86+
layout="vertical"
87+
>
88+
<Form.Item
89+
name="targetEnvId"
90+
label="Target Environment"
91+
rules={[{ required: true, message: 'Please select a target environment' }]}
92+
>
93+
<Select placeholder="Select target environment">
94+
{targetEnvironments.map((env) => (
95+
<Select.Option key={env.environmentId} value={env.environmentId}>
96+
{env.environmentName}
97+
</Select.Option>
98+
))}
99+
</Select>
100+
</Form.Item>
101+
102+
{/* Render dynamic fields based on config */}
103+
{config.deploy?.fields.map(field => {
104+
switch (field.type) {
105+
case 'checkbox':
106+
return (
107+
<Form.Item
108+
key={field.name}
109+
name={field.name}
110+
valuePropName="checked"
111+
initialValue={field.defaultValue}
112+
>
113+
<Checkbox>{field.label}</Checkbox>
114+
</Form.Item>
115+
);
116+
case 'select':
117+
return (
118+
<Form.Item
119+
key={field.name}
120+
name={field.name}
121+
label={field.label}
122+
initialValue={field.defaultValue}
123+
rules={field.required ? [{ required: true, message: `Please select ${field.label}` }] : undefined}
124+
>
125+
<Select placeholder={`Select ${field.label}`}>
126+
{field.options?.map(option => (
127+
<Select.Option key={option.value} value={option.value}>
128+
{option.label}
129+
</Select.Option>
130+
))}
131+
</Select>
132+
</Form.Item>
133+
);
134+
case 'input':
135+
return (
136+
<Form.Item
137+
key={field.name}
138+
name={field.name}
139+
label={field.label}
140+
initialValue={field.defaultValue}
141+
rules={field.required ? [{ required: true, message: `Please input ${field.label}` }] : undefined}
142+
>
143+
<Input placeholder={`Enter ${field.label}`} />
144+
</Form.Item>
145+
);
146+
default:
147+
return null;
148+
}
149+
})}
150+
151+
<Form.Item>
152+
<Button type="default" onClick={onClose} style={{ marginRight: 8 }}>
153+
Cancel
154+
</Button>
155+
<Button type="primary" onClick={handleDeploy} loading={deploying}>
156+
Deploy
157+
</Button>
158+
</Form.Item>
159+
</Form>
160+
)}
161+
</Modal>
162+
);
163+
}
164+
165+
export default DeployItemModal;

‎client/packages/lowcoder/src/pages/setting/environments/components/DeployableItemsList.tsx

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
// components/DeployableItemsList.tsx
22
import React from 'react';
3-
import { Table, Tag, Empty, Spin, Switch, Space } from 'antd';
3+
import { Table, Tag, Empty, Spin, Switch, Space, Button, Tooltip } from 'antd';
4+
import { CloudUploadOutlined } from '@ant-design/icons';
45
import history from '@lowcoder-ee/util/history';
56
import { DeployableItem, BaseStats, DeployableItemConfig } from '../types/deployable-item.types';
67
import { Environment } from '../types/environment.types';
8+
import { useDeployModal } from '../context/DeployModalContext';
79

810
interface DeployableItemsListProps<T extends DeployableItem, S extends BaseStats> {
911
items: T[];
@@ -26,6 +28,10 @@ function DeployableItemsList<T extends DeployableItem, S extends BaseStats>({
2628
onToggleManaged,
2729
additionalParams = {}
2830
}: DeployableItemsListProps<T, S>) {
31+
32+
const { openDeployModal } = useDeployModal();
33+
34+
2935
// Handle row click for navigation
3036
// Handle row click for navigation
3137
const handleRowClick = (item: T) => {
@@ -72,6 +78,32 @@ const handleRowClick = (item: T) => {
7278
});
7379
}
7480

81+
// Add deploy action column if enabled
82+
if (config.deploy?.enabled) {
83+
columns.push({
84+
title: 'Actions',
85+
key: 'actions',
86+
render: (_, record: T) => (
87+
<Space>
88+
<Tooltip title={`Deploy this ${config.singularLabel.toLowerCase()} to another environment`}>
89+
<Button
90+
icon={<CloudUploadOutlined />}
91+
onClick={(e) => {
92+
e.stopPropagation(); // Prevent row click navigation
93+
openDeployModal(record, config, environment);
94+
}}
95+
type="primary"
96+
ghost
97+
>
98+
Deploy
99+
</Button>
100+
</Tooltip>
101+
</Space>
102+
),
103+
});
104+
}
105+
106+
75107
if (loading) {
76108
return (
77109
<div style={{ display: 'flex', justifyContent: 'center', padding: '20px' }}>

‎client/packages/lowcoder/src/pages/setting/environments/components/EnvironmentScopedRoutes.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { EnvironmentProvider } from "../context/EnvironmentContext";
44

55
import EnvironmentDetail from "../EnvironmentDetail";
66
import WorkspaceDetail from "../WorkspaceDetail";
7+
import { DeployModalProvider } from "../context/DeployModalContext";
78

89
import {
910
ENVIRONMENT_DETAIL,
@@ -15,6 +16,8 @@ const EnvironmentScopedRoutes: React.FC = () => {
1516

1617
return (
1718
<EnvironmentProvider envId={environmentId}>
19+
20+
<DeployModalProvider>
1821
<Switch>
1922
<Route exact path={ENVIRONMENT_DETAIL}>
2023
<EnvironmentDetail />
@@ -24,6 +27,7 @@ const EnvironmentScopedRoutes: React.FC = () => {
2427
<WorkspaceDetail />
2528
</Route>
2629
</Switch>
30+
</DeployModalProvider>
2731
</EnvironmentProvider>
2832
);
2933
};

‎client/packages/lowcoder/src/pages/setting/environments/config/apps.config.tsx

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Row, Col, Statistic, Tag, Space, Button, Tooltip } from 'antd';
44
import { AppstoreOutlined, CloudUploadOutlined } from '@ant-design/icons';
55
import {DeployableItemConfig } from '../types/deployable-item.types';
66
import { Environment } from '../types/environment.types';
7-
import { getMergedWorkspaceApps } from '../services/apps.service';
7+
import { getMergedWorkspaceApps, deployApp } from '../services/apps.service';
88
import { connectManagedApp, unconnectManagedApp } from '../services/enterprise.service';
99
import { App, AppStats } from '../types/app.types';
1010

@@ -81,27 +81,6 @@ export const appsConfig: DeployableItemConfig<App, AppStats> = {
8181
</Tag>
8282
),
8383
},
84-
{
85-
title: 'Actions',
86-
key: 'actions',
87-
render: (_, record: App) => (
88-
<Space>
89-
<Tooltip title="Deploy to another environment">
90-
<Button
91-
icon={<CloudUploadOutlined />}
92-
onClick={(e) => {
93-
e.stopPropagation(); // Prevent row click navigation
94-
// Deploy action will be handled by the DeployItemModal
95-
}}
96-
type="primary"
97-
ghost
98-
>
99-
Deploy
100-
</Button>
101-
</Tooltip>
102-
</Space>
103-
),
104-
}
10584
],
10685

10786
// Deployment options
@@ -139,5 +118,48 @@ export const appsConfig: DeployableItemConfig<App, AppStats> = {
139118
console.error('Error toggling managed status:', error);
140119
return false;
141120
}
121+
},
122+
// deployment options
123+
124+
deploy: {
125+
enabled: true,
126+
fields: [
127+
{
128+
name: 'updateDependenciesIfNeeded',
129+
label: 'Update Dependencies If Needed',
130+
type: 'checkbox',
131+
defaultValue: false
132+
},
133+
{
134+
name: 'publishOnTarget',
135+
label: 'Publish On Target',
136+
type: 'checkbox',
137+
defaultValue: false
138+
},
139+
{
140+
name: 'publicToAll',
141+
label: 'Public To All',
142+
type: 'checkbox',
143+
defaultValue: false
144+
},
145+
{
146+
name: 'publicToMarketplace',
147+
label: 'Public To Marketplace',
148+
type: 'checkbox',
149+
defaultValue: false
150+
}
151+
],
152+
prepareParams: (item: App, values: any, sourceEnv: Environment, targetEnv: Environment) => {
153+
return {
154+
envId: sourceEnv.environmentId,
155+
targetEnvId: targetEnv.environmentId,
156+
applicationId: item.applicationId,
157+
updateDependenciesIfNeeded: values.updateDependenciesIfNeeded,
158+
publishOnTarget: values.publishOnTarget,
159+
publicToAll: values.publicToAll,
160+
publicToMarketplace: values.publicToMarketplace,
161+
};
162+
},
163+
execute: (params: any) => deployApp(params)
142164
}
143165
};

0 commit comments

Comments
 (0)