fix(modpackchecker): Move card to right column, match StatBlock style
build.sh: - Changed injection from ServerConsoleContainer to AfterInformation.tsx - Card now appears in right column after Network stats wrapper.tsx: - Redesigned to match Pterodactyl StatBlock aesthetic - Uses Tailwind classes (bg-gray-600, rounded, etc.) - FontAwesome cube icon with status colors - Compact layout: title + Check button on one line - Fire (#FF6B35/orange-400) for updates, Frost (#4ECDC4/cyan-400) for current Fixes layout issue identified in Wizard review. Signed-off-by: Claude (Chronicler #63) <claude@firefrostgaming.com>
This commit is contained in:
@@ -28,7 +28,7 @@ fi
|
||||
echo "Using extension directory: $EXT_DIR"
|
||||
|
||||
# ===========================================
|
||||
# 1. CONSOLE WIDGET INJECTION
|
||||
# 1. CONSOLE WIDGET INJECTION (Right Column)
|
||||
# ===========================================
|
||||
echo ""
|
||||
echo "--- Console Widget ---"
|
||||
@@ -40,14 +40,20 @@ else
|
||||
echo "⚠ wrapper.tsx not found, skipping console widget"
|
||||
fi
|
||||
|
||||
# Inject into ServerConsoleContainer.tsx
|
||||
if ! grep -q "ModpackVersionCard" resources/scripts/components/server/console/ServerConsoleContainer.tsx 2>/dev/null; then
|
||||
sed -i '1i import ModpackVersionCard from "@/components/server/ModpackVersionCard";' resources/scripts/components/server/console/ServerConsoleContainer.tsx
|
||||
# Place after ServerDetailsBlock for consistent positioning
|
||||
sed -i '/<ServerDetailsBlock/a \ <ModpackVersionCard />' resources/scripts/components/server/console/ServerConsoleContainer.tsx
|
||||
echo "✓ Injected ModpackVersionCard into ServerConsoleContainer.tsx"
|
||||
# Inject into AfterInformation.tsx (right column, after stats)
|
||||
AFTER_INFO="resources/scripts/blueprint/components/Server/Terminal/AfterInformation.tsx"
|
||||
if [ -f "$AFTER_INFO" ]; then
|
||||
if ! grep -q "ModpackVersionCard" "$AFTER_INFO" 2>/dev/null; then
|
||||
# Add import after the blueprint/import comment
|
||||
sed -i '/\/\* blueprint\/import \*\//a import ModpackVersionCard from "@/components/server/ModpackVersionCard";' "$AFTER_INFO"
|
||||
# Add component inside the fragment after blueprint/react comment
|
||||
sed -i 's|{/\* blueprint/react \*/}|{/* blueprint/react */}\n <ModpackVersionCard />|' "$AFTER_INFO"
|
||||
echo "✓ Injected ModpackVersionCard into AfterInformation.tsx"
|
||||
else
|
||||
echo "○ ModpackVersionCard already present in AfterInformation.tsx"
|
||||
fi
|
||||
else
|
||||
echo "○ ModpackVersionCard already present in ServerConsoleContainer.tsx"
|
||||
echo "⚠ AfterInformation.tsx not found, skipping injection"
|
||||
fi
|
||||
|
||||
# ===========================================
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React, { useState } from 'react';
|
||||
import { ServerContext } from '@/state/server';
|
||||
import http from '@/api/http';
|
||||
import { faCube } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
|
||||
interface VersionData {
|
||||
success: boolean;
|
||||
@@ -24,12 +26,10 @@ const ModpackVersionCard: React.FC = () => {
|
||||
|
||||
setStatus('loading');
|
||||
try {
|
||||
// Updated to match Batch 1 route optimization
|
||||
const response = await http.post(`/api/client/extensions/modpackchecker/servers/${uuid}/check`);
|
||||
setData(response.data);
|
||||
setStatus(response.data.success ? 'success' : 'error');
|
||||
} catch (error: any) {
|
||||
// Handle 429 Rate Limit responses with user-friendly message
|
||||
if (error.response?.status === 429) {
|
||||
setData({
|
||||
success: false,
|
||||
@@ -45,121 +45,65 @@ const ModpackVersionCard: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const getPlatformIcon = (platform?: string) => {
|
||||
switch (platform) {
|
||||
case 'curseforge':
|
||||
return '🔥';
|
||||
case 'modrinth':
|
||||
return '🌿';
|
||||
case 'technic':
|
||||
return '⚙️';
|
||||
case 'ftb':
|
||||
return '📦';
|
||||
default:
|
||||
return '❓';
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusColor = (status?: string) => {
|
||||
switch (status) {
|
||||
case 'up_to_date':
|
||||
return '#4ECDC4'; // Frost
|
||||
case 'update_available':
|
||||
return '#FF6B35'; // Fire
|
||||
case 'error':
|
||||
return '#ef4444';
|
||||
default:
|
||||
return '#888';
|
||||
}
|
||||
};
|
||||
|
||||
// Match StatBlock styling from Pterodactyl
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
background: 'rgba(30, 30, 46, 0.8)',
|
||||
borderRadius: '8px',
|
||||
padding: '16px',
|
||||
marginBottom: '16px',
|
||||
border: '1px solid rgba(255, 255, 255, 0.1)',
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '12px' }}>
|
||||
<h3 style={{ margin: 0, fontSize: '14px', fontWeight: 600, color: '#fff' }}>
|
||||
Modpack Version
|
||||
</h3>
|
||||
<div className={'bg-gray-600 rounded p-3 mt-2'}>
|
||||
<div className={'flex items-center justify-between'}>
|
||||
<div className={'flex items-center'}>
|
||||
<FontAwesomeIcon
|
||||
icon={faCube}
|
||||
className={`w-4 h-4 mr-2 ${
|
||||
status === 'success' && data?.status === 'update_available'
|
||||
? 'text-orange-400'
|
||||
: status === 'success'
|
||||
? 'text-cyan-400'
|
||||
: 'text-gray-400'
|
||||
}`}
|
||||
/>
|
||||
<span className={'text-gray-200 text-sm font-medium'}>Modpack</span>
|
||||
</div>
|
||||
{status === 'idle' && (
|
||||
<button
|
||||
onClick={checkForUpdates}
|
||||
style={{
|
||||
background: 'linear-gradient(135deg, #FF6B35, #4ECDC4)',
|
||||
border: 'none',
|
||||
borderRadius: '6px',
|
||||
padding: '6px 12px',
|
||||
color: '#fff',
|
||||
fontSize: '12px',
|
||||
fontWeight: 500,
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
className={'text-xs text-cyan-400 hover:text-cyan-300 transition-colors'}
|
||||
>
|
||||
Check for Updates
|
||||
Check
|
||||
</button>
|
||||
)}
|
||||
{status === 'success' && (
|
||||
<button
|
||||
onClick={checkForUpdates}
|
||||
style={{
|
||||
background: 'rgba(255, 255, 255, 0.1)',
|
||||
border: '1px solid rgba(255, 255, 255, 0.2)',
|
||||
borderRadius: '6px',
|
||||
padding: '6px 12px',
|
||||
color: '#888',
|
||||
fontSize: '12px',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
className={'text-xs text-gray-400 hover:text-gray-300 transition-colors'}
|
||||
>
|
||||
Refresh
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{status === 'loading' && (
|
||||
<div style={{ color: '#888', fontSize: '13px' }}>
|
||||
<span style={{ display: 'inline-block', animation: 'pulse 1.5s infinite' }}>
|
||||
Checking version...
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{status === 'success' && data?.success && (
|
||||
<div style={{ fontSize: '13px' }}>
|
||||
<div style={{ marginBottom: '8px', color: '#ccc' }}>
|
||||
<span style={{ marginRight: '8px' }}>{getPlatformIcon(data.platform)}</span>
|
||||
<strong style={{ color: '#fff' }}>{data.modpack_name}</strong>
|
||||
<span style={{ color: '#666', marginLeft: '8px', textTransform: 'capitalize' }}>
|
||||
({data.platform})
|
||||
<div className={'mt-1'}>
|
||||
{status === 'idle' && (
|
||||
<span className={'text-gray-400 text-sm'}>Click to check</span>
|
||||
)}
|
||||
{status === 'loading' && (
|
||||
<span className={'text-gray-400 text-sm'}>Checking...</span>
|
||||
)}
|
||||
{status === 'success' && data?.success && (
|
||||
<div>
|
||||
<span className={'text-white text-sm font-semibold'}>
|
||||
{data.modpack_name || data.modpack_id}
|
||||
</span>
|
||||
<span className={`ml-2 text-xs ${
|
||||
data.status === 'update_available' ? 'text-orange-400' : 'text-cyan-400'
|
||||
}`}>
|
||||
{data.status === 'update_available' ? `→ ${data.latest_version}` : 'Up to date'}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '16px', color: '#888' }}>
|
||||
<div>
|
||||
<span style={{ color: '#666' }}>Latest: </span>
|
||||
<span style={{ color: getStatusColor(data.status) }}>{data.latest_version}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(status === 'error' || (status === 'success' && !data?.success)) && (
|
||||
<div style={{ color: '#ef4444', fontSize: '13px' }}>
|
||||
{data?.error || data?.message || 'Unknown error'}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<style>{`
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
}
|
||||
`}</style>
|
||||
)}
|
||||
{(status === 'error' || (status === 'success' && !data?.success)) && (
|
||||
<span className={'text-red-400 text-xs'}>
|
||||
{data?.error || data?.message || 'Error'}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user