diff --git a/client/src/Components/Inputs/TextInput/index.jsx b/client/src/Components/Inputs/TextInput/index.jsx index 650dc29c5..58e0e17eb 100644 --- a/client/src/Components/Inputs/TextInput/index.jsx +++ b/client/src/Components/Inputs/TextInput/index.jsx @@ -104,6 +104,7 @@ const TextInput = forwardRef( fontSize={"var(--env-var-font-size-medium)"} color={theme.palette.primary.contrastTextSecondary} fontWeight={500} + mb={theme.spacing(2)} > {label} {isRequired && } diff --git a/client/src/Features/Settings/settingsSlice.js b/client/src/Features/Settings/settingsSlice.js index 967858901..02724af02 100644 --- a/client/src/Features/Settings/settingsSlice.js +++ b/client/src/Features/Settings/settingsSlice.js @@ -6,6 +6,7 @@ const initialState = { apiBaseUrl: "", logLevel: "debug", language: "gb", + pagespeedApiKey: "", }; export const getAppSettings = createAsyncThunk( @@ -33,6 +34,7 @@ export const updateAppSettings = createAsyncThunk( try { const parsedSettings = { language: settings.language, + pagespeedApiKey: settings.pagespeedApiKey, }; const res = await networkService.updateAppSettings({ settings: parsedSettings }); return res.data; @@ -56,6 +58,7 @@ const handleGetSettingsFulfilled = (state, action) => { state.apiBaseUrl = action.payload.data.apiBaseUrl; state.logLevel = action.payload.data.logLevel; state.language = action.payload.data.language; + state.pagespeedApiKey = action.payload.data.pagespeedApiKey; }; const handleGetSettingsRejected = (state, action) => { state.isLoading = false; diff --git a/client/src/Pages/Settings/index.jsx b/client/src/Pages/Settings/index.jsx index c208dd63c..554ee6857 100644 --- a/client/src/Pages/Settings/index.jsx +++ b/client/src/Pages/Settings/index.jsx @@ -6,6 +6,8 @@ import Select from "../../Components/Inputs/Select"; import { useIsAdmin } from "../../Hooks/useIsAdmin"; import Dialog from "../../Components/Dialog"; import ConfigBox from "../../Components/ConfigBox"; +import { PasswordEndAdornment } from "../../Components/Inputs/TextInput/Adornments"; +import { getAppSettings } from "../../Features/Settings/settingsSlice"; // import { // WalletMultiButton, // WalletDisconnectButton, @@ -54,8 +56,11 @@ const Settings = () => { const [form, setForm] = useState({ enableDistributedUptime: distributedUptimeEnabled, ttl: checkTTL ? (checkTTL / SECONDS_PER_DAY).toString() : 0, + pagespeedApiKey: "", }); const [version, setVersion] = useState("unknown"); + const [apiKeyFieldType, setApiKeyFieldType] = useState("password"); + const [isApiKeySet, setIsApiKeySet] = useState(false); const [errors, setErrors] = useState({}); const deleteStatsMonitorsInitState = { deleteMonitors: false, deleteStats: false }; const [isOpen, setIsOpen] = useState(deleteStatsMonitorsInitState); @@ -80,6 +85,28 @@ const Settings = () => { fetchLatestVersion(); }, []); + useEffect(() => { + dispatch(getAppSettings()); + }, []); + + const { pagespeedApiKey } = useSelector((state) => state.settings); + + useEffect(() => { + if (pagespeedApiKey) { + setIsApiKeySet(true); + setForm((prev) => ({ + ...prev, + pagespeedApiKey: t("maskedPageSpeedKeyPlaceholder"), + })); + } else { + setIsApiKeySet(false); + setForm((prev) => ({ + ...prev, + pagespeedApiKey: "", + })); + } + }, [pagespeedApiKey]); + const handleChange = (event) => { const { type, checked, value, id } = event.target; @@ -91,12 +118,17 @@ const Settings = () => { return; } + let inputValue = value; + if (id === "ttl") { + inputValue = value.replace(/[^0-9]/g, ""); + } + + const updatedForm = { ...form, [id]: inputValue }; const { error } = settingsValidation.validate( - { [id]: value }, - { - abortEarly: false, - } + updatedForm, + { abortEarly: false } ); + if (!error || error.details.length === 0) { setErrors({}); } else { @@ -107,12 +139,8 @@ const Settings = () => { setErrors(newErrors); logger.error("Validation errors:", error.details); } - let inputValue = value; - id === "ttl" && (inputValue = value.replace(/[^0-9]/g, "")); - setForm((prev) => ({ - ...prev, - [id]: inputValue, - })); + + setForm(updatedForm); }; // TODO Handle saving @@ -125,7 +153,7 @@ const Settings = () => { const updatedUser = { ...user, checkTTL: form.ttl }; const [userAction, settingsAction] = await Promise.all([ dispatch(update({ localData: updatedUser })), - dispatch(updateAppSettings({ settings: { language: language } })), + dispatch(updateAppSettings({ settings: { language: language, pagespeedApiKey: form.pagespeedApiKey } })), ]); if (userAction.payload.success && settingsAction.payload.success) { @@ -187,6 +215,14 @@ const Settings = () => { } }; + const handleResetApiKey = () => { + setIsApiKeySet(false); + setForm((prev) => ({ + ...prev, + pagespeedApiKey: "", + })); + }; + const languages = Object.keys(i18n.options.resources || {}); return ( @@ -277,6 +313,43 @@ const Settings = () => { )} */} + + + {t("pageSpeedApiKeyFieldTitle")} + + {t("pageSpeedApiKeyFieldDescription")} + + + + + ) + } + /> + {isApiKeySet && ( + + {t("pageSpeedApiKeyFieldResetLabel")} + + + )} + + {/* {isAdmin && ( diff --git a/client/src/Validation/validation.js b/client/src/Validation/validation.js index 01a2fd48c..6f27b8f32 100644 --- a/client/src/Validation/validation.js +++ b/client/src/Validation/validation.js @@ -257,7 +257,9 @@ const settingsValidation = joi.object({ "number.base": "Please enter a valid number", "any.required": "Please enter a value" }), -}); + pagespeedApiKey: joi.string().allow("").optional(), +}) +.unknown(true); const dayjsValidator = (value, helpers) => { if (!dayjs(value).isValid()) { diff --git a/client/src/locales/gb.json b/client/src/locales/gb.json index a2db25374..46174c62c 100644 --- a/client/src/locales/gb.json +++ b/client/src/locales/gb.json @@ -430,6 +430,12 @@ "noFileSelected": "No file selected", "fallbackPage": "Import a file to upload a list of servers in bulk" }, - "publicLink": "Public link" + "publicLink": "Public link", + "maskedPageSpeedKeyPlaceholder": "*************************************", + "pageSpeedApiKeyFieldTitle": "Google PageSpeed API key", + "pageSpeedApiKeyFieldLabel": "PageSpeed API key", + "pageSpeedApiKeyFieldDescription": "Enter your Google PageSpeed API key to enable pagespeed monitoring. Click Reset to update the key.", + "pageSpeedApiKeyFieldResetLabel": "API key is set. Click Reset to change it.", + "reset": "Reset" }