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>
100 lines
4.1 KiB
TypeScript
100 lines
4.1 KiB
TypeScript
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';
|
|
import classNames from 'classnames';
|
|
|
|
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}/check`);
|
|
setData(response.data);
|
|
setStatus(response.data.success ? 'success' : 'error');
|
|
} catch (error: any) {
|
|
if (error.response?.status === 429) {
|
|
setData({ success: false, error: 'rate_limited' });
|
|
} else if (error.response?.status === 404) {
|
|
setData({ success: false, error: 'not_found' });
|
|
} else {
|
|
setData({ success: false, error: 'api_error' });
|
|
}
|
|
setStatus('error');
|
|
}
|
|
};
|
|
|
|
// 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={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={'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>
|
|
);
|
|
};
|
|
|
|
export default ModpackVersionCard;
|