admin(ui): add blank 'create' views
This commit is contained in:
parent
e7021dfc39
commit
d81aef68b5
|
@ -0,0 +1,12 @@
|
||||||
|
import http from '@/api/http';
|
||||||
|
import { Location } from '@/api/admin/locations/getLocations';
|
||||||
|
|
||||||
|
export default (short: string, long?: string): Promise<Location> => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
http.post('/api/application/locations', {
|
||||||
|
short, long,
|
||||||
|
})
|
||||||
|
.then(({ data }) => resolve(data.attributes))
|
||||||
|
.catch(reject);
|
||||||
|
});
|
||||||
|
};
|
|
@ -1,5 +1,5 @@
|
||||||
import { Role } from '@/api/admin/roles/getRoles';
|
|
||||||
import http from '@/api/http';
|
import http from '@/api/http';
|
||||||
|
import { Role } from '@/api/admin/roles/getRoles';
|
||||||
|
|
||||||
export default (name: string, description?: string): Promise<Role> => {
|
export default (name: string, description?: string): Promise<Role> => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
|
@ -64,13 +64,15 @@ const DatabasesContainer = () => {
|
||||||
<AdminContentBlock>
|
<AdminContentBlock>
|
||||||
<div css={tw`w-full flex flex-row items-center mb-8`}>
|
<div css={tw`w-full flex flex-row items-center mb-8`}>
|
||||||
<div css={tw`flex flex-col`}>
|
<div css={tw`flex flex-col`}>
|
||||||
<h2 css={tw`text-2xl text-neutral-50 font-header font-medium`}>Databases</h2>
|
<h2 css={tw`text-2xl text-neutral-50 font-header font-medium`}>Database Hosts</h2>
|
||||||
<p css={tw`text-base text-neutral-400`}>Database hosts that servers can have databases created on.</p>
|
<p css={tw`text-base text-neutral-400`}>Database hosts that servers can have databases created on.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button type={'button'} size={'large'} css={tw`h-10 ml-auto px-4 py-0`}>
|
<NavLink to={`${match.url}/new`} css={tw`ml-auto`}>
|
||||||
New Database
|
<Button type={'button'} size={'large'} css={tw`h-10 px-4 py-0`}>
|
||||||
</Button>
|
New Database Host
|
||||||
|
</Button>
|
||||||
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FlashMessageRender byKey={'databases'} css={tw`mb-4`}/>
|
<FlashMessageRender byKey={'databases'} css={tw`mb-4`}/>
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import React from 'react';
|
||||||
|
import tw from 'twin.macro';
|
||||||
|
import AdminContentBlock from '@/components/admin/AdminContentBlock';
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
return (
|
||||||
|
<AdminContentBlock>
|
||||||
|
<div css={tw`w-full flex flex-row items-center mb-8`}>
|
||||||
|
<div css={tw`flex flex-col`}>
|
||||||
|
<h2 css={tw`text-2xl text-neutral-50 font-header font-medium`}>Create Database Host</h2>
|
||||||
|
<p css={tw`text-base text-neutral-400`}>Add a new database host to the panel.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AdminContentBlock>
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,4 +1,3 @@
|
||||||
import CopyOnClick from '@/components/elements/CopyOnClick';
|
|
||||||
import React, { useContext, useEffect, useState } from 'react';
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
import getLocations, { Context as LocationsContext } from '@/api/admin/locations/getLocations';
|
import getLocations, { Context as LocationsContext } from '@/api/admin/locations/getLocations';
|
||||||
import FlashMessageRender from '@/components/FlashMessageRender';
|
import FlashMessageRender from '@/components/FlashMessageRender';
|
||||||
|
@ -9,7 +8,8 @@ import tw from 'twin.macro';
|
||||||
import AdminContentBlock from '@/components/admin/AdminContentBlock';
|
import AdminContentBlock from '@/components/admin/AdminContentBlock';
|
||||||
import AdminCheckbox from '@/components/admin/AdminCheckbox';
|
import AdminCheckbox from '@/components/admin/AdminCheckbox';
|
||||||
import AdminTable, { TableBody, TableHead, TableHeader, TableRow, Pagination, Loading, NoItems, ContentWrapper } from '@/components/admin/AdminTable';
|
import AdminTable, { TableBody, TableHead, TableHeader, TableRow, Pagination, Loading, NoItems, ContentWrapper } from '@/components/admin/AdminTable';
|
||||||
import Button from '@/components/elements/Button';
|
import NewLocationButton from '@/components/admin/locations/NewLocationButton';
|
||||||
|
import CopyOnClick from '@/components/elements/CopyOnClick';
|
||||||
|
|
||||||
const RowCheckbox = ({ id }: { id: number}) => {
|
const RowCheckbox = ({ id }: { id: number}) => {
|
||||||
const isChecked = AdminContext.useStoreState(state => state.locations.selectedLocations.indexOf(id) >= 0);
|
const isChecked = AdminContext.useStoreState(state => state.locations.selectedLocations.indexOf(id) >= 0);
|
||||||
|
@ -68,9 +68,7 @@ const LocationsContainer = () => {
|
||||||
<p css={tw`text-base text-neutral-400`}>All locations that nodes can be assigned to for easier categorization.</p>
|
<p css={tw`text-base text-neutral-400`}>All locations that nodes can be assigned to for easier categorization.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button type={'button'} size={'large'} css={tw`h-10 ml-auto px-4 py-0`}>
|
<NewLocationButton/>
|
||||||
New Location
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FlashMessageRender byKey={'locations'} css={tw`mb-4`}/>
|
<FlashMessageRender byKey={'locations'} css={tw`mb-4`}/>
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import Button from '@/components/elements/Button';
|
||||||
|
import Field from '@/components/elements/Field';
|
||||||
|
import Modal from '@/components/elements/Modal';
|
||||||
|
import FlashMessageRender from '@/components/FlashMessageRender';
|
||||||
|
import useFlash from '@/plugins/useFlash';
|
||||||
|
import { Form, Formik, FormikHelpers } from 'formik';
|
||||||
|
import tw from 'twin.macro';
|
||||||
|
import { object, string } from 'yup';
|
||||||
|
import createLocation from '@/api/admin/locations/createLocation';
|
||||||
|
import getLocations from '@/api/admin/locations/getLocations';
|
||||||
|
|
||||||
|
interface Values {
|
||||||
|
short: string,
|
||||||
|
long: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
const schema = object().shape({
|
||||||
|
short: string()
|
||||||
|
.required('A location short name must be provided.')
|
||||||
|
.max(32, 'Location short name must not exceed 32 characters.'),
|
||||||
|
long: string()
|
||||||
|
.max(255, 'Location long name must not exceed 255 characters.'),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
const [ visible, setVisible ] = useState(false);
|
||||||
|
const { clearFlashes, clearAndAddHttpError } = useFlash();
|
||||||
|
const { mutate } = getLocations();
|
||||||
|
|
||||||
|
const submit = ({ short, long }: Values, { setSubmitting }: FormikHelpers<Values>) => {
|
||||||
|
clearFlashes('location:create');
|
||||||
|
setSubmitting(true);
|
||||||
|
|
||||||
|
createLocation(short, long)
|
||||||
|
.then(location => {
|
||||||
|
mutate(data => ({ ...data, items: data.items.concat(location) }), false);
|
||||||
|
setVisible(false);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
clearAndAddHttpError(error);
|
||||||
|
setSubmitting(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Formik
|
||||||
|
onSubmit={submit}
|
||||||
|
initialValues={{ short: '', long: '' }}
|
||||||
|
validationSchema={schema}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
({ isSubmitting, resetForm }) => (
|
||||||
|
<Modal
|
||||||
|
visible={visible}
|
||||||
|
dismissable={!isSubmitting}
|
||||||
|
showSpinnerOverlay={isSubmitting}
|
||||||
|
onDismissed={() => {
|
||||||
|
resetForm();
|
||||||
|
setVisible(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FlashMessageRender byKey={'location:create'} css={tw`mb-6`}/>
|
||||||
|
|
||||||
|
<h2 css={tw`text-neutral-100 text-2xl mb-6`}>New Location</h2>
|
||||||
|
|
||||||
|
<Form css={tw`m-0`}>
|
||||||
|
<Field
|
||||||
|
type={'string'}
|
||||||
|
id={'short'}
|
||||||
|
name={'short'}
|
||||||
|
label={'Short'}
|
||||||
|
description={'A short name used to identify this location.'}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div css={tw`mt-6`}>
|
||||||
|
<Field
|
||||||
|
type={'string'}
|
||||||
|
id={'long'}
|
||||||
|
name={'long'}
|
||||||
|
label={'Long'}
|
||||||
|
description={'A long name for this location.'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div css={tw`flex flex-wrap justify-end mt-6`}>
|
||||||
|
<Button
|
||||||
|
type={'button'}
|
||||||
|
isSecondary
|
||||||
|
css={tw`w-full sm:w-auto sm:mr-2`}
|
||||||
|
onClick={() => setVisible(false)}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button css={tw`w-full mt-4 sm:w-auto sm:mt-0`} type={'submit'}>
|
||||||
|
Create Location
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</Formik>
|
||||||
|
|
||||||
|
<Button type={'button'} size={'large'} css={tw`h-10 ml-auto px-4 py-0`} onClick={() => setVisible(true)}>
|
||||||
|
New Location
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
|
@ -62,7 +62,9 @@ export default () => {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FlashMessageRender byKey={'nest:create'} css={tw`mb-6`}/>
|
<FlashMessageRender byKey={'nest:create'} css={tw`mb-6`}/>
|
||||||
|
|
||||||
<h2 css={tw`text-neutral-100 text-2xl mb-6`}>New Nest</h2>
|
<h2 css={tw`text-neutral-100 text-2xl mb-6`}>New Nest</h2>
|
||||||
|
|
||||||
<Form css={tw`m-0`}>
|
<Form css={tw`m-0`}>
|
||||||
<Field
|
<Field
|
||||||
type={'string'}
|
type={'string'}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import React from 'react';
|
||||||
|
import tw from 'twin.macro';
|
||||||
|
import AdminContentBlock from '@/components/admin/AdminContentBlock';
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
return (
|
||||||
|
<AdminContentBlock>
|
||||||
|
<div css={tw`w-full flex flex-row items-center mb-8`}>
|
||||||
|
<div css={tw`flex flex-col`}>
|
||||||
|
<h2 css={tw`text-2xl text-neutral-50 font-header font-medium`}>Create Node</h2>
|
||||||
|
<p css={tw`text-base text-neutral-400`}>Add a new node to the panel.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AdminContentBlock>
|
||||||
|
);
|
||||||
|
};
|
|
@ -69,9 +69,11 @@ const NodesContainer = () => {
|
||||||
<p css={tw`text-base text-neutral-400`}>All nodes available on the system.</p>
|
<p css={tw`text-base text-neutral-400`}>All nodes available on the system.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button type={'button'} size={'large'} css={tw`h-10 ml-auto px-4 py-0`}>
|
<NavLink to={`${match.url}/new`} css={tw`ml-auto`}>
|
||||||
New Node
|
<Button type={'button'} size={'large'} css={tw`h-10 px-4 py-0`}>
|
||||||
</Button>
|
New Node
|
||||||
|
</Button>
|
||||||
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FlashMessageRender byKey={'nodes'} css={tw`mb-4`}/>
|
<FlashMessageRender byKey={'nodes'} css={tw`mb-4`}/>
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import React from 'react';
|
||||||
|
import tw from 'twin.macro';
|
||||||
|
import AdminContentBlock from '@/components/admin/AdminContentBlock';
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
return (
|
||||||
|
<AdminContentBlock>
|
||||||
|
<div css={tw`w-full flex flex-row items-center mb-8`}>
|
||||||
|
<div css={tw`flex flex-col`}>
|
||||||
|
<h2 css={tw`text-2xl text-neutral-50 font-header font-medium`}>Create User</h2>
|
||||||
|
<p css={tw`text-base text-neutral-400`}>Add a new user to the panel.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AdminContentBlock>
|
||||||
|
);
|
||||||
|
};
|
|
@ -68,9 +68,11 @@ const UsersContainer = () => {
|
||||||
<p css={tw`text-base text-neutral-400`}>All registered users on the system.</p>
|
<p css={tw`text-base text-neutral-400`}>All registered users on the system.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button type={'button'} size={'large'} css={tw`h-10 ml-auto px-4 py-0`}>
|
<NavLink to={`${match.url}/new`} css={tw`ml-auto`}>
|
||||||
New User
|
<Button type={'button'} size={'large'} css={tw`h-10 px-4 py-0`}>
|
||||||
</Button>
|
New User
|
||||||
|
</Button>
|
||||||
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FlashMessageRender byKey={'users'} css={tw`mb-4`}/>
|
<FlashMessageRender byKey={'users'} css={tw`mb-4`}/>
|
||||||
|
|
|
@ -10,11 +10,14 @@ import OverviewContainer from '@/components/admin/overview/OverviewContainer';
|
||||||
import SettingsContainer from '@/components/admin/settings/SettingsContainer';
|
import SettingsContainer from '@/components/admin/settings/SettingsContainer';
|
||||||
import ApiKeysContainer from '@/components/admin/api/ApiKeysContainer';
|
import ApiKeysContainer from '@/components/admin/api/ApiKeysContainer';
|
||||||
import DatabasesContainer from '@/components/admin/databases/DatabasesContainer';
|
import DatabasesContainer from '@/components/admin/databases/DatabasesContainer';
|
||||||
|
import NewDatabaseContainer from '@/components/admin/databases/NewDatabaseContainer';
|
||||||
import NodesContainer from '@/components/admin/nodes/NodesContainer';
|
import NodesContainer from '@/components/admin/nodes/NodesContainer';
|
||||||
|
import NewNodeContainer from '@/components/admin/nodes/NewNodeContainer';
|
||||||
import LocationsContainer from '@/components/admin/locations/LocationsContainer';
|
import LocationsContainer from '@/components/admin/locations/LocationsContainer';
|
||||||
import ServersContainer from '@/components/admin/servers/ServersContainer';
|
import ServersContainer from '@/components/admin/servers/ServersContainer';
|
||||||
import NewServerContainer from '@/components/admin/servers/NewServerContainer';
|
import NewServerContainer from '@/components/admin/servers/NewServerContainer';
|
||||||
import UsersContainer from '@/components/admin/users/UsersContainer';
|
import UsersContainer from '@/components/admin/users/UsersContainer';
|
||||||
|
import NewUserContainer from '@/components/admin/users/NewUserContainer';
|
||||||
import RolesContainer from '@/components/admin/roles/RolesContainer';
|
import RolesContainer from '@/components/admin/roles/RolesContainer';
|
||||||
import RoleEditContainer from '@/components/admin/roles/RoleEditContainer';
|
import RoleEditContainer from '@/components/admin/roles/RoleEditContainer';
|
||||||
import NestsContainer from '@/components/admin/nests/NestsContainer';
|
import NestsContainer from '@/components/admin/nests/NestsContainer';
|
||||||
|
@ -174,13 +177,18 @@ const AdminRouter = ({ location, match }: RouteComponentProps) => {
|
||||||
<Route path={`${match.path}/api`} component={ApiKeysContainer} exact/>
|
<Route path={`${match.path}/api`} component={ApiKeysContainer} exact/>
|
||||||
|
|
||||||
<Route path={`${match.path}/databases`} component={DatabasesContainer} exact/>
|
<Route path={`${match.path}/databases`} component={DatabasesContainer} exact/>
|
||||||
|
<Route path={`${match.path}/databases/new`} component={NewDatabaseContainer} exact/>
|
||||||
|
|
||||||
<Route path={`${match.path}/locations`} component={LocationsContainer} exact/>
|
<Route path={`${match.path}/locations`} component={LocationsContainer} exact/>
|
||||||
|
|
||||||
<Route path={`${match.path}/nodes`} component={NodesContainer} exact/>
|
<Route path={`${match.path}/nodes`} component={NodesContainer} exact/>
|
||||||
|
<Route path={`${match.path}/nodes/new`} component={NewNodeContainer} exact/>
|
||||||
|
|
||||||
<Route path={`${match.path}/servers`} component={ServersContainer} exact/>
|
<Route path={`${match.path}/servers`} component={ServersContainer} exact/>
|
||||||
<Route path={`${match.path}/servers/new`} component={NewServerContainer} exact/>
|
<Route path={`${match.path}/servers/new`} component={NewServerContainer} exact/>
|
||||||
|
|
||||||
<Route path={`${match.path}/users`} component={UsersContainer} exact/>
|
<Route path={`${match.path}/users`} component={UsersContainer} exact/>
|
||||||
|
<Route path={`${match.path}/users/new`} component={NewUserContainer} exact/>
|
||||||
|
|
||||||
<Route path={`${match.path}/roles`} component={RolesContainer} exact/>
|
<Route path={`${match.path}/roles`} component={RolesContainer} exact/>
|
||||||
<Route
|
<Route
|
||||||
|
|
Loading…
Reference in New Issue