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 tw from 'twin.macro';
|
||||||
import AdminContentBlock from '@/components/admin/AdminContentBlock';
|
import AdminContentBlock from '@/components/admin/AdminContentBlock';
|
||||||
|
import { object } from 'yup';
|
||||||
|
import { Values } from '@/api/admin/servers/updateServer';
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
|
const [ egg, setEgg ] = useState<Egg | null>(null);
|
||||||
|
|
||||||
|
const submit = (_: Values) => {
|
||||||
|
//
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AdminContentBlock title={'New Server'}>
|
<AdminContentBlock title={'New Server'}>
|
||||||
<div css={tw`w-full flex flex-row items-center mb-8`}>
|
<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>
|
<p css={tw`text-base text-neutral-400 whitespace-nowrap overflow-ellipsis overflow-hidden`}>Add a new server to the panel.</p>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</AdminContentBlock>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,10 +3,10 @@ import { useFormikContext } from 'formik';
|
||||||
import SearchableSelect, { Option } from '@/components/elements/SearchableSelect';
|
import SearchableSelect, { Option } from '@/components/elements/SearchableSelect';
|
||||||
import { User, searchUserAccounts } from '@/api/admin/user';
|
import { User, searchUserAccounts } from '@/api/admin/user';
|
||||||
|
|
||||||
export default ({ selected }: { selected: User }) => {
|
export default ({ selected }: { selected?: User }) => {
|
||||||
const context = useFormikContext();
|
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 [ users, setUsers ] = useState<User[] | null>(null);
|
||||||
|
|
||||||
const onSearch = async (query: string) => {
|
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 { isSubmitting } = useFormikContext();
|
||||||
|
|
||||||
const [ nestId, setNestId ] = useState(server.nestId);
|
const [ nestId, setNestId ] = useState(_nestId);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AdminBox title={'Service Configuration'} isLoading={isSubmitting} css={tw`w-full`}>
|
<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();
|
const { isSubmitting } = useFormikContext();
|
||||||
|
|
||||||
return (
|
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 key = 'environment.' + variable.environmentVariable;
|
||||||
|
|
||||||
const { isSubmitting, setFieldValue } = useFormikContext();
|
const { isSubmitting, setFieldValue } = useFormikContext();
|
||||||
|
@ -140,7 +140,7 @@ function ServerStartupForm ({ egg, setEgg, server }: { egg: Egg | null, setEgg:
|
||||||
<ServerServiceContainer
|
<ServerServiceContainer
|
||||||
egg={egg}
|
egg={egg}
|
||||||
setEgg={setEgg}
|
setEgg={setEgg}
|
||||||
server={server}
|
nestId={server.nestId}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -11,14 +11,12 @@ export default () => {
|
||||||
const { data: server } = useServerFromRoute();
|
const { data: server } = useServerFromRoute();
|
||||||
const { isSubmitting } = useFormikContext();
|
const { isSubmitting } = useFormikContext();
|
||||||
|
|
||||||
if (!server) return null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AdminBox icon={faCogs} title={'Settings'} isLoading={isSubmitting}>
|
<AdminBox icon={faCogs} title={'Settings'} isLoading={isSubmitting}>
|
||||||
<div css={tw`grid grid-cols-1 xl:grid-cols-2 gap-4 lg:gap-6`}>
|
<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={'name'} name={'name'} label={'Server Name'} type={'text'}/>
|
||||||
<Field id={'externalId'} name={'externalId'} label={'External Identifier'} type={'text'}/>
|
<Field id={'externalId'} name={'externalId'} label={'External Identifier'} type={'text'}/>
|
||||||
<OwnerSelect selected={server.relationships.user}/>
|
<OwnerSelect selected={server?.relationships.user}/>
|
||||||
</div>
|
</div>
|
||||||
</AdminBox>
|
</AdminBox>
|
||||||
);
|
);
|
||||||
|
|
|
@ -13,9 +13,13 @@ export default () => {
|
||||||
const { isSubmitting } = useFormikContext();
|
const { isSubmitting } = useFormikContext();
|
||||||
const { data: server } = useServerFromRoute();
|
const { data: server } = useServerFromRoute();
|
||||||
|
|
||||||
if (!server) return null;
|
|
||||||
|
|
||||||
const loadOptions = async (inputValue: string, callback: (options: Option[]) => void) => {
|
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' });
|
const allocations = await getAllocations(server.nodeId, { ip: inputValue, server_id: '0' });
|
||||||
|
|
||||||
callback(allocations.map(a => {
|
callback(allocations.map(a => {
|
||||||
|
@ -29,7 +33,7 @@ export default () => {
|
||||||
<div>
|
<div>
|
||||||
<Label htmlFor={'allocationId'}>Primary Allocation</Label>
|
<Label htmlFor={'allocationId'}>Primary Allocation</Label>
|
||||||
<Select id={'allocationId'} name={'allocationId'}>
|
<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>
|
<option key={a.id} value={a.id}>{a.getDisplayText()}</option>
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
|
@ -45,7 +49,7 @@ export default () => {
|
||||||
id={'removeAllocations'}
|
id={'removeAllocations'}
|
||||||
name={'removeAllocations'}
|
name={'removeAllocations'}
|
||||||
label={'Remove Allocations'}
|
label={'Remove Allocations'}
|
||||||
options={server.relationships.allocations?.map(a => {
|
options={server?.relationships.allocations?.map(a => {
|
||||||
return { value: a.id.toString(), label: a.getDisplayText() };
|
return { value: a.id.toString(), label: a.getDisplayText() };
|
||||||
}) || []}
|
}) || []}
|
||||||
isMulti
|
isMulti
|
||||||
|
|
|
@ -5,13 +5,9 @@ import tw from 'twin.macro';
|
||||||
import Field from '@/components/elements/Field';
|
import Field from '@/components/elements/Field';
|
||||||
import FormikSwitch from '@/components/elements/FormikSwitch';
|
import FormikSwitch from '@/components/elements/FormikSwitch';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useServerFromRoute } from '@/api/admin/server';
|
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const { isSubmitting } = useFormikContext();
|
const { isSubmitting } = useFormikContext();
|
||||||
const { data: server } = useServerFromRoute();
|
|
||||||
|
|
||||||
if (!server) return null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AdminBox icon={faBalanceScale} title={'Resources'} isLoading={isSubmitting}>
|
<AdminBox icon={faBalanceScale} title={'Resources'} isLoading={isSubmitting}>
|
||||||
|
|
Loading…
Reference in New Issue