fix(modpackchecker): Console card redesign - StatBlock style + short errors
wrapper.tsx complete rewrite: - Matches Pterodactyl StatBlock styling exactly - Uses col-span classes for proper grid layout - Icon with status color (orange=update, cyan=current, gray=idle) - Clickable card instead of separate button - Short error codes for better UX: - 'Not configured' (no modpack variables) - 'Wait 60s' (rate limited) - 'Not found' (404) - 'API error' (general failure) - 'Check failed' (long error truncated) build.sh: - Injects into AfterInformation.tsx (right column) - Card appears after Network stats Signed-off-by: Claude (Chronicler #63) <claude@firefrostgaming.com>
This commit is contained in:
@@ -3,6 +3,7 @@ import { ServerContext } from '@/state/server';
|
||||
import http from '@/api/http';
|
||||
import { faCube } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import classNames from 'classnames';
|
||||
|
||||
interface VersionData {
|
||||
success: boolean;
|
||||
@@ -31,78 +32,65 @@ const ModpackVersionCard: React.FC = () => {
|
||||
setStatus(response.data.success ? 'success' : 'error');
|
||||
} catch (error: any) {
|
||||
if (error.response?.status === 429) {
|
||||
setData({
|
||||
success: false,
|
||||
error: 'Rate limit reached. Please wait 60 seconds.',
|
||||
});
|
||||
setData({ success: false, error: 'rate_limited' });
|
||||
} else if (error.response?.status === 404) {
|
||||
setData({ success: false, error: 'not_found' });
|
||||
} else {
|
||||
setData({
|
||||
success: false,
|
||||
error: error.response?.data?.message || 'Failed to check for updates',
|
||||
});
|
||||
setData({ success: false, error: 'api_error' });
|
||||
}
|
||||
setStatus('error');
|
||||
}
|
||||
};
|
||||
|
||||
// Match StatBlock styling from Pterodactyl
|
||||
// Convert error codes to short display messages
|
||||
const getErrorMessage = (error?: string): string => {
|
||||
if (!error) return 'Error';
|
||||
if (error.includes('detect') || error.includes('MODPACK')) return 'Not configured';
|
||||
if (error === 'rate_limited') return 'Wait 60s';
|
||||
if (error === 'not_found') return 'Not found';
|
||||
if (error === 'api_error') return 'API error';
|
||||
if (error.length > 20) return 'Check failed';
|
||||
return error;
|
||||
};
|
||||
|
||||
const getBgColor = () => {
|
||||
if (status === 'success' && data?.status === 'update_available') return 'bg-orange-500';
|
||||
if (status === 'success' && data?.success) return 'bg-cyan-500';
|
||||
return 'bg-gray-700';
|
||||
};
|
||||
|
||||
return (
|
||||
<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}
|
||||
className={'text-xs text-cyan-400 hover:text-cyan-300 transition-colors'}
|
||||
>
|
||||
Check
|
||||
</button>
|
||||
)}
|
||||
{status === 'success' && (
|
||||
<button
|
||||
onClick={checkForUpdates}
|
||||
className={'text-xs text-gray-400 hover:text-gray-300 transition-colors'}
|
||||
>
|
||||
Refresh
|
||||
</button>
|
||||
)}
|
||||
<div
|
||||
className={classNames(
|
||||
'flex items-center rounded shadow-lg relative bg-gray-600 cursor-pointer hover:bg-gray-500 transition-colors',
|
||||
'col-span-3 md:col-span-2 lg:col-span-6',
|
||||
'px-3 py-2 md:p-3 lg:p-4 mt-2'
|
||||
)}
|
||||
onClick={status !== 'loading' ? checkForUpdates : undefined}
|
||||
title={'Click to check for modpack updates'}
|
||||
>
|
||||
<div className={classNames('w-1 h-full absolute left-0 top-0 rounded-l sm:hidden', getBgColor())} />
|
||||
|
||||
<div className={classNames(
|
||||
'hidden flex-shrink-0 items-center justify-center rounded-lg shadow-md w-12 h-12 transition-colors duration-500',
|
||||
'sm:flex sm:mr-4',
|
||||
getBgColor()
|
||||
)}>
|
||||
<FontAwesomeIcon icon={faCube} className={'w-6 h-6 text-gray-50'} />
|
||||
</div>
|
||||
<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>
|
||||
)}
|
||||
{(status === 'error' || (status === 'success' && !data?.success)) && (
|
||||
<span className={'text-red-400 text-xs'}>
|
||||
{data?.error || data?.message || 'Error'}
|
||||
</span>
|
||||
)}
|
||||
|
||||
<div className={'flex flex-col justify-center overflow-hidden w-full'}>
|
||||
<p className={'font-header font-medium leading-tight text-xs md:text-sm text-gray-200'}>
|
||||
Modpack Version
|
||||
</p>
|
||||
<div className={'h-[1.75rem] w-full font-semibold text-gray-50 truncate text-sm'}>
|
||||
{status === 'idle' && <span className={'text-gray-400'}>Click to check</span>}
|
||||
{status === 'loading' && <span className={'text-gray-400'}>Checking...</span>}
|
||||
{status === 'success' && data?.success && <span>{data.latest_version}</span>}
|
||||
{(status === 'error' || (status === 'success' && !data?.success)) && (
|
||||
<span className={'text-red-400'}>{getErrorMessage(data?.error || data?.message)}</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user