mirror of
https://github.com/pyrohost/pyrodactyl.git
synced 2026-04-05 19:51:59 +02:00
fix: linting + ssh & api icons
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,7 +4,6 @@
|
||||
!.env.example
|
||||
.env*
|
||||
.vagrant/
|
||||
.vscode/
|
||||
storage/framework/*
|
||||
/.idea
|
||||
/nbproject
|
||||
|
||||
3
.vscode/extensions.json
vendored
Normal file
3
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["esbenp.prettier-vscode", "dbaeumer.vscode-eslint"]
|
||||
}
|
||||
8
.vscode/settings.json
vendored
Normal file
8
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"prettier.endOfLine": "lf",
|
||||
"editor.formatOnSave": true,
|
||||
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import requestPasswordResetEmail from '@/api/auth/requestPasswordResetEmail';
|
||||
import { httpErrorToHuman } from '@/api/http';
|
||||
|
||||
import useFlash from '@/plugins/useFlash';
|
||||
|
||||
import Logo from '../elements/PyroLogo';
|
||||
|
||||
interface Values {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { faTrashAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { format } from 'date-fns';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
@@ -15,8 +16,6 @@ import getApiKeys, { ApiKey } from '@/api/account/getApiKeys';
|
||||
|
||||
import { useFlashKey } from '@/plugins/useFlash';
|
||||
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
|
||||
export default () => {
|
||||
const [deleteIdentifier, setDeleteIdentifier] = useState('');
|
||||
const [keys, setKeys] = useState<ApiKey[]>([]);
|
||||
@@ -46,12 +45,12 @@ export default () => {
|
||||
return (
|
||||
<PageContentBlock title={'Account API'}>
|
||||
{/* Flash messages will now appear at the top of the page */}
|
||||
<FlashMessageRender byKey="account" />
|
||||
<div className="md:flex flex-nowrap my-10 space-x-8">
|
||||
<ContentBox title={'Create API Key'} className="flex-none w-full md:w-1/2">
|
||||
<FlashMessageRender byKey='account' />
|
||||
<div className='md:flex flex-nowrap my-10 space-x-8'>
|
||||
<ContentBox title={'Create API Key'} className='flex-none w-full md:w-1/2'>
|
||||
<CreateApiKeyForm onKeyCreated={(key) => setKeys((s) => [...s!, key])} />
|
||||
</ContentBox>
|
||||
<ContentBox title={'API Keys'} className="flex-1 overflow-hidden mt-8 md:mt-0">
|
||||
<ContentBox title={'API Keys'} className='flex-1 overflow-hidden mt-8 md:mt-0'>
|
||||
<SpinnerOverlay visible={loading} />
|
||||
<Dialog.Confirm
|
||||
title={'Delete API Key'}
|
||||
@@ -63,29 +62,30 @@ export default () => {
|
||||
All requests using the <Code>{deleteIdentifier}</Code> key will be invalidated.
|
||||
</Dialog.Confirm>
|
||||
{keys.length === 0 ? (
|
||||
<p className="text-center text-sm text-gray-500">
|
||||
<p className='text-center text-sm text-gray-500'>
|
||||
{loading ? 'Loading...' : 'No API keys exist for this account.'}
|
||||
</p>
|
||||
) : (
|
||||
keys.map((key) => (
|
||||
<div key={key.identifier} className="flex flex-col mb-6 space-y-4">
|
||||
<div className="flex items-center justify-between space-x-4 border border-gray-300 rounded-lg p-4 transition duration-200">
|
||||
<div className="flex-1">
|
||||
<p className="text-sm font-medium">{key.description}</p>
|
||||
<p className="text-xs text-gray-500 uppercase">
|
||||
Last used: {key.lastUsedAt ? format(key.lastUsedAt, 'MMM d, yyyy HH:mm') : 'Never'}
|
||||
<div key={key.identifier} className='flex flex-col mb-6 space-y-4'>
|
||||
<div className='flex items-center justify-between space-x-4 border border-gray-300 rounded-lg p-4 transition duration-200'>
|
||||
<div className='flex-1'>
|
||||
<p className='text-sm font-medium'>{key.description}</p>
|
||||
<p className='text-xs text-gray-500 uppercase'>
|
||||
Last used:{' '}
|
||||
{key.lastUsedAt ? format(key.lastUsedAt, 'MMM d, yyyy HH:mm') : 'Never'}
|
||||
</p>
|
||||
</div>
|
||||
<p className="text-sm text-gray-600 hidden md:block">
|
||||
<code className="font-mono py-1 px-2 bg-gray-800 rounded text-white">
|
||||
<p className='text-sm text-gray-600 hidden md:block'>
|
||||
<code className='font-mono py-1 px-2 bg-gray-800 rounded text-white'>
|
||||
{key.identifier}
|
||||
</code>
|
||||
</p>
|
||||
<button
|
||||
className="p-2 text-red-500 hover:text-red-700"
|
||||
className='p-2 text-red-500 hover:text-red-700'
|
||||
onClick={() => setDeleteIdentifier(key.identifier)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faTrashAlt} size="lg" />
|
||||
<FontAwesomeIcon icon={faTrashAlt} size='lg' />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { faClone } from '@fortawesome/free-solid-svg-icons';
|
||||
import { useContext } from 'react';
|
||||
import ModalContext from '@/context/ModalContext';
|
||||
import { faClone } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import FlashMessageRender from '@/components/FlashMessageRender';
|
||||
import Button from '@/components/elements/Button';
|
||||
import CopyOnClick from '@/components/elements/CopyOnClick';
|
||||
import FlashMessageRender from '@/components/FlashMessageRender';
|
||||
|
||||
import asModal from '@/hoc/asModal';
|
||||
|
||||
interface Props {
|
||||
@@ -16,39 +17,40 @@ const ApiKeyModal = ({ apiKey }: Props) => {
|
||||
const { dismiss } = useContext(ModalContext);
|
||||
|
||||
return (
|
||||
<div className="p-6 space-y-6 max-w-lg mx-auto rounded-lg shadow-lg ">
|
||||
<div className='p-6 space-y-6 max-w-lg mx-auto rounded-lg shadow-lg '>
|
||||
{/* Flash message section */}
|
||||
<FlashMessageRender byKey="account" />
|
||||
<FlashMessageRender byKey='account' />
|
||||
|
||||
{/* Modal Header */}
|
||||
<p className="text-sm text-white-600 mt-2 ">
|
||||
The API key you have requested is shown below. Please store it in a safe place, as it will not be shown again.
|
||||
<p className='text-sm text-white-600 mt-2 '>
|
||||
The API key you have requested is shown below. Please store it in a safe place, as it will not be shown
|
||||
again.
|
||||
</p>
|
||||
|
||||
{/* API Key Display Section */}
|
||||
<div className="relative mt-6">
|
||||
<pre className="bg-gray-900 text-white p-4 rounded-lg font-mono overflow-x-auto">
|
||||
<div className='relative mt-6'>
|
||||
<pre className='bg-gray-900 text-white p-4 rounded-lg font-mono overflow-x-auto'>
|
||||
<CopyOnClick text={apiKey}>
|
||||
<code className="text-sm break-words">{apiKey}</code>
|
||||
<code className='text-sm break-words'>{apiKey}</code>
|
||||
</CopyOnClick>
|
||||
|
||||
{/* Copy button with icon */}
|
||||
<div className="absolute top-2 right-2">
|
||||
<div className='absolute top-2 right-2'>
|
||||
<FontAwesomeIcon
|
||||
icon={faClone}
|
||||
size="lg"
|
||||
className="text-gray-400 hover:text-gray-600 cursor-pointer"
|
||||
size='lg'
|
||||
className='text-gray-400 hover:text-gray-600 cursor-pointer'
|
||||
/>
|
||||
</div>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
{/* Action Buttons */}
|
||||
<div className="flex justify-end space-x-4">
|
||||
<div className='flex justify-end space-x-4'>
|
||||
<Button
|
||||
type="button"
|
||||
type='button'
|
||||
onClick={() => dismiss()}
|
||||
className="bg-red-600 text-white hover:bg-red-700 px-6 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-gray-500"
|
||||
className='bg-red-600 text-white hover:bg-red-700 px-6 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-gray-500'
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
@@ -61,6 +63,6 @@ ApiKeyModal.displayName = 'ApiKeyModal';
|
||||
|
||||
export default asModal<Props>({
|
||||
title: 'Your API Key',
|
||||
closeOnEscape: true, // Allows closing the modal by pressing Escape
|
||||
closeOnBackground: true, // Allows closing by clicking outside the modal
|
||||
closeOnEscape: true, // Allows closing the modal by pressing Escape
|
||||
closeOnBackground: true, // Allows closing by clicking outside the modal
|
||||
})(ApiKeyModal);
|
||||
|
||||
@@ -3,12 +3,12 @@ import { Field, Form, Formik, FormikHelpers } from 'formik';
|
||||
import { useState } from 'react';
|
||||
import { object, string } from 'yup';
|
||||
|
||||
import FlashMessageRender from '@/components/FlashMessageRender';
|
||||
import ApiKeyModal from '@/components/dashboard/ApiKeyModal';
|
||||
import Button from '@/components/elements/Button';
|
||||
import FormikFieldWrapper from '@/components/elements/FormikFieldWrapper';
|
||||
import Input from '@/components/elements/Input';
|
||||
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
|
||||
import FlashMessageRender from '@/components/FlashMessageRender';
|
||||
|
||||
import createApiKey from '@/api/account/createApiKey';
|
||||
import { ApiKey } from '@/api/account/getApiKeys';
|
||||
@@ -45,7 +45,7 @@ export default ({ onKeyCreated }: { onKeyCreated: (key: ApiKey) => void }) => {
|
||||
return (
|
||||
<>
|
||||
{/* Flash Messages */}
|
||||
<FlashMessageRender byKey="account" />
|
||||
<FlashMessageRender byKey='account' />
|
||||
|
||||
{/* Modal for API Key */}
|
||||
<ApiKeyModal visible={apiKey.length > 0} onModalDismissed={() => setApiKey('')} apiKey={apiKey} />
|
||||
@@ -60,33 +60,33 @@ export default ({ onKeyCreated }: { onKeyCreated: (key: ApiKey) => void }) => {
|
||||
})}
|
||||
>
|
||||
{({ isSubmitting }) => (
|
||||
<Form className="space-y-6">
|
||||
<Form className='space-y-6'>
|
||||
{/* Show spinner overlay when submitting */}
|
||||
<SpinnerOverlay visible={isSubmitting} />
|
||||
|
||||
{/* Description Field */}
|
||||
<FormikFieldWrapper
|
||||
label="Description"
|
||||
name="description"
|
||||
description="A description of this API key."
|
||||
label='Description'
|
||||
name='description'
|
||||
description='A description of this API key.'
|
||||
>
|
||||
<Field name="description" as={Input} />
|
||||
<Field name='description' as={Input} />
|
||||
</FormikFieldWrapper>
|
||||
|
||||
{/* Allowed IPs Field */}
|
||||
<FormikFieldWrapper
|
||||
label="Allowed IPs"
|
||||
name="allowedIps"
|
||||
description="Leave blank to allow any IP address to use this API key, otherwise provide each IP address on a new line."
|
||||
label='Allowed IPs'
|
||||
name='allowedIps'
|
||||
description='Leave blank to allow any IP address to use this API key, otherwise provide each IP address on a new line.'
|
||||
>
|
||||
<Field name="allowedIps" as={Input} />
|
||||
<Field name='allowedIps' as={Input} />
|
||||
</FormikFieldWrapper>
|
||||
|
||||
{/* Submit Button below form fields */}
|
||||
<div className="flex justify-end mt-6">
|
||||
<div className='flex justify-end mt-6'>
|
||||
<Button
|
||||
type="submit"
|
||||
className="bg-red-600 text-white hover:bg-red-700 px-6 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-gray-500"
|
||||
type='submit'
|
||||
className='bg-red-600 text-white hover:bg-red-700 px-6 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-gray-500'
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
{isSubmitting ? 'Creating...' : 'Create API Key'}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { faTrashAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { format } from 'date-fns';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import FlashMessageRender from '@/components/FlashMessageRender';
|
||||
import CreateSSHKeyForm from '@/components/dashboard/ssh/CreateSSHKeyForm';
|
||||
import DeleteSSHKeyButton from '@/components/dashboard/ssh/DeleteSSHKeyButton';
|
||||
import Code from '@/components/elements/Code';
|
||||
import ContentBox from '@/components/elements/ContentBox';
|
||||
import PageContentBlock from '@/components/elements/PageContentBlock';
|
||||
@@ -13,8 +15,6 @@ import { Dialog } from '@/components/elements/dialog';
|
||||
import { useSSHKeys } from '@/api/account/ssh-keys';
|
||||
|
||||
import { useFlashKey } from '@/plugins/useFlash';
|
||||
import DeleteSSHKeyButton from '@/components/dashboard/ssh/DeleteSSHKeyButton';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
|
||||
export default () => {
|
||||
const [deleteIdentifier, setDeleteIdentifier] = useState('');
|
||||
@@ -36,14 +36,14 @@ export default () => {
|
||||
return (
|
||||
<PageContentBlock title={'SSH Keys'}>
|
||||
<FlashMessageRender byKey={'account'} />
|
||||
<div className="md:flex flex-nowrap my-10 space-x-8">
|
||||
<div className='md:flex flex-nowrap my-10 space-x-8'>
|
||||
{/* Create SSH Key Section */}
|
||||
<ContentBox title={'Add SSH Key'} className="flex-none w-full md:w-1/2">
|
||||
<ContentBox title={'Add SSH Key'} className='flex-none w-full md:w-1/2'>
|
||||
<CreateSSHKeyForm />
|
||||
</ContentBox>
|
||||
|
||||
{/* SSH Keys List Section */}
|
||||
<ContentBox title={'SSH Keys'} className="flex-1 overflow-hidden mt-8 md:mt-0">
|
||||
<ContentBox title={'SSH Keys'} className='flex-1 overflow-hidden mt-8 md:mt-0'>
|
||||
<SpinnerOverlay visible={!data && isValidating} />
|
||||
<Dialog.Confirm
|
||||
title={'Delete SSH Key'}
|
||||
@@ -55,29 +55,29 @@ export default () => {
|
||||
Deleting this key will revoke access for any system using it.
|
||||
</Dialog.Confirm>
|
||||
{!data || data.length === 0 ? (
|
||||
<p className="text-center text-sm text-gray-500">
|
||||
<p className='text-center text-sm text-gray-500'>
|
||||
{!data ? 'Loading...' : 'No SSH keys exist for this account.'}
|
||||
</p>
|
||||
) : (
|
||||
data.map((key) => (
|
||||
<div key={key.fingerprint} className="flex flex-col mb-6 space-y-4">
|
||||
<div className="flex items-center justify-between space-x-4 border border-gray-300 rounded-lg p-4 transition duration-200">
|
||||
<div className="flex-1">
|
||||
<p className="text-sm font-medium">{key.name}</p>
|
||||
<p className="text-xs text-gray-500 uppercase">
|
||||
<div key={key.fingerprint} className='flex flex-col mb-6 space-y-4'>
|
||||
<div className='flex items-center justify-between space-x-4 border border-gray-300 rounded-lg p-4 transition duration-200'>
|
||||
<div className='flex-1'>
|
||||
<p className='text-sm font-medium'>{key.name}</p>
|
||||
<p className='text-xs text-gray-500 uppercase'>
|
||||
Added on: {format(key.createdAt, 'MMM d, yyyy HH:mm')}
|
||||
</p>
|
||||
</div>
|
||||
<p className="text-sm text-gray-600 hidden md:block">
|
||||
<code className="font-mono py-1 px-2 bg-gray-800 rounded text-white">
|
||||
<p className='text-sm text-gray-600 hidden md:block'>
|
||||
<code className='font-mono py-1 px-2 bg-gray-800 rounded text-white'>
|
||||
SHA256: {key.fingerprint}
|
||||
</code>
|
||||
</p>
|
||||
<button
|
||||
className="p-2 text-red-500 hover:text-red-700"
|
||||
className='p-2 text-red-500 hover:text-red-700'
|
||||
onClick={() => setDeleteIdentifier(key.fingerprint)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faTrashAlt} size="lg" />
|
||||
<FontAwesomeIcon icon={faTrashAlt} size='lg' />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,11 +3,11 @@ import { Field, Form, Formik, FormikHelpers } from 'formik';
|
||||
import { useState } from 'react';
|
||||
import { object, string } from 'yup';
|
||||
|
||||
import FlashMessageRender from '@/components/FlashMessageRender';
|
||||
import Button from '@/components/elements/Button';
|
||||
import FormikFieldWrapper from '@/components/elements/FormikFieldWrapper';
|
||||
import Input from '@/components/elements/Input';
|
||||
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
|
||||
import FlashMessageRender from '@/components/FlashMessageRender';
|
||||
|
||||
import { createSSHKey } from '@/api/account/ssh-keys';
|
||||
import { useSSHKeys } from '@/api/account/ssh-keys';
|
||||
@@ -32,7 +32,7 @@ export default () => {
|
||||
resetForm();
|
||||
setSubmitting(false);
|
||||
setSshKey(`${key.name}`);
|
||||
mutate((data) => (data || []).concat(key)); // Update the list of SSH keys after creation
|
||||
mutate((data) => (data || []).concat(key)); // Update the list of SSH keys after creation
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
@@ -44,7 +44,7 @@ export default () => {
|
||||
return (
|
||||
<>
|
||||
{/* Flash Messages */}
|
||||
<FlashMessageRender byKey="account" />
|
||||
<FlashMessageRender byKey='account' />
|
||||
|
||||
{/* Modal for SSH Key */}
|
||||
{/* Add your modal logic here to display the SSH key details after creation */}
|
||||
@@ -59,33 +59,33 @@ export default () => {
|
||||
})}
|
||||
>
|
||||
{({ isSubmitting }) => (
|
||||
<Form className="space-y-6">
|
||||
<Form className='space-y-6'>
|
||||
{/* Show spinner overlay when submitting */}
|
||||
<SpinnerOverlay visible={isSubmitting} />
|
||||
|
||||
{/* SSH Key Name Field */}
|
||||
<FormikFieldWrapper
|
||||
label="SSH Key Name"
|
||||
name="name"
|
||||
description="A name to identify this SSH key."
|
||||
label='SSH Key Name'
|
||||
name='name'
|
||||
description='A name to identify this SSH key.'
|
||||
>
|
||||
<Field name="name" as={Input} />
|
||||
<Field name='name' as={Input} />
|
||||
</FormikFieldWrapper>
|
||||
|
||||
{/* Public Key Field */}
|
||||
<FormikFieldWrapper
|
||||
label="Public Key"
|
||||
name="publicKey"
|
||||
description="Enter your public SSH key."
|
||||
label='Public Key'
|
||||
name='publicKey'
|
||||
description='Enter your public SSH key.'
|
||||
>
|
||||
<Field name="publicKey" as={Input} />
|
||||
<Field name='publicKey' as={Input} />
|
||||
</FormikFieldWrapper>
|
||||
|
||||
{/* Submit Button below form fields */}
|
||||
<div className="flex justify-end mt-6">
|
||||
<div className='flex justify-end mt-6'>
|
||||
<Button
|
||||
type="submit"
|
||||
className="bg-red-600 text-white hover:bg-red-700 px-6 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-gray-500"
|
||||
type='submit'
|
||||
className='bg-red-600 text-white hover:bg-red-700 px-6 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-gray-500'
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
{isSubmitting ? 'Creating...' : 'Create SSH Key'}
|
||||
|
||||
@@ -2,14 +2,12 @@ import { useState } from 'react';
|
||||
|
||||
import Code from '@/components/elements/Code';
|
||||
import { Dialog } from '@/components/elements/dialog';
|
||||
import HugeIconsTrash from '@/components/elements/hugeicons/Trash';
|
||||
|
||||
import { deleteSSHKey, useSSHKeys } from '@/api/account/ssh-keys';
|
||||
|
||||
import { useFlashKey } from '@/plugins/useFlash';
|
||||
|
||||
import HugeIconsTrash from '@/components/elements/hugeicons/Trash';
|
||||
|
||||
|
||||
export default ({ name, fingerprint }: { name: string; fingerprint: string }) => {
|
||||
const { clearAndAddHttpError } = useFlashKey('account');
|
||||
const [visible, setVisible] = useState(false);
|
||||
@@ -38,9 +36,7 @@ export default ({ name, fingerprint }: { name: string; fingerprint: string }) =>
|
||||
>
|
||||
Removing the <Code>{name}</Code> SSH key will invalidate its usage across the Panel.
|
||||
</Dialog.Confirm>
|
||||
<button className={`ml-4 p-2 text-sm`} onClick={() => setVisible(true)}>
|
||||
|
||||
</button>
|
||||
<button className={`ml-4 p-2 text-sm`} onClick={() => setVisible(true)}></button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,12 +2,36 @@ import { HugeIconProps } from './props';
|
||||
|
||||
const HugeIconsApi = (props: HugeIconProps) => {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" color="#ffffff" fill="none">
|
||||
<path d="M2.5 12C2.5 7.52166 2.5 5.28249 3.89124 3.89124C5.28249 2.5 7.52166 2.5 12 2.5C16.4783 2.5 18.7175 2.5 20.1088 3.89124C21.5 5.28249 21.5 7.52166 21.5 12C21.5 16.4783 21.5 18.7175 20.1088 20.1088C18.7175 21.5 16.4783 21.5 12 21.5C7.52166 21.5 5.28249 21.5 3.89124 20.1088C2.5 18.7175 2.5 16.4783 2.5 12Z" stroke="currentColor" stroke-width="1.5" />
|
||||
<path d="M6 13.5L7.5 9L9.375 13.5M6 13.5L5.5 15M6 13.5H9.375M9.375 13.5L10 15" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M12.5 12V9.7C12.5 9.51387 12.5 9.42081 12.5245 9.34549C12.5739 9.19327 12.6933 9.07393 12.8455 9.02447C12.9208 9 13.0139 9 13.2 9H14.5C15.3284 9 16 9.67157 16 10.5C16 11.3284 15.3284 12 14.5 12H12.5ZM12.5 12V15" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M18.5 9V15" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
<svg
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
viewBox='0 0 24 24'
|
||||
width='24'
|
||||
height='24'
|
||||
fill='none'
|
||||
color={props.fill}
|
||||
className={'h-6 w-6' + (props.className ? ` ${props.className}` : '')}
|
||||
>
|
||||
<path
|
||||
d='M2.5 12C2.5 7.52166 2.5 5.28249 3.89124 3.89124C5.28249 2.5 7.52166 2.5 12 2.5C16.4783 2.5 18.7175 2.5 20.1088 3.89124C21.5 5.28249 21.5 7.52166 21.5 12C21.5 16.4783 21.5 18.7175 20.1088 20.1088C18.7175 21.5 16.4783 21.5 12 21.5C7.52166 21.5 5.28249 21.5 3.89124 20.1088C2.5 18.7175 2.5 16.4783 2.5 12Z'
|
||||
stroke='currentColor'
|
||||
strokeWidth='1.5'
|
||||
/>
|
||||
<path
|
||||
d='M6 13.5L7.5 9L9.375 13.5M6 13.5L5.5 15M6 13.5H9.375M9.375 13.5L10 15'
|
||||
stroke='currentColor'
|
||||
strokeWidth='1.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<path
|
||||
d='M12.5 12V9.7C12.5 9.51387 12.5 9.42081 12.5245 9.34549C12.5739 9.19327 12.6933 9.07393 12.8455 9.02447C12.9208 9 13.0139 9 13.2 9H14.5C15.3284 9 16 9.67157 16 10.5C16 11.3284 15.3284 12 14.5 12H12.5ZM12.5 12V15'
|
||||
stroke='currentColor'
|
||||
strokeWidth='1.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<path d='M18.5 9V15' stroke='currentColor' strokeWidth='1.5' strokeLinecap='round' strokeLinejoin='round' />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -2,10 +2,30 @@ import { HugeIconProps } from './props';
|
||||
|
||||
const HugeIconsSsh = (props: HugeIconProps) => {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" color="#ffffff" fill="none">
|
||||
<path d="M15.5 14.5C18.8137 14.5 21.5 11.8137 21.5 8.5C21.5 5.18629 18.8137 2.5 15.5 2.5C12.1863 2.5 9.5 5.18629 9.5 8.5C9.5 9.38041 9.68962 10.2165 10.0303 10.9697L2.5 18.5V21.5H5.5V19.5H7.5V17.5H9.5L13.0303 13.9697C13.7835 14.3104 14.6196 14.5 15.5 14.5Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M17.5 6.5L16.5 7.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
<svg
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
viewBox='0 0 24 24'
|
||||
width='24'
|
||||
height='24'
|
||||
fill='none'
|
||||
color={props.fill}
|
||||
className={'h-6 w-6' + (props.className ? ` ${props.className}` : '')}
|
||||
>
|
||||
<path
|
||||
d='M15.5 14.5C18.8137 14.5 21.5 11.8137 21.5 8.5C21.5 5.18629 18.8137 2.5 15.5 2.5C12.1863 2.5 9.5 5.18629 9.5 8.5C9.5 9.38041 9.68962 10.2165 10.0303 10.9697L2.5 18.5V21.5H5.5V19.5H7.5V17.5H9.5L13.0303 13.9697C13.7835 14.3104 14.6196 14.5 15.5 14.5Z'
|
||||
stroke='currentColor'
|
||||
strokeWidth='1.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<path
|
||||
d='M17.5 6.5L16.5 7.5'
|
||||
stroke='currentColor'
|
||||
strokeWidth='1.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { faTrashAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { Actions, useStoreActions } from 'easy-peasy';
|
||||
import { useState } from 'react';
|
||||
|
||||
@@ -9,8 +11,6 @@ import deleteSubuser from '@/api/server/users/deleteSubuser';
|
||||
import { ApplicationStore } from '@/state';
|
||||
import { ServerContext } from '@/state/server';
|
||||
import { Subuser } from '@/state/server/subusers';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faTrashAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
export default ({ subuser }: { subuser: Subuser }) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
@@ -16,24 +16,21 @@ import MainSidebar from '@/components/elements/MainSidebar';
|
||||
import MainWrapper from '@/components/elements/MainWrapper';
|
||||
import Logo from '@/components/elements/PyroLogo';
|
||||
import { NotFound } from '@/components/elements/ScreenBlock';
|
||||
import HugeIconsApi from '@/components/elements/hugeicons/Api';
|
||||
import HugeIconsDashboardSettings from '@/components/elements/hugeicons/DashboardSettings';
|
||||
import HugeIconsHome from '@/components/elements/hugeicons/Home';
|
||||
import HugeIconsApi from '@/components/elements/hugeicons/Api';
|
||||
import HugeIconsSsh from '@/components/elements/hugeicons/Ssh';
|
||||
|
||||
import http from '@/api/http';
|
||||
|
||||
|
||||
|
||||
|
||||
export default () => {
|
||||
const location = useLocation();
|
||||
const rootAdmin = useStoreState((state) => state.user.data!.rootAdmin);
|
||||
|
||||
const [isSidebarVisible, setSidebarVisible] = useState(false);
|
||||
const [isSidebarVisible, setSidebarVisible] = useState(false);
|
||||
|
||||
const toggleSidebar = () => {
|
||||
setSidebarVisible(!isSidebarVisible); // Toggle sidebar visibility
|
||||
setSidebarVisible(!isSidebarVisible); // Toggle sidebar visibility
|
||||
};
|
||||
|
||||
const onTriggerLogout = () => {
|
||||
@@ -72,7 +69,6 @@ export default () => {
|
||||
return '0';
|
||||
};
|
||||
|
||||
|
||||
const top = calculateTop(location.pathname);
|
||||
|
||||
const [height, setHeight] = useState('40px');
|
||||
@@ -83,21 +79,28 @@ export default () => {
|
||||
return () => clearTimeout(timeoutId);
|
||||
}, [top]);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<Fragment key={'dashboard-router'}>
|
||||
<button
|
||||
id="sidebarToggle"
|
||||
id='sidebarToggle'
|
||||
className={`lg:hidden fixed top-4 left-4 z-50 bg-transparent p-2 rounded-md text-white ${
|
||||
isSidebarVisible ? 'left-[300px]' : 'left-4'
|
||||
}`}
|
||||
onClick={toggleSidebar}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth="1.5" stroke="currentColor" className="size-6">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
|
||||
<svg
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
fill='none'
|
||||
viewBox='0 0 24 24'
|
||||
strokeWidth='1.5'
|
||||
stroke='currentColor'
|
||||
className='size-6'
|
||||
>
|
||||
<path
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
d='M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5'
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
@@ -163,7 +166,7 @@ export default () => {
|
||||
</NavLink>
|
||||
<NavLink to={'/account/api'} end className='flex flex-row items-center' ref={NavigationApi}>
|
||||
<HugeIconsApi fill='currentColor' />
|
||||
<p>Api Keys</p>
|
||||
<p>API Keys</p>
|
||||
</NavLink>
|
||||
<NavLink to={'/account/ssh'} end className='flex flex-row items-center' ref={NavigationSSH}>
|
||||
<HugeIconsSsh fill='currentColor' />
|
||||
@@ -173,9 +176,7 @@ export default () => {
|
||||
<HugeIconsDashboardSettings fill='currentColor' />
|
||||
<p>Settings</p>
|
||||
</NavLink>
|
||||
|
||||
</ul>
|
||||
|
||||
</MainSidebar>
|
||||
|
||||
<Suspense fallback={null}>
|
||||
@@ -201,7 +202,6 @@ export default () => {
|
||||
</main>
|
||||
</MainWrapper>
|
||||
</Suspense>
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user