From 8a9ef87e6c5e198957798efd7e1656db235a7219 Mon Sep 17 00:00:00 2001 From: Akshata Katwal Date: Mon, 27 Jan 2025 14:36:48 +0530 Subject: [PATCH 1/5] Issue feat PS-3578: combine fl and lastname pass it to username...create custom widget for suggestion list of username after onblur(end user complete his username typing on UI) --- src/components/AddLeanerModal.tsx | 42 +++++++++---- src/components/DynamicForm.tsx | 39 +++++++++++- src/components/UsernameWithSuggestions.tsx | 70 ++++++++++++++++++++++ 3 files changed, 135 insertions(+), 16 deletions(-) create mode 100644 src/components/UsernameWithSuggestions.tsx diff --git a/src/components/AddLeanerModal.tsx b/src/components/AddLeanerModal.tsx index 70335590..1d75cfa8 100644 --- a/src/components/AddLeanerModal.tsx +++ b/src/components/AddLeanerModal.tsx @@ -308,20 +308,36 @@ const AddLearnerModal: React.FC = ({ }; - const handleChange = (event: IChangeEvent) => { - // if (!isEditModal) { - // const { firstName, lastName } = event.formData; - - // if (firstName && lastName) { - // event.formData.username = firstName + lastName; - // } else { - // event.formData.username = ""; - // } - // setCustomFormData({ ...event.formData }); - - // } +const handleChange = (event: IChangeEvent) => { + const { formData } = event; + + + if (!isEditModal) { + const { firstName, lastName, username } = formData; + if (firstName && lastName) { + const updatedUsername = event.formData.username + ? username + : firstName && lastName + ? (firstName + lastName).toLowerCase() + : ''; + - }; + const updatedFormData = { + ...formData, + username: updatedUsername, + }; + + setCustomFormData(updatedFormData); + } else { + setCustomFormData({ ...event.formData }); + } + } else { + setCustomFormData({ ...formData }); + } +}; + + + const handleError = (errors: any) => { diff --git a/src/components/DynamicForm.tsx b/src/components/DynamicForm.tsx index 5dfa9ca8..f85eacb2 100644 --- a/src/components/DynamicForm.tsx +++ b/src/components/DynamicForm.tsx @@ -5,11 +5,12 @@ import { Theme as MaterialUITheme } from '@rjsf/mui'; import { RJSFSchema, RegistryFieldsType, WidgetProps } from '@rjsf/utils'; import validator from '@rjsf/validator-ajv8'; import { useTranslation } from 'next-i18next'; -import React, { ReactNode, useEffect } from 'react'; +import React, { ReactNode, useEffect, useState } from 'react'; import CustomRadioWidget from './CustomRadioWidget'; import MultiSelectCheckboxes from './MultiSelectCheckboxes'; import MultiSelectDropdown from './MultiSelectDropdown'; import CustomNumberWidget from './CustomNumberWidget'; +import UsernameWithSuggestions from './UsernameWithSuggestions'; const FormWithMaterialUI = withTheme(MaterialUITheme); @@ -49,8 +50,10 @@ const DynamicForm: React.FC = ({ CustomRadioWidget: CustomRadioWidget, MultiSelectDropdown: MultiSelectDropdown, CustomNumberWidget: CustomNumberWidget, + UsernameWithSuggestions: UsernameWithSuggestions as React.FC> // Ensure correct type }; const { t } = useTranslation(); + const [suggestions, setSuggestions] = useState([]); const submittedButtonStatus = useSubmittedButtonStore( (state: any) => state.submittedButtonStatus @@ -266,13 +269,31 @@ const DynamicForm: React.FC = ({ const sanitizedData = sanitizeFormData(event.formData); onChange({ ...event, formData: sanitizedData }); } + const handleUsernameBlur = async (username: string) => { + if (username) { + try { + + console.log('Username onblur called'); + // setSuggestions(["1234"]) + + } catch (error) { + console.error('Error validating username:', error); + } + } + }; + const handleSuggestionSelect = (suggestion: string) => { + console.log("Selected Suggestion:", suggestion); + setSuggestions([]); + }; return (
= ({ onError={handleError} transformErrors={transformErrors} fields={customFields} + formContext={{ + suggestions, + onSuggestionSelect: handleSuggestionSelect, + }} + onBlur={(field, value) => { + if (field==="username") { + + handleUsernameBlur(value); + } + }} > + + {children}
diff --git a/src/components/UsernameWithSuggestions.tsx b/src/components/UsernameWithSuggestions.tsx new file mode 100644 index 00000000..47042cd1 --- /dev/null +++ b/src/components/UsernameWithSuggestions.tsx @@ -0,0 +1,70 @@ +import { ListItemText, MenuItem, TextField } from "@mui/material"; +import { WidgetProps } from "@rjsf/utils"; +import React from "react"; +interface UsernameWidgetProps { + formContext: any + value: any; + onChange: (value: any) => void; + onBlur: (field: any ,value: any) => void; + + +} +const UsernameWithSuggestions: React.FC = ({ + formContext, + value, + onBlur, + onChange, + ...rest +}) => { + const { suggestions, onSuggestionSelect } = formContext; + + const handleBlur = (event: React.FocusEvent) => { + if (onBlur) { + onBlur(event.target.name, event.target.value); // Forwarding onBlur event to the parent + } + }; + + const handleWheel = (event: any) => { + if (event.target instanceof HTMLInputElement) { + event.target.blur(); + } + }; + + const handleUsernameChange = (event: React.ChangeEvent) => { + onChange(event.target.value); + }; + + return ( +
+ + {suggestions.length > 0 && ( +
+ {suggestions.map((suggestion: any, index: number) => ( + onSuggestionSelect(suggestion)} + > + + + ))} +
+ )} +
+ ); +}; + +export default UsernameWithSuggestions; + + + + From 21672f06ef031edbd5c397bb2156cd81c76e6376 Mon Sep 17 00:00:00 2001 From: Akshata Katwal Date: Mon, 27 Jan 2025 19:57:51 +0530 Subject: [PATCH 2/5] update pr --- src/components/AddLeanerModal.tsx | 1 + src/components/DynamicForm.tsx | 29 +++++++++++++++------- src/components/GeneratedSchemas.ts | 3 +++ src/components/UsernameWithSuggestions.tsx | 27 ++++++++++++++------ 4 files changed, 44 insertions(+), 16 deletions(-) diff --git a/src/components/AddLeanerModal.tsx b/src/components/AddLeanerModal.tsx index 1d75cfa8..730bf922 100644 --- a/src/components/AddLeanerModal.tsx +++ b/src/components/AddLeanerModal.tsx @@ -387,6 +387,7 @@ const handleChange = (event: IChangeEvent) => { showErrorList={true} customFields={customFields} formData={customFormData ?? undefined} + setFormData={setCustomFormData} > , event: React.FormEvent ) => void | Promise; onChange: (event: IChangeEvent) => void; onError: (errors: any) => void; + setFormData?: (data: any) => void; showErrorList: boolean; widgets: { @@ -44,13 +48,14 @@ const DynamicForm: React.FC = ({ onError, customFields, children, + setFormData }) => { const widgets = { MultiSelectCheckboxes: MultiSelectCheckboxes, CustomRadioWidget: CustomRadioWidget, MultiSelectDropdown: MultiSelectDropdown, CustomNumberWidget: CustomNumberWidget, - UsernameWithSuggestions: UsernameWithSuggestions as React.FC> // Ensure correct type + UsernameWithSuggestions: UsernameWithSuggestions as React.FC> // Ensure correct type }; const { t } = useTranslation(); const [suggestions, setSuggestions] = useState([]); @@ -282,17 +287,23 @@ const DynamicForm: React.FC = ({ } } }; - const handleSuggestionSelect = (suggestion: string) => { - console.log("Selected Suggestion:", suggestion); - setSuggestions([]); + + + const handleSuggestionSelect = (selectedUsername: string) => { + if(setFormData) + setFormData((prev: any) => ({ + ...prev, + username: selectedUsername + })); + setSuggestions([]); }; + + return (
= ({ {suggestions.length > 0 && (
{suggestions.map((suggestion: any, index: number) => ( - onSuggestionSelect(suggestion)} - > - - + + {/* Availble suggestion : */} + onSuggestionSelect(suggestion)} + sx={{cursor:'pointer', + color:'green', + + }} + > + {suggestion} + + + + // onSuggestionSelect(suggestion)} + // > + // + // ))}
)} From 8ae0fb1a5be4b552be6521600f2715f5ffe0cce3 Mon Sep 17 00:00:00 2001 From: Akshata Katwal Date: Tue, 28 Jan 2025 11:20:57 +0530 Subject: [PATCH 3/5] update pr --- src/components/UsernameWithSuggestions.tsx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/components/UsernameWithSuggestions.tsx b/src/components/UsernameWithSuggestions.tsx index 2520e978..39b3e11c 100644 --- a/src/components/UsernameWithSuggestions.tsx +++ b/src/components/UsernameWithSuggestions.tsx @@ -2,11 +2,17 @@ import { Box, ListItemText, MenuItem, TextField, Typography } from "@mui/materia import { WidgetProps } from "@rjsf/utils"; import React from "react"; interface UsernameWidgetProps { - formContext: any - value: any; - onChange: (value: any) => void; - onBlur: (field: any ,value: any) => void; - + // formContext: any + // value: any; + // onChange: (value: any) => void; + // onBlur: (field: any ,value: any) => void; + formContext: { + suggestions: string[]; + onSuggestionSelect: (suggestion: string) => void; + }; + value: string; + onChange: (value: string) => void; + onBlur: (field: string, value: string) => void; } const UsernameWithSuggestions: React.FC = ({ From 0413770433bef51fbe19b36d550628882596eaaf Mon Sep 17 00:00:00 2001 From: Akshata Katwal Date: Tue, 28 Jan 2025 11:36:54 +0530 Subject: [PATCH 4/5] update pr --- src/components/DynamicForm.tsx | 6 +++--- src/components/UsernameWithSuggestions.tsx | 22 +++++++--------------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/src/components/DynamicForm.tsx b/src/components/DynamicForm.tsx index e5199dfc..542f4df9 100644 --- a/src/components/DynamicForm.tsx +++ b/src/components/DynamicForm.tsx @@ -18,8 +18,8 @@ interface DynamicFormProps { schema: any; uiSchema: object; formData?: { - username?: string; // Add username explicitly as a field - [key: string]: any; // Allow for other fields as well + username?: string; + [key: string]: any; }; onSubmit: ( data: IChangeEvent, @@ -55,7 +55,7 @@ const DynamicForm: React.FC = ({ CustomRadioWidget: CustomRadioWidget, MultiSelectDropdown: MultiSelectDropdown, CustomNumberWidget: CustomNumberWidget, - UsernameWithSuggestions: UsernameWithSuggestions as React.FC> // Ensure correct type + UsernameWithSuggestions: UsernameWithSuggestions as React.FC> }; const { t } = useTranslation(); const [suggestions, setSuggestions] = useState([]); diff --git a/src/components/UsernameWithSuggestions.tsx b/src/components/UsernameWithSuggestions.tsx index 39b3e11c..3e927c0e 100644 --- a/src/components/UsernameWithSuggestions.tsx +++ b/src/components/UsernameWithSuggestions.tsx @@ -2,10 +2,7 @@ import { Box, ListItemText, MenuItem, TextField, Typography } from "@mui/materia import { WidgetProps } from "@rjsf/utils"; import React from "react"; interface UsernameWidgetProps { - // formContext: any - // value: any; - // onChange: (value: any) => void; - // onBlur: (field: any ,value: any) => void; + formContext: { suggestions: string[]; onSuggestionSelect: (suggestion: string) => void; @@ -26,7 +23,7 @@ const UsernameWithSuggestions: React.FC = ({ const handleBlur = (event: React.FocusEvent) => { if (onBlur) { - onBlur(event.target.name, event.target.value); // Forwarding onBlur event to the parent + onBlur(event.target.name, event.target.value); } }; @@ -43,19 +40,19 @@ const UsernameWithSuggestions: React.FC = ({ return (
- {suggestions.length > 0 && ( + {suggestions?.length > 0 && (
- {suggestions.map((suggestion: any, index: number) => ( + {suggestions?.map((suggestion: any, index: number) => ( {/* Availble suggestion : */} = ({ - // onSuggestionSelect(suggestion)} - // > - // - // + ))}
)} From 2f07864390bdade4d0f00c39de1e6d401ed7a82a Mon Sep 17 00:00:00 2001 From: Akshata Katwal Date: Tue, 28 Jan 2025 11:56:31 +0530 Subject: [PATCH 5/5] formatted files --- src/components/AddLeanerModal.tsx | 100 +++++++++------------ src/components/DynamicForm.tsx | 67 +++++++------- src/components/GeneratedSchemas.ts | 21 +++-- src/components/UsernameWithSuggestions.tsx | 49 +++++----- 4 files changed, 105 insertions(+), 132 deletions(-) diff --git a/src/components/AddLeanerModal.tsx b/src/components/AddLeanerModal.tsx index 730bf922..1f24f92b 100644 --- a/src/components/AddLeanerModal.tsx +++ b/src/components/AddLeanerModal.tsx @@ -39,7 +39,6 @@ interface AddLearnerModalProps { onReload?: (() => void) | undefined; learnerEmailId?: string; learnerUserName?: string; - } const AddLearnerModal: React.FC = ({ open, @@ -50,7 +49,7 @@ const AddLearnerModal: React.FC = ({ userId, onReload, learnerUserName, - learnerEmailId + learnerEmailId, }) => { const [schema, setSchema] = React.useState(); const [uiSchema, setUiSchema] = React.useState(); @@ -111,21 +110,17 @@ const AddLearnerModal: React.FC = ({ data: IChangeEvent, event: React.FormEvent ) => { - if(data?.formData?.name) - { - data.formData.name = data?.formData?.name?.trim() + if (data?.formData?.name) { + data.formData.name = data?.formData?.name?.trim(); } - if(data?.formData?.father_name) - { - data.formData.father_name = data?.formData?.father_name?.trim() + if (data?.formData?.father_name) { + data.formData.father_name = data?.formData?.father_name?.trim(); } setTimeout(() => { setLearnerFormData(data.formData); }); - const formData = data.formData; - }; useEffect(() => { @@ -202,7 +197,6 @@ const AddLearnerModal: React.FC = ({ fieldId: fieldData?.state?.districtId, value: [fieldData?.state?.districtCode], }); - } try { @@ -213,11 +207,11 @@ const AddLearnerModal: React.FC = ({ father_name: apiBody.father_name, username: apiBody.username, email: apiBody?.email, - firstName:apiBody?.firstName, - middleName:apiBody?.middleName, - lastName:apiBody?.lastName, - dob:apiBody?.dob, - gender:apiBody?.gender + firstName: apiBody?.firstName, + middleName: apiBody?.middleName, + lastName: apiBody?.lastName, + dob: apiBody?.dob, + gender: apiBody?.gender, }; const customFields = apiBody.customFields; const object = { @@ -225,13 +219,10 @@ const AddLearnerModal: React.FC = ({ customFields: customFields, }; - if(learnerEmailId===userData.email) - { + if (learnerEmailId === userData.email) { delete userData.email; - } - if(learnerUserName===userData.username) - delete userData.username; + if (learnerUserName === userData.username) delete userData.username; const response = await editEditUser(userId, object); if (response) { showToastMessage( @@ -243,8 +234,7 @@ const AddLearnerModal: React.FC = ({ onClose(); } } else { - if(apiBody?.phone_number) - { + if (apiBody?.phone_number) { apiBody.mobile = apiBody?.phone_number; } const response = await createUser(apiBody); @@ -291,11 +281,12 @@ const AddLearnerModal: React.FC = ({ } } } catch (error: any) { - if (error?.response?.data?.params?.err === "User already exist.") { - showToastMessage(error?.response?.data?.params?.err, "error"); - } - else if (error?.response?.data?.params?.errmsg === "Email already exists") { - showToastMessage(error?.response?.data?.params?.errmsg, "error"); + if (error?.response?.data?.params?.err === 'User already exist.') { + showToastMessage(error?.response?.data?.params?.err, 'error'); + } else if ( + error?.response?.data?.params?.errmsg === 'Email already exists' + ) { + showToastMessage(error?.response?.data?.params?.errmsg, 'error'); } else { showToastMessage(t('COMMON.SOMETHING_WENT_WRONG'), 'error'); } @@ -307,38 +298,31 @@ const AddLearnerModal: React.FC = ({ } }; - -const handleChange = (event: IChangeEvent) => { - const { formData } = event; - - - if (!isEditModal) { - const { firstName, lastName, username } = formData; - if (firstName && lastName) { - const updatedUsername = event.formData.username - ? username - : firstName && lastName - ? (firstName + lastName).toLowerCase() - : ''; - - - const updatedFormData = { - ...formData, - username: updatedUsername, - }; - - setCustomFormData(updatedFormData); + const handleChange = (event: IChangeEvent) => { + const { formData } = event; + + if (!isEditModal) { + const { firstName, lastName, username } = formData; + if (firstName && lastName) { + const updatedUsername = event.formData.username + ? username + : firstName && lastName + ? (firstName + lastName).toLowerCase() + : ''; + + const updatedFormData = { + ...formData, + username: updatedUsername, + }; + + setCustomFormData(updatedFormData); + } else { + setCustomFormData({ ...event.formData }); + } } else { - setCustomFormData({ ...event.formData }); + setCustomFormData({ ...formData }); } - } else { - setCustomFormData({ ...formData }); - } -}; - - - - + }; const handleError = (errors: any) => { console.log('Form errors:', errors); diff --git a/src/components/DynamicForm.tsx b/src/components/DynamicForm.tsx index 542f4df9..3e403643 100644 --- a/src/components/DynamicForm.tsx +++ b/src/components/DynamicForm.tsx @@ -18,10 +18,10 @@ interface DynamicFormProps { schema: any; uiSchema: object; formData?: { - username?: string; + username?: string; [key: string]: any; - }; - onSubmit: ( + }; + onSubmit: ( data: IChangeEvent, event: React.FormEvent ) => void | Promise; @@ -48,14 +48,16 @@ const DynamicForm: React.FC = ({ onError, customFields, children, - setFormData + setFormData, }) => { const widgets = { MultiSelectCheckboxes: MultiSelectCheckboxes, CustomRadioWidget: CustomRadioWidget, MultiSelectDropdown: MultiSelectDropdown, CustomNumberWidget: CustomNumberWidget, - UsernameWithSuggestions: UsernameWithSuggestions as React.FC> + UsernameWithSuggestions: UsernameWithSuggestions as React.FC< + WidgetProps + >, }; const { t } = useTranslation(); const [suggestions, setSuggestions] = useState([]); @@ -91,13 +93,16 @@ const DynamicForm: React.FC = ({ }; const sanitizeFormData = (data: any): any => { if (Array.isArray(data)) { - return data.map(item => (typeof item === "undefined" ? '' : sanitizeFormData(item))); + return data.map((item) => + typeof item === 'undefined' ? '' : sanitizeFormData(item) + ); } if (data !== null && typeof data === 'object') { return Object.fromEntries( - - Object.entries(data)?.map(([key, value]) => [key, value === "undefined" ? "" : sanitizeFormData(value)]) - + Object.entries(data)?.map(([key, value]) => [ + key, + value === 'undefined' ? '' : sanitizeFormData(value), + ]) ); } return data; @@ -194,16 +199,12 @@ const DynamicForm: React.FC = ({ ); break; } - case '^[a-zA-Z0-9.@]+$': - - { - error.message = t( - 'FORM_ERROR_MESSAGES.SPACE_AND_SPECIAL_CHARACTERS_NOT_ALLOWED' - ); - break; - - - } + case '^[a-zA-Z0-9.@]+$': { + error.message = t( + 'FORM_ERROR_MESSAGES.SPACE_AND_SPECIAL_CHARACTERS_NOT_ALLOWED' + ); + break; + } default: { if (error?.property === '.email') { const validEmail = emailPattern.test(pattern); @@ -277,34 +278,29 @@ const DynamicForm: React.FC = ({ const handleUsernameBlur = async (username: string) => { if (username) { try { - - console.log('Username onblur called'); - // setSuggestions(["1234"]) - - + console.log('Username onblur called'); + // setSuggestions(["1234"]) } catch (error) { console.error('Error validating username:', error); } } }; - const handleSuggestionSelect = (selectedUsername: string) => { - if(setFormData) - setFormData((prev: any) => ({ - ...prev, - username: selectedUsername - })); + if (setFormData) + setFormData((prev: any) => ({ + ...prev, + username: selectedUsername, + })); setSuggestions([]); }; - return (
= ({ onSuggestionSelect: handleSuggestionSelect, }} onBlur={(field, value) => { - if (field==="username") { - + if (field === 'username') { handleUsernameBlur(value); } }} > - - {children}
diff --git a/src/components/GeneratedSchemas.ts b/src/components/GeneratedSchemas.ts index 1fc297d6..e8aef18f 100644 --- a/src/components/GeneratedSchemas.ts +++ b/src/components/GeneratedSchemas.ts @@ -2,7 +2,11 @@ import { UiSchema } from '@rjsf/utils'; import { JSONSchema7 } from 'json-schema'; import NumberInputField from './form/NumberInputField'; import { FormData, Field, FieldOption } from '@/utils/Interfaces'; -import { getCurrentYearPattern, getEmailPattern, getLastDayDate } from '@/utils/Helper'; +import { + getCurrentYearPattern, + getEmailPattern, + getLastDayDate, +} from '@/utils/Helper'; export const customFields = { NumberInputField: NumberInputField, @@ -57,9 +61,8 @@ export const GenerateSchemaAndUiSchema = ( if (field?.hint) { fieldUiSchema['ui:help'] = t(`FORM.${field?.hint}`); } - if(name==="username") - fieldUiSchema['ui:widget'] = 'UsernameWithSuggestions'; - + if (name === 'username') + fieldUiSchema['ui:widget'] = 'UsernameWithSuggestions'; break; case 'email': @@ -118,11 +121,11 @@ export const GenerateSchemaAndUiSchema = ( })); fieldUiSchema['ui:widget'] = 'CustomRadioWidget'; break; - case 'date': - fieldSchema.type = 'string'; - fieldSchema.format = 'date'; - fieldUiSchema['ui:widget'] = 'date'; - break; + case 'date': + fieldSchema.type = 'string'; + fieldSchema.format = 'date'; + fieldUiSchema['ui:widget'] = 'date'; + break; default: break; } diff --git a/src/components/UsernameWithSuggestions.tsx b/src/components/UsernameWithSuggestions.tsx index 3e927c0e..1fb3d0f2 100644 --- a/src/components/UsernameWithSuggestions.tsx +++ b/src/components/UsernameWithSuggestions.tsx @@ -1,16 +1,20 @@ -import { Box, ListItemText, MenuItem, TextField, Typography } from "@mui/material"; -import { WidgetProps } from "@rjsf/utils"; -import React from "react"; +import { + Box, + ListItemText, + MenuItem, + TextField, + Typography, +} from '@mui/material'; +import { WidgetProps } from '@rjsf/utils'; +import React from 'react'; interface UsernameWidgetProps { - formContext: { - suggestions: string[]; - onSuggestionSelect: (suggestion: string) => void; - }; - value: string; - onChange: (value: string) => void; - onBlur: (field: string, value: string) => void; - + suggestions: string[]; + onSuggestionSelect: (suggestion: string) => void; + }; + value: string; + onChange: (value: string) => void; + onBlur: (field: string, value: string) => void; } const UsernameWithSuggestions: React.FC = ({ formContext, @@ -23,7 +27,7 @@ const UsernameWithSuggestions: React.FC = ({ const handleBlur = (event: React.FocusEvent) => { if (onBlur) { - onBlur(event.target.name, event.target.value); + onBlur(event.target.name, event.target.value); } }; @@ -34,20 +38,18 @@ const UsernameWithSuggestions: React.FC = ({ }; const handleUsernameChange = (event: React.ChangeEvent) => { - onChange(event.target.value); + onChange(event.target.value); }; return (
{suggestions?.length > 0 && ( @@ -56,17 +58,12 @@ const UsernameWithSuggestions: React.FC = ({ {/* Availble suggestion : */} onSuggestionSelect(suggestion)} - sx={{cursor:'pointer', - color:'green', - - }} + onClick={() => onSuggestionSelect(suggestion)} + sx={{ cursor: 'pointer', color: 'green' }} > - {suggestion} - + {suggestion} - ))}
)} @@ -75,7 +72,3 @@ const UsernameWithSuggestions: React.FC = ({ }; export default UsernameWithSuggestions; - - - -