EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
DownloadAgentModal.jsx
Go to the documentation of this file.
1import React, { useState, useEffect } from "react";
2import "./DownloadAgentModal.css";
3
4export default function DownloadAgentModal({ tenantId, onClose }) {
5 const [error, setError] = useState(null);
6 const [downloadToken, setDownloadToken] = useState(null);
7 const [loading, setLoading] = useState(false);
8 const [copied, setCopied] = useState(false);
9 const [selectedPlatform, setSelectedPlatform] = useState('windows');
10
11 useEffect(() => {
12 generateDownloadToken();
13 }, []);
14
15 const generateDownloadToken = async () => {
16 try {
17 setLoading(true);
18 setError(null);
19
20 const token = localStorage.getItem('token');
21 const backendUrl = 'https://rmm-psa-backend-t9f7k.ondigitalocean.app';
22 const response = await fetch(`${backendUrl}/api/agent-v3/download-token`, {
23 headers: {
24 'Authorization': `Bearer ${token}`
25 }
26 });
27
28 if (!response.ok) throw new Error('Failed to generate download token');
29
30 const data = await response.json();
31 setDownloadToken(data.token);
32 } catch (err) {
33 setError(err.message);
34 } finally {
35 setLoading(false);
36 }
37 };
38
39 const getDownloadUrl = (platform) => {
40 if (!downloadToken) return '';
41 const baseUrl = window.location.origin;
42 const backendUrl = 'https://rmm-psa-backend-t9f7k.ondigitalocean.app';
43 return `${backendUrl}/api/agent-v3/download/${platform}/${downloadToken}?tenant=${tenantId}`;
44 };
45
46 const getInstallCommand = (platform) => {
47 const url = getDownloadUrl(platform);
48 switch (platform) {
49 case 'windows':
50 return `# Run in PowerShell as Administrator\niwr -useb "${url}" -OutFile "$env:TEMP\\agent-v3.exe"; Start-Process "$env:TEMP\\agent-v3.exe" -ArgumentList "/S" -Wait`;
51 case 'linux':
52 return `# Run in terminal\nwget -qO- "${url}" | sudo bash`;
53 case 'macos':
54 return `# Run in terminal\ncurl -sL "${url}" -o ~/Downloads/agent-v3.pkg && open ~/Downloads/agent-v3.pkg`;
55 default:
56 return url;
57 }
58 };
59
60 const copyToClipboard = (text) => {
61 navigator.clipboard.writeText(text);
62 setCopied(true);
63 setTimeout(() => setCopied(false), 2000);
64 };
65
66 const platforms = [
67 { key: 'windows', label: 'Windows', icon: 'window' },
68 { key: 'linux', label: 'Linux', icon: 'terminal' },
69 { key: 'macos', label: 'macOS', icon: 'apple' }
70 ];
71
72 return (
73 <div className="download-modal-overlay">
74 <div className="download-modal modern-modal">
75 <div className="download-modal-header modern-header">
76 <h2>
77 <span className="material-symbols-outlined">download</span>
78 Download Agent v3
79 </h2>
80 <button className="neutral modern-close" onClick={onClose} title="Close">
81 <span className="material-symbols-outlined">close</span>
82 </button>
83 </div>
84 <div className="download-modal-body modern-body">
85 {/* Agent v3 Section */}
86 <div style={{ background: 'linear-gradient(135deg, #f0f9ff, #e0f2fe)', padding: '1.5rem', borderRadius: '8px' }}>
87 <h3 style={{ margin: '0 0 0.5rem 0', fontSize: '1.1rem', fontWeight: 600, color: 'var(--text)', display: 'flex', alignItems: 'center', gap: '8px' }}>
88 <span className="material-symbols-outlined" style={{ fontSize: '1.3rem', color: '#0ea5e9' }}>
89 rocket_launch
90 </span>
91 EverydayTech Agent v3
92 </h3>
93 <p style={{
94 fontSize: '0.9rem',
95 color: 'var(--text-muted, #334155)',
96 margin: '0 0 1rem 0',
97 lineHeight: 1.4
98 }}>
99 Enterprise-grade MeshCentral agent with white-label branding. Automatically registers to tenant {tenantId}.
100 </p>
101
102 {/* Platform Selector */}
103 <div style={{ marginBottom: '1rem' }}>
104 <label style={{ display: 'block', marginBottom: '8px', fontWeight: 500, fontSize: '0.9rem', color: 'var(--text)' }}>
105 Select Platform:
106 </label>
107 <div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap' }}>
108 {platforms.map(platform => (
109 <button
110 key={platform.key}
111 className={`secondary modern-action ${selectedPlatform === platform.key ? 'selected' : ''}`}
112 onClick={() => setSelectedPlatform(platform.key)}
113 style={{
114 minWidth: '100px',
115 background: selectedPlatform === platform.key ? 'linear-gradient(135deg, #0ea5e9, #06b6d4)' : undefined,
116 color: selectedPlatform === platform.key ? 'white' : undefined
117 }}
118 >
119 <span className="material-symbols-outlined">{platform.icon}</span>
120 {platform.label}
121 </button>
122 ))}
123 </div>
124 </div>
125
126 {/* Download URL */}
127 {loading ? (
128 <div style={{ textAlign: 'center', padding: '1rem', color: 'var(--text-muted)' }}>
129 <span className="material-symbols-outlined" style={{ fontSize: '2rem', animation: 'spin 1s linear infinite' }}>refresh</span>
130 <p>Generating download link...</p>
131 </div>
132 ) : downloadToken ? (
133 <>
134 <div style={{ marginBottom: '1rem' }}>
135 <label style={{ display: 'block', marginBottom: '8px', fontWeight: 500, fontSize: '0.9rem', color: 'var(--text)' }}>
136 Direct Download URL:
137 </label>
138 <div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
139 <input
140 type="text"
141 value={getDownloadUrl(selectedPlatform)}
142 readOnly
143 style={{
144 flex: 1,
145 padding: '8px 12px',
146 border: '1px solid var(--border)',
147 borderRadius: '6px',
148 fontSize: '0.85rem',
149 fontFamily: 'monospace',
150 background: 'var(--bg-secondary)',
151 color: 'var(--text)'
152 }}
153 />
154 <button
155 className="secondary modern-action"
156 onClick={() => copyToClipboard(getDownloadUrl(selectedPlatform))}
157 title="Copy URL"
158 >
159 <span className="material-symbols-outlined">
160 {copied ? 'check' : 'content_copy'}
161 </span>
162 {copied ? 'Copied!' : 'Copy'}
163 </button>
164 </div>
165 </div>
166
167 {/* Install Command */}
168 <div style={{ marginBottom: '1rem' }}>
169 <label style={{ display: 'block', marginBottom: '8px', fontWeight: 500, fontSize: '0.9rem', color: 'var(--text)' }}>
170 One-Liner Install Command:
171 </label>
172 <div style={{ position: 'relative' }}>
173 <pre style={{
174 padding: '12px',
175 background: '#1e293b',
176 color: '#e2e8f0',
177 borderRadius: '6px',
178 fontSize: '0.8rem',
179 overflow: 'auto',
180 margin: 0,
181 whiteSpace: 'pre-wrap',
182 wordBreak: 'break-all'
183 }}>
184 {getInstallCommand(selectedPlatform)}
185 </pre>
186 <button
187 className="secondary modern-action"
188 onClick={() => copyToClipboard(getInstallCommand(selectedPlatform))}
189 style={{
190 position: 'absolute',
191 top: '8px',
192 right: '8px',
193 minWidth: 'auto',
194 padding: '4px 8px'
195 }}
196 title="Copy command"
197 >
198 <span className="material-symbols-outlined" style={{ fontSize: '1rem' }}>
199 {copied ? 'check' : 'content_copy'}
200 </span>
201 </button>
202 </div>
203 </div>
204
205 {/* Features */}
206 <div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.5rem', marginBottom: '1rem' }}>
207 <span style={{ fontSize: '0.75rem', background: '#f0fdf4', color: '#166534', padding: '4px 8px', borderRadius: '4px', border: '1px solid #bbf7d0' }}>✓ Remote Desktop</span>
208 <span style={{ fontSize: '0.75rem', background: '#f0fdf4', color: '#166534', padding: '4px 8px', borderRadius: '4px', border: '1px solid #bbf7d0' }}>✓ Terminal Access</span>
209 <span style={{ fontSize: '0.75rem', background: '#f0fdf4', color: '#166534', padding: '4px 8px', borderRadius: '4px', border: '1px solid #bbf7d0' }}>✓ File Transfer</span>
210 <span style={{ fontSize: '0.75rem', background: '#f0fdf4', color: '#166534', padding: '4px 8px', borderRadius: '4px', border: '1px solid #bbf7d0' }}>✓ Auto-Registration</span>
211 <span style={{ fontSize: '0.75rem', background: '#f0fdf4', color: '#166534', padding: '4px 8px', borderRadius: '4px', border: '1px solid #bbf7d0' }}>✓ Wake-on-LAN</span>
212 <span style={{ fontSize: '0.75rem', background: '#f0fdf4', color: '#166534', padding: '4px 8px', borderRadius: '4px', border: '1px solid #bbf7d0' }}>✓ Intel AMT</span>
213 </div>
214
215 {/* Info Note */}
216 <div style={{
217 background: '#fef3c7',
218 border: '1px solid #fbbf24',
219 borderRadius: '6px',
220 padding: '10px',
221 fontSize: '0.85rem',
222 color: '#92400e',
223 marginBottom: '1rem'
224 }}>
225 <strong>📝 Note:</strong> This download link is valid for 5 minutes and can only be used once. Share it with your client to install the agent.
226 </div>
227 </>
228 ) : null}
229
230 <div className="modern-actions-row" style={{ justifyContent: 'flex-start', gap: '0.5rem', flexWrap: 'wrap' }}>
231 <button
232 className="secondary modern-action"
233 onClick={() => window.open('/agent-v3', '_blank')}
234 title="Open Agent v3 Download Page"
235 >
236 <span className="material-symbols-outlined">open_in_new</span>
237 View Full Documentation
238 </button>
239 <button
240 className="secondary modern-action"
241 onClick={generateDownloadToken}
242 disabled={loading}
243 title="Generate new download link"
244 >
245 <span className="material-symbols-outlined">refresh</span>
246 {loading ? 'Generating...' : 'New Link'}
247 </button>
248 </div>
249 </div>
250
251 {error && <div className="download-status-error modern-error">{error}</div>}
252 </div>
253 <div className="download-modal-footer modern-footer">
254 <button className="neutral modern-cancel" onClick={onClose}>
255 <span className="material-symbols-outlined">cancel</span>
256 Cancel
257 </button>
258 </div>
259 </div>
260 </div>
261 );
262}