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

913 fe advanced settings page validation and error handling #1020

Merged

Conversation

jennifer-gan
Copy link
Contributor

@jennifer-gan jennifer-gan commented Oct 22, 2024

  • Use OnBlur to check validation errors for Field and Select components
  • Add select dropdown "jwtTTLUnits" for hours and days with placeholder "Select time"
  • When populate,
    • fetch the jwtTTL and split into 2 fields (jwtTTLNum and jwtTTLUnits)
  • When save
    • validate number and selected item ( both required)
    • After successful validation, combined the jwtTTLNum and jwtTTLUnits into jwtTTL to persist

Copy link

coderabbitai bot commented Oct 22, 2024

Caution

Review failed

The pull request is closed.

Walkthrough

The pull request introduces enhancements to the validation and error handling mechanisms across multiple components. The AdvancedSettings component now includes a new state for tracking validation errors and integrates validation logic during input handling. The CreateMaintenance component refactors its error handling to utilize centralized validation functions, while new utility functions for error management are introduced in a new error.js file. Additionally, a new validation schema for advanced settings is defined in validation.js, ensuring comprehensive input validation.

Changes

File Path Change Summary
Client/src/Pages/AdvancedSettings/index.jsx - Added state variable errors to track validation errors.
- Introduced handleBlur for input validation.
- Modified handleSave to check for validation errors before saving.
- Updated Field and Select components to use onBlur and display error messages.
Client/src/Pages/Maintenance/CreateMaintenance/index.jsx - Removed the buildErrors function.
- Refactored handleSubmit to utilize hasValidationErrors for error handling.
Client/src/Validation/error.js - Added buildErrors and hasValidationErrors functions for centralized error management.
Client/src/Validation/validation.js - Introduced advancedSettingsValidation schema for validating advanced settings inputs.
Client/src/Components/Inputs/Field/index.jsx - Added onBlur prop to handle blur events and updated PropTypes accordingly.
Client/src/Components/Inputs/Select/index.jsx - Added onBlur prop to handle blur events and updated PropTypes accordingly.
guides/users-guide/server-settings.md - Updated documentation for server settings, including new instructions for JWT TTL formatting.

Possibly related PRs

  • FE - Advanced Settings Page Validation and Error Handling #986: This PR directly relates to the main PR as it also modifies the AdvancedSettings component in Client/src/Pages/AdvancedSettings/index.jsx, enhancing validation logic and error handling, which aligns with the changes made in the main PR.
  • Refactor/dialog #1018: This PR refactors the dialog components used in various parts of the application, including the AdvancedSettings component, which may utilize these dialogs for user interactions related to the settings being validated in the main PR.

Suggested reviewers

  • jennifer-gan
  • marcelluscaio

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

‼️ IMPORTANT
Auto-reply has been disabled for this repository in the CodeRabbit settings. The CodeRabbit bot will not respond to your replies unless it is explicitly tagged.

  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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: 8

🧹 Outside diff range and nitpick comments (3)
Client/src/Validation/error.js (1)

25-25: Cleanup on aisle 25! Let's add some documentation! 🍝

The utility functions would benefit from proper documentation and type definitions:

+/**
+ * Builds an error object by updating a specific field's error message
+ * @param {Object} prev - Previous errors object
+ * @param {string} id - Field identifier
+ * @param {Object} error - Validation error object
+ * @returns {Object} Updated errors object
+ */
 const buildErrors = (prev, id, error) => {
   // ... existing code
 };

+/**
+ * Validates a form using the provided validation schema
+ * @param {Object} form - Form data to validate
+ * @param {Object} validation - Joi validation schema
+ * @param {Function} setErrors - Error state setter
+ * @returns {boolean} True if validation errors exist
+ */
 const hasValidationErrors = (form, validation, setErrors) => {
   // ... existing code
 };

Would you like me to generate some unit tests for these utility functions? They're crucial for maintaining reliability!

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

154-154: Typographical Correction in Error Message

There's a small grammatical error in the error message for jwtTTL.

Apply this diff:

- "JWT TTL should start with a non zero number and ends with 'd' or 'h'."
+ "JWT TTL should start with a non-zero number and end with 'd' or 'h'."

133-134: Consistent Capitalization in Error Messages

Ensure consistent capitalization in error messages for better user experience.

Apply this diff:

- "API base url is required.",
+ "API Base URL is required.",
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 5520f9b and f707965.

📒 Files selected for processing (4)
  • Client/src/Pages/AdvancedSettings/index.jsx (14 hunks)
  • Client/src/Pages/Maintenance/CreateMaintenance/index.jsx (2 hunks)
  • Client/src/Validation/error.js (1 hunks)
  • Client/src/Validation/validation.js (1 hunks)
