mirror of
https://github.com/pyrohost/pyrodactyl.git
synced 2026-04-06 04:01:58 +02:00
chore(ui): replaced all server ui icons with gravity-ui icons
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { ArrowDownToLine, ClockArrowRotateLeft, Funnel, Magnifier, Xmark } from '@gravity-ui/icons';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import FlashMessageRender from '@/components/FlashMessageRender';
|
||||
@@ -7,11 +8,6 @@ import Select from '@/components/elements/Select';
|
||||
import ServerContentBlock from '@/components/elements/ServerContentBlock';
|
||||
import Spinner from '@/components/elements/Spinner';
|
||||
import ActivityLogEntry from '@/components/elements/activity/ActivityLogEntry';
|
||||
import HugeIconsDownload from '@/components/elements/hugeicons/Download';
|
||||
import HugeIconsFilter from '@/components/elements/hugeicons/Filter';
|
||||
import HugeIconsHistory from '@/components/elements/hugeicons/History';
|
||||
import HugeIconsSearch from '@/components/elements/hugeicons/Search';
|
||||
import HugeIconsX from '@/components/elements/hugeicons/X';
|
||||
import { Input } from '@/components/elements/inputs';
|
||||
import PaginationFooter from '@/components/elements/table/PaginationFooter';
|
||||
|
||||
@@ -178,7 +174,7 @@ const ServerActivityLogContainer = () => {
|
||||
className='flex items-center gap-2'
|
||||
title='Toggle Filters (Ctrl+F)'
|
||||
>
|
||||
<HugeIconsFilter className='w-4 h-4' fill='currentColor' />
|
||||
<Funnel width={22} height={22} className='w-4 h-4' fill='currentColor' />
|
||||
Filters
|
||||
{hasActiveFilters && <span className='w-2 h-2 bg-brand rounded-full'></span>}
|
||||
</ActionButton>
|
||||
@@ -189,7 +185,7 @@ const ServerActivityLogContainer = () => {
|
||||
className='flex items-center gap-2'
|
||||
title='Export CSV (Ctrl+E)'
|
||||
>
|
||||
<HugeIconsDownload className='w-4 h-4' fill='currentColor' />
|
||||
<ArrowDownToLine width={22} height={22} className='w-4 h-4' fill='currentColor' />
|
||||
Export
|
||||
</ActionButton>
|
||||
</div>
|
||||
@@ -214,7 +210,12 @@ const ServerActivityLogContainer = () => {
|
||||
<div className='bg-gradient-to-b from-[#ffffff08] to-[#ffffff05] border-[1px] border-[#ffffff12] rounded-xl p-4 hover:border-[#ffffff20] transition-all duration-150 shadow-sm'>
|
||||
<div className='flex items-center gap-2 mb-4'>
|
||||
<div className='w-5 h-5 rounded-lg bg-[#ffffff11] flex items-center justify-center'>
|
||||
<HugeIconsFilter className='w-2.5 h-2.5 text-zinc-400' fill='currentColor' />
|
||||
<Funnel
|
||||
width={22}
|
||||
height={22}
|
||||
className='w-2.5 h-2.5 text-zinc-400'
|
||||
fill='currentColor'
|
||||
/>
|
||||
</div>
|
||||
<h3 className='text-base font-semibold text-zinc-100'>Filters</h3>
|
||||
</div>
|
||||
@@ -223,7 +224,9 @@ const ServerActivityLogContainer = () => {
|
||||
<div>
|
||||
<label className='block text-sm font-medium text-zinc-300 mb-2'>Search</label>
|
||||
<div className='relative'>
|
||||
<HugeIconsSearch
|
||||
<Magnifier
|
||||
width={22}
|
||||
height={22}
|
||||
className='absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-zinc-400 pointer-events-none z-10'
|
||||
fill='currentColor'
|
||||
/>
|
||||
@@ -291,7 +294,7 @@ const ServerActivityLogContainer = () => {
|
||||
onClick={clearAllFilters}
|
||||
className='flex items-center gap-2 w-full'
|
||||
>
|
||||
<HugeIconsX className='w-4 h-4' fill='currentColor' />
|
||||
<Xmark width={22} height={22} className='w-4 h-4' fill='currentColor' />
|
||||
Clear All Filters
|
||||
</ActionButton>
|
||||
)}
|
||||
@@ -312,7 +315,12 @@ const ServerActivityLogContainer = () => {
|
||||
<div className='bg-gradient-to-b from-[#ffffff08] to-[#ffffff05] border-[1px] border-[#ffffff12] rounded-xl p-4 hover:border-[#ffffff20] transition-all duration-150 shadow-sm'>
|
||||
<div className='flex items-center gap-2 mb-4'>
|
||||
<div className='w-5 h-5 rounded-lg bg-[#ffffff11] flex items-center justify-center'>
|
||||
<HugeIconsHistory className='w-2.5 h-2.5 text-zinc-400' fill='currentColor' />
|
||||
<ClockArrowRotateLeft
|
||||
width={22}
|
||||
height={22}
|
||||
className=' text-zinc-400'
|
||||
fill='currentColor'
|
||||
/>
|
||||
</div>
|
||||
<h3 className='text-base font-semibold text-zinc-100'>Events</h3>
|
||||
{filteredData?.items && (
|
||||
@@ -326,7 +334,12 @@ const ServerActivityLogContainer = () => {
|
||||
<Spinner centered />
|
||||
) : !filteredData?.items?.length ? (
|
||||
<div className='text-center py-12'>
|
||||
<HugeIconsHistory className='w-16 h-16 text-zinc-600 mb-4' fill='currentColor' />
|
||||
<ClockArrowRotateLeft
|
||||
width={22}
|
||||
height={22}
|
||||
className=' text-zinc-600 mb-4'
|
||||
fill='currentColor'
|
||||
/>
|
||||
<h3 className='text-lg font-semibold text-zinc-300 mb-2'>
|
||||
{hasActiveFilters ? 'No Matching Activity' : 'No Server Activity Yet'}
|
||||
</h3>
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import {
|
||||
ArrowDownToLine,
|
||||
Bars,
|
||||
CloudArrowUpIn,
|
||||
Pencil,
|
||||
Shield,
|
||||
TrashBin,
|
||||
TriangleExclamation,
|
||||
} from '@gravity-ui/icons';
|
||||
import { useStoreState } from 'easy-peasy';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import FlashMessageRender from '@/components/FlashMessageRender';
|
||||
import ActionButton from '@/components/elements/ActionButton';
|
||||
import Can from '@/components/elements/Can';
|
||||
import {
|
||||
@@ -10,28 +20,19 @@ import {
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/elements/DropdownMenu';
|
||||
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
|
||||
import Spinner from '@/components/elements/Spinner';
|
||||
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
|
||||
import { Dialog } from '@/components/elements/dialog';
|
||||
import HugeIconsAlert from '@/components/elements/hugeicons/Alert';
|
||||
import HugeIconsCloudUp from '@/components/elements/hugeicons/CloudUp';
|
||||
import HugeIconsDelete from '@/components/elements/hugeicons/Delete';
|
||||
import HugeIconsFileDownload from '@/components/elements/hugeicons/FileDownload';
|
||||
import HugeIconsFileSecurity from '@/components/elements/hugeicons/FileSecurity';
|
||||
import HugeIconsPencil from '@/components/elements/hugeicons/Pencil';
|
||||
import HugeIconsHamburger from '@/components/elements/hugeicons/hamburger';
|
||||
import FlashMessageRender from '@/components/FlashMessageRender';
|
||||
|
||||
import http, { httpErrorToHuman } from '@/api/http';
|
||||
import {
|
||||
getServerBackupDownloadUrl,
|
||||
} from '@/api/server/backups';
|
||||
import { getServerBackupDownloadUrl } from '@/api/server/backups';
|
||||
import { ServerBackup } from '@/api/server/types';
|
||||
|
||||
import { ApplicationStore } from '@/state';
|
||||
import { ServerContext } from '@/state/server';
|
||||
|
||||
import useFlash from '@/plugins/useFlash';
|
||||
|
||||
import { useUnifiedBackups } from './useUnifiedBackups';
|
||||
|
||||
interface Props {
|
||||
@@ -244,11 +245,15 @@ const BackupContextMenu = ({ backup }: Props) => {
|
||||
>
|
||||
This backup will no longer be protected from automated or accidental deletions.
|
||||
</Dialog.Confirm>
|
||||
<Dialog open={modal === 'restore'} onClose={() => {
|
||||
setModal('');
|
||||
setRestorePassword('');
|
||||
setRestoreTotpCode('');
|
||||
}} title='Restore Backup'>
|
||||
<Dialog
|
||||
open={modal === 'restore'}
|
||||
onClose={() => {
|
||||
setModal('');
|
||||
setRestorePassword('');
|
||||
setRestoreTotpCode('');
|
||||
}}
|
||||
title='Restore Backup'
|
||||
>
|
||||
<FlashMessageRender byKey={'backup:restore'} />
|
||||
<div className='space-y-4'>
|
||||
<div className='space-y-2'>
|
||||
@@ -261,7 +266,12 @@ const BackupContextMenu = ({ backup }: Props) => {
|
||||
|
||||
<div className='p-4 bg-red-500/10 border border-red-500/20 rounded-lg'>
|
||||
<div className='flex items-start space-x-3'>
|
||||
<HugeIconsAlert fill='currentColor' className='w-5 h-5 text-red-400 flex-shrink-0 mt-0.5' />
|
||||
<TriangleExclamation
|
||||
width={22}
|
||||
height={22}
|
||||
fill='currentColor'
|
||||
className=' text-red-400 flex-shrink-0 mt-0.5'
|
||||
/>
|
||||
<div className='space-y-1'>
|
||||
<h4 className='text-sm text-red-200 font-medium'>
|
||||
Destructive Action - Complete Server Restore
|
||||
@@ -274,16 +284,16 @@ const BackupContextMenu = ({ backup }: Props) => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className='space-y-3'>
|
||||
<div>
|
||||
<label htmlFor="restore-password" className="block text-sm font-medium text-zinc-300 mb-1">
|
||||
<label htmlFor='restore-password' className='block text-sm font-medium text-zinc-300 mb-1'>
|
||||
Password
|
||||
</label>
|
||||
<input
|
||||
id="restore-password"
|
||||
type="password"
|
||||
className="w-full px-4 py-2 rounded-lg outline-hidden bg-[#ffffff17] text-sm border border-zinc-700 focus:border-brand"
|
||||
placeholder="Enter your password"
|
||||
id='restore-password'
|
||||
type='password'
|
||||
className='w-full px-4 py-2 rounded-lg outline-hidden bg-[#ffffff17] text-sm border border-zinc-700 focus:border-brand'
|
||||
placeholder='Enter your password'
|
||||
value={restorePassword}
|
||||
onChange={(e) => setRestorePassword(e.target.value)}
|
||||
disabled={loading}
|
||||
@@ -292,14 +302,14 @@ const BackupContextMenu = ({ backup }: Props) => {
|
||||
|
||||
{hasTwoFactor && (
|
||||
<div>
|
||||
<label htmlFor="restore-totp" className="block text-sm font-medium text-zinc-300 mb-1">
|
||||
<label htmlFor='restore-totp' className='block text-sm font-medium text-zinc-300 mb-1'>
|
||||
Two-Factor Authentication Code
|
||||
</label>
|
||||
<input
|
||||
id="restore-totp"
|
||||
type="text"
|
||||
className="w-full px-4 py-2 rounded-lg outline-hidden bg-[#ffffff17] text-sm border border-zinc-700 focus:border-brand"
|
||||
placeholder="6-digit code"
|
||||
id='restore-totp'
|
||||
type='text'
|
||||
className='w-full px-4 py-2 rounded-lg outline-hidden bg-[#ffffff17] text-sm border border-zinc-700 focus:border-brand'
|
||||
placeholder='6-digit code'
|
||||
maxLength={6}
|
||||
value={restoreTotpCode}
|
||||
onChange={(e) => setRestoreTotpCode(e.target.value.replace(/[^0-9]/g, ''))}
|
||||
@@ -311,55 +321,80 @@ const BackupContextMenu = ({ backup }: Props) => {
|
||||
</div>
|
||||
|
||||
<Dialog.Footer>
|
||||
<ActionButton onClick={() => {
|
||||
setModal('');
|
||||
setRestorePassword('');
|
||||
setRestoreTotpCode('');
|
||||
}} variant='secondary' disabled={loading}>
|
||||
<ActionButton
|
||||
onClick={() => {
|
||||
setModal('');
|
||||
setRestorePassword('');
|
||||
setRestoreTotpCode('');
|
||||
}}
|
||||
variant='secondary'
|
||||
disabled={loading}
|
||||
>
|
||||
Cancel
|
||||
</ActionButton>
|
||||
<ActionButton onClick={() => doRestorationAction()} variant='danger' disabled={countdown > 0 || loading}>
|
||||
<ActionButton
|
||||
onClick={() => doRestorationAction()}
|
||||
variant='danger'
|
||||
disabled={countdown > 0 || loading}
|
||||
>
|
||||
{loading && <Spinner size='small' />}
|
||||
{loading ? 'Restoring...' : countdown > 0 ? `Delete All & Restore (${countdown}s)` : 'Delete All & Restore Backup'}
|
||||
{loading
|
||||
? 'Restoring...'
|
||||
: countdown > 0
|
||||
? `Delete All & Restore (${countdown}s)`
|
||||
: 'Delete All & Restore Backup'}
|
||||
</ActionButton>
|
||||
</Dialog.Footer>
|
||||
</Dialog>
|
||||
<Dialog open={modal === 'delete'} onClose={() => {
|
||||
setModal('');
|
||||
setDeletePassword('');
|
||||
setDeleteTotpCode('');
|
||||
}} title={`Delete "${backup.name}"`}>
|
||||
<Dialog
|
||||
open={modal === 'delete'}
|
||||
onClose={() => {
|
||||
setModal('');
|
||||
setDeletePassword('');
|
||||
setDeleteTotpCode('');
|
||||
}}
|
||||
title={`Delete "${backup.name}"`}
|
||||
>
|
||||
<FlashMessageRender byKey={'backup:delete'} />
|
||||
<div className="space-y-4">
|
||||
<p className="text-sm text-zinc-300">
|
||||
<div className='space-y-4'>
|
||||
<p className='text-sm text-zinc-300'>
|
||||
This is a permanent operation. The backup cannot be recovered once deleted.
|
||||
</p>
|
||||
|
||||
<div className="p-4 bg-red-500/10 border border-red-500/20 rounded-lg">
|
||||
<div className="flex items-start gap-3">
|
||||
<svg className="w-5 h-5 text-red-400 mt-0.5 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2}
|
||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
||||
<div className='p-4 bg-red-500/10 border border-red-500/20 rounded-lg'>
|
||||
<div className='flex items-start gap-3'>
|
||||
<svg
|
||||
className='w-5 h-5 text-red-400 mt-0.5 flex-shrink-0'
|
||||
fill='none'
|
||||
viewBox='0 0 24 24'
|
||||
stroke='currentColor'
|
||||
>
|
||||
<path
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
strokeWidth={2}
|
||||
d='M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z'
|
||||
/>
|
||||
</svg>
|
||||
<div className="text-sm">
|
||||
<p className="font-medium text-red-300">Warning</p>
|
||||
<p className="text-red-400 mt-1">
|
||||
<div className='text-sm'>
|
||||
<p className='font-medium text-red-300'>Warning</p>
|
||||
<p className='text-red-400 mt-1'>
|
||||
The backup file and its snapshot will be permanently deleted.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className='space-y-3'>
|
||||
<div>
|
||||
<label htmlFor="delete-password" className="block text-sm font-medium text-zinc-300 mb-1">
|
||||
<label htmlFor='delete-password' className='block text-sm font-medium text-zinc-300 mb-1'>
|
||||
Password
|
||||
</label>
|
||||
<input
|
||||
id="delete-password"
|
||||
type="password"
|
||||
className="w-full px-4 py-2 rounded-lg outline-hidden bg-[#ffffff17] text-sm border border-zinc-700 focus:border-brand"
|
||||
placeholder="Enter your password"
|
||||
id='delete-password'
|
||||
type='password'
|
||||
className='w-full px-4 py-2 rounded-lg outline-hidden bg-[#ffffff17] text-sm border border-zinc-700 focus:border-brand'
|
||||
placeholder='Enter your password'
|
||||
value={deletePassword}
|
||||
onChange={(e) => setDeletePassword(e.target.value)}
|
||||
disabled={loading}
|
||||
@@ -368,14 +403,14 @@ const BackupContextMenu = ({ backup }: Props) => {
|
||||
|
||||
{hasTwoFactor && (
|
||||
<div>
|
||||
<label htmlFor="delete-totp" className="block text-sm font-medium text-zinc-300 mb-1">
|
||||
<label htmlFor='delete-totp' className='block text-sm font-medium text-zinc-300 mb-1'>
|
||||
Two-Factor Authentication Code
|
||||
</label>
|
||||
<input
|
||||
id="delete-totp"
|
||||
type="text"
|
||||
className="w-full px-4 py-2 rounded-lg outline-hidden bg-[#ffffff17] text-sm border border-zinc-700 focus:border-brand"
|
||||
placeholder="6-digit code"
|
||||
id='delete-totp'
|
||||
type='text'
|
||||
className='w-full px-4 py-2 rounded-lg outline-hidden bg-[#ffffff17] text-sm border border-zinc-700 focus:border-brand'
|
||||
placeholder='6-digit code'
|
||||
maxLength={6}
|
||||
value={deleteTotpCode}
|
||||
onChange={(e) => setDeleteTotpCode(e.target.value.replace(/[^0-9]/g, ''))}
|
||||
@@ -398,11 +433,7 @@ const BackupContextMenu = ({ backup }: Props) => {
|
||||
>
|
||||
Cancel
|
||||
</ActionButton>
|
||||
<ActionButton
|
||||
variant='danger'
|
||||
onClick={doDeletion}
|
||||
disabled={loading}
|
||||
>
|
||||
<ActionButton variant='danger' onClick={doDeletion} disabled={loading}>
|
||||
{loading && <Spinner size='small' />}
|
||||
{loading ? 'Deleting...' : 'Delete Backup'}
|
||||
</ActionButton>
|
||||
@@ -418,30 +449,32 @@ const BackupContextMenu = ({ backup }: Props) => {
|
||||
disabled={loading}
|
||||
className='flex items-center justify-center w-8 h-8 p-0 hover:bg-zinc-700'
|
||||
>
|
||||
<HugeIconsHamburger className='h-4 w-4' fill='currentColor' />
|
||||
<div>
|
||||
<Bars width={22} height={22} fill='currentColor' />
|
||||
</div>
|
||||
</ActionButton>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align='end' className='w-48'>
|
||||
<Can action={'backup.download'}>
|
||||
<DropdownMenuItem onClick={doDownload} className='cursor-pointer'>
|
||||
<HugeIconsFileDownload className='h-4 w-4 mr-2' fill='currentColor' />
|
||||
<ArrowDownToLine width={22} height={22} className='mr-2' fill='currentColor' />
|
||||
Download
|
||||
</DropdownMenuItem>
|
||||
</Can>
|
||||
<Can action={'backup.restore'}>
|
||||
<DropdownMenuItem onClick={() => setModal('restore')} className='cursor-pointer'>
|
||||
<HugeIconsCloudUp className='h-4 w-4 mr-2' fill='currentColor' />
|
||||
<CloudArrowUpIn width={22} height={22} className=' mr-2' fill='currentColor' />
|
||||
Restore
|
||||
</DropdownMenuItem>
|
||||
</Can>
|
||||
<Can action={'backup.delete'}>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onClick={() => setModal('rename')} className='cursor-pointer'>
|
||||
<HugeIconsPencil className='h-4 w-4 mr-2' fill='currentColor' />
|
||||
<Pencil width={22} height={22} className=' mr-2' fill='currentColor' />
|
||||
Rename
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={onLockToggle} className='cursor-pointer'>
|
||||
<HugeIconsFileSecurity className='h-4 w-4 mr-2' fill='currentColor' />
|
||||
<Shield width={22} height={22} className=' mr-2' fill='currentColor' />
|
||||
{backup.isLocked ? 'Unlock' : 'Lock'}
|
||||
</DropdownMenuItem>
|
||||
{!backup.isLocked && (
|
||||
@@ -451,7 +484,7 @@ const BackupContextMenu = ({ backup }: Props) => {
|
||||
onClick={() => setModal('delete')}
|
||||
className='cursor-pointer text-red-400 focus:text-red-300'
|
||||
>
|
||||
<HugeIconsDelete className='h-4 w-4 mr-2' fill='currentColor' />
|
||||
<TrashBin width={22} height={22} className=' mr-2' fill='currentColor' />
|
||||
Delete
|
||||
</DropdownMenuItem>
|
||||
</>
|
||||
@@ -467,7 +500,7 @@ const BackupContextMenu = ({ backup }: Props) => {
|
||||
disabled={loading}
|
||||
className='flex items-center gap-2'
|
||||
>
|
||||
<HugeIconsDelete className='h-4 w-4' fill='currentColor' />
|
||||
<TrashBin width={22} height={22} fill='currentColor' />
|
||||
<span className='hidden sm:inline'>Delete</span>
|
||||
</ActionButton>
|
||||
)}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { Cloud, CloudArrowUpIn, Lock } from '@gravity-ui/icons';
|
||||
import { format, formatDistanceToNow } from 'date-fns';
|
||||
|
||||
import Can from '@/components/elements/Can';
|
||||
import { Checkbox } from '@/components/elements/CheckboxNew';
|
||||
import Spinner from '@/components/elements/Spinner';
|
||||
import HugeIconsSquareLock from '@/components/elements/hugeicons/SquareLock';
|
||||
import HugeIconsStorage from '@/components/elements/hugeicons/Storage';
|
||||
import HugeIconsRefresh from '@/components/elements/hugeicons/Refresh';
|
||||
import { PageListItem } from '@/components/elements/pages/PageList';
|
||||
|
||||
import { bytesToString } from '@/lib/formatters';
|
||||
@@ -46,7 +44,6 @@ interface Props {
|
||||
const BackupItem = ({ backup, isSelected = false, onToggleSelect, isSelectable = false, retryBackup }: Props) => {
|
||||
const { addFlash, clearFlashes } = useFlash();
|
||||
|
||||
|
||||
const handleRetry = async () => {
|
||||
if (!backup.canRetry) return;
|
||||
|
||||
@@ -75,11 +72,11 @@ const BackupItem = ({ backup, isSelected = false, onToggleSelect, isSelectable =
|
||||
if (isActive) {
|
||||
return <Spinner size={'small'} />;
|
||||
} else if (backup.isLocked) {
|
||||
return <HugeIconsSquareLock className='text-red-400 w-4 h-4' fill='currentColor' />;
|
||||
return <Lock width={22} height={22} className='text-red-400 ' fill='currentColor' />;
|
||||
} else if (backup.status === 'completed' || backup.isSuccessful) {
|
||||
return <HugeIconsStorage className='text-green-400 w-4 h-4' fill='currentColor' />;
|
||||
return <Cloud width={22} height={22} className='text-green-400 ' fill='currentColor' />;
|
||||
} else {
|
||||
return <HugeIconsStorage className='text-red-400 w-4 h-4' fill='currentColor' />;
|
||||
return <Cloud width={22} height={22} className='text-red-400 ' fill='currentColor' />;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -172,9 +169,8 @@ const BackupItem = ({ backup, isSelected = false, onToggleSelect, isSelectable =
|
||||
</div>
|
||||
<div className='w-full bg-zinc-700 rounded-full h-2'>
|
||||
<div
|
||||
className={`h-2 rounded-full transition-all duration-300 ${
|
||||
backup.status === 'completed' ? 'bg-green-500' : 'bg-blue-500'
|
||||
}`}
|
||||
className={`h-2 rounded-full transition-all duration-300 ${backup.status === 'completed' ? 'bg-green-500' : 'bg-blue-500'
|
||||
}`}
|
||||
style={{ width: `${backup.progress || 0}%` }}
|
||||
/>
|
||||
</div>
|
||||
@@ -187,7 +183,6 @@ const BackupItem = ({ backup, isSelected = false, onToggleSelect, isSelectable =
|
||||
)}
|
||||
|
||||
{backup.checksum && <p className='text-xs text-zinc-400 font-mono truncate'>{backup.checksum}</p>}
|
||||
|
||||
</div>
|
||||
|
||||
{/* Size info for completed backups */}
|
||||
@@ -226,7 +221,7 @@ const BackupItem = ({ backup, isSelected = false, onToggleSelect, isSelectable =
|
||||
className='p-2 rounded-lg bg-blue-500/10 border border-blue-500/20 text-blue-400 hover:bg-blue-500/20 transition-colors'
|
||||
title='Retry backup'
|
||||
>
|
||||
<HugeIconsRefresh className='w-4 h-4' />
|
||||
<CloudArrowUpIn width={22} height={22} />
|
||||
</button>
|
||||
</Can>
|
||||
)}
|
||||
@@ -250,7 +245,7 @@ const BackupItem = ({ backup, isSelected = false, onToggleSelect, isSelectable =
|
||||
jobMessage: backup.message,
|
||||
jobId: '',
|
||||
jobError: null,
|
||||
object: 'backup' as const
|
||||
object: 'backup' as const,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -261,4 +256,5 @@ const BackupItem = ({ backup, isSelected = false, onToggleSelect, isSelectable =
|
||||
);
|
||||
};
|
||||
|
||||
export default BackupItem;
|
||||
export default BackupItem;
|
||||
|
||||
|
||||
@@ -43,8 +43,8 @@ const ServerConsoleContainer = () => {
|
||||
{isNodeUnderMaintenance
|
||||
? 'The node of this server is currently under maintenance and all actions are unavailable.'
|
||||
: isInstalling
|
||||
? 'This server is currently running its installation process and most actions are unavailable.'
|
||||
: 'This server is currently being transferred to another node and all actions are unavailable.'}
|
||||
? 'This server is currently running its installation process and most actions are unavailable.'
|
||||
: 'This server is currently being transferred to another node and all actions are unavailable.'}
|
||||
</Alert>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { ArrowsRotateRight } from '@gravity-ui/icons';
|
||||
import { Actions, useStoreActions } from 'easy-peasy';
|
||||
import { useState } from 'react';
|
||||
|
||||
import ActionButton from '@/components/elements/ActionButton';
|
||||
import Spinner from '@/components/elements/Spinner';
|
||||
import HugeIconsRefresh from '@/components/elements/hugeicons/Refresh';
|
||||
|
||||
import { httpErrorToHuman } from '@/api/http';
|
||||
import { ServerDatabase } from '@/api/server/databases/getServerDatabases';
|
||||
@@ -51,8 +51,8 @@ const RotatePasswordButton = ({
|
||||
|
||||
return (
|
||||
<ActionButton onClick={rotate} className='flex-none'>
|
||||
<div className='flex justify-center items-center h-4 w-4'>
|
||||
{!loading && <HugeIconsRefresh className='h-4 w-4' />}
|
||||
<div className='flex justify-center items-center'>
|
||||
{!loading && <ArrowsRotateRight width={22} height={22} />}
|
||||
{loading && <Spinner size={'small'} />}
|
||||
</div>
|
||||
</ActionButton>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ChevronDown, ChevronUp } from '@gravity-ui/icons';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import ActionButton from '@/components/elements/ActionButton';
|
||||
@@ -12,8 +13,6 @@ import {
|
||||
} from '@/components/elements/DropdownMenu';
|
||||
import Modal from '@/components/elements/Modal';
|
||||
import Spinner from '@/components/elements/Spinner';
|
||||
import HugeIconsArrowDown from '@/components/elements/hugeicons/ArrowDown';
|
||||
import HugeIconsArrowUp from '@/components/elements/hugeicons/ArrowUp';
|
||||
import { SocketEvent, SocketRequest } from '@/components/server/events';
|
||||
|
||||
import setSelectedDockerImage from '@/api/server/setSelectedDockerImage';
|
||||
@@ -112,12 +111,9 @@ const JavaVersionModalFeature = () => {
|
||||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ') || 'Select a version'}
|
||||
{dropDownOpen ? (
|
||||
<HugeIconsArrowUp fill={'currentColor'} className={`ml-2 w-[16px] h-[16px]`} />
|
||||
<ChevronUp fill={'currentColor'} className={`ml-2 w-[16px] h-[16px]`} />
|
||||
) : (
|
||||
<HugeIconsArrowDown
|
||||
fill={'currentColor'}
|
||||
className={`ml-2 w-[16px] h-[16px]`}
|
||||
/>
|
||||
<ChevronDown fill={'currentColor'} className={`ml-2 w-[16px] h-[16px]`} />
|
||||
)}
|
||||
</button>
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Check, Link, TriangleExclamation } from '@gravity-ui/icons';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
@@ -5,9 +6,6 @@ import ActionButton from '@/components/elements/ActionButton';
|
||||
import Modal from '@/components/elements/Modal';
|
||||
import Spinner from '@/components/elements/Spinner';
|
||||
import { Alert } from '@/components/elements/alert';
|
||||
import HugeIconsAlert from '@/components/elements/hugeicons/Alert';
|
||||
import HugeIconsCheck from '@/components/elements/hugeicons/Check';
|
||||
import HugeIconsLink from '@/components/elements/hugeicons/Link';
|
||||
import { SocketEvent } from '@/components/server/events';
|
||||
|
||||
import { debounce, isCrashLine } from '@/lib/mclogsUtils';
|
||||
@@ -286,7 +284,12 @@ const AnalysisModal = ({
|
||||
<div className='space-y-6'>
|
||||
<div className='bg-red-500/10 border border-red-500/20 rounded-lg p-4'>
|
||||
<div className='flex items-start gap-3'>
|
||||
<HugeIconsAlert className='w-6 h-6 text-red-400 flex-shrink-0 mt-0.5' fill='currentColor' />
|
||||
<TriangleExclamation
|
||||
width={22}
|
||||
height={22}
|
||||
className='w-6 h-6 text-red-400 flex-shrink-0 mt-0.5'
|
||||
fill='currentColor'
|
||||
/>
|
||||
<div className='flex-1'>
|
||||
<h3 className='font-semibold text-red-400 text-lg'>Analysis Failed</h3>
|
||||
<p className='text-neutral-300 mt-2'>{error}</p>
|
||||
@@ -320,7 +323,7 @@ const AnalysisModal = ({
|
||||
rel='noopener noreferrer'
|
||||
className='text-sm text-blue-400 hover:text-blue-300 flex items-center gap-1.5 transition-colors'
|
||||
>
|
||||
<HugeIconsLink className='w-4 h-4' />
|
||||
<Link width={22} height={22} className='w-4 h-4' />
|
||||
Powered by mclo.gs
|
||||
</a>
|
||||
</div>
|
||||
@@ -370,7 +373,12 @@ const AnalysisModal = ({
|
||||
return (
|
||||
<div className='bg-green-500/10 border border-green-500/20 rounded-lg p-4 mb-6'>
|
||||
<div className='flex items-start gap-3'>
|
||||
<HugeIconsCheck className='w-6 h-6 text-green-400 flex-shrink-0 mt-0.5' fill='currentColor' />
|
||||
<Check
|
||||
width={22}
|
||||
height={22}
|
||||
className='w-6 h-6 text-green-400 flex-shrink-0 mt-0.5'
|
||||
fill='currentColor'
|
||||
/>
|
||||
<div>
|
||||
<h3 className='font-semibold text-green-400 text-lg'>No Issues Detected</h3>
|
||||
<p className='text-neutral-300 mt-2'>
|
||||
@@ -392,7 +400,9 @@ const AnalysisModal = ({
|
||||
<div key={idx} className='bg-red-500/10 border border-red-500/20 rounded-lg overflow-hidden'>
|
||||
<div className='p-4'>
|
||||
<div className='flex items-start gap-3'>
|
||||
<HugeIconsAlert
|
||||
<TriangleExclamation
|
||||
width={22}
|
||||
height={22}
|
||||
className='w-5 h-5 text-red-400 flex-shrink-0 mt-0.5'
|
||||
fill='currentColor'
|
||||
/>
|
||||
@@ -444,7 +454,12 @@ const AnalysisModal = ({
|
||||
{allSolutions.map((solution, idx) => (
|
||||
<div key={idx} className='flex items-start gap-3'>
|
||||
<div className='bg-green-500/20 rounded-full p-1 flex-shrink-0 mt-0.5'>
|
||||
<HugeIconsCheck className='w-4 h-4 text-green-400' fill='currentColor' />
|
||||
<Check
|
||||
width={22}
|
||||
height={22}
|
||||
className='w-4 h-4 text-green-400'
|
||||
fill='currentColor'
|
||||
/>
|
||||
</div>
|
||||
<div className='flex-1'>
|
||||
<p className='text-neutral-200 leading-relaxed'>{solution.message}</p>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { TrashBin } from '@gravity-ui/icons';
|
||||
import { useState } from 'react';
|
||||
|
||||
import ActionButton from '@/components/elements/ActionButton';
|
||||
import { Dialog } from '@/components/elements/dialog';
|
||||
import HugeIconsDelete from '@/components/elements/hugeicons/Delete';
|
||||
|
||||
import deleteServerAllocation from '@/api/server/network/deleteServerAllocation';
|
||||
import getServerAllocations from '@/api/swr/getServerAllocations';
|
||||
@@ -55,7 +55,7 @@ const DeleteAllocationButton = ({ allocation }: Props) => {
|
||||
onClick={() => setConfirm(true)}
|
||||
className='flex items-center gap-2'
|
||||
>
|
||||
<HugeIconsDelete className='h-4 w-4' fill='currentColor' />
|
||||
<TrashBin width={22} height={22} fill='currentColor' />
|
||||
<span className='hidden sm:inline'>Delete</span>
|
||||
</ActionButton>
|
||||
</>
|
||||
|
||||
@@ -6,9 +6,6 @@ import * as yup from 'yup';
|
||||
import FlashMessageRender from '@/components/FlashMessageRender';
|
||||
import ActionButton from '@/components/elements/ActionButton';
|
||||
import FormikFieldWrapper from '@/components/elements/FormikFieldWrapper';
|
||||
import Input from '@/components/elements/Input';
|
||||
import Select from '@/components/elements/Select';
|
||||
import HugeIconsLink from '@/components/elements/hugeicons/Link';
|
||||
|
||||
import {
|
||||
SubdomainInfo,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { TriangleExclamation } from '@gravity-ui/icons';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import ActionButton from '@/components/elements/ActionButton';
|
||||
import Spinner from '@/components/elements/Spinner';
|
||||
import { Dialog } from '@/components/elements/dialog';
|
||||
import HugeIconsAlert from '@/components/elements/hugeicons/Alert';
|
||||
|
||||
import {
|
||||
UI_CONFIG,
|
||||
@@ -111,7 +111,9 @@ const OperationProgressModal: React.FC<Props> = ({
|
||||
</div>
|
||||
);
|
||||
case 'error':
|
||||
return <HugeIconsAlert fill='currentColor' className='w-5 h-5 text-red-400' />;
|
||||
return (
|
||||
<TriangleExclamation width={22} height={22} fill='currentColor' className='w-5 h-5 text-red-400' />
|
||||
);
|
||||
default:
|
||||
return <Spinner size={'small'} />;
|
||||
}
|
||||
@@ -131,7 +133,7 @@ const OperationProgressModal: React.FC<Props> = ({
|
||||
return (
|
||||
<Dialog
|
||||
open={visible}
|
||||
onClose={canClose ? handleClose : () => {}}
|
||||
onClose={canClose ? handleClose : () => { }}
|
||||
preventExternalClose={!canClose}
|
||||
hideCloseIcon={!canClose}
|
||||
title={operationType}
|
||||
@@ -150,7 +152,12 @@ const OperationProgressModal: React.FC<Props> = ({
|
||||
{error ? (
|
||||
<div className='space-y-4'>
|
||||
<div className='flex items-center justify-center space-x-3'>
|
||||
<HugeIconsAlert fill='currentColor' className='w-6 h-6 text-red-400' />
|
||||
<TriangleExclamation
|
||||
width={22}
|
||||
height={22}
|
||||
fill='currentColor'
|
||||
className='w-6 h-6 text-red-400'
|
||||
/>
|
||||
<span className='text-red-400 font-semibold text-lg'>Error</span>
|
||||
</div>
|
||||
<div className='p-4 bg-red-500/10 border border-red-500/20 rounded-lg'>
|
||||
@@ -213,7 +220,12 @@ const OperationProgressModal: React.FC<Props> = ({
|
||||
{isFailedStatus(operation.status) && (
|
||||
<div className='p-4 bg-red-500/10 border border-red-500/20 rounded-lg'>
|
||||
<div className='flex items-center justify-center space-x-2 mb-2'>
|
||||
<HugeIconsAlert fill='currentColor' className='w-5 h-5 text-red-400' />
|
||||
<TriangleExclamation
|
||||
width={22}
|
||||
height={22}
|
||||
fill='currentColor'
|
||||
className='w-5 h-5 text-red-400'
|
||||
/>
|
||||
<p className='text-sm text-red-300 font-medium'>Operation failed</p>
|
||||
</div>
|
||||
{operation.message && (
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import ModalContext from '@/context/ModalContext';
|
||||
import { TZDate } from '@date-fns/tz';
|
||||
import { Link, TriangleExclamation } from '@gravity-ui/icons';
|
||||
import { format } from 'date-fns';
|
||||
import { useStoreState } from 'easy-peasy';
|
||||
import { Form, Formik, FormikHelpers } from 'formik';
|
||||
@@ -10,8 +11,6 @@ import ActionButton from '@/components/elements/ActionButton';
|
||||
import Field from '@/components/elements/Field';
|
||||
import FormikSwitchV2 from '@/components/elements/FormikSwitchV2';
|
||||
import ItemContainer from '@/components/elements/ItemContainer';
|
||||
import HugeIconsAlert from '@/components/elements/hugeicons/Alert';
|
||||
import HugeIconsLink from '@/components/elements/hugeicons/Link';
|
||||
|
||||
import asModal from '@/hoc/asModal';
|
||||
|
||||
@@ -192,7 +191,9 @@ const EditScheduleModal = ({ schedule }: Props) => {
|
||||
{timezoneInfo.isDifferent && (
|
||||
<div className={'bg-blue-900/20 border border-blue-400/30 rounded-lg p-4 my-2'}>
|
||||
<div className={'flex items-start gap-3'}>
|
||||
<HugeIconsAlert
|
||||
<TriangleExclamation
|
||||
width={22}
|
||||
height={22}
|
||||
fill='currentColor'
|
||||
className={'text-blue-400 mt-0.5 flex-shrink-0 h-5 w-5'}
|
||||
/>
|
||||
@@ -243,7 +244,7 @@ const EditScheduleModal = ({ schedule }: Props) => {
|
||||
// onChange={() => setShowCheetsheet((s) => !s)}
|
||||
labelClasses='cursor-pointer'
|
||||
>
|
||||
<HugeIconsLink fill='currentColor' className={`px-5 h-5 w-5`} />
|
||||
<Link width={22} height={22} fill='currentColor' className={`px-5 h-5 w-5`} />
|
||||
</ItemContainer>
|
||||
</a>
|
||||
{/* This table would be pretty awkward to make look nice
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Calendar } from '@gravity-ui/icons';
|
||||
import { format } from 'date-fns';
|
||||
|
||||
import HugeIconsCalendar from '@/components/elements/hugeicons/Calendar';
|
||||
import ScheduleCronRow from '@/components/server/schedules/ScheduleCronRow';
|
||||
|
||||
import { Schedule } from '@/api/server/schedules/getServerSchedules';
|
||||
@@ -9,7 +9,7 @@ const ScheduleRow = ({ schedule }: { schedule: Schedule }) => (
|
||||
<>
|
||||
<div className={`flex-auto`}>
|
||||
<div className='flex flex-row flex-none align-middle items-center gap-6'>
|
||||
<HugeIconsCalendar className='flex-none' fill='currentColor' />
|
||||
<Calendar width={25} height={25} className='flex-none' fill='currentColor' />
|
||||
<div>
|
||||
<div className='flex flex-row items-center gap-2 text-lg'>
|
||||
<p>{schedule.name}</p>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ComponentType } from 'react';
|
||||
import { CircleQuestion, CloudArrowUpIn, PencilToLine, Power, Terminal, TrashBin } from '@gravity-ui/icons';
|
||||
import { useState } from 'react';
|
||||
|
||||
import ActionButton from '@/components/elements/ActionButton';
|
||||
@@ -6,13 +6,6 @@ import Can from '@/components/elements/Can';
|
||||
import ConfirmationModal from '@/components/elements/ConfirmationModal';
|
||||
import ItemContainer from '@/components/elements/ItemContainer';
|
||||
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
|
||||
import HugeIconsCopy from '@/components/elements/hugeicons/Copy';
|
||||
import HugeIconsPencil from '@/components/elements/hugeicons/Pencil';
|
||||
import HugeIconsPower from '@/components/elements/hugeicons/Power';
|
||||
import HugeIconsQuestion from '@/components/elements/hugeicons/Question';
|
||||
import HugeIconsTerminal from '@/components/elements/hugeicons/Terminal';
|
||||
import HugeIconsTrash from '@/components/elements/hugeicons/Trash';
|
||||
import { HugeIconProps } from '@/components/elements/hugeicons/props';
|
||||
import TaskDetailsModal from '@/components/server/schedules/TaskDetailsModal';
|
||||
|
||||
import { httpErrorToHuman } from '@/api/http';
|
||||
@@ -28,16 +21,16 @@ interface Props {
|
||||
task: Task;
|
||||
}
|
||||
|
||||
const getActionDetails = (action: string): [string, ComponentType<HugeIconProps>, boolean?] => {
|
||||
const getActionDetails = (action: string): [string, any, boolean?] => {
|
||||
switch (action) {
|
||||
case 'command':
|
||||
return ['Send Command', HugeIconsTerminal, true];
|
||||
return ['Send Command', Terminal, true];
|
||||
case 'power':
|
||||
return ['Send Power Action', HugeIconsPower];
|
||||
return ['Send Power Action', Power];
|
||||
case 'backup':
|
||||
return ['Create Backup', HugeIconsCopy];
|
||||
return ['Create Backup', CloudArrowUpIn];
|
||||
default:
|
||||
return ['Unknown Action', HugeIconsQuestion];
|
||||
return ['Unknown Action', CircleQuestion];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -132,7 +125,7 @@ const ScheduleTaskRow = ({ schedule, task }: Props) => {
|
||||
onClick={() => setIsEditing(true)}
|
||||
aria-label='Edit scheduled task'
|
||||
>
|
||||
<HugeIconsPencil fill='currentColor' />
|
||||
<PencilToLine width={22} height={22} fill='currentColor' />
|
||||
Edit
|
||||
</ActionButton>
|
||||
</Can>
|
||||
@@ -144,7 +137,7 @@ const ScheduleTaskRow = ({ schedule, task }: Props) => {
|
||||
className='flex items-center gap-2'
|
||||
aria-label='Delete scheduled task'
|
||||
>
|
||||
<HugeIconsTrash fill='currentColor' className='w-4 h-4' />
|
||||
<TrashBin width={22} height={22} fill='currentColor' className='w-4 h-4' />
|
||||
<span className='hidden sm:inline'>Delete</span>
|
||||
</ActionButton>
|
||||
</Can>
|
||||
|
||||
@@ -25,7 +25,6 @@ import useFlash from '@/plugins/useFlash';
|
||||
// TODO: Port modern dropdowns to Formik and integrate them
|
||||
// import { DropdownMenu, DropdownMenuContent, DropdownMenuRadioGroup, DropdownMenuRadioItem } from '@/components/elements/DropdownMenu';
|
||||
// import { DropdownMenuTrigger } from '@radix-ui/react-dropdown-menu';
|
||||
// import HugeIconsArrowUp from '@/components/elements/hugeicons/ArrowUp';
|
||||
|
||||
const Label = styled.label`
|
||||
display: inline-block;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Box, TriangleExclamation } from '@gravity-ui/icons';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import isEqual from 'react-fast-compare';
|
||||
import { toast } from 'sonner';
|
||||
@@ -16,8 +17,6 @@ import ServerContentBlock from '@/components/elements/ServerContentBlock';
|
||||
import Spinner from '@/components/elements/Spinner';
|
||||
import { Switch } from '@/components/elements/SwitchV2';
|
||||
import TitledGreyBox from '@/components/elements/TitledGreyBox';
|
||||
import HugeIconsAlert from '@/components/elements/hugeicons/Alert';
|
||||
import HugeIconsEggs from '@/components/elements/hugeicons/Egg';
|
||||
import OperationProgressModal from '@/components/server/operations/OperationProgressModal';
|
||||
|
||||
import { httpErrorToHuman } from '@/api/http';
|
||||
@@ -508,8 +507,8 @@ const SoftwareContainer = () => {
|
||||
selectedDockerImage && eggPreview.docker_images
|
||||
? eggPreview.docker_images[selectedDockerImage]
|
||||
: eggPreview.default_docker_image && eggPreview.docker_images
|
||||
? eggPreview.docker_images[eggPreview.default_docker_image]
|
||||
: '';
|
||||
? eggPreview.docker_images[eggPreview.default_docker_image]
|
||||
: '';
|
||||
|
||||
// Filter out empty environment variables to prevent validation issues
|
||||
const filteredEnvironment: Record<string, string> = {};
|
||||
@@ -617,7 +616,12 @@ const SoftwareContainer = () => {
|
||||
<div className='flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4'>
|
||||
<div className='flex items-center gap-3 sm:gap-4 min-w-0 flex-1'>
|
||||
<div className='w-10 h-10 sm:w-12 sm:h-12 bg-[#ffffff11] rounded-lg flex items-center justify-center flex-shrink-0'>
|
||||
<HugeIconsEggs fill='currentColor' className='w-5 h-5 sm:w-6 sm:h-6 text-neutral-300' />
|
||||
<Box
|
||||
width={22}
|
||||
height={22}
|
||||
fill='currentColor'
|
||||
className='w-5 h-5 sm:w-6 sm:h-6 text-neutral-300'
|
||||
/>
|
||||
</div>
|
||||
<div className='min-w-0 flex-1'>
|
||||
{currentEggName ? (
|
||||
@@ -884,11 +888,10 @@ const SoftwareContainer = () => {
|
||||
handleVariableChange(variable.env_variable, e.target.value)
|
||||
}
|
||||
placeholder={variable.default_value || 'Enter value...'}
|
||||
className={`w-full px-3 py-2 bg-[#ffffff08] border rounded-lg text-sm text-neutral-200 placeholder:text-neutral-500 focus:outline-none transition-colors ${
|
||||
variableErrors[variable.env_variable]
|
||||
className={`w-full px-3 py-2 bg-[#ffffff08] border rounded-lg text-sm text-neutral-200 placeholder:text-neutral-500 focus:outline-none transition-colors ${variableErrors[variable.env_variable]
|
||||
? 'border-red-500 focus:border-red-500'
|
||||
: 'border-[#ffffff12] focus:border-brand'
|
||||
}`}
|
||||
}`}
|
||||
/>
|
||||
{variableErrors[variable.env_variable] && (
|
||||
<p className='text-xs text-red-400 mt-1'>
|
||||
@@ -928,7 +931,8 @@ const SoftwareContainer = () => {
|
||||
Create Backup
|
||||
</label>
|
||||
<p className='text-xs text-neutral-400 leading-relaxed'>
|
||||
{backupLimit !== 0 && (backupLimit === null || (backups?.backupCount || 0) < backupLimit)
|
||||
{backupLimit !== 0 &&
|
||||
(backupLimit === null || (backups?.backupCount || 0) < backupLimit)
|
||||
? 'Automatically create a backup before applying changes'
|
||||
: backupLimit === 0
|
||||
? 'Backups are disabled for this server'
|
||||
@@ -939,7 +943,10 @@ const SoftwareContainer = () => {
|
||||
<Switch
|
||||
checked={shouldBackup}
|
||||
onCheckedChange={setShouldBackup}
|
||||
disabled={backupLimit === 0 || (backupLimit !== null && (backups?.backupCount || 0) >= backupLimit)}
|
||||
disabled={
|
||||
backupLimit === 0 ||
|
||||
(backupLimit !== null && (backups?.backupCount || 0) >= backupLimit)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1087,24 +1094,23 @@ const SoftwareContainer = () => {
|
||||
{eggPreview.warnings.map((warning, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`p-4 border rounded-lg ${
|
||||
warning.severity === 'error'
|
||||
className={`p-4 border rounded-lg ${warning.severity === 'error'
|
||||
? 'bg-red-500/10 border-red-500/20'
|
||||
: 'bg-amber-500/10 border-amber-500/20'
|
||||
}`}
|
||||
}`}
|
||||
>
|
||||
<div className='flex items-start gap-3'>
|
||||
<HugeIconsAlert
|
||||
<TriangleExclamation
|
||||
width={22}
|
||||
height={22}
|
||||
fill='currentColor'
|
||||
className={`w-5 h-5 flex-shrink-0 mt-0.5 ${
|
||||
warning.severity === 'error' ? 'text-red-400' : 'text-amber-400'
|
||||
}`}
|
||||
className={`w-5 h-5 flex-shrink-0 mt-0.5 ${warning.severity === 'error' ? 'text-red-400' : 'text-amber-400'
|
||||
}`}
|
||||
/>
|
||||
<div>
|
||||
<h4
|
||||
className={`font-semibold mb-2 ${
|
||||
warning.severity === 'error' ? 'text-red-400' : 'text-amber-400'
|
||||
}`}
|
||||
className={`font-semibold mb-2 ${warning.severity === 'error' ? 'text-red-400' : 'text-amber-400'
|
||||
}`}
|
||||
>
|
||||
{warning.type === 'subdomain_incompatible'
|
||||
? 'Subdomain Will Be Deleted'
|
||||
@@ -1121,7 +1127,9 @@ const SoftwareContainer = () => {
|
||||
{/* General Warning */}
|
||||
<div className='p-4 bg-amber-500/10 border border-amber-500/20 rounded-lg'>
|
||||
<div className='flex items-start gap-3'>
|
||||
<HugeIconsAlert
|
||||
<TriangleExclamation
|
||||
width={22}
|
||||
height={22}
|
||||
fill='currentColor'
|
||||
className='w-5 h-5 text-amber-400 flex-shrink-0 mt-0.5'
|
||||
/>
|
||||
@@ -1232,7 +1240,12 @@ const SoftwareContainer = () => {
|
||||
>
|
||||
<div className='space-y-4'>
|
||||
<div className='flex items-start gap-3 p-4 bg-red-500/10 border border-red-500/20 rounded-lg'>
|
||||
<HugeIconsAlert fill='currentColor' className='w-5 h-5 text-red-400 flex-shrink-0 mt-0.5' />
|
||||
<TriangleExclamation
|
||||
width={22}
|
||||
height={22}
|
||||
fill='currentColor'
|
||||
className='w-5 h-5 text-red-400 flex-shrink-0 mt-0.5'
|
||||
/>
|
||||
<div>
|
||||
<h4 className='text-red-400 font-semibold mb-2'>DANGER: No Backup Selected</h4>
|
||||
<p className='text-sm text-neutral-300'>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ChevronDown, ChevronUp, Lock } from '@gravity-ui/icons';
|
||||
import debounce from 'debounce';
|
||||
import { memo, useState } from 'react';
|
||||
import isEqual from 'react-fast-compare';
|
||||
@@ -13,9 +14,6 @@ import {
|
||||
import InputSpinner from '@/components/elements/InputSpinner';
|
||||
import { Switch } from '@/components/elements/SwitchV2';
|
||||
import { Input } from '@/components/elements/TextInput';
|
||||
import HugeIconsArrowDown from '@/components/elements/hugeicons/ArrowDown';
|
||||
import HugeIconsArrowUp from '@/components/elements/hugeicons/ArrowUp';
|
||||
import HugeIconsSquareLock from '@/components/elements/hugeicons/SquareLock';
|
||||
|
||||
import { ServerEggVariable } from '@/api/server/types';
|
||||
import updateStartupVariable from '@/api/server/updateStartupVariable';
|
||||
@@ -77,7 +75,9 @@ const VariableBox = ({ variable }: Props) => {
|
||||
<div className='flex flex-col items-baseline sm:flex-row sm:justify-between gap-2 sm:gap-3'>
|
||||
<div className='flex items-center gap-2 min-w-0'>
|
||||
{!variable.isEditable && (
|
||||
<HugeIconsSquareLock
|
||||
<Lock
|
||||
width={22}
|
||||
height={22}
|
||||
fill={'currentColor'}
|
||||
className='text-neutral-500 w-4 h-4 flex-shrink-0'
|
||||
/>
|
||||
@@ -101,8 +101,8 @@ const VariableBox = ({ variable }: Props) => {
|
||||
? 'Enabled'
|
||||
: 'Disabled'
|
||||
: variable.serverValue === '1'
|
||||
? 'On'
|
||||
: 'Off'}
|
||||
? 'On'
|
||||
: 'Off'}
|
||||
</span>
|
||||
<Switch
|
||||
disabled={!canEdit || !variable.isEditable}
|
||||
@@ -134,12 +134,16 @@ const VariableBox = ({ variable }: Props) => {
|
||||
{variable.serverValue}
|
||||
</span>
|
||||
{dropDownOpen ? (
|
||||
<HugeIconsArrowUp
|
||||
<ChevronUp
|
||||
width={22}
|
||||
height={22}
|
||||
fill={'currentColor'}
|
||||
className='w-[14px] h-[14px] opacity-60 flex-shrink-0'
|
||||
/>
|
||||
) : (
|
||||
<HugeIconsArrowDown
|
||||
<ChevronDown
|
||||
width={22}
|
||||
height={22}
|
||||
fill={'currentColor'}
|
||||
className='w-[14px] h-[14px] opacity-60 flex-shrink-0'
|
||||
/>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { ChevronLeft } from '@gravity-ui/icons';
|
||||
import { useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import ActionButton from '@/components/elements/ActionButton';
|
||||
import { MainPageHeader } from '@/components/elements/MainPageHeader';
|
||||
import ServerContentBlock from '@/components/elements/ServerContentBlock';
|
||||
import HugeIconsArrowLeft from '@/components/elements/hugeicons/ArrowLeft';
|
||||
import UserFormComponent from '@/components/server/users/UserFormComponent';
|
||||
|
||||
import { ServerContext } from '@/state/server';
|
||||
@@ -32,7 +32,7 @@ const CreateUserContainer = () => {
|
||||
className='flex items-center gap-2'
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
<HugeIconsArrowLeft className='w-4 h-4' fill='currentColor' />
|
||||
<ChevronLeft width={22} height={22} fill='currentColor' />
|
||||
Back to Users
|
||||
</ActionButton>
|
||||
</MainPageHeader>
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { ChevronLeft, Person } from '@gravity-ui/icons';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
import ActionButton from '@/components/elements/ActionButton';
|
||||
import { MainPageHeader } from '@/components/elements/MainPageHeader';
|
||||
import ServerContentBlock from '@/components/elements/ServerContentBlock';
|
||||
import HugeIconsArrowLeft from '@/components/elements/hugeicons/ArrowLeft';
|
||||
import HugeIconsUser from '@/components/elements/hugeicons/User';
|
||||
import UserFormComponent from '@/components/server/users/UserFormComponent';
|
||||
|
||||
import { ServerContext } from '@/state/server';
|
||||
@@ -47,7 +46,7 @@ const EditUserContainer = () => {
|
||||
onClick={() => navigate(`/server/${serverId}/users`)}
|
||||
className='flex items-center gap-2'
|
||||
>
|
||||
<HugeIconsArrowLeft className='w-4 h-4' fill='currentColor' />
|
||||
<ChevronLeft width={22} height={22} fill='currentColor' />
|
||||
Back to Users
|
||||
</ActionButton>
|
||||
</MainPageHeader>
|
||||
@@ -68,14 +67,14 @@ const EditUserContainer = () => {
|
||||
onClick={() => navigate(`/server/${serverId}/users`)}
|
||||
className='flex items-center gap-2'
|
||||
>
|
||||
<HugeIconsArrowLeft className='w-4 h-4' fill='currentColor' />
|
||||
<ChevronLeft width={22} height={22} className='w-4 h-4' fill='currentColor' />
|
||||
Back to Users
|
||||
</ActionButton>
|
||||
</MainPageHeader>
|
||||
<div className='flex flex-col items-center justify-center py-12 px-4'>
|
||||
<div className='text-center'>
|
||||
<div className='w-16 h-16 mx-auto mb-4 rounded-full bg-[#ffffff11] flex items-center justify-center'>
|
||||
<HugeIconsUser className='w-8 h-8 text-zinc-400' fill='currentColor' />
|
||||
<Person width={22} height={22} className='w-8 h-8 text-zinc-400' fill='currentColor' />
|
||||
</div>
|
||||
<h3 className='text-lg font-medium text-zinc-200 mb-2'>User not found</h3>
|
||||
<p className='text-sm text-zinc-400 max-w-sm'>
|
||||
@@ -96,7 +95,7 @@ const EditUserContainer = () => {
|
||||
className='flex items-center gap-2'
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
<HugeIconsArrowLeft className='w-4 h-4' fill='currentColor' />
|
||||
<ChevronLeft width={22} height={22} className='w-4 h-4' fill='currentColor' />
|
||||
Back to Users
|
||||
</ActionButton>
|
||||
</MainPageHeader>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { TrashBin } from '@gravity-ui/icons';
|
||||
import { Actions, useStoreActions } from 'easy-peasy';
|
||||
import { useState } from 'react';
|
||||
|
||||
import ActionButton from '@/components/elements/ActionButton';
|
||||
import ConfirmationModal from '@/components/elements/ConfirmationModal';
|
||||
import HugeIconsTrash from '@/components/elements/hugeicons/Trash';
|
||||
|
||||
import { httpErrorToHuman } from '@/api/http';
|
||||
import deleteSubuser from '@/api/server/users/deleteSubuser';
|
||||
@@ -55,7 +55,7 @@ const RemoveSubuserButton = ({ subuser }: { subuser: Subuser }) => {
|
||||
onClick={() => setShowConfirmation(true)}
|
||||
aria-label='Delete subuser'
|
||||
>
|
||||
<HugeIconsTrash fill='currentColor' className='w-4 h-4' />
|
||||
<TrashBin width={22} height={22} fill='currentColor' className='w-4 h-4' />
|
||||
Delete
|
||||
</ActionButton>
|
||||
</>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { AntennaSignal, Calendar, Copy, Database, FolderOpen, Gear, Person, Server, Shield } from '@gravity-ui/icons';
|
||||
import { Actions, useStoreActions, useStoreState } from 'easy-peasy';
|
||||
import { Form, Formik } from 'formik';
|
||||
import { useEffect, useState } from 'react';
|
||||
@@ -7,15 +8,6 @@ import FlashMessageRender from '@/components/FlashMessageRender';
|
||||
import ActionButton from '@/components/elements/ActionButton';
|
||||
import Can from '@/components/elements/Can';
|
||||
import Field from '@/components/elements/Field';
|
||||
import HugeIconsCalendar from '@/components/elements/hugeicons/Calendar';
|
||||
import HugeIconsCopy from '@/components/elements/hugeicons/Copy';
|
||||
import HugeIconsDatabase from '@/components/elements/hugeicons/Database';
|
||||
import HugeIconsFile from '@/components/elements/hugeicons/File';
|
||||
import HugeIconsNetworkAntenna from '@/components/elements/hugeicons/NetworkAntenna';
|
||||
import HugeIconsServer from '@/components/elements/hugeicons/Server';
|
||||
import HugeIconsSettings from '@/components/elements/hugeicons/Settings';
|
||||
import HugeIconsShield from '@/components/elements/hugeicons/Shield';
|
||||
import HugeIconsUser from '@/components/elements/hugeicons/User';
|
||||
import PermissionRow from '@/components/server/users/PermissionRow';
|
||||
|
||||
import createOrUpdateSubuser from '@/api/server/users/createOrUpdateSubuser';
|
||||
@@ -94,23 +86,23 @@ const UserFormComponent = ({ subuser, onSuccess, onCancel, flashKey, isSubmittin
|
||||
const getPermissionIcon = (key: string) => {
|
||||
switch (key) {
|
||||
case 'control':
|
||||
return HugeIconsServer;
|
||||
return Server;
|
||||
case 'user':
|
||||
return HugeIconsUser;
|
||||
return Person;
|
||||
case 'file':
|
||||
return HugeIconsFile;
|
||||
return FolderOpen;
|
||||
case 'backup':
|
||||
return HugeIconsCopy;
|
||||
return Copy;
|
||||
case 'allocation':
|
||||
return HugeIconsNetworkAntenna;
|
||||
return AntennaSignal;
|
||||
case 'startup':
|
||||
return HugeIconsSettings;
|
||||
return Gear;
|
||||
case 'database':
|
||||
return HugeIconsDatabase;
|
||||
return Database;
|
||||
case 'schedule':
|
||||
return HugeIconsCalendar;
|
||||
return Calendar;
|
||||
default:
|
||||
return HugeIconsShield;
|
||||
return Shield;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -141,7 +133,12 @@ const UserFormComponent = ({ subuser, onSuccess, onCancel, flashKey, isSubmittin
|
||||
<div className='bg-gradient-to-b from-[#ffffff08] to-[#ffffff05] border border-[#ffffff12] rounded-xl p-6'>
|
||||
<div className='flex items-center gap-3 mb-6'>
|
||||
<div className='w-10 h-10 rounded-lg bg-brand/20 flex items-center justify-center'>
|
||||
<HugeIconsUser fill='currentColor' className='w-5 h-5 text-brand' />
|
||||
<Person
|
||||
width={22}
|
||||
height={22}
|
||||
fill='currentColor'
|
||||
className='w-5 h-5 text-brand'
|
||||
/>
|
||||
</div>
|
||||
<h3 className='text-xl font-semibold text-zinc-100'>User Information</h3>
|
||||
</div>
|
||||
@@ -160,7 +157,12 @@ const UserFormComponent = ({ subuser, onSuccess, onCancel, flashKey, isSubmittin
|
||||
<div className='flex items-center justify-between mb-6'>
|
||||
<div className='flex items-center gap-3'>
|
||||
<div className='w-10 h-10 rounded-lg bg-brand/20 flex items-center justify-center'>
|
||||
<HugeIconsSettings fill='currentColor' className='w-5 h-5 text-brand' />
|
||||
<Gear
|
||||
width={22}
|
||||
height={22}
|
||||
fill='currentColor'
|
||||
className='w-5 h-5 text-brand'
|
||||
/>
|
||||
</div>
|
||||
<h3 className='text-xl font-semibold text-zinc-100'>Detailed Permissions</h3>
|
||||
</div>
|
||||
@@ -190,7 +192,12 @@ const UserFormComponent = ({ subuser, onSuccess, onCancel, flashKey, isSubmittin
|
||||
{!isRootAdmin && loggedInPermissions[0] !== '*' && (
|
||||
<div className='mb-6 p-4 bg-brand/10 border border-brand/20 rounded-lg'>
|
||||
<div className='flex items-center gap-3 mb-2'>
|
||||
<HugeIconsShield fill='currentColor' className='w-5 h-5 text-brand' />
|
||||
<Shield
|
||||
width={22}
|
||||
height={22}
|
||||
fill='currentColor'
|
||||
className='w-5 h-5 text-brand'
|
||||
/>
|
||||
<span className='text-sm font-semibold text-brand'>Permission Restriction</span>
|
||||
</div>
|
||||
<p className='text-sm text-zinc-300 leading-relaxed'>
|
||||
@@ -210,8 +217,10 @@ const UserFormComponent = ({ subuser, onSuccess, onCancel, flashKey, isSubmittin
|
||||
const Icon = getPermissionIcon(key);
|
||||
return (
|
||||
<Icon
|
||||
width={22}
|
||||
height={22}
|
||||
fill='currentColor'
|
||||
className='w-4 h-4 text-brand flex-shrink-0 mt-0.5'
|
||||
className=' text-brand flex-shrink-0 mt-0.5'
|
||||
/>
|
||||
);
|
||||
})()}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Pencil } from '@gravity-ui/icons';
|
||||
import { useStoreState } from 'easy-peasy';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import ActionButton from '@/components/elements/ActionButton';
|
||||
import Can from '@/components/elements/Can';
|
||||
import HugeIconsPencil from '@/components/elements/hugeicons/Pencil';
|
||||
import { PageListItem } from '@/components/elements/pages/PageList';
|
||||
import RemoveSubuserButton from '@/components/server/users/RemoveSubuserButton';
|
||||
|
||||
@@ -53,7 +53,7 @@ const UserRow = ({ subuser }: Props) => {
|
||||
onClick={handleEditClick}
|
||||
aria-label='Edit subuser'
|
||||
>
|
||||
<HugeIconsPencil className='w-4 h-4' fill='currentColor' />
|
||||
<Pencil width={22} height={22} fill='currentColor' />
|
||||
Edit
|
||||
</ActionButton>
|
||||
</Can>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Person, Plus } from '@gravity-ui/icons';
|
||||
import { Actions, useStoreActions, useStoreState } from 'easy-peasy';
|
||||
import { For } from 'million/react';
|
||||
import { useEffect, useState } from 'react';
|
||||
@@ -8,8 +9,6 @@ import ActionButton from '@/components/elements/ActionButton';
|
||||
import Can from '@/components/elements/Can';
|
||||
import { MainPageHeader } from '@/components/elements/MainPageHeader';
|
||||
import ServerContentBlock from '@/components/elements/ServerContentBlock';
|
||||
import HugeIconsPlus from '@/components/elements/hugeicons/Plus';
|
||||
import HugeIconsUser from '@/components/elements/hugeicons/User';
|
||||
import { PageListContainer } from '@/components/elements/pages/PageList';
|
||||
import UserRow from '@/components/server/users/UserRow';
|
||||
|
||||
@@ -68,7 +67,7 @@ const UsersContainer = () => {
|
||||
onClick={() => navigate(`/server/${serverId}/users/new`)}
|
||||
className='flex items-center gap-2'
|
||||
>
|
||||
<HugeIconsPlus className='w-4 h-4' fill='currentColor' />
|
||||
<Plus width={22} height={22} className='w-4 h-4' fill='currentColor' />
|
||||
New User
|
||||
</ActionButton>
|
||||
</Can>
|
||||
@@ -102,7 +101,7 @@ const UsersContainer = () => {
|
||||
onClick={() => navigate(`/server/${serverId}/users/new`)}
|
||||
className='flex items-center gap-2'
|
||||
>
|
||||
<HugeIconsPlus className='w-4 h-4' fill='currentColor' />
|
||||
<Plus width={22} height={22} className='w-4 h-4' fill='currentColor' />
|
||||
New User
|
||||
</ActionButton>
|
||||
</Can>
|
||||
@@ -118,7 +117,7 @@ const UsersContainer = () => {
|
||||
<div className='flex flex-col items-center justify-center min-h-[60vh] py-12 px-4'>
|
||||
<div className='text-center'>
|
||||
<div className='w-16 h-16 mx-auto mb-4 rounded-full bg-[#ffffff11] flex items-center justify-center'>
|
||||
<HugeIconsUser className='w-8 h-8 text-zinc-400' fill='currentColor' />
|
||||
<Person width={22} height={22} className='w-8 h-8 text-zinc-400' fill='currentColor' />
|
||||
</div>
|
||||
<h3 className='text-lg font-medium text-zinc-200 mb-2'>No users found</h3>
|
||||
<p className='text-sm text-zinc-400 max-w-sm'>
|
||||
|
||||
Reference in New Issue
Block a user