Add advacned settings page

This commit is contained in:
Alex Holliday
2024-09-27 10:55:37 +08:00
parent 179e95eefe
commit 488ba717ac
6 changed files with 194 additions and 45 deletions
+17 -2
View File
@@ -19,6 +19,7 @@ import SetNewPassword from "./Pages/Auth/SetNewPassword";
import NewPasswordConfirmed from "./Pages/Auth/NewPasswordConfirmed";
import ProtectedRoute from "./Components/ProtectedRoute";
import Details from "./Pages/Monitors/Details";
import AdvancedSettings from "./Pages/AdvancedSettings";
// import Maintenance from "./Pages/Maintenance";
import withAdminCheck from "./HOC/withAdminCheck";
import withAdminProp from "./HOC/withAdminProp";
@@ -33,7 +34,9 @@ import lightTheme from "./Utils/Theme/lightTheme";
import darkTheme from "./Utils/Theme/darkTheme";
import { useSelector } from "react-redux";
import { CssBaseline } from "@mui/material";
import { useEffect } from "react";
import { useDispatch } from "react-redux";
import { getAppSettings } from "./Features/Settings/settingsSlice";
function App() {
const AdminCheckedRegister = withAdminCheck(Register);
const MonitorsWithAdminProp = withAdminProp(Monitors);
@@ -42,8 +45,14 @@ function App() {
const PageSpeedDetailsWithAdminProp = withAdminProp(PageSpeedDetails);
// const MaintenanceWithAdminProp = withAdminProp(Maintenance);
const SettingsWithAdminProp = withAdminProp(Settings);
const AdvancedSettingsWithAdminProp = withAdminProp(AdvancedSettings);
const mode = useSelector((state) => state.ui.mode);
const { authToken } = useSelector((state) => state.auth);
const dispatch = useDispatch();
useEffect(() => {
dispatch(getAppSettings(authToken));
}, [dispatch, authToken]);
return (
<ThemeProvider theme={mode === "light" ? lightTheme : darkTheme}>
@@ -96,6 +105,12 @@ function App() {
path="settings"
element={<ProtectedRoute Component={SettingsWithAdminProp} />}
/>
<Route
path="advanced-settings"
element={
<ProtectedRoute Component={AdvancedSettingsWithAdminProp} />
}
/>
<Route
path="account/profile"
element={<ProtectedRoute Component={Account} open="profile" />}
+3 -14
View File
@@ -23,10 +23,10 @@ const initialState = {
export const getAppSettings = createAsyncThunk(
"settings/getSettings",
async (data, thunkApi) => {
async (authToken, thunkApi) => {
try {
const res = await networkService.getAppSettings({
authToken: data.authToken,
authToken: authToken,
});
return res.data;
} catch (error) {
@@ -65,6 +65,7 @@ const handleGetSettingsFulfilled = (state, action) => {
state.isLoading = false;
state.success = action.payload.success;
state.msg = action.payload.msg;
Object.assign(state, action.payload.data);
};
const handleGetSettingsRejected = (state, action) => {
state.isLoading = false;
@@ -87,17 +88,7 @@ const handleUpdateSettingsRejected = (state, action) => {
const settingsSlice = createSlice({
name: "settings",
initialState,
reducers: {
clearAuthState: (state) => {
state.authToken = "";
state.user = "";
state.isLoading = false;
state.success = true;
state.msg = "Logged out successfully";
},
},
extraReducers: (builder) => {
// Register thunk
builder
.addCase(getAppSettings.pending, (state) => {
state.isLoading = true;
@@ -105,7 +96,6 @@ const settingsSlice = createSlice({
.addCase(getAppSettings.fulfilled, handleGetSettingsFulfilled)
.addCase(getAppSettings.rejected, handleGetSettingsRejected);
// Login thunk
builder
.addCase(updateAppSettings.pending, (state) => {
state.isLoading = true;
@@ -116,4 +106,3 @@ const settingsSlice = createSlice({
});
export default settingsSlice.reducer;
export const { clearAuthState } = settingsSlice.actions;
+155
View File
@@ -0,0 +1,155 @@
import { useTheme } from "@emotion/react";
import { Box, Stack, Typography, Button } from "@mui/material";
import Field from "../../Components/Inputs/Field";
import Link from "../../Components/Link";
import { logger } from "../../Utils/Logger";
import "./index.css";
import { useDispatch, useSelector } from "react-redux";
import { createToast } from "../../Utils/toastUtils";
import PropTypes from "prop-types";
import LoadingButton from "@mui/lab/LoadingButton";
import { ConfigBox } from "../Settings/styled";
import { useNavigate } from "react-router";
import { getAppSettings } from "../../Features/Settings/settingsSlice";
import { useEffect, useState } from "react";
import Select from "../../Components/Inputs/Select";
const AdvancedSettings = ({ isAdmin }) => {
const theme = useTheme();
const { user, authToken } = useSelector((state) => state.auth);
const navigate = useNavigate();
const settings = useSelector((state) => state.settings);
const [localSettings, setLocalSettings] = useState(settings);
const logItems = [
{ _id: 1, name: "none" },
{ _id: 2, name: "debug" },
{ _id: 3, name: "error" },
{ _id: 4, name: "warn" },
];
const logItemLookup = {
none: 1,
debug: 2,
error: 3,
warn: 4,
};
const handleLogLevel = (e) => {
const id = e.target.value;
const newLogLevel = logItems.find((item) => item._id === id).name;
setLocalSettings({ ...localSettings, logLevel: newLogLevel });
};
const handleChange = (event) => {
const { value, id } = event.target;
setLocalSettings({ ...localSettings, [id]: value });
};
return (
<Box
className="settings"
style={{
paddingBottom: 0,
}}
>
<Stack
component="form"
gap={theme.spacing(12)}
noValidate
spellCheck="false"
>
\{" "}
<ConfigBox>
<Box>
<Typography component="h1">Client Settings</Typography>
<Typography sx={{ mt: theme.spacing(2) }}>
Modify client settings here
</Typography>
</Box>
<Stack gap={theme.spacing(20)}>
<Field
id="clientHost"
label="Client Host"
value={localSettings.clientHost}
onChange={handleChange}
/>
<Select
id="logLevel"
label="logLevel"
name="logLevel"
items={logItems}
value={logItemLookup[localSettings.logLevel]}
onChange={handleLogLevel}
/>
</Stack>
</ConfigBox>
<ConfigBox>
<Box>
<Typography component="h1">Email Settings</Typography>
<Typography sx={{ mt: theme.spacing(2) }}>
Set your host email settings here. These settings are used for
sending system emails
</Typography>
</Box>
<Stack gap={theme.spacing(20)}>
<Field
type="text"
id="systemEmailHost"
label="Email Host"
name="systemEmailHost"
value={localSettings.systemEmailHost}
onChange={handleChange}
/>
<Field
type="number"
id="systemEmailPort"
label="System Email Address"
name="systemEmailPort"
value={localSettings.systemEmailPort.toString()}
onChange={handleChange}
/>
<Field
type="email"
id="systemEmailAddress"
label="System Email Address"
name="systemEmailAddress"
value={localSettings.systemEmailAddress}
onChange={handleChange}
/>
<Field
type="text"
id="systemEmailPassword"
label="System Email Password"
name="systemEmailPassword"
value={localSettings.systemEmailPassword}
onChange={handleChange}
/>
</Stack>
</ConfigBox>
<ConfigBox>
<Box>
<Typography component="h1">About</Typography>
</Box>
<Box>
<Typography component="h2">BlueWave Uptime v1.0.0</Typography>
<Typography
sx={{ mt: theme.spacing(2), mb: theme.spacing(6), opacity: 0.6 }}
>
Developed by Bluewave Labs.
</Typography>
<Link
level="secondary"
url="https://github.com/bluewave-labs"
label="https://github.com/bluewave-labs"
/>
</Box>
</ConfigBox>
</Stack>
</Box>
);
};
AdvancedSettings.propTypes = {
isAdmin: PropTypes.bool,
};
export default AdvancedSettings;
+16 -28
View File
@@ -1,5 +1,5 @@
import { useTheme } from "@emotion/react";
import { Box, Stack, Typography } from "@mui/material";
import { Box, Stack, Typography, Button } from "@mui/material";
import Field from "../../Components/Inputs/Field";
import Link from "../../Components/Link";
import Select from "../../Components/Inputs/Select";
@@ -13,15 +13,15 @@ import {
deleteAllMonitors,
} from "../../Features/UptimeMonitors/uptimeMonitorsSlice";
import { update } from "../../Features/Auth/authSlice";
import { getAppSettings } from "../../Features/Settings/settingsSlice";
import PropTypes from "prop-types";
import LoadingButton from "@mui/lab/LoadingButton";
import { setTimezone } from "../../Features/UI/uiSlice";
import timezones from "../../Utils/timezones.json";
import { useEffect, useState } from "react";
import { useState } from "react";
import { ConfigBox } from "./styled";
import { networkService } from "../../main";
import { settingsValidation } from "../../Validation/validation";
import { useNavigate } from "react-router";
const SECONDS_PER_DAY = 86400;
@@ -36,24 +36,9 @@ const Settings = ({ isAdmin }) => {
const [form, setForm] = useState({
ttl: checkTTL ? (checkTTL / SECONDS_PER_DAY).toString() : 0,
});
const [settings, setSettings] = useState({
apiBaseUrl: "",
});
const [errors, setErrors] = useState({});
const dispatch = useDispatch();
useEffect(() => {
const fetchSettings = async () => {
const action = await dispatch(getAppSettings({ authToken }));
if (getAppSettings.fulfilled.match(action)) {
const settings = action.payload.data;
setSettings(settings);
} else if (getAppSettings.rejected.match(action)) {
throw new Error(action.error.message);
}
};
fetchSettings();
}, [dispatch, authToken]);
const navigate = useNavigate();
const handleChange = (event) => {
const { value, id } = event.target;
@@ -268,19 +253,22 @@ const Settings = ({ isAdmin }) => {
)}
<ConfigBox>
<Box>
<Typography component="h1">Client Settings</Typography>
<Typography component="h1">Advanced Settings</Typography>
<Typography sx={{ mt: theme.spacing(2) }}>
Here you can modify settings for the client.
Click here to modify advanced settings
</Typography>
</Box>
<Stack gap={theme.spacing(20)}>
<Field
id="apiBaseUrl"
label="Base URL for backend API"
value={settings.apiBaseUrl}
onChange={handleChange}
error={errors["settings-client-base-url"]}
/>
<Box>
<Button
variant="contained"
onClick={() => {
navigate("/advanced-settings");
}}
>
Advanced Settings
</Button>
</Box>
</Stack>
</ConfigBox>
<ConfigBox>
+3 -1
View File
@@ -4,6 +4,7 @@ import uptimeMonitorsReducer from "./Features/UptimeMonitors/uptimeMonitorsSlice
import pageSpeedMonitorReducer from "./Features/PageSpeedMonitor/pageSpeedMonitorSlice";
import authReducer from "./Features/Auth/authSlice";
import uiReducer from "./Features/UI/uiSlice";
import settingsReducer from "./Features/Settings/settingsSlice";
import storage from "redux-persist/lib/storage";
import { persistReducer, persistStore, createTransform } from "redux-persist";
@@ -21,7 +22,7 @@ const authTransform = createTransform(
const persistConfig = {
key: "root",
storage,
whitielist: ["auth", "monitors", "pageSpeed", "ui"],
whitelist: ["auth", "monitors", "pageSpeed", "ui", "settings"],
transforms: [authTransform],
};
@@ -30,6 +31,7 @@ const rootReducer = combineReducers({
auth: authReducer,
pageSpeedMonitors: pageSpeedMonitorReducer,
ui: uiReducer,
settings: settingsReducer,
});
const persistedReducer = persistReducer(persistConfig, rootReducer);