From 179e95eefe4fc371de2463e2bd535c38cb2e3287 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 26 Sep 2024 16:18:25 +0800 Subject: [PATCH] add redux state for settings --- Client/src/Features/Settings/settingsSlice.js | 119 ++++++++++++++++++ Client/src/Pages/Settings/index.jsx | 36 +++++- Client/src/Utils/NetworkService.js | 21 ++++ 3 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 Client/src/Features/Settings/settingsSlice.js diff --git a/Client/src/Features/Settings/settingsSlice.js b/Client/src/Features/Settings/settingsSlice.js new file mode 100644 index 000000000..0a7cb52c1 --- /dev/null +++ b/Client/src/Features/Settings/settingsSlice.js @@ -0,0 +1,119 @@ +import { networkService } from "../../main"; +import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; +import { jwtDecode } from "jwt-decode"; +import axios from "axios"; + +const initialState = { + isLoading: false, + apiBaseUrl: "http://localhost:5000/api/v1", + logLevel: "debug", + clientHost: "http://localhost:5173", + jwtSecret: "my_secret", + dbType: "MongoDB", + dbConnectionString: "mongodb://localhost:27017/uptime_db", + redisHost: "127.0.0.1", + redisPort: 6379, + jwtTTL: "99d", + pagespeedApiKey: "", + systemEmailHost: "smtp.gmail.com", + systemEmailPort: 465, + systemEmailAddress: "", + systemEmailPassword: "", +}; + +export const getAppSettings = createAsyncThunk( + "settings/getSettings", + async (data, thunkApi) => { + try { + const res = await networkService.getAppSettings({ + authToken: data.authToken, + }); + return res.data; + } catch (error) { + if (error.response.data) { + return thunkApi.rejectWithValue(error.response.data); + } + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); + } + } +); + +export const updateAppSettings = createAsyncThunk( + "settings/updateSettings", + async (form, thunkApi) => { + try { + const res = await networkService.loginUser(form); + return res.data; + } catch (error) { + if (error.response && error.response.data) { + return thunkApi.rejectWithValue(error.response.data); + } + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); + } + } +); + +const handleGetSettingsFulfilled = (state, action) => { + state.isLoading = false; + state.success = action.payload.success; + state.msg = action.payload.msg; +}; +const handleGetSettingsRejected = (state, action) => { + state.isLoading = false; + state.success = false; + state.msg = action.payload ? action.payload.msg : "Failed to get settings."; +}; +const handleUpdateSettingsFulfilled = (state, action) => { + state.isLoading = false; + state.success = action.payload.success; + state.msg = action.payload.msg; +}; +const handleUpdateSettingsRejected = (state, action) => { + state.isLoading = false; + state.success = false; + state.msg = action.payload + ? action.payload.msg + : "Failed to update settings."; +}; + +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; + }) + .addCase(getAppSettings.fulfilled, handleGetSettingsFulfilled) + .addCase(getAppSettings.rejected, handleGetSettingsRejected); + + // Login thunk + builder + .addCase(updateAppSettings.pending, (state) => { + state.isLoading = true; + }) + .addCase(updateAppSettings.fulfilled, handleUpdateSettingsFulfilled) + .addCase(updateAppSettings.rejected, handleUpdateSettingsRejected); + }, +}); + +export default settingsSlice.reducer; +export const { clearAuthState } = settingsSlice.actions; diff --git a/Client/src/Pages/Settings/index.jsx b/Client/src/Pages/Settings/index.jsx index 2e59c8187..4a7ed13a9 100644 --- a/Client/src/Pages/Settings/index.jsx +++ b/Client/src/Pages/Settings/index.jsx @@ -13,11 +13,12 @@ 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 { useState } from "react"; +import { useEffect, useState } from "react"; import { ConfigBox } from "./styled"; import { networkService } from "../../main"; import { settingsValidation } from "../../Validation/validation"; @@ -35,9 +36,25 @@ 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 handleChange = (event) => { const { value, id } = event.target; const { error } = settingsValidation.validate( @@ -249,6 +266,23 @@ const Settings = ({ isAdmin }) => { )} + + + Client Settings + + Here you can modify settings for the client. + + + + + + About diff --git a/Client/src/Utils/NetworkService.js b/Client/src/Utils/NetworkService.js index 905385e70..9545fb247 100644 --- a/Client/src/Utils/NetworkService.js +++ b/Client/src/Utils/NetworkService.js @@ -619,6 +619,27 @@ class NetworkService { } ); } + + /** + * ************************************ + * Get app settings + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.authToken - The authorization token to be used in the request header. + * @returns {Promise} The response from the axios GET request. + * + */ + + async getAppSettings(config) { + return this.axiosInstance.get("/settings", { + headers: { + Authorization: `Bearer ${config.authToken}`, + "Content-Type": "application/json", + }, + }); + } } export default NetworkService;