Skip to content

Commit 817ff1a

Browse files
committed
Add hubspot modal for the unlicensed environment
1 parent 6fafbae commit 817ff1a

File tree

2 files changed

+215
-131
lines changed

2 files changed

+215
-131
lines changed
Lines changed: 172 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -1,132 +1,173 @@
1-
import React, { useEffect } from 'react';
2-
import { Modal, Card, Row, Col, Typography, Divider } from 'antd';
3-
import { CustomerServiceOutlined, CloudServerOutlined } from '@ant-design/icons';
4-
import { useSelector, useDispatch } from 'react-redux';
5-
import { getDeploymentId } from 'redux/selectors/configSelectors';
6-
import { fetchDeploymentIdAction } from 'redux/reduxActions/configActions';
7-
import { Environment } from '../types/environment.types';
8-
9-
const { Title, Text } = Typography;
10-
11-
interface ContactLowcoderModalProps {
12-
visible: boolean;
13-
onClose: () => void;
14-
environment: Environment;
15-
}
16-
17-
/**
18-
* Professional modal for contacting Lowcoder team with HubSpot form integration
19-
*/
20-
const ContactLowcoderModal: React.FC<ContactLowcoderModalProps> = ({
21-
visible,
22-
onClose,
23-
environment
24-
}) => {
25-
// Get deploymentId from Redux config provider
26-
const deploymentId = useSelector(getDeploymentId);
27-
const dispatch = useDispatch();
28-
29-
// Fetch deployment ID when modal opens if not already available
30-
useEffect(() => {
31-
if (visible && !deploymentId) {
32-
dispatch(fetchDeploymentIdAction());
33-
}
34-
}, [visible, deploymentId, dispatch]);
35-
36-
return (
37-
<Modal
38-
title={
39-
<div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
40-
<CustomerServiceOutlined style={{ fontSize: '20px', color: '#1890ff' }} />
41-
<span style={{ fontSize: '18px', fontWeight: 600 }}>Contact Lowcoder Team</span>
42-
</div>
43-
}
44-
open={visible}
45-
onCancel={onClose}
46-
footer={null}
47-
width={800}
48-
centered
49-
style={{ top: 20 }}
50-
bodyStyle={{ padding: '24px' }}
51-
>
52-
{/* Environment Context Section */}
53-
<Card
54-
style={{
55-
marginBottom: '24px',
56-
borderRadius: '8px',
57-
background: '#fafafa',
58-
border: '1px solid #f0f0f0'
59-
}}
60-
bodyStyle={{ padding: '16px' }}
61-
>
62-
<Row gutter={[16, 8]} align="middle">
63-
<Col>
64-
<CloudServerOutlined style={{ fontSize: '24px', color: '#1890ff' }} />
65-
</Col>
66-
<Col flex={1}>
67-
<div>
68-
<Text strong style={{ fontSize: '16px', color: '#262626' }}>
69-
Environment: {environment.environmentName || 'Unnamed Environment'}
70-
</Text>
71-
<br />
72-
<Text style={{ fontSize: '14px', color: '#8c8c8c', fontFamily: 'monospace' }}>
73-
Environment ID: {environment.environmentId}
74-
</Text>
75-
<br />
76-
<Text style={{ fontSize: '14px', color: '#8c8c8c', fontFamily: 'monospace' }}>
77-
Deployment ID: {deploymentId || 'Loading...'}
78-
</Text>
79-
</div>
80-
</Col>
81-
</Row>
82-
</Card>
83-
84-
<Divider style={{ margin: '16px 0' }} />
85-
86-
{/* HubSpot Form Integration Section */}
87-
<div style={{ minHeight: '400px', padding: '20px 0' }}>
88-
<Title level={4} style={{ textAlign: 'center', marginBottom: '24px', color: '#262626' }}>
89-
Get in Touch
90-
</Title>
91-
92-
<Text style={{
93-
textAlign: 'center',
94-
display: 'block',
95-
marginBottom: '32px',
96-
fontSize: '16px',
97-
color: '#595959',
98-
lineHeight: '1.6'
99-
}}>
100-
Our team is here to help you resolve licensing issues and get your environment up and running.
101-
</Text>
102-
103-
{/* HubSpot Form Container */}
104-
<div style={{
105-
minHeight: '300px',
106-
display: 'flex',
107-
alignItems: 'center',
108-
justifyContent: 'center',
109-
background: '#fdfdfd',
110-
borderRadius: '8px',
111-
border: '1px solid #f0f0f0'
112-
}}>
113-
{/* HubSpot form will be integrated here */}
114-
<div style={{
115-
textAlign: 'center',
116-
color: '#8c8c8c',
117-
fontSize: '14px'
118-
}}>
119-
<CustomerServiceOutlined style={{ fontSize: '48px', marginBottom: '16px', color: '#d9d9d9' }} />
120-
<div>Contact form will be integrated here</div>
121-
</div>
122-
</div>
123-
124-
{/* Environment data is available for form pre-filling */}
125-
{/* Data available: environment.environmentName, environment.environmentId, deploymentId,
126-
environment.licenseStatus, environment.environmentType, environment.licenseError */}
127-
</div>
128-
</Modal>
129-
);
130-
};
131-
1+
import React, { useState, useEffect } from 'react';
2+
import { Modal, Card, Row, Col, Typography, Divider, Spin, Alert } from 'antd';
3+
import { CustomerServiceOutlined, CloudServerOutlined } from '@ant-design/icons';
4+
import { Environment } from '../types/environment.types';
5+
import { getEnvironmentDeploymentId } from '../services/environments.service';
6+
import { HubspotModal } from '../../hubspotModal';
7+
8+
const { Title, Text } = Typography;
9+
10+
interface ContactLowcoderModalProps {
11+
visible: boolean;
12+
onClose: () => void;
13+
environment: Environment;
14+
}
15+
16+
/**
17+
* Professional modal for contacting Lowcoder team with HubSpot form integration
18+
*/
19+
const ContactLowcoderModal: React.FC<ContactLowcoderModalProps> = ({
20+
visible,
21+
onClose,
22+
environment
23+
}) => {
24+
const [deploymentId, setDeploymentId] = useState<string | null>(null);
25+
const [isLoading, setIsLoading] = useState(false);
26+
const [error, setError] = useState<string | null>(null);
27+
const [showHubspotModal, setShowHubspotModal] = useState(false);
28+
29+
// Fetch deployment ID when modal opens
30+
useEffect(() => {
31+
if (visible && environment.environmentApiServiceUrl && environment.environmentApikey) {
32+
setIsLoading(true);
33+
setError(null);
34+
35+
getEnvironmentDeploymentId(
36+
environment.environmentApiServiceUrl,
37+
environment.environmentApikey
38+
)
39+
.then((id) => {
40+
setDeploymentId(id);
41+
setShowHubspotModal(true);
42+
})
43+
.catch((err) => {
44+
console.error('Failed to fetch deployment ID:', err);
45+
setError(err instanceof Error ? err.message : 'Failed to fetch deployment ID');
46+
})
47+
.finally(() => {
48+
setIsLoading(false);
49+
});
50+
} else if (visible) {
51+
setError('Environment API service URL or API key not configured');
52+
}
53+
}, [visible, environment.environmentApiServiceUrl, environment.environmentApikey]);
54+
55+
// Handle HubSpot modal close
56+
const handleHubspotClose = () => {
57+
setShowHubspotModal(false);
58+
onClose();
59+
};
60+
61+
// Handle main modal close
62+
const handleClose = () => {
63+
setShowHubspotModal(false);
64+
setDeploymentId(null);
65+
setError(null);
66+
onClose();
67+
};
68+
69+
// Show HubSpot modal if we have deployment ID
70+
if (showHubspotModal && deploymentId) {
71+
return (
72+
<HubspotModal
73+
open={showHubspotModal}
74+
onClose={handleHubspotClose}
75+
orgId={environment.environmentId}
76+
deploymentIds={[deploymentId]}
77+
/>
78+
);
79+
}
80+
81+
return (
82+
<Modal
83+
title={
84+
<div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
85+
<CustomerServiceOutlined style={{ fontSize: '20px', color: '#1890ff' }} />
86+
<span style={{ fontSize: '18px', fontWeight: 600 }}>Contact Lowcoder Team</span>
87+
</div>
88+
}
89+
open={visible}
90+
onCancel={handleClose}
91+
footer={null}
92+
width={800}
93+
centered
94+
style={{ top: 20 }}
95+
bodyStyle={{ padding: '24px' }}
96+
>
97+
{/* Environment Context Section */}
98+
<Card
99+
style={{
100+
marginBottom: '24px',
101+
borderRadius: '8px',
102+
background: '#fafafa',
103+
border: '1px solid #f0f0f0'
104+
}}
105+
bodyStyle={{ padding: '16px' }}
106+
>
107+
<Row gutter={[16, 8]} align="middle">
108+
<Col>
109+
<CloudServerOutlined style={{ fontSize: '24px', color: '#1890ff' }} />
110+
</Col>
111+
<Col flex={1}>
112+
<div>
113+
<Text strong style={{ fontSize: '16px', color: '#262626' }}>
114+
Environment: {environment.environmentName || 'Unnamed Environment'}
115+
</Text>
116+
<br />
117+
<Text style={{ fontSize: '14px', color: '#8c8c8c', fontFamily: 'monospace' }}>
118+
Environment ID: {environment.environmentId}
119+
</Text>
120+
<br />
121+
<Text style={{ fontSize: '14px', color: '#8c8c8c', fontFamily: 'monospace' }}>
122+
Deployment ID: {isLoading ? 'Loading...' : deploymentId || 'Not available'}
123+
</Text>
124+
</div>
125+
</Col>
126+
</Row>
127+
</Card>
128+
129+
<Divider style={{ margin: '16px 0' }} />
130+
131+
{/* Loading, Error, or Success State */}
132+
<div style={{ minHeight: '200px', padding: '20px 0' }}>
133+
{isLoading && (
134+
<div style={{
135+
display: 'flex',
136+
flexDirection: 'column',
137+
alignItems: 'center',
138+
justifyContent: 'center',
139+
minHeight: '200px'
140+
}}>
141+
<Spin size="large" />
142+
<Text style={{ marginTop: '16px', color: '#8c8c8c' }}>
143+
Fetching deployment information...
144+
</Text>
145+
</div>
146+
)}
147+
148+
{error && (
149+
<Alert
150+
message="Unable to Load Contact Form"
151+
description={error}
152+
type="error"
153+
showIcon
154+
style={{ marginBottom: '16px' }}
155+
/>
156+
)}
157+
158+
{!isLoading && !error && !deploymentId && (
159+
<div style={{
160+
textAlign: 'center',
161+
color: '#8c8c8c',
162+
fontSize: '14px'
163+
}}>
164+
<CustomerServiceOutlined style={{ fontSize: '48px', marginBottom: '16px', color: '#d9d9d9' }} />
165+
<div>Please ensure the environment is properly configured to contact support.</div>
166+
</div>
167+
)}
168+
</div>
169+
</Modal>
170+
);
171+
};
172+
132173
export default ContactLowcoderModal;

