Skip to content

Commit

Permalink
refactor: change profile and password form schemas
Browse files Browse the repository at this point in the history
  • Loading branch information
brunomous committed Feb 13, 2025
1 parent 018d1f5 commit fd3c9c3
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 80 deletions.
13 changes: 4 additions & 9 deletions packages/frontend/src/pages/Profile/ChangePasswordForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@ import useTheme from "@mui/material/styles/useTheme";
import { type PasswordChangeFormData } from "./types";
import {
passwordChangeFormSchema,
widgets,
advancedProperties,
validationRules,
validateForm,
passwordChangeUiSchema,
customValidate,
} from "./constants";

interface ChangePasswordFormProps {
Expand Down Expand Up @@ -38,18 +36,15 @@ const ChangePasswordForm: FC<ChangePasswordFormProps> = ({
return (
<SchemaForm.Form
schema={passwordChangeFormSchema}
advancedProperties={advancedProperties}
uiSchema={passwordChangeUiSchema}
onSubmit={handleFormSubmit}
widgets={widgets}
noHtml5Validate={true}
showErrorList={false}
customValidate={customValidate}
formData={formData}
onChange={({ formData }) => {
setFormData(formData);
}}
customValidate={(formData, errors) =>
validateForm(formData, errors, validationRules)
}
>
<Box
display="flex"
Expand Down
97 changes: 33 additions & 64 deletions packages/frontend/src/pages/Profile/constants.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
import type { RJSFSchema } from "@rjsf/utils";
import { UiSchema, RJSFSchema, CustomValidator } from "@rjsf/utils";
import { CustomTextFieldWidget } from "@concepta/react-material-ui/dist/styles/CustomWidgets";
import { AdvancedProperty } from "@concepta/react-material-ui/dist/components/SchemaForm/types";
import { FormValidation } from "@rjsf/utils";
import { PasswordChangeFormData } from "./types";
import { CustomPasswordFieldWidget } from "@concepta/react-material-ui/dist/styles/CustomWidgets";
import { validatePasswordScore } from "@concepta/react-material-ui/dist/components/TextField/utils";

type PasswordUpdateForm = {
oldPassword: string;
newPassword: string;
confirmNewPassword: string;
};

export const widgets = {
TextWidget: CustomTextFieldWidget,
};

export const profileFormSchema: RJSFSchema = {
type: "object",
required: ["firstName", "lastName"],
required: ["fullName", "nickname", "email"],
properties: {
firstName: { type: "string", title: "First name", minLength: 3 },
lastName: { type: "string", title: "Last name", minLength: 3 },
fullName: { type: "string", title: "Full Name", minLength: 3 },
nickname: { type: "string", title: "Nickname", minLength: 3 },
email: { type: "string", title: "Email", minLength: 3, readOnly: true },
},
};
Expand All @@ -22,77 +27,41 @@ export const passwordChangeFormSchema: RJSFSchema = {
type: "object",
required: ["oldPassword", "newPassword", "confirmNewPassword"],
properties: {
oldPassword: { type: "string", title: "Old password" },
oldPassword: { type: "string", title: "Current password" },
newPassword: { type: "string", title: "New password" },
confirmNewPassword: { type: "string", title: "Confirm new password" },
},
};

export const advancedProperties: Record<string, AdvancedProperty> = {
export const passwordChangeUiSchema: UiSchema = {
oldPassword: {
type: "password",
"ui:widget": CustomPasswordFieldWidget,
},
newPassword: {
type: "password",
"ui:widget": CustomPasswordFieldWidget,
"ui:passwordStrengthConfig": {
hideStrengthBar: false,
hideRulesText: false,
},
},
confirmNewPassword: {
type: "password",
"ui:widget": CustomPasswordFieldWidget,
},
};

export const validationRules: ValidationRule<PasswordChangeFormData>[] = [
{
field: "oldPassword",
test: (value) => !value,
message: "Required field",
},
{
field: "newPassword",
test: (value) => !value,
message: "Required field",
},
{
field: "confirmNewPassword",
test: (value) => !value,
message: "Required field",
},
{
field: "confirmNewPassword",
test: (value, formData) => value !== formData.newPassword,
message: "Your passwords don't match. Please try again",
},
];

export type ValidationRule<T> = {
field: keyof T;
test: (value: T[keyof T] | undefined | null, formData: T) => boolean;
message: string;
};

export type ValidateFormErrors<T> = {
[K in keyof T]?: boolean;
};

export const validateForm = <T>(
formData: T,
errors: FormValidation<T>,
validationRules: ValidationRule<T>[]
): FormValidation<T> => {
const errorsAdded: ValidateFormErrors<T> = {};

for (const rule of validationRules) {
const { field, test, message } = rule;
const value = formData?.[field];

if (test(value, formData)) {
if (!errorsAdded?.[field]) {
errors?.[field]?.addError(message);
errorsAdded[field] = true;
}
}
export const customValidate: CustomValidator<
PasswordUpdateForm,
RJSFSchema,
Record<string, unknown>
> = (formData, errors) => {
if (formData?.confirmNewPassword !== formData?.newPassword) {
errors?.confirmNewPassword?.addError("Your passwords don't match.");
}
if (!validatePasswordScore(formData?.newPassword ?? "")) {
errors?.newPassword?.addError(
"Your password do not meet the security criteria."
);
}

return errors;
};

export default validateForm;
18 changes: 12 additions & 6 deletions packages/frontend/src/pages/Profile/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ const ProfileScreen: FC = () => {
const [isConfirmationModalOpen, setConfirmationModalOpen] =
useState<boolean>(false);
const [formData, setFormData] = useState({
fullName: "",
nickname: "",
email: "",
firstName: "John",
lastName: "Smith",
});
const [isLoadingSubmit, setLoadingSubmit] = useState(false);

Expand Down Expand Up @@ -58,10 +58,16 @@ const ProfileScreen: FC = () => {
};

useEffect(() => {
if (user && !formData.email) {
setFormData({ ...formData, email: (user as User).email });
if (user) {
const form = user as User;

setFormData({
fullName: form.userProfile?.fullName || "",
nickname: form.userProfile?.nickname || "",
email: form.email || "",
});
}
}, [user, formData]);
}, [user]);

return (
<Box>
Expand Down Expand Up @@ -133,7 +139,7 @@ const ProfileScreen: FC = () => {
<Dialog
open={isPasswordChangeModalOpen}
handleClose={closePasswordChangeModal}
title="Change password"
title="Update password"
>
<ChangePasswordForm
closePasswordChangeModal={closePasswordChangeModal}
Expand Down
25 changes: 24 additions & 1 deletion packages/frontend/src/pages/Profile/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,36 @@ export interface PasswordChangeFormData {
confirmNewPassword: string;
}

export interface User {
export interface UserRole {
id: string;
dateCreated: string;
dateUpdated: string;
dateDeleted: string | null;
version: number;
}

export interface UserProfile {
id: string;
dateCreated: string;
dateUpdated: string;
dateDeleted: string | null;
version: number;
fullName: string;
nickname: string;
emailVerified: boolean;
activityStatus: string;
userId: string;
}

export interface User {
email: string;
username: string;
active: boolean;
id: string;
dateCreated: string;
dateUpdated: string;
dateDeleted: string | null;
version: number;
userRoles: UserRole[];
userProfile: UserProfile;
}

0 comments on commit fd3c9c3

Please sign in to comment.