diff --git a/Client/package-lock.json b/Client/package-lock.json
index 73edbf7fb..a4462bddf 100644
--- a/Client/package-lock.json
+++ b/Client/package-lock.json
@@ -12,6 +12,7 @@
"@emotion/styled": "^11.11.5",
"@fontsource/roboto": "^5.0.13",
"@mui/icons-material": "^5.15.17",
+ "@mui/lab": "^5.0.0-alpha.170",
"@mui/material": "^5.15.16",
"@mui/x-charts": "^7.5.1",
"@mui/x-data-grid": "7.3.2",
@@ -1152,6 +1153,46 @@
}
}
},
+ "node_modules/@mui/lab": {
+ "version": "5.0.0-alpha.170",
+ "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-5.0.0-alpha.170.tgz",
+ "integrity": "sha512-0bDVECGmrNjd3+bLdcLiwYZ0O4HP5j5WSQm5DV6iA/Z9kr8O6AnvZ1bv9ImQbbX7Gj3pX4o43EKwCutj3EQxQg==",
+ "dependencies": {
+ "@babel/runtime": "^7.23.9",
+ "@mui/base": "5.0.0-beta.40",
+ "@mui/system": "^5.15.15",
+ "@mui/types": "^7.2.14",
+ "@mui/utils": "^5.15.14",
+ "clsx": "^2.1.0",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.5.0",
+ "@emotion/styled": "^11.3.0",
+ "@mui/material": ">=5.15.0",
+ "@types/react": "^17.0.0 || ^18.0.0",
+ "react": "^17.0.0 || ^18.0.0",
+ "react-dom": "^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ },
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@mui/material": {
"version": "5.15.19",
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.19.tgz",
@@ -2647,7 +2688,6 @@
"integrity": "sha512-NglN/xprcM+SHD2XCli4oC6bWe6kHoytcyLKCWXmRL854F0qhPhaYgUswUsglnPxYaNQIg2uMY4BvaomIf3kLA==",
"dev": true,
"license": "ISC"
-
},
"node_modules/error-ex": {
"version": "1.3.2",
diff --git a/Client/package.json b/Client/package.json
index 505fe25d8..f5df1b5f8 100644
--- a/Client/package.json
+++ b/Client/package.json
@@ -14,6 +14,7 @@
"@emotion/styled": "^11.11.5",
"@fontsource/roboto": "^5.0.13",
"@mui/icons-material": "^5.15.17",
+ "@mui/lab": "^5.0.0-alpha.170",
"@mui/material": "^5.15.16",
"@mui/x-charts": "^7.5.1",
"@mui/x-data-grid": "7.3.2",
diff --git a/Client/src/Components/ButtonSpinner/index.jsx b/Client/src/Components/ButtonSpinner/index.jsx
new file mode 100644
index 000000000..2cfdaad5d
--- /dev/null
+++ b/Client/src/Components/ButtonSpinner/index.jsx
@@ -0,0 +1,105 @@
+import LoadingButton from "@mui/lab/LoadingButton";
+import PropTypes from "prop-types";
+
+const levelConfig = {
+ primary: {
+ variant: "contained",
+ color: "primary",
+ },
+ secondary: {
+ variant: "outlined",
+ color: "secondary",
+ },
+ tertiary: {
+ variant: "text",
+ color: "tertiary",
+ },
+ error: {
+ variant: "contained",
+ color: "error",
+ },
+ imagePrimary: {
+ color: "primary",
+ variant: "contained",
+ },
+ imageSecondary: {
+ color: "secondary",
+ variant: "outlined",
+ },
+ imageTertiary: {
+ color: "tertiary",
+ variant: "text",
+ },
+};
+
+/**
+ * ButtonSpinner component displays a button with loading loadingText capability.
+ * @component
+ * @param {Object} props
+ * @param {'primary' | 'secondary' | 'tertiary' | 'error' | 'imagePrimary' | 'imageSecondary' | 'imageTertiary'} props.level - The style level of the button.
+ * @param {string} props.label - The label text displayed on the button.
+ * @param {React.ReactNode} [props.img] - Icon or image element to display within the button.
+ * @param {boolean} [props.disabled=false] - Determines if the button is disabled.
+ * @param {string} [props.loadingText] - Text displayed when the button is in a loading state.
+ * @param {'start' | 'end'} [props.position] - Specifies where the icon or loading loadingText should be positioned relative to the label.
+ * @param {function} props.onClick - Callback function invoked when the button is clicked.
+ * @param {boolean} props.isLoading - Indicates if the button is in a loading state.
+ * @param {Object} [props.sx] - Additional styles to apply to the button.
+ * @returns {JSX.Element}
+ * @example
+ * // Render a primary button with an icon at the end
+ *