client/packages/lowcoder/src/pages/setting/environments/services/environments.service.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,4 +578,47 @@ export async function getEnvironmentsWithLicenseStatus(): Promise<Environment[]>
578578
messageInstance.error(errorMessage);
579579
throw error;
580580
}
581+
}
582+
583+
/**
584+
* Fetch deployment ID from a specific environment
585+
* @param apiServiceUrl - API service URL for the environment
586+
* @param apiKey - API key for the environment
587+
* @returns Promise with deployment ID string
588+
*/
589+
export async function getEnvironmentDeploymentId(
590+
apiServiceUrl: string,
591+
apiKey: string
592+
): Promise<string> {
593+
try {
594+
// Check if required parameters are provided
595+
if (!apiServiceUrl) {
596+
throw new Error('API service URL is required');
597+
}
598+
599+
if (!apiKey) {
600+
throw new Error('API key is required to fetch deployment ID');
601+
}
602+
603+
// Set up headers with the Bearer token format
604+
const headers = {
605+
Authorization: `Bearer ${apiKey}`
606+
};
607+
608+
// Make the API request to get deployment ID
609+
const response = await axios.get(`${apiServiceUrl}/api/configs/deploymentId`, { headers });
610+
611+
// Check if response is valid
612+
if (!response.data) {
613+
throw new Error('Failed to fetch deployment ID');
614+
}
615+
616+
// The response should return a string directly
617+
return response.data;
618+
} catch (error) {
619+
// Handle and transform error
620+
const errorMessage = error instanceof Error ? error.message : 'Failed to fetch deployment ID';
621+
messageInstance.error(errorMessage);
622+
throw error;
623+
}
581624
}

0 commit comments

Comments
 (0)