React 18 and Vite (#4510)
This commit is contained in:
parent
1bb1b13f6d
commit
21613fa602
244 changed files with 4547 additions and 8933 deletions
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import ContentBox from '@/components/elements/ContentBox';
|
||||
import CreateApiKeyForm from '@/components/dashboard/forms/CreateApiKeyForm';
|
||||
import getApiKeys, { ApiKey } from '@/api/account/getApiKeys';
|
||||
|
@ -23,9 +23,9 @@ export default () => {
|
|||
|
||||
useEffect(() => {
|
||||
getApiKeys()
|
||||
.then((keys) => setKeys(keys))
|
||||
.then(keys => setKeys(keys))
|
||||
.then(() => setLoading(false))
|
||||
.catch((error) => clearAndAddHttpError(error));
|
||||
.catch(error => clearAndAddHttpError(error));
|
||||
}, []);
|
||||
|
||||
const doDeletion = (identifier: string) => {
|
||||
|
@ -33,8 +33,8 @@ export default () => {
|
|||
|
||||
clearAndAddHttpError();
|
||||
deleteApiKey(identifier)
|
||||
.then(() => setKeys((s) => [...(s || []).filter((key) => key.identifier !== identifier)]))
|
||||
.catch((error) => clearAndAddHttpError(error))
|
||||
.then(() => setKeys(s => [...(s || []).filter(key => key.identifier !== identifier)]))
|
||||
.catch(error => clearAndAddHttpError(error))
|
||||
.then(() => {
|
||||
setLoading(false);
|
||||
setDeleteIdentifier('');
|
||||
|
@ -46,7 +46,7 @@ export default () => {
|
|||
<FlashMessageRender byKey={'account'} />
|
||||
<div css={tw`md:flex flex-nowrap my-10`}>
|
||||
<ContentBox title={'Create API Key'} css={tw`flex-none w-full md:w-1/2`}>
|
||||
<CreateApiKeyForm onKeyCreated={(key) => setKeys((s) => [...s!, key])} />
|
||||
<CreateApiKeyForm onKeyCreated={key => setKeys(s => [...s!, key])} />
|
||||
</ContentBox>
|
||||
<ContentBox title={'API Keys'} css={tw`flex-1 overflow-hidden mt-8 md:mt-0 md:ml-8`}>
|
||||
<SpinnerOverlay visible={loading} />
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import * as React from 'react';
|
||||
import ContentBox from '@/components/elements/ContentBox';
|
||||
import UpdatePasswordForm from '@/components/dashboard/forms/UpdatePasswordForm';
|
||||
import UpdateEmailAddressForm from '@/components/dashboard/forms/UpdateEmailAddressForm';
|
||||
|
@ -6,7 +5,7 @@ import ConfigureTwoFactorForm from '@/components/dashboard/forms/ConfigureTwoFac
|
|||
import PageContentBlock from '@/components/elements/PageContentBlock';
|
||||
import tw from 'twin.macro';
|
||||
import { breakpoint } from '@/theme';
|
||||
import styled from 'styled-components/macro';
|
||||
import styled from 'styled-components';
|
||||
import MessageBox from '@/components/MessageBox';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
|
@ -27,24 +26,26 @@ const Container = styled.div`
|
|||
`;
|
||||
|
||||
export default () => {
|
||||
const { state } = useLocation<undefined | { twoFactorRedirect?: boolean }>();
|
||||
const { state } = useLocation();
|
||||
|
||||
return (
|
||||
<PageContentBlock title={'Account Overview'}>
|
||||
<PageContentBlock title="Account Overview">
|
||||
{state?.twoFactorRedirect && (
|
||||
<MessageBox title={'2-Factor Required'} type={'error'}>
|
||||
<MessageBox title="2-Factor Required" type="error">
|
||||
Your account must have two-factor authentication enabled in order to continue.
|
||||
</MessageBox>
|
||||
)}
|
||||
|
||||
<Container css={[tw`lg:grid lg:grid-cols-3 mb-10`, state?.twoFactorRedirect ? tw`mt-4` : tw`mt-10`]}>
|
||||
<ContentBox title={'Update Password'} showFlashes={'account:password'}>
|
||||
<ContentBox title="Update Password" showFlashes="account:password">
|
||||
<UpdatePasswordForm />
|
||||
</ContentBox>
|
||||
<ContentBox css={tw`mt-8 sm:mt-0 sm:ml-8`} title={'Update Email Address'} showFlashes={'account:email'}>
|
||||
|
||||
<ContentBox css={tw`mt-8 sm:mt-0 sm:ml-8`} title="Update Email Address" showFlashes="account:email">
|
||||
<UpdateEmailAddressForm />
|
||||
</ContentBox>
|
||||
<ContentBox css={tw`md:ml-8 mt-8 md:mt-0`} title={'Two-Step Verification'}>
|
||||
|
||||
<ContentBox css={tw`md:ml-8 mt-8 md:mt-0`} title="Two-Step Verification">
|
||||
<ConfigureTwoFactorForm />
|
||||
</ContentBox>
|
||||
</Container>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useContext } from 'react';
|
||||
import { useContext } from 'react';
|
||||
import tw from 'twin.macro';
|
||||
import Button from '@/components/elements/Button';
|
||||
import asModal from '@/hoc/asModal';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Server } from '@/api/server/getServer';
|
||||
import getServers from '@/api/getServers';
|
||||
import ServerRow from '@/components/dashboard/ServerRow';
|
||||
|
@ -20,13 +20,13 @@ export default () => {
|
|||
|
||||
const [page, setPage] = useState(!isNaN(defaultPage) && defaultPage > 0 ? defaultPage : 1);
|
||||
const { clearFlashes, clearAndAddHttpError } = useFlash();
|
||||
const uuid = useStoreState((state) => state.user.data!.uuid);
|
||||
const rootAdmin = useStoreState((state) => state.user.data!.rootAdmin);
|
||||
const uuid = useStoreState(state => state.user.data!.uuid);
|
||||
const rootAdmin = useStoreState(state => state.user.data!.rootAdmin);
|
||||
const [showOnlyAdmin, setShowOnlyAdmin] = usePersistedState(`${uuid}:show_all_servers`, false);
|
||||
|
||||
const { data: servers, error } = useSWR<PaginatedResult<Server>>(
|
||||
['/api/client/servers', showOnlyAdmin && rootAdmin, page],
|
||||
() => getServers({ page, type: showOnlyAdmin && rootAdmin ? 'admin' : undefined })
|
||||
() => getServers({ page, type: showOnlyAdmin && rootAdmin ? 'admin' : undefined }),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -58,7 +58,7 @@ export default () => {
|
|||
<Switch
|
||||
name={'show_all_servers'}
|
||||
defaultChecked={showOnlyAdmin}
|
||||
onChange={() => setShowOnlyAdmin((s) => !s)}
|
||||
onChange={() => setShowOnlyAdmin(s => !s)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React, { memo, useEffect, useRef, useState } from 'react';
|
||||
import { memo, useEffect, useRef, useState } from 'react';
|
||||
import * as React from 'react';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faEthernet, faHdd, faMemory, faMicrochip, faServer } from '@fortawesome/free-solid-svg-icons';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
@ -8,7 +9,7 @@ import { bytesToString, ip, mbToBytes } from '@/lib/formatters';
|
|||
import tw from 'twin.macro';
|
||||
import GreyRowBox from '@/components/elements/GreyRowBox';
|
||||
import Spinner from '@/components/elements/Spinner';
|
||||
import styled from 'styled-components/macro';
|
||||
import styled from 'styled-components';
|
||||
import isEqual from 'react-fast-compare';
|
||||
|
||||
// Determines if the current value is in an alarm threshold so we can show it in red rather
|
||||
|
@ -17,14 +18,14 @@ const isAlarmState = (current: number, limit: number): boolean => limit > 0 && c
|
|||
|
||||
const Icon = memo(
|
||||
styled(FontAwesomeIcon)<{ $alarm: boolean }>`
|
||||
${(props) => (props.$alarm ? tw`text-red-400` : tw`text-neutral-500`)};
|
||||
${props => (props.$alarm ? tw`text-red-400` : tw`text-neutral-500`)};
|
||||
`,
|
||||
isEqual
|
||||
isEqual,
|
||||
);
|
||||
|
||||
const IconDescription = styled.p<{ $alarm: boolean }>`
|
||||
${tw`text-sm ml-2`};
|
||||
${(props) => (props.$alarm ? tw`text-white` : tw`text-neutral-400`)};
|
||||
${props => (props.$alarm ? tw`text-white` : tw`text-neutral-400`)};
|
||||
`;
|
||||
|
||||
const StatusIndicatorBox = styled(GreyRowBox)<{ $status: ServerPowerState | undefined }>`
|
||||
|
@ -56,8 +57,8 @@ export default ({ server, className }: { server: Server; className?: string }) =
|
|||
|
||||
const getStats = () =>
|
||||
getServerResourceUsage(server.uuid)
|
||||
.then((data) => setStats(data))
|
||||
.catch((error) => console.error(error));
|
||||
.then(data => setStats(data))
|
||||
.catch(error => console.error(error));
|
||||
|
||||
useEffect(() => {
|
||||
setIsSuspended(stats?.isSuspended || server.status === 'suspended');
|
||||
|
@ -106,8 +107,8 @@ export default ({ server, className }: { server: Server; className?: string }) =
|
|||
<FontAwesomeIcon icon={faEthernet} css={tw`text-neutral-500`} />
|
||||
<p css={tw`text-sm text-neutral-400 ml-2`}>
|
||||
{server.allocations
|
||||
.filter((alloc) => alloc.isDefault)
|
||||
.map((allocation) => (
|
||||
.filter(alloc => alloc.isDefault)
|
||||
.map(allocation => (
|
||||
<React.Fragment key={allocation.ip + allocation.port.toString()}>
|
||||
{allocation.alias || ip(allocation.ip)}:{allocation.port}
|
||||
</React.Fragment>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { ActivityLogFilters, useActivityLogs } from '@/api/account/activity';
|
||||
import { useFlashKey } from '@/plugins/useFlash';
|
||||
import PageContentBlock from '@/components/elements/PageContentBlock';
|
||||
|
@ -23,7 +23,7 @@ export default () => {
|
|||
});
|
||||
|
||||
useEffect(() => {
|
||||
setFilters((value) => ({ ...value, filters: { ip: hash.ip, event: hash.event } }));
|
||||
setFilters(value => ({ ...value, filters: { ip: hash.ip, event: hash.event } }));
|
||||
}, [hash]);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -38,7 +38,7 @@ export default () => {
|
|||
<Link
|
||||
to={'#'}
|
||||
className={classNames(btnStyles.button, btnStyles.text, 'w-full sm:w-auto')}
|
||||
onClick={() => setFilters((value) => ({ ...value, filters: {} }))}
|
||||
onClick={() => setFilters(value => ({ ...value, filters: {} }))}
|
||||
>
|
||||
Clear Filters <XCircleIcon className={'w-4 h-4 ml-2'} />
|
||||
</Link>
|
||||
|
@ -48,7 +48,7 @@ export default () => {
|
|||
<Spinner centered />
|
||||
) : (
|
||||
<div className={'bg-gray-700'}>
|
||||
{data?.items.map((activity) => (
|
||||
{data?.items.map(activity => (
|
||||
<ActivityLogEntry key={activity.id} activity={activity}>
|
||||
{typeof activity.properties.useragent === 'string' && (
|
||||
<Tooltip content={activity.properties.useragent} placement={'top'}>
|
||||
|
@ -64,7 +64,7 @@ export default () => {
|
|||
{data && (
|
||||
<PaginationFooter
|
||||
pagination={data.pagination}
|
||||
onPageSelect={(page) => setFilters((value) => ({ ...value, page }))}
|
||||
onPageSelect={page => setFilters(value => ({ ...value, page }))}
|
||||
/>
|
||||
)}
|
||||
</PageContentBlock>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useStoreState } from 'easy-peasy';
|
||||
import { ApplicationStore } from '@/state';
|
||||
import tw from 'twin.macro';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { Field, Form, Formik, FormikHelpers } from 'formik';
|
||||
import { object, string } from 'yup';
|
||||
import FormikFieldWrapper from '@/components/elements/FormikFieldWrapper';
|
||||
|
@ -11,7 +11,7 @@ import { ApiKey } from '@/api/account/getApiKeys';
|
|||
import tw from 'twin.macro';
|
||||
import Button from '@/components/elements/Button';
|
||||
import Input, { Textarea } from '@/components/elements/Input';
|
||||
import styled from 'styled-components/macro';
|
||||
import styled from 'styled-components';
|
||||
import ApiKeyModal from '@/components/dashboard/ApiKeyModal';
|
||||
|
||||
interface Values {
|
||||
|
@ -36,7 +36,7 @@ export default ({ onKeyCreated }: { onKeyCreated: (key: ApiKey) => void }) => {
|
|||
setApiKey(`${key.identifier}${secretToken}`);
|
||||
onKeyCreated(key);
|
||||
})
|
||||
.catch((error) => {
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
|
||||
addError({ key: 'account', message: httpErrorToHuman(error) });
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
import * as React from 'react';
|
||||
import asDialog from '@/hoc/asDialog';
|
||||
import { Dialog, DialogWrapperContext } from '@/components/elements/dialog';
|
||||
import { Button } from '@/components/elements/button/index';
|
||||
|
@ -14,10 +15,10 @@ const DisableTOTPDialog = () => {
|
|||
const [password, setPassword] = useState('');
|
||||
const { clearAndAddHttpError } = useFlashKey('account:two-step');
|
||||
const { close, setProps } = useContext(DialogWrapperContext);
|
||||
const updateUserData = useStoreActions((actions) => actions.user.updateUserData);
|
||||
const updateUserData = useStoreActions(actions => actions.user.updateUserData);
|
||||
|
||||
useEffect(() => {
|
||||
setProps((state) => ({ ...state, preventExternalClose: submitting }));
|
||||
setProps(state => ({ ...state, preventExternalClose: submitting }));
|
||||
}, [submitting]);
|
||||
|
||||
const submit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
|
@ -48,7 +49,7 @@ const DisableTOTPDialog = () => {
|
|||
type={'password'}
|
||||
variant={Input.Text.Variants.Loose}
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.currentTarget.value)}
|
||||
onChange={e => setPassword(e.currentTarget.value)}
|
||||
/>
|
||||
<Dialog.Footer>
|
||||
<Button.Text onClick={close}>Cancel</Button.Text>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import React from 'react';
|
||||
import { Dialog, DialogProps } from '@/components/elements/dialog';
|
||||
import { Button } from '@/components/elements/button/index';
|
||||
import CopyOnClick from '@/components/elements/CopyOnClick';
|
||||
|
@ -30,7 +29,7 @@ export default ({ tokens, open, onClose }: RecoveryTokenDialogProps) => {
|
|||
<Dialog.Icon position={'container'} type={'success'} />
|
||||
<CopyOnClick text={tokens.join('\n')} showInNotification={false}>
|
||||
<pre className={'bg-gray-800 rounded p-2 mt-6'}>
|
||||
{grouped.map((value) => (
|
||||
{grouped.map(value => (
|
||||
<span key={value.join('_')} className={'block'}>
|
||||
{value[0]}
|
||||
<span className={'mx-2 selection:bg-gray-800'}> </span>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
import * as React from 'react';
|
||||
import { Dialog, DialogWrapperContext } from '@/components/elements/dialog';
|
||||
import getTwoFactorTokenData, { TwoFactorTokenData } from '@/api/account/getTwoFactorTokenData';
|
||||
import { useFlashKey } from '@/plugins/useFlash';
|
||||
|
@ -32,11 +33,11 @@ const ConfigureTwoFactorForm = ({ onTokens }: Props) => {
|
|||
useEffect(() => {
|
||||
getTwoFactorTokenData()
|
||||
.then(setToken)
|
||||
.catch((error) => clearAndAddHttpError(error));
|
||||
.catch(error => clearAndAddHttpError(error));
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setProps((state) => ({ ...state, preventExternalClose: submitting }));
|
||||
setProps(state => ({ ...state, preventExternalClose: submitting }));
|
||||
}, [submitting]);
|
||||
|
||||
const submit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
|
@ -48,11 +49,11 @@ const ConfigureTwoFactorForm = ({ onTokens }: Props) => {
|
|||
setSubmitting(true);
|
||||
clearAndAddHttpError();
|
||||
enableAccountTwoFactor(value, password)
|
||||
.then((tokens) => {
|
||||
.then(tokens => {
|
||||
updateUserData({ useTotp: true });
|
||||
onTokens(tokens);
|
||||
})
|
||||
.catch((error) => {
|
||||
.catch(error => {
|
||||
clearAndAddHttpError(error);
|
||||
setSubmitting(false);
|
||||
});
|
||||
|
@ -81,7 +82,7 @@ const ConfigureTwoFactorForm = ({ onTokens }: Props) => {
|
|||
aria-labelledby={'totp-code-description'}
|
||||
variant={Input.Text.Variants.Loose}
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.currentTarget.value)}
|
||||
onChange={e => setValue(e.currentTarget.value)}
|
||||
className={'mt-3'}
|
||||
placeholder={'000000'}
|
||||
type={'text'}
|
||||
|
@ -97,7 +98,7 @@ const ConfigureTwoFactorForm = ({ onTokens }: Props) => {
|
|||
className={'mt-1'}
|
||||
type={'password'}
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.currentTarget.value)}
|
||||
onChange={e => setPassword(e.currentTarget.value)}
|
||||
/>
|
||||
<Dialog.Footer>
|
||||
<Button.Text onClick={close}>Cancel</Button.Text>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react';
|
||||
import { Fragment } from 'react';
|
||||
import { Actions, State, useStoreActions, useStoreState } from 'easy-peasy';
|
||||
import { Form, Formik, FormikHelpers } from 'formik';
|
||||
import * as Yup from 'yup';
|
||||
|
@ -34,15 +34,15 @@ export default () => {
|
|||
type: 'success',
|
||||
key: 'account:email',
|
||||
message: 'Your primary email has been updated.',
|
||||
})
|
||||
}),
|
||||
)
|
||||
.catch((error) =>
|
||||
.catch(error =>
|
||||
addFlash({
|
||||
type: 'error',
|
||||
key: 'account:email',
|
||||
title: 'Error',
|
||||
message: httpErrorToHuman(error),
|
||||
})
|
||||
}),
|
||||
)
|
||||
.then(() => {
|
||||
resetForm();
|
||||
|
@ -53,7 +53,7 @@ export default () => {
|
|||
return (
|
||||
<Formik onSubmit={submit} validationSchema={schema} initialValues={{ email: user!.email, password: '' }}>
|
||||
{({ isSubmitting, isValid }) => (
|
||||
<React.Fragment>
|
||||
<Fragment>
|
||||
<SpinnerOverlay size={'large'} visible={isSubmitting} />
|
||||
<Form css={tw`m-0`}>
|
||||
<Field id={'current_email'} type={'email'} name={'email'} label={'Email'} />
|
||||
|
@ -69,7 +69,7 @@ export default () => {
|
|||
<Button disabled={isSubmitting || !isValid}>Update Email</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</React.Fragment>
|
||||
</Fragment>
|
||||
)}
|
||||
</Formik>
|
||||
);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react';
|
||||
import { Fragment } from 'react';
|
||||
import { Actions, State, useStoreActions, useStoreState } from 'easy-peasy';
|
||||
import { Form, Formik, FormikHelpers } from 'formik';
|
||||
import Field from '@/components/elements/Field';
|
||||
|
@ -24,7 +24,7 @@ const schema = Yup.object().shape({
|
|||
'Password confirmation does not match the password you entered.',
|
||||
function (value) {
|
||||
return value === this.parent.password;
|
||||
}
|
||||
},
|
||||
),
|
||||
});
|
||||
|
||||
|
@ -43,26 +43,26 @@ export default () => {
|
|||
// @ts-expect-error this is valid
|
||||
window.location = '/auth/login';
|
||||
})
|
||||
.catch((error) =>
|
||||
.catch(error =>
|
||||
addFlash({
|
||||
key: 'account:password',
|
||||
type: 'error',
|
||||
title: 'Error',
|
||||
message: httpErrorToHuman(error),
|
||||
})
|
||||
}),
|
||||
)
|
||||
.then(() => setSubmitting(false));
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Fragment>
|
||||
<Formik
|
||||
onSubmit={submit}
|
||||
validationSchema={schema}
|
||||
initialValues={{ current: '', password: '', confirmPassword: '' }}
|
||||
>
|
||||
{({ isSubmitting, isValid }) => (
|
||||
<React.Fragment>
|
||||
<Fragment>
|
||||
<SpinnerOverlay size={'large'} visible={isSubmitting} />
|
||||
<Form css={tw`m-0`}>
|
||||
<Field
|
||||
|
@ -94,9 +94,9 @@ export default () => {
|
|||
<Button disabled={isSubmitting || !isValid}>Update Password</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</React.Fragment>
|
||||
</Fragment>
|
||||
)}
|
||||
</Formik>
|
||||
</React.Fragment>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faSearch } from '@fortawesome/free-solid-svg-icons';
|
||||
import useEventListener from '@/plugins/useEventListener';
|
||||
|
@ -18,7 +18,8 @@ export default () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
{visible && <SearchModal appear visible={visible} onDismissed={() => setVisible(false)} />}
|
||||
<SearchModal appear visible={visible} onDismissed={() => setVisible(false)} />
|
||||
|
||||
<Tooltip placement={'bottom'} content={'Search'}>
|
||||
<div className={'navigation-link'} onClick={() => setVisible(true)}>
|
||||
<FontAwesomeIcon icon={faSearch} />
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import Modal, { RequiredModalProps } from '@/components/elements/Modal';
|
||||
import { Field, Form, Formik, FormikHelpers, useFormikContext } from 'formik';
|
||||
import { Actions, useStoreActions, useStoreState } from 'easy-peasy';
|
||||
|
@ -10,7 +10,7 @@ import getServers from '@/api/getServers';
|
|||
import { Server } from '@/api/server/getServer';
|
||||
import { ApplicationStore } from '@/state';
|
||||
import { Link } from 'react-router-dom';
|
||||
import styled from 'styled-components/macro';
|
||||
import styled from 'styled-components';
|
||||
import tw from 'twin.macro';
|
||||
import Input from '@/components/elements/Input';
|
||||
import { ip } from '@/lib/formatters';
|
||||
|
@ -47,10 +47,10 @@ const SearchWatcher = () => {
|
|||
|
||||
export default ({ ...props }: Props) => {
|
||||
const ref = useRef<HTMLInputElement>(null);
|
||||
const isAdmin = useStoreState((state) => state.user.data!.rootAdmin);
|
||||
const isAdmin = useStoreState(state => state.user.data!.rootAdmin);
|
||||
const [servers, setServers] = useState<Server[]>([]);
|
||||
const { clearAndAddHttpError, clearFlashes } = useStoreActions(
|
||||
(actions: Actions<ApplicationStore>) => actions.flashes
|
||||
(actions: Actions<ApplicationStore>) => actions.flashes,
|
||||
);
|
||||
|
||||
const search = debounce(({ term }: Values, { setSubmitting }: FormikHelpers<Values>) => {
|
||||
|
@ -58,8 +58,8 @@ export default ({ ...props }: Props) => {
|
|||
|
||||
// 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) => {
|
||||
.then(servers => setServers(servers.items.filter((_, index) => index < 5)))
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
clearAndAddHttpError({ key: 'search', error });
|
||||
})
|
||||
|
@ -100,7 +100,7 @@ export default ({ ...props }: Props) => {
|
|||
</Form>
|
||||
{servers.length > 0 && (
|
||||
<div css={tw`mt-6`}>
|
||||
{servers.map((server) => (
|
||||
{servers.map(server => (
|
||||
<ServerResult
|
||||
key={server.uuid}
|
||||
to={`/server/${server.id}`}
|
||||
|
@ -110,8 +110,8 @@ export default ({ ...props }: Props) => {
|
|||
<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) => (
|
||||
.filter(alloc => alloc.isDefault)
|
||||
.map(allocation => (
|
||||
<span key={allocation.ip + allocation.port.toString()}>
|
||||
{allocation.alias || ip(allocation.ip)}:{allocation.port}
|
||||
</span>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import ContentBox from '@/components/elements/ContentBox';
|
||||
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
|
||||
import FlashMessageRender from '@/components/FlashMessageRender';
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import React from 'react';
|
||||
import { Field, Form, Formik, FormikHelpers } from 'formik';
|
||||
import { object, string } from 'yup';
|
||||
import FormikFieldWrapper from '@/components/elements/FormikFieldWrapper';
|
||||
|
@ -6,7 +5,7 @@ import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
|
|||
import tw from 'twin.macro';
|
||||
import Button from '@/components/elements/Button';
|
||||
import Input, { Textarea } from '@/components/elements/Input';
|
||||
import styled from 'styled-components/macro';
|
||||
import styled from 'styled-components';
|
||||
import { useFlashKey } from '@/plugins/useFlash';
|
||||
import { createSSHKey, useSSHKeys } from '@/api/account/ssh-keys';
|
||||
|
||||
|
@ -27,11 +26,11 @@ export default () => {
|
|||
clearAndAddHttpError();
|
||||
|
||||
createSSHKey(values.name, values.publicKey)
|
||||
.then((key) => {
|
||||
.then(key => {
|
||||
resetForm();
|
||||
mutate((data) => (data || []).concat(key));
|
||||
mutate(data => (data || []).concat(key));
|
||||
})
|
||||
.catch((error) => clearAndAddHttpError(error))
|
||||
.catch(error => clearAndAddHttpError(error))
|
||||
.then(() => setSubmitting(false));
|
||||
};
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import tw from 'twin.macro';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faTrashAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
import React, { useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { useFlashKey } from '@/plugins/useFlash';
|
||||
import { deleteSSHKey, useSSHKeys } from '@/api/account/ssh-keys';
|
||||
import { Dialog } from '@/components/elements/dialog';
|
||||
|
@ -16,9 +16,9 @@ export default ({ name, fingerprint }: { name: string; fingerprint: string }) =>
|
|||
clearAndAddHttpError();
|
||||
|
||||
Promise.all([
|
||||
mutate((data) => data?.filter((value) => value.fingerprint !== fingerprint), false),
|
||||
mutate(data => data?.filter(value => value.fingerprint !== fingerprint), false),
|
||||
deleteSSHKey(fingerprint),
|
||||
]).catch((error) => {
|
||||
]).catch(error => {
|
||||
mutate(undefined, true).catch(console.error);
|
||||
clearAndAddHttpError(error);
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue