ui(admin): fix egg variables

This commit is contained in:
Matthew Penner 2021-11-04 14:32:42 -06:00
parent f7c824743f
commit ce0bc477c2
No known key found for this signature in database
GPG key ID: BAB67850901908A8
12 changed files with 157 additions and 100 deletions

View file

@ -55,8 +55,8 @@ class VariableCreationService
'description' => $data['description'] ?? '', 'description' => $data['description'] ?? '',
'env_variable' => $data['env_variable'] ?? '', 'env_variable' => $data['env_variable'] ?? '',
'default_value' => $data['default_value'] ?? '', 'default_value' => $data['default_value'] ?? '',
'user_viewable' => in_array('user_viewable', $options), 'user_viewable' => $data['user_viewable'],
'user_editable' => in_array('user_editable', $options), 'user_editable' => $data['user_editable'],
'rules' => $data['rules'] ?? '', 'rules' => $data['rules'] ?? '',
]); ]);

View file

@ -2,6 +2,9 @@ import { Model, UUID, WithRelationships, withRelationships } from '@/api/admin/i
import { Nest } from '@/api/admin/nest'; import { Nest } from '@/api/admin/nest';
import http, { QueryBuilderParams, withQueryBuilderParams } from '@/api/http'; import http, { QueryBuilderParams, withQueryBuilderParams } from '@/api/http';
import { AdminTransformers } from '@/api/admin/transformers'; import { AdminTransformers } from '@/api/admin/transformers';
import { AxiosError } from 'axios';
import { useRouteMatch } from 'react-router-dom';
import useSWR, { SWRResponse } from 'swr';
export interface Egg extends Model { export interface Egg extends Model {
id: number; id: number;
@ -39,16 +42,22 @@ export interface EggVariable extends Model {
defaultValue: string; defaultValue: string;
isUserViewable: boolean; isUserViewable: boolean;
isUserEditable: boolean; isUserEditable: boolean;
isRequired: boolean; // isRequired: boolean;
rules: string; rules: string;
createdAt: Date; createdAt: Date;
updatedAt: Date; updatedAt: Date;
} }
/**
* A standard API response with the minimum viable details for the frontend
* to correctly render a egg.
*/
type LoadedEgg = WithRelationships<Egg, 'nest' | 'variables'>;
/** /**
* Gets a single egg from the database and returns it. * Gets a single egg from the database and returns it.
*/ */
export const getEgg = async (id: number | string): Promise<WithRelationships<Egg, 'nest' | 'variables'>> => { export const getEgg = async (id: number | string): Promise<LoadedEgg> => {
const { data } = await http.get(`/api/application/eggs/${id}`, { const { data } = await http.get(`/api/application/eggs/${id}`, {
params: { params: {
include: [ 'nest', 'variables' ], include: [ 'nest', 'variables' ],
@ -73,3 +82,16 @@ export const exportEgg = async (eggId: number): Promise<Record<string, any>> =>
const { data } = await http.get(`/api/application/eggs/${eggId}/export`); const { data } = await http.get(`/api/application/eggs/${eggId}/export`);
return data; return data;
}; };
/**
* Returns an SWR instance by automatically loading in the server for the currently
* loaded route match in the admin area.
*/
export const useEggFromRoute = (): SWRResponse<LoadedEgg, AxiosError> => {
const { params } = useRouteMatch<{ id: string }>();
return useSWR(`/api/application/eggs/${params.id}`, async () => getEgg(params.id), {
revalidateOnMount: false,
revalidateOnFocus: false,
});
};

View file

@ -1,21 +1,22 @@
import http from '@/api/http'; import http from '@/api/http';
import { EggVariable, rawDataToEggVariable } from '@/api/admin/eggs/getEgg'; import { EggVariable } from '@/api/admin/egg';
import { AdminTransformers } from '@/api/admin/transformers';
export default (eggId: number, variable: Omit<EggVariable, 'id' | 'eggId' | 'createdAt' | 'updatedAt'>): Promise<EggVariable> => { export type CreateEggVariable = Omit<EggVariable, 'id' | 'eggId' | 'createdAt' | 'updatedAt' | 'relationships'>;
return new Promise((resolve, reject) => {
http.post( export default async (eggId: number, variable: CreateEggVariable): Promise<EggVariable> => {
`/api/application/eggs/${eggId}/variables`, const { data } = await http.post(
{ `/api/application/eggs/${eggId}/variables`,
name: variable.name, {
description: variable.description, name: variable.name,
env_variable: variable.envVariable, description: variable.description,
default_value: variable.defaultValue, env_variable: variable.environmentVariable,
user_viewable: variable.userViewable, default_value: variable.defaultValue,
user_editable: variable.userEditable, user_viewable: variable.isUserViewable,
rules: variable.rules, user_editable: variable.isUserEditable,
}, rules: variable.rules,
) },
.then(({ data }) => resolve(rawDataToEggVariable(data))) );
.catch(reject);
}); return AdminTransformers.toEggVariable(data);
}; };

View file

@ -1,22 +1,21 @@
import http from '@/api/http'; import http from '@/api/http';
import { EggVariable, rawDataToEggVariable } from '@/api/admin/eggs/getEgg'; import { EggVariable } from '@/api/admin/egg';
import { AdminTransformers } from '@/api/admin/transformers';
export default (eggId: number, variables: Omit<EggVariable, 'eggId' | 'createdAt' | 'updatedAt'>[]): Promise<EggVariable[]> => { export default async (eggId: number, variables: Omit<EggVariable, 'eggId' | 'createdAt' | 'updatedAt'>[]): Promise<EggVariable[]> => {
return new Promise((resolve, reject) => { const { data } = await http.patch(
http.patch( `/api/application/eggs/${eggId}/variables`,
`/api/application/eggs/${eggId}/variables`, variables.map(variable => ({
variables.map(variable => ({ id: variable.id,
id: variable.id, name: variable.name,
name: variable.name, description: variable.description,
description: variable.description, env_variable: variable.environmentVariable,
env_variable: variable.envVariable, default_value: variable.defaultValue,
default_value: variable.defaultValue, user_viewable: variable.isUserViewable,
user_viewable: variable.userViewable, user_editable: variable.isUserEditable,
user_editable: variable.userEditable, rules: variable.rules,
rules: variable.rules, })),
})), );
)
.then(({ data }) => resolve((data.data || []).map(rawDataToEggVariable))) return data.data.map(AdminTransformers.toEggVariable);
.catch(reject);
});
}; };

View file

@ -168,7 +168,7 @@ export class AdminTransformers {
defaultValue: attributes.default_value, defaultValue: attributes.default_value,
isUserViewable: attributes.user_viewable, isUserViewable: attributes.user_viewable,
isUserEditable: attributes.user_editable, isUserEditable: attributes.user_editable,
isRequired: attributes.required, // isRequired: attributes.required,
rules: attributes.rules, rules: attributes.rules,
createdAt: new Date(attributes.created_at), createdAt: new Date(attributes.created_at),
updatedAt: new Date(attributes.updated_at), updatedAt: new Date(attributes.updated_at),

View file

@ -1,4 +1,4 @@
import { Egg } from '@/api/admin/eggs/getEgg'; import { useEggFromRoute } from '@/api/admin/egg';
import updateEgg from '@/api/admin/eggs/updateEgg'; import updateEgg from '@/api/admin/eggs/updateEgg';
import Field from '@/components/elements/Field'; import Field from '@/components/elements/Field';
import useFlash from '@/plugins/useFlash'; import useFlash from '@/plugins/useFlash';
@ -18,9 +18,15 @@ interface Values {
scriptInstall: string; scriptInstall: string;
} }
export default function EggInstallContainer ({ egg }: { egg: Egg }) { export default function EggInstallContainer () {
const { clearFlashes, clearAndAddHttpError } = useFlash(); const { clearFlashes, clearAndAddHttpError } = useFlash();
const { data: egg } = useEggFromRoute();
if (!egg) {
return null;
}
let fetchFileContent: (() => Promise<string>) | null = null; let fetchFileContent: (() => Promise<string>) | null = null;
const submit = async (values: Values, { setSubmitting }: FormikHelpers<Values>) => { const submit = async (values: Values, { setSubmitting }: FormikHelpers<Values>) => {

View file

@ -1,10 +1,11 @@
import { useEggFromRoute } from '@/api/admin/egg';
import EggInstallContainer from '@/components/admin/nests/eggs/EggInstallContainer'; import EggInstallContainer from '@/components/admin/nests/eggs/EggInstallContainer';
import EggVariablesContainer from '@/components/admin/nests/eggs/EggVariablesContainer'; import EggVariablesContainer from '@/components/admin/nests/eggs/EggVariablesContainer';
import React from 'react'; import useFlash from '@/plugins/useFlash';
import React, { useEffect } from 'react';
import { useLocation } from 'react-router'; import { useLocation } from 'react-router';
import tw from 'twin.macro'; import tw from 'twin.macro';
import { Route, Switch, useRouteMatch } from 'react-router-dom'; import { Route, Switch, useRouteMatch } from 'react-router-dom';
import getEgg from '@/api/admin/eggs/getEgg';
import AdminContentBlock from '@/components/admin/AdminContentBlock'; import AdminContentBlock from '@/components/admin/AdminContentBlock';
import Spinner from '@/components/elements/Spinner'; import Spinner from '@/components/elements/Spinner';
import FlashMessageRender from '@/components/FlashMessageRender'; import FlashMessageRender from '@/components/FlashMessageRender';
@ -13,18 +14,24 @@ import EggSettingsContainer from '@/components/admin/nests/eggs/EggSettingsConta
const EggRouter = () => { const EggRouter = () => {
const location = useLocation(); const location = useLocation();
const match = useRouteMatch<{ id?: string }>(); const match = useRouteMatch();
const { data: egg } = getEgg(Number(match.params?.id)); const { clearFlashes, clearAndAddHttpError } = useFlash();
const { data: egg, error, isValidating, mutate } = useEggFromRoute();
if (egg === undefined) { useEffect(() => {
mutate();
}, []);
useEffect(() => {
if (!error) clearFlashes('egg');
if (error) clearAndAddHttpError({ error, key: 'egg' });
}, [ error ]);
if (!egg || (error && isValidating)) {
return ( return (
<AdminContentBlock> <AdminContentBlock showFlashKey={'egg'}>
<FlashMessageRender byKey={'egg'} css={tw`mb-4`}/> <Spinner size={'large'} centered/>
<div css={tw`w-full flex flex-col items-center justify-center`} style={{ height: '24rem' }}>
<Spinner size={'base'}/>
</div>
</AdminContentBlock> </AdminContentBlock>
); );
} }
@ -62,15 +69,15 @@ const EggRouter = () => {
<Switch location={location}> <Switch location={location}>
<Route path={`${match.path}`} exact> <Route path={`${match.path}`} exact>
<EggSettingsContainer egg={egg}/> <EggSettingsContainer/>
</Route> </Route>
<Route path={`${match.path}/variables`} exact> <Route path={`${match.path}/variables`} exact>
<EggVariablesContainer egg={egg}/> <EggVariablesContainer/>
</Route> </Route>
<Route path={`${match.path}/install`} exact> <Route path={`${match.path}/install`} exact>
<EggInstallContainer egg={egg}/> <EggInstallContainer/>
</Route> </Route>
</Switch> </Switch>
</AdminContentBlock> </AdminContentBlock>

View file

@ -1,3 +1,4 @@
import { useEggFromRoute } from '@/api/admin/egg';
import updateEgg from '@/api/admin/eggs/updateEgg'; import updateEgg from '@/api/admin/eggs/updateEgg';
import EggDeleteButton from '@/components/admin/nests/eggs/EggDeleteButton'; import EggDeleteButton from '@/components/admin/nests/eggs/EggDeleteButton';
import EggExportButton from '@/components/admin/nests/eggs/EggExportButton'; import EggExportButton from '@/components/admin/nests/eggs/EggExportButton';
@ -13,7 +14,6 @@ import { faDocker } from '@fortawesome/free-brands-svg-icons';
import { faEgg, faFireAlt, faMicrochip, faTerminal } from '@fortawesome/free-solid-svg-icons'; import { faEgg, faFireAlt, faMicrochip, faTerminal } from '@fortawesome/free-solid-svg-icons';
import React, { forwardRef, useImperativeHandle, useRef } from 'react'; import React, { forwardRef, useImperativeHandle, useRef } from 'react';
import AdminBox from '@/components/admin/AdminBox'; import AdminBox from '@/components/admin/AdminBox';
import { Egg } from '@/api/admin/eggs/getEgg';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import tw from 'twin.macro'; import tw from 'twin.macro';
import { object } from 'yup'; import { object } from 'yup';
@ -45,7 +45,13 @@ export function EggInformationContainer () {
); );
} }
function EggDetailsContainer ({ egg }: { egg: Egg }) { function EggDetailsContainer () {
const { data: egg } = useEggFromRoute();
if (!egg) {
return null;
}
return ( return (
<AdminBox icon={faEgg} title={'Egg Details'} css={tw`relative`}> <AdminBox icon={faEgg} title={'Egg Details'} css={tw`relative`}>
<div css={tw`mb-6`}> <div css={tw`mb-6`}>
@ -200,12 +206,18 @@ interface Values {
configFiles: string; configFiles: string;
} }
export default function EggSettingsContainer ({ egg }: { egg: Egg }) { export default function EggSettingsContainer () {
const history = useHistory(); const history = useHistory();
const ref = useRef<EggProcessContainerRef>();
const { clearFlashes, clearAndAddHttpError } = useFlash(); const { clearFlashes, clearAndAddHttpError } = useFlash();
const ref = useRef<EggProcessContainerRef>(); const { data: egg } = useEggFromRoute();
if (!egg) {
return null;
}
const submit = async (values: Values, { setSubmitting }: FormikHelpers<Values>) => { const submit = async (values: Values, { setSubmitting }: FormikHelpers<Values>) => {
clearFlashes('egg'); clearFlashes('egg');
@ -240,7 +252,7 @@ export default function EggSettingsContainer ({ egg }: { egg: Egg }) {
<Form> <Form>
<div css={tw`grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-6 mb-6`}> <div css={tw`grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-6 mb-6`}>
<EggInformationContainer/> <EggInformationContainer/>
<EggDetailsContainer egg={egg}/> <EggDetailsContainer/>
</div> </div>
<EggStartupContainer css={tw`mb-6`}/> <EggStartupContainer css={tw`mb-6`}/>

View file

@ -5,7 +5,7 @@ import { Form, Formik, FormikHelpers, useFormikContext } from 'formik';
import React, { useState } from 'react'; import React, { useState } from 'react';
import tw from 'twin.macro'; import tw from 'twin.macro';
import { array, boolean, object, string } from 'yup'; import { array, boolean, object, string } from 'yup';
import getEgg, { Egg, EggVariable } from '@/api/admin/eggs/getEgg'; import { EggVariable, useEggFromRoute } from '@/api/admin/egg';
import updateEggVariables from '@/api/admin/eggs/updateEggVariables'; import updateEggVariables from '@/api/admin/eggs/updateEggVariables';
import NewVariableButton from '@/components/admin/nests/eggs/NewVariableButton'; import NewVariableButton from '@/components/admin/nests/eggs/NewVariableButton';
import AdminBox from '@/components/admin/AdminBox'; import AdminBox from '@/components/admin/AdminBox';
@ -19,10 +19,10 @@ import { TrashIcon } from '@heroicons/react/outline';
export const validationSchema = object().shape({ export const validationSchema = object().shape({
name: string().required().min(1).max(191), name: string().required().min(1).max(191),
description: string(), description: string(),
envVariable: string().required().min(1).max(191), environmentVariable: string().required().min(1).max(191),
defaultValue: string(), defaultValue: string(),
userViewable: boolean().required(), isUserViewable: boolean().required(),
userEditable: boolean().required(), isUserEditable: boolean().required(),
rules: string().required(), rules: string().required(),
}); });
@ -47,8 +47,8 @@ export function EggVariableForm ({ prefix }: { prefix: string }) {
<FieldRow> <FieldRow>
<Field <Field
id={`${prefix}envVariable`} id={`${prefix}environmentVariable`}
name={`${prefix}envVariable`} name={`${prefix}environmentVariable`}
label={'Environment Variable'} label={'Environment Variable'}
type={'text'} type={'text'}
/> />
@ -63,14 +63,14 @@ export function EggVariableForm ({ prefix }: { prefix: string }) {
<div css={tw`flex flex-row mb-6`}> <div css={tw`flex flex-row mb-6`}>
<Checkbox <Checkbox
id={`${prefix}userViewable`} id={`${prefix}isUserViewable`}
name={`${prefix}userViewable`} name={`${prefix}isUserViewable`}
label={'User Viewable'} label={'User Viewable'}
/> />
<Checkbox <Checkbox
id={`${prefix}userEditable`} id={`${prefix}isUserEditable`}
name={`${prefix}userEditable`} name={`${prefix}isUserEditable`}
label={'User Editable'} label={'User Editable'}
css={tw`ml-auto`} css={tw`ml-auto`}
/> />
@ -95,7 +95,7 @@ function EggVariableDeleteButton ({ onClick }: { onClick: (success: () => void)
setLoading(true); setLoading(true);
onClick(() => { onClick(() => {
setLoading(false); //setLoading(false);
}); });
}; };
@ -140,14 +140,18 @@ function EggVariableBox ({ onDeleteClick, variable, prefix }: { onDeleteClick: (
); );
} }
export default function EggVariablesContainer ({ egg }: { egg: Egg }) { export default function EggVariablesContainer () {
const { clearAndAddHttpError } = useFlash(); const { clearAndAddHttpError } = useFlash();
const { mutate } = getEgg(egg.id); const { data: egg, mutate } = useEggFromRoute();
if (!egg) {
return null;
}
const submit = (values: EggVariable[], { setSubmitting }: FormikHelpers<EggVariable[]>) => { const submit = (values: EggVariable[], { setSubmitting }: FormikHelpers<EggVariable[]>) => {
updateEggVariables(egg.id, values) updateEggVariables(egg.id, values)
.then(async (variables) => await mutate(egg => ({ ...egg!, relations: { variables: variables } }))) .then(async () => await mutate())
.catch(error => clearAndAddHttpError({ key: 'egg', error })) .catch(error => clearAndAddHttpError({ key: 'egg', error }))
.then(() => setSubmitting(false)); .then(() => setSubmitting(false));
}; };
@ -155,17 +159,17 @@ export default function EggVariablesContainer ({ egg }: { egg: Egg }) {
return ( return (
<Formik <Formik
onSubmit={submit} onSubmit={submit}
initialValues={egg.relations?.variables || []} initialValues={egg.relationships.variables}
validationSchema={array().of(validationSchema)} validationSchema={array().of(validationSchema)}
> >
{({ isSubmitting, isValid }) => ( {({ isSubmitting, isValid }) => (
<Form> <Form>
<div css={tw`flex flex-col mb-16`}> <div css={tw`flex flex-col mb-16`}>
{egg.relations?.variables?.length === 0 ? {egg.relationships.variables?.length === 0 ?
<NoItems css={tw`bg-neutral-700 rounded-md shadow-md`}/> <NoItems css={tw`bg-neutral-700 rounded-md shadow-md`}/>
: :
<div css={tw`grid grid-cols-1 lg:grid-cols-2 gap-x-8 gap-y-6`}> <div css={tw`grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-x-8 gap-y-6`}>
{egg.relations?.variables?.map((v, i) => ( {egg.relationships.variables.map((v, i) => (
<EggVariableBox <EggVariableBox
key={i} key={i}
prefix={`[${i}].`} prefix={`[${i}].`}
@ -175,8 +179,9 @@ export default function EggVariablesContainer ({ egg }: { egg: Egg }) {
.then(async () => { .then(async () => {
await mutate(egg => ({ await mutate(egg => ({
...egg!, ...egg!,
relations: { relationships: {
variables: egg!.relations.variables!.filter(v2 => v.id === v2.id), ...egg!.relationships,
variables: egg!.relationships.variables!.filter(v2 => v.id === v2.id),
}, },
})); }));
success(); success();
@ -190,7 +195,7 @@ export default function EggVariablesContainer ({ egg }: { egg: Egg }) {
<div css={tw`bg-neutral-700 rounded shadow-md py-2 px-4 mt-6`}> <div css={tw`bg-neutral-700 rounded shadow-md py-2 px-4 mt-6`}>
<div css={tw`flex flex-row`}> <div css={tw`flex flex-row`}>
<NewVariableButton eggId={egg.id}/> <NewVariableButton/>
<Button type={'submit'} size={'small'} css={tw`ml-auto`} disabled={isSubmitting || !isValid}> <Button type={'submit'} size={'small'} css={tw`ml-auto`} disabled={isSubmitting || !isValid}>
Save Changes Save Changes

View file

@ -1,26 +1,32 @@
import { Form, Formik, FormikHelpers } from 'formik'; import { Form, Formik, FormikHelpers, useFormikContext } from 'formik';
import React, { useState } from 'react'; import React, { useState } from 'react';
import tw from 'twin.macro'; import tw from 'twin.macro';
import createEggVariable from '@/api/admin/eggs/createEggVariable'; import createEggVariable, { CreateEggVariable } from '@/api/admin/eggs/createEggVariable';
import getEgg from '@/api/admin/eggs/getEgg'; import { useEggFromRoute } from '@/api/admin/egg';
import { EggVariableForm, validationSchema } from '@/components/admin/nests/eggs/EggVariablesContainer'; import { EggVariableForm, validationSchema } from '@/components/admin/nests/eggs/EggVariablesContainer';
import Modal from '@/components/elements/Modal'; import Modal from '@/components/elements/Modal';
import FlashMessageRender from '@/components/FlashMessageRender'; import FlashMessageRender from '@/components/FlashMessageRender';
import Button from '@/components/elements/Button'; import Button from '@/components/elements/Button';
import useFlash from '@/plugins/useFlash'; import useFlash from '@/plugins/useFlash';
export default function NewVariableButton ({ eggId }: { eggId: number }) { export default function NewVariableButton () {
const { setValues } = useFormikContext();
const [ visible, setVisible ] = useState(false); const [ visible, setVisible ] = useState(false);
const { clearFlashes, clearAndAddHttpError } = useFlash(); const { clearFlashes, clearAndAddHttpError } = useFlash();
const { mutate } = getEgg(eggId); const { data: egg, mutate } = useEggFromRoute();
const submit = (values: any, { setSubmitting }: FormikHelpers<any>) => { if (!egg) {
return null;
}
const submit = (values: CreateEggVariable, { setSubmitting }: FormikHelpers<CreateEggVariable>) => {
clearFlashes('variable:create'); clearFlashes('variable:create');
createEggVariable(eggId, values) createEggVariable(egg.id, values)
.then(async (v) => { .then(async (variable) => {
await mutate(egg => ({ ...egg!, relations: { variables: [ ...egg!.relations.variables!, v ] } })); setValues([ ...egg.relationships.variables, variable ]);
await mutate(egg => ({ ...egg!, relationships: { ...egg!.relationships, variables: [ ...egg!.relationships.variables, variable ] } }));
setVisible(false); setVisible(false);
}) })
.catch(error => { .catch(error => {
@ -36,15 +42,15 @@ export default function NewVariableButton ({ eggId }: { eggId: number }) {
initialValues={{ initialValues={{
name: '', name: '',
description: '', description: '',
envVariable: '', environmentVariable: '',
defaultValue: '', defaultValue: '',
userViewable: false, isUserViewable: false,
userEditable: false, isUserEditable: false,
rules: '', rules: '',
}} }}
validationSchema={validationSchema} validationSchema={validationSchema}
> >
{({ isSubmitting, resetForm }) => ( {({ isSubmitting, isValid, resetForm }) => (
<Modal <Modal
visible={visible} visible={visible}
dismissable={!isSubmitting} dismissable={!isSubmitting}
@ -70,7 +76,7 @@ export default function NewVariableButton ({ eggId }: { eggId: number }) {
> >
Cancel Cancel
</Button> </Button>
<Button css={tw`w-full mt-4 sm:w-auto sm:mt-0`} type={'submit'}> <Button css={tw`w-full mt-4 sm:w-auto sm:mt-0`} type={'submit'} disabled={isSubmitting || !isValid}>
Create Variable Create Variable
</Button> </Button>
</div> </div>

View file

@ -146,7 +146,6 @@ export default () => {
const { clearFlashes, clearAndAddHttpError } = useFlash(); const { clearFlashes, clearAndAddHttpError } = useFlash();
const submit = (r: CreateServerRequest, { setSubmitting }: FormikHelpers<CreateServerRequest>) => { const submit = (r: CreateServerRequest, { setSubmitting }: FormikHelpers<CreateServerRequest>) => {
console.log(r);
clearFlashes('server:create'); clearFlashes('server:create');
createServer(r) createServer(r)

View file

@ -32,7 +32,7 @@ export default () => {
if (!server || (error && isValidating)) { if (!server || (error && isValidating)) {
return ( return (
<AdminContentBlock showFlashKey={'server'}> <AdminContentBlock showFlashKey={'server'}>
<Spinner size={'large'} centered/>; <Spinner size={'large'} centered/>
</AdminContentBlock> </AdminContentBlock>
); );
} }