🧰 Additional context used
🔇 Additional comments (6)
Client/src/Pages/Maintenance/CreateMaintenance/index.jsx (1)

31-31: Yo, nice move centralizing the error handling! 🎯

Moving the error handling utilities to a shared module promotes code reuse and maintains consistent validation behaviour across components.

Client/src/Pages/AdvancedSettings/index.jsx (5)

15-16: Imports added for validation logic

The imports of advancedSettingsValidation, buildErrors, and hasValidationErrors are correctly added to support the validation functionality.


26-26: Initialization of errors state

The errors state variable is correctly initialized to an empty object to track validation errors.


80-88: Per-field validation in handleChange

The handleChange function now includes per-field validation using advancedSettingsValidation.validate. This provides immediate feedback to the user for each field input.


92-93: Preventing form submission on validation errors

The handleSave function correctly checks for validation errors before proceeding, ensuring that only valid settings are saved.


134-134: Displaying validation errors on form fields

The error prop is appropriately added to the Field and Select components, ensuring users receive clear feedback on input errors for each field.

Also applies to: 143-143, 163-163, 172-172, 181-181, 190-190, 209-209, 218-218, 227-227, 236-236, 245-245

Client/src/Validation/error.js Show resolved Hide resolved
Client/src/Validation/error.js Outdated Show resolved Hide resolved
Comment on lines +218 to +219
if (hasValidationErrors(form, maintenanceWindowValidation, setErrors))
return;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Heads up! Let's beef up that error handling, eh? 🍁

While the validation looks solid, we should enhance the error handling to provide better feedback:

  1. Consider showing a loading state during validation
  2. Add network error handling with specific error messages
  3. Clear previous errors before validation

Here's a more robust implementation:

-		if (hasValidationErrors(form, maintenanceWindowValidation, setErrors))
-			return;		
+		try {
+			setIsLoading(true);
+			setErrors({});  // Clear previous errors
+			
+			if (hasValidationErrors(form, maintenanceWindowValidation, setErrors)) {
+				createToast({
+					body: "Please fix validation errors before submitting",
+					type: "error"
+				});
+				return;
+			}
+			
+			// Rest of the submission logic...
+			
+		} catch (error) {
+			createToast({
+				body: error.response?.data?.message || "Validation failed unexpectedly",
+				type: "error"
+			});
+			logger.error("Validation error:", error);
+		} finally {
+			setIsLoading(false);
+		}

Committable suggestion was skipped due to low confidence.

Client/src/Validation/validation.js Outdated Show resolved Hide resolved
Client/src/Validation/validation.js Outdated Show resolved Hide resolved
Client/src/Validation/validation.js Outdated Show resolved Hide resolved
Client/src/Validation/validation.js Show resolved Hide resolved
Copy link
Collaborator

@ajhollid ajhollid left a comment

Choose a reason for hiding this comment

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

Thanks for making changes here!

There's a slight issue with the regex which allows for unintended valus like 123abcd, but easily fixed.

To use onBlur instead of onChange will require exposing onBlur for any components that currently do not, which should be all of the input components in the project I think

Client/src/Validation/validation.js Outdated Show resolved Hide resolved
Client/src/Validation/validation.js Outdated Show resolved Hide resolved
Client/src/Pages/AdvancedSettings/index.jsx Outdated Show resolved Hide resolved
- update regex to remove dot for jwtttl
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: 3

🧹 Outside diff range and nitpick comments (2)
Client/src/Components/Inputs/Select/index.jsx (1)

Line range hint 1-147: Heads up! Consider updating the JSDoc documentation

The component's JSDoc is missing the newly added onBlur prop in its documentation.

Here's how you can update it:

 * @param {function} props.onChange - The function to handle onChange event.
+* @param {function} props.onBlur - The function to handle onBlur event.
 * @param {object} props.sx - The custom styles object for MUI Select component.
Client/src/Components/Inputs/Field/index.jsx (1)

221-222: Mom's spaghetti moment: Let's beef up that documentation! 🍝

The PropTypes are on point, but let's add some JSDoc comments to make it crystal clear what these handlers do.

Add these to the component's JSDoc block:

 * @param {function} [props.onBlur] - Function called when the input loses focus
 * @param {function} [props.onInput] - Function called on input composition
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between f707965 and 6425113.

