diff --git a/resources/scripts/components/App.tsx b/resources/scripts/components/App.tsx index 286261742..dac7fd102 100644 --- a/resources/scripts/components/App.tsx +++ b/resources/scripts/components/App.tsx @@ -12,7 +12,6 @@ import ProgressBar from '@/components/elements/ProgressBar'; import NotFound from '@/components/screens/NotFound'; import tw from 'twin.macro'; import GlobalStylesheet from '@/assets/css/GlobalStylesheet'; -import TransitionRouter from '@/TransitionRouter'; interface ExtendedWindow extends Window { SiteConfiguration?: SiteSettings; diff --git a/resources/scripts/components/NavigationBar.tsx b/resources/scripts/components/NavigationBar.tsx index f8213a34b..206e035c3 100644 --- a/resources/scripts/components/NavigationBar.tsx +++ b/resources/scripts/components/NavigationBar.tsx @@ -7,6 +7,7 @@ import { ApplicationStore } from '@/state'; import SearchContainer from '@/components/dashboard/search/SearchContainer'; import tw from 'twin.macro'; import styled from 'styled-components/macro'; +// @ts-ignore import * as config from '@/../../tailwind.config.js'; const Navigation = styled.div` diff --git a/resources/scripts/components/NetworkErrorMessage.tsx b/resources/scripts/components/NetworkErrorMessage.tsx deleted file mode 100644 index 8d4150f2c..000000000 --- a/resources/scripts/components/NetworkErrorMessage.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import * as React from 'react'; -import MessageBox from '@/components/MessageBox'; - -export default ({ message }: { message: string | undefined | null }) => ( - !message ? - null - : -
- - {message} - -
-); diff --git a/resources/scripts/components/ServerOverviewContainer.tsx b/resources/scripts/components/ServerOverviewContainer.tsx deleted file mode 100644 index 8cc5cf7cb..000000000 --- a/resources/scripts/components/ServerOverviewContainer.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import * as React from 'react'; -import { NavLink } from 'react-router-dom'; - -export default class ServerOverviewContainer extends React.PureComponent { - render () { - return ( -
- Account - Design -
- ); - } -} diff --git a/resources/scripts/components/dashboard/ServerRow.tsx b/resources/scripts/components/dashboard/ServerRow.tsx index 1cfe87d87..63866467c 100644 --- a/resources/scripts/components/dashboard/ServerRow.tsx +++ b/resources/scripts/components/dashboard/ServerRow.tsx @@ -39,6 +39,7 @@ export default ({ server }: { server: Server }) => { useEffect(() => { getStats().then(() => { + // @ts-ignore interval.current = setInterval(() => getStats(), 20000); }); @@ -79,7 +80,7 @@ export default ({ server }: { server: Server }) => {
{!stats ? !statsError ? - + : server.isInstalling ?
diff --git a/resources/scripts/components/elements/PageContentBlock.tsx b/resources/scripts/components/elements/PageContentBlock.tsx index c9a7018f9..6613b6e51 100644 --- a/resources/scripts/components/elements/PageContentBlock.tsx +++ b/resources/scripts/components/elements/PageContentBlock.tsx @@ -3,10 +3,10 @@ import ContentContainer from '@/components/elements/ContentContainer'; import { CSSTransition } from 'react-transition-group'; import tw from 'twin.macro'; -const PageContentBlock: React.FC = ({ children }) => ( +const PageContentBlock: React.FC<{ className?: string }> = ({ children, className }) => ( <> - + {children} diff --git a/resources/scripts/components/elements/SubNavigation.tsx b/resources/scripts/components/elements/SubNavigation.tsx new file mode 100644 index 000000000..83221e935 --- /dev/null +++ b/resources/scripts/components/elements/SubNavigation.tsx @@ -0,0 +1,31 @@ +import styled from 'styled-components/macro'; +import tw from 'twin.macro'; +// @ts-ignore +import config from '../../../../tailwind.config'; + +const SubNavigation = styled.div` + ${tw`w-full bg-neutral-700 shadow`}; + + & > div { + ${tw`flex items-center text-sm mx-auto px-2`}; + max-width: 1200px; + + & > a, & > div { + ${tw`inline-block py-3 px-4 text-neutral-300 no-underline transition-all duration-150`}; + + &:not(:first-of-type) { + ${tw`ml-2`}; + } + + &:active, &:hover { + ${tw`text-neutral-100`}; + } + + &:active, &:hover, &.active { + box-shadow: inset 0 -2px ${config.theme.colors.cyan['500']}; + } + } + } +`; + +export default SubNavigation; diff --git a/resources/scripts/components/elements/SuspenseSpinner.tsx b/resources/scripts/components/elements/SuspenseSpinner.tsx index 870ebc536..fca1e91b2 100644 --- a/resources/scripts/components/elements/SuspenseSpinner.tsx +++ b/resources/scripts/components/elements/SuspenseSpinner.tsx @@ -1,10 +1,11 @@ import React, { Suspense } from 'react'; import Spinner from '@/components/elements/Spinner'; +import tw from 'twin.macro'; const SuspenseSpinner = ({ children }: { children?: React.ReactNode }) => ( +
} diff --git a/resources/scripts/components/elements/TitledGreyBox.tsx b/resources/scripts/components/elements/TitledGreyBox.tsx index 7e5bb1619..3e83f8689 100644 --- a/resources/scripts/components/elements/TitledGreyBox.tsx +++ b/resources/scripts/components/elements/TitledGreyBox.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { IconProp } from '@fortawesome/fontawesome-svg-core'; +import tw from 'twin.macro'; interface Props { icon?: IconProp; @@ -10,17 +11,17 @@ interface Props { } const TitledGreyBox = ({ icon, title, children, className }: Props) => ( -
-
+
+
{typeof title === 'string' ? -

- {icon && }{title} +

+ {icon && }{title}

: title }
-
+
{children}
diff --git a/resources/scripts/components/screens/ScreenBlock.tsx b/resources/scripts/components/screens/ScreenBlock.tsx index f20836493..90a39e4dd 100644 --- a/resources/scripts/components/screens/ScreenBlock.tsx +++ b/resources/scripts/components/screens/ScreenBlock.tsx @@ -1,10 +1,9 @@ import React from 'react'; import PageContentBlock from '@/components/elements/PageContentBlock'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faArrowLeft } from '@fortawesome/free-solid-svg-icons/faArrowLeft'; -import { faSyncAlt } from '@fortawesome/free-solid-svg-icons/faSyncAlt'; +import { faArrowLeft, faSyncAlt } from '@fortawesome/free-solid-svg-icons'; import classNames from 'classnames'; -import styled from 'styled-components/macro'; +import styled, { keyframes } from 'styled-components/macro'; import tw from 'twin.macro'; interface BaseProps { @@ -27,17 +26,15 @@ interface PropsWithBack extends BaseProps { type Props = PropsWithBack | PropsWithRetry; +const spin = keyframes` + to { transform: rotate(360deg) } +`; + const ActionButton = styled.button` ${tw`rounded-full w-8 h-8 flex items-center justify-center`}; &.hover\\:spin:hover { - animation: spin 2s linear infinite; - } - - @keyframes spin { - to { - transform: rotate(360deg); - } + animation: ${spin} 2s linear infinite; } `; diff --git a/resources/scripts/components/server/Console.tsx b/resources/scripts/components/server/Console.tsx index 5deb90a6d..4ab66f960 100644 --- a/resources/scripts/components/server/Console.tsx +++ b/resources/scripts/components/server/Console.tsx @@ -4,9 +4,7 @@ import * as TerminalFit from 'xterm/lib/addons/fit/fit'; import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; import { ServerContext } from '@/state/server'; import styled from 'styled-components/macro'; -import Can from '@/components/elements/Can'; import { usePermissions } from '@/plugins/usePermissions'; -import classNames from 'classnames'; import tw from 'twin.macro'; const theme = { @@ -56,7 +54,7 @@ export default () => { const useRef = useCallback(node => setTerminalElement(node), []); const terminal = useMemo(() => new Terminal({ ...terminalProps }), []); const { connected, instance } = ServerContext.useStoreState(state => state.socket); - const [ canSendCommands ] = usePermissions([ 'control.console']); + const [ canSendCommands ] = usePermissions([ 'control.console' ]); const handleConsoleOutput = (line: string, prelude = false) => terminal.writeln( (prelude ? TERMINAL_PRELUDE : '') + line.replace(/(?:\r\n|\r|\n)$/im, '') + '\u001b[0m', @@ -123,12 +121,13 @@ export default () => { }, [ connected, instance ]); return ( -
+
{
{canSendCommands && -
-
$
-
+
+
$
+
handleCommandKeydown(e)} />
diff --git a/resources/scripts/components/server/ServerConsole.tsx b/resources/scripts/components/server/ServerConsole.tsx index 16675b35e..d2f7d5f95 100644 --- a/resources/scripts/components/server/ServerConsole.tsx +++ b/resources/scripts/components/server/ServerConsole.tsx @@ -13,35 +13,15 @@ import TitledGreyBox from '@/components/elements/TitledGreyBox'; import Can from '@/components/elements/Can'; import PageContentBlock from '@/components/elements/PageContentBlock'; import ContentContainer from '@/components/elements/ContentContainer'; +import tw from 'twin.macro'; +import Button from '@/components/elements/Button'; +import StopOrKillButton from '@/components/server/StopOrKillButton'; -type PowerAction = 'start' | 'stop' | 'restart' | 'kill'; +export type PowerAction = 'start' | 'stop' | 'restart' | 'kill'; const ChunkedConsole = lazy(() => import(/* webpackChunkName: "console" */'@/components/server/Console')); const ChunkedStatGraphs = lazy(() => import(/* webpackChunkName: "graphs" */'@/components/server/StatGraphs')); -const StopOrKillButton = ({ onPress }: { onPress: (action: PowerAction) => void }) => { - const [ clicked, setClicked ] = useState(false); - const status = ServerContext.useStoreState(state => state.status.value); - - useEffect(() => { - setClicked(state => [ 'stopping' ].indexOf(status) < 0 ? false : state); - }, [ status ]); - - return ( - - ); -}; - export default () => { const [ memory, setMemory ] = useState(0); const [ cpu, setCpu ] = useState(0); @@ -81,17 +61,17 @@ export default () => { }; }, [ instance, connected ]); - const disklimit = server.limits.disk != 0 ? bytesToHuman(server.limits.disk * 1000 * 1000) : "Unlimited"; - const memorylimit = server.limits.memory != 0 ? bytesToHuman(server.limits.memory * 1000 * 1000) : "Unlimited"; + const disklimit = server.limits.disk ? bytesToHuman(server.limits.disk * 1000 * 1000) : 'Unlimited'; + const memorylimit = server.limits.memory ? bytesToHuman(server.limits.memory * 1000 * 1000) : 'Unlimited'; return ( - -
+ +
-

+

{ />  {status}

-

- -  {cpu.toFixed(2)} % +

+ {cpu.toFixed(2)}%

-

- -  {bytesToHuman(memory)} - / {memorylimit} -

-

- -  {bytesToHuman(disk)} - / {disklimit} +

+ {bytesToHuman(memory)} + / {memorylimit} +

+

+  {bytesToHuman(disk)} + / {disklimit}

{!server.isInstalling ? - -
+ +
- + - + sendPowerCommand(action)}/> @@ -159,9 +129,9 @@ export default () => {
: -
+
-

+

This server is currently running its installation process and most actions are unavailable.

@@ -169,7 +139,7 @@ export default () => {
}
-
+
diff --git a/resources/scripts/components/server/StatGraphs.tsx b/resources/scripts/components/server/StatGraphs.tsx index a7595b947..e404a8475 100644 --- a/resources/scripts/components/server/StatGraphs.tsx +++ b/resources/scripts/components/server/StatGraphs.tsx @@ -6,6 +6,7 @@ import merge from 'lodash-es/merge'; import TitledGreyBox from '@/components/elements/TitledGreyBox'; import { faMemory } from '@fortawesome/free-solid-svg-icons/faMemory'; import { faMicrochip } from '@fortawesome/free-solid-svg-icons/faMicrochip'; +import tw from 'twin.macro'; const chartDefaults: ChartConfiguration = { type: 'line', @@ -157,21 +158,21 @@ export default () => { }, [ instance, connected, memory, cpu ]); return ( -
- +
+ {status !== 'offline' ? : -

+

Server is offline.

}
- + {status !== 'offline' ? : -

+

Server is offline.

} diff --git a/resources/scripts/components/server/StopOrKillButton.tsx b/resources/scripts/components/server/StopOrKillButton.tsx new file mode 100644 index 000000000..0d8b3db2e --- /dev/null +++ b/resources/scripts/components/server/StopOrKillButton.tsx @@ -0,0 +1,31 @@ +import React, { useEffect, useState } from 'react'; +import { ServerContext } from '@/state/server'; +import tw from 'twin.macro'; +import { PowerAction } from '@/components/server/ServerConsole'; +import Button from '@/components/elements/Button'; + +const StopOrKillButton = ({ onPress }: { onPress: (action: PowerAction) => void }) => { + const [ clicked, setClicked ] = useState(false); + const status = ServerContext.useStoreState(state => state.status.value); + + useEffect(() => { + setClicked(state => [ 'stopping' ].indexOf(status) < 0 ? false : state); + }, [ status ]); + + return ( + + ); +}; + +export default StopOrKillButton; diff --git a/resources/scripts/components/server/WebsocketHandler.tsx b/resources/scripts/components/server/WebsocketHandler.tsx index dc7ffa3c3..ae6d8c165 100644 --- a/resources/scripts/components/server/WebsocketHandler.tsx +++ b/resources/scripts/components/server/WebsocketHandler.tsx @@ -71,7 +71,7 @@ export default () => {

- We're having some trouble connecting to your server, please wait... + We're having some trouble connecting to your server, please wait...

diff --git a/resources/scripts/routers/DashboardRouter.tsx b/resources/scripts/routers/DashboardRouter.tsx index c3abf3176..79ebbe4a1 100644 --- a/resources/scripts/routers/DashboardRouter.tsx +++ b/resources/scripts/routers/DashboardRouter.tsx @@ -5,35 +5,8 @@ import NavigationBar from '@/components/NavigationBar'; import DashboardContainer from '@/components/dashboard/DashboardContainer'; import AccountApiContainer from '@/components/dashboard/AccountApiContainer'; import NotFound from '@/components/screens/NotFound'; -import styled from 'styled-components/macro'; -import tw from 'twin.macro'; -import config from '@/../../tailwind.config.js'; import TransitionRouter from '@/TransitionRouter'; - -const SubNavigation = styled.div` - ${tw`w-full bg-neutral-700 shadow`}; - - & > div { - ${tw`flex items-center text-sm mx-auto px-2`}; - max-width: 1200px; - - & > a, & > div { - ${tw`inline-block py-3 px-4 text-neutral-300 no-underline transition-all duration-150`}; - - &:not(:first-of-type) { - ${tw`ml-2`}; - } - - &:active, &:hover { - ${tw`text-neutral-100`}; - } - - &:active, &:hover, &.active { - box-shadow: inset 0 -2px ${config.theme.colors.cyan['500']}; - } - } - } -`; +import SubNavigation from '@/components/elements/SubNavigation'; export default ({ location }: RouteComponentProps) => ( <> diff --git a/resources/scripts/routers/ServerRouter.tsx b/resources/scripts/routers/ServerRouter.tsx index 82f6d0c77..a053262d6 100644 --- a/resources/scripts/routers/ServerRouter.tsx +++ b/resources/scripts/routers/ServerRouter.tsx @@ -23,6 +23,7 @@ import NotFound from '@/components/screens/NotFound'; import { useStoreState } from 'easy-peasy'; import useServer from '@/plugins/useServer'; import ScreenBlock from '@/components/screens/ScreenBlock'; +import SubNavigation from '@/components/elements/SubNavigation'; const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>) => { const { rootAdmin } = useStoreState(state => state.user.data!); @@ -71,8 +72,8 @@ const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>) : <> -
-
+ +
Console File Manager @@ -93,7 +94,7 @@ const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>) Settings
-
+ {(installing && (!rootAdmin || (rootAdmin && !location.pathname.endsWith(`/server/${server.id}`)))) ?