diff --git a/frontend/src/components/layout/ProfileButton.js b/frontend/src/components/layout/ProfileButton.js index 5843a57..5e92221 100644 --- a/frontend/src/components/layout/ProfileButton.js +++ b/frontend/src/components/layout/ProfileButton.js @@ -6,17 +6,16 @@ import AccountBoxIcon from "@mui/icons-material/AccountBox"; import SettingsIcon from "@mui/icons-material/Settings"; import { useNavigate } from "react-router-dom"; import { useTuitioClient } from "@flare/tuitio-client-react"; -import { useToast } from "../../hooks"; +import { blip } from "../../utils"; import { useTranslation } from "react-i18next"; const ProfileButton = () => { const navigate = useNavigate(); - const { error } = useToast(); const { t } = useTranslation(); const { logout } = useTuitioClient({ - onLogoutFailed: errorMessage => error(errorMessage), - onLogoutError: err => error(err.message) + onLogoutFailed: errorMessage => blip.error(errorMessage), + onLogoutError: err => blip.error(err.message) }); const [anchorEl, setAnchorEl] = useState(null); diff --git a/frontend/src/features/about/system/SystemVersionContainer.js b/frontend/src/features/about/system/SystemVersionContainer.js deleted file mode 100644 index c4731fa..0000000 --- a/frontend/src/features/about/system/SystemVersionContainer.js +++ /dev/null @@ -1,18 +0,0 @@ -import React, { useState, useEffect } from "react"; -import SystemVersionComponent from "./SystemVersionComponent"; -import { routes, get } from "../../../utils/api"; - -const SystemVersionContainer = () => { - const [state, setState] = useState({ data: {}, loaded: false }); - - useEffect(() => { - if (state.loaded) return; - get(routes.systemVersion, { - onCompleted: data => setState({ data, loaded: true }) - }); - }, [state.loaded]); - - return <>{state.loaded && }; -}; - -export default SystemVersionContainer; diff --git a/frontend/src/features/about/system/SystemVersionContainer.tsx b/frontend/src/features/about/system/SystemVersionContainer.tsx new file mode 100644 index 0000000..46aaa75 --- /dev/null +++ b/frontend/src/features/about/system/SystemVersionContainer.tsx @@ -0,0 +1,26 @@ +import React, { useState, useEffect } from "react"; +import SystemVersionComponent from "./SystemVersionComponent"; +import { routes, get, endpoints } from "../../../utils/api"; +import useSWR from "swr"; +import { fetcher } from "utils/swr"; +import { blip } from "utils"; + +const SystemVersionContainer: React.FC = () => { + const { data: namespaces } = useSWR(endpoints.system.version, fetcher, { + revalidateOnFocus: false, + onError: err => blip.error(err.message) + }); + + const [state, setState] = useState({ data: {}, loaded: false }); + + useEffect(() => { + if (state.loaded) return; + get(routes.systemVersion, { + onCompleted: (data: any) => setState({ data, loaded: true }) + }); + }, [state.loaded]); + + return <>{state.loaded && }; +}; + +export default SystemVersionContainer; diff --git a/frontend/src/features/login/components/LoginContainer.js b/frontend/src/features/login/components/LoginContainer.js index c3f23af..150790d 100644 --- a/frontend/src/features/login/components/LoginContainer.js +++ b/frontend/src/features/login/components/LoginContainer.js @@ -1,6 +1,6 @@ import React, { useState } from "react"; import LoginCard from "./LoginCard"; -import { useToast } from "../../../hooks"; +import { blip } from "../../../utils"; import { useTranslation } from "react-i18next"; import { useTuitioClient } from "@flare/tuitio-client-react"; @@ -10,11 +10,10 @@ const LoginContainer = () => { password: "" }); - const { error } = useToast(); const { t } = useTranslation(); const { login } = useTuitioClient({ - onLoginFailed: () => error(t("Login.IncorrectCredentials")), - onLoginError: err => error(err.message) + onLoginFailed: () => blip.error(t("Login.IncorrectCredentials")), + onLoginError: err => blip.error(err.message) }); const handleChange = prop => event => { diff --git a/frontend/src/features/machines/components/MachineContainer.js b/frontend/src/features/machines/components/MachineContainer.js index e1a3236..54fcf4e 100644 --- a/frontend/src/features/machines/components/MachineContainer.js +++ b/frontend/src/features/machines/components/MachineContainer.js @@ -3,15 +3,13 @@ import PropTypes from "prop-types"; import MachineTableRow from "./MachineTableRow"; import MachineAccordion from "./MachineAccordion"; import { ViewModes } from "./ViewModeSelection"; -import { useToast } from "../../../hooks"; +import { blip } from "../../../utils"; import { LastPage, RotateLeft, Launch, Stop } from "@mui/icons-material"; import { useTranslation } from "react-i18next"; import { routes, post } from "../../../utils/api"; const MachineContainer = ({ machine, viewMode }) => { const [logs, setLogs] = useState([]); - - const { success, error } = useToast(); const { t } = useTranslation(); const addLog = useCallback( @@ -25,12 +23,12 @@ const MachineContainer = ({ machine, viewMode }) => { response => { addLog(`Success: ${response.success}. Status: ${response.status}`); if (response.success) { - success(response.status); + blip.success(response.status); } else { - error(response.status); + blip.error(response.status); } }, - [error, success, addLog] + [addLog] ); const pingMachine = useCallback( diff --git a/frontend/src/features/machines/components/common/WakeComponent.js b/frontend/src/features/machines/components/common/WakeComponent.js index 6f7b0bc..69a9d6b 100644 --- a/frontend/src/features/machines/components/common/WakeComponent.js +++ b/frontend/src/features/machines/components/common/WakeComponent.js @@ -3,7 +3,7 @@ import PropTypes from "prop-types"; import { IconButton, Tooltip } from "@mui/material"; import { PowerSettingsNew } from "@mui/icons-material"; import { useTranslation } from "react-i18next"; -import { useToast } from "../../../../hooks"; +import { blip } from "../../../../utils"; import { msToMinAndSec } from "../../../../utils/time"; import { routes, post } from "../../../../utils/api"; @@ -16,7 +16,6 @@ const WakeComponent = ({ machine, addLog, disabled }) => { const [trigger, setTrigger] = useState(false); const { t } = useTranslation(); - const { success, error } = useToast(); const pingInterval = process.env.REACT_APP_MACHINE_PING_INTERVAL || defaultPingInterval; const startingTime = process.env.REACT_APP_MACHINE_STARTING_TIME || defaultStartingTime; @@ -40,7 +39,7 @@ const WakeComponent = ({ machine, addLog, disabled }) => { setState(prev => ({ ...prev, on: result.success })); log(`[Wake]: Success: ${result.success}. Status: ${result.status}`); if (result.success) { - success(result.status); + blip.success(result.status); //retrigger log(`Periodic ping will be re-triggered in ${startingTime} ms [${msToMinAndSec(startingTime)}]`); @@ -48,12 +47,12 @@ const WakeComponent = ({ machine, addLog, disabled }) => { setTrigger(prev => !prev); }, startingTime); } else { - error(result.status); + blip.error(result.status); } } } ); - }, [log, success, error, startingTime, machine.machineId]); + }, [log, startingTime, machine.machineId]); const pingInLoop = useCallback(async () => { if (disabled) return; diff --git a/frontend/src/features/settings/system/CacheSettingsContainer.js b/frontend/src/features/settings/system/CacheSettingsContainer.js index 4283fcd..3e94592 100644 --- a/frontend/src/features/settings/system/CacheSettingsContainer.js +++ b/frontend/src/features/settings/system/CacheSettingsContainer.js @@ -2,7 +2,7 @@ import React, { useCallback } from "react"; import CacheSettingsComponent from "./CacheSettingsComponent"; import { useTranslation } from "react-i18next"; import { routes, post } from "utils/api"; -import { info } from "utils/toast"; +import { blip } from "utils"; const CacheSettingsContainer = () => { const { t } = useTranslation(); @@ -11,7 +11,7 @@ const CacheSettingsContainer = () => { routes.resetCache, {}, { - onCompleted: () => info(t("Settings.Cache.ResetInfo")) + onCompleted: () => blip.info(t("Settings.Cache.ResetInfo")) } ); }, [t]); diff --git a/frontend/src/hooks/index.js b/frontend/src/hooks/index.js index 44ec796..f28fc19 100644 --- a/frontend/src/hooks/index.js +++ b/frontend/src/hooks/index.js @@ -1,6 +1,5 @@ -import { useToast } from "./useToast"; import { useSensitiveInfo } from "../providers/SensitiveInfoProvider"; import { usePermissions } from "../providers/UserPermissionsProvider"; import { useClipboard } from "./useClipboard"; -export { useToast, useSensitiveInfo, usePermissions, useClipboard }; +export { useSensitiveInfo, usePermissions, useClipboard }; diff --git a/frontend/src/hooks/useClipboard.js b/frontend/src/hooks/useClipboard.js index b2fd329..956f5e2 100644 --- a/frontend/src/hooks/useClipboard.js +++ b/frontend/src/hooks/useClipboard.js @@ -1,16 +1,15 @@ import { useCallback } from "react"; import { useTranslation } from "react-i18next"; -import { useToast } from "./useToast"; +import { blip } from "../utils"; const useClipboard = () => { const { t } = useTranslation(); - const { info } = useToast(); const copy = useCallback( url => () => { navigator.clipboard.writeText(url); - info(t("Generic.CopiedToClipboard")); + blip.info(t("Generic.CopiedToClipboard")); }, - [info, t] + [t] ); return { copy }; }; diff --git a/frontend/src/hooks/useToast.js b/frontend/src/hooks/useToast.js deleted file mode 100644 index 2255151..0000000 --- a/frontend/src/hooks/useToast.js +++ /dev/null @@ -1,5 +0,0 @@ -import { info, success, warning, error, dark } from "utils/toast"; - -export const useToast = () => { - return { info, success, warning, error, dark }; -}; diff --git a/frontend/src/utils/api.js b/frontend/src/utils/api.js index 08dd64b..6b9e85f 100644 --- a/frontend/src/utils/api.js +++ b/frontend/src/utils/api.js @@ -2,10 +2,12 @@ import * as axios from "../utils/axios"; import { toast } from "react-toastify"; import env from "../utils/env"; -const networkRoute = `${env.REACT_APP_NETWORK_RESURRECTOR_API_URL}/network`; -const systemRoute = `${env.REACT_APP_NETWORK_RESURRECTOR_API_URL}/system`; -const powerActionsRoute = `${env.REACT_APP_NETWORK_RESURRECTOR_API_URL}/resurrector`; -const securityRoute = `${env.REACT_APP_NETWORK_RESURRECTOR_API_URL}/security`; +const apiHost = env.REACT_APP_NETWORK_RESURRECTOR_API_URL; + +const networkRoute = `${apiHost}/network`; +const systemRoute = `${apiHost}/system`; +const powerActionsRoute = `${apiHost}/resurrector`; +const securityRoute = `${apiHost}/security`; const routes = { permissions: `${securityRoute}/permissions`, @@ -16,7 +18,12 @@ const routes = { wakeMachine: `${powerActionsRoute}/wake`, pingMachine: `${powerActionsRoute}/ping`, shutdownMachine: `${powerActionsRoute}/shutdown`, - restartMachine: `${powerActionsRoute}/restart` + restartMachine: `${powerActionsRoute}/restart`, + system: { + version: `${systemRoute}/version`, + releaseNotes: `${systemRoute}/release-notes`, + resetCache: `${systemRoute}/reset-cache` + } }; const handleError = err => { @@ -64,4 +71,6 @@ const post = (route, data, options) => { return promise; }; -export { routes, get, post }; +const endpoints = routes; + +export { routes, get, post, endpoints }; diff --git a/frontend/src/utils/toast.js b/frontend/src/utils/blip.js similarity index 75% rename from frontend/src/utils/toast.js rename to frontend/src/utils/blip.js index 4045135..82c7f0e 100644 --- a/frontend/src/utils/toast.js +++ b/frontend/src/utils/blip.js @@ -6,4 +6,6 @@ const warning = message => toast.warning(message); const error = message => toast.error(message); const dark = message => toast.dark(message); -export { info, success, warning, error, dark }; +const blip = { info, success, warning, error, dark }; +export { blip }; +export default blip; diff --git a/frontend/src/utils/index.js b/frontend/src/utils/index.js deleted file mode 100644 index 0dbd747..0000000 --- a/frontend/src/utils/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import { getRandomElement } from "./random"; - -export { getRandomElement }; diff --git a/frontend/src/utils/index.ts b/frontend/src/utils/index.ts new file mode 100644 index 0000000..2115555 --- /dev/null +++ b/frontend/src/utils/index.ts @@ -0,0 +1,4 @@ +import { getRandomElement } from "./random"; +import blip from "./blip"; + +export { getRandomElement, blip }; diff --git a/frontend/src/utils/swr.ts b/frontend/src/utils/swr.ts new file mode 100644 index 0000000..2c718f3 --- /dev/null +++ b/frontend/src/utils/swr.ts @@ -0,0 +1,40 @@ +import i18next from "i18next"; +import { acquire as fetchTuitioData } from "@flare/tuitio-client"; + +const getHeaders = (): HeadersInit => { + const { token } = fetchTuitioData(); + const language = i18next.language; + + const headers: HeadersInit = { + "Content-Type": "application/json" + }; + + if (token) { + headers.Authorization = `Tuitio ${token}`; + } + + if (language) { + headers["Accept-Language"] = language; + } + + return headers; +}; + +const fetcher = (url: string) => fetch(url, { method: "GET", headers: getHeaders() }).then(res => res.json()); + +async function mutationFetcher(url: string, { arg }: { arg: Command }) { + return fetch(url, { + method: "POST", + headers: getHeaders(), + body: JSON.stringify(arg) + }).then(res => res.json()); +} + +// async function deleteFetcher(url: string, { arg }: { arg: Command }) { +// return fetch(combine(url, arg as number), { +// method: "DELETE", +// headers: getHeaders() +// }).then(res => res.json()); +// } + +export { fetcher, mutationFetcher };