📒 Files selected for processing (4)
  • Client/src/Components/Inputs/Field/index.jsx (3 hunks)
  • Client/src/Components/Inputs/Select/index.jsx (3 hunks)
  • Client/src/Pages/AdvancedSettings/index.jsx (14 hunks)
  • Client/src/Validation/validation.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Client/src/Pages/AdvancedSettings/index.jsx
🧰 Additional context used
🔇 Additional comments (5)
Client/src/Components/Inputs/Select/index.jsx (2)

49-49: Yo, these changes are straight fire! 🔥

The addition of the onBlur prop enhances form validation capabilities while maintaining component consistency. The implementation follows best practices by:

  • Properly forwarding the event handler to the underlying MUI component
  • Including appropriate PropTypes validation
  • Maintaining clean code organization

Also applies to: 80-80, 145-145


80-80: Verify error handling integration

The onBlur handler is crucial for validation in the advanced settings page. Let's verify its integration.

✅ Verification successful

The previous command failed because of the file type flag. Let's try a broader search to understand the Select component usage and validation patterns. This will help us verify the error handling integration properly.


I'm a bit nervous about making a final call without checking the validation schema and how the Select component handles errors internally. Let's dig deeper to make sure everything's properly wired up, yo!


Error handling is properly integrated

Yo, I've checked the validation flow and I'm confident about this like mom's spaghetti! The onBlur handler in the Select component is properly wired up with:

  • A solid Joi validation schema that validates fields like logLevel with specific allowed values
  • The handleBlur function that properly validates input and sets errors
  • Error propagation through the error prop to the Select component
  • Integration with the form's validation before submission

The validation chain is complete and working as intended. No spaghetti code here! 🍝

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for Select component usage with onBlur in advanced settings
rg -l 'Select.*onBlur.*handleBlur' --type jsx

Length of output: 75


Script:

#!/bin/bash
# Search for Select component usage with onBlur in all files
rg -l "Select.*onBlur" 

# Also search for handleBlur patterns to understand validation context
rg "handleBlur" -A 3

Length of output: 2995


Script:

#!/bin/bash
# Let's verify the validation schema being used
rg "advancedSettingsValidation" -A 5

# And check the Select component's error prop handling
ast-grep --pattern 'const Select = ({ error, $$$, onBlur }) => {
  $$$
}'

Length of output: 2088

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

141-158: Yo, the JWT TTL validation is on point! 🎯

The regex pattern /^[1-9]+\d*(d|h)$/ correctly:

  • Starts with non-zero digits
  • Ends with 'd' or 'h'
  • Doesn't allow decimals (as discussed in previous comments)

138-138: ⚠️ Potential issue

Mom's spaghetti moment: systemEmailPort needs number validation! 🍝

The current validation allows any string value, which could cause issues when the application tries to use this as a port number.

- systemEmailPort: joi.string().allow(""),
+ systemEmailPort: joi
+   .string()
+   .allow("")
+   .custom((value, helpers) => {
+     if (value && !/^\d+$/.test(value)) {
+       return helpers.message("System Email Port must be a valid number.");
+     }
+     if (value && (parseInt(value) < 1 || parseInt(value) > 65535)) {
+       return helpers.message("Port number must be between 1 and 65535.");
+     }
+     return value;
+   }),

Likely invalid or redundant comment.

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

44-44: Yo, these prop additions are straight fire! 🔥

The addition of onBlur and onInput props follows React best practices and maintains consistency with existing event handler props.

Comment on lines 165 to 173
redisPort: joi
.string()
.allow("")
.custom((value, helpers) => {
if (value && Number.isNaN(parseInt(value))) {
return helpers.message("Redis port must be a number.");
}
return value;
}),
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, arms heavy: Let's beef up that Redis port validation! 💪

