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:
Claude (Chronicler #63)
2026-04-06 12:21:19 +00:00
parent d735e3d9db
commit c160647f0b
2 changed files with 58 additions and 108 deletions

View File

@@ -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 ServerConsoleContainer.tsx"
echo "○ ModpackVersionCard already present in AfterInformation.tsx"
fi
else
echo "⚠ AfterInformation.tsx not found, skipping injection"
fi
# ===========================================

View File

@@ -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>
<div className={'mt-1'}>
{status === 'idle' && (
<span className={'text-gray-400 text-sm'}>Click to check</span>
)}
{status === 'loading' && (
<div style={{ color: '#888', fontSize: '13px' }}>
<span style={{ display: 'inline-block', animation: 'pulse 1.5s infinite' }}>
Checking version...
</span>
</div>
<span className={'text-gray-400 text-sm'}>Checking...</span>
)}
{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>
<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>
<span className={'text-red-400 text-xs'}>
{data?.error || data?.message || 'Error'}
</span>
)}
<style>{`
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
`}</style>
</div>
</div>
);
};