Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create status page PR2 #1494

Open
wants to merge 56 commits into
base: develop
Choose a base branch
from
Open

Create status page PR2 #1494

wants to merge 56 commits into from

Conversation

jennifer-gan
Copy link
Contributor

@jennifer-gan jennifer-gan commented Dec 30, 2024

  • Add static status page
  • Add status page configuration including general settings and content tabs
  • Add routes to status pages
  • Add context provider for status page
  • Customized the Image for uploading logo in general setting tab
  • Implemented drag and drop for content tab server list section
  • Invoke form validation upon submission or onBlur
  • Create status page and getstatus page by url now working end to end as MVP
  • Update dnd library to @hello-pangea/dnd due to React beautiful dnd is deprecated
  • Currently all fields are sent for submission as seen in below video
  • Logo upload UI is done, need BE to save into
  • show publicly visible, show barcode and show uptime percentage UI are done, need BE to save into
Screencast.from.2025-01-21.14-05-19.webm

- WIP logo uploading in general settings tab
- Implemented drag and drop for the server list section
- Add the rest of content panel UI layouts
- Update the card value with onChange
- Fix the warning possible change from uncontrolled to controlled component
- Make static status page and actual status config page toggleable
- Remove the sidebar nested menu item entry for status page tabs
- Pass context to status config page which contains general settings and contents tabs
- Status config page is now one submission and one validation
- Change layout to use the one that has vertical line
- Update name to CreateStatusContext instead of TabContext to make it clearer
- Handle errors to land on the respective page tab
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
Client/src/Validation/validation.js (1)

177-190: ⚠️ Potential issue

Yo dawg, we heard you like validation! 🍝

This schema's got some spaghetti that needs cleaning up, and I'm nervous just looking at it! Previous review still stands, but here's what's making my palms sweaty:

  1. You've got two image validation schemas now (imageValidation and logoImageValidation) that are making me lose myself in the code.
  2. That 800*800 calculation is giving me weak knees - let's make it a constant!
  3. The error message about "pixels" when we're checking bytes? Mom's spaghetti! 🍝

Here's a fresh take on the fix:

+const MAX_LOGO_SIZE_BYTES = 640000; // 800*800 bytes
+const ALLOWED_IMAGE_TYPES = ['image/jpeg', 'image/png'];

-const logoImageValidation = joi.object({
+// Extend the base imageValidation schema with logo-specific constraints
+const logoImageValidation = imageValidation.keys({
   size: joi
     .number()
-    .max(800*800)
+    .max(MAX_LOGO_SIZE_BYTES)
     .messages({
       "number.base": "File size must be a number.",
-      "number.max": "File size must be less than 640000 pixels.",
+      "number.max": `File size must be less than ${MAX_LOGO_SIZE_BYTES} bytes.`,
       "number.empty": "File size required.",
     }),
 });
🧹 Nitpick comments (3)
Client/src/Components/Inputs/Search/index.jsx (3)

71-72: Bruh, let's make this handler safer than mom's spaghetti! 🍝

The event handler could be more robust using optional chaining.

-handleChange && handleChange(e, newValue);
+handleChange?.(e, newValue);
🧰 Tools
🪛 Biome (1.9.4)

[error] 72-72: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


79-79: That fallback is straight fire, but let's make it even more lit! 🎯

The nullish coalescing operator provides a good fallback, but consider adding type checking for better robustness.

-getOptionLabel={(option) => option[filteredBy]??""}
+getOptionLabel={(option) => typeof option === 'object' && option ? option[filteredBy] ?? "" : ""}

197-197: PropTypes be spittin' straight facts! 📝

The updated PropTypes accurately reflect the component's enhanced capabilities. The oneOfType for value is particularly clutch for supporting both single and multiple selection modes.

Just one thing to consider - should we add shape validation for the adornment objects?

-startAdornment: PropTypes.object,
-endAdornment: PropTypes.object,
+startAdornment: PropTypes.shape({
+  // Add expected adornment props here
+}),
+endAdornment: PropTypes.shape({
+  // Add expected adornment props here
+}),

Also applies to: 205-206

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a544a0f and ff7fe7f.

⛔ Files ignored due to path filters (2)
  • Client/package-lock.json is excluded by !**/package-lock.json
  • Docker/dist/docker-compose.yaml is excluded by !**/dist/**
📒 Files selected for processing (7)
  • Client/package.json (1 hunks)
  • Client/src/Components/Inputs/Search/index.jsx (4 hunks)
  • Client/src/Routes/index.jsx (2 hunks)
  • Client/src/Utils/NetworkService.js (1 hunks)
  • Client/src/Validation/validation.js (2 hunks)
  • Docker/prod/docker-compose.yaml (1 hunks)
  • Docker/test/docker-compose.yaml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • Client/package.json
  • Docker/test/docker-compose.yaml
  • Client/src/Routes/index.jsx
  • Client/src/Utils/NetworkService.js
  • Docker/prod/docker-compose.yaml
🧰 Additional context used
🪛 Biome (1.9.4)
Client/src/Components/Inputs/Search/index.jsx

[error] 72-72: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (3)
Client/src/Components/Inputs/Search/index.jsx (2)

57-58: Yo dawg, these new adornment props are straight fire! 🔥

The addition of startAdornment and endAdornment props makes the component more flexible and reusable. This aligns perfectly with the status page requirements.


94-100: Yo, these slot props are stacked like a tower of pancakes! 🥞

The conditional spreading of adornments is clean, but there's potential for prop conflicts when multiple conditions are true.

Consider the case where both isAdorned and startAdornment are true - which one takes precedence? Let's verify the component's behavior:

Client/src/Validation/validation.js (1)

324-325: Exports looking clean, no vomit on this sweater! 🧥

The new exports are properly organized and follow the existing pattern. You've got the flow going!

Comment on lines 192 to 209
const publicPageGeneralSettingsValidation = joi.object({
publish: joi.bool(),
companyName: joi
.string()
.trim()
.messages({ "string.empty": "Company name is required." }),
url: joi.string().trim().messages({ "string.empty": "URL is required." }),
timezone: joi.string().trim().messages({ "string.empty": "Timezone is required." }),
color: joi.string().trim().messages({ "string.empty": "Color is required." }),
theme: joi.string(),
monitors: joi.array().min(1).items(joi.string().required()).messages({
"string.pattern.base": "Must be a valid monitor ID",
"array.base": "Monitors must be an array",
"array.empty": "At least one monitor is required",
"any.required": "Monitors are required",
}),
logo: logoImageValidation
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Snap back to reality, oh there goes validation! 🎵

The schema's looking clean overall, but there's opportunity in every line! Here's what's making my knees weak:

  1. That URL validation is basic - we should use the same robust URL regex we got in monitorValidation
  2. The color field's just vibing without format validation (hex, rgb, named colors?)
  3. Theme field's like mom's spaghetti - it'll accept anything!

Here's how we clean this up:

+const VALID_THEMES = ['light', 'dark', 'system'];
+const COLOR_REGEX = /^(#[0-9A-Fa-f]{6}|rgb\(\d{1,3},\s*\d{1,3},\s*\d{1,3}\))$/;

 const publicPageGeneralSettingsValidation = joi.object({
   publish: joi.bool(),
   companyName: joi.string().trim()
     .messages({ "string.empty": "Company name is required." }),
-  url: joi.string().trim()
+  url: joi.string().trim()
+    .custom((value, helpers) => {
+      // Reuse the URL regex from monitorValidation
+      const urlRegex = /^(?:(?:https?|ftp):\/\/)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62})?[a-z0-9\u00a1-\uffff]\.)+(?:[a-z\u00a1-\uffff]{2,}\.?))?(?::\d{2,5})?(?:[/?#]\S*)?$/i;
+      return urlRegex.test(value) ? value : helpers.error('string.invalidUrl');
+    })
     .messages({ 
       "string.empty": "URL is required.",
+      "string.invalidUrl": "Please enter a valid URL"
     }),
   timezone: joi.string().trim()
     .messages({ "string.empty": "Timezone is required." }),
-  color: joi.string().trim()
+  color: joi.string().trim()
+    .pattern(COLOR_REGEX)
     .messages({ 
       "string.empty": "Color is required.",
+      "string.pattern.base": "Color must be a valid hex (#RRGGBB) or RGB value"
     }),
-  theme: joi.string(),
+  theme: joi.string().valid(...VALID_THEMES)
+    .messages({
+      "any.only": `Theme must be one of: ${VALID_THEMES.join(', ')}`
+    }),
   monitors: joi.array().min(1).items(joi.string().required())
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const publicPageGeneralSettingsValidation = joi.object({
publish: joi.bool(),
companyName: joi
.string()
.trim()
.messages({ "string.empty": "Company name is required." }),
url: joi.string().trim().messages({ "string.empty": "URL is required." }),
timezone: joi.string().trim().messages({ "string.empty": "Timezone is required." }),
color: joi.string().trim().messages({ "string.empty": "Color is required." }),
theme: joi.string(),
monitors: joi.array().min(1).items(joi.string().required()).messages({
"string.pattern.base": "Must be a valid monitor ID",
"array.base": "Monitors must be an array",
"array.empty": "At least one monitor is required",
"any.required": "Monitors are required",
}),
logo: logoImageValidation
});
const VALID_THEMES = ['light', 'dark', 'system'];
const COLOR_REGEX = /^(#[0-9A-Fa-f]{6}|rgb\(\d{1,3},\s*\d{1,3},\s*\d{1,3}\))$/;
const publicPageGeneralSettingsValidation = joi.object({
publish: joi.bool(),
companyName: joi
.string()
.trim()
.messages({ "string.empty": "Company name is required." }),
url: joi.string().trim()
.custom((value, helpers) => {
// Reuse the URL regex from monitorValidation
const urlRegex = /^(?:(?:https?|ftp):\/\/)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62})?[a-z0-9\u00a1-\uffff]\.)+(?:[a-z\u00a1-\uffff]{2,}\.?))?(?::\d{2,5})?(?:[/?#]\S*)?$/i;
return urlRegex.test(value) ? value : helpers.error('string.invalidUrl');
})
.messages({
"string.empty": "URL is required.",
"string.invalidUrl": "Please enter a valid URL"
}),
timezone: joi.string().trim().messages({ "string.empty": "Timezone is required." }),
color: joi.string().trim()
.pattern(COLOR_REGEX)
.messages({
"string.empty": "Color is required.",
"string.pattern.base": "Color must be a valid hex (#RRGGBB) or RGB value"
}),
theme: joi.string().valid(...VALID_THEMES)
.messages({
"any.only": `Theme must be one of: ${VALID_THEMES.join(', ')}`
}),
monitors: joi.array().min(1).items(joi.string().required()).messages({
"string.pattern.base": "Must be a valid monitor ID",
"array.base": "Monitors must be an array",
"array.empty": "At least one monitor is required",
"any.required": "Monitors are required",
}),
logo: logoImageValidation
});

- Update getmonitors to match be change (-1 is not accepted as valid param anymore)
- Fix Select not updateable
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
Client/src/Components/TabPanels/Status/GeneralSettingsPanel.jsx (1)

95-106: ⚠️ Potential issue

Mom's spaghetti moment: Memory leaks in logo handling!

The URL.createObjectURL creates object URLs that need to be revoked to prevent memory leaks. Also, there's duplicate code in setting the logo state.

+ const objectUrl = URL.createObjectURL(pic);
 setLogo({
-  src: URL.createObjectURL(pic),
+  src: objectUrl,
   name: pic.name,
   type: pic.type,
   size: pic.size,
 });
-setForm({...form, logo: {
-  src: URL.createObjectURL(pic),
-  name: pic.name,
-  type: pic.type,
-  size: pic.size,
-}})
+setForm(prev => ({
+  ...prev,
+  logo: {
+    src: objectUrl,
+    name: pic.name,
+    type: pic.type,
+    size: pic.size,
+  }
+}));
+
+return () => {
+  URL.revokeObjectURL(objectUrl);
+};
🧹 Nitpick comments (3)
Client/src/Components/TabPanels/Status/GeneralSettingsPanel.jsx (3)

21-24: Yo dawg, there's a typo in your JSDoc!

The word "ued" should be "used" in the component description.

- * General settings panel is ued to compose part of the public static page
+ * General settings panel is used to compose part of the public static page

146-152: Vomit on his sweater already: Remove commented code!

Commented-out code adds noise and confusion. If the feature is planned for later, track it in your issue tracker instead.

-						{/* <Checkbox
-							id="published-to-public"
-							label={`Published and visible to the public`}
-							isChecked={form.publish}
-							onChange={handleChange}
-							onBlur={handleBlur}
-						/> */}

174-174: Palms are sweaty: Simplify those boolean expressions!

The ternary operators for error props can be simplified.

-error={errors["companyName"] ? true : false}
+error={!!errors["companyName"]}

-error={errors["url"] ? true : false}
+error={!!errors["url"]}

Also applies to: 190-190

🧰 Tools
🪛 Biome (1.9.4)

[error] 174-174: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ff7fe7f and e8d9f31.

📒 Files selected for processing (4)
  • Client/src/Components/Inputs/Image/index.jsx (9 hunks)
  • Client/src/Components/TabPanels/Status/ContentPanel.jsx (1 hunks)
  • Client/src/Components/TabPanels/Status/GeneralSettingsPanel.jsx (1 hunks)
  • Client/src/Validation/validation.js (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Client/src/Components/TabPanels/Status/ContentPanel.jsx
🧰 Additional context used
🪛 Biome (1.9.4)
Client/src/Components/TabPanels/Status/GeneralSettingsPanel.jsx

[error] 174-174: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)


[error] 190-190: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)


