1import React, { useState, useEffect } from "react";
2import "./DownloadAgentModal.css";
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');
12 generateDownloadToken();
15 const generateDownloadToken = async () => {
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`, {
24 'Authorization': `Bearer ${token}`
28 if (!response.ok) throw new Error('Failed to generate download token');
30 const data = await response.json();
31 setDownloadToken(data.token);
33 setError(err.message);
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}`;
46 const getInstallCommand = (platform) => {
47 const url = getDownloadUrl(platform);
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`;
52 return `# Run in terminal\nwget -qO- "${url}" | sudo bash`;
54 return `# Run in terminal\ncurl -sL "${url}" -o ~/Downloads/agent-v3.pkg && open ~/Downloads/agent-v3.pkg`;
60 const copyToClipboard = (text) => {
61 navigator.clipboard.writeText(text);
63 setTimeout(() => setCopied(false), 2000);
67 { key: 'windows', label: 'Windows', icon: 'window' },
68 { key: 'linux', label: 'Linux', icon: 'terminal' },
69 { key: 'macos', label: 'macOS', icon: 'apple' }
73 <div className="download-modal-overlay">
74 <div className="download-modal modern-modal">
75 <div className="download-modal-header modern-header">
77 <span className="material-symbols-outlined">download</span>
80 <button className="neutral modern-close" onClick={onClose} title="Close">
81 <span className="material-symbols-outlined">close</span>
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' }}>
95 color: 'var(--text-muted, #334155)',
99 Enterprise-grade MeshCentral agent with white-label branding. Automatically registers to tenant {tenantId}.
102 {/* Platform Selector */}
103 <div style={{ marginBottom: '1rem' }}>
104 <label style={{ display: 'block', marginBottom: '8px', fontWeight: 500, fontSize: '0.9rem', color: 'var(--text)' }}>
107 <div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap' }}>
108 {platforms.map(platform => (
111 className={`secondary modern-action ${selectedPlatform === platform.key ? 'selected' : ''}`}
112 onClick={() => setSelectedPlatform(platform.key)}
115 background: selectedPlatform === platform.key ? 'linear-gradient(135deg, #0ea5e9, #06b6d4)' : undefined,
116 color: selectedPlatform === platform.key ? 'white' : undefined
119 <span className="material-symbols-outlined">{platform.icon}</span>
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>
132 ) : downloadToken ? (
134 <div style={{ marginBottom: '1rem' }}>
135 <label style={{ display: 'block', marginBottom: '8px', fontWeight: 500, fontSize: '0.9rem', color: 'var(--text)' }}>
138 <div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
141 value={getDownloadUrl(selectedPlatform)}
146 border: '1px solid var(--border)',
149 fontFamily: 'monospace',
150 background: 'var(--bg-secondary)',
155 className="secondary modern-action"
156 onClick={() => copyToClipboard(getDownloadUrl(selectedPlatform))}
159 <span className="material-symbols-outlined">
160 {copied ? 'check' : 'content_copy'}
162 {copied ? 'Copied!' : 'Copy'}
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:
172 <div style={{ position: 'relative' }}>
175 background: '#1e293b',
181 whiteSpace: 'pre-wrap',
182 wordBreak: 'break-all'
184 {getInstallCommand(selectedPlatform)}
187 className="secondary modern-action"
188 onClick={() => copyToClipboard(getInstallCommand(selectedPlatform))}
190 position: 'absolute',
198 <span className="material-symbols-outlined" style={{ fontSize: '1rem' }}>
199 {copied ? 'check' : 'content_copy'}
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>
217 background: '#fef3c7',
218 border: '1px solid #fbbf24',
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.
230 <div className="modern-actions-row" style={{ justifyContent: 'flex-start', gap: '0.5rem', flexWrap: 'wrap' }}>
232 className="secondary modern-action"
233 onClick={() => window.open('/agent-v3', '_blank')}
234 title="Open Agent v3 Download Page"
236 <span className="material-symbols-outlined">open_in_new</span>
237 View Full Documentation
240 className="secondary modern-action"
241 onClick={generateDownloadToken}
243 title="Generate new download link"
245 <span className="material-symbols-outlined">refresh</span>
246 {loading ? 'Generating...' : 'New Link'}
251 {error && <div className="download-status-error modern-error">{error}</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>