ui(admin): add user editing

This commit is contained in:
Matthew Penner 2021-09-12 17:00:22 -06:00
parent c716be263b
commit fc2f8744da
No known key found for this signature in database
GPG key ID: 030E4AB751DC756F
5 changed files with 71 additions and 122 deletions

View file

@ -2,7 +2,7 @@ import React 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 FlashMessageRender from '@/components/FlashMessageRender'; import FlashMessageRender from '@/components/FlashMessageRender';
import { InformationContainer } from '@/components/admin/users/UserEditContainer'; import UserForm from '@/components/admin/users/UserForm';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { Actions, useStoreActions } from 'easy-peasy'; import { Actions, useStoreActions } from 'easy-peasy';
import { ApplicationStore } from '@/state'; import { ApplicationStore } from '@/state';
@ -37,7 +37,7 @@ export default () => {
<FlashMessageRender byKey={'user:create'} css={tw`mb-4`}/> <FlashMessageRender byKey={'user:create'} css={tw`mb-4`}/>
<InformationContainer title={'Create User'} onSubmit={submit} role={null}/> <UserForm title={'Create User'} onSubmit={submit} role={null}/>
</AdminContentBlock> </AdminContentBlock>
); );
}; };

View file

@ -0,0 +1,61 @@
import updateUser, { Values } from '@/api/admin/users/updateUser';
import UserDeleteButton from '@/components/admin/users/UserDeleteButton';
import UserForm from '@/components/admin/users/UserForm';
import { Context } from '@/components/admin/users/UserRouter';
import { ApplicationStore } from '@/state';
import { Actions, useStoreActions } from 'easy-peasy';
import { FormikHelpers } from 'formik';
import React from 'react';
import { useHistory } from 'react-router-dom';
import tw from 'twin.macro';
const UserAboutContainer = () => {
const history = useHistory();
const { clearFlashes, clearAndAddHttpError } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
const user = Context.useStoreState(state => state.user);
const setUser = Context.useStoreActions(actions => actions.setUser);
if (user === undefined) {
return (
<></>
);
}
const submit = (values: Values, { setSubmitting }: FormikHelpers<Values>) => {
clearFlashes('user');
updateUser(user.id, values)
.then(() => setUser({ ...user, ...values }))
.catch(error => {
console.error(error);
clearAndAddHttpError({ key: 'user', error });
})
.then(() => setSubmitting(false));
};
return (
<UserForm
title={'Edit User'}
initialValues={{
username: user.username,
email: user.email,
adminRoleId: user.adminRoleId,
password: '',
}}
onSubmit={submit}
role={user?.relationships.role || null}
exists
>
<div css={tw`flex`}>
<UserDeleteButton
userId={user.id}
onDeleted={() => history.push('/admin/users')}
/>
</div>
</UserForm>
);
};
export default UserAboutContainer;

View file