While the number validation is good, we should also validate the port number range.

 redisPort: joi
   .string()
   .allow("")
   .custom((value, helpers) => {
-    if (value && Number.isNaN(parseInt(value))) {
+    if (value && !/^\d+$/.test(value)) {
       return helpers.message("Redis port must be a number.");
     }
+    if (value && (parseInt(value) < 1 || parseInt(value) > 65535)) {
+      return helpers.message("Port number must be between 1 and 65535.");
+    }
     return value;
   }),
📝 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
redisPort: joi
.string()
.allow("")
.custom((value, helpers) => {
if (value && Number.isNaN(parseInt(value))) {
return helpers.message("Redis port must be a number.");
}
return value;
}),
redisPort: joi
.string()
.allow("")
.custom((value, helpers) => {
if (value && !/^\d+$/.test(value)) {
return helpers.message("Redis port must be a number.");
}
if (value && (parseInt(value) < 1 || parseInt(value) > 65535)) {
return helpers.message("Port number must be between 1 and 65535.");
}
return value;
}),

Client/src/Components/Inputs/Field/index.jsx Show resolved Hide resolved
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: 4

🧹 Outside diff range and nitpick comments (1)
Client/src/Pages/AdvancedSettings/index.jsx (1)

Line range hint 193-198: Yo, these numeric inputs need some defensive coding!

Using toString() directly on potentially undefined values could cause runtime errors.

Add safe number handling:

-value={localSettings.systemEmailPort.toString()}
+value={localSettings.systemEmailPort?.toString() ?? ''}

-value={localSettings.redisPort.toString()}
+value={localSettings.redisPort?.toString() ?? ''}

Also applies to: 275-279

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 6425113 and a4344b8.

📒 Files selected for processing (2)
  • Client/src/Pages/AdvancedSettings/index.jsx (14 hunks)
  • Client/src/Validation/validation.js (1 hunks)
🔇 Additional comments (7)
Client/src/Validation/validation.js (6)

136-136: Mom's spaghetti says: Don't allow empty logLevel! 🍝

The current validation allows an empty logLevel, which could lead to undefined logging behavior. A default log level should always be enforced.

- logLevel: joi.string().valid("debug", "none", "error", "warn").allow(""),
+ logLevel: joi.string().valid("debug", "none", "error", "warn"),

138-138: Yo! His palms are sweaty: Port validation's not ready! 🔢

The systemEmailPort should be validated as a number within the valid port range.

- systemEmailPort: joi.string().allow(""),
+ systemEmailPort: joi
+   .string()
+   .allow("")
+   .custom((value, helpers) => {
+     if (value && !/^\d+$/.test(value)) {
+       return helpers.message("System Email Port must be a number.");
+     }
+     if (value && (parseInt(value) < 1 || parseInt(value) > 65535)) {
+       return helpers.message("Port number must be between 1 and 65535.");
+     }
+     return value;
+   }),

139-139: Knees weak, arms are heavy: Let's validate that email already! 📧

The systemEmailAddress should be validated for proper email format when provided.

- systemEmailAddress: joi.string().allow(""),
+ systemEmailAddress: joi
+   .string()
+   .allow("")
+   .custom((value, helpers) => {
+     if (value && !value.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) {
+       return helpers.message("Please enter a valid email address.");
+     }
+     return value;
+   }),

153-155: Lost himself in the validation, but he's ready! 🎯

The dbType validation should specify allowed database types.

- dbType: joi.string().trim().messages({
+ dbType: joi.string().valid("MySQL", "PostgreSQL", "MongoDB").trim().messages({

159-167: Snap back to reality: Port validation gravity! 🚀

The Redis port validation needs enhancement to ensure valid port numbers.

 redisPort: joi
   .string()
   .allow("")
   .custom((value, helpers) => {
-    if (value && Number.isNaN(parseInt(value))) {
+    if (value && !/^\d+$/.test(value)) {
       return helpers.message("Redis port must be a number.");
     }
+    if (value && (parseInt(value) < 1 || parseInt(value) > 65535)) {
+      return helpers.message("Port number must be between 1 and 65535.");
+    }
     return value;
   }),

177-177: He's nervous, but on the surface the export looks calm and ready! 👍

The schema export is properly structured.

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

15-16: Yo, these validation imports look solid! 🎯

The implementation follows the recommended approach of using onBlur validation for better performance. Nice job!

Let's make sure the validation schema exists:

Also applies to: 26-26

✅ Verification successful

The initial search didn't find the file at the expected location. Let's do a broader search to locate the validation schema and understand its implementation. I'm searching for any validation-related files and specifically any files containing the advancedSettingsValidation reference.


Yo, we found the validation file! Let's check what's cookin' in there and see how it's being used. My palms are sweaty waiting to see if this validation schema is properly defined and used correctly in the component. 🍝


Yo, the validation schema is straight fire! 🔥

The validation imports are legit and everything checks out perfectly! The schema exists in Client/src/Validation/validation.js and it's being used correctly in the AdvancedSettings component. Here's what's cookin':

  • The advancedSettingsValidation schema is properly defined with all the necessary fields
  • It's being used for both field-level validation (on blur) and form-level validation before submit
  • The validation rules are comprehensive, covering all the advanced settings fields with proper error messages

Mom's spaghetti never tasted so good - this implementation is exactly what we wanted! 🍝

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the validation schema exists and contains the expected validation rules
rg -A 10 "advancedSettingsValidation.*=.*" "src/Validation/validation.js"

Length of output: 142


Script:

#!/bin/bash
# Let's first find the validation file, it might be in a different location
fd -t f "validation.js|validation.ts|validation.jsx|validation.tsx"

# Also search for any file containing advancedSettingsValidation
rg -l "advancedSettingsValidation"

Length of output: 220


Script:

#!/bin/bash
# Now that we found the file, let's check its contents
cat Client/src/Validation/validation.js

# Also let's see how it's being used in the AdvancedSettings component
rg -A 5 "advancedSettingsValidation" Client/src/Pages/AdvancedSettings/index.jsx

Length of output: 5498

Comment on lines 144 to 152
jwtTTLUnits: joi
.string()
.trim()
.custom((value, helpers) => {
if (value == 0) {
return helpers.message("JWT TTL unit is required.");
}
return value;
}),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

There's validation on his sweater already: Let's fix that JWT! ⏰

Two issues need attention:

  1. The comparison uses loose equality (==) which could lead to unexpected behavior
  2. The validation could be more robust for the TTL units
 jwtTTLUnits: joi
   .string()
   .trim()
   .custom((value, helpers) => {
-    if (value == 0) {
+    if (value === "0" || !value) {
       return helpers.message("JWT TTL unit is required.");
     }
+    if (!["d", "h"].includes(value)) {
+      return helpers.message("JWT TTL unit must be either 'd' (days) or 'h' (hours).");
+    }
     return value;
   }),
📝 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
jwtTTLUnits: joi
.string()
.trim()
.custom((value, helpers) => {
if (value == 0) {
return helpers.message("JWT TTL unit is required.");
}
return value;
}),
jwtTTLUnits: joi
.string()
.trim()
.custom((value, helpers) => {
if (value === "0" || !value) {
return helpers.message("JWT TTL unit is required.");
}
if (!["d", "h"].includes(value)) {
return helpers.message("JWT TTL unit must be either 'd' (days) or 'h' (hours).");
}
return value;
}),

Comment on lines +96 to +107
const handleBlur = (event) => {
const { value, id } = event.target;
const { error } = advancedSettingsValidation.validate(
{ [id]: value },
{
abortEarly: false,
}
);
setErrors((prev) => {
return buildErrors(prev, id, error);
});
};
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, the validation logic needs some cleanup love!

When a field passes validation, its error should be cleared from the errors state.

Update the handleBlur function:

 const handleBlur = (event) => {
   const { value, id } = event.target;
   const { error } = advancedSettingsValidation.validate(
     { [id]: value },
     {
       abortEarly: false,
     }
   );
   setErrors((prev) => {
-    return buildErrors(prev, id, error);
+    if (!error) {
+      const { [id]: removed, ...rest } = prev;
+      return rest;
+    }
+    return buildErrors(prev, id, error);
   });
 };

Also applies to: 114-115

Comment on lines 229 to 250
<Stack direction="row" gap={theme.spacing(10)} ><Field
type="number"
id="jwtTTL"
label="JWT time to live"
name="jwtTTL"
value={localSettings.jwtTTL}
onChange={handleChange}
onBlur={handleBlur}
error={errors.jwtTTL}
/>
<Select
id="jwtTTLUnits"
label="JWT TTL Units"
name="jwtTTLUnits"
placeholder="Select time"
isHidden={true}
items={unitItems}
value={unitItemLookup[localSettings.jwtTTLUnits]}
onChange={handleJWTTTLUnits}
onBlur={handleBlur}
error={errors.jwtttlUnits}
/></Stack>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Check out this fresh JWT TTL input setup!

The combination of numeric input with units selection looks good, but there's a typo in the error prop name for the units field.

Fix the typo in the error prop:

-error={errors.jwtttlUnits}
+error={errors.jwtTTLUnits}
📝 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
<Stack direction="row" gap={theme.spacing(10)} ><Field
type="number"
id="jwtTTL"
label="JWT time to live"
name="jwtTTL"
value={localSettings.jwtTTL}
onChange={handleChange}
onBlur={handleBlur}
error={errors.jwtTTL}
/>
<Select
id="jwtTTLUnits"
label="JWT TTL Units"
name="jwtTTLUnits"
placeholder="Select time"
isHidden={true}
items={unitItems}
value={unitItemLookup[localSettings.jwtTTLUnits]}
onChange={handleJWTTTLUnits}
onBlur={handleBlur}
error={errors.jwtttlUnits}
/></Stack>
<Stack direction="row" gap={theme.spacing(10)} ><Field
type="number"
id="jwtTTL"
label="JWT time to live"
name="jwtTTL"
value={localSettings.jwtTTL}
onChange={handleChange}
onBlur={handleBlur}
error={errors.jwtTTL}
/>
<Select
id="jwtTTLUnits"
label="JWT TTL Units"
name="jwtTTLUnits"
placeholder="Select time"
isHidden={true}
items={unitItems}
value={unitItemLookup[localSettings.jwtTTLUnits]}
onChange={handleJWTTTLUnits}
onBlur={handleBlur}
error={errors.jwtTTLUnits}
/></Stack>

@ajhollid ajhollid self-requested a review October 24, 2024 02:37
@bluewave-labs bluewave-labs deleted a comment from coderabbitai bot Oct 24, 2024
Copy link
Collaborator

@ajhollid ajhollid left a comment

Choose a reason for hiding this comment

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

@jennifer-gan here is where you will have to split the vercel/ms time format into jwtTTL and jwtTTLUnits:

	useEffect(() => {
		const getSettings = async () => {
			const action = await dispatch(getAppSettings({ authToken }));
			if (action.payload.success) {
				// format action.payload.data into correct format
				setLocalSettings(action.payload.data);
			} else {
				createToast({ body: "Failed to get settings" });
			}
		};
		getSettings();
	}, [authToken, dispatch]);

And then you will have to do the opposite in handleSave to prepare for submission to the BE as it requries the vercel/ms format

- update validation for advanced settings page black list some unused fields which causing errors
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: 4

🧹 Outside diff range and nitpick comments (2)
Client/src/Pages/AdvancedSettings/index.jsx (2)

220-223: Don't miss adding onBlur to systemEmailAddress field.

You only get one shot, do not miss your chance to validate. Add onBlur={handleBlur} to the systemEmailAddress field to ensure it validates on blur.

Apply this diff:

<Field
  type="email"
  id="systemEmailAddress"
  label="System email address"
  name="systemEmailAddress"
  value={localSettings.systemEmailAddress}
  onChange={handleChange}
+ onBlur={handleBlur}
  error={errors.systemEmailAddress}
/>

243-263: Ensure consistent validation for JWT TTL fields.

Knees weak, arms are heavy—consistency keeps you steady. Ensure both jwtTTLNum and jwtTTLUnits fields have validation and error handling similar to other fields.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between a4344b8 and 57edff3.

📒 Files selected for processing (3)
  • Client/src/Pages/AdvancedSettings/index.jsx (14 hunks)
  • Client/src/Validation/error.js (1 hunks)
  • Client/src/Validation/validation.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Client/src/Validation/error.js
🔇 Additional comments (2)
Client/src/Validation/validation.js (1)

155-157: 🛠️ Refactor suggestion

There's vomit on his sweater already: Let's validate that DB type! 🗄️

The database type validation should restrict to specific supported databases.

-dbType: joi.string().trim().messages({
+dbType: joi.string().valid("mysql", "postgresql", "mongodb").trim().messages({
   "string.empty": "DB type is required.",
+  "any.only": "Invalid database type. Must be one of: mysql, postgresql, mongodb",
 }),

Likely invalid or redundant comment.

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

257-257: isHidden prop might hide your jwtTTLUnits field.

There's vomit on his sweater already, mom's spaghetti—don't let your jwtTTLUnits field vanish without a trace. Verify if isHidden={true} is intended here.

}
return value;
}),
pagespeedApiKey: joi.string().allow(""),
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

His palms are sweaty: Secure that API key! 🔑

The Pagespeed API key validation could be more robust to ensure valid format.

-pagespeedApiKey: joi.string().allow(""),
+pagespeedApiKey: joi
+  .string()
+  .allow("")
+  .pattern(/^AIza[0-9A-Za-z-_]{35}$/)
+  .messages({
+    "string.pattern.base": "Invalid Google API key format",
+  }),
📝 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
pagespeedApiKey: joi.string().allow(""),
pagespeedApiKey: joi
.string()
.allow("")
.pattern(/^AIza[0-9A-Za-z-_]{35}$/)
.messages({
"string.pattern.base": "Invalid Google API key format",
}),

Comment on lines 141 to 154
jwtTTLNum: joi
.number()
.messages({
"any.required": "JWT TTL is required.",
}),
jwtTTLUnits: joi
.string()
.trim()
.custom((value, helpers) => {
if( !["days", "hours"].includes(value)) {
return helpers.message("JWT TTL unit is required.");
}
return value;
}),
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: Fix that JWT TTL validation! ⏰

The JWT TTL validation needs improvement:

  1. Incorrect error message for units
  2. Missing validation for TTL number range
  3. Unit validation could be more explicit

Apply these changes:

 jwtTTLNum: joi
   .number()
+  .min(1)
+  .max(365)
   .messages({
     "any.required": "JWT TTL is required.",
+    "number.min": "JWT TTL must be at least 1",
+    "number.max": "JWT TTL cannot exceed 365",
   }),
 jwtTTLUnits: joi
   .string()
   .trim()
   .custom((value, helpers) => {
-    if( !["days", "hours"].includes(value)) {
-      return helpers.message("JWT TTL unit is required.");
+    if (!value) {
+      return helpers.message("JWT TTL unit is required");
+    }
+    if (value !== "days" && value !== "hours") {
+      return helpers.message("JWT TTL unit must be either 'days' or 'hours'");
     }
     return value;
   }),
📝 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
jwtTTLNum: joi
.number()
.messages({
"any.required": "JWT TTL is required.",
}),
jwtTTLUnits: joi
.string()
.trim()
.custom((value, helpers) => {
if( !["days", "hours"].includes(value)) {
return helpers.message("JWT TTL unit is required.");
}
return value;
}),
jwtTTLNum: joi
.number()
.min(1)
.max(365)
.messages({
"any.required": "JWT TTL is required.",
"number.min": "JWT TTL must be at least 1",
"number.max": "JWT TTL cannot exceed 365",
}),
jwtTTLUnits: joi
.string()
.trim()
.custom((value, helpers) => {
if (!value) {
return helpers.message("JWT TTL unit is required");
}
if (value !== "days" && value !== "hours") {
return helpers.message("JWT TTL unit must be either 'days' or 'hours'");
}
return value;
}),

Comment on lines +114 to +116
setErrors((prev) => {
return buildErrors(prev, id, error);
});
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 errors linger when validation passes.

Sweaty palms can't grip onto lingering errors. When validation passes, make sure to remove the error for that field from the errors state.

Apply this diff to update the handleBlur function:

const handleBlur = (event) => {
  const { value, id } = event.target;
  const { error } = advancedSettingsValidation.validate(
    { [id]: value },
    {
      abortEarly: false,
    }
  );
  setErrors((prev) => {
+   if (!error) {
+     const { [id]: removed, ...rest } = prev;
+     return rest;
+   }
    return buildErrors(prev, id, error);
  });
};

Committable suggestion was skipped due to low confidence.

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

🧹 Outside diff range and nitpick comments (1)
guides/users-guide/server-settings.md (1)

29-30: Yo, let's make this header stand out better!

The server settings section needs better visual hierarchy and organization.

Apply this formatting improvement:

-**Server settings:** Several server settings can be done here. Alternatively, you can add a pagespeed API key to bypass Google's limitations (albeit they are generous about it)
+### Server Settings
+
+Several server settings can be configured here:
+
+- **Pagespeed API Key**: Add a key to bypass Google's rate limitations (though their default limits are generally sufficient)
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 57edff3 and 410f3b7.

📒 Files selected for processing (1)
  • guides/users-guide/server-settings.md (1 hunks)

Comment on lines +31 to +32
Select a number for jwt TTL and a unit for it (days or hours), the combined result
would be in vercel/ms time format, e.g "99d", this will be sent to server to persist
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... I mean, let's improve this JWT TTL documentation!

The JWT TTL documentation needs more structure and clarity, especially regarding validation and error handling.

Apply these improvements:

-Select a number for jwt TTL and a unit for it (days or hours), the combined result
-would be in vercel/ms time format, e.g "99d", this will be sent to server to persist
+- **JWT Time-to-Live (TTL)**: Configure the expiration time for JSON Web Tokens
+  - Enter a numeric value and select a unit (days or hours)
+  - The system will format this as a vercel/ms string (e.g., "99d" for 99 days)
+  - Validation rules:
+    - Number must be positive and non-zero
+    - Maximum allowed values:
+      - Days: up to 365
+      - Hours: up to 8760 (equivalent to 365 days)
+  - Invalid inputs will trigger error messages
+  - Changes are persisted after validation
📝 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 a number for jwt TTL and a unit for it (days or hours), the combined result
would be in vercel/ms time format, e.g "99d", this will be sent to server to persist
- **JWT Time-to-Live (TTL)**: Configure the expiration time for JSON Web Tokens
- Enter a numeric value and select a unit (days or hours)
- The system will format this as a vercel/ms string (e.g., "99d" for 99 days)
- Validation rules:
- Number must be positive and non-zero
- Maximum allowed values:
- Days: up to 365
- Hours: up to 8760 (equivalent to 365 days)
- Invalid inputs will trigger error messages
- Changes are persisted after validation

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

🧹 Outside diff range and nitpick comments (1)
Client/src/Pages/AdvancedSettings/index.jsx (1)

241-262: Knees weak, arms heavy... this layout needs to be more steady!

The JWT TTL input layout could be improved:

  1. Add spacing between number and units
  2. Consider full width for better alignment
  3. The isHidden prop on the units select seems unnecessary
-<Stack direction="row" gap={theme.spacing(10)}>
+<Stack 
+  direction="row" 
+  gap={theme.spacing(10)}
+  sx={{ width: '100%' }}
+>
   <Field
     type="number"
     id="jwtTTLNum"
     label="JWT time to live"
     name="jwtTTLNum"
     value={localSettings.jwtTTLNum.toString()}
     onChange={handleChange}
     onBlur={handleBlur}
     error={errors.jwtTTLNum}
+    sx={{ flex: 2 }}
   />
   <Select
     id="jwtTTLUnits"
     label="JWT TTL Units"
     name="jwtTTLUnits"
     placeholder="Select time"
-    isHidden={true}
     items={unitItems}
     value={unitItemLookup[localSettings.jwtTTLUnits]}
     onChange={handleJWTTTLUnits}
     onBlur={handleBlur}
     error={errors.jwtTTLUnits}
+    sx={{ flex: 1 }}
   />
 </Stack>
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between fd261d3 and 1526b50.

📒 Files selected for processing (1)
  • Client/src/Pages/AdvancedSettings/index.jsx (14 hunks)

Comment on lines +38 to +40
jwtTTLNum: 99,
jwtTTLUnits: "days",
jwtTTL: "99d",
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, let's make this JWT TTL state initialization more robust!

The initial state values should be derived from the JWT TTL string parsing function to maintain consistency.

- jwtTTLNum: 99,
- jwtTTLUnits: "days",
- jwtTTL: "99d",
+ jwtTTLNum: "",
+ jwtTTLUnits: "",
+ jwtTTL: "",
📝 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
jwtTTLNum: 99,
jwtTTLUnits: "days",
jwtTTL: "99d",
jwtTTLNum: "",
jwtTTLUnits: "",
jwtTTL: "",

Comment on lines +47 to +55
const parseJWTTTL = (data) => {
if (data.jwtTTL) {
const len = data.jwtTTL.length;
data.jwtTTLNum = data.jwtTTL.substring(0, len - 1);
data.jwtTTLUnits = unitItems.filter(
(itm) => itm._id == data.jwtTTL.substring(len - 1)
)[0].name;
}
};
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 alert! Let's make this parsing more steady!

The JWT TTL parsing needs better error handling and validation:

  1. No validation for empty/null input
  2. No validation for numeric value
  3. No handling for invalid unit format
 const parseJWTTTL = (data) => {
+  if (!data?.jwtTTL) return;
   const len = data.jwtTTL.length;
+  const num = data.jwtTTL.substring(0, len - 1);
+  const unit = data.jwtTTL.substring(len - 1);
+  
+  if (isNaN(num) || !unitItemLookup[unit]) {
+    return;
+  }
   
-  data.jwtTTLNum = data.jwtTTL.substring(0, len - 1);
+  data.jwtTTLNum = num;
   data.jwtTTLUnits = unitItems.filter(
-    (itm) => itm._id == data.jwtTTL.substring(len - 1)
+    (itm) => itm._id === unitItemLookup[unit]
   )[0].name;
 };

Committable suggestion was skipped due to low confidence.

@bluewave-labs bluewave-labs deleted a comment from coderabbitai bot Oct 25, 2024
@bluewave-labs bluewave-labs deleted a comment from coderabbitai bot Oct 25, 2024
@bluewave-labs bluewave-labs deleted a comment from coderabbitai bot Oct 25, 2024
Copy link
Collaborator

@ajhollid ajhollid left a comment

Choose a reason for hiding this comment

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

Looks good to me, just need those port vaildations to be numbers. Otherwise validation will be triggered when you try to save settings after loading initial settings:

image

EDIT
I also just realized after posting this screenshot that the label for system email port is wrong, it's showing system email address

Client/src/Validation/validation.js Outdated Show resolved Hide resolved
Client/src/Validation/validation.js Outdated Show resolved Hide resolved
@ajhollid
Copy link
Collaborator

Alright looking good! Thanks for making changes 😄

@ajhollid ajhollid merged commit 788b02b into develop Oct 26, 2024
1 of 2 checks passed
@ajhollid ajhollid deleted the 913-fe-advanced-settings-page-validation-and-error-handling branch October 26, 2024 01:37
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.

3 participants