Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions web-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,12 @@
"universal-cookie": "^4.0.4"
},
"scripts": {
"build-ladle": "ladle build",
"build-storybook": "storybook build",
"build": "vite build",
"coverage": "vitest run --coverage --watch=false",
"format": "prettier --write 'src/**/*.{css,html,js,jsx,ts,tsx}'",
"ladle": "ladle serve",
"lint": "eslint --fix --quiet",
"serve": "vite preview",
"start": "vite",
Expand All @@ -74,6 +76,7 @@
]
},
"devDependencies": {
"@ladle/react": "^4.0.3",
"@storybook/addon-actions": "^8.0.4",
"@storybook/addon-essentials": "^8.0.4",
"@storybook/addon-links": "^8.0.4",
Expand Down
2 changes: 1 addition & 1 deletion web-ui/src/api/api.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {UPDATE_TOAST} from '../context/actions';
import { UPDATE_TOAST } from '../context/actions';
import qs from 'qs';

export const BASE_API_URL = import.meta.env.VITE_APP_API_URL
Expand Down
3 changes: 2 additions & 1 deletion web-ui/src/components/menu/Menu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ const adminLinks = [
['/admin/roles', 'Roles'],
['/admin/users', 'Users'],
['/admin/email', 'Send Email'],
['/admin/edit-skills', 'Skills']
['/admin/edit-skills', 'Skills'],
['/admin/settings', 'Settings']
];

const checkInLinks = [
Expand Down
5 changes: 5 additions & 0 deletions web-ui/src/components/routes/Routes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import BirthdayReportPage from '../../pages/BirthdayReportPage';
import CheckinsPage from '../../pages/CheckinsPage';
import CheckinsReportPage from '../../pages/CheckinsReportPage';
import EditSkillsPage from '../../pages/EditSkillsPage';
import SettingsPage from '../../pages/SettingsPage';
import EditPermissionsPage from '../../pages/PermissionsPage';
import GroupIcon from '@mui/icons-material/Group';
import GuildsPage from '../../pages/GuildsPage';
Expand Down Expand Up @@ -108,6 +109,10 @@ export default function Routes() {
<Header title="Skills" />
<EditSkillsPage />
</Route>
<Route path="/admin/settings">
<Header title="Settings" />
<SettingsPage />
</Route>
<Route path="/admin/permissions">
<Header title="Permissions" />
<EditPermissionsPage />
Expand Down
5 changes: 5 additions & 0 deletions web-ui/src/components/settings/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export { default as SettingsBoolean } from './types/boolean.jsx';
export { default as SettingsColor } from './types/color.jsx';
export { default as SettingsFile } from './types/file.jsx';
export { default as SettingsNumber } from './types/number.jsx';
export { default as SettingsString } from './types/string.jsx';
38 changes: 38 additions & 0 deletions web-ui/src/components/settings/types/boolean.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';
import { Switch, Typography } from '@mui/material';
import { createLabelId } from '../../../helpers/strings.js';

/**
* A component for rendering a boolean input field in the settings.
*
* @component
* @param {Object} props
* @param {string} props.label - The label for the settings component.
* @param {string} [props.description] - The description for the settings component.
* @param {boolean} props.value - The value of the settings component.
* @param {Function} props.handleChange - The function to handle the change event of the settings component.
* @returns {JSX.Element} The rendered boolean settings component.
*/
const SettingsBoolean = ({ label, description, value, handleChange }) => {
const labelId = createLabelId(label);

return (
<div className="settings-type">
<label htmlFor={labelId}>
<Typography variant="h5" gutterBottom>
{label}
</Typography>
</label>
{description ?? <p>{description}</p>}
<Switch
id={labelId}
className="settings-control"
type="checkbox"
checked={value}
onChange={handleChange}
/>
</div>
);
};

export default SettingsBoolean;
26 changes: 26 additions & 0 deletions web-ui/src/components/settings/types/boolean.stories.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';
import SettingsBoolean from './boolean';
import '../../../pages/SettingsPage.css';

export default {
component: SettingsBoolean,
title: 'Check Ins/Settings',
decorators: [
SettingsBoolean => (
<div className="settings-page">
<SettingsBoolean />
</div>
)
]
};

const Template = args => {
return <SettingsBoolean {...args} />;
};

export const Boolean = Template.bind({});
Boolean.args = {
label: 'Boolean Control',
description: 'A control to hold a boolean value',
handleChange: event => console.log(`BOOLEAN ${event.target.checked}`)
};
38 changes: 38 additions & 0 deletions web-ui/src/components/settings/types/color.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';
import { Typography } from '@mui/material';
import { createLabelId } from '../../../helpers/strings.js';

/**
* A component for rendering a color picker field in the settings.
*
* @component
* @param {Object} props
* @param {string} props.label - The label for the setting.
* @param {string} [props.description] - The description for the setting (optional).
* @param {string} props.value - The current value of the setting.
* @param {function} props.handleChange - The function to handle changes to the setting value (optional).
* @returns {JSX.Element} The rendered component.
*/
const SettingsColor = ({ label, description, value, handleChange }) => {
const labelId = createLabelId(label);

return (
<div className="settings-type">
<label htmlFor={labelId}>
<Typography variant="h5" gutterBottom>
{label}
</Typography>
</label>
{description ?? <p>{description}</p>}
<input
id={labelId}
className="settings-control"
type="color"
value={value}
onChange={handleChange}
/>
</div>
);
};

export default SettingsColor;
27 changes: 27 additions & 0 deletions web-ui/src/components/settings/types/color.stories.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react';
import SettingsColor from './color';
import '../../../pages/SettingsPage.css';

export default {
component: SettingsColor,
title: 'Check Ins/Settings',
decorators: [
SettingsColor => (
<div className="settings-page">
<SettingsColor />
</div>
)
]
};

const Template = args => {
return <SettingsColor {...args} />;
};

export const Color = Template.bind({});
Color.args = {
label: 'Color Control',
description: 'A control to hold a color value',
value: '#2c519e',
handleChange: event => console.log(`COLOR: ${event.target.value}`)
};
54 changes: 54 additions & 0 deletions web-ui/src/components/settings/types/file.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React from 'react';
import { Button, Typography } from '@mui/material';
import { AddCircle } from '@mui/icons-material';
import { createLabelId } from '../../../helpers/strings.js';

/**
* A component for handling file settings.
*
* @component
* @param {Object} props - The component props.
* @param {string} props.label - The label of the file settings.
* @param {string} [props.description] - The description of the file settings.
* @param {Object} props.fileRef - The reference to the file input element.
* @param {Function} props.handleFile - The function to handle file upload.
* @returns {JSX.Element} The rendered component.
*/
const SettingsFile = ({ fileRef, handleFile, label, description }) => {
const labelId = createLabelId(label);

const handleClick = event => {
fileRef.current.click();
};

const handleChange = event => {
const fileUploaded = event.target.files[0];
handleFile(fileUploaded);
};

return (
<div className="settings-type">
<label htmlFor={labelId}>
<Typography variant="h5" gutterBottom>
{label}
</Typography>
</label>
{description ?? <p>{description}</p>}
<div className="settings-control">
<Button onClick={handleClick}>
<AddCircle />
&nbsp; Upload
</Button>
<input
id={labelId}
type="file"
ref={fileRef}
onChange={handleChange}
className="hidden"
/>
</div>
</div>
);
};

export default SettingsFile;
29 changes: 29 additions & 0 deletions web-ui/src/components/settings/types/file.stories.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react';
import SettingsFile from './file';
import '../../../pages/SettingsPage.css';

const fileRef = null;

export default {
component: SettingsFile,
title: 'Check Ins/Settings',
decorators: [
SettingsFile => (
<div className="settings-page">
<SettingsFile />
</div>
)
]
};

const Template = args => {
return <SettingsFile {...args} />;
};

export const File = Template.bind({});
File.args = {
label: 'File Control',
description: 'A control to upload a File',
fileRef,
handleFile: event => console.log(`File: ${event.target.value}`)
};
38 changes: 38 additions & 0 deletions web-ui/src/components/settings/types/number.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';
import { Input, Typography } from '@mui/material';
import { createLabelId } from '../../../helpers/strings.js';

/**
* A component for rendering a number input field in the settings.
*
* @component
* @param {Object} props
* @param {string} props.label - The label for the number input field.
* @param {string} [props.description] - The description for the input field (optional).
* @param {number} props.value - The current value of the number input field.
* @param {function} props.handleChange - The callback function to handle value changes.
* @returns {JSX.Element} - The rendered component.
*/
const SettingsNumber = ({ label, description, value, handleChange }) => {
const labelId = createLabelId(label);

return (
<div className="settings-type">
<label htmlFor={labelId}>
<Typography variant="h5" gutterBottom>
{label}
</Typography>
</label>
{description ?? <p>{description}</p>}
<Input
id={labelId}
className="settings-control"
type="number"
value={value}
onChange={handleChange}
/>
</div>
);
};

export default SettingsNumber;
25 changes: 25 additions & 0 deletions web-ui/src/components/settings/types/number.stories.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';
import SettingsNumber from './number';
import '../../../pages/SettingsPage.css';

export default {
component: SettingsNumber,
title: 'Check Ins/Settings',
decorators: [
SettingsNumber => (
<div className="settings-page">
<SettingsNumber />
</div>
)
]
};

const Template = args => {
return <SettingsNumber {...args} />;
};

export const Number = Template.bind({});
Number.args = {
label: 'Number Control',
description: 'A control to hold a numerical value'
};
46 changes: 46 additions & 0 deletions web-ui/src/components/settings/types/string.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from 'react';
import { Input, Typography } from '@mui/material';
import { createLabelId } from '../../../helpers/strings.js';

/**
* A component for rendering a number input field in the settings.
*
* @component
* @param {Object} props
* @param {string} props.label - The label for the input field.
* @param {string} [props.description] - The description for the input field (optional).
* @param {string} props.value - The current value of the input field.
* @param {string} [props.placeholder] - The placeholder text for the input field (optional).
* @param {function} props.handleChange - The function to handle input field changes.
* @returns {JSX.Element} - The rendered component.
*/
const SettingsString = ({
label,
description,
value,
placeholder,
handleChange
}) => {
const labelId = createLabelId(label);

return (
<div className="settings-type">
<label htmlFor={labelId}>
<Typography variant="h5" gutterBottom>
{label}
</Typography>
</label>
{description ?? <p>{description}</p>}
<Input
id={labelId}
className="settings-control"
type="text"
value={value}
placeholder={placeholder ?? `Enter ${label}`}
onChange={handleChange}
/>
</div>
);
};

export default SettingsString;
Loading