@ -1,23 +1,16 @@
import React, { useEffect, useState } from 'react'; import React from 'react';
import tw from 'twin.macro'; import tw from 'twin.macro';
import { useHistory, useRouteMatch } from 'react-router-dom'; import { action, Action, createContextStore } from 'easy-peasy';
import { action, Action, Actions, createContextStore, useStoreActions } from 'easy-peasy';
import { User } from '@/api/admin/users/getUsers'; import { User } from '@/api/admin/users/getUsers';
import getUser from '@/api/admin/users/getUser';
import AdminContentBlock from '@/components/admin/AdminContentBlock';
import Spinner from '@/components/elements/Spinner';
import FlashMessageRender from '@/components/FlashMessageRender';
import { ApplicationStore } from '@/state';
import AdminBox from '@/components/admin/AdminBox'; import AdminBox from '@/components/admin/AdminBox';
import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
import { Form, Formik, FormikHelpers } from 'formik'; import { Form, Formik, FormikHelpers } from 'formik';
import { object, string } from 'yup'; import { object, string } from 'yup';
import { Role } from '@/api/admin/roles/getRoles'; import { Role } from '@/api/admin/roles/getRoles';
import updateUser, { Values } from '@/api/admin/users/updateUser'; import { Values } from '@/api/admin/users/updateUser';
import Button from '@/components/elements/Button'; import Button from '@/components/elements/Button';
import Field from '@/components/elements/Field'; import Field from '@/components/elements/Field';
import RoleSelect from '@/components/admin/users/RoleSelect'; import RoleSelect from '@/components/admin/users/RoleSelect';
import UserDeleteButton from '@/components/admin/users/UserDeleteButton';
interface ctx { interface ctx {
user: User | undefined; user: User | undefined;
@ -43,7 +36,7 @@ export interface Params {
role: Role | null; role: Role | null;
} }
export function InformationContainer ({ title, initialValues, children, onSubmit, exists, role }: Params) { export default function UserForm ({ title, initialValues, children, onSubmit, exists, role }: Params) {
const submit = (values: Values, helpers: FormikHelpers<Values>) => { const submit = (values: Values, helpers: FormikHelpers<Values>) => {
onSubmit(values, helpers); onSubmit(values, helpers);
}; };
@ -127,109 +120,3 @@ export function InformationContainer ({ title, initialValues, children, onSubmit
</Formik> </Formik>
); );
} }
function EditInformationContainer () {
const history = useHistory();
const { clearFlashes, clearAndAddHttpError } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
const user = Context.useStoreState(state => state.user);
const setUser = Context.useStoreActions(actions => actions.setUser);
if (user === undefined) {
return (
<></>
);
}
const submit = (values: Values, { setSubmitting }: FormikHelpers<Values>) => {
clearFlashes('user');
updateUser(user.id, values)
.then(() => setUser({ ...user, ...values }))
.catch(error => {
console.error(error);
clearAndAddHttpError({ key: 'user', error });
})
.then(() => setSubmitting(false));
};
return (
<InformationContainer
title={'Edit User'}
initialValues={{
username: user.username,
email: user.email,
adminRoleId: user.adminRoleId,
password: '',
}}
onSubmit={submit}
role={user?.relationships.role || null}
exists
>
<div css={tw`flex`}>
<UserDeleteButton
userId={user.id}
onDeleted={() => history.push('/admin/users')}
/>
</div>
</InformationContainer>
);
}
function UserEditContainer () {
const match = useRouteMatch<{ id?: string }>();
const { clearFlashes, clearAndAddHttpError } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
const [ loading, setLoading ] = useState(true);
const user = Context.useStoreState(state => state.user);
const setUser = Context.useStoreActions(actions => actions.setUser);
useEffect(() => {
clearFlashes('user');
getUser(Number(match.params?.id), [ 'role' ])
.then(user => setUser(user))
.catch(error => {
console.error(error);
clearAndAddHttpError({ key: 'user', error });
})
.then(() => setLoading(false));
}, []);
if (loading || user === undefined) {
return (
<AdminContentBlock>
<FlashMessageRender byKey={'user'} css={tw`mb-4`}/>
<div css={tw`w-full flex flex-col items-center justify-center`} style={{ height: '24rem' }}>
<Spinner size={'base'}/>
</div>
</AdminContentBlock>
);
}
return (
<AdminContentBlock title={'User - ' + user.id}>
<div css={tw`w-full flex flex-row items-center mb-8`}>
<div css={tw`flex flex-col flex-shrink`} style={{ minWidth: '0' }}>
<h2 css={tw`text-2xl text-neutral-50 font-header font-medium`}>{user.email}</h2>
<p css={tw`text-base text-neutral-400 whitespace-nowrap overflow-ellipsis overflow-hidden`}>{user.uuid}</p>
</div>
</div>
<FlashMessageRender byKey={'user'} css={tw`mb-4`}/>
<EditInformationContainer/>
</AdminContentBlock>
);
}
export default () => {
return (
<Context.Provider>
<UserEditContainer/>
</Context.Provider>
);
};

View file

@ -1,3 +1,4 @@
import UserAboutContainer from '@/components/admin/users/UserAboutContainer';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useLocation } from 'react-router'; import { useLocation } from 'react-router';
import tw from 'twin.macro'; import tw from 'twin.macro';
@ -38,7 +39,7 @@ const UserRouter = () => {
useEffect(() => { useEffect(() => {
clearFlashes('user'); clearFlashes('user');
getUser(Number(match.params?.id), [ 'database_host', 'location' ]) getUser(Number(match.params?.id), [ 'role' ])
.then(user => setUser(user)) .then(user => setUser(user))
.catch(error => { .catch(error => {
console.error(error); console.error(error);
@ -86,7 +87,7 @@ const UserRouter = () => {
<Switch location={location}> <Switch location={location}>
<Route path={`${match.path}`} exact> <Route path={`${match.path}`} exact>
<p>About</p> <UserAboutContainer/>
</Route> </Route>
<Route path={`${match.path}/servers`} exact> <Route path={`${match.path}/servers`} exact>

View file

@ -6,7 +6,7 @@ function UserServers () {
const user = Context.useStoreState(state => state.user); const user = Context.useStoreState(state => state.user);
return ( return (
<ServersTable filters={{ owner_id: user?.id?.toString() }}/> <ServersTable filters={{ owner_id: user?.id?.toString?.() }}/>
); );
} }