Files
firefrost-services/services/modpack-version-checker/blueprint-extension/views/server/wrapper.tsx
Claude (Chronicler #62) 1eda8894d5 fix: ModpackChecker Phase 3 complete - working end-to-end pipeline
PHASE 3 COMPLETE - All systems operational on Dev Panel

Changes:
- Renamed controllers/ to Controllers/ (PSR-4 case sensitivity fix)
- Updated namespace to use capital C in Controllers
- Fixed getEggVariable() method to use correct Pterodactyl model structure
  - Changed from whereHas('variable'...) to direct where('env_variable'...)
  - Changed return from variable_value to server_value
- Updated routes/client.php with correct namespace
- Updated wrapper.tsx with correct API path (/api/client/extensions/...)
- Added build.sh for React component injection via sed

Tested and verified:
- Admin UI renders correctly
- Client panel loads without 500 error
- React component appears on server console page
- API call executes successfully
- Returns proper 'no modpack detected' message for unconfigured servers

Key learnings documented:
- Blueprint wrapper field is for Blade only, not TSX
- TSX components require build.sh + sed injection + yarn build
- PHP-FPM OPCache requires restart after adding new classes
- Controller namespace must match directory case exactly

Dev Panel: http://64.50.188.14:128
Test Server UUID: c0a133db-6cb7-497d-a2ed-22ae66eb0de8

Next: Phase 4 - Real modpack testing with CurseForge API

Signed-off-by: Claude (Chronicler #62) <claude@firefrostgaming.com>
2026-04-06 01:39:04 +00:00

159 lines
5.6 KiB
TypeScript

import React, { useState } from 'react';
import { ServerContext } from '@/state/server';
import http from '@/api/http';
interface VersionData {
success: boolean;
platform?: string;
modpack_id?: string;
modpack_name?: string;
current_version?: string;
latest_version?: string;
status?: string;
message?: string;
error?: string;
}
const ModpackVersionCard: React.FC = () => {
const uuid = ServerContext.useStoreState((state) => state.server.data?.uuid);
const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
const [data, setData] = useState<VersionData | null>(null);
const checkForUpdates = async () => {
if (!uuid) return;
setStatus('loading');
try {
const response = await http.post(`/api/client/extensions/modpackchecker/servers/${uuid}/ext/modpackchecker/check`);
setData(response.data);
setStatus(response.data.success ? 'success' : 'error');
} catch (error: any) {
setData({
success: false,
error: error.response?.data?.message || 'Failed to check for updates',
});
setStatus('error');
}
};
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';
}
};
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>
{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',
}}
>
Check for Updates
</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',
}}
>
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})
</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>
</div>
);
};
export default ModpackVersionCard;