Support much better server querying from frontend

Search all servers if making a query as an admin, allow searching by a more complex set of data, fix unfocus on search field when loading indicator was rendered
This commit is contained in:
Dane Everitt 2020-10-15 21:21:38 -07:00
parent 9726a0de46
commit f30dab053b
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
6 changed files with 152 additions and 75 deletions

View file

@ -47,23 +47,21 @@ const SearchWatcher = () => {
export default ({ ...props }: Props) => {
const ref = useRef<HTMLInputElement>(null);
const [ loading, setLoading ] = useState(false);
const [ servers, setServers ] = useState<Server[]>([]);
const isAdmin = useStoreState(state => state.user.data!.rootAdmin);
const { addError, clearFlashes } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
const [ servers, setServers ] = useState<Server[]>([]);
const { clearAndAddHttpError, clearFlashes } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
const search = debounce(({ term }: Values, { setSubmitting }: FormikHelpers<Values>) => {
setLoading(true);
setSubmitting(false);
clearFlashes('search');
getServers({ query: term })
// if (ref.current) ref.current.focus();
getServers({ query: term, type: isAdmin ? 'admin-all' : undefined })
.then(servers => setServers(servers.items.filter((_, index) => index < 5)))
.catch(error => {
console.error(error);
addError({ key: 'search', message: httpErrorToHuman(error) });
clearAndAddHttpError({ key: 'search', error });
})
.then(() => setLoading(false))
.then(() => setSubmitting(false))
.then(() => ref.current?.focus());
}, 500);
@ -74,7 +72,7 @@ export default ({ ...props }: Props) => {
}, [ props.visible ]);
// Formik does not support an innerRef on custom components.
const InputWithRef = (props: any) => <Input {...props} ref={ref}/>;
const InputWithRef = (props: any) => <Input autoFocus {...props} ref={ref}/>;
return (
<Formik
@ -84,53 +82,51 @@ export default ({ ...props }: Props) => {
})}
initialValues={{ term: '' } as Values}
>
<Modal {...props}>
<Form>
<FormikFieldWrapper
name={'term'}
label={'Search term'}
description={
isAdmin
? 'Enter a server name, user email, or uuid to begin searching.'
: 'Enter a server name to begin searching.'
}
>
<SearchWatcher/>
<InputSpinner visible={loading}>
<Field as={InputWithRef} name={'term'}/>
</InputSpinner>
</FormikFieldWrapper>
</Form>
{servers.length > 0 &&
<div css={tw`mt-6`}>
{
servers.map(server => (
<ServerResult
key={server.uuid}
to={`/server/${server.id}`}
onClick={() => props.onDismissed()}
>
<div>
<p css={tw`text-sm`}>{server.name}</p>
<p css={tw`mt-1 text-xs text-neutral-400`}>
{
server.allocations.filter(alloc => alloc.isDefault).map(allocation => (
<span key={allocation.ip + allocation.port.toString()}>{allocation.alias || allocation.ip}:{allocation.port}</span>
))
}
</p>
</div>
<div css={tw`flex-1 text-right`}>
{({ isSubmitting }) => (
<Modal {...props}>
<Form>
<FormikFieldWrapper
name={'term'}
label={'Search term'}
description={'Enter a server name, uuid, or allocation to begin searching.'}
>
<SearchWatcher/>
<InputSpinner visible={isSubmitting}>
<Field as={InputWithRef} name={'term'}/>
</InputSpinner>
</FormikFieldWrapper>
</Form>
{servers.length > 0 &&
<div css={tw`mt-6`}>
{
servers.map(server => (
<ServerResult
key={server.uuid}
to={`/server/${server.id}`}
onClick={() => props.onDismissed()}
>
<div>
<p css={tw`text-sm`}>{server.name}</p>
<p css={tw`mt-1 text-xs text-neutral-400`}>
{
server.allocations.filter(alloc => alloc.isDefault).map(allocation => (
<span key={allocation.ip + allocation.port.toString()}>{allocation.alias || allocation.ip}:{allocation.port}</span>
))
}
</p>
</div>
<div css={tw`flex-1 text-right`}>
<span css={tw`text-xs py-1 px-2 bg-cyan-800 text-cyan-100 rounded`}>
{server.node}
</span>
</div>
</ServerResult>
))
</div>
</ServerResult>
))
}
</div>
}
</div>
}
</Modal>
</Modal>
)}
</Formik>
);
};