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>
159 lines
5.6 KiB
TypeScript
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;
|