diff --git a/Client/src/Components/Announcements/AnnouncementUpdateSubscription/AnnouncementUpdateSubscription.jsx b/Client/src/Components/Announcements/AnnouncementUpdateSubscription/AnnouncementUpdateSubscription.jsx
new file mode 100644
index 000000000..f3363e866
--- /dev/null
+++ b/Client/src/Components/Announcements/AnnouncementUpdateSubscription/AnnouncementUpdateSubscription.jsx
@@ -0,0 +1,71 @@
+import "./announcementUpdateSubscription.css";
+import React from "react";
+import PropTypes from "prop-types";
+import { useTheme } from "@mui/material";
+
+/**
+ * @component
+ * @param {Object} props
+ * @param {string} props.title - Title text for the update subscription dialog (required)
+ * @param {string} props.text - Body text for the update subscription dialog (required)
+ * @param {string} props.cancel - Text for the negative (cancel) button (required)
+ * @param {string} props.positive - Text for the positive button (required)
+ * @param {string} props.header - Title text for the email subscription section (required)
+ * @param {string} props.button - Text for the subscribe button (required)
+ * @returns {JSX.Element} - Renders the announcement update subscription component
+ */
+
+const AnnouncementUpdateSubscription = ({
+ title,
+ text,
+ cancel,
+ positive,
+ header,
+ button,
+}) => {
+ const theme = useTheme();
+
+ const fontLookup = {
+ default: theme.font.default.font,
+ };
+
+ const fontFamily = fontLookup["default"];
+
+ return (
+
+
+
{title}
+
+
{text}
+
+
+
+
+
+
+
+
+
{header}
+
+
+
+
+
+
+ );
+};
+
+AnnouncementUpdateSubscription.propTypes = {
+ title: PropTypes.string.isRequired,
+ text: PropTypes.string.isRequired,
+ cancel: PropTypes.string.isRequired,
+ positive: PropTypes.string.isRequired,
+ header: PropTypes.string.isRequired,
+ button: PropTypes.string.isRequired,
+};
+
+export default AnnouncementUpdateSubscription;
diff --git a/Client/src/Components/Announcements/AnnouncementUpdateSubscription/announcementUpdateSubscription.css b/Client/src/Components/Announcements/AnnouncementUpdateSubscription/announcementUpdateSubscription.css
new file mode 100644
index 000000000..7974ee799
--- /dev/null
+++ b/Client/src/Components/Announcements/AnnouncementUpdateSubscription/announcementUpdateSubscription.css
@@ -0,0 +1,99 @@
+:root {
+ --update-subscription-color-0: #101828;
+ --update-subscription-color-1: #475467;
+ --update-subscription-color-2: #fff;
+ --update-subscription-color-3: #eaecf0;
+ --update-subscription-color-4: #7f56d9;
+ --update-subscription-color-5: #1570ef;
+ --update-subscription-color-6: #344054;
+ --update-subscription-color-7: #d0d5dd;
+ --update-subscription-padding-0: 12px 16px;
+ --update-subscription-margin-right: 12px;
+ --update-subscription-font-size: 13px;
+ --update-subscription-radius-0: 4px;
+ --spacing-1: 16px;
+ --spacing-2: 20px;
+}
+
+.update-subscription,
+.update-subscription-dialog {
+ max-width: 770px;
+ border-radius: 4px;
+ border: 1px solid #eaecf0;
+}
+
+.update-subscription-dialog {
+ padding: 15px 30px;
+}
+
+.subscription-dialog-title {
+ font-size: 1rem;
+ font-weight: 700;
+ color: var(--update-subscription-color-0);
+}
+
+.v-spacing-small {
+ height: 10px;
+}
+
+.v-spacing-medium {
+ height: 15px;
+}
+
+.subscription-dialog-text {
+ font-size: 13px;
+ color: var(--update-subscription-color-1);
+}
+
+.subscription-dialog-controls {
+ display: flex;
+}
+
+.controls-negative {
+ padding: var(--update-subscription-padding-0);
+ margin-right: var(--update-subscription-margin-right);
+ border-radius: var(--update-subscription-radius-0);
+ border: 1px solid var(--update-subscription-color-3);
+ background-color: var(--update-subscription-color-2);
+ font-size: var(--update-subscription-font-size);
+}
+
+.controls-positive,
+.subscribe-button {
+ padding: var(--update-subscription-padding-0);
+ margin-right: var(--update-subscription-margin-right);
+ border-radius: var(--update-subscription-radius-0);
+ border: 1px solid var(--update-subscription-color-4);
+ background-color: var(--update-subscription-color-5);
+ color: var(--update-subscription-color-2);
+ font-size: var(--update-subscription-font-size);
+}
+
+.update-subscription-title {
+ color: var(--update-subscription-color-6);
+ padding: 0 var(--spacing-2);
+ font-weight: 700;
+ font-size: 13px;
+ margin-bottom: 10px;
+}
+
+.subscription-email {
+ display: flex;
+ padding: 0 var(--spacing-2);
+}
+
+.subscription-email-field {
+ width: 290px;
+ height: 24px;
+ border-radius: var(--update-subscription-radius-0);
+ border: 1px solid var(--update-subscription-color-7);
+ box-shadow: 0px 1px 2px 0px rgba(101, 182, 28, 0.05);
+ padding: 5px 15px;
+ margin-right: var(--spacing-2);
+}
+
+.subscribe-button {
+ width: 100px;
+ height: 34px;
+ padding: 2px 15px;
+}
diff --git a/Client/src/Components/Announcements/AnnouncementsDualButton/AnnouncementsDualButton.jsx b/Client/src/Components/Announcements/AnnouncementsDualButton/AnnouncementsDualButton.jsx
new file mode 100644
index 000000000..2abbadb1e
--- /dev/null
+++ b/Client/src/Components/Announcements/AnnouncementsDualButton/AnnouncementsDualButton.jsx
@@ -0,0 +1,55 @@
+import "./announcementsDualButton.css";
+import React from "react";
+import CloseIcon from "@mui/icons-material/Close";
+import PropTypes from "prop-types";
+import { useTheme } from "@mui/material";
+
+/**
+ * @component
+ * @param {Object} props
+ * @param {string} props.subject - The announcement subject text
+ * @param {string} props.body - The announcement body text content
+ * @param {string} props.esc - The text for the escape button
+ * @param {string} props.primary - The text for the primary button
+ * @returns {JSX.Element} - Renders the announcement dual button component
+ */
+
+const AnnouncementsDualButton = ({ subject, body, esc, primary }) => {
+ const theme = useTheme();
+
+ const fontLookup = {
+ default: theme.font.default.font,
+ };
+
+ const fontFamily = fontLookup["default"];
+
+ return (
+
+
+
{subject}
+
+
{body}
+
+
+
+
+
+
+
+ );
+};
+
+AnnouncementsDualButton.propTypes = {
+ subject: PropTypes.string.isRequired,
+ body: PropTypes.string.isRequired,
+ esc: PropTypes.string.isRequired,
+ primary: PropTypes.string.isRequired,
+};
+
+export default AnnouncementsDualButton;
diff --git a/Client/src/Components/Announcements/AnnouncementsDualButton/announcementsDualButton.css b/Client/src/Components/Announcements/AnnouncementsDualButton/announcementsDualButton.css
new file mode 100644
index 000000000..837fd0bb5
--- /dev/null
+++ b/Client/src/Components/Announcements/AnnouncementsDualButton/announcementsDualButton.css
@@ -0,0 +1,38 @@
+:root {
+ --announcement-border-color: #d0d5dd;
+ --announcement-border-radius: 4px;
+ --announcement-border-radius-2: 8px;
+ /* --announcement-font-family: "Roboto", "Helvetica", "Arial", sans-serif; */
+ --announcement-font-size-0: 13px;
+ --announcement-font-color-0: #344054;
+ --announcement-font-color-1: #475467;
+ --announcement-font-color-2: #1570ef;
+ --announcement-padding: 16px;
+ --info-icon-frame-color: #eaecf0;
+}
+
+.announcement-without-tile {
+ max-width: 343px;
+ width: fit-content;
+ display: flex;
+ border: 1px solid var(--announcement-border-color);
+ /* font-family: var(--announcement-font-family); */
+ padding: var(--announcement-padding);
+ border-radius: var(--announcement-border-radius);
+}
+
+.h-spacing {
+ width: 20px;
+}
+
+.announcement-without-content-subject {
+ font-size: var(--announcement-font-size-0);
+ color: var(--announcement-font-color-0);
+ font-weight: 700;
+ margin-bottom: 10px;
+}
+
+.announcement-without-content-body {
+ font-size: var(--announcement-font-size-0);
+ color: var(--announcement-font-color-1);
+}
diff --git a/Client/src/Components/Announcements/AnnouncementsDualButtonWithIcon/announcementsDualButtonWithIcon.css b/Client/src/Components/Announcements/AnnouncementsDualButtonWithIcon/announcementsDualButtonWithIcon.css
new file mode 100644
index 000000000..80c6ae265
--- /dev/null
+++ b/Client/src/Components/Announcements/AnnouncementsDualButtonWithIcon/announcementsDualButtonWithIcon.css
@@ -0,0 +1,69 @@
+:root {
+ --announcement-border-color: #d0d5dd;
+ --announcement-border-radius: 4px;
+ --announcement-border-radius-2: 8px;
+ --announcement-font-size-0: 13px;
+ --announcement-font-color-0: #344054;
+ --announcement-font-color-1: #475467;
+ --announcement-font-color-2: #1570ef;
+ --announcement-padding: 16px;
+ --info-icon-frame-color: #eaecf0;
+}
+
+.announcement-tile {
+ width: fit-content;
+ display: flex;
+ border: 1px solid var(--announcement-border-color);
+ padding: var(--announcement-padding);
+ border-radius: var(--announcement-border-radius);
+}
+
+.info-icon-frame {
+ display: flex;
+ width: 40px;
+ height: 40px;
+ border: 1px solid var(--info-icon-frame-color);
+ border-radius: var(--announcement-border-radius-2);
+ justify-content: center;
+ align-items: center;
+}
+
+.announcement-content {
+ padding: 0 var(--announcement-padding);
+}
+
+.announcement-content-subject {
+ font-size: var(--announcement-font-size-0);
+ color: var(--announcement-font-color-0);
+ font-weight: 700;
+ margin-bottom: 10px;
+}
+
+.announcement-content-body {
+ font-size: var(--announcement-font-size-0);
+ color: var(--announcement-font-color-1);
+ font-weight: 300;
+}
+
+.announcement-content-controllers {
+ display: flex;
+ font-size: var(--announcement-font-size-0);
+ margin-top: 15px;
+}
+
+.controllers-button-esc {
+ font-size: var(--announcement-font-size-0);
+ font-weight: 600;
+ margin-right: 12px;
+}
+
+.controllers-button-primary {
+ font-size: var(--announcement-font-size-0);
+ font-weight: 600;
+ color: var(--announcement-font-color-2);
+}
+
+.announcement-close {
+ width: 24px;
+ height: 24px;
+}
diff --git a/Client/src/Components/Announcements/AnnouncementsMessageBar/AnnouncementsMessageBar.jsx b/Client/src/Components/Announcements/AnnouncementsMessageBar/AnnouncementsMessageBar.jsx
new file mode 100644
index 000000000..20a179052
--- /dev/null
+++ b/Client/src/Components/Announcements/AnnouncementsMessageBar/AnnouncementsMessageBar.jsx
@@ -0,0 +1,36 @@
+import "./announcementsMessageBar.css";
+import React from "react";
+import CloseIcon from "@mui/icons-material/Close";
+import PropTypes from "prop-types";
+import { useTheme } from "@mui/material";
+
+/**
+ * @component
+ * @param {Object} props
+ * @param {string} props.message - The announcement message text content (required)
+ * @returns {JSX.Element} - Renders the announcement message bar component
+ */
+const AnnouncementsMessageBar = ({ message }) => {
+ const theme = useTheme();
+
+ const fontLookup = {
+ default: theme.font.default.font,
+ };
+
+ const fontFamily = fontLookup["default"];
+
+ return (
+
+ );
+};
+
+AnnouncementsMessageBar.propTypes = {
+ message: PropTypes.string.isRequired,
+};
+
+export default AnnouncementsMessageBar;
diff --git a/Client/src/Components/Announcements/AnnouncementsMessageBar/announcementsMessageBar.css b/Client/src/Components/Announcements/AnnouncementsMessageBar/announcementsMessageBar.css
new file mode 100644
index 000000000..2e9f95e83
--- /dev/null
+++ b/Client/src/Components/Announcements/AnnouncementsMessageBar/announcementsMessageBar.css
@@ -0,0 +1,12 @@
+:root {
+ --announcement-font-size-0: 13px;
+ --announcement-padding: 16px;
+}
+
+.announcement-messagebar-body {
+ font-size: var(--announcement-font-size-0);
+ padding: 0 var(--announcement-padding) 0 0;
+ justify-content: center;
+ align-items: center;
+ margin: auto;
+}
diff --git a/Client/src/Components/Charts/Servers/ServerStatus.jsx b/Client/src/Components/Charts/Servers/ServerStatus.jsx
new file mode 100644
index 000000000..f910abf27
--- /dev/null
+++ b/Client/src/Components/Charts/Servers/ServerStatus.jsx
@@ -0,0 +1,37 @@
+import React from "react";
+import "./serverStatus.css";
+import PropTypes from "prop-types";
+import { useTheme } from "@mui/material";
+
+/**
+ * @component
+ * @param {Object} props
+ * @param {string} props.title - The title text for the server status (required)
+ * @param {string} props.value - The value text to be displayed (required)
+ * @param {string} props.state - The state of the server (e.g., "online", "offline", "warning") (required)
+ * @returns {JSX.Element} - Renders the server status component
+ */
+const ServerStatus = ({ title, value, state }) => {
+ const theme = useTheme();
+
+ const fontLookup = {
+ default: theme.font.default.font,
+ };
+
+ const fontFamily = fontLookup["default"];
+
+ return (
+
+ );
+};
+
+ServerStatus.propTypes = {
+ title: PropTypes.string.isRequired,
+ value: PropTypes.string.isRequired,
+ state: PropTypes.string.isRequired,
+};
+
+export default ServerStatus;
diff --git a/Client/src/Components/Charts/Servers/serverStatus.css b/Client/src/Components/Charts/Servers/serverStatus.css
new file mode 100644
index 000000000..9126d9b3b
--- /dev/null
+++ b/Client/src/Components/Charts/Servers/serverStatus.css
@@ -0,0 +1,44 @@
+:root {
+ --spacing-general-0: 10px 25px;
+ --spacing-general-1: 20px;
+ --color-border-0: #eaecf0;
+ --border-radius-0: 4px;
+ --font-size-0: 1rem;
+ --font-size-2: 36px;
+
+ --color-up: #17b26a;
+ --color-down: #f04438;
+ --color-pause: #fdb022;
+}
+
+.server-status-tile {
+ width: 230px;
+ margin: var(--spacing-general-0);
+ margin-left: 10px;
+ padding: var(--spacing-general-1);
+ border: 1px solid var(--color-border-0);
+ border-radius: var(--border-radius-0);
+}
+
+.server-status-tile-title {
+ font-size: var(--font-size-0);
+ font-weight: bold;
+ margin-bottom: 25px;
+}
+
+.server-status-tile-value {
+ font-size: var(--font-size-2);
+ font-weight: bolder;
+}
+
+.up {
+ color: var(--color-up);
+}
+
+.down {
+ color: var(--color-down);
+}
+
+.pause {
+ color: var(--color-pause);
+}
diff --git a/Client/src/Components/Charts/Statistics/Statistic.jsx b/Client/src/Components/Charts/Statistics/Statistic.jsx
new file mode 100644
index 000000000..99ad2066f
--- /dev/null
+++ b/Client/src/Components/Charts/Statistics/Statistic.jsx
@@ -0,0 +1,35 @@
+import React from "react";
+import "./statistic.css";
+import PropTypes from "prop-types";
+import { useTheme } from "@mui/material";
+
+/**
+ * @component
+ * @param {Object} props
+ * @param {string} props.title - The title text for the statistic (required)
+ * @param {string} props.value - The numerical or textual value for the statistic (required)
+ * @returns {JSX.Element} - Renders the statistic component
+ */
+const Statistic = ({ title, value }) => {
+ const theme = useTheme();
+
+ const fontLookup = {
+ default: theme.font.default.font,
+ };
+
+ const fontFamily = fontLookup["default"];
+
+ return (
+
+ );
+};
+
+Statistic.propTypes = {
+ title: PropTypes.string.isRequired,
+ value: PropTypes.string.isRequired,
+};
+
+export default Statistic;
diff --git a/Client/src/Components/Charts/Statistics/statistic.css b/Client/src/Components/Charts/Statistics/statistic.css
new file mode 100644
index 000000000..a8834199d
--- /dev/null
+++ b/Client/src/Components/Charts/Statistics/statistic.css
@@ -0,0 +1,31 @@
+:root {
+ --stats-spacing-general-0: 5px 10px;
+ --color-border-0: #eaecf0;
+ --border-radius-0: 4px;
+ --font-size-0: 1rem;
+ --font-size-1: 13px;
+ --font-color-0: #344054;
+ --margin-bottom-0: 10px;
+}
+
+.statistic-tile {
+ margin: var(--stats-spacing-general-0);
+ padding: var(--stats-spacing-general-0);
+ border: 1px solid var(--color-border-0);
+ border-radius: var(--border-radius-0);
+
+ width: 180px;
+}
+
+.statistic-tile-title {
+ font-size: var(--font-size-0);
+ color: var(--font-color-0);
+ margin-bottom: var(--margin-bottom-0);
+}
+
+.statistic-tile-value {
+ font-size: var(--font-size-1);
+ color: var(--font-color-0);
+ font-weight: 700;
+ margin-bottom: var(--margin-bottom-0);
+}
diff --git a/Client/src/Components/Icons/ComplexAlert/ComplexAlert.jsx b/Client/src/Components/Icons/ComplexAlert/ComplexAlert.jsx
new file mode 100644
index 000000000..67af3ab88
--- /dev/null
+++ b/Client/src/Components/Icons/ComplexAlert/ComplexAlert.jsx
@@ -0,0 +1,36 @@
+import "./complexAlert.css";
+import React from "react";
+import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline";
+import PropTypes from "prop-types";
+
+/**
+ * @component
+ * @param {Object} props
+ * @param {"red" | "green"} props.theme - The color theme for the alert (required) - must be either "red" or "green"
+ * @returns {JSX.Element} - Renders the complex alert component with dynamic color theme
+ */
+const ComplexAlert = ({ theme }) => {
+ if (theme === "red") {
+ return (
+
+ );
+ } else if (theme === "green") {
+ return (
+
+ );
+ }
+};
+
+ComplexAlert.propTypes = {
+ theme: PropTypes.string.isRequired,
+};
+
+export default ComplexAlert;
diff --git a/Client/src/Components/Icons/ComplexAlert/complexAlert.css b/Client/src/Components/Icons/ComplexAlert/complexAlert.css
new file mode 100644
index 000000000..ab3cea714
--- /dev/null
+++ b/Client/src/Components/Icons/ComplexAlert/complexAlert.css
@@ -0,0 +1,44 @@
+.icon-holder-outer-red {
+ width: 38px;
+ height: 38px;
+ border-radius: 50%;
+ border: 2px solid #d92d2019;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.icon-holder-inner-red {
+ width: 28px;
+ height: 28px;
+ border-radius: 50%;
+ border: 2px solid #d92d204d;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.icon-holder-outer-green {
+ width: 38px;
+ height: 38px;
+ border-radius: 50%;
+ border: 2px solid #07945519;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.icon-holder-inner-green {
+ width: 28px;
+ height: 28px;
+ border-radius: 50%;
+ border: 2px solid #0794554d;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.icon-core {
+ width: 20px;
+ height: 20px;
+}
diff --git a/Client/src/Components/PopupModals/DualButtonPopupModal/DualButtonPopupModal.jsx b/Client/src/Components/PopupModals/DualButtonPopupModal/DualButtonPopupModal.jsx
new file mode 100644
index 000000000..42a3f2a8a
--- /dev/null
+++ b/Client/src/Components/PopupModals/DualButtonPopupModal/DualButtonPopupModal.jsx
@@ -0,0 +1,55 @@
+import "./dualButtonPopupModal.css";
+import React from "react";
+import PropTypes from "prop-types";
+import { useTheme } from "@mui/material";
+
+/**
+ * @component
+ * @param {Object} props
+ * @param {boolean} [props.open=true] - Controls the visibility of the modal (defaults to true)
+ * @param {string} props.subject - The title text for the modal (required)
+ * @param {string} props.description - The description text for the modal (required)
+ * @param {string} props.esc - The text for the discard button (usually "Cancel", "Dismiss" or "Discard") (required)
+ * @param {string} props.save - The text for the save button (required)
+ * @returns {JSX.Element} - Renders the dual button popup modal component
+ */
+const DualButtonPopupModal = ({
+ open = true,
+ subject,
+ description,
+ esc,
+ save,
+}) => {
+ const theme = useTheme();
+
+ const fontLookup = {
+ default: theme.font.default.font,
+ };
+
+ const fontFamily = fontLookup["default"];
+
+ return (
+
+
{subject}
+
{description}
+
+
+
+
+
+
+ );
+};
+
+DualButtonPopupModal.propTypes = {
+ open: PropTypes.bool,
+ subject: PropTypes.string.isRequired,
+ description: PropTypes.string.isRequired,
+ esc: PropTypes.string.isRequired,
+ save: PropTypes.string.isRequired,
+};
+
+export default DualButtonPopupModal;
diff --git a/Client/src/Components/PopupModals/DualButtonPopupModal/dualButtonPopupModal.css b/Client/src/Components/PopupModals/DualButtonPopupModal/dualButtonPopupModal.css
new file mode 100644
index 000000000..92ac7f5d6
--- /dev/null
+++ b/Client/src/Components/PopupModals/DualButtonPopupModal/dualButtonPopupModal.css
@@ -0,0 +1,60 @@
+:root {
+ --popup-color-border-0: #f2f2f2;
+ --popup-color-0: #101828;
+ --popup-color-1: #475467;
+ --color-0: #1570ef;
+ --color-1: #175cd3;
+}
+
+.dual-button-popup-modal-holder {
+ width: 380px;
+ height: 130px;
+ margin: 10px 20px;
+ padding: 30px;
+ box-shadow: 0px 8px 8px -4px rgba(16, 24, 40, 0.03),
+ 0px 20px 24px -4px rgba(16, 24, 40, 0.08);
+ border: 1px solid var(--popup-color-border-0);
+ border-radius: 4px;
+}
+
+.dual-button-popup-modal-subject {
+ color: var(--popup-color-0);
+ font-weight: bolder;
+ font-size: 16px;
+}
+
+.dual-button-popup-modal-description {
+ color: var(--popup-color-1);
+ font-size: 13px;
+ margin-top: 10px;
+}
+
+.dual-modal-spacing {
+ height: 40px;
+}
+
+.dual-button-popup-modal-controllers {
+ display: flex;
+ justify-content: end;
+}
+
+.blue-save-button {
+ width: 145px;
+ height: 35px;
+ padding: 5px 20px;
+ background-color: var(--color-0);
+ border: 1px solid var(--color-1);
+ border-radius: 4px;
+ margin: 5px 0;
+ margin-left: 20px;
+ color: white;
+ cursor: pointer;
+ font-size: 13px;
+}
+
+.transparent-discard-button {
+ border: none;
+ background-color: transparent;
+ margin: 5px 10px;
+ font-size: 13px;
+}
diff --git a/Client/src/Components/PopupModals/DualButtonPopupModalWithTextfields/DualButtonPopupModalWithTextfields.jsx b/Client/src/Components/PopupModals/DualButtonPopupModalWithTextfields/DualButtonPopupModalWithTextfields.jsx
new file mode 100644
index 000000000..b8b424323
--- /dev/null
+++ b/Client/src/Components/PopupModals/DualButtonPopupModalWithTextfields/DualButtonPopupModalWithTextfields.jsx
@@ -0,0 +1,73 @@
+import "./dualButtonPopupModalWithTextfields.css";
+import React from "react";
+import CloseIcon from "@mui/icons-material/Close";
+import PropTypes from "prop-types";
+import { useTheme } from "@mui/material";
+
+/**
+ * @component
+ * @param {Object} props
+ * @param {string} props.title - The title text for the modal (required)
+ * @returns {JSX.Element} - Renders a single text field component within a popup modal
+ */
+const PopupModalTextfield = ({ title }) => {
+ const theme = useTheme();
+
+ const fontLookup = {
+ default: theme.font.default.font,
+ };
+
+ const fontFamily = fontLookup["default"];
+
+ return (
+
+ );
+};
+
+/**
+ * @component
+ * @param {Object} props
+ * @param {string} props.title - The title text for the modal (required)
+ * @param {string} props.esc - The text for the cancel button (required)
+ * @param {string} props.save - The text for the save button (required)
+ * @returns {JSX.Element} - Renders the dual button popup modal component with text fields
+ */
+const DualButtonPopupModalWithTextfields = ({ title, esc, save }) => {
+ const theme = useTheme();
+
+ const fontLookup = {
+ default: theme.font.default.font,
+ };
+
+ const fontFamily = fontLookup["default"];
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+DualButtonPopupModalWithTextfields.propTypes = {
+ title: PropTypes.string.isRequired,
+ esc: PropTypes.string.isRequired,
+ save: PropTypes.string.isRequired,
+};
+
+export default DualButtonPopupModalWithTextfields;
diff --git a/Client/src/Components/PopupModals/DualButtonPopupModalWithTextfields/dualButtonPopupModalWithTextfields.css b/Client/src/Components/PopupModals/DualButtonPopupModalWithTextfields/dualButtonPopupModalWithTextfields.css
new file mode 100644
index 000000000..a1be3cd3a
--- /dev/null
+++ b/Client/src/Components/PopupModals/DualButtonPopupModalWithTextfields/dualButtonPopupModalWithTextfields.css
@@ -0,0 +1,61 @@
+:root {
+ --popup-color-with-input-border-0: #f2f2f2;
+ --popup-color-with-input-border-1: #d0d5dd;
+ --popup-modal-with-input-title-color: #344054;
+}
+
+.popup-modal-holder {
+ width: 480px;
+ font-family: var(--popup-font-family-0);
+ padding: 50px 40px;
+ border-radius: 4px;
+ margin: 10px 20px;
+ border: 1px solid var(--popup-color-with-input-border-0);
+ box-shadow: 0px 8px 8px -4px rgba(16, 24, 40, 0.03),
+ 0px 20px 24px -4px rgba(16, 24, 40, 0.08);
+}
+
+.popup-modal-header {
+ display: flex;
+ justify-content: space-between;
+ font-weight: bold;
+}
+
+.popup-modal-close {
+ cursor: pointer;
+}
+
+.spacing-height {
+ height: 30px;
+}
+
+.popup-modal-input-title {
+ margin-bottom: 15px;
+ color: var(--popup-modal-with-input-title-color);
+}
+
+.popup-modal-text-field {
+ width: 100%;
+ height: 35px;
+ border: 1px solid var(--popup-color-with-input-border-1);
+ border-radius: 4px;
+}
+
+.popup-modal-controllers {
+ display: flex;
+ justify-content: end;
+}
+
+.white-cancel-button {
+ border: none;
+ background-color: transparent;
+ font-family: var(--popup-font-family-0);
+ margin: 5px 10px;
+ width: 145px;
+ height: 35px;
+ padding: 5px 20px;
+ border: 1px solid var(--popup-color-with-input-border-1);
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 13px;
+}
diff --git a/Client/src/Components/TextFields/Description/DescriptionTextField.jsx b/Client/src/Components/TextFields/Description/DescriptionTextField.jsx
new file mode 100644
index 000000000..7f3ba908a
--- /dev/null
+++ b/Client/src/Components/TextFields/Description/DescriptionTextField.jsx
@@ -0,0 +1,47 @@
+import React from "react";
+import "./descriptionTextField.css";
+import { TextField } from "@mui/material";
+import PropTypes from "prop-types";
+import { useTheme } from "@mui/material";
+
+/**
+ * @component
+ * @param {Object} props
+ * @param {string} props.hintText - The hint text displayed within the text field (required)
+ * @param {boolean} props.hasError - A flag indicating if the text field has an error (required)
+ * @returns {JSX.Element} - Renders the description text field component
+ */
+const DescriptionTextField = ({ hintText, hasError }) => {
+ const theme = useTheme();
+
+ const fontLookup = {
+ default: theme.font.default.font,
+ };
+
+ const fontFamily = fontLookup["default"];
+
+ return (
+
+ );
+};
+
+DescriptionTextField.propTypes = {
+ hintText: PropTypes.string.isRequired,
+ hasError: PropTypes.bool.isRequired,
+};
+
+export default DescriptionTextField;
diff --git a/Client/src/Components/TextFields/Description/descriptionTextField.css b/Client/src/Components/TextFields/Description/descriptionTextField.css
new file mode 100644
index 000000000..28616863e
--- /dev/null
+++ b/Client/src/Components/TextFields/Description/descriptionTextField.css
@@ -0,0 +1,45 @@
+:root {
+ --color-title-1: #344054;
+ --text-field-margin-0: 10px 20px;
+ --description-color-0: #344054;
+}
+
+.description-field-holder {
+ margin: var(--text-field-margin-0);
+ margin-top: 20px;
+}
+
+.text-field-title {
+ color: var(--color-title-1);
+ font-weight: bold;
+ margin-bottom: 10px;
+ font-size: 13px;
+ color: var(--description-color-0);
+ font-weight: bold;
+}
+
+.description-field-area {
+ min-width: 320px;
+ font-size: 13px;
+ border-radius: 8px;
+}
+
+.description-field-label {
+ margin-top: 10px;
+ font-size: 11px;
+}
+
+.with-error {
+ color: red;
+}
+
+#description-field-area-helper-text {
+ margin-left: 0;
+ margin-top: 10px;
+ font-size: 11px;
+}
+
+#description-field-area {
+ min-width: 320px;
+ font-size: 13px;
+}
diff --git a/Client/src/Components/TextFields/Email/EmailTextField.jsx b/Client/src/Components/TextFields/Email/EmailTextField.jsx
new file mode 100644
index 000000000..b1428e1c5
--- /dev/null
+++ b/Client/src/Components/TextFields/Email/EmailTextField.jsx
@@ -0,0 +1,88 @@
+import React, { useState } from "react";
+import "./emailTextField.css";
+import { InputAdornment, TextField } from "@mui/material";
+import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline";
+import HelpOutlineIcon from "@mui/icons-material/HelpOutline";
+import { useTheme } from "@mui/material";
+
+/**
+ * @component
+ * @param {Object} props
+ * @param {string} props.id - Unique ID for the text field (optional)
+ * @param {string} props.label - The label text displayed above the text field (optional)
+ * @param {"standard" | "outlined" | "filled"} props.variant - The variant of the text field (e.g., "outlined") (optional)
+ * @param {string} props.placeholder - The placeholder text displayed within the text field (optional)
+ * @param {"error" | "help"} [props.icon] - The type of icon to display (error or help) (optional)
+ * @param {string} props.helperText - The helper text displayed below the text field (optional)
+ * @param {boolean} props.error - A flag indicating if the text field has an error (required)
+ * @returns {JSX.Element} - Renders the email text field component with dynamic icon display
+ */
+const EmailTextField = ({
+ id,
+ label,
+ variant,
+ placeholder,
+ icon,
+ helperText,
+ error,
+}) => {
+ const theme = useTheme();
+
+ const fontLookup = {
+ default: theme.font.default.font,
+ };
+
+ const fontFamily = fontLookup["default"];
+
+ const [showIcon, setShowIcon] = useState(false);
+
+ const handleMouseEnter = () => setShowIcon(true);
+ const handleMouseLeave = () => setShowIcon(false);
+
+ return (
+
+
Email
+
+
+
+ {error && showIcon && (
+
+ )}
+ {error && !showIcon && (
+
+ )}
+ {!error && showIcon && (
+
+ )}
+
+
+ ),
+ }}
+ helperText={helperText}
+ onMouseEnter={handleMouseEnter}
+ onMouseLeave={handleMouseLeave}
+ />
+
+
+ );
+};
+
+export default EmailTextField;
diff --git a/Client/src/Components/TextFields/Email/emailTextField.css b/Client/src/Components/TextFields/Email/emailTextField.css
new file mode 100644
index 000000000..ff38a29df
--- /dev/null
+++ b/Client/src/Components/TextFields/Email/emailTextField.css
@@ -0,0 +1,41 @@
+:root {
+ --textfield-color-0: #344054;
+}
+
+.email-text-field {
+ position: relative;
+ margin: 10px 20px;
+}
+
+#outlined-basic {
+ width: 320px;
+ height: 14px;
+ font-size: 13px;
+ padding: 8px 12px;
+}
+
+.email-text-field-title {
+ margin: 0 20px;
+ margin-top: 30px;
+ font-size: 13px;
+ color: var(--textfield-color-0);
+ font-weight: bold;
+}
+
+#outlined-basic-helper-text {
+ margin-left: 0;
+ margin-top: 10px;
+ font-size: 11px;
+}
+
+.text-field-icon {
+ width: 16px;
+ height: 16px;
+}
+
+.icon-holder {
+ width: 16px;
+ height: 16px;
+ padding: 0;
+ margin: 0;
+}
diff --git a/Client/src/Components/TextFields/Website/WebsiteTextField.jsx b/Client/src/Components/TextFields/Website/WebsiteTextField.jsx
new file mode 100644
index 000000000..d18e33dd9
--- /dev/null
+++ b/Client/src/Components/TextFields/Website/WebsiteTextField.jsx
@@ -0,0 +1,72 @@
+import React, { useState } from "react";
+import "./websiteTextField.css";
+import HelpOutlineIcon from "@mui/icons-material/HelpOutline";
+import ContentCopyIcon from "@mui/icons-material/ContentCopy";
+import { useTheme } from "@mui/material";
+
+/**
+ * @component
+ * @param {Object} props
+ * @param {boolean} [props.showHelp=true] - Controls the visibility of the help icon (defaults to true)
+ * @param {boolean} [props.hasCopyButton=false] - Controls the visibility of the copy button (defaults to false)
+ * @param {string} props.hintText - The hint text displayed below the text field (required)
+ * @returns {JSX.Element} - Renders the website text field component with optional help and copy features
+ */
+const WebsiteTextField = ({
+ showHelp = true,
+ hasCopyButton = false,
+ hintText,
+}) => {
+ const theme = useTheme();
+
+ const fontLookup = {
+ default: theme.font.default.font,
+ };
+
+ const fontFamily = fontLookup["default"];
+
+ const [copied, setCopied] = useState(false);
+
+ const handleCopy = () => {
+ setCopied(true);
+ setTimeout(() => setCopied(false), 1000);
+ };
+
+ return (
+
+
Website
+
+
+
+ {showHelp && (
+
+
+
+ )}
+ {hasCopyButton && (
+
+
+ {copied ? " Copied " : " Copy "}
+
+ )}
+
+
+
+ );
+};
+
+export default WebsiteTextField;
diff --git a/Client/src/Components/TextFields/Website/websiteTextField.css b/Client/src/Components/TextFields/Website/websiteTextField.css
new file mode 100644
index 000000000..66cdf4fa4
--- /dev/null
+++ b/Client/src/Components/TextFields/Website/websiteTextField.css
@@ -0,0 +1,82 @@
+:root {
+ --website-color-0: #344054;
+ --border-color: #d0d5dd;
+ --label-font-color: #475467;
+ --icon-color: #98a2b3;
+ --text-field-margin-0: 10px 20px;
+ --text-field-margin-1: 10px 20px 5px;
+}
+
+.website-textfield-title {
+ margin: 0 20px;
+ margin-top: 30px;
+ font-size: 13px;
+ color: var(--website-color-0);
+ font-weight: bold;
+}
+
+.website-textfield {
+ display: flex;
+ align-items: center;
+ position: relative;
+ margin: var(--text-field-margin-1);
+}
+
+.website-textfield-hint {
+ margin: var(--text-field-margin-0);
+ font-size: 11px;
+}
+
+.website-label {
+ padding: 6px 20px 6px 10px;
+ border: solid 1px var(--border-color);
+ border-right: none;
+ border-radius: 4px 0 0 4px;
+ font-size: 13px;
+ text-align: center;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ color: var(--label-font-color);
+}
+
+.website-url {
+ width: 248px;
+ height: 31.5px;
+ padding: 0px 15px;
+ border: solid 1px var(--border-color);
+}
+
+.website-icon-holder {
+ width: 40px;
+ height: 33px;
+ padding: 5px;
+ border: solid 1px var(--border-color);
+ border-left: none;
+ text-align: center;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.copy-to-clipboard-button {
+ padding: 5.5px 20px 6px 10px;
+ border: solid 1px var(--border-color);
+ border-left: none;
+ border-radius: 0 8px 8px 0;
+ font-size: 13px;
+ text-align: center;
+ display: flex;
+ justify-content: space-evenly;
+ align-items: center;
+ color: var(--label-font-color);
+ cursor: pointer;
+}
+
+.copy-icon {
+ margin-right: 5px;
+}
+
+.with-no-copy-button {
+ display: none;
+}
diff --git a/Client/src/Components/Tooltips/TooltipWithTail/tooltipWithTail.css b/Client/src/Components/Tooltips/TooltipWithTail/tooltipWithTail.css
new file mode 100644
index 000000000..1b1adf507
--- /dev/null
+++ b/Client/src/Components/Tooltips/TooltipWithTail/tooltipWithTail.css
@@ -0,0 +1,17 @@
+:root {
+ --tooltip-color-0: #d0d5dd;
+ --tooltip-margin: 5px;
+ --tooltip-font-size: 12px;
+}
+
+.tooltip-title {
+ font-size: var(--tooltip-font-size);
+ font-weight: bold;
+ margin: var(--tooltip-margin);
+}
+
+.tooltip-description {
+ font-size: var(--tooltip-font-size);
+ color: var(--tooltip-color-0);
+ margin: var(--tooltip-margin);
+}