1import { useState, useEffect } from 'react';
2import { apiFetch } from '../lib/api';
4export default function WordPressSiteModal({ domain, onClose }) {
5 const [loading, setLoading] = useState(true);
6 const [site, setSite] = useState(null);
7 const [error, setError] = useState('');
8 const [loginUrl, setLoginUrl] = useState('');
9 const [generating, setGenerating] = useState(false);
17 const fetchSiteDetails = async () => {
21 const res = await apiFetch(`/wordpress/sites/${domain}`);
23 const data = await res.json();
26 setError('Failed to load site details');
29 setError(err.message || 'Failed to load site details');
35 const handleLogin = async () => {
39 const res = await apiFetch(`/wordpress/sites/${domain}/login`, {
43 const data = await res.json();
44 // Open login URL in new tab
45 window.open(`https://${domain}/wp-admin/`, '_blank');
46 setLoginUrl(data.loginUrl);
48 alert('Failed to generate login URL');
51 alert(err.message || 'Failed to generate login URL');
57 const handleUpdatePlugin = async (pluginName) => {
58 if (!confirm(`Update plugin "${pluginName}"?`)) return;
59 alert('Plugin update feature coming soon');
62 const handleUpdateCore = async () => {
63 if (!confirm(`Update WordPress core to ${site.core?.updates[0]?.version}?`)) return;
64 alert('Core update feature coming soon');
67 if (!domain) return null;
70 <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
71 <div className="bg-white rounded-lg shadow-xl max-w-5xl w-full max-h-[90vh] overflow-hidden flex flex-col">
73 <div className="px-6 py-4 border-b border-gray-200 flex items-center justify-between bg-gradient-to-r from-blue-500 to-blue-600 text-white">
75 <h2 className="text-2xl font-bold">{domain}</h2>
77 <p className="text-blue-100 text-sm mt-1">
78 WordPress {site.wp_version} • {site.plugin_count} plugins • {site.disk_usage}
84 className="text-white hover:text-gray-200 text-2xl font-bold"
91 <div className="flex-1 overflow-y-auto p-6">
93 <div className="flex items-center justify-center h-64">
94 <div className="text-gray-500">Loading site details...</div>
97 <div className="text-red-500 p-4 bg-red-50 rounded">{error}</div>
99 <div className="space-y-6">
100 {/* Quick Actions */}
101 <div className="flex gap-3">
103 onClick={handleLogin}
104 disabled={generating}
105 className="bg-blue-600 text-white px-6 py-2 rounded-lg hover:bg-blue-700 disabled:opacity-50 flex items-center gap-2"
107 <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
108 <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1" />
110 {generating ? 'Generating...' : 'WP Admin Login'}
113 href={`https://${domain}`}
115 rel="noopener noreferrer"
116 className="bg-gray-100 text-gray-700 px-6 py-2 rounded-lg hover:bg-gray-200 flex items-center gap-2"
118 <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
119 <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
125 {/* Security Issues */}
126 {site.malware_issues > 0 && (
127 <div className="bg-red-50 border border-red-200 rounded-lg p-4">
128 <div className="flex items-center gap-2 text-red-800 font-semibold mb-2">
129 <svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
130 <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
132 Security Issues Detected
134 <p className="text-red-700">
135 {site.malware_issues} suspicious file{site.malware_issues !== 1 ? 's' : ''} found
138 <div className="mt-3 text-sm text-red-600 space-y-1">
139 {site.security.php_in_uploads > 0 && <div>• {site.security.php_in_uploads} PHP files in uploads directory</div>}
140 {site.security.suspicious_files > 0 && <div>• {site.security.suspicious_files} suspicious files</div>}
141 {site.security.modified_core_files > 0 && <div>• {site.security.modified_core_files} modified core files</div>}
144 <button className="mt-3 bg-red-600 text-white px-4 py-2 rounded hover:bg-red-700 text-sm">
145 Clean Threats (Coming Soon)
150 {/* WordPress Core */}
152 <div className="bg-white border border-gray-200 rounded-lg p-4">
153 <h3 className="font-semibold text-gray-900 mb-3 flex items-center gap-2">
154 <svg className="w-5 h-5 text-blue-600" fill="currentColor" viewBox="0 0 20 20">
155 <path d="M10.894 2.553a1 1 0 00-1.788 0l-7 14a1 1 0 001.169 1.409l5-1.429A1 1 0 009 15.571V11a1 1 0 112 0v4.571a1 1 0 00.725.962l5 1.428a1 1 0 001.17-1.408l-7-14z" />
159 <div className="flex items-center justify-between">
161 <div className="text-sm text-gray-600">Current Version</div>
162 <div className="font-medium">{site.core.version}</div>
164 {site.core.update_available && site.core.updates.length > 0 && (
167 onClick={handleUpdateCore}
168 className="bg-orange-500 text-white px-4 py-2 rounded-lg hover:bg-orange-600 text-sm flex items-center gap-2"
170 <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
171 <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" />
173 Update to {site.core.updates[0].version}
182 <div className="bg-white border border-gray-200 rounded-lg p-4">
183 <h3 className="font-semibold text-gray-900 mb-3 flex items-center gap-2">
184 <svg className="w-5 h-5 text-purple-600" fill="currentColor" viewBox="0 0 20 20">
185 <path fillRule="evenodd" d="M11.3 1.046A1 1 0 0112 2v5h4a1 1 0 01.82 1.573l-7 10A1 1 0 018 18v-5H4a1 1 0 01-.82-1.573l7-10a1 1 0 011.12-.38z" clipRule="evenodd" />
187 Plugins ({site.plugin_count})
189 {site.plugins && site.plugins.length > 0 ? (
190 <div className="space-y-2 max-h-64 overflow-y-auto">
191 {site.plugins.map((plugin, idx) => (
192 <div key={idx} className="flex items-center justify-between p-3 bg-gray-50 rounded">
194 <div className="font-medium">{plugin.title || plugin.name}</div>
195 <div className="text-sm text-gray-600">
197 <span className={`ml-2 px-2 py-0.5 rounded text-xs ${
198 plugin.status === 'active' ? 'bg-green-100 text-green-800' : 'bg-gray-200 text-gray-700'
204 {plugin.update_available === 'available' && (
206 onClick={() => handleUpdatePlugin(plugin.name)}
207 className="text-orange-600 hover:text-orange-700 text-sm font-medium"
209 Update to v{plugin.new_version}
216 <div className="text-gray-500 text-sm">
217 {site.ssh_error ? `Unable to fetch plugin details: ${site.ssh_error}` : `${site.plugin_count} plugins installed (details loading...)`}
223 <div className="bg-white border border-gray-200 rounded-lg p-4">
224 <h3 className="font-semibold text-gray-900 mb-3 flex items-center gap-2">
225 <svg className="w-5 h-5 text-pink-600" fill="currentColor" viewBox="0 0 20 20">
226 <path fillRule="evenodd" d="M4 2a2 2 0 00-2 2v11a3 3 0 106 0V4a2 2 0 00-2-2H4zm1 14a1 1 0 100-2 1 1 0 000 2zm5-1.757l4.9-4.9a2 2 0 000-2.828L13.485 5.1a2 2 0 00-2.828 0L10 5.757v8.486zM16 18H9.071l6-6H16a2 2 0 012 2v2a2 2 0 01-2 2z" clipRule="evenodd" />
230 {site.themes && site.themes.length > 0 ? (
231 <div className="space-y-2">
232 {site.themes.map((theme, idx) => (
233 <div key={idx} className="flex items-center justify-between p-3 bg-gray-50 rounded">
235 <div className="font-medium">{theme.title || theme.name}</div>
236 <div className="text-sm text-gray-600">
238 <span className={`ml-2 px-2 py-0.5 rounded text-xs ${
239 theme.status === 'active' ? 'bg-blue-100 text-blue-800' : 'bg-gray-200 text-gray-700'
245 {theme.update_available === 'available' && (
246 <button className="text-orange-600 hover:text-orange-700 text-sm font-medium">
247 Update to v{theme.new_version}
254 <div className="text-gray-500 text-sm">No theme details available</div>
259 <div className="bg-gray-50 border border-gray-200 rounded-lg p-4">
260 <h3 className="font-semibold text-gray-900 mb-3">Server Information</h3>
261 <div className="grid grid-cols-2 gap-4 text-sm">
263 <div className="text-gray-600">Server IP</div>
264 <div className="font-medium">{site.server_ip}</div>
267 <div className="text-gray-600">Hostname</div>
268 <div className="font-medium">{site.server_hostname}</div>
271 <div className="text-gray-600">Disk Usage</div>
272 <div className="font-medium">{site.disk_usage}</div>
275 <div className="text-gray-600">Status</div>
276 <div className={`font-medium ${site.status === 'online' ? 'text-green-600' : 'text-red-600'}`}>
283 <div className="text-gray-600">Database</div>
284 <div className="font-medium text-xs">{site.database.db_name}</div>
287 <div className="text-gray-600">DB User</div>
288 <div className="font-medium text-xs">{site.database.db_user}</div>
299 <div className="px-6 py-4 border-t border-gray-200 bg-gray-50 flex justify-between items-center">
300 <div className="text-sm text-gray-600">
301 {site && site.last_updated && (
302 <>Last updated: {new Date(site.last_updated).toLocaleString()}</>
307 className="bg-gray-600 text-white px-6 py-2 rounded-lg hover:bg-gray-700"