From db5647d7e15543337e6e06997c2c4d9d4a3fe73e Mon Sep 17 00:00:00 2001 From: Chad Elliott Date: Fri, 27 Sep 2024 10:58:30 -0500 Subject: [PATCH 1/2] Group settings by category and display with category title. Standardized the setting handlers, converted setting name to human readable string (for display), and show warning if user tries to use the file upload setting. --- web-ui/src/pages/SettingsPage.jsx | 73 ++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 17 deletions(-) diff --git a/web-ui/src/pages/SettingsPage.jsx b/web-ui/src/pages/SettingsPage.jsx index ae31a7548..ea918ca92 100644 --- a/web-ui/src/pages/SettingsPage.jsx +++ b/web-ui/src/pages/SettingsPage.jsx @@ -1,7 +1,7 @@ import React, { useContext, useEffect, useState } from 'react'; import { UPDATE_TOAST } from '../context/actions'; import { AppContext } from '../context/AppContext'; -import { Button } from '@mui/material'; +import { Button, Typography } from '@mui/material'; import { SettingsBoolean, SettingsColor, @@ -30,6 +30,8 @@ const SettingsPage = () => { const [settingsControls, setSettingsControls] = useState([]); const [update, setState] = useState(); + const categories = {}; + useEffect(() => { const fetchData = async () => { // Get the options from the server @@ -40,37 +42,61 @@ const SettingsPage = () => { // create new settings and PUT to modify existing settings. for (let option of allOptions) { option.exists = option.id != '00000000-0000-0000-0000-000000000000'; + + // Add this category and mark it as not displayed as of yet. + categories[option.category] = false; } - // Store them and upate the state. - setSettingsControls(allOptions); + // Sort the options by category, store them, and upate the state. + setSettingsControls( + allOptions.sort((l, r) => l.category.localeCompare(r.category))); }; fetchData(); }, []); - /* - for specific settings, add a handleFunction to the settings object - format should be handleSetting and then add it to the handlers object - with the setting name as the key - */ + // Replace all underscores with spaces, lowercase the string, and + // capitalize each word. + const humanize = (name) => { + return name.toLowerCase().replaceAll('_', ' ') + .replace(/(?:^|\s|["'([{])+\S/g, match => match.toUpperCase()); + }; + + + // For specific settings, add a handleFunction to the settings object. + // Format should be handleSetting and then add it to the handlers object + // with the setting name as the key. const handleLogoUrl = file => { if (csrf) { - // TODO: need to have a storage bucket to upload the file to + // TODO: Need to upload the file to a storage bucket... + dispatch({ + type: UPDATE_TOAST, + payload: { + severity: 'warning', + toast: "The Logo URL setting has yet to be implemented.", + } + }); } }; - const handlePulseEmailFrequency = (event) => { - const key = 'PULSE_EMAIL_FREQUENCY'; + const keyedHandler = (key, event) => { if (handlers[key]) { handlers[key].setting.value = event.target.value; setState({update: true}); } }; + const handlePulseEmailFrequency = (event) => { + keyedHandler('PULSE_EMAIL_FREQUENCY', event); + }; + const handlers = { // File handlers do not modify settings values and, therefore, do not - // need to keep a reference to the setting object. - LOGO_URL: handleLogoUrl, + // need to keep a reference to the setting object. However, they do need + // a file reference object. + LOGO_URL: { + onChange: handleLogoUrl, + setting: fileRef, + }, // All others need to provide an `onChange` method and a `setting` object. PULSE_EMAIL_FREQUENCY: { @@ -86,8 +112,8 @@ const SettingsPage = () => { if (setting.type.toUpperCase() === 'FILE') { return { ...setting, - handleFunction: handler, - fileRef: fileRef + handleFunction: handler.onChange, + fileRef: handler.setting, }; } @@ -95,7 +121,7 @@ const SettingsPage = () => { return { ...setting, handleChange: handler.onChange }; } - console.log(`WARNING: No handler for ${setting.name}`); + console.warn(`WARNING: No handler for ${setting.name}`); return setting; }); }; @@ -165,7 +191,20 @@ const SettingsPage = () => {
{updatedSettingsControls.map((componentInfo, index) => { const Component = componentMapping[componentInfo.type.toUpperCase()]; - return ; + const info = {...componentInfo, name: humanize(componentInfo.name)}; + if (categories[info.category]) { + return ; + } else { + categories[info.category] = true; + return ( + <> + {humanize(info.category)} + + + ); + } })}