import React, { useState, useEffect, useMemo } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { ArrowLeft, Download, Star, Calendar, Package, ExternalLink, Tag, Server } from 'lucide-react'; import { colors } from '../../theme/colors'; import { Dropdown } from '../../components/dashboardPrimitives'; import { API_BASE_URL, WS_BASE_URL as WS_URL } from '../../config/api'; import branding from '../../branding'; function PluginDetails() { const { pluginId } = useParams(); const navigate = useNavigate(); const [plugin, setPlugin] = useState(null); const [versions, setVersions] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); const [selectedVersion, setSelectedVersion] = useState(null); const [servers, setServers] = useState([]); const [selectedServer, setSelectedServer] = useState(null); const [downloadingToServer, setDownloadingToServer] = useState(false); const [downloadStatus, setDownloadStatus] = useState(''); const serverDropdownOptions = useMemo( () => servers.map((server) => ({ value: server.id, label: server.name })), [servers], ); useEffect(() => { const fetchPluginDetails = async () => { setLoading(true); setError(''); try { // Fetch project details const projectResponse = await fetch( `${API_BASE_URL}/api/modrinth/project/${pluginId}`, { headers: { 'User-Agent': `${branding.name}/1.0` } } ); if (!projectResponse.ok) { throw new Error('Failed to fetch plugin details'); } const projectData = await projectResponse.json(); setPlugin(projectData); // Fetch versions const versionsResponse = await fetch( `${API_BASE_URL}/api/modrinth/project/${pluginId}/versions` ); if (versionsResponse.ok) { const versionsData = await versionsResponse.json(); setVersions(versionsData); if (versionsData.length > 0) { setSelectedVersion(versionsData[0]); } } } catch (err) { setError(err.message); console.error('Error fetching plugin details:', err); } finally { setLoading(false); } }; const fetchServers = async () => { try { const token = localStorage.getItem('anchorhost_token'); if (!token) return; const ws = new WebSocket(`${WS_URL}?token=${token}`); ws.onopen = () => { ws.send(JSON.stringify({ type: 'get_servers' })); }; ws.onmessage = (event) => { const data = JSON.parse(event.data); if (data.type === 'servers_list') { // Filter only Minecraft servers const minecraftServers = (data.servers || []).filter( server => server.image === 'minecraft' && server.type === 'game' ); setServers(minecraftServers); ws.close(); } }; ws.onerror = (error) => { console.error('WebSocket error:', error); ws.close(); }; } catch (err) { console.error('Error fetching servers:', err); } }; if (pluginId) { fetchPluginDetails(); fetchServers(); } }, [pluginId]); const formatDate = (dateString) => { if (!dateString) return 'Unknown'; const date = new Date(dateString); return date.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }); }; const handleDownload = () => { if (selectedVersion && selectedVersion.files && selectedVersion.files.length > 0) { const primaryFile = selectedVersion.files.find(f => f.primary) || selectedVersion.files[0]; window.open(primaryFile.url, '_blank'); } }; const handleDownloadToServer = async () => { if (!selectedServer || !selectedVersion) { setError('Please select a server first'); return; } if (!selectedVersion.files || selectedVersion.files.length === 0) { setError('No file available for this version'); return; } const primaryFile = selectedVersion.files.find(f => f.primary) || selectedVersion.files[0]; setDownloadingToServer(true); setDownloadStatus('Downloading plugin...'); try { const token = localStorage.getItem('anchorhost_token'); if (!token) { throw new Error('Not authenticated'); } const response = await fetch(`${API_BASE_URL}/api/plugins/download-to-server`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ server_id: selectedServer.id, plugin_url: primaryFile.url, plugin_name: plugin.title || pluginId, file_name: primaryFile.filename || `${pluginId}.jar` }) }); const result = await response.json(); if (!response.ok) { throw new Error(result.error || 'Failed to download plugin to server'); } setDownloadStatus('Plugin downloaded successfully!'); setTimeout(() => { setDownloadStatus(''); setDownloadingToServer(false); }, 3000); } catch (err) { setError(err.message); setDownloadingToServer(false); setDownloadStatus(''); } }; if (loading) { return (
); } if (error || !plugin) { return (

{error || 'Plugin not found'}

); } return (
{/* Back Button */} {/* Plugin Header */}
{plugin.icon_url ? ( {plugin.title} ) : (
)}

{plugin.title}

by {plugin.team || (plugin.team_data && plugin.team_data.name) || 'Unknown'}

{plugin.downloads?.toLocaleString() || 0} downloads
{plugin.followers !== undefined && (
{plugin.followers?.toLocaleString() || 0} followers
)}
Updated {formatDate(plugin.updated)}
View on Modrinth
{/* Description */} {plugin.description && (

Description

{typeof plugin.description === 'string' ? new DOMParser().parseFromString(plugin.description, 'text/html').body.textContent : ''}

)} {/* Categories/Tags */} {plugin.categories && plugin.categories.length > 0 && (

Categories

{plugin.categories.map((category) => ( {category} ))}
)} {/* Loaders and Versions */} {(plugin.loaders || plugin.game_versions) && (

Compatibility

{plugin.loaders && plugin.loaders.length > 0 && (

Supported Loaders

{plugin.loaders.map((loader) => ( {loader} ))}
)} {plugin.game_versions && plugin.game_versions.length > 0 && (

Supported Versions

{plugin.game_versions.slice(0, 10).map((version) => ( {version} ))} {plugin.game_versions.length > 10 && ( +{plugin.game_versions.length - 10} more )}
)}
)} {/* Versions */} {versions.length > 0 && (

Versions

{versions.slice(0, 10).map((version) => (
setSelectedVersion(version)} >

{version.name}

{version.game_versions && version.game_versions.map((v) => ( {v} ))} {version.loaders && version.loaders.map((l) => ( {l} ))}
{formatDate(version.date_published)}
))}
)} {/* Download Buttons */} {selectedVersion && selectedVersion.files && selectedVersion.files.length > 0 && (
{/* Server Selection Dropdown */} {servers.length > 0 && ( setSelectedServer(servers.find((server) => server.id === serverId) || null)} icon={Server} /> )} {/* Download to Server Button */} {selectedServer && ( )} {/* Regular Download Button */} {/* Download Status */} {downloadStatus && (
{downloadStatus}
)}
)}
); } export default PluginDetails;