[error] 231-231: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (6)
Client/src/Components/Inputs/Image/index.jsx (4)

14-19: Yo dawg, props documentation is on point! 🎯

The JSDoc accurately reflects the component's props, including the new isRound and maxSize parameters.


21-23: Clean style handling, straight fire! 🔥

The conditional styles for error borders and round shapes are well-implemented using object spread.


71-71: Z-index game strong! 💪

The z-index adjustments ensure proper layering of the file input and upload instructions.

Also applies to: 88-88


132-144: Error handling's looking clean as mom's spaghetti! 🍝

The error message implementation follows Material-UI best practices with proper styling and accessibility.

Client/src/Validation/validation.js (2)

192-209: 🛠️ Refactor suggestion

Time to level up this validation game! 🎮

The schema needs some improvements:

  1. URL validation should use the robust regex from monitorValidation
  2. Color field needs format validation (hex/rgb)
  3. Theme field should have predefined valid values

Here's the glow-up:

+const VALID_THEMES = ['light', 'dark', 'system'];
+const COLOR_REGEX = /^(#[0-9A-Fa-f]{6}|rgb\(\d{1,3},\s*\d{1,3},\s*\d{1,3}\))$/;

 const publicPageGeneralSettingsValidation = joi.object({
   publish: joi.bool(),
   companyName: joi.string().trim()
     .messages({ "string.empty": "Company name is required." }),
-  url: joi.string().trim()
+  url: joi.string().trim()
+    .custom((value, helpers) => {
+      // Reuse the URL regex from monitorValidation
+      const urlRegex = /^(?:(?:https?|ftp):\/\/)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62})?[a-z0-9\u00a1-\uffff]\.)+(?:[a-z\u00a1-\uffff]{2,}\.?))?(?::\d{2,5})?(?:[/?#]\S*)?$/i;
+      return urlRegex.test(value) ? value : helpers.error('string.invalidUrl');
+    })
     .messages({ 
       "string.empty": "URL is required.",
+      "string.invalidUrl": "Please enter a valid URL"
     }),
   timezone: joi.string().trim()
     .messages({ "string.empty": "Timezone is required." }),
-  color: joi.string().trim()
+  color: joi.string().trim()
+    .pattern(COLOR_REGEX)
     .messages({ 
       "string.empty": "Color is required.",
+      "string.pattern.base": "Color must be a valid hex (#RRGGBB) or RGB value"
     }),
-  theme: joi.string(),
+  theme: joi.string().valid(...VALID_THEMES)
+    .messages({
+      "any.only": `Theme must be one of: ${VALID_THEMES.join(', ')}`
+    }),
   monitors: joi.array().min(1).items(joi.string().required())

Likely invalid or redundant comment.


177-190: 🛠️ Refactor suggestion

Yo! Let's make this validation schema cleaner than your code editor! 🧹

The schema needs some cleanup:

  1. The size calculation 800*800 should be a named constant
  2. The error message mentions KB but we're using pixels

Here's how we fix this:

+const MAX_LOGO_SIZE = 800 * 800; // 640000 pixels
+const VALID_IMAGE_TYPES = ['image/jpeg', 'image/png'];

 const logoImageValidation = joi.object({
-  type: joi.string().valid("image/jpeg", "image/png").messages({
+  type: joi.string().valid(...VALID_IMAGE_TYPES).messages({
     "any.only": "Invalid file format.",
     "string.empty": "File type required.",
   }),
   size: joi
     .number()
-    .max(800*800)
+    .max(MAX_LOGO_SIZE)
     .messages({
       "number.base": "File size must be a number.",
-      "number.max": "File size must be less than 640KB.",
+      "number.max": `File size must be less than ${MAX_LOGO_SIZE} pixels.`,
       "number.empty": "File size required.",
     }),
 });

Likely invalid or redundant comment.

Comment on lines +78 to +87
const validateField = (toValidate, schema, name = "logo") => {
const { error } = schema.validate(toValidate, { abortEarly: false });
setErrors((prev) => {
let prevErrors = { ...prev };
if (error) prevErrors[name] = error?.details[0].message;
else delete prevErrors[name];
return prevErrors;
});
if (error) return true;
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Knees weak: Inconsistent return value in validateField!

The function returns true on error but undefined on success. This inconsistency could lead to bugs.

 const validateField = (toValidate, schema, name = "logo") => {
   const { error } = schema.validate(toValidate, { abortEarly: false });
   setErrors((prev) => {
     let prevErrors = { ...prev };
     if (error) prevErrors[name] = error?.details[0].message;
     else delete prevErrors[name];
     return prevErrors;
   });
-  if (error) return true;
+  return !!error;
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const validateField = (toValidate, schema, name = "logo") => {
const { error } = schema.validate(toValidate, { abortEarly: false });
setErrors((prev) => {
let prevErrors = { ...prev };
if (error) prevErrors[name] = error?.details[0].message;
else delete prevErrors[name];
return prevErrors;
});
if (error) return true;
};
const validateField = (toValidate, schema, name = "logo") => {
const { error } = schema.validate(toValidate, { abortEarly: false });
setErrors((prev) => {
let prevErrors = { ...prev };
if (error) prevErrors[name] = error?.details[0].message;
else delete prevErrors[name];
return prevErrors;
});
return !!error;
};

Comment on lines +107 to +117
intervalRef.current = setInterval(() => {
const buffer = 12;
setProgress((prev) => {
if (prev.value + buffer >= 100) {
clearInterval(intervalRef.current);
return { value: 100, isLoading: false };
}
return { ...prev, value: prev.value + buffer };
});
}, 120);
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Arms are heavy: Clean up that interval properly!

The interval cleanup should be handled in a useEffect to prevent memory leaks if the component unmounts during upload simulation.

+useEffect(() => {
+  return () => {
+    if (intervalRef.current) {
+      clearInterval(intervalRef.current);
+    }
+  };
+}, []);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
intervalRef.current = setInterval(() => {
const buffer = 12;
setProgress((prev) => {
if (prev.value + buffer >= 100) {
clearInterval(intervalRef.current);
return { value: 100, isLoading: false };
}
return { ...prev, value: prev.value + buffer };
});
}, 120);
};
intervalRef.current = setInterval(() => {
const buffer = 12;
setProgress((prev) => {
if (prev.value + buffer >= 100) {
clearInterval(intervalRef.current);
return { value: 100, isLoading: false };
}
return { ...prev, value: prev.value + buffer };
});
}, 120);
};
useEffect(() => {
return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
};
}, []);

- Query only uptime monitors without checks
- Change log validation from 640Kb  to 3Mb, fixed validation error text contrast issue
- Word/syntax updates, suburl is disabled and provided via env
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
Client/src/Pages/Status/CreateStatus/index.jsx (1)

28-31: Yo! Let's make this error mapping more maintainable!

The hardcoded error-to-tab mapping array could be refactored into a more maintainable object structure.

-const error_tab_maping = [
-  ["companyName", "url", "timezone", "color", "publish", "logo"],
-  ["monitors", "showUptimePercentage", "showBarcode"],
-];
+const ERROR_TAB_MAPPING = {
+  GENERAL_SETTINGS: ["companyName", "url", "timezone", "color", "publish", "logo"],
+  CONTENT: ["monitors", "showUptimePercentage", "showBarcode"]
+};
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e8d9f31 and a1d4ec4.

📒 Files selected for processing (5)
  • Client/src/Components/ConfigBox/index.jsx (1 hunks)
  • Client/src/Components/TabPanels/Status/ContentPanel.jsx (1 hunks)
  • Client/src/Components/TabPanels/Status/GeneralSettingsPanel.jsx (1 hunks)
  • Client/src/Pages/Status/CreateStatus/index.jsx (1 hunks)
  • Client/src/Validation/validation.js (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • Client/src/Components/TabPanels/Status/ContentPanel.jsx
🧰 Additional context used
🪛 Biome (1.9.4)
Client/src/Components/TabPanels/Status/GeneralSettingsPanel.jsx

[error] 166-166: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)


[error] 183-183: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)


[error] 223-223: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (5)
Client/src/Components/ConfigBox/index.jsx (1)

32-32: Yo, we need to verify this styling change!

The removal of paragraph text color styling could affect visual consistency. Let's make sure this was intentional and doesn't mess up the text readability in the GeneralSettingsPanel.

Let's check how this affects other instances:

Client/src/Pages/Status/CreateStatus/index.jsx (1)

91-94: Yo! Fix this status check before it makes your knees weak!

The status check has incorrect syntax that will always evaluate to false.

Client/src/Components/TabPanels/Status/GeneralSettingsPanel.jsx (2)

79-88: Knees weak: Inconsistent return value in validateField!

The function returns true on error but undefined on success.


90-114: Arms are heavy: Clean up that interval properly!

The interval cleanup should be handled in a useEffect.

Client/src/Validation/validation.js (1)

192-209: Snap back to reality, oh there goes validation! 🎵

The schema needs stronger validation for URLs and colors.

Comment on lines 71 to 79
let localData = {
...form,
monitors:
form.monitors[0] && Object.keys(form.monitors[0]).includes("_id")
? form.monitors.map((m) => m._id)
: form.monitors,
theme: mode,
logo: { type: form.logo?.type ?? "", size: form.logo?.size ?? "" },
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Clean up that spaghetti code with safer object handling!

The object spread and optional chaining could be more defensive:

 let localData = {
   ...form,
   monitors:
-    form.monitors[0] && Object.keys(form.monitors[0]).includes("_id")
+    Array.isArray(form.monitors) && form.monitors[0]?.hasOwnProperty("_id")
       ? form.monitors.map((m) => m._id)
       : form.monitors,
   theme: mode,
-  logo: { type: form.logo?.type ?? "", size: form.logo?.size ?? "" },
+  logo: form.logo ? { type: form.logo.type || "", size: form.logo.size || "" } : null,
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let localData = {
...form,
monitors:
form.monitors[0] && Object.keys(form.monitors[0]).includes("_id")
? form.monitors.map((m) => m._id)
: form.monitors,
theme: mode,
logo: { type: form.logo?.type ?? "", size: form.logo?.size ?? "" },
};
let localData = {
...form,
monitors:
Array.isArray(form.monitors) && form.monitors[0]?.hasOwnProperty("_id")
? form.monitors.map((m) => m._id)
: form.monitors,
theme: mode,
logo: form.logo ? { type: form.logo.type || "", size: form.logo.size || "" } : null,
};

Comment on lines +177 to +190
const logoImageValidation = joi.object({
type: joi.string().valid("image/jpeg", "image/png").messages({
"any.only": "Invalid file format.",
"string.empty": "File type required.",
}),
size: joi
.number()
.max(3000000)
.messages({
"number.base": "File size must be a number.",
"number.max": "File size must be less than 3MB.",
"number.empty": "File size required.",
}),
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Yo! This validation's making me lose myself in the duplication!

The logoImageValidation schema duplicates logic from imageValidation.

+const MAX_IMAGE_SIZE = 3 * 1024 * 1024; // 3MB
+const VALID_IMAGE_TYPES = ["image/jpeg", "image/png"];
+
+const baseImageValidation = {
+  type: joi.string().valid(...VALID_IMAGE_TYPES).messages({
+    "any.only": "Invalid file format.",
+    "string.empty": "File type required.",
+  }),
+  size: joi.number().max(MAX_IMAGE_SIZE).messages({
+    "number.base": "File size must be a number.",
+    "number.max": "File size must be less than 3MB.",
+    "number.empty": "File size required.",
+  }),
+};
+
-const logoImageValidation = joi.object({
-  type: joi.string().valid("image/jpeg", "image/png")...
-});
+const logoImageValidation = joi.object(baseImageValidation);
+const imageValidation = joi.object(baseImageValidation);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const logoImageValidation = joi.object({
type: joi.string().valid("image/jpeg", "image/png").messages({
"any.only": "Invalid file format.",
"string.empty": "File type required.",
}),
size: joi
.number()
.max(3000000)
.messages({
"number.base": "File size must be a number.",
"number.max": "File size must be less than 3MB.",
"number.empty": "File size required.",
}),
});
const MAX_IMAGE_SIZE = 3 * 1024 * 1024; // 3MB
const VALID_IMAGE_TYPES = ["image/jpeg", "image/png"];
const baseImageValidation = {
type: joi.string().valid(...VALID_IMAGE_TYPES).messages({
"any.only": "Invalid file format.",
"string.empty": "File type required.",
}),
size: joi.number().max(MAX_IMAGE_SIZE).messages({
"number.base": "File size must be a number.",
"number.max": "File size must be less than 3MB.",
"number.empty": "File size required.",
}),
};
const logoImageValidation = joi.object(baseImageValidation);
const imageValidation = joi.object(baseImageValidation);

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (5)
Client/src/Pages/Status/CreateStatus/index.jsx (2)

34-34: ⚠️ Potential issue

Same typo, different file! Fix that environment variable!

The environment variable typo is repeated here too.

-const STATUS_PAGE = import.meta.env.VITE_STATU_PAGE_URL?? "status-page";				
+const STATUS_PAGE = import.meta.env.VITE_STATUS_PAGE_URL ?? "status-page";

94-94: ⚠️ Potential issue

Yo! Fix this status check before it makes your knees weak!

The status check has incorrect syntax that will always evaluate to false.

-if (!res.status === 200) {
+if (res.status !== 200) {
Client/src/Components/TabPanels/Status/GeneralSettingsPanel.jsx (3)

103-111: ⚠️ Potential issue

Clean up those object URLs before they make you lose your spaghetti!

The URL.createObjectURL creates object URLs that need to be revoked to prevent memory leaks.

+const objectUrl = URL.createObjectURL(pic);
 const newLogo = {
-  src: URL.createObjectURL(pic),
+  src: objectUrl,
   name: pic.name,
   type: pic.type,
   size: pic.size,
 }
 setProgress((prev) => ({ ...prev, isLoading: true }));
 setLogo(newLogo);
 setForm({ ...form, logo: newLogo });
+
+return () => {
+  URL.revokeObjectURL(objectUrl);
+};

112-122: ⚠️ Potential issue

Arms are heavy: Clean up that interval properly!

The interval cleanup should be handled in a useEffect to prevent memory leaks.

+useEffect(() => {
+  return () => {
+    if (intervalRef.current) {
+      clearInterval(intervalRef.current);
+    }
+  };
+}, []);

87-96: ⚠️ Potential issue

Knees weak: Inconsistent return value in validateField!

The function returns true on error but undefined on success. This inconsistency could lead to bugs.

 const validateField = (toValidate, schema, name = "logo") => {
   const { error } = schema.validate(toValidate, { abortEarly: false });
   setErrors((prev) => {
     let prevErrors = { ...prev };
     if (error) prevErrors[name] = error?.details[0].message;
     else delete prevErrors[name];
     return prevErrors;
   });
-  if (error) return true;
+  return !!error;
 };
🧹 Nitpick comments (8)
Client/src/Components/Inputs/ColorPicker/index.jsx (2)

1-25: Yo, these imports are clean, but let's beef up that documentation!

The JSDoc is solid, but we could make it even better by adding:

  • Return type annotation
  • Types for the parameters instead of using *
 /**
- * @param {*} id The ID of the component
- * @param {*} label The Label of the component
- * @param {*} value The color value of the component
- * @param {*} error The error of the component
- * @param {*} onChange The Change handler function
- * @param {*} onBlur The Blur handler function
+ * @param {string} id The ID of the component
+ * @param {string} label The Label of the component
+ * @param {string} value The color value of the component
+ * @param {string} error The error of the component
+ * @param {function} onChange The Change handler function
+ * @param {function} onBlur The Blur handler function
+ * @returns {JSX.Element} The ColorPicker component

26-53: Mom's spaghetti approved! But let's add some accessibility sauce 🍝

The component looks solid, but we could enhance it with some accessibility features:

 <MuiColorInput
   format="hex"
   value={value}
   id={id}
   label={label}
   onChange={onChange}
   onBlur={onBlur}
+  aria-describedby={error ? `${id}-error` : undefined}
 />
 {error && (
   <Typography
     component="span"
     className="input-error"
+    id={`${id}-error`}
+    role="alert"
     color={theme.palette.error.main}
Client/src/Pages/Status/index.jsx (2)

23-33: Enhance that error handling before your palms get sweaty!

The error handling could be more specific about what went wrong. Consider adding error details and different handling for various error scenarios.

 const getStatusPage = async () => {
   let config = { authToken: authToken, url: STATUS_PAGE };
   try {
     let res = await networkService.getStatusPageByUrl(config);			
-    if(res && res.data)
-      setInitForm( res.data.data)
+    if (!res) {
+      throw new Error('No response received');
+    }
+    if (!res.data) {
+      throw new Error('No data in response');
+    }
+    setInitForm(res.data.data);
   }catch (error) {
-    logger.error("Failed to fetch status page", error);
+    logger.error(`Failed to fetch status page: ${error.message}`, error);
   } 
 };
🧰 Tools
🪛 Biome (1.9.4)

[error] 27-27: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


39-39: Clean up that spaghetti condition!

The empty object check can be simplified using a more idiomatic approach.

-{Object.keys(initForm).length===0? (
+{!Object.keys(initForm).length ? (
Client/src/Pages/Status/CreateStatus/index.jsx (1)

43-49: Mom's spaghetti alert! Clean up those commented fields!

The commented fields suggest incomplete implementation. Either implement these fields or remove the comments to keep the code clean.

-//which fields matching below?
-//publish: false,
 logo: null,
 monitors: [],
-// showUptimePercentage: false,
-// showBarcode: false,
Client/src/Components/TabPanels/Status/GeneralSettingsPanel.jsx (1)

172-172: Simplify those boolean expressions, they're making me nervous!

The ternary operators for boolean conversion are unnecessary.

-error={errors["companyName"] ? true : false}
+error={!!errors["companyName"]}

-error={errors["url"] ? true : false}
+error={!!errors["url"]}

Also applies to: 189-189

🧰 Tools
🪛 Biome (1.9.4)

[error] 172-172: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)

Client/src/Components/TabPanels/Status/Card/Server/index.jsx (2)

1-8: Yo dawg, let's clean up these imports!

The React import on line 8 is redundant since we're already using hooks which include the React namespace. Also, consider organizing imports by grouping them:

  1. External dependencies (React, MUI)
  2. Internal components
  3. Hooks and utilities
-import {
-	ServerEndAdornment,
-} from "../../../../Inputs/TextInput/Adornments";
-import Search from "../../../../Inputs/Search";
-import { useState } from "react";
-import ReorderRoundedIcon from "@mui/icons-material/ReorderRounded";
-import { Stack, IconButton } from "@mui/material";
-import React from "react";
+import { useState } from "react";
+import { Stack, IconButton } from "@mui/material";
+import ReorderRoundedIcon from "@mui/icons-material/ReorderRounded";
+
+import Search from "../../../../Inputs/Search";
+import { ServerEndAdornment } from "../../../../Inputs/TextInput/Adornments";

9-18: Mom's spaghetti... I mean, let's beef up these docs!

The JSDoc comments need some love. They're missing specific types and example usage.

 /**
- *
- * @param {*} id The Id of the Server component
- * @param {*} removeItem The function used to remove a single server
- * @param {*} onChange The Change handler function to handle when the server value is changed
- * used to update the server(monitor) lists
- * @param {*} monitors The server monitors options
- * @param {*} value the option label of the server/monitor, namely the monitor name field
+ * Server component for selecting and managing monitor options
+ * @param {string} id - The Id of the Server component
+ * @param {Function} removeItem - Callback function(id: string) => void to remove a server
+ * @param {Function} onChange - Callback function(value: any) => void to handle monitor selection
+ * @param {Array<{id: string, name: string}>} monitors - Available monitor options
+ * @param {string} value - Selected monitor name
+ * @returns {JSX.Element} Server component
+ * @example
+ * <Server
+ *   id="server1"
+ *   monitors={[{id: '1', name: 'Monitor 1'}]}
+ *   value="Monitor 1"
+ *   onChange={(val) => console.log(val)}
+ *   removeItem={(id) => console.log(id)}
+ * />
  */
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a1d4ec4 and b5409f0.

⛔ Files ignored due to path filters (1)
  • Client/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (7)
  • Client/package.json (1 hunks)
  • Client/src/Components/Inputs/ColorPicker/index.jsx (1 hunks)
  • Client/src/Components/TabPanels/Status/Card/Server/index.jsx (1 hunks)
  • Client/src/Components/TabPanels/Status/GeneralSettingsPanel.jsx (1 hunks)
  • Client/src/Pages/Status/CreateStatus/index.jsx (1 hunks)
  • Client/src/Pages/Status/index.jsx (1 hunks)
  • Client/src/Utils/NetworkService.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Client/src/Utils/NetworkService.js
🧰 Additional context used
🪛 Biome (1.9.4)
Client/src/Components/TabPanels/Status/GeneralSettingsPanel.jsx

[error] 172-172: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)


[error] 189-189: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)


[error] 229-229: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

Client/src/Pages/Status/index.jsx

[error] 27-27: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

Client/src/Pages/Status/CreateStatus/index.jsx

[error] 81-81: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (5)
Client/src/Components/Inputs/ColorPicker/index.jsx (1)

55-62: Props validation's got me weak (in a good way)! 💪

The PropTypes are well-defined and comprehensive. Nice work on making them strict with .isRequired where appropriate.

Client/package.json (1)

26-32: Knees weak from these heavy dependencies! Let's verify them! 🏋️‍♂️

The new packages look good for the status page features, but let's verify:

Also, consider:

  1. Lock the versions (remove ^) to prevent unexpected updates
  2. Add peer dependencies if required by these packages
✅ Verification successful

Yo! These dependencies ain't heavy at all! 🎵

After checking the security vulnerabilities and bundle sizes:

  • No known vulnerabilities in any package 🛡️
  • Total gzipped size is only ~24KB (that's lighter than mom's spaghetti!) 🍝
  • Each package is well-optimized for its functionality

Still recommend locking those versions though (removing ^), just to keep things stable like your stance!

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for known vulnerabilities and bundle sizes

# Check for vulnerabilities in new packages
echo "Checking vulnerabilities..."
for pkg in "immutability-helper" "mui-color-input" "react-dnd" "react-dnd-html5-backend"; do
  gh api graphql -f query='{
    securityVulnerabilities(first: 5, ecosystem: NPM, package: "'$pkg'") {
      nodes {
        advisory {
          summary
          severity
          publishedAt
        }
        vulnerableVersionRange
      }
    }
  }'
done

# Check bundle sizes
echo "Checking bundle sizes..."
curl -s https://bundlephobia.com/api/size?[email protected] | jq '.gzip'
curl -s https://bundlephobia.com/api/size?[email protected] | jq '.gzip'
curl -s https://bundlephobia.com/api/size?[email protected] | jq '.gzip'
curl -s https://bundlephobia.com/api/size?[email protected] | jq '.gzip'

Length of output: 2125

Client/src/Pages/Status/CreateStatus/index.jsx (1)

81-81: Avoid using delete for performance and maintainability reasons.

Reassigning the property to undefined or using an empty string is usually safer and more consistent.

-delete localData.logo
+localData.logo = undefined;
🧰 Tools
🪛 Biome (1.9.4)

[error] 81-81: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

Client/src/Components/TabPanels/Status/Card/Server/index.jsx (2)

50-50: Export's ready, mom's spaghetti!

The default export is properly implemented.


19-48: ⚠️ Potential issue

Weak knees alert! We're missing some crucial handlers here!

The IconButton with ReorderRoundedIcon suggests drag-and-drop functionality (mentioned in PR objectives) but lacks implementation.

Add drag-and-drop handlers to enable reordering:

+ import { useDrag, useDrop } from 'react-dnd';
+
  const Server = (props) => {
    const [search, setSearch] = useState("");
+   const [{ isDragging }, drag] = useDrag({
+     type: 'SERVER',
+     item: { id: props.id },
+     collect: (monitor) => ({
+       isDragging: monitor.isDragging(),
+     }),
+   });
+
    const handleSearch = (val) => {
      setSearch(val);
    };
+
    return (
-     <Stack direction={"row"}>
+     <Stack 
+       direction={"row"}
+       ref={drag}
+       sx={{ opacity: isDragging ? 0.5 : 1 }}
+     >

Let's check if react-dnd is already set up:

const navigate = useNavigate();
const {authToken} = useSelector((state) => state.auth);
const [initForm, setInitForm] = useState({});
const STATUS_PAGE = import.meta.env.VITE_STATU_PAGE_URL?? "status-page";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Yo! Fix that typo in the environment variable name!

There's a typo in the environment variable name: VITE_STATU_PAGE_URL should be VITE_STATUS_PAGE_URL. This could cause issues if the environment variable is set with the correct spelling.

-const STATUS_PAGE = import.meta.env.VITE_STATU_PAGE_URL?? "status-page";					
+const STATUS_PAGE = import.meta.env.VITE_STATUS_PAGE_URL ?? "status-page";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const STATUS_PAGE = import.meta.env.VITE_STATU_PAGE_URL?? "status-page";
const STATUS_PAGE = import.meta.env.VITE_STATUS_PAGE_URL ?? "status-page";

Comment on lines 19 to 48
const Server = (props) => {
const [search, setSearch] = useState("");
const handleSearch = (val) => {
setSearch(val);
};
return (
<Stack direction={"row"}>
<IconButton>
<ReorderRoundedIcon />
</IconButton>
<Search
id={props.id}
multiple={false}
isAdorned={false}
options={props.monitors ? props.monitors : []}
filteredBy="name"
inputValue={search}
value={props.value}
endAdornment={
<ServerEndAdornment
id={props.id}
removeItem={props.removeItem}
/>
}
handleInputChange={handleSearch}
handleChange={props.onChange}
/>
</Stack>
);
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Props validation? More like props spaghetti!

Add PropTypes to validate component props and prevent runtime errors.

+ import PropTypes from 'prop-types';
+
  const Server = (props) => {
    // ... component implementation
  };
+
+ Server.propTypes = {
+   id: PropTypes.string.isRequired,
+   removeItem: PropTypes.func.isRequired,
+   onChange: PropTypes.func.isRequired,
+   monitors: PropTypes.arrayOf(
+     PropTypes.shape({
+       id: PropTypes.string.isRequired,
+       name: PropTypes.string.isRequired,
+     })
+   ),
+   value: PropTypes.string,
+ };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const Server = (props) => {
const [search, setSearch] = useState("");
const handleSearch = (val) => {
setSearch(val);
};
return (
<Stack direction={"row"}>
<IconButton>
<ReorderRoundedIcon />
</IconButton>
<Search
id={props.id}
multiple={false}
isAdorned={false}
options={props.monitors ? props.monitors : []}
filteredBy="name"
inputValue={search}
value={props.value}
endAdornment={
<ServerEndAdornment
id={props.id}
removeItem={props.removeItem}
/>
}
handleInputChange={handleSearch}
handleChange={props.onChange}
/>
</Stack>
);
};
import PropTypes from 'prop-types';
const Server = (props) => {
const [search, setSearch] = useState("");
const handleSearch = (val) => {
setSearch(val);
};
return (
<Stack direction={"row"}>
<IconButton>
<ReorderRoundedIcon />
</IconButton>
<Search
id={props.id}
multiple={false}
isAdorned={false}
options={props.monitors ? props.monitors : []}
filteredBy="name"
inputValue={search}
value={props.value}
endAdornment={
<ServerEndAdornment
id={props.id}
removeItem={props.removeItem}
/>
}
handleInputChange={handleSearch}
handleChange={props.onChange}
/>
</Stack>
);
};
Server.propTypes = {
id: PropTypes.string.isRequired,
removeItem: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
monitors: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
})
),
value: PropTypes.string,
};

- The dnded servers list order can be sent for persistence now
- Removed the previous React DND and Html5 Backend libraries as it
is more lower level and hard to enable the drag indicator within AutoComplete
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (5)
Client/src/Components/TabPanels/Status/ContentPanel.jsx (3)

14-19: Yo dawg, let's beef up that documentation!

The component documentation could use more details about:

  • Props (if any)
  • Context dependencies
  • State management approach
  • Component lifecycle

51-54: Error handling's heavy, but we gotta make it ready!

The error handling could be more specific:

  1. Different toast messages for different error types
  2. Include error status code in the log
 } catch (error) {
-  createToast({ body: "Failed to fetch monitors data" });
-  logger.error("Failed to fetch monitors", error);
+  const message = error.response?.status === 404
+    ? "No monitors found"
+    : "Failed to fetch monitors data";
+  createToast({ body: message });
+  logger.error("Failed to fetch monitors", {
+    status: error.response?.status,
+    error
+  });
 }

75-111: These styles got your arms heavy? Let's move 'em to a stylesheet!

Consider moving inline styles to a separate stylesheet for better maintainability:

  1. Create a new file: ContentPanel.styles.js
  2. Move all sx prop styles there
  3. Import and use them in the component
Client/src/Components/TabPanels/Status/ServersList/Server/index.jsx (1)

31-31: Mom's spaghetti moment: We need better null checking! 🍝

The monitors prop is used directly without proper null checking. While there's a fallback, it's better to be explicit:

-options={monitors ? monitors : []}
+options={monitors ?? []}
Client/src/Components/Inputs/Search/index.jsx (1)

70-72: Lose yourself in the code, but don't lose the optional chain! 🍝

Use optional chaining to safely call handleChange:

 onChange={(e, newValue) => {
-  handleChange && handleChange(e, newValue);
+  handleChange?.(e, newValue);
 }}
🧰 Tools
🪛 Biome (1.9.4)

[error] 71-71: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b5409f0 and 642c5f0.

⛔ Files ignored due to path filters (1)
  • Client/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • Client/package.json (1 hunks)
  • Client/src/Components/Inputs/Search/index.jsx (4 hunks)
  • Client/src/Components/Inputs/TextInput/Adornments/index.jsx (3 hunks)
  • Client/src/Components/TabPanels/Status/ContentPanel.jsx (1 hunks)
  • Client/src/Components/TabPanels/Status/ServersList/Server/index.jsx (1 hunks)
  • Client/src/Components/TabPanels/Status/ServersList/index.jsx (1 hunks)
  • Client/src/Utils/NetworkService.js (1 hunks)
  • Client/src/main.jsx (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • Client/src/main.jsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • Client/package.json
  • Client/src/Utils/NetworkService.js
🧰 Additional context used
🪛 Biome (1.9.4)
Client/src/Components/Inputs/Search/index.jsx

[error] 71-71: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (1)
Client/src/Components/TabPanels/Status/ContentPanel.jsx (1)

28-57: Mom's spaghetti won't clean up your memory leaks!

The useEffect hook needs a cleanup function to prevent memory leaks when component unmounts during fetch operations.

Comment on lines 170 to 186
{/* <Stack sx={{margin: theme.spacing(20)}}
>
<Checkbox
id="show-barcode"
label={`Show Barcode`}
isChecked={form.showBarcode}
onChange={handleChange}
onBlur={handleBlur}
/>
<Checkbox
id="show-uptime-percentage"
label={`Show Uptime Percentage`}
isChecked={form.showUptimePercentage}
onChange={handleChange}
onBlur={handleBlur}
/>
</Stack> */}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Clean up that commented code - no vomit on your sweater!

Remove commented-out code to maintain cleanliness. If this feature is planned for future implementation, track it in your issue tracker instead.

Comment on lines 37 to 39
if(response.data.data.monitors.length==0){
setErrors({monitors: "Please config monitors to setup status page"})
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Triple equals keeps the code from getting weak in the knees!

Use strict equality comparison (===) instead of loose equality (==):

-if(response.data.data.monitors.length==0){
+if(response.data.data.monitors.length === 0){
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if(response.data.data.monitors.length==0){
setErrors({monitors: "Please config monitors to setup status page"})
}
if(response.data.data.monitors.length === 0){
setErrors({monitors: "Please config monitors to setup status page"})
}

Comment on lines 58 to 62
const handleAddNew = () => {
if (cards.length === monitors.length) return;
const newCards = [...cards, { id: "" + Math.random(),val:{} }];
setCards(newCards);
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Don't let your IDs be spaghetti - use a proper UUID!

Using Math.random() for generating IDs is not reliable. Consider using a proper UUID library:

-const newCards = [...cards, { id: "" + Math.random(), val: {} }];
+import { v4 as uuidv4 } from 'uuid';
+const newCards = [...cards, { id: uuidv4(), val: {} }];

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 49 to 56
Server.propTypes = {
id: PropTypes.string.isRequired,
monitors: PropTypes.array.isRequired,
value: PropTypes.string.isRequired,
removeItem: PropTypes.function.isRequired,
onChange: PropTypes.function.isRequired,
dragHandleProps: PropTypes.object.isRequired,
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Knees weak, arms heavy: PropTypes got bugs already! 🍝

The PropTypes definition contains invalid types. PropTypes.function is not valid, it should be PropTypes.func:

 Server.propTypes = {
   id: PropTypes.string.isRequired,
   monitors: PropTypes.array.isRequired,
   value: PropTypes.string.isRequired,
-  removeItem: PropTypes.function.isRequired,
-  onChange: PropTypes.function.isRequired,
+  removeItem: PropTypes.func.isRequired,
+  onChange: PropTypes.func.isRequired,
   dragHandleProps: PropTypes.object.isRequired,
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Server.propTypes = {
id: PropTypes.string.isRequired,
monitors: PropTypes.array.isRequired,
value: PropTypes.string.isRequired,
removeItem: PropTypes.function.isRequired,
onChange: PropTypes.function.isRequired,
dragHandleProps: PropTypes.object.isRequired,
};
Server.propTypes = {
id: PropTypes.string.isRequired,
monitors: PropTypes.array.isRequired,
value: PropTypes.string.isRequired,
removeItem: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
dragHandleProps: PropTypes.object.isRequired,
};

Comment on lines +1 to +4
import { ServerStartAdornment, ServerEndAdornment } from "../../../../Inputs/TextInput/Adornments";
import Search from "../../../../Inputs/Search";
import {useState} from "react"
import React from "react";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Yo, we're missing that PropTypes import! 🍝

Add the PropTypes import to prevent runtime errors:

+import PropTypes from 'prop-types';
 import { ServerStartAdornment, ServerEndAdornment } from "../../../../Inputs/TextInput/Adornments";
 import Search from "../../../../Inputs/Search";
-import {useState} from "react"
-import React from "react"; 
+import { useState } from "react";
+import React from "react";
 import { Stack } from "@mui/material";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { ServerStartAdornment, ServerEndAdornment } from "../../../../Inputs/TextInput/Adornments";
import Search from "../../../../Inputs/Search";
import {useState} from "react"
import React from "react";
import PropTypes from 'prop-types';
import { ServerStartAdornment, ServerEndAdornment } from "../../../../Inputs/TextInput/Adornments";
import Search from "../../../../Inputs/Search";
import { useState } from "react";
import React from "react";
import { Stack } from "@mui/material";

Comment on lines 127 to 134
ServersList.propTypes = {
monitors: PropTypes.array.isRequired,
cards: PropTypes.array.isRequired,
setCards: PropTypes.function.isRequired,
form: PropTypes.object.isRequired,
setForm: PropTypes.function.isRequired,
removeItem: PropTypes.function.isRequired
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Lost yourself in the PropTypes again! 🍝

Fix the invalid PropTypes:

 ServersList.propTypes = {
   monitors: PropTypes.array.isRequired,
   cards: PropTypes.array.isRequired,
-  setCards: PropTypes.function.isRequired,
+  setCards: PropTypes.func.isRequired,
   form: PropTypes.object.isRequired,
-  setForm: PropTypes.function.isRequired,
+  setForm: PropTypes.func.isRequired,
-  removeItem: PropTypes.function.isRequired
+  removeItem: PropTypes.func.isRequired
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ServersList.propTypes = {
monitors: PropTypes.array.isRequired,
cards: PropTypes.array.isRequired,
setCards: PropTypes.function.isRequired,
form: PropTypes.object.isRequired,
setForm: PropTypes.function.isRequired,
removeItem: PropTypes.function.isRequired
};
ServersList.propTypes = {
monitors: PropTypes.array.isRequired,
cards: PropTypes.array.isRequired,
setCards: PropTypes.func.isRequired,
form: PropTypes.object.isRequired,
setForm: PropTypes.func.isRequired,
removeItem: PropTypes.func.isRequired
};

Comment on lines +21 to +41
const handleCardChange = (event, val) => {
let newCards;
const { id } = event.target;
let idx = cards.findIndex((a) => {
let found = false;
let optionIdx = id.indexOf("-option");
if (optionIdx !== -1) found = a.id == id.substr(0, optionIdx);
else found = a.id == id;
return found;
});

if (idx >= 0) {
let x = JSON.parse(JSON.stringify(cards[idx]));
x.val = val;
newCards = update(cards, { $splice: [[idx, 1, x]] });
} else {
newCards = update(cards, { $push: [{ id: id, val: val }] });
}
setCards(newCards);
setForm({ ...form, monitors: newCards.map((c) => c.val) });
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

There's vomit on his sweater: This code needs cleanup! 🍝

The card handling logic can be simplified:

  1. Unnecessary JSON operations can be replaced with object spread
  2. Complex string manipulation for finding index can be simplified
 const handleCardChange = (event, val) => {
   let newCards;
   const { id } = event.target;
-  let idx = cards.findIndex((a) => {
-    let found = false;
-    let optionIdx = id.indexOf("-option");
-    if (optionIdx !== -1) found = a.id == id.substr(0, optionIdx);
-    else found = a.id == id;
-    return found;
-  });
+  const baseId = id.split('-option')[0];
+  const idx = cards.findIndex(a => a.id === baseId);

   if (idx >= 0) {
-    let x = JSON.parse(JSON.stringify(cards[idx]));
-    x.val = val;
-    newCards = update(cards, { $splice: [[idx, 1, x]] });
+    newCards = update(cards, { 
+      [idx]: { $merge: { val } }
+    });
   } else {
     newCards = update(cards, { $push: [{ id: id, val: val }] });
   }
   setCards(newCards);
   setForm({ ...form, monitors: newCards.map((c) => c.val) });
 };

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +70 to +78
export const ServerStartAdornment = (props) => {
return (
<InputAdornment position="start">
<Stack {...props.dragHandleProps}>
<ReorderRoundedIcon />
</Stack>
</InputAdornment>
);
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Snap back to reality: We need PropTypes! 🍝

Add PropTypes definition for ServerStartAdornment:

 export const ServerStartAdornment = (props) => {
   return (
     <InputAdornment position="start">
       <Stack {...props.dragHandleProps}>
         <ReorderRoundedIcon />
       </Stack>
     </InputAdornment>
   );
 };
+
+ServerStartAdornment.propTypes = {
+  dragHandleProps: PropTypes.object.isRequired,
+};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const ServerStartAdornment = (props) => {
return (
<InputAdornment position="start">
<Stack {...props.dragHandleProps}>
<ReorderRoundedIcon />
</Stack>
</InputAdornment>
);
};
export const ServerStartAdornment = (props) => {
return (
<InputAdornment position="start">
<Stack {...props.dragHandleProps}>
<ReorderRoundedIcon />
</Stack>
</InputAdornment>
);
};
ServerStartAdornment.propTypes = {
dragHandleProps: PropTypes.object.isRequired,
};

}}
fullWidth
freeSolo
disabled={disabled}
disableClearable
options={options}
getOptionLabel={(option) => option[filteredBy]}
getOptionLabel={(option) => option[filteredBy]??""}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Don't let your code slip, mom's spaghetti! 🍝

Improve null checking in getOptionLabel:

-getOptionLabel={(option) => option[filteredBy]??""}
+getOptionLabel={(option) => option?.[filteredBy] ?? ""}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
getOptionLabel={(option) => option[filteredBy]??""}
getOptionLabel={(option) => option?.[filteredBy] ?? ""}

- Add label for logo
- Replace react beautiful dnd with hello-pangea to fix the  "defaultProps will not be supported " console warning due to react beatuful dnd deprecation
- Add all the checkboxes and respective validation
- Remove subdomain from UI display
- Pass Handlers as context for both tabs to ease code scalability
- All Status page fields are now ready for submission
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (6)
Client/src/Components/TabPanels/Status/ContentPanel.jsx (1)

70-77: Fix that loose equality before it makes your palms sweaty!

Use strict equality comparison to avoid type coercion issues:

 const removeCard = (id) => {
-  const newCards = cards.filter((c) => c?.id != id);
+  const newCards = cards.filter((c) => c?.id !== id);
   setCards(newCards);
   setForm({
     ...form,
     monitors: newCards.filter((c) => c.val !== undefined).map((c) => c.val),
   });
 };
Client/src/Pages/Status/CreateStatus/index.jsx (3)

36-36: Clean up that environment variable handling!

The environment variable fallback could be improved:

-const STATUS_PAGE = import.meta.env.VITE_STATU_PAGE_URL?? "status-page";
+const STATUS_PAGE = import.meta.env.VITE_STATUS_PAGE_URL || "status-page";

58-58: Fix that loose equality in the error mapping!

Use strict equality comparison:

-let anyMatch = val.some((vl) => vl == id);
+let anyMatch = val.some((vl) => vl === id);

113-121: Clean up that checkbox handler - no vomit on your sweater!

Fix the typo and simplify the code:

-const handelCheckboxChange = (e) => {			
+const handleCheckboxChange = (e) => {			
   const { id } = e.target;
   
   setForm((prev) => ({
-    return ({
     ...prev,
     [id ]: !prev[id]
-    })
   }));
 }
Client/src/Components/TabPanels/Status/GeneralSettingsPanel.jsx (2)

146-146: Knees weak: Simplify those error props! 🍝

The error props use unnecessary ternary operators with boolean literals.

-error={errors["companyName"] ? true : false}
+error={!!errors["companyName"]}
-error={errors["url"] ? true : false}
+error={!!errors["url"]}

Also applies to: 157-157

🧰 Tools
🪛 Biome (1.9.4)

[error] 146-146: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)


207-222: Mom's spaghetti: Simplify that logo condition! 🍝

The logo condition can be simplified using optional chaining.

-) : logo && logo.type ? (
+) : logo?.type ? (
🧰 Tools
🪛 Biome (1.9.4)

[error] 207-207: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 642c5f0 and 045a9cf.

⛔ Files ignored due to path filters (1)
  • Client/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (10)
  • Client/package.json (2 hunks)
  • Client/src/Components/Inputs/Checkbox/index.jsx (5 hunks)
  • Client/src/Components/Inputs/ColorPicker/index.jsx (1 hunks)
  • Client/src/Components/TabPanels/Status/ContentPanel.jsx (1 hunks)
  • Client/src/Components/TabPanels/Status/GeneralSettingsPanel.jsx (1 hunks)
  • Client/src/Components/TabPanels/Status/ServersList/Server/index.jsx (1 hunks)
  • Client/src/Components/TabPanels/Status/ServersList/index.jsx (1 hunks)
  • Client/src/Pages/Status/CreateStatus/index.jsx (1 hunks)
  • Client/src/Pages/Status/CreateStatusContext/index.jsx (1 hunks)
  • Client/src/Validation/validation.js (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • Client/src/Pages/Status/CreateStatusContext/index.jsx
  • Client/package.json
  • Client/src/Components/TabPanels/Status/ServersList/Server/index.jsx
  • Client/src/Components/Inputs/Checkbox/index.jsx
🧰 Additional context used
🪛 Biome (1.9.4)
Client/src/Validation/validation.js

[error] 210-210: This property value named showBarcode is later overwritten by an object member with the same name.

Overwritten with this value.

If an object property with the same name is defined multiple times (except when combining a getter with a setter), only the last definition makes it into the object and previous definitions are ignored.
Unsafe fix: Remove this property value named showBarcode

(lint/suspicious/noDuplicateObjectKeys)

Client/src/Components/TabPanels/Status/GeneralSettingsPanel.jsx

[error] 146-146: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)


[error] 157-157: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)


[error] 207-207: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (12)
Client/src/Components/TabPanels/Status/ContentPanel.jsx (3)

79-189: LGTM! Clean JSX structure and styling!

The component structure is well-organized with proper Material-UI usage and error handling.


65-69: 🛠️ Refactor suggestion

Don't let your IDs be spaghetti - use a proper UUID!

Using Math.random() for generating IDs is not reliable. Consider using a proper UUID library:

+import { v4 as uuidv4 } from 'uuid';

 const handleAddNew = () => {
   if (cards.length === monitors.length) return;
-  const newCards = [...cards, { id: "" + Math.random(), val: {} }];
+  const newCards = [...cards, { id: uuidv4(), val: {} }];
   setCards(newCards);
 };

Likely invalid or redundant comment.


35-64: ⚠️ Potential issue

Yo! Clean up that useEffect before it makes your knees weak!

The useEffect hook needs some cleanup to prevent memory leaks and improve comparison logic:

 useEffect(() => {
+  let isSubscribed = true;
   const fetchMonitors = async () => {
     try {
       const response = await networkService.getMonitorsByTeamId({
         authToken: authToken,
         teamId: user.teamId,
         limit: null,
         types: ["http"],
       });
-      if (response.data.data.monitors.length == 0) {
+      if (!isSubscribed) return;
+      if (response.data.data.monitors.length === 0) {
         setErrors({ monitors: "Please config monitors to setup status page" });
       }
       const fullMonitors = response.data.data.monitors;
       setMonitors(fullMonitors);
       if (form.monitors.length > 0) {
         const initiCards = form.monitors.map((mid, idx) => ({
           id: "" + idx,
           val: fullMonitors.filter((fM) =>
-            mid._id ? fM._id == mid._id : fM._id == mid
+            mid._id ? fM._id === mid._id : fM._id === mid
           )[0],
         }));
         setCards(initiCards);
       }
     } catch (error) {
+      if (!isSubscribed) return;
       createToast({ body: "Failed to fetch monitors data" });
       logger.error("Failed to fetch monitors", error);
     }
   };
   fetchMonitors();
+  return () => {
+    isSubscribed = false;
+  };
 }, [user, authToken]);

Likely invalid or redundant comment.

Client/src/Components/Inputs/ColorPicker/index.jsx (1)

1-60: LGTM! Clean and well-documented component!

The ColorPicker component is well-implemented with proper documentation, error handling, and PropTypes validation.

Client/src/Components/TabPanels/Status/ServersList/index.jsx (2)

130-137: ⚠️ Potential issue

Lost yourself in the PropTypes again!

Fix the invalid PropTypes:

 ServersList.propTypes = {
   monitors: PropTypes.array.isRequired,
   cards: PropTypes.array.isRequired,
   setCards: PropTypes.func.isRequired,
   form: PropTypes.object.isRequired,
   setForm: PropTypes.func.isRequired,
-  removeItem: PropTypes.function.isRequired
+  removeItem: PropTypes.func.isRequired
 };

Likely invalid or redundant comment.


24-44: 🛠️ Refactor suggestion

Clean up that spaghetti code - simplify the card handling!

The card handling logic can be simplified:

 const handleCardChange = (event, val) => {
   let newCards;
   const { id } = event.target;
-  let idx = cards.findIndex((a) => {
-    let found = false;
-    let optionIdx = id.indexOf("-option");
-    if (optionIdx !== -1) found = a.id == id.substr(0, optionIdx);
-    else found = a.id == id;
-    return found;
-  });
+  const baseId = id.split('-option')[0];
+  const idx = cards.findIndex(a => a.id === baseId);

   if (idx >= 0) {
-    let x = JSON.parse(JSON.stringify(cards[idx]));
-    x.val = val;
-    newCards = update(cards, { $splice: [[idx, 1, x]] });
+    newCards = update(cards, { 
+      [idx]: { $merge: { val } }
+    });
   } else {
     newCards = update(cards, { $push: [{ id: id, val: val }] });
   }
   setCards(newCards);
   setForm({ ...form, monitors: newCards.map((c) => c.val) });
 };

Likely invalid or redundant comment.

Client/src/Pages/Status/CreateStatus/index.jsx (2)

95-95: ⚠️ Potential issue

Yo! Fix this status check before it makes your knees weak!

The status check has incorrect syntax:

-if (!res.status === 200) {
+if (res.status !== 200) {

Likely invalid or redundant comment.


74-82: 🛠️ Refactor suggestion

Clean up that spaghetti code with safer object handling!

The object spread and optional chaining could be more defensive:

 let localData = {
   ...form,
   monitors:
-    form.monitors[0] && Object.keys(form.monitors[0]).includes("_id")
+    Array.isArray(form.monitors) && form.monitors[0]?.hasOwnProperty("_id")
       ? form.monitors.map((m) => m._id)
       : form.monitors,
   theme: mode,
-  logo: { type: form.logo?.type ?? "", size: form.logo?.size ?? "" },
+  logo: form.logo ? { type: form.logo.type || "", size: form.logo.size || "" } : null,
 };

Likely invalid or redundant comment.

Client/src/Components/TabPanels/Status/GeneralSettingsPanel.jsx (2)

76-84: Yo! Clean up those object URLs before they make you lose your spaghetti! 🍝

Memory leaks alert! The URL.createObjectURL creates object URLs that need to be revoked.


85-95: Arms are heavy: Clean up that interval properly!

The interval cleanup should be handled in a useEffect to prevent memory leaks.

Client/src/Validation/validation.js (2)

177-190: Yo! This validation's making me lose myself in the duplication!

The logoImageValidation schema duplicates logic from imageValidation.


192-213: Snap back to reality, oh there goes validation! 🎵

The schema needs stronger validation for URL, color, and theme fields.

🧰 Tools
🪛 Biome (1.9.4)

[error] 210-210: This property value named showBarcode is later overwritten by an object member with the same name.

Overwritten with this value.

If an object property with the same name is defined multiple times (except when combining a getter with a setter), only the last definition makes it into the object and previous definitions are ignored.
Unsafe fix: Remove this property value named showBarcode

(lint/suspicious/noDuplicateObjectKeys)

Comment on lines +210 to +211
showBarcode: joi.boolean(),
showBarcode: joi.boolean()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Vomit on his sweater already: Duplicate showBarcode! 🍝

The showBarcode property is defined twice, which will cause the first definition to be ignored.

 showBarcode: joi.boolean(),
-showBarcode: joi.boolean()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
showBarcode: joi.boolean(),
showBarcode: joi.boolean()
showBarcode: joi.boolean(),
🧰 Tools
🪛 Biome (1.9.4)

[error] 210-210: This property value named showBarcode is later overwritten by an object member with the same name.

Overwritten with this value.

If an object property with the same name is defined multiple times (except when combining a getter with a setter), only the last definition makes it into the object and previous definitions are ignored.
Unsafe fix: Remove this property value named showBarcode

(lint/suspicious/noDuplicateObjectKeys)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
Client/src/Components/Inputs/Search/index.jsx (2)

15-15: Yo dawg, I spotted a typo in your JSDoc! 🍝

There's a typo in the parameter documentation:

-@param {Function} Prop.onBlur - Function to call when the input is blured
+@param {Function} props.onBlur - Function to call when the input loses focus

73-75: Let's make that onChange handler bulletproof! 🍝

Use optional chaining since handleChange is an optional prop:

-handleChange && handleChange(e, newValue);
+handleChange?.(e, newValue);
🧰 Tools
🪛 Biome (1.9.4)

[error] 74-74: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

Client/src/Components/TabPanels/Status/ContentPanel.jsx (1)

72-79: Clean up that comparison spaghetti! 🍝

The removeCard function needs some cleanup:

  1. Use strict equality comparison
  2. Improve undefined handling
 const removeCard = (id) => {
-  const newCards = cards.filter((c) => c?.id != id);
+  const newCards = cards.filter((c) => c?.id !== id);
   setCards(newCards);
   setForm({
     ...form,
-    monitors: newCards.filter((c) => c.val !== undefined).map((c) => c.val),
+    monitors: newCards.filter((c) => c?.val != null).map((c) => c.val),
   });
 };
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f0352cc and dba8517.

📒 Files selected for processing (4)
  • Client/src/Components/Inputs/Search/index.jsx (4 hunks)
  • Client/src/Components/TabPanels/Status/ContentPanel.jsx (1 hunks)
  • Client/src/Components/TabPanels/Status/ServersList/Server/index.jsx (1 hunks)
  • Client/src/Components/TabPanels/Status/ServersList/index.jsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Client/src/Components/TabPanels/Status/ServersList/Server/index.jsx
🧰 Additional context used
🪛 Biome (1.9.4)
Client/src/Components/Inputs/Search/index.jsx

[error] 74-74: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (9)
Client/src/Components/Inputs/Search/index.jsx (4)

58-60: Props declaration looking clean! 🍝

The new props are well-structured and follow the component's naming conventions.


81-81: Don't let your code slip, mom's spaghetti! 🍝

Improve null checking in getOptionLabel:

-getOptionLabel={(option) => option[filteredBy]??""}
+getOptionLabel={(option) => option?.[filteredBy] ?? ""}

96-102: Clean implementation of input slots! 🍝

The migration from InputProps to slotProps follows the latest MUI best practices. Nice work on the conditional adornment handling!


199-199: PropTypes looking sharp! 🍝

The type definitions are well-structured and properly handle both array and object values for the value prop.

Also applies to: 207-209

Client/src/Components/TabPanels/Status/ServersList/index.jsx (2)

132-140: Props looking clean, no spaghetti in sight! 🍝

The PropTypes are well-defined and properly validate all required props.


25-45: 🛠️ Refactor suggestion

Yo! Clean up this spaghetti code! 🍝

The card handling logic needs some cleanup:

  1. Replace complex string manipulation with split()
  2. Remove unnecessary JSON operations
  3. Use strict equality (===)
 const handleCardChange = (event, val) => {
   let newCards;
   const { id } = event.target;
-  let idx = cards.findIndex((a) => {
-    let found = false;
-    let optionIdx = id.indexOf("-option");
-    if (optionIdx !== -1) found = a.id == id.substr(0, optionIdx);
-    else found = a.id == id;
-    return found;
-  });
+  const baseId = id.split('-option')[0];
+  const idx = cards.findIndex(a => a.id === baseId);

   if (idx >= 0) {
-    let x = JSON.parse(JSON.stringify(cards[idx]));
-    x.val = val;
-    newCards = update(cards, { $splice: [[idx, 1, x]] });
+    newCards = update(cards, {
+      [idx]: { $merge: { val } }
+    });
   } else {
     newCards = update(cards, { $push: [{ id: id, val: val }] });
   }
   setCards(newCards);
   setForm({ ...form, monitors: newCards.map((c) => c.val) });
 };

Likely invalid or redundant comment.

Client/src/Components/TabPanels/Status/ContentPanel.jsx (3)

81-91: Validation looking clean, no spaghetti here! 🍝

The handleServersBlur function is well-implemented with proper error handling.


67-71: 🛠️ Refactor suggestion

Mom's spaghetti alert! Replace Math.random() with UUID! 🍝

Using Math.random() for ID generation isn't reliable.

+import { v4 as uuidv4 } from 'uuid';
 const handleAddNew = () => {
   if (cards.length === monitors.length) return;
-  const newCards = [...cards, { id: "" + Math.random(), val: {} }];
+  const newCards = [...cards, { id: uuidv4(), val: {} }];
   setCards(newCards);
 };

Likely invalid or redundant comment.


37-66: 🛠️ Refactor suggestion

Cleanup missing in useEffect - don't let those memory leaks make you weak in the knees! 🍝

The useEffect hook needs some improvements:

  1. Add cleanup function to prevent memory leaks
  2. Use strict equality comparison
  3. Add loading state management
+const [isLoading, setIsLoading] = useState(false);
 useEffect(() => {
+  let isSubscribed = true;
   const fetchMonitors = async () => {
+    setIsLoading(true);
     try {
       const response = await networkService.getMonitorsByTeamId({
         authToken: authToken,
         teamId: user.teamId,
         limit: null,
         types: ["http"],
       });
+      if (!isSubscribed) return;
-      if (response.data.data.monitors.length == 0) {
+      if (response.data.data.monitors.length === 0) {
         setErrors({ monitors: "Please config monitors to setup status page" });
       }
       const fullMonitors = response.data.data.monitors;
       setMonitors(fullMonitors);
       if (form.monitors.length > 0) {
         const initiCards = form.monitors.map((mid, idx) => ({
           id: "" + idx,
           val: fullMonitors.filter((fM) =>
-            mid._id ? fM._id == mid._id : fM._id == mid
+            mid._id ? fM._id === mid._id : fM._id === mid
           )[0],
         }));
         setCards(initiCards);
       }
     } catch (error) {
+      if (!isSubscribed) return;
       createToast({ body: "Failed to fetch monitors data" });
       logger.error("Failed to fetch monitors", error);
+    } finally {
+      if (isSubscribed) {
+        setIsLoading(false);
+      }
+    }
   };
   fetchMonitors();
+  return () => {
+    isSubscribed = false;
+  };
 }, [user, authToken]);

Likely invalid or redundant comment.

Comment on lines +110 to +119
<Server
key={index}
id={item?.id ?? "" + Math.random()}
monitors={monitors}
value={item.val ?? {}}
onChange={handleCardChange}
removeItem={removeItem}
dragHandleProps={provided.dragHandleProps}
onBlur={onBlur}
/>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Don't let your IDs be spaghetti - use a proper UUID! 🍝

Using Math.random() for ID generation isn't reliable and could lead to collisions.

+import { v4 as uuidv4 } from 'uuid';
 <Server
   key={index}
-  id={item?.id ?? "" + Math.random()}
+  id={item?.id ?? uuidv4()}
   monitors={monitors}
   value={item.val ?? {}}
   onChange={handleCardChange}
   removeItem={removeItem}
   dragHandleProps={provided.dragHandleProps}
   onBlur={onBlur}
 />

Committable suggestion skipped: line range outside the PR's diff.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
Client/src/Components/TabPanels/Status/GeneralSettingsPanel.jsx (1)

76-84: ⚠️ Potential issue

Vomit on his sweater: Memory leaks in logo handling!

The URL.createObjectURL creates object URLs that need to be revoked to prevent memory leaks. Also, there's duplicate state setting logic.

+ const objectUrl = URL.createObjectURL(pic);
 const newLogo = {
-  src: URL.createObjectURL(pic),
+  src: objectUrl,
   name: pic.name,
   type: pic.type,
   size: pic.size,
 };
 setProgress((prev) => ({ ...prev, isLoading: true }));
 setLogo(newLogo);
 setForm({ ...form, logo: newLogo });
+ 
+ useEffect(() => {
+   return () => {
+     if (objectUrl) {
+       URL.revokeObjectURL(objectUrl);
+     }
+   };
+ }, []);
🧹 Nitpick comments (3)
Client/src/Components/TabPanels/Status/GeneralSettingsPanel.jsx (3)

19-22: Yo, there's spaghetti in your docs!

There's a typo in the documentation comment: "ued" should be "used".

- * General settings panel is ued to compose part of the public static page
+ * General settings panel is used to compose part of the public static page

146-146: Knees weak: Simplify those boolean conversions!

The boolean conversions in error handling can be simplified by removing the ternary operators.

- error={errors["companyName"] ? true : false}
+ error={!!errors["companyName"]}

- error={errors["url"] ? true : false}
+ error={!!errors["url"]}

Also applies to: 157-157

🧰 Tools
🪛 Biome (1.9.4)

[error] 146-146: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)


207-207: Arms are heavy: Simplify with optional chaining!

The conditional check can be simplified using optional chaining.

- ) : logo && logo.type ? (
+ ) : logo?.type ? (
🧰 Tools
🪛 Biome (1.9.4)

[error] 207-207: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dba8517 and 66325bd.

📒 Files selected for processing (1)
  • Client/src/Components/TabPanels/Status/GeneralSettingsPanel.jsx (1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
Client/src/Components/TabPanels/Status/GeneralSettingsPanel.jsx

[error] 146-146: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)


[error] 157-157: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)


[error] 207-207: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (1)
Client/src/Components/TabPanels/Status/GeneralSettingsPanel.jsx (1)

99-106: Snap back to reality: Missing form submission handler!

The form element lacks an onSubmit handler. This could lead to unexpected form submissions.

Add an onSubmit handler to prevent default form submission:

 <Stack
   component="form"
   className="status-general-settings-form"
   noValidate
   spellCheck="false"
+  onSubmit={(e) => e.preventDefault()}
   gap={theme.spacing(12)}
   mt={theme.spacing(6)}
 >

Comment on lines +171 to +180
<Select
id="timezone"
name="timezone"
label="Display timezone"
value={form.timezone}
onChange={handleChange}
onBlur={handleBlur}
items={timezones}
error={errors["display-timezone"]}
/>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Lost himself in the spaghetti: Error key mismatch!

The error key for the timezone field doesn't match its id. This will cause validation errors to not display properly.

- error={errors["display-timezone"]}
+ error={errors["timezone"]}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Select
id="timezone"
name="timezone"
label="Display timezone"
value={form.timezone}
onChange={handleChange}
onBlur={handleBlur}
items={timezones}
error={errors["display-timezone"]}
/>
<Select
id="timezone"
name="timezone"
label="Display timezone"
value={form.timezone}
onChange={handleChange}
onBlur={handleBlur}
items={timezones}
error={errors["timezone"]}
/>


const [progress, setProgress] = useState({ value: 0, isLoading: false });
const intervalRef = useRef(null);
const STATUS_PAGE = import.meta.env.VITE_STATU_PAGE_URL ?? "status-page";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Mom's spaghetti: Environment variable typo!

The environment variable name contains a typo: VITE_STATU_PAGE_URL should be VITE_STATUS_PAGE_URL.

- const STATUS_PAGE = import.meta.env.VITE_STATU_PAGE_URL ?? "status-page";
+ const STATUS_PAGE = import.meta.env.VITE_STATUS_PAGE_URL ?? "status-page";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const STATUS_PAGE = import.meta.env.VITE_STATU_PAGE_URL ?? "status-page";
const STATUS_PAGE = import.meta.env.VITE_STATUS_PAGE_URL ?? "status-page";

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
Client/src/Pages/Status/CreateStatus/index.jsx (1)

95-95: ⚠️ Potential issue

Yo! Fix this status check before it makes your knees weak!

The status check has incorrect syntax that will always evaluate to false.

-if (!res.status === 200) {
+if (res.status !== 200) {
🧹 Nitpick comments (4)
Client/src/Pages/Status/CreateStatus/index.jsx (4)

45-45: Clean up that spaghetti code by removing the TODO comment!

Remove the unclear comment as it doesn't provide any value.

-//which fields matching below?

55-64: Mom's spaghetti alert! Clean up that error handling logic!

Using map for side effects is an anti-pattern. Use forEach or for...of instead.

-Object.keys(errors).map((id) => {
+for (const id of Object.keys(errors)) {
   if (newIdx !== -1) return;
-  error_tab_maping.map((val, idx) => {
+  for (const [idx, val] of error_tab_maping.entries()) {
     let anyMatch = val.some((vl) => vl == id);
     if (anyMatch) {
       newIdx = idx;
-      return;
+      break;
     }
-  });
+  }
-});

113-121: Yo! These typos and formatting are making me lose myself!

There's a typo in the function name and inconsistent spacing.

-const handelCheckboxChange = (e) => {			
+const handleCheckboxChange = (e) => {
   const { id } = e.target;
-   
+
   setForm((prev) => {
-      return ({
+    return {
       ...prev,
-      [id ]: !prev[id]
-   })});
+      [id]: !prev[id]
+    };
+  });
 }

165-178: Clean up that style spaghetti by moving it to the theme!

Move these inline styles to the theme configuration for better maintainability and reusability.

Create a theme extension for tab styles:

// theme.js
{
  components: {
    MuiTab: {
      styleOverrides: {
        root: {
          fontSize: 13,
          textTransform: 'none',
          minWidth: 'fit-content',
          minHeight: 0,
          paddingLeft: 0,
          fontWeight: 400,
          '&:focus': {
            outline: 'none',
          },
        },
      },
    },
  },
}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 66325bd and 083a134.

⛔ Files ignored due to path filters (1)
  • Client/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (13)
  • Client/package.json (2 hunks)
  • Client/src/App.jsx (1 hunks)
  • Client/src/Components/ConfigBox/index.jsx (1 hunks)
  • Client/src/Components/Inputs/Checkbox/index.jsx (5 hunks)
  • Client/src/Components/Inputs/Image/index.jsx (9 hunks)
  • Client/src/Components/Inputs/Search/index.jsx (4 hunks)
  • Client/src/Components/Inputs/TextInput/Adornments/index.jsx (3 hunks)
  • Client/src/Components/ProgressBars/index.jsx (1 hunks)
  • Client/src/Components/Sidebar/index.jsx (2 hunks)
  • Client/src/Components/TabPanels/Status/ContentPanel.jsx (1 hunks)
  • Client/src/Pages/Maintenance/CreateMaintenance/index.jsx (1 hunks)
  • Client/src/Pages/Status/CreateStatus/index.jsx (1 hunks)
  • Client/src/Pages/Status/index.jsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (8)
  • Client/src/App.jsx
  • Client/src/Components/ConfigBox/index.jsx
  • Client/src/Components/Sidebar/index.jsx
  • Client/package.json
  • Client/src/Pages/Maintenance/CreateMaintenance/index.jsx
  • Client/src/Components/ProgressBars/index.jsx
  • Client/src/Components/Inputs/Checkbox/index.jsx
  • Client/src/Components/Inputs/Image/index.jsx
🧰 Additional context used
🪛 Biome (1.9.4)
Client/src/Pages/Status/index.jsx

[error] 27-27: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

Client/src/Components/Inputs/Search/index.jsx

[error] 74-74: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (8)
Client/src/Components/TabPanels/Status/ContentPanel.jsx (2)

67-79: ⚠️ Potential issue

Don't let your IDs be spaghetti - use a proper UUID! 🍝

The card management logic needs improvement:

+import { v4 as uuidv4 } from 'uuid';
 const handleAddNew = () => {
   if (cards.length === monitors.length) return;
-  const newCards = [...cards, { id: "" + Math.random(), val: {} }];
+  const newCards = [...cards, { id: uuidv4(), val: {} }];
   setCards(newCards);
 };
 const removeCard = (id) => {
-  const newCards = cards.filter((c) => c?.id != id);
+  const newCards = cards.filter((c) => c?.id !== id);
   setCards(newCards);
   setForm({
     ...form,
     monitors: newCards.filter((c) => c.val !== undefined).map((c) => c.val),
   });
 };

Likely invalid or redundant comment.


37-66: ⚠️ Potential issue

Yo! Clean up that useEffect before it leaks like mom's spaghetti! 🍝

The useEffect hook needs some cleanup to prevent memory leaks and improve error handling:

 useEffect(() => {
+  let isSubscribed = true;
   const fetchMonitors = async () => {
     try {
       const response = await networkService.getMonitorsByTeamId({
         authToken: authToken,
         teamId: user.teamId,
         limit: null,
         types: ["http"],
       });
-      if (response.data.data.monitors.length == 0) {
+      if (!isSubscribed) return;
+      if (response.data.data.monitors.length === 0) {
         setErrors({ monitors: "Please config monitors to setup status page" });
       }
       const fullMonitors = response.data.data.monitors;
       setMonitors(fullMonitors);
       if (form.monitors.length > 0) {
         const initiCards = form.monitors.map((mid, idx) => ({
           id: "" + idx,
           val: fullMonitors.filter((fM) =>
-            mid._id ? fM._id == mid._id : fM._id == mid
+            mid._id ? fM._id === mid._id : fM._id === mid
           )[0],
         }));
         setCards(initiCards);
       }
     } catch (error) {
+      if (!isSubscribed) return;
       createToast({ body: "Failed to fetch monitors data" });
       logger.error("Failed to fetch monitors", error);
     }
   };
   fetchMonitors();
+  return () => {
+    isSubscribed = false;
+  };
 }, [user, authToken]);

Likely invalid or redundant comment.

Client/src/Pages/Status/index.jsx (1)

21-21: ⚠️ Potential issue

Fix that typo in the environment variable name! 🍝

-const STATUS_PAGE = import.meta.env.VITE_STATU_PAGE_URL?? "status-page";					
+const STATUS_PAGE = import.meta.env.VITE_STATUS_PAGE_URL ?? "status-page";

Likely invalid or redundant comment.

Client/src/Components/Inputs/TextInput/Adornments/index.jsx (1)

70-78: ⚠️ Potential issue

Snap back to reality: We need PropTypes! 🍝

 export const ServerStartAdornment = (props) => {
   return (
     <InputAdornment position="start">
       <Stack {...props.dragHandleProps}>
         <ReorderRoundedIcon />
       </Stack>
     </InputAdornment>
   );
 };
+
+ServerStartAdornment.propTypes = {
+  dragHandleProps: PropTypes.object.isRequired,
+};

Likely invalid or redundant comment.

Client/src/Components/Inputs/Search/index.jsx (2)

73-75: ⚠️ Potential issue

Don't let your handlers slip, mom's spaghetti! 🍝

-onChange={(e, newValue) => {
-  handleChange && handleChange(e, newValue);
-}}
+onChange={(e, newValue) => {
+  handleChange?.(e, newValue);
+}}

Likely invalid or redundant comment.

🧰 Tools
🪛 Biome (1.9.4)

[error] 74-74: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


81-81: ⚠️ Potential issue

Don't let your code slip, mom's spaghetti! 🍝

-getOptionLabel={(option) => option[filteredBy]??""}
+getOptionLabel={(option) => option?.[filteredBy] ?? ""}

Likely invalid or redundant comment.

Client/src/Pages/Status/CreateStatus/index.jsx (2)

74-82: Clean up that spaghetti code with safer object handling!

The object spread and optional chaining could be more defensive.

let localData = {
  ...form,
  monitors:
-   form.monitors[0] && Object.keys(form.monitors[0]).includes("_id")
+   Array.isArray(form.monitors) && form.monitors[0]?.hasOwnProperty("_id")
      ? form.monitors.map((m) => m._id)
      : form.monitors,
  theme: mode,
-  logo: { type: form.logo?.type ?? "", size: form.logo?.size ?? "" },
+  logo: form.logo ? { type: form.logo.type || "", size: form.logo.size || "" } : null,
};

42-42: Yo! That URL initialization is making me nervous!

The URL is being initialized with a leading slash which might cause double-slash issues when combined with base URLs.

-url: "/"+STATUS_PAGE,
+url: STATUS_PAGE,

Comment on lines +26 to +28
let res = await networkService.getStatusPageByUrl(config);
if(res && res.data)
setInitForm( res.data.data)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add optional chaining to prevent that runtime spaghetti! 🍝

-if(res && res.data)
-  setInitForm( res.data.data)
+if(res?.data?.data)
+  setInitForm(res.data.data)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let res = await networkService.getStatusPageByUrl(config);
if(res && res.data)
setInitForm( res.data.data)
let res = await networkService.getStatusPageByUrl(config);
if(res?.data?.data)
setInitForm(res.data.data)
🧰 Tools
🪛 Biome (1.9.4)

[error] 27-27: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

];
const tabList = ["General settings", "Contents"];
const hasInitForm = initForm && Object.keys(initForm).length > 0;
const STATUS_PAGE = import.meta.env.VITE_STATU_PAGE_URL?? "status-page";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Yo! Fix that typo in the environment variable name before it makes your palms sweaty!

There's a typo in VITE_STATU_PAGE_URL (missing 'S' in STATUS).

-const STATUS_PAGE = import.meta.env.VITE_STATU_PAGE_URL?? "status-page";
+const STATUS_PAGE = import.meta.env.VITE_STATUS_PAGE_URL ?? "status-page";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const STATUS_PAGE = import.meta.env.VITE_STATU_PAGE_URL?? "status-page";
const STATUS_PAGE = import.meta.env.VITE_STATUS_PAGE_URL ?? "status-page";

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
Client/src/Pages/Status/CreateStatus/index.jsx (3)

45-45: Clean up that commented question, it's making me nervous!

Remove or address the TODO comment "which fields matching below?" as it might confuse other developers.


53-66: Yo! Let's make this error handling logic less spaghetti-like!

The nested map with early returns could be simplified using Array.findIndex for better readability and performance.

 const setActiveTabOnErrors = () => {
-  let newIdx = -1;
-  Object.keys(errors).map((id) => {
-    if (newIdx !== -1) return;
-    error_tab_maping.map((val, idx) => {
-      let anyMatch = val.some((vl) => vl == id);
-      if (anyMatch) {
-        newIdx = idx;
-        return;
-      }
-    });
-  });
-  if (newIdx !== -1) setTabIdx(newIdx);
+  const errorKey = Object.keys(errors)[0];
+  if (!errorKey) return;
+  const tabIndex = error_tab_maping.findIndex(
+    (fields) => fields.some((field) => field === errorKey)
+  );
+  if (tabIndex !== -1) setTabIdx(tabIndex);
 };

113-121: Fix that typo in the handler name, it's making me lose my spaghetti!

There's a typo in the handler name and the code formatting could be improved.

-const handelCheckboxChange = (e) => {			
+const handleCheckboxChange = (e) => {
   const { id } = e.target;
-  
   setForm((prev) => ({
     ...prev,
     [id]: !prev[id]
-  })});
+  }));
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 083a134 and b7a70c4.

📒 Files selected for processing (1)
  • Client/src/Pages/Status/CreateStatus/index.jsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (6)
Client/src/Pages/Status/CreateStatus/index.jsx (6)

1-23: Yo dawg, these imports are straight fire! 🔥

The imports are well-organized and the component documentation is on point!


36-36: Yo! Fix that typo in the environment variable name before it makes your palms sweaty!

There's a typo in VITE_STATU_PAGE_URL (missing 'S' in STATUS).

-const STATUS_PAGE = import.meta.env.VITE_STATU_PAGE_URL?? "status-page";
+const STATUS_PAGE = import.meta.env.VITE_STATUS_PAGE_URL ?? "status-page";

74-82: Clean up that spaghetti code with safer object handling!

The object spread and optional chaining could be more defensive.

 let localData = {
   ...form,
   monitors:
-    form.monitors[0] && Object.keys(form.monitors[0]).includes("_id")
+    Array.isArray(form.monitors) && form.monitors[0]?.hasOwnProperty("_id")
       ? form.monitors.map((m) => m._id)
       : form.monitors,
   theme: mode,
-  logo: { type: form.logo?.type ?? "", size: form.logo?.size ?? "" },
+  logo: form.logo ? { type: form.logo.type || "", size: form.logo.size || "" } : null,
 };

94-95: Yo! Fix this status check before it makes your knees weak!

The status check has incorrect syntax that will always evaluate to false.

-if (!res.status === 200) {
+if (res.status !== 200) {

137-221: This rendering code is fresher than mom's spaghetti! 🍝

The component structure is well-organized with consistent theme usage and proper tab navigation implementation.


223-227: Props validation looking clean! 👌

The PropTypes definition and export are properly implemented.

@gorkem-bwl gorkem-bwl linked an issue Jan 28, 2025 that may be closed by this pull request
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
Client/src/Validation/validation.js (2)

172-185: 🛠️ Refactor suggestion

Yo! This validation's making me lose myself in the duplication! 🍝

The logoImageValidation schema duplicates logic from imageValidation. Let's clean this up!

Apply this diff to consolidate the validation logic:

+const MAX_IMAGE_SIZE = 3 * 1024 * 1024; // 3MB
+const VALID_IMAGE_TYPES = ["image/jpeg", "image/png"];
+
+const baseImageValidation = {
+  type: joi.string().valid(...VALID_IMAGE_TYPES).messages({
+    "any.only": "Invalid file format.",
+    "string.empty": "File type required.",
+  }),
+  size: joi.number().max(MAX_IMAGE_SIZE).messages({
+    "number.base": "File size must be a number.",
+    "number.max": "File size must be less than 3MB.",
+    "number.empty": "File size required.",
+  }),
+};
+
-const logoImageValidation = joi.object({
-  type: joi.string().valid("image/jpeg", "image/png")...
-});
+const logoImageValidation = joi.object(baseImageValidation);
+const imageValidation = joi.object(baseImageValidation);

205-206: ⚠️ Potential issue

Vomit on his sweater already: Duplicate showBarcode! 🍝

The showBarcode property is defined twice, which will cause the first definition to be ignored.

 showBarcode: joi.boolean(),
-showBarcode: joi.boolean()
🧰 Tools
🪛 Biome (1.9.4)

[error] 205-205: This property value named showBarcode is later overwritten by an object member with the same name.

Overwritten with this value.

If an object property with the same name is defined multiple times (except when combining a getter with a setter), only the last definition makes it into the object and previous definitions are ignored.
Unsafe fix: Remove this property value named showBarcode

(lint/suspicious/noDuplicateObjectKeys)

🧹 Nitpick comments (3)
Client/src/Components/Inputs/Search/index.jsx (3)

15-15: Yo! Fix that JSDoc typo, or it's gonna haunt you like mom's spaghetti! 🍝

There's a typo in the JSDoc parameter documentation.

-  * @param {Function} Prop.onBlur - Function to call when the input is blured
+  * @param {Function} props.onBlur - Function to call when the input loses focus

73-75: Don't let your code slip, arms are heavy! 💪

Use optional chaining for safer function calls.

-			onChange={(e, newValue) => {
-				handleChange && handleChange(e, newValue);
-			}}
+			onChange={(e, newValue) => {
+				handleChange?.(e, newValue);
+			}}
🧰 Tools
🪛 Biome (1.9.4)

[error] 74-74: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


221-223: There's vomit on his sweater already! Add those default props! 🧥

Consider adding default values for the new props to make the component more robust.

Add these default props after the PropTypes definition:

Search.defaultProps = {
  ...Search.defaultProps,
  startAdornment: undefined,
  endAdornment: undefined,
  onBlur: undefined
};
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b7a70c4 and 12b14e1.

⛔ Files ignored due to path filters (1)
  • Client/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (6)
  • Client/package.json (2 hunks)
  • Client/src/Components/Inputs/Search/index.jsx (4 hunks)
  • Client/src/Pages/Maintenance/CreateMaintenance/index.jsx (1 hunks)
  • Client/src/Routes/index.jsx (2 hunks)
  • Client/src/Utils/NetworkService.js (1 hunks)
  • Client/src/Validation/validation.js (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • Client/package.json
  • Client/src/Pages/Maintenance/CreateMaintenance/index.jsx
  • Client/src/Utils/NetworkService.js
  • Client/src/Routes/index.jsx
🧰 Additional context used
🪛 Biome (1.9.4)
Client/src/Validation/validation.js

[error] 205-205: This property value named showBarcode is later overwritten by an object member with the same name.

Overwritten with this value.

If an object property with the same name is defined multiple times (except when combining a getter with a setter), only the last definition makes it into the object and previous definitions are ignored.
Unsafe fix: Remove this property value named showBarcode

(lint/suspicious/noDuplicateObjectKeys)

Client/src/Components/Inputs/Search/index.jsx

[error] 74-74: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (4)
Client/src/Components/Inputs/Search/index.jsx (2)

81-81: Don't let your code slip, mom's spaghetti! 🍝

Improve null checking in getOptionLabel:

-getOptionLabel={(option) => option[filteredBy]??""}
+getOptionLabel={(option) => option?.[filteredBy] ?? ""}

96-102: Knees weak! Watch out for those adornment collisions! 🎯

The current implementation allows both isAdorned and startAdornment props to set the startAdornment, which could lead to unexpected behaviour.

Consider prioritizing one over the other or combining them:

-								...(isAdorned && { startAdornment: <SearchAdornment /> }),
-								...(startAdornment && { startAdornment: startAdornment }),
+								startAdornment: startAdornment || (isAdorned ? <SearchAdornment /> : undefined),
Client/src/Validation/validation.js (2)

323-324: LGTM! The exports are clean like mom's spaghetti! 🍝

The new schemas are properly exported, maintaining consistency with the existing pattern.


187-204: 🛠️ Refactor suggestion

Snap back to reality, oh there goes validation! 🎵

The schema's looking clean overall, but there's opportunity in every line:

+const VALID_THEMES = ['light', 'dark', 'system'];
+const COLOR_REGEX = /^(#[0-9A-Fa-f]{6}|rgb\(\d{1,3},\s*\d{1,3},\s*\d{1,3}\))$/;

 const publicPageSettingsValidation = joi.object({
   publish: joi.bool(),
   companyName: joi.string().trim()
     .messages({ "string.empty": "Company name is required." }),
-  url: joi.string().trim()
+  url: joi.string().trim()
+    .custom((value, helpers) => {
+      // Reuse the URL regex from monitorValidation
+      const urlRegex = /^(?:(?:https?|ftp):\/\/)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62})?[a-z0-9\u00a1-\uffff]\.)+(?:[a-z\u00a1-\uffff]{2,}\.?))?(?::\d{2,5})?(?:[/?#]\S*)?$/i;
+      return urlRegex.test(value) ? value : helpers.error('string.invalidUrl');
+    })
     .messages({ 
       "string.empty": "URL is required.",
+      "string.invalidUrl": "Please enter a valid URL"
     }),
   timezone: joi.string().trim()
     .messages({ "string.empty": "Timezone is required." }),
-  color: joi.string().trim()
+  color: joi.string().trim()
+    .pattern(COLOR_REGEX)
     .messages({ 
       "string.empty": "Color is required.",
+      "string.pattern.base": "Color must be a valid hex (#RRGGBB) or RGB value"
     }),
-  theme: joi.string(),
+  theme: joi.string().valid(...VALID_THEMES)
+    .messages({
+      "any.only": `Theme must be one of: ${VALID_THEMES.join(', ')}`
+    }),

Likely invalid or redundant comment.

@jennifer-gan jennifer-gan mentioned this pull request Jan 31, 2025
9 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Status page fixes
1 participant