Files
firefrost-services/services/modpack-version-checker/blueprint-extension/views/server/wrapper.tsx
Claude (Chronicler #63) 05d2164dce 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>
2026-04-06 12:32:41 +00:00

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;