Logic improvements, move stat blocks to right side
This commit is contained in:
parent
ad6e9f076b
commit
bf287c45d6
5 changed files with 27 additions and 32 deletions
|
@ -4,7 +4,7 @@ import Portal from '@/components/elements/Portal';
|
||||||
import copy from 'copy-to-clipboard';
|
import copy from 'copy-to-clipboard';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
const CopyOnClick: React.FC<{ text: any; disabled?: boolean }> = ({ text, disabled, children }) => {
|
const CopyOnClick: React.FC<{ text: string | number | null | undefined }> = ({ text, children }) => {
|
||||||
const [copied, setCopied] = useState(false);
|
const [copied, setCopied] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -23,12 +23,12 @@ const CopyOnClick: React.FC<{ text: any; disabled?: boolean }> = ({ text, disabl
|
||||||
throw new Error('Component passed to <CopyOnClick/> must be a valid React element.');
|
throw new Error('Component passed to <CopyOnClick/> must be a valid React element.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const child = disabled
|
const child = !text
|
||||||
? React.Children.only(children)
|
? React.Children.only(children)
|
||||||
: React.cloneElement(React.Children.only(children), {
|
: React.cloneElement(React.Children.only(children), {
|
||||||
className: classNames(children.props.className || '', 'cursor-pointer'),
|
className: classNames(children.props.className || '', 'cursor-pointer'),
|
||||||
onClick: (e: React.MouseEvent<HTMLElement>) => {
|
onClick: (e: React.MouseEvent<HTMLElement>) => {
|
||||||
copy(text);
|
copy(String(text));
|
||||||
setCopied(true);
|
setCopied(true);
|
||||||
if (typeof children.props.onClick === 'function') {
|
if (typeof children.props.onClick === 'function') {
|
||||||
children.props.onClick(e);
|
children.props.onClick(e);
|
||||||
|
|
|
@ -50,7 +50,7 @@ export default ({ className }: PowerButtonProps) => {
|
||||||
</Dialog.Confirm>
|
</Dialog.Confirm>
|
||||||
<Can action={'control.start'}>
|
<Can action={'control.start'}>
|
||||||
<Button
|
<Button
|
||||||
className={'w-full sm:w-24'}
|
className={'flex-1'}
|
||||||
disabled={status !== 'offline'}
|
disabled={status !== 'offline'}
|
||||||
onClick={onButtonClick.bind(this, 'start')}
|
onClick={onButtonClick.bind(this, 'start')}
|
||||||
>
|
>
|
||||||
|
@ -58,17 +58,13 @@ export default ({ className }: PowerButtonProps) => {
|
||||||
</Button>
|
</Button>
|
||||||
</Can>
|
</Can>
|
||||||
<Can action={'control.restart'}>
|
<Can action={'control.restart'}>
|
||||||
<Button.Text
|
<Button.Text className={'flex-1'} disabled={!status} onClick={onButtonClick.bind(this, 'restart')}>
|
||||||
className={'w-full sm:w-24'}
|
|
||||||
disabled={!status}
|
|
||||||
onClick={onButtonClick.bind(this, 'restart')}
|
|
||||||
>
|
|
||||||
Restart
|
Restart
|
||||||
</Button.Text>
|
</Button.Text>
|
||||||
</Can>
|
</Can>
|
||||||
<Can action={'control.stop'}>
|
<Can action={'control.stop'}>
|
||||||
<Button.Danger
|
<Button.Danger
|
||||||
className={'w-full sm:w-24'}
|
className={'flex-1'}
|
||||||
disabled={status === 'offline'}
|
disabled={status === 'offline'}
|
||||||
onClick={onButtonClick.bind(this, killable ? 'kill' : 'stop')}
|
onClick={onButtonClick.bind(this, killable ? 'kill' : 'stop')}
|
||||||
>
|
>
|
||||||
|
|
|
@ -23,24 +23,24 @@ const ServerConsoleContainer = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ServerContentBlock title={'Console'} className={'flex flex-col gap-2 sm:gap-4'}>
|
<ServerContentBlock title={'Console'} className={'flex flex-col gap-2 sm:gap-4'}>
|
||||||
<div className={'flex gap-4 items-end'}>
|
<div className={'grid grid-cols-4 gap-4'}>
|
||||||
<div className={'hidden sm:block flex-1'}>
|
<div className={'hidden sm:block sm:col-span-2 lg:col-span-3 pr-4'}>
|
||||||
<h1 className={'font-header text-2xl text-gray-50 leading-relaxed line-clamp-1'}>{name}</h1>
|
<h1 className={'font-header text-2xl text-gray-50 leading-relaxed line-clamp-1'}>{name}</h1>
|
||||||
<p className={'text-sm line-clamp-2'}>{description}</p>
|
<p className={'text-sm line-clamp-2'}>{description}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className={'flex-1'}>
|
<div className={'col-span-4 sm:col-span-2 lg:col-span-1 self-end'}>
|
||||||
<Can action={['control.start', 'control.stop', 'control.restart']} matchAny>
|
<Can action={['control.start', 'control.stop', 'control.restart']} matchAny>
|
||||||
<PowerButtons className={'flex sm:justify-end space-x-2'} />
|
<PowerButtons className={'flex sm:justify-end space-x-2'} />
|
||||||
</Can>
|
</Can>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={'grid grid-cols-4 gap-2 sm:gap-4'}>
|
<div className={'grid grid-cols-4 gap-2 sm:gap-4'}>
|
||||||
<ServerDetailsBlock className={'col-span-4 lg:col-span-1 order-last lg:order-none'} />
|
|
||||||
<div className={'col-span-4 lg:col-span-3'}>
|
<div className={'col-span-4 lg:col-span-3'}>
|
||||||
<Spinner.Suspense>
|
<Spinner.Suspense>
|
||||||
<Console />
|
<Console />
|
||||||
</Spinner.Suspense>
|
</Spinner.Suspense>
|
||||||
</div>
|
</div>
|
||||||
|
<ServerDetailsBlock className={'col-span-4 lg:col-span-1 order-last lg:order-none'} />
|
||||||
{isInstalling ? (
|
{isInstalling ? (
|
||||||
<div css={tw`mt-4 rounded bg-yellow-500 p-3`}>
|
<div css={tw`mt-4 rounded bg-yellow-500 p-3`}>
|
||||||
<ContentContainer>
|
<ContentContainer>
|
||||||
|
|
|
@ -22,7 +22,6 @@ export default ({ title, copyOnClick, icon, color, description, className, child
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip arrow placement={'top'} disabled={!description} content={description || ''}>
|
<Tooltip arrow placement={'top'} disabled={!description} content={description || ''}>
|
||||||
<CopyOnClick text={copyOnClick} disabled={!copyOnClick}>
|
|
||||||
<div className={classNames(styles.stat_block, 'bg-gray-600', className)}>
|
<div className={classNames(styles.stat_block, 'bg-gray-600', className)}>
|
||||||
<div className={classNames(styles.status_bar, color || 'bg-gray-700')} />
|
<div className={classNames(styles.status_bar, color || 'bg-gray-700')} />
|
||||||
<div className={classNames(styles.icon, color || 'bg-gray-700')}>
|
<div className={classNames(styles.icon, color || 'bg-gray-700')}>
|
||||||
|
@ -36,6 +35,7 @@ export default ({ title, copyOnClick, icon, color, description, className, child
|
||||||
</div>
|
</div>
|
||||||
<div className={'flex flex-col justify-center overflow-hidden w-full'}>
|
<div className={'flex flex-col justify-center overflow-hidden w-full'}>
|
||||||
<p className={'font-header leading-tight text-xs md:text-sm text-gray-200'}>{title}</p>
|
<p className={'font-header leading-tight text-xs md:text-sm text-gray-200'}>{title}</p>
|
||||||
|
<CopyOnClick text={copyOnClick}>
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={'h-[1.75rem] w-full font-semibold text-gray-50 truncate'}
|
className={'h-[1.75rem] w-full font-semibold text-gray-50 truncate'}
|
||||||
|
@ -43,9 +43,9 @@ export default ({ title, copyOnClick, icon, color, description, className, child
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CopyOnClick>
|
</CopyOnClick>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
* Determines if the value provided to the function is an object type that
|
* Determines if the value provided to the function is an object type that
|
||||||
* is not null.
|
* is not null.
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
function isObject(val: unknown): val is Record<string, unknown> {
|
||||||
function isObject(val: unknown): val is {} {
|
|
||||||
return typeof val === 'object' && val !== null && !Array.isArray(val);
|
return typeof val === 'object' && val !== null && !Array.isArray(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue