Skip to content

Commit e86b6bb

Browse files
authored
Merge pull request #1343 from gnmyt/updates/redesign
🎨 Redesign
2 parents 3dcce3b + 721b177 commit e86b6bb

134 files changed

Lines changed: 5537 additions & 2438 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

client/public/assets/locales/en.json

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
"upload": "Optimal up-speed",
5353
"download": "Optimal down-speed",
5454
"recommendations": "Recommendations",
55+
"optimal_values": "Optimal values",
5556
"change_provider": "Change provider",
5657
"password": "Change password",
5758
"cron": "Set frequency",
@@ -64,10 +65,18 @@
6465
"dark_mode": "Dark mode",
6566
"theme_switched_light": "Switched to light mode",
6667
"theme_switched_dark": "Switched to dark mode",
67-
"info": "About the project",
6868
"provider": "About the provider",
6969
"integrations": "Integrations"
7070
},
71+
"about": {
72+
"title": "About MySpeed",
73+
"description": "MySpeed is an open-source speedtest analysis software that records your internet speed for up to 30 days.",
74+
"github": "GitHub",
75+
"website": "Website",
76+
"donate": "Donate",
77+
"translate": "Translate",
78+
"made_by": "Made with"
79+
},
7180
"options": {
7281
"cron": {
7382
"continuous": "Continuous (every minute)",
@@ -122,6 +131,7 @@
122131
"paused": "Speedtests are currently paused. Please continue them if you want to do one.",
123132
"running": "A speedtest is already running. Please wait a moment.",
124133
"admin_login": "Admin Login",
134+
"servers": "Servers",
125135
"beta": {
126136
"title": "Beta Version",
127137
"description": "This feature is still in beta. If you find any bugs, please report them <Link>here</Link>."
@@ -165,7 +175,6 @@
165175
"before": "before"
166176
},
167177
"info": {
168-
"credits": "<MSpeed>MySpeed</MSpeed> is a open source project provided by GNMYT. Leave a star on <Github>GitHub</Github> or <Donate>donate</Donate> to support the project.",
169178
"recommendations_error": "You have to do at least 10 tests to get an average. It doesn't matter if the tests were done manually or automatically.",
170179
"recommendations_info": "Based on the last 10 tests, it was found that the optimal ping was <Bold>{{ping}} ms</Bold>, the download at <Bold>{{down}} Mbps</Bold> and the upload at <Bold>{{up}} Mbps</Bold>. It is best to orientate yourself on your internet contract and only adopt it if it matches that.",
171180
"update": "An update to version {{version}} is available. See <Changes>the changes</Changes> and <DLLink>download the update</DLLink>.",
@@ -217,11 +226,7 @@
217226
"automatic": "automatically"
218227
},
219228
"overview": {
220-
"title": "Test overview of the last {{amount}}",
221-
"1": "24 hours",
222-
"2": "2 days",
223-
"3": "7 days",
224-
"4": "30 days"
229+
"title_range": "Test overview from {{from}} to {{to}}"
225230
}
226231
},
227232
"errors": {
@@ -236,6 +241,7 @@
236241
"invalid_view": "Invalid view"
237242
},
238243
"statistics": {
244+
"average": "Average",
239245
"overview": {
240246
"total_title": "Total tests",
241247
"total_description": "The number of executed tests",
@@ -268,8 +274,42 @@
268274
"avg": "Average",
269275
"down": "Download values",
270276
"up": "Upload values"
277+
},
278+
"hourly": {
279+
"title": "Speed by Time of Day",
280+
"sample_count": "Tests"
281+
},
282+
"consistency": {
283+
"title": "Connection Stability",
284+
"excellent": "Excellent",
285+
"good": "Good",
286+
"fair": "Fair",
287+
"poor": "Poor",
288+
"ping_variance": "Ping variance"
289+
},
290+
"aggregation": {
291+
"daily": "Showing daily averages",
292+
"weekly": "Showing weekly averages"
271293
}
272294
},
295+
"calendar": {
296+
"select_range": "Select date range",
297+
"select_start": "Select start date",
298+
"select_end": "Select end date",
299+
"from": "From",
300+
"to": "To",
301+
"last_7_days": "Last 7 days",
302+
"last_30_days": "Last 30 days",
303+
"last_90_days": "Last 90 days",
304+
"last_year": "Last year",
305+
"mon": "Mon",
306+
"tue": "Tue",
307+
"wed": "Wed",
308+
"thu": "Thu",
309+
"fri": "Fri",
310+
"sat": "Sat",
311+
"sun": "Sun"
312+
},
273313
"nodes": {
274314
"add": "Add server",
275315
"create": "Add",
@@ -392,5 +432,20 @@
392432
},
393433
"common": {
394434
"back_to_top": "Back to top"
435+
},
436+
"not_found": {
437+
"title": "Page not found",
438+
"description": "The page you're looking for doesn't exist or has been moved.",
439+
"back_home": "Back to Home"
440+
},
441+
"route_error": {
442+
"title": "Oops!",
443+
"subtitle": "Something went wrong",
444+
"description": "An unexpected error occurred. Please try again or go back to the home page.",
445+
"reload": "Reload Page"
446+
},
447+
"optimal_values": {
448+
"title": "Optimal Values",
449+
"use_recommended": "Use recommended"
395450
}
396451
}

client/src/App.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {ThemeProvider} from "@/common/contexts/Theme";
1616
import i18n from './i18n';
1717
import Loading from "@/pages/Loading";
1818
import Error from "@/pages/Error";
19+
import RouteError from "@/pages/RouteError";
1920
import {ToastNotificationProvider} from "@/common/contexts/ToastNotification";
2021
import {NodeProvider} from "@/common/contexts/Node";
2122
import {library} from '@fortawesome/fontawesome-svg-core';
@@ -65,6 +66,7 @@ const App = () => {
6566
<main><Outlet/></main>
6667
</Providers>
6768
),
69+
errorElement: <RouteError />,
6870
children: [
6971
{path: "/", element: <Home/>},
7072
{path: "/nodes", element: <Nodes/>},
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import {DialogContext, DialogProvider} from "@/common/contexts/Dialog";
2+
import "./styles.sass";
3+
import React, {useContext, useEffect, useState} from "react";
4+
import {t} from "i18next";
5+
import {Trans} from "react-i18next";
6+
import {faClose, faGlobe, faHeart, faLanguage} from "@fortawesome/free-solid-svg-icons";
7+
import {faGithub} from "@fortawesome/free-brands-svg-icons";
8+
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
9+
import {jsonRequest} from "@/common/utils/RequestUtil";
10+
import {PROJECT_URL, WEB_URL, DONATION_URL} from "@/index";
11+
12+
const Dialog = ({version}) => {
13+
const close = useContext(DialogContext);
14+
15+
const links = [
16+
{icon: faGithub, label: t("about.github"), url: PROJECT_URL},
17+
{icon: faGlobe, label: t("about.website"), url: WEB_URL},
18+
{icon: faHeart, label: t("about.donate"), url: DONATION_URL, accent: true},
19+
{icon: faLanguage, label: t("about.translate"), url: "https://crowdin.com/project/myspeed"}
20+
];
21+
22+
return (
23+
<>
24+
<div className="dialog-header">
25+
<h4 className="dialog-text">{t("about.title")}</h4>
26+
<FontAwesomeIcon icon={faClose} className="dialog-text dialog-icon" onClick={() => close()}/>
27+
</div>
28+
29+
<div className="about-wrapper">
30+
<div className="about-hero">
31+
<img src="/assets/img/logo192.png" alt="MySpeed Logo" className="about-logo"/>
32+
<div className="about-title-section">
33+
<h1 className="about-title">MySpeed</h1>
34+
<span className="about-version">v{version}</span>
35+
</div>
36+
</div>
37+
38+
<p className="about-description">
39+
<Trans i18nKey="about.description">
40+
MySpeed is an open-source speedtest analysis software that records your internet speed for up to 30 days.
41+
</Trans>
42+
</p>
43+
44+
<div className="about-links">
45+
{links.map((link, index) => (
46+
<a key={index} href={link.url} target="_blank" rel="noopener noreferrer"
47+
className={"about-link" + (link.accent ? " about-link-accent" : "")}>
48+
<FontAwesomeIcon icon={link.icon}/>
49+
<span>{link.label}</span>
50+
</a>
51+
))}
52+
</div>
53+
54+
<p className="about-footer">
55+
{t("about.made_by")} <FontAwesomeIcon icon={faHeart} className="about-heart"/> by <a href="https://github.com/gnmyt" target="_blank" rel="noopener noreferrer">GNMYT</a>
56+
</p>
57+
</div>
58+
</>
59+
);
60+
};
61+
62+
export const AboutDialog = (props) => {
63+
const [version, setVersion] = useState(null);
64+
65+
useEffect(() => {
66+
jsonRequest("/info/version").then(data => setVersion(data.local));
67+
}, []);
68+
69+
return (
70+
<DialogProvider close={props.onClose} customClass={!version ? "dialog-loading" : "about-dialog"}>
71+
{!version && (
72+
<div className="lds-ellipsis"><div/><div/><div/><div/></div>
73+
)}
74+
{version && <Dialog version={version}/>}
75+
</DialogProvider>
76+
);
77+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export {AboutDialog as default} from "./AboutDialog.jsx";
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
@use "@/common/styles/colors" as *
2+
3+
.about-dialog
4+
max-width: 26rem
5+
6+
.about-wrapper
7+
display: flex
8+
flex-direction: column
9+
gap: 1rem
10+
margin-top: 1rem
11+
12+
.about-hero
13+
display: flex
14+
align-items: center
15+
gap: 1rem
16+
17+
.about-logo
18+
width: 56px
19+
height: 56px
20+
21+
.about-title-section
22+
display: flex
23+
flex-direction: column
24+
gap: 0.15rem
25+
26+
.about-title
27+
margin: 0
28+
font-size: 1.5rem
29+
font-weight: 700
30+
color: $white
31+
32+
.about-version
33+
font-size: 0.85rem
34+
color: $accent-primary
35+
font-weight: 600
36+
37+
.about-description
38+
margin: 0
39+
font-size: 0.9rem
40+
color: $subtext
41+
line-height: 1.5
42+
43+
.about-links
44+
display: grid
45+
grid-template-columns: 1fr 1fr
46+
gap: 0.5rem
47+
48+
.about-link
49+
display: flex
50+
align-items: center
51+
gap: 0.6rem
52+
padding: 0.6rem 0.85rem
53+
border: 1px solid $light-gray
54+
border-radius: 0.5rem
55+
color: $white
56+
text-decoration: none
57+
font-size: 0.85rem
58+
font-weight: 500
59+
transition: all 0.2s ease
60+
61+
svg
62+
color: $subtext
63+
font-size: 1rem
64+
65+
&:hover
66+
background: $darker-gray
67+
68+
svg
69+
color: $white
70+
71+
.about-link-accent
72+
svg
73+
color: $accent-danger
74+
75+
&:hover
76+
svg
77+
color: $accent-danger
78+
79+
.about-footer
80+
margin: 0
81+
padding-top: 0.5rem
82+
font-size: 0.8rem
83+
color: $subtext
84+
text-align: center
85+
border-top: 1px solid $light-gray
86+
87+
a
88+
color: $accent-primary
89+
text-decoration: none
90+
91+
&:hover
92+
text-decoration: underline
93+
94+
.about-heart
95+
color: $accent-danger
96+
font-size: 0.7rem
97+
margin: 0 0.1rem
98+
99+
@media (max-width: 500px)
100+
.about-dialog
101+
max-width: 90vw
102+
103+
.about-links
104+
grid-template-columns: 1fr
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { useEffect, useCallback } from "react";
2+
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
3+
import { faXmark } from "@fortawesome/free-solid-svg-icons";
4+
import "./styles.sass";
5+
6+
export const ChartModal = ({ isOpen, onClose, isChart = false, children }) => {
7+
const handleEscape = useCallback((e) => {
8+
if (e.key === "Escape") {
9+
onClose();
10+
}
11+
}, [onClose]);
12+
13+
useEffect(() => {
14+
if (isOpen) {
15+
document.addEventListener("keydown", handleEscape);
16+
document.body.style.overflow = "hidden";
17+
}
18+
return () => {
19+
document.removeEventListener("keydown", handleEscape);
20+
document.body.style.overflow = "";
21+
};
22+
}, [isOpen, handleEscape]);
23+
24+
if (!isOpen) return null;
25+
26+
const handleBackdropClick = (e) => {
27+
if (e.target === e.currentTarget) {
28+
onClose();
29+
}
30+
};
31+
32+
return (
33+
<div className="chart-modal-backdrop" onClick={handleBackdropClick}>
34+
<div className={`chart-modal-content${isChart ? ' modal-chart' : ''}`}>
35+
<button className="chart-modal-close" onClick={onClose}>
36+
<FontAwesomeIcon icon={faXmark} />
37+
</button>
38+
<div className={`chart-modal-body${isChart ? ' modal-body-chart' : ''}`}>
39+
{children}
40+
</div>
41+
</div>
42+
</div>
43+
);
44+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { ChartModal as default } from "./ChartModal";

0 commit comments

Comments
 (0)