Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

📦 Sammelansicht von Servern #252

Merged
merged 68 commits into from
Apr 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
e6c47ff
Made the chooser dialog wrap in the ViewDialog styles.sass
gnmyt Apr 1, 2023
0a1858b
Uploaded a node view placeholder image
gnmyt Apr 1, 2023
d09786e
Implemented the node view into the ViewDialog.jsx
gnmyt Apr 1, 2023
45fe141
Created the Nodes page
gnmyt Apr 1, 2023
b73670d
Created the Nodes page index
gnmyt Apr 1, 2023
853351f
Implemented the Nodes view into the App.jsx
gnmyt Apr 1, 2023
cf5ae01
Implemented the start & limit parameters in the speedtest controller
gnmyt Apr 1, 2023
50a5293
Integrated the new start & limit parameters into the GET /api/speedte…
gnmyt Apr 1, 2023
1be6a87
Reverted the changes in the view dialog
gnmyt Apr 1, 2023
6655ff2
Fixed the error page style
gnmyt Apr 3, 2023
defec62
Created the Node model
gnmyt Apr 3, 2023
caf4daf
Created the node controller
gnmyt Apr 3, 2023
10ac122
Created all node routes
gnmyt Apr 3, 2023
750d869
Integrated the node routes into the index.js
gnmyt Apr 3, 2023
823bd4a
The RequestUtil.js now supports loading information from a specific node
gnmyt Apr 3, 2023
6b4724d
Created the NodeContext.jsx
gnmyt Apr 3, 2023
1e29d5b
Created the NodeContext index
gnmyt Apr 3, 2023
436372d
Created the NodeHeader.jsx component
gnmyt Apr 3, 2023
af106e4
Created the NodeHeader component style
gnmyt Apr 3, 2023
3f8ff68
Created the NodeHeader component index
gnmyt Apr 3, 2023
b39cfbc
Removed additional debug information from the nodes route
gnmyt Apr 3, 2023
59fadcf
Updated the password field in the Node model
gnmyt Apr 3, 2023
2111ffa
Fixed the proxy route in the node.js routes
gnmyt Apr 3, 2023
eeeda0a
Fixed a bug in the Statistics.jsx
gnmyt Apr 3, 2023
7cfc6fc
Fixed a bug in the nodes.js route
gnmyt Apr 3, 2023
f141588
Removed a debug statement from the nodes.js route
gnmyt Apr 3, 2023
b169338
Fixed a bug in the ConfigContext.jsx
gnmyt Apr 3, 2023
28c2620
Added the mainRed property to the InputDialog.jsx
gnmyt Apr 3, 2023
d5fd3c4
Removed the beta span from the Header style
gnmyt Apr 3, 2023
81a065e
Integrated the server chooser into the HeaderComponent.jsx
gnmyt Apr 3, 2023
de6bf87
Updated the NodeHeader style
gnmyt Apr 3, 2023
af500fa
Created the NodeContainer.jsx
gnmyt Apr 3, 2023
e56e38f
Created the NodeContainer index
gnmyt Apr 3, 2023
39cf490
Created the NodeContainer style
gnmyt Apr 3, 2023
c74e0f0
Created the CreateNodeDialog.jsx
gnmyt Apr 3, 2023
9af73f1
Created the CreateNodeDialog index
gnmyt Apr 3, 2023
5749c5b
Created the CreateNodeDialog style
gnmyt Apr 3, 2023
52418e5
Created the Nodes page
gnmyt Apr 3, 2023
006a984
Created the Nodes style
gnmyt Apr 3, 2023
9fd39b9
Integrated the node system into the App.jsx
gnmyt Apr 3, 2023
c5de017
Added the node translations to the de.json
gnmyt Apr 3, 2023
3a88862
Translated the new node system into english
gnmyt Apr 3, 2023
a5e1e6f
Added german translations
gnmyt Apr 3, 2023
c182090
Integrated the translations into the CreateNodeDialog.jsx
gnmyt Apr 3, 2023
325318e
Updated the english translations
gnmyt Apr 3, 2023
cad6dba
Added auto interval in the NodeContainer.jsx
gnmyt Apr 3, 2023
006b927
Added password protection to the nodes.js route
gnmyt Apr 4, 2023
6f3d983
Removed the NodeContext from the ConfigContext
gnmyt Apr 4, 2023
2f934cd
The NodeContext.jsx now only updates the nodes when the config is loaded
gnmyt Apr 4, 2023
8953609
The HeaderComponent.jsx now disables the nodes feature in view mode
gnmyt Apr 4, 2023
753d98b
Updated the HeaderComponent style
gnmyt Apr 4, 2023
a264fe2
Updated the hierarchy in the App.jsx
gnmyt Apr 4, 2023
5a7dd16
The NodeContainer.jsx now reloads the config on node switch
gnmyt Apr 4, 2023
80b5ed9
Fixed a bug in the InputDialog.jsx
gnmyt Apr 4, 2023
7bf0f99
Optimized the CreateNodeDialog.jsx
gnmyt Apr 4, 2023
ff7de0f
Added mobile optimization to the CreateNodeDialog style
gnmyt Apr 4, 2023
e46fe87
Added the content disposition header to the node proxy route
gnmyt Apr 4, 2023
645080e
Added updateName & updatePassword to the node controller
gnmyt Apr 4, 2023
0a21591
Created patch routes to change the name & password of the nodes
gnmyt Apr 4, 2023
232d835
Added the icon-text class to the NodeContainer style
gnmyt Apr 4, 2023
502bce2
The Nodes now load on render
gnmyt Apr 4, 2023
575edab
Implemented custom errors into the NodeContainer.jsx
gnmyt Apr 4, 2023
5641270
Updated the password changer in the DropdownComponent.jsx
gnmyt Apr 4, 2023
ee2524e
The ConfigContext.jsx now redirects to the server selector if the cur…
gnmyt Apr 4, 2023
ab900fc
Optimized the NodeContainer.jsx
gnmyt Apr 4, 2023
f4531dd
Added new german translations
gnmyt Apr 4, 2023
f07476c
Added new english translations
gnmyt Apr 4, 2023
c85fafe
Added the "this server" translation (DE and EN)
gnmyt Apr 4, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion client/public/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
"view_title": "Ansicht wechseln"
},
"header": {
"title": "Netzwerkanalyse",
"title": "MySpeed",
"running_tooltip": "Speedtest läuft",
"start_tooltip": "Speedtest starten",
"new_update": "Update verfügbar",
Expand Down Expand Up @@ -237,5 +237,34 @@
"down": "Download-Werte",
"up": "Upload-Werte"
}
},
"nodes": {
"add": "Server hinzufügen",
"create": "Hinzufügen",
"this_server": "Dieser Server",
"created": "Der Server wurde erfolgreich hinzugefügt",
"password_required": "Für diese Node ist ein Passwort erforderlich",
"update_password": "Bitte aktualisiere das Passwort dieser Node",
"password_outdated": "Passwort veraltet",
"password_updated": "Das Passwort wurde erfolgreich aktualisiert",
"placeholder": {
"name": "MySpeed Instanz",
"url": "https://dein-server.de"
},
"group": {
"name": "Servername",
"url": "Serveradresse"
},
"delete": {
"title": "Server löschen",
"description": "Der Server <Bold>{{name}}</Bold> (#{{id}}) wird gelöscht. Diese Aktion kann nicht rückgängig gemacht werden. Möchtest du fortfahren?",
"yes": "Ja, löschen",
"success": "Der Server wurde erfolgreich gelöscht"
},
"messages": {
"not_reachable": "Server ist nicht erreichbar",
"password_changed": "Passwort wurde geändert",
"tests_pending": "Testergebnisse ausstehend"
}
}
}
31 changes: 30 additions & 1 deletion client/public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
"view_title": "Switch view"
},
"header": {
"title": "Network Analysis",
"title": "MySpeed",
"running_tooltip": "Speedtest running",
"start_tooltip": "Start speedtest",
"new_update": "Update available",
Expand Down Expand Up @@ -237,5 +237,34 @@
"down": "Download values",
"up": "Upload values"
}
},
"nodes": {
"add": "Add server",
"create": "Add",
"this_server": "This server",
"created": "The server has been successfully added",
"password_required": "This node requires a password",
"update_password": "Please update the password of this node",
"password_outdated": "Password outdated",
"password_updated": "The password has been successfully updated",
"placeholder": {
"name": "MySpeed instance",
"url": "https://your-server.com"
},
"group": {
"name": "Server name",
"url": "Server address"
},
"delete": {
"title": "Delete server",
"description": "The server <Bold>{{name}}</Bold> (#{{id}}) will be deleted. This action cannot be undone. Do you want to continue?",
"yes": "Yes, delete",
"success": "The server has been deleted successfully"
},
"messages": {
"not_reachable": "Server is not reachable",
"password_changed": "Password changed",
"tests_pending": "Test results pending"
}
}
}
32 changes: 20 additions & 12 deletions client/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {ViewContext, ViewProvider} from "@/common/contexts/View";
import Statistics from "@/pages/Statistics";
import {t} from "i18next";
import {ToastNotificationProvider} from "@/common/contexts/ToastNotification";
import Nodes from "@/pages/Nodes";
import {NodeProvider} from "@/common/contexts/Node";

const MainContent = () => {
const [view] = useContext(ViewContext);
Expand All @@ -30,27 +32,33 @@ const App = () => {
const [translationsLoaded, setTranslationsLoaded] = useState(false);
const [translationError, setTranslationError] = useState(false);

const [showNodePage, setShowNodePage] = useState(false);

i18n.on("initialized", () => setTranslationsLoaded(true));
i18n.on("failedLoading", () => setTranslationError(true));

return (
<>
{!translationsLoaded && !translationError && <Loading/>}
{translationError && <Error text="Failed to load translations"/>}
{translationsLoaded && !translationError && <SpeedtestProvider>
<InputDialogProvider>
<ToastNotificationProvider>
<InputDialogProvider>
<ViewProvider>
<ConfigProvider>
<StatusProvider>
<HeaderComponent/>
<MainContent/>
</StatusProvider>
</ConfigProvider>
</ViewProvider>
</InputDialogProvider>
<ConfigProvider showNodePage={setShowNodePage}>
<NodeProvider>
{translationsLoaded && !translationError && showNodePage &&
<Nodes setShowNodePage={setShowNodePage}/>}
{translationsLoaded && !translationError && !showNodePage && <SpeedtestProvider>
<ViewProvider>
<StatusProvider>
<HeaderComponent showNodePage={setShowNodePage}/>
<MainContent/>
</StatusProvider>
</ViewProvider>
</SpeedtestProvider>}
</NodeProvider>
</ConfigProvider>
</ToastNotificationProvider>
</SpeedtestProvider>}
</InputDialogProvider>
</>
);
}
Expand Down
9 changes: 7 additions & 2 deletions client/src/common/components/Dropdown/DropdownComponent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {ConfigContext} from "@/common/contexts/Config";
import {StatusContext} from "@/common/contexts/Status";
import {InputDialogContext} from "@/common/contexts/InputDialog";
import {SpeedtestContext} from "@/common/contexts/Speedtests";
import {downloadRequest, jsonRequest, patchRequest, postRequest} from "@/common/utils/RequestUtil";
import {baseRequest, downloadRequest, jsonRequest, patchRequest, postRequest} from "@/common/utils/RequestUtil";
import {creditsInfo, healthChecksInfo, recommendationsInfo} from "@/common/components/Dropdown/utils/infos";
import {
exportOptions, languageOptions, levelOptions,
Expand All @@ -36,6 +36,7 @@ import {changeLanguage, t} from "i18next";
import ViewDialog from "@/common/components/ViewDialog";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {ToastNotificationContext} from "@/common/contexts/ToastNotification";
import {NodeContext} from "@/common/contexts/Node";

let icon;

Expand All @@ -56,6 +57,7 @@ export const toggleDropdown = (setIcon) => {
function DropdownComponent() {
const [config, reloadConfig] = useContext(ConfigContext);
const [status, updateStatus] = useContext(StatusContext);
const currentNode = useContext(NodeContext)[2];
const updateTests = useContext(SpeedtestContext)[1];
const updateToast = useContext(ToastNotificationContext);
const [setDialog] = useContext(InputDialogContext);
Expand Down Expand Up @@ -153,7 +155,10 @@ function DropdownComponent() {
.then(() => localStorage.removeItem("password")),
onSuccess: (value) => patchRequest("/config/password", {value})
.then(() => showFeedback(undefined, false))
.then(() => localStorage.setItem("password", value))
.then(() => {
currentNode !== 0 ? baseRequest("/nodes/" + currentNode + "/password", "PATCH",
{password: value}) : localStorage.setItem("password", value);
})
})
}

Expand Down
30 changes: 19 additions & 11 deletions client/src/common/components/Header/HeaderComponent.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import "./styles.sass";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faCircleArrowUp, faGaugeHigh, faGear, faLock} from "@fortawesome/free-solid-svg-icons";
import {
faCircleArrowUp,
faGaugeHigh,
faGear,
faLock,
faServer
} from "@fortawesome/free-solid-svg-icons";
import {useContext, useEffect, useState} from "react";
import DropdownComponent, {toggleDropdown} from "../Dropdown/DropdownComponent";
import {InputDialogContext} from "@/common/contexts/InputDialog";
Expand All @@ -11,19 +17,19 @@ import {updateInfo} from "@/common/components/Header/utils/infos";
import {t} from "i18next";
import {ConfigContext} from "@/common/contexts/Config";
import {SpeedtestDialog} from "@/common/components/SpeedtestDialog";
import {ViewContext} from "@/common/contexts/View";
import {Trans} from "react-i18next";
import {PROJECT_URL} from "@/index";
import {NodeContext} from "@/common/contexts/Node";

function HeaderComponent(props) {
const nodes = useContext(NodeContext)[0];
const currentNode = useContext(NodeContext)[2];

function HeaderComponent() {
const [setDialog] = useContext(InputDialogContext);
const [icon, setIcon] = useState(faGear);
const [status, updateStatus] = useContext(StatusContext);
const [startedManually, setStartedManually] = useState(false);
const updateTests = useContext(SpeedtestContext)[1];
const [config, reloadConfig, checkConfig] = useContext(ConfigContext);
const [updateAvailable, setUpdateAvailable] = useState("");
const [view] = useContext(ViewContext);

function switchDropdown() {
toggleDropdown(setIcon);
Expand Down Expand Up @@ -76,15 +82,17 @@ function HeaderComponent() {
if (!config.viewMode) updateVersion();
}, [config]);

const getNodeName = () =>
currentNode === "0" ? t("header.title") : nodes?.find(node => node.id === currentNode)?.name || t("header.title");

if (Object.keys(config).length === 0) return <></>;

return (
<header>
<SpeedtestDialog isOpen={startedManually}/>
<div className="header-main">
<h2>{t("header.title")} {view === 1 && <span className="beta-span" onClick={() => setDialog({
title: t("header.beta.title"),
description: <Trans components={{Link: <a href={PROJECT_URL+"/issues/new/choose"} target="_blank" />}}>header.beta.description</Trans>,
buttonText: t("dialog.okay")
})}>BETA</span>}</h2>
{config.viewMode ? <h2>{t("header.title")}</h2> : <h2 onClick={() => props.showNodePage(true)} className="h2-click"><FontAwesomeIcon icon={faServer} /> {getNodeName()}</h2>}

<div className="header-right">
{updateAvailable ?
<div><FontAwesomeIcon icon={faCircleArrowUp} className="header-icon icon-orange update-icon"
Expand Down
29 changes: 16 additions & 13 deletions client/src/common/components/Header/styles.sass
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,18 @@
.header-main h2
margin-left: 10%
display: flex
gap: 0.3rem
gap: 1rem
align-items: center
padding: 0.5rem 1rem
border-radius: 1rem

.beta-span
display: flex
.header-main .h2-click
cursor: pointer
align-items: center
justify-content: center
font-size: 14pt
user-select: none

.header-main .h2-click:hover
color: $green-hover
background-color: $dark-gray
color: $green
padding: 0.1rem 0.4rem
border-radius: 0.7rem

.header-main div
margin-right: 10%
Expand Down Expand Up @@ -75,14 +75,17 @@
margin-right: 0
.header-main h2
margin-left: 0
font-size: 18pt
font-size: 24pt
.header-icon
width: 20px
height: 20px
width: 25px
height: 25px


@media (max-width: 360px)
.header-main h2
font-size: 16pt
text-overflow: ellipsis
overflow: hidden
overflow: hidden
.header-icon
width: 20px
height: 20px
1 change: 1 addition & 0 deletions client/src/common/components/ViewDialog/styles.sass
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
.chooser-dialog
display: flex
justify-content: center
flex-wrap: wrap
align-items: center
padding: 2rem 3.5rem
gap: 1rem
Expand Down
14 changes: 9 additions & 5 deletions client/src/common/contexts/Config/ConfigContext.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {acceptDialog, apiErrorDialog, passwordRequiredDialog} from "@/common/con
export const ConfigContext = createContext({});

export const ConfigProvider = (props) => {

const [config, setConfig] = useState({});
const [setDialog] = useContext(InputDialogContext);
const [dialogShown, setDialogShown] = useState(false);
Expand All @@ -15,15 +14,20 @@ export const ConfigProvider = (props) => {
request("/config").then(async res => {
if (res.status === 401) throw 1;
if (!res.ok) throw 2;

try {
return JSON.parse(await res.text());
} catch (e) {
throw 2;
}
})
.then(result => config !== result ? setConfig(result) : null)
.catch((code) => setDialog(code === 1 ? passwordRequiredDialog() : apiErrorDialog()));
}).then(result => {
if (config !== result)
result.viewMode && localStorage.getItem("currentNode") !== null && localStorage.getItem("currentNode") !== "0"
? props.showNodePage(true) : setConfig(result);
}).catch((code) => {
localStorage.getItem("currentNode") !== null && localStorage.getItem("currentNode") !== "0"
? props.showNodePage(true) : setDialog(code === 1 ? passwordRequiredDialog() : apiErrorDialog());
});
}

const checkConfig = async () => (await request("/config")).json();
Expand Down
24 changes: 15 additions & 9 deletions client/src/common/contexts/InputDialog/InputDialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,22 @@ const DialogArea = ({dialog}) => {
if (dialog.value) setValue(dialog.value);
}, [dialog.value]);

document.onkeyup = e => {
if (e.key === "Enter") {
e.preventDefault();
submit();
useEffect(() => {
document.onkeyup = e => {
if (e.key === "Enter") {
e.preventDefault();
submit();
}
if (e.key === "Escape" && !dialog.disableCloseButton) {
e.preventDefault();
closeDialog();
}
}
if (e.key === "Escape" && !dialog.disableCloseButton) {
e.preventDefault();
closeDialog();

return () => {
document.onkeyup = null;
}
}
});

function updateValue(e) {
if (dialog.updateDescription) dialog.description = dialog.updateDescription(e.target.value);
Expand Down Expand Up @@ -72,7 +78,7 @@ const DialogArea = ({dialog}) => {
<div className="dialog-buttons">
{dialog.unsetButton ? <button className="dialog-btn dialog-secondary"
onClick={clear}>{dialog.unsetButton || t("dialog.unset")}</button> : ""}
<button className="dialog-btn" onClick={submit}>{dialog.buttonText || t("dialog.update")}</button>
<button className={"dialog-btn"+(dialog.mainRed ? " dialog-secondary" : "")} onClick={submit}>{dialog.buttonText || t("dialog.update")}</button>
</div>
</>
)
Expand Down
Loading