forked from Alex/Pterodactyl-Panel
ui(admin): rough layout on new server page
This commit is contained in:
parent
bee7c4515c
commit
cf1cc97340
@ -1,8 +1,30 @@
|
||||
import React from 'react';
|
||||
import { Egg } from '@/api/admin/egg';
|
||||
import AdminBox from '@/components/admin/AdminBox';
|
||||
import { ServerImageContainer, ServerServiceContainer, ServerVariableContainer } from '@/components/admin/servers/ServerStartupContainer';
|
||||
import BaseSettingsBox from '@/components/admin/servers/settings/BaseSettingsBox';
|
||||
import FeatureLimitsBox from '@/components/admin/servers/settings/FeatureLimitsBox';
|
||||
import ServerResourceBox from '@/components/admin/servers/settings/ServerResourceBox';
|
||||
import Button from '@/components/elements/Button';
|
||||
import Field from '@/components/elements/Field';
|
||||
import Label from '@/components/elements/Label';
|
||||
import Select from '@/components/elements/Select';
|
||||
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
|
||||
import FlashMessageRender from '@/components/FlashMessageRender';
|
||||
import { faNetworkWired } from '@fortawesome/free-solid-svg-icons';
|
||||
import { Form, Formik } from 'formik';
|
||||
import React, { useState } from 'react';
|
||||
import tw from 'twin.macro';
|
||||
import AdminContentBlock from '@/components/admin/AdminContentBlock';
|
||||
import { object } from 'yup';
|
||||
import { Values } from '@/api/admin/servers/updateServer';
|
||||
|
||||
export default () => {
|
||||
const [ egg, setEgg ] = useState<Egg | null>(null);
|
||||
|
||||
const submit = (_: Values) => {
|
||||
//
|
||||
};
|
||||
|
||||
return (
|
||||
<AdminContentBlock title={'New Server'}>
|
||||
<div css={tw`w-full flex flex-row items-center mb-8`}>
|
||||
@ -11,6 +33,108 @@ export default () => {
|
||||
<p css={tw`text-base text-neutral-400 whitespace-nowrap overflow-ellipsis overflow-hidden`}>Add a new server to the panel.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<FlashMessageRender byKey={'server:create'} css={tw`mb-4`}/>
|
||||
|
||||
<Formik
|
||||
onSubmit={submit}
|
||||
initialValues={{
|
||||
externalId: '',
|
||||
name: '',
|
||||
ownerId: 0,
|
||||
limits: {
|
||||
memory: 0,
|
||||
swap: 0,
|
||||
disk: 0,
|
||||
io: 0,
|
||||
cpu: 0,
|
||||
threads: '',
|
||||
// This value is inverted to have the switch be on when the
|
||||
// OOM Killer is enabled, rather than when disabled.
|
||||
oomDisabled: false,
|
||||
},
|
||||
featureLimits: {
|
||||
allocations: 1,
|
||||
backups: 0,
|
||||
databases: 0,
|
||||
},
|
||||
allocationId: 0,
|
||||
addAllocations: [] as number[],
|
||||
removeAllocations: [] as number[],
|
||||
}}
|
||||
validationSchema={object().shape({})}
|
||||
>
|
||||
{({ isSubmitting, isValid }) => (
|
||||
<Form>
|
||||
<div css={tw`grid grid-cols-1 md:grid-cols-2 gap-y-6 gap-x-8 mb-16`}>
|
||||
<div css={tw`grid grid-cols-1 gap-y-6`}>
|
||||
<BaseSettingsBox/>
|
||||
<FeatureLimitsBox/>
|
||||
{/* TODO: in networking box only show primary allocation and additional allocations */}
|
||||
{/* TODO: add node select */}
|
||||
<ServerServiceContainer
|
||||
egg={egg}
|
||||
setEgg={setEgg}
|
||||
/* TODO: Get lowest nest_id rather than always defaulting to 1 */
|
||||
nestId={1}
|
||||
/>
|
||||
</div>
|
||||
<div css={tw`grid grid-cols-1 gap-y-6`}>
|
||||
<AdminBox icon={faNetworkWired} title={'Networking'} isLoading={isSubmitting}>
|
||||
<div css={tw`grid grid-cols-1 gap-4 lg:gap-6`}>
|
||||
<div>
|
||||
<Label htmlFor={'allocationId'}>Primary Allocation</Label>
|
||||
<Select id={'allocationId'} name={'allocationId'}/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor={'additionalAllocations'}>Additional Allocations</Label>
|
||||
<Select id={'additionalAllocations'} name={'additionalAllocations'}/>
|
||||
</div>
|
||||
</div>
|
||||
</AdminBox>
|
||||
<ServerResourceBox/>
|
||||
<ServerImageContainer/>
|
||||
</div>
|
||||
|
||||
<AdminBox title={'Startup Command'} css={tw`relative w-full col-span-2`}>
|
||||
<SpinnerOverlay visible={isSubmitting}/>
|
||||
|
||||
<Field
|
||||
id={'startup'}
|
||||
name={'startup'}
|
||||
label={'Startup Command'}
|
||||
type={'text'}
|
||||
description={'Edit your server\'s startup command here. The following variables are available by default: {{SERVER_MEMORY}}, {{SERVER_IP}}, and {{SERVER_PORT}}.'}
|
||||
placeholder={egg?.startup || ''}
|
||||
/>
|
||||
</AdminBox>
|
||||
|
||||
<div css={tw`col-span-2 grid grid-cols-1 md:grid-cols-2 gap-y-6 gap-x-8`}>
|
||||
{egg?.relationships.variables?.map((v, i) => (
|
||||
<ServerVariableContainer
|
||||
key={i}
|
||||
variable={v}
|
||||
defaultValue={v.defaultValue}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div css={tw`bg-neutral-700 rounded shadow-md px-4 py-3 col-span-2`}>
|
||||
<div css={tw`flex flex-row`}>
|
||||
<Button
|
||||
type="submit"
|
||||
size="small"
|
||||
css={tw`ml-auto`}
|
||||
disabled={isSubmitting || !isValid}
|
||||
>
|
||||
Create Server
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</AdminContentBlock>
|
||||
);
|
||||
};
|
||||
|
@ -3,10 +3,10 @@ import { useFormikContext } from 'formik';
|
||||
import SearchableSelect, { Option } from '@/components/elements/SearchableSelect';
|
||||
import { User, searchUserAccounts } from '@/api/admin/user';
|
||||
|
||||
export default ({ selected }: { selected: User }) => {
|
||||
export default ({ selected }: { selected?: User }) => {
|
||||
const context = useFormikContext();
|
||||
|
||||
const [ user, setUser ] = useState<User | null>(selected);
|
||||
const [ user, setUser ] = useState<User | null>(selected || null);
|
||||
const [ users, setUsers ] = useState<User[] | null>(null);
|
||||
|
||||
const onSearch = async (query: string) => {
|
||||
|
@ -57,10 +57,10 @@ function ServerStartupLineContainer ({ egg, server }: { egg: Egg | null; server:
|
||||
);
|
||||
}
|
||||
|
||||
function ServerServiceContainer ({ egg, setEgg, server }: { egg: Egg | null, setEgg: (value: Egg | null) => void, server: Server }) {
|
||||
export function ServerServiceContainer ({ egg, setEgg, nestId: _nestId }: { egg: Egg | null, setEgg: (value: Egg | null) => void, nestId: number }) {
|
||||
const { isSubmitting } = useFormikContext();
|
||||
|
||||
const [ nestId, setNestId ] = useState(server.nestId);
|
||||
const [ nestId, setNestId ] = useState(_nestId);
|
||||
|
||||
return (
|
||||
<AdminBox title={'Service Configuration'} isLoading={isSubmitting} css={tw`w-full`}>
|
||||
@ -77,7 +77,7 @@ function ServerServiceContainer ({ egg, setEgg, server }: { egg: Egg | null, set
|
||||
);
|
||||
}
|
||||
|
||||
function ServerImageContainer () {
|
||||
export function ServerImageContainer () {
|
||||
const { isSubmitting } = useFormikContext();
|
||||
|
||||
return (
|
||||
@ -98,7 +98,7 @@ function ServerImageContainer () {
|
||||
);
|
||||
}
|
||||
|
||||
function ServerVariableContainer ({ variable, defaultValue }: { variable: EggVariable, defaultValue: string }) {
|
||||
export function ServerVariableContainer ({ variable, defaultValue }: { variable: EggVariable, defaultValue: string }) {
|
||||
const key = 'environment.' + variable.environmentVariable;
|
||||
|
||||
const { isSubmitting, setFieldValue } = useFormikContext();
|
||||
@ -140,7 +140,7 @@ function ServerStartupForm ({ egg, setEgg, server }: { egg: Egg | null, setEgg:
|
||||
<ServerServiceContainer
|
||||
egg={egg}
|
||||
setEgg={setEgg}
|
||||
server={server}
|
||||
nestId={server.nestId}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
@ -11,14 +11,12 @@ export default () => {
|
||||
const { data: server } = useServerFromRoute();
|
||||
const { isSubmitting } = useFormikContext();
|
||||
|
||||
if (!server) return null;
|
||||
|
||||
return (
|
||||
<AdminBox icon={faCogs} title={'Settings'} isLoading={isSubmitting}>
|
||||
<div css={tw`grid grid-cols-1 xl:grid-cols-2 gap-4 lg:gap-6`}>
|
||||
<Field id={'name'} name={'name'} label={'Server Name'} type={'text'}/>
|
||||
<Field id={'externalId'} name={'externalId'} label={'External Identifier'} type={'text'}/>
|
||||
<OwnerSelect selected={server.relationships.user}/>
|
||||
<OwnerSelect selected={server?.relationships.user}/>
|
||||
</div>
|
||||
</AdminBox>
|
||||
);
|
||||
|
@ -13,9 +13,13 @@ export default () => {
|
||||
const { isSubmitting } = useFormikContext();
|
||||
const { data: server } = useServerFromRoute();
|
||||
|
||||
if (!server) return null;
|
||||
|
||||
const loadOptions = async (inputValue: string, callback: (options: Option[]) => void) => {
|
||||
if (!server) {
|
||||
// eslint-disable-next-line node/no-callback-literal
|
||||
callback([] as Option[]);
|
||||
return;
|
||||
}
|
||||
|
||||
const allocations = await getAllocations(server.nodeId, { ip: inputValue, server_id: '0' });
|
||||
|
||||
callback(allocations.map(a => {
|
||||
@ -29,7 +33,7 @@ export default () => {
|
||||
<div>
|
||||
<Label htmlFor={'allocationId'}>Primary Allocation</Label>
|
||||
<Select id={'allocationId'} name={'allocationId'}>
|
||||
{server.relationships.allocations?.map(a => (
|
||||
{server?.relationships.allocations?.map(a => (
|
||||
<option key={a.id} value={a.id}>{a.getDisplayText()}</option>
|
||||
))}
|
||||
</Select>
|
||||
@ -45,7 +49,7 @@ export default () => {
|
||||
id={'removeAllocations'}
|
||||
name={'removeAllocations'}
|
||||
label={'Remove Allocations'}
|
||||
options={server.relationships.allocations?.map(a => {
|
||||
options={server?.relationships.allocations?.map(a => {
|
||||
return { value: a.id.toString(), label: a.getDisplayText() };
|
||||
}) || []}
|
||||
isMulti
|
||||
|
@ -5,13 +5,9 @@ import tw from 'twin.macro';
|
||||
import Field from '@/components/elements/Field';
|
||||
import FormikSwitch from '@/components/elements/FormikSwitch';
|
||||
import React from 'react';
|
||||
import { useServerFromRoute } from '@/api/admin/server';
|
||||
|
||||
export default () => {
|
||||
const { isSubmitting } = useFormikContext();
|
||||
const { data: server } = useServerFromRoute();
|
||||
|
||||
if (!server) return null;
|
||||
|
||||
return (
|
||||
<AdminBox icon={faBalanceScale} title={'Resources'} isLoading={isSubmitting}>
|
||||
|
Loading…
Reference in New Issue